summary refs log tree commit diff
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2017-11-09 23:29:39 +0100
committerLudovic Courtès <ludo@gnu.org>2017-11-09 23:54:47 +0100
commita2985bb101faac9f085176e0329488b91b81dfb5 (patch)
treeb2c27f187aabe74c3cf619bcb8b2dc3c16e2c098
parent935542fbde17f0bc865cbcbb8d9f632bd592cc96 (diff)
downloadguix-a2985bb101faac9f085176e0329488b91b81dfb5.tar.gz
ui: Provide hints for unbound-variable errors.
* guix/ui.scm (known-variable-definition): New procedure.
(report-load-error): Handle 'unbound-variable'.
-rw-r--r--guix/ui.scm42
1 files changed, 42 insertions, 0 deletions
diff --git a/guix/ui.scm b/guix/ui.scm
index 02f3638f3a..9f790b6451 100644
--- a/guix/ui.scm
+++ b/guix/ui.scm
@@ -229,6 +229,38 @@ messages."
              (else
               #t))))))
 
+(define (known-variable-definition variable)
+  "Search among the currently loaded modules one that defines a variable named
+VARIABLE and return it, or #f if none was found."
+  (define (module<? m1 m2)
+    (match (module-name m2)
+      (('gnu _ ...) #t)
+      (('guix _ ...)
+       (match (module-name m1)
+         (('gnu _ ...) #f)
+         (_ #t)))
+      (_ #f)))
+
+  (let loop ((modules (list (resolve-module '() #f #f #:ensure #f)))
+             (suggestions '()))
+    (match modules
+      (()
+       ;; Pick the "best" suggestion.
+       (match (sort suggestions module<?)
+         (() #f)
+         ((first _ ...) first)))
+      ((head tail ...)
+       (let ((next (append tail
+                           (hash-map->list (lambda (name module)
+                                             module)
+                                           (module-submodules head)))))
+         (match (module-local-variable head variable)
+           (#f (loop next suggestions))
+           (_
+            (match (module-name head)
+              (('gnu _ ...) head)                 ;must be that one
+              (_ (loop next (cons head suggestions)))))))))))
+
 (define* (display-hint message #:optional (port (current-error-port)))
   "Display MESSAGE, a l10n message possibly containing Texinfo markup, to
 PORT."
@@ -256,6 +288,16 @@ ARGS is the list of arguments received by the 'throw' handler."
      (let ((loc (source-properties->location properties)))
        (format (current-error-port) (G_ "~a: error: ~a~%")
                (location->string loc) message)))
+    (('unbound-variable proc message (variable) _ ...)
+     (match args
+       ((key . args)
+        (print-exception (current-error-port) frame key args)))
+     (match (known-variable-definition variable)
+       (#f
+        (display-hint (G_ "Did you forget a @code{use-modules} form?")))
+       (module
+        (display-hint (format #f (G_ "Try adding @code{(use-modules ~a)}.")
+                              (module-name module))))))
     (('srfi-34 obj)
      (if (message-condition? obj)
          (if (error-location? obj)