diff options
| author | Noam Postavsky | 2018-03-10 18:12:55 -0500 |
|---|---|---|
| committer | Noam Postavsky | 2019-11-28 18:10:07 -0500 |
| commit | b2790db049da98b541d07bac21ca7d7c220d3be0 (patch) | |
| tree | 61aba17be4946bf84dc0dd57b1d7157b88725c83 /lisp | |
| parent | 85f586f3ce5c6d9598d345440fd57e0fc9b8d98b (diff) | |
| download | emacs-b2790db049da98b541d07bac21ca7d7c220d3be0.tar.gz emacs-b2790db049da98b541d07bac21ca7d7c220d3be0.zip | |
Improve errors & warnings due to fancy quoted vars (Bug#32939)
Add some hints to the message for byte compiler free & unused variable
warnings, and 'void-variable' errors where the variable has confusable
quote characters in it.
* lisp/help.el (uni-confusables), uni-confusables-regexp): New
constants.
(help-command-error-confusable-suggestions): New function, added to
`command-error-function'.
(help-uni-confusable-suggestions): New function.
* lisp/emacs-lisp/bytecomp.el (byte-compile-variable-ref):
* lisp/emacs-lisp/cconv.el (cconv--analyze-use): Use it.
* lisp/emacs-lisp/lisp-mode.el
(lisp--match-confusable-symbol-character): New function.
(lisp-fdefs): Use it to fontify confusable characters with
font-lock-warning-face when they occur in symbol names.
* doc/lispref/modes.texi (Faces for Font Lock):
* doc/lispref/objects.texi (Basic Char Syntax): Recommend backslash
escaping of confusable characters, and mention new fontification.
* etc/NEWS: Announce the new fontification behavior.
* test/lisp/emacs-lisp/lisp-mode-tests.el (lisp-fontify-confusables):
New test.
Diffstat (limited to 'lisp')
| -rw-r--r-- | lisp/emacs-lisp/bytecomp.el | 10 | ||||
| -rw-r--r-- | lisp/emacs-lisp/cconv.el | 6 | ||||
| -rw-r--r-- | lisp/emacs-lisp/lisp-mode.el | 18 | ||||
| -rw-r--r-- | lisp/help.el | 49 |
4 files changed, 78 insertions, 5 deletions
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el index 905d99a5971..118356ec26a 100644 --- a/lisp/emacs-lisp/bytecomp.el +++ b/lisp/emacs-lisp/bytecomp.el | |||
| @@ -3428,7 +3428,10 @@ for symbols generated by the byte compiler itself." | |||
| 3428 | (boundp var) | 3428 | (boundp var) |
| 3429 | (memq var byte-compile-bound-variables) | 3429 | (memq var byte-compile-bound-variables) |
| 3430 | (memq var byte-compile-free-references)) | 3430 | (memq var byte-compile-free-references)) |
| 3431 | (byte-compile-warn "reference to free variable `%S'" var) | 3431 | (let* ((varname (prin1-to-string var)) |
| 3432 | (suggestions (help-uni-confusable-suggestions varname))) | ||
| 3433 | (byte-compile-warn "reference to free variable `%s'%s" varname | ||
| 3434 | (if suggestions (concat "\n " suggestions) ""))) | ||
| 3432 | (push var byte-compile-free-references)) | 3435 | (push var byte-compile-free-references)) |
| 3433 | (byte-compile-dynamic-variable-op 'byte-varref var)))) | 3436 | (byte-compile-dynamic-variable-op 'byte-varref var)))) |
| 3434 | 3437 | ||
| @@ -3444,7 +3447,10 @@ for symbols generated by the byte compiler itself." | |||
| 3444 | (boundp var) | 3447 | (boundp var) |
| 3445 | (memq var byte-compile-bound-variables) | 3448 | (memq var byte-compile-bound-variables) |
| 3446 | (memq var byte-compile-free-assignments)) | 3449 | (memq var byte-compile-free-assignments)) |
| 3447 | (byte-compile-warn "assignment to free variable `%s'" var) | 3450 | (let* ((varname (prin1-to-string var)) |
| 3451 | (suggestions (help-uni-confusable-suggestions varname))) | ||
| 3452 | (byte-compile-warn "assignment to free variable `%s'%s" varname | ||
| 3453 | (if suggestions (concat "\n " suggestions) ""))) | ||
| 3448 | (push var byte-compile-free-assignments)) | 3454 | (push var byte-compile-free-assignments)) |
| 3449 | (byte-compile-dynamic-variable-op 'byte-varset var)))) | 3455 | (byte-compile-dynamic-variable-op 'byte-varset var)))) |
| 3450 | 3456 | ||
diff --git a/lisp/emacs-lisp/cconv.el b/lisp/emacs-lisp/cconv.el index 58ca9d5f57e..09af7cb9104 100644 --- a/lisp/emacs-lisp/cconv.el +++ b/lisp/emacs-lisp/cconv.el | |||
| @@ -591,8 +591,10 @@ FORM is the parent form that binds this var." | |||
| 591 | (eq ?_ (aref (symbol-name var) 0)) | 591 | (eq ?_ (aref (symbol-name var) 0)) |
| 592 | ;; As a special exception, ignore "ignore". | 592 | ;; As a special exception, ignore "ignore". |
| 593 | (eq var 'ignored)) | 593 | (eq var 'ignored)) |
| 594 | (byte-compile-warn "Unused lexical %s `%S'" | 594 | (let ((suggestions (help-uni-confusable-suggestions (symbol-name var)))) |
| 595 | varkind var))) | 595 | (byte-compile-warn "Unused lexical %s `%S'%s" |
| 596 | varkind var | ||
| 597 | (if suggestions (concat "\n " suggestions) ""))))) | ||
| 596 | ;; If it's unused, there's no point converting it into a cons-cell, even if | 598 | ;; If it's unused, there's no point converting it into a cons-cell, even if |
| 597 | ;; it's captured and mutated. | 599 | ;; it's captured and mutated. |
| 598 | (`(,binder ,_ t t ,_) | 600 | (`(,binder ,_ t t ,_) |
diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el index 5df52ebc98f..56f8ef63682 100644 --- a/lisp/emacs-lisp/lisp-mode.el +++ b/lisp/emacs-lisp/lisp-mode.el | |||
| @@ -280,6 +280,19 @@ This will generate compile-time constants from BINDINGS." | |||
| 280 | `(face ,font-lock-warning-face | 280 | `(face ,font-lock-warning-face |
| 281 | help-echo "This \\ has no effect")))) | 281 | help-echo "This \\ has no effect")))) |
| 282 | 282 | ||
| 283 | (defun lisp--match-confusable-symbol-character (limit) | ||
| 284 | ;; Match a confusable character within a Lisp symbol. | ||
| 285 | (catch 'matched | ||
| 286 | (while t | ||
| 287 | (if (re-search-forward uni-confusables-regexp limit t) | ||
| 288 | ;; Skip confusables which are backslash escaped, or inside | ||
| 289 | ;; strings or comments. | ||
| 290 | (save-match-data | ||
| 291 | (unless (or (eq (char-before (match-beginning 0)) ?\\) | ||
| 292 | (nth 8 (syntax-ppss))) | ||
| 293 | (throw 'matched t))) | ||
| 294 | (throw 'matched nil))))) | ||
| 295 | |||
| 283 | (let-when-compile | 296 | (let-when-compile |
| 284 | ((lisp-fdefs '("defmacro" "defun")) | 297 | ((lisp-fdefs '("defmacro" "defun")) |
| 285 | (lisp-vdefs '("defvar")) | 298 | (lisp-vdefs '("defvar")) |
| @@ -463,7 +476,10 @@ This will generate compile-time constants from BINDINGS." | |||
| 463 | (3 'font-lock-regexp-grouping-construct prepend)) | 476 | (3 'font-lock-regexp-grouping-construct prepend)) |
| 464 | (lisp--match-hidden-arg | 477 | (lisp--match-hidden-arg |
| 465 | (0 '(face font-lock-warning-face | 478 | (0 '(face font-lock-warning-face |
| 466 | help-echo "Hidden behind deeper element; move to another line?"))) | 479 | help-echo "Hidden behind deeper element; move to another line?"))) |
| 480 | (lisp--match-confusable-symbol-character | ||
| 481 | 0 '(face font-lock-warning-face | ||
| 482 | help-echo "Confusable character")) | ||
| 467 | )) | 483 | )) |
| 468 | "Gaudy level highlighting for Emacs Lisp mode.") | 484 | "Gaudy level highlighting for Emacs Lisp mode.") |
| 469 | 485 | ||
diff --git a/lisp/help.el b/lisp/help.el index 604a365957c..8bb942abf4f 100644 --- a/lisp/help.el +++ b/lisp/help.el | |||
| @@ -1508,6 +1508,55 @@ the same names as used in the original source code, when possible." | |||
| 1508 | (help--docstring-quote (format "%S" (help--make-usage fn arglist))))) | 1508 | (help--docstring-quote (format "%S" (help--make-usage fn arglist))))) |
| 1509 | 1509 | ||
| 1510 | 1510 | ||
| 1511 | |||
| 1512 | ;; Just some quote-like characters for now. TODO: generate this stuff | ||
| 1513 | ;; from official Unicode data. | ||
| 1514 | (defconst uni-confusables | ||
| 1515 | '((#x2018 . "'") ;; LEFT SINGLE QUOTATION MARK | ||
| 1516 | (#x2019 . "'") ;; RIGHT SINGLE QUOTATION MARK | ||
| 1517 | (#x201B . "'") ;; SINGLE HIGH-REVERSED-9 QUOTATION MARK | ||
| 1518 | (#x201C . "\"") ;; LEFT DOUBLE QUOTATION MARK | ||
| 1519 | (#x201D . "\"") ;; RIGHT DOUBLE QUOTATION MARK | ||
| 1520 | (#x201F . "\"") ;; DOUBLE HIGH-REVERSED-9 QUOTATION MARK | ||
| 1521 | (#x301E . "\"") ;; DOUBLE PRIME QUOTATION MARK | ||
| 1522 | (#xFF02 . "'") ;; FULLWIDTH QUOTATION MARK | ||
| 1523 | (#xFF07 . "'") ;; FULLWIDTH APOSTROPHE | ||
| 1524 | )) | ||
| 1525 | |||
| 1526 | (defconst uni-confusables-regexp | ||
| 1527 | (concat "[" (mapcar #'car uni-confusables) "]")) | ||
| 1528 | |||
| 1529 | (defun help-uni-confusable-suggestions (string) | ||
| 1530 | "Return a message describing confusables in STRING." | ||
| 1531 | (let ((i 0) | ||
| 1532 | (confusables nil)) | ||
| 1533 | (while (setq i (string-match uni-confusables-regexp string i)) | ||
| 1534 | (let ((replacement (alist-get (aref string i) uni-confusables))) | ||
| 1535 | (push (aref string i) confusables) | ||
| 1536 | (setq string (replace-match replacement t t string)) | ||
| 1537 | (setq i (+ i (length replacement))))) | ||
| 1538 | (when confusables | ||
| 1539 | (format-message | ||
| 1540 | (if (> (length confusables) 1) | ||
| 1541 | "Found confusable characters: %s; perhaps you meant: `%s'?" | ||
| 1542 | "Found confusable character: %s, perhaps you meant: `%s'?") | ||
| 1543 | (mapconcat (lambda (c) (format-message "`%c'" c)) | ||
| 1544 | confusables ", ") | ||
| 1545 | string)))) | ||
| 1546 | |||
| 1547 | (defun help-command-error-confusable-suggestions (data _context _signal) | ||
| 1548 | (pcase data | ||
| 1549 | (`(void-variable ,var) | ||
| 1550 | (let ((suggestions (help-uni-confusable-suggestions | ||
| 1551 | (symbol-name var)))) | ||
| 1552 | (when suggestions | ||
| 1553 | (princ (concat "\n " suggestions) t)))) | ||
| 1554 | (_ nil))) | ||
| 1555 | |||
| 1556 | (add-function :after command-error-function | ||
| 1557 | #'help-command-error-confusable-suggestions) | ||
| 1558 | |||
| 1559 | |||
| 1511 | (provide 'help) | 1560 | (provide 'help) |
| 1512 | 1561 | ||
| 1513 | ;;; help.el ends here | 1562 | ;;; help.el ends here |