diff options
| author | Philipp Stephani | 2021-03-18 12:40:08 +0100 |
|---|---|---|
| committer | Philipp Stephani | 2021-04-10 18:19:49 +0200 |
| commit | 53dfd85a7f971875e716a55f010ee508bce89eed (patch) | |
| tree | 3eb0b220ff838287f4d6cd3cd45d19794aceecf7 | |
| parent | b72571ca49dd16be174f02ed14b460c136c9aaf2 (diff) | |
| download | emacs-53dfd85a7f971875e716a55f010ee508bce89eed.tar.gz emacs-53dfd85a7f971875e716a55f010ee508bce89eed.zip | |
Edebug: Disable backtracking when hitting a &define keyword.
Edebug doesn't deal well with backtracking out of definitions, see
Bug#41988. Rather than trying to support this rare situation (e.g. by
implementing a multipass parser), prevent it by adding an implicit
gate.
* lisp/emacs-lisp/edebug.el (edebug--match-&-spec-op): Disable
backtracking when hitting a &define keyword.
* test/lisp/emacs-lisp/edebug-tests.el
(edebug-tests-duplicate-&define): New unit test.
(edebug-tests--duplicate-&define): New helper macro.
* doc/lispref/edebug.texi (Backtracking): Mention &define in the list
of constructs that disable backtracking.
* etc/NEWS: Document new behavior.
| -rw-r--r-- | doc/lispref/edebug.texi | 10 | ||||
| -rw-r--r-- | etc/NEWS | 3 | ||||
| -rw-r--r-- | lisp/emacs-lisp/edebug.el | 18 | ||||
| -rw-r--r-- | test/lisp/emacs-lisp/edebug-tests.el | 25 |
4 files changed, 43 insertions, 13 deletions
diff --git a/doc/lispref/edebug.texi b/doc/lispref/edebug.texi index 8942f55affb..323130f2378 100644 --- a/doc/lispref/edebug.texi +++ b/doc/lispref/edebug.texi | |||
| @@ -1510,11 +1510,11 @@ form specifications (that is, @code{form}, @code{body}, @code{def-form}, and | |||
| 1510 | must be in the form itself rather than at a higher level. | 1510 | must be in the form itself rather than at a higher level. |
| 1511 | 1511 | ||
| 1512 | Backtracking is also disabled after successfully matching a quoted | 1512 | Backtracking is also disabled after successfully matching a quoted |
| 1513 | symbol or string specification, since this usually indicates a | 1513 | symbol, string specification, or @code{&define} keyword, since this |
| 1514 | recognized construct. But if you have a set of alternative constructs that | 1514 | usually indicates a recognized construct. But if you have a set of |
| 1515 | all begin with the same symbol, you can usually work around this | 1515 | alternative constructs that all begin with the same symbol, you can |
| 1516 | constraint by factoring the symbol out of the alternatives, e.g., | 1516 | usually work around this constraint by factoring the symbol out of the |
| 1517 | @code{["foo" &or [first case] [second case] ...]}. | 1517 | alternatives, e.g., @code{["foo" &or [first case] [second case] ...]}. |
| 1518 | 1518 | ||
| 1519 | Most needs are satisfied by these two ways that backtracking is | 1519 | Most needs are satisfied by these two ways that backtracking is |
| 1520 | automatically disabled, but occasionally it is useful to explicitly | 1520 | automatically disabled, but occasionally it is useful to explicitly |
| @@ -2524,6 +2524,9 @@ back in Emacs 23.1. The affected functions are: 'make-obsolete', | |||
| 2524 | 2524 | ||
| 2525 | ** The 'values' variable is now obsolete. | 2525 | ** The 'values' variable is now obsolete. |
| 2526 | 2526 | ||
| 2527 | ** The '&define' keyword in an Edebug specification now disables | ||
| 2528 | backtracking. | ||
| 2529 | |||
| 2527 | 2530 | ||
| 2528 | * Lisp Changes in Emacs 28.1 | 2531 | * Lisp Changes in Emacs 28.1 |
| 2529 | 2532 | ||
diff --git a/lisp/emacs-lisp/edebug.el b/lisp/emacs-lisp/edebug.el index f1455ffe73b..365bc748741 100644 --- a/lisp/emacs-lisp/edebug.el +++ b/lisp/emacs-lisp/edebug.el | |||
| @@ -1942,14 +1942,16 @@ a sequence of elements." | |||
| 1942 | ;; Normally, &define is interpreted specially other places. | 1942 | ;; Normally, &define is interpreted specially other places. |
| 1943 | ;; This should only be called inside of a spec list to match the remainder | 1943 | ;; This should only be called inside of a spec list to match the remainder |
| 1944 | ;; of the current list. e.g. ("lambda" &define args def-body) | 1944 | ;; of the current list. e.g. ("lambda" &define args def-body) |
| 1945 | (edebug-make-form-wrapper | 1945 | (prog1 (edebug-make-form-wrapper |
| 1946 | cursor | 1946 | cursor |
| 1947 | (edebug-before-offset cursor) | 1947 | (edebug-before-offset cursor) |
| 1948 | ;; Find the last offset in the list. | 1948 | ;; Find the last offset in the list. |
| 1949 | (let ((offsets (edebug-cursor-offsets cursor))) | 1949 | (let ((offsets (edebug-cursor-offsets cursor))) |
| 1950 | (while (consp offsets) (setq offsets (cdr offsets))) | 1950 | (while (consp offsets) (setq offsets (cdr offsets))) |
| 1951 | offsets) | 1951 | offsets) |
| 1952 | specs)) | 1952 | specs) |
| 1953 | ;; Stop backtracking here (Bug#41988). | ||
| 1954 | (setq edebug-gate t))) | ||
| 1953 | 1955 | ||
| 1954 | (cl-defmethod edebug--match-&-spec-op ((_ (eql &name)) cursor specs) | 1956 | (cl-defmethod edebug--match-&-spec-op ((_ (eql &name)) cursor specs) |
| 1955 | "Compute the name for `&name SPEC FUN` spec operator. | 1957 | "Compute the name for `&name SPEC FUN` spec operator. |
diff --git a/test/lisp/emacs-lisp/edebug-tests.el b/test/lisp/emacs-lisp/edebug-tests.el index dcb261c2eb9..7d45432e57e 100644 --- a/test/lisp/emacs-lisp/edebug-tests.el +++ b/test/lisp/emacs-lisp/edebug-tests.el | |||
| @@ -1061,5 +1061,30 @@ backtracking (Bug#42701)." | |||
| 1061 | "edebug-anon10001" | 1061 | "edebug-anon10001" |
| 1062 | "edebug-tests-duplicate-symbol-backtrack")))))) | 1062 | "edebug-tests-duplicate-symbol-backtrack")))))) |
| 1063 | 1063 | ||
| 1064 | (defmacro edebug-tests--duplicate-&define (_arg) | ||
| 1065 | "Helper macro for the ERT test `edebug-tests-duplicate-&define'. | ||
| 1066 | The Edebug specification is similar to the one used by `cl-flet' | ||
| 1067 | previously; see Bug#41988." | ||
| 1068 | (declare (debug (&or (&define name function-form) (defun))))) | ||
| 1069 | |||
| 1070 | (ert-deftest edebug-tests-duplicate-&define () | ||
| 1071 | "Check that Edebug doesn't backtrack out of `&define' forms. | ||
| 1072 | This avoids potential duplicate definitions (Bug#41988)." | ||
| 1073 | (with-temp-buffer | ||
| 1074 | (print '(defun edebug-tests-duplicate-&define () | ||
| 1075 | (edebug-tests--duplicate-&define | ||
| 1076 | (edebug-tests-duplicate-&define-inner () nil))) | ||
| 1077 | (current-buffer)) | ||
| 1078 | (let* ((edebug-all-defs t) | ||
| 1079 | (edebug-initial-mode 'Go-nonstop) | ||
| 1080 | (instrumented-names ()) | ||
| 1081 | (edebug-new-definition-function | ||
| 1082 | (lambda (name) | ||
| 1083 | (when (memq name instrumented-names) | ||
| 1084 | (error "Duplicate definition of `%s'" name)) | ||
| 1085 | (push name instrumented-names) | ||
| 1086 | (edebug-new-definition name)))) | ||
| 1087 | (should-error (eval-buffer) :type 'invalid-read-syntax)))) | ||
| 1088 | |||
| 1064 | (provide 'edebug-tests) | 1089 | (provide 'edebug-tests) |
| 1065 | ;;; edebug-tests.el ends here | 1090 | ;;; edebug-tests.el ends here |