aboutsummaryrefslogtreecommitdiffstats
path: root/lisp
diff options
context:
space:
mode:
authorNoam Postavsky2018-03-10 18:12:55 -0500
committerNoam Postavsky2019-11-28 18:10:07 -0500
commitb2790db049da98b541d07bac21ca7d7c220d3be0 (patch)
tree61aba17be4946bf84dc0dd57b1d7157b88725c83 /lisp
parent85f586f3ce5c6d9598d345440fd57e0fc9b8d98b (diff)
downloademacs-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.el10
-rw-r--r--lisp/emacs-lisp/cconv.el6
-rw-r--r--lisp/emacs-lisp/lisp-mode.el18
-rw-r--r--lisp/help.el49
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