aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias EngdegÄrd2023-05-11 19:24:51 +0200
committerMattias EngdegÄrd2023-05-13 11:53:25 +0200
commitbfc07100d28d0f687da0a1dd5fdfa42a92a93f88 (patch)
tree4ca024cacb42464e68c51f07bbbae3d1fe9af4eb
parentfa598571adab4858282f337b45984517e197f8a9 (diff)
downloademacs-bfc07100d28d0f687da0a1dd5fdfa42a92a93f88.tar.gz
emacs-bfc07100d28d0f687da0a1dd5fdfa42a92a93f88.zip
Byte-compiler warning about mutation of constant values
When we can easily detect mutation of constants (quoted lists, strings and vectors), warn. For example, (setcdr '(1 . 2) 3) (nreverse [1 2 3]) (put-text-property 0 3 'face 'highlight "moo") Such code can result in surprising behaviour and problems that are difficult to debug. * lisp/emacs-lisp/bytecomp.el (byte-compile-form, mutating-fns): Add the warning and a list of functions to warn about. * etc/NEWS: Announce. * test/lisp/emacs-lisp/bytecomp-tests.el (bytecomp-test--with-suppressed-warnings): Add test cases.
-rw-r--r--etc/NEWS20
-rw-r--r--lisp/emacs-lisp/bytecomp.el53
-rw-r--r--test/lisp/emacs-lisp/bytecomp-tests.el30
3 files changed, 103 insertions, 0 deletions
diff --git a/etc/NEWS b/etc/NEWS
index 3bef9d2ed2a..7d033b0b13e 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -510,6 +510,26 @@ This warning can be suppressed using 'with-suppressed-warnings' with
510the warning name 'suspicious'. 510the warning name 'suspicious'.
511 511
512--- 512---
513*** Warn about mutation of constant values.
514The compiler now warns about code that modifies program constants in
515some obvious cases. Examples:
516
517 (setcar '(1 2) 7)
518 (aset [3 4] 0 8)
519 (aset "abc" 1 ?d)
520
521Such code may have unpredictable behaviour because the constants are
522part of the program, not data structures generated afresh during
523execution, and the compiler does not expect them to change.
524
525To avoid the warning, operate on an object created by the program
526(maybe a copy of the constant), or use a non-destructive operation
527instead.
528
529This warning can be suppressed using 'with-suppressed-warnings' with
530the warning name 'suspicious'.
531
532---
513*** Warn about more ignored function return values. 533*** Warn about more ignored function return values.
514The compiler now warns when the return value from certain functions is 534The compiler now warns when the return value from certain functions is
515implicitly ignored. Example: 535implicitly ignored. Example:
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index 6c804056ee7..d17f1c93a76 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -3488,6 +3488,22 @@ lambda-expression."
3488 (format-message "; use `%s' instead." 3488 (format-message "; use `%s' instead."
3489 interactive-only)) 3489 interactive-only))
3490 (t ".")))) 3490 (t "."))))
3491 (let ((mutargs (function-get (car form) 'mutates-arguments)))
3492 (when mutargs
3493 (dolist (idx (if (eq mutargs 'all-but-last)
3494 (number-sequence 1 (- (length form) 2))
3495 mutargs))
3496 (let ((arg (nth idx form)))
3497 (when (and (or (and (eq (car-safe arg) 'quote)
3498 (consp (nth 1 arg)))
3499 (arrayp arg))
3500 (byte-compile-warning-enabled-p
3501 'suspicious (car form)))
3502 (byte-compile-warn-x form "`%s' on constant %s (arg %d)"
3503 (car form)
3504 (if (consp arg) "list" (type-of arg))
3505 idx))))))
3506
3491 (if (eq (car-safe (symbol-function (car form))) 'macro) 3507 (if (eq (car-safe (symbol-function (car form))) 'macro)
3492 (byte-compile-report-error 3508 (byte-compile-report-error
3493 (format-message "`%s' defined after use in %S (missing `require' of a library file?)" 3509 (format-message "`%s' defined after use in %S (missing `require' of a library file?)"
@@ -3557,6 +3573,43 @@ lambda-expression."
3557 (dolist (fn important-return-value-fns) 3573 (dolist (fn important-return-value-fns)
3558 (put fn 'important-return-value t))) 3574 (put fn 'important-return-value t)))
3559 3575
3576(let ((mutating-fns
3577 ;; FIXME: Should there be a function declaration for this?
3578 ;;
3579 ;; (FUNC . ARGS) means that FUNC mutates arguments whose indices are
3580 ;; in the list ARGS, starting at 1, or all but the last argument if
3581 ;; ARGS is `all-but-last'.
3582 '(
3583 (setcar 1) (setcdr 1) (aset 1)
3584 (nreverse 1)
3585 (nconc . all-but-last)
3586 (nbutlast 1) (ntake 2)
3587 (sort 1)
3588 (delq 2) (delete 2)
3589 (delete-dups 1) (delete-consecutive-dups 1)
3590 (plist-put 1)
3591 (fillarray 1)
3592 (store-substring 1)
3593 (clear-string 1)
3594
3595 (add-text-properties 4) (put-text-property 5) (set-text-properties 4)
3596 (remove-text-properties 4) (remove-list-of-text-properties 4)
3597 (alter-text-property 5)
3598 (add-face-text-property 5) (add-display-text-property 5)
3599
3600 (cl-delete 2) (cl-delete-if 2) (cl-delete-if-not 2)
3601 (cl-delete-duplicates 1)
3602 (cl-nsubst 3) (cl-nsubst-if 3) (cl-nsubst-if-not 3)
3603 (cl-nsubstitute 3) (cl-nsubstitute-if 3) (cl-nsubstitute-if-not 3)
3604 (cl-nsublis 2)
3605 (cl-nunion 1 2) (cl-nintersection 1 2) (cl-nset-difference 1 2)
3606 (cl-nset-exclusive-or 1 2)
3607 (cl-nreconc 1)
3608 (cl-sort 1) (cl-stable-sort 1) (cl-merge 2 3)
3609 )))
3610 (dolist (entry mutating-fns)
3611 (put (car entry) 'mutates-arguments (cdr entry))))
3612
3560 3613
3561(defun byte-compile-normal-call (form) 3614(defun byte-compile-normal-call (form)
3562 (when (and (symbolp (car form)) 3615 (when (and (symbolp (car form))
diff --git a/test/lisp/emacs-lisp/bytecomp-tests.el b/test/lisp/emacs-lisp/bytecomp-tests.el
index 222065c2e4e..9136a6cd9b3 100644
--- a/test/lisp/emacs-lisp/bytecomp-tests.el
+++ b/test/lisp/emacs-lisp/bytecomp-tests.el
@@ -1518,6 +1518,36 @@ literals (Bug#20852)."
1518 )) 1518 ))
1519 '((empty-body with-suppressed-warnings)) 1519 '((empty-body with-suppressed-warnings))
1520 "Warning: `with-suppressed-warnings' with empty body") 1520 "Warning: `with-suppressed-warnings' with empty body")
1521
1522 (test-suppression
1523 '(defun zot ()
1524 (setcar '(1 2) 3))
1525 '((suspicious setcar))
1526 "Warning: `setcar' on constant list (arg 1)")
1527
1528 (test-suppression
1529 '(defun zot ()
1530 (aset [1 2] 1 3))
1531 '((suspicious aset))
1532 "Warning: `aset' on constant vector (arg 1)")
1533
1534 (test-suppression
1535 '(defun zot ()
1536 (aset "abc" 1 ?d))
1537 '((suspicious aset))
1538 "Warning: `aset' on constant string (arg 1)")
1539
1540 (test-suppression
1541 '(defun zot (x y)
1542 (nconc x y '(1 2) '(3 4)))
1543 '((suspicious nconc))
1544 "Warning: `nconc' on constant list (arg 3)")
1545
1546 (test-suppression
1547 '(defun zot ()
1548 (put-text-property 0 2 'prop 'val "abc"))
1549 '((suspicious put-text-property))
1550 "Warning: `put-text-property' on constant string (arg 5)")
1521 ) 1551 )
1522 1552
1523(ert-deftest bytecomp-tests--not-writable-directory () 1553(ert-deftest bytecomp-tests--not-writable-directory ()