]> git.eshelyaron.com Git - emacs.git/commitdiff
Add byte-compiler warning about useless trailing cond clauses
authorMattias Engdegård <mattiase@acm.org>
Sat, 9 Sep 2023 11:04:54 +0000 (13:04 +0200)
committerMattias Engdegård <mattiase@acm.org>
Sat, 9 Sep 2023 11:24:31 +0000 (13:24 +0200)
Warn about clauses after the default clause, as in

    (cond ((= x 0) (say "none"))
          (t (say "some"))
          (say "goodbye"))

because they are very much an indicator of a mistake (such as
misplaced brackets), and since they are deleted by the optimiser, any
other warnings there are lost and the user wouldn't know that
something is wrong otherwise.

* lisp/emacs-lisp/macroexp.el (macroexp--expand-all): Add warning.
* etc/NEWS: Announce.
* test/lisp/emacs-lisp/bytecomp-tests.el
(bytecomp-test--with-suppressed-warnings): Add test case.

etc/NEWS
lisp/emacs-lisp/macroexp.el
test/lisp/emacs-lisp/bytecomp-tests.el

index f6be603294e3adf48f093e8a58be70a1199a9098..51e89fc96dd677fc470166bcdf118e98ca55819e 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1084,6 +1084,21 @@ simplified away.
 This warning can be suppressed using 'with-suppressed-warnings' with
 the warning name 'suspicious'.
 
+---
+*** Warn about useless trailing 'cond' clauses.
+The compiler now warns when a 'cond' form contains clauses following a
+default (unconditional) clause.  Example:
+
+    (cond ((= x 0) (say "none"))
+          (t (say "some"))
+          (say "goodbye"))
+
+Such a clause will never be executed but is likely to be a mistake,
+perhaps due to misplaced brackets.
+
+This warning can be suppressed using 'with-suppressed-warnings' with
+the warning name 'suspicious'.
+
 ---
 *** Warn about mutation of constant values.
 The compiler now warns about code that modifies program constants in
index 6d5cf8723f907bc60aecd636a2a1770a25398156..b21f0f0d47f3d3e209a24ae10f59c80d0833ca04 100644 (file)
@@ -330,7 +330,34 @@ Assumes the caller has bound `macroexpand-all-environment'."
         (let ((fn (car-safe form)))
           (pcase form
             (`(cond . ,clauses)
-             (macroexp--cons fn (macroexp--all-clauses clauses) form))
+             ;; Check for rubbish clauses at the end before macro-expansion,
+             ;; to avoid nuisance warnings from clauses that become
+             ;; unconditional through that process.
+             ;; FIXME: this strategy is defeated by forced `macroexpand-all',
+             ;; such as in `cl-flet'.  Haven't seen that in the wild, though.
+             (let ((default-tail nil)
+                   (n 0)
+                   (rest clauses))
+               (while rest
+                 (let ((c (car-safe (car rest))))
+                   (when (cond ((consp c) (and (memq (car c) '(quote function))
+                                               (cadr c)))
+                               ((symbolp c) (or (eq c t) (keywordp c)))
+                               (t t))
+                     ;; This is unquestionably a default clause.
+                     (setq default-tail (cdr rest))
+                     (setq clauses (take (1+ n) clauses))  ; trim the tail
+                     (setq rest nil)))
+                 (setq n (1+ n))
+                 (setq rest (cdr rest)))
+               (let ((expanded-form
+                      (macroexp--cons fn (macroexp--all-clauses clauses) form)))
+                 (if default-tail
+                     (macroexp-warn-and-return
+                      (format-message
+                       "Useless clause following default `cond' clause")
+                      expanded-form '(suspicious cond) t default-tail)
+                   expanded-form))))
             (`(condition-case . ,(or `(,err ,body . ,handlers) pcase--dontcare))
              (let ((exp-body (macroexp--expand-all body)))
                (if handlers
index 8bc8a51b04ed26c9b2ba05ffcee14eb0d2677911..cd1bda3ec55248c806513306fa17b21021d4cf90 100644 (file)
@@ -1512,6 +1512,15 @@ literals (Bug#20852)."
    '((suspicious unwind-protect))
    "Warning: `unwind-protect' without unwind forms")
 
+  (test-suppression
+   '(defun zot (x)
+      (cond
+       ((zerop x) 'zero)
+       (t 'nonzero)
+       (happy puppy)))
+   '((suspicious cond))
+   "Warning: Useless clause following default `cond' clause")
+
   (test-suppression
    '(defun zot ()
       (let ((_ 1))