summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2020-07-25 17:59:13 +0200
committerLudovic Courtès <ludo@gnu.org>2020-07-25 19:11:36 +0200
commit252a1926bc7d7aa0b39d89a484c0c1b82e945fcd (patch)
tree1a9be616bc28507c7d4d7459d59f817b00c44dc2
parent860f3d77495aad0061c4ee9b6de73d6fe9fc40e9 (diff)
diagnostics: Add '&formatted-message'.
This allows 'gettext' to be called on the format string at the site where the exception is caught (rather than the site where it's thrown). It also allows for argument highlighting. * guix/diagnostics.scm (&formatted-message): New condition type. (check-format-string): New procedure. (formatted-message): New macro. * guix/ui.scm (report-load-error): Add clause for 'formatted-message?'. (warn-about-load-error): Likewise. (call-with-error-handling): Likewise. (read/eval): Likewise.
-rw-r--r--guix/diagnostics.scm65
-rw-r--r--guix/ui.scm62
2 files changed, 110 insertions, 17 deletions
diff --git a/guix/diagnostics.scm b/guix/diagnostics.scm
index 3b536d8e96d..7b9ffc61b5e 100644
--- a/guix/diagnostics.scm
+++ b/guix/diagnostics.scm
@@ -19,6 +19,7 @@
19(define-module (guix diagnostics) 19(define-module (guix diagnostics)
20 #:use-module (guix colors) 20 #:use-module (guix colors)
21 #:use-module (guix i18n) 21 #:use-module (guix i18n)
22 #:use-module (srfi srfi-1)
22 #:use-module (srfi srfi-9) 23 #:use-module (srfi srfi-9)
23 #:use-module (srfi srfi-26) 24 #:use-module (srfi srfi-26)
24 #:use-module (srfi srfi-35) 25 #:use-module (srfi srfi-35)
@@ -43,6 +44,11 @@
43 error-location? 44 error-location?
44 error-location 45 error-location
45 46
47 formatted-message
48 formatted-message?
49 formatted-message-string
50 formatted-message-arguments
51
46 &fix-hint 52 &fix-hint
47 fix-hint? 53 fix-hint?
48 condition-fix-hint 54 condition-fix-hint
@@ -255,6 +261,65 @@ a location object."
255 fix-hint? 261 fix-hint?
256 (hint condition-fix-hint)) ;string 262 (hint condition-fix-hint)) ;string
257 263
264(define-condition-type &formatted-message &error
265 formatted-message?
266 (format formatted-message-string)
267 (arguments formatted-message-arguments))
268
269(define (check-format-string location format args)
270 "Check that FORMAT, a format string, contains valid escapes, and that the
271number of arguments in ARGS matches the escapes in FORMAT."
272 (define actual-count
273 (length args))
274
275 (define allowed-chars ;for 'simple-format'
276 '(#\A #\S #\a #\s #\~ #\%))
277
278 (define (format-chars fmt)
279 (let loop ((chars (string->list fmt))
280 (result '()))
281 (match chars
282 (()
283 (reverse result))
284 ((#\~ opt rest ...)
285 (loop rest (cons opt result)))
286 ((chr rest ...)
287 (and (memv chr allowed-chars)
288 (loop rest result))))))
289
290 (match (format-chars format)
291 (#f
292 ;; XXX: In this case it could be that FMT contains invalid escapes, or it
293 ;; could be that it contains escapes beyond ALLOWED-CHARS, for (ice-9
294 ;; format). Instead of implementing '-Wformat', do nothing.
295 #f)
296 (chars
297 (let ((count (fold (lambda (chr count)
298 (case chr
299 ((#\~ #\%) count)
300 (else (+ count 1))))
301 0
302 chars)))
303 (unless (= count actual-count)
304 (warning location (G_ "format string got ~a arguments, expected ~a~%")
305 actual-count count))))))
306
307(define-syntax formatted-message
308 (lambda (s)
309 "Return a '&formatted-message' error condition."
310 (syntax-case s (G_)
311 ((_ (G_ str) args ...)
312 (string? (syntax->datum #'str))
313 (let ((str (syntax->datum #'str)))
314 ;; Implement a subset of '-Wformat'.
315 (check-format-string (source-properties->location
316 (syntax-source s))
317 str #'(args ...))
318 (with-syntax ((str (string-append str "\n")))
319 #'(condition
320 (&formatted-message (format str)
321 (arguments (list args ...))))))))))
322
258 323
259(define guix-warning-port 324(define guix-warning-port
260 (make-parameter (current-warning-port))) 325 (make-parameter (current-warning-port)))
diff --git a/guix/ui.scm b/guix/ui.scm
index 588eb8480e6..162eb35d268 100644
--- a/guix/ui.scm
+++ b/guix/ui.scm
@@ -388,12 +388,18 @@ ARGS is the list of arguments received by the 'throw' handler."
388 (('unbound-variable _ ...) 388 (('unbound-variable _ ...)
389 (report-unbound-variable-error args #:frame frame)) 389 (report-unbound-variable-error args #:frame frame))
390 (((or 'srfi-34 '%exception) obj) 390 (((or 'srfi-34 '%exception) obj)
391 (if (message-condition? obj) 391 (cond ((message-condition? obj)
392 (report-error (and (error-location? obj) 392 (report-error (and (error-location? obj)
393 (error-location obj)) 393 (error-location obj))
394 (G_ "~a~%") 394 (G_ "~a~%")
395 (gettext (condition-message obj) %gettext-domain)) 395 (gettext (condition-message obj) %gettext-domain)))
396 (report-error (G_ "exception thrown: ~s~%") obj)) 396 ((formatted-message? obj)
397 (apply report-error
398 (and (error-location? obj) (error-location obj))
399 (gettext (formatted-message-string obj) %gettext-domain)
400 (formatted-message-arguments obj)))
401 (else
402 (report-error (G_ "exception thrown: ~s~%") obj)))
397 (when (fix-hint? obj) 403 (when (fix-hint? obj)
398 (display-hint (condition-fix-hint obj)))) 404 (display-hint (condition-fix-hint obj))))
399 ((key args ...) 405 ((key args ...)
@@ -420,12 +426,19 @@ exiting. ARGS is the list of arguments received by the 'throw' handler."
420 (('unbound-variable _ ...) 426 (('unbound-variable _ ...)
421 (report-unbound-variable-error args)) 427 (report-unbound-variable-error args))
422 (((or 'srfi-34 '%exception) obj) 428 (((or 'srfi-34 '%exception) obj)
423 (if (message-condition? obj) 429 (cond ((message-condition? obj)
424 (warning (G_ "failed to load '~a': ~a~%") 430 (warning (G_ "failed to load '~a': ~a~%")
425 file 431 file
426 (gettext (condition-message obj) %gettext-domain)) 432 (gettext (condition-message obj) %gettext-domain)))
427 (warning (G_ "failed to load '~a': exception thrown: ~s~%") 433 ((formatted-message? obj)
428 file obj))) 434 (warning (G_ "failed to load '~a': ~a~%")
435 (apply format #f
436 (gettext (formatted-message-string obj)
437 %gettext-domain)
438 (formatted-message-arguments obj))))
439 (else
440 (warning (G_ "failed to load '~a': exception thrown: ~s~%")
441 file obj))))
429 ((error args ...) 442 ((error args ...)
430 (warning (G_ "failed to load '~a':~%") module) 443 (warning (G_ "failed to load '~a':~%") module)
431 (apply display-error #f (current-error-port) args) 444 (apply display-error #f (current-error-port) args)
@@ -791,6 +804,15 @@ directories:~{ ~a~}~%")
791 (display-hint (condition-fix-hint c))) 804 (display-hint (condition-fix-hint c)))
792 (exit 1)) 805 (exit 1))
793 806
807 ((formatted-message? c)
808 (apply report-error
809 (and (error-location? c) (error-location c))
810 (gettext (formatted-message-string c) %gettext-domain)
811 (formatted-message-arguments c))
812 (when (fix-hint? c)
813 (display-hint (condition-fix-hint c)))
814 (exit 1))
815
794 ;; On Guile 3.0.0, exceptions such as 'unbound-variable' are 816 ;; On Guile 3.0.0, exceptions such as 'unbound-variable' are
795 ;; compound and include a '&message'. However, that message only 817 ;; compound and include a '&message'. However, that message only
796 ;; contains the format string. Thus, special-case it here to 818 ;; contains the format string. Thus, special-case it here to
@@ -854,11 +876,17 @@ similar."
854 (('syntax-error proc message properties form . rest) 876 (('syntax-error proc message properties form . rest)
855 (report-error (G_ "syntax error: ~a~%") message)) 877 (report-error (G_ "syntax error: ~a~%") message))
856 (((or 'srfi-34 '%exception) obj) 878 (((or 'srfi-34 '%exception) obj)
857 (if (message-condition? obj) 879 (cond ((message-condition? obj)
858 (report-error (G_ "~a~%") 880 (report-error (G_ "~a~%")
859 (gettext (condition-message obj) 881 (gettext (condition-message obj)
860 %gettext-domain)) 882 %gettext-domain)))
861 (report-error (G_ "exception thrown: ~s~%") obj))) 883 ((formatted-message? obj)
884 (apply report-error #f
885 (gettext (formatted-message-string obj)
886 %gettext-domain)
887 (formatted-message-arguments obj)))
888 (else
889 (report-error (G_ "exception thrown: ~s~%") obj))))
862 ((error args ...) 890 ((error args ...)
863 (apply display-error #f (current-error-port) args)) 891 (apply display-error #f (current-error-port) args))
864 (what? #f)) 892 (what? #f))