diff options
| author | Gemini Lasswell | 2017-05-13 11:35:49 -0700 |
|---|---|---|
| committer | Dmitry Gutov | 2017-05-14 23:32:27 +0300 |
| commit | e6f64df9c2b443d3385c2c25c29ccd5283d37e3f (patch) | |
| tree | 487563d05e1133b4dc7147ed48b87c69e6fd4ff7 | |
| parent | 10037e4be2358597125a05db93f6fee551131d83 (diff) | |
| download | emacs-e6f64df9c2b443d3385c2c25c29ccd5283d37e3f.tar.gz emacs-e6f64df9c2b443d3385c2c25c29ccd5283d37e3f.zip | |
Make edebug-step-in work on generic methods (Bug#22294)
* lisp/emacs-lisp/edebug.el (edebug-match-cl-generic-method-args):
New function to implement the edebug-form-spec property of
the symbol cl-generic-method-args.
(edebug-instrument-function): If the function is a generic
function, find and instrument all of its methods. Return a list
instead of a single symbol.
(edebug-instrument-callee): Now returns a list. Update docstring.
(edebug-step-in): Handle the list returned by edebug-instrument-callee.
* lisp/emacs-lisp/cl-generic.el (cl-defmethod): Use name and
cl-generic-method-args in its Edebug spec.
* lisp/emacs-lisp/eieio-compat.el (defmethod): Use name and
cl-generic-method-args in its Edebug spec.
* lisp/subr.el (method-files): New function.
* test/lisp/subr-tests.el (subr-tests--method-files--finds-methods)
(subr-tests--method-files--nonexistent-methods): New tests.
| -rw-r--r-- | lisp/emacs-lisp/cl-generic.el | 4 | ||||
| -rw-r--r-- | lisp/emacs-lisp/edebug.el | 53 | ||||
| -rw-r--r-- | lisp/emacs-lisp/eieio-compat.el | 4 | ||||
| -rw-r--r-- | lisp/subr.el | 19 | ||||
| -rw-r--r-- | test/lisp/subr-tests.el | 24 |
5 files changed, 89 insertions, 15 deletions
diff --git a/lisp/emacs-lisp/cl-generic.el b/lisp/emacs-lisp/cl-generic.el index 068f4fb0c84..c64376b940f 100644 --- a/lisp/emacs-lisp/cl-generic.el +++ b/lisp/emacs-lisp/cl-generic.el | |||
| @@ -413,12 +413,12 @@ The set of acceptable TYPEs (also called \"specializers\") is defined | |||
| 413 | (declare (doc-string 3) (indent 2) | 413 | (declare (doc-string 3) (indent 2) |
| 414 | (debug | 414 | (debug |
| 415 | (&define ; this means we are defining something | 415 | (&define ; this means we are defining something |
| 416 | [&or symbolp ("setf" symbolp)] | 416 | [&or name ("setf" name :name setf)] |
| 417 | ;; ^^ This is the methods symbol | 417 | ;; ^^ This is the methods symbol |
| 418 | [ &rest atom ] ; Multiple qualifiers are allowed. | 418 | [ &rest atom ] ; Multiple qualifiers are allowed. |
| 419 | ; Like in CLOS spec, we support | 419 | ; Like in CLOS spec, we support |
| 420 | ; any non-list values. | 420 | ; any non-list values. |
| 421 | listp ; arguments | 421 | cl-generic-method-args ; arguments |
| 422 | [ &optional stringp ] ; documentation string | 422 | [ &optional stringp ] ; documentation string |
| 423 | def-body))) ; part to be debugged | 423 | def-body))) ; part to be debugged |
| 424 | (let ((qualifiers nil)) | 424 | (let ((qualifiers nil)) |
diff --git a/lisp/emacs-lisp/edebug.el b/lisp/emacs-lisp/edebug.el index 4116e31d0a9..65e30f86778 100644 --- a/lisp/emacs-lisp/edebug.el +++ b/lisp/emacs-lisp/edebug.el | |||
| @@ -1607,6 +1607,7 @@ expressions; a `progn' form will be returned enclosing these forms." | |||
| 1607 | ;; Less frequently used: | 1607 | ;; Less frequently used: |
| 1608 | ;; (function . edebug-match-function) | 1608 | ;; (function . edebug-match-function) |
| 1609 | (lambda-expr . edebug-match-lambda-expr) | 1609 | (lambda-expr . edebug-match-lambda-expr) |
| 1610 | (cl-generic-method-args . edebug-match-cl-generic-method-args) | ||
| 1610 | (¬ . edebug-match-¬) | 1611 | (¬ . edebug-match-¬) |
| 1611 | (&key . edebug-match-&key) | 1612 | (&key . edebug-match-&key) |
| 1612 | (place . edebug-match-place) | 1613 | (place . edebug-match-place) |
| @@ -1900,6 +1901,16 @@ expressions; a `progn' form will be returned enclosing these forms." | |||
| 1900 | spec)) | 1901 | spec)) |
| 1901 | nil) | 1902 | nil) |
| 1902 | 1903 | ||
| 1904 | (defun edebug-match-cl-generic-method-args (cursor) | ||
| 1905 | (let ((args (edebug-top-element-required cursor "Expected arguments"))) | ||
| 1906 | (if (not (consp args)) | ||
| 1907 | (edebug-no-match cursor "List expected")) | ||
| 1908 | ;; Append the arguments to edebug-def-name. | ||
| 1909 | (setq edebug-def-name | ||
| 1910 | (intern (format "%s %s" edebug-def-name args))) | ||
| 1911 | (edebug-move-cursor cursor) | ||
| 1912 | (list args))) | ||
| 1913 | |||
| 1903 | (defun edebug-match-arg (cursor) | 1914 | (defun edebug-match-arg (cursor) |
| 1904 | ;; set the def-args bound in edebug-defining-form | 1915 | ;; set the def-args bound in edebug-defining-form |
| 1905 | (let ((edebug-arg (edebug-top-element-required cursor "Expected arg"))) | 1916 | (let ((edebug-arg (edebug-top-element-required cursor "Expected arg"))) |
| @@ -3186,8 +3197,11 @@ go to the end of the last sexp, or if that is the same point, then step." | |||
| 3186 | ))))) | 3197 | ))))) |
| 3187 | 3198 | ||
| 3188 | (defun edebug-instrument-function (func) | 3199 | (defun edebug-instrument-function (func) |
| 3189 | ;; Func should be a function symbol. | 3200 | "Instrument the function or generic method FUNC. |
| 3190 | ;; Return the function symbol, or nil if not instrumented. | 3201 | Return the list of function symbols which were instrumented. |
| 3202 | This may be simply (FUNC) for a normal function, or a list of | ||
| 3203 | generated symbols for methods. If a function or method to | ||
| 3204 | instrument cannot be found, signal an error." | ||
| 3191 | (let ((func-marker (get func 'edebug))) | 3205 | (let ((func-marker (get func 'edebug))) |
| 3192 | (cond | 3206 | (cond |
| 3193 | ((and (markerp func-marker) (marker-buffer func-marker)) | 3207 | ((and (markerp func-marker) (marker-buffer func-marker)) |
| @@ -3195,10 +3209,24 @@ go to the end of the last sexp, or if that is the same point, then step." | |||
| 3195 | (with-current-buffer (marker-buffer func-marker) | 3209 | (with-current-buffer (marker-buffer func-marker) |
| 3196 | (goto-char func-marker) | 3210 | (goto-char func-marker) |
| 3197 | (edebug-eval-top-level-form) | 3211 | (edebug-eval-top-level-form) |
| 3198 | func)) | 3212 | (list func))) |
| 3199 | ((consp func-marker) | 3213 | ((consp func-marker) |
| 3200 | (message "%s is already instrumented." func) | 3214 | (message "%s is already instrumented." func) |
| 3201 | func) | 3215 | (list func)) |
| 3216 | ((get func 'cl--generic) | ||
| 3217 | (let ((method-defs (method-files func)) | ||
| 3218 | symbols) | ||
| 3219 | (unless method-defs | ||
| 3220 | (error "Could not find any method definitions for %s" func)) | ||
| 3221 | (pcase-dolist (`(,file . ,spec) method-defs) | ||
| 3222 | (let* ((loc (find-function-search-for-symbol spec 'cl-defmethod file))) | ||
| 3223 | (unless (cdr loc) | ||
| 3224 | (error "Could not find the definition for %s in its file" spec)) | ||
| 3225 | (with-current-buffer (car loc) | ||
| 3226 | (goto-char (cdr loc)) | ||
| 3227 | (edebug-eval-top-level-form) | ||
| 3228 | (push (edebug-form-data-symbol) symbols)))) | ||
| 3229 | symbols)) | ||
| 3202 | (t | 3230 | (t |
| 3203 | (let ((loc (find-function-noselect func t))) | 3231 | (let ((loc (find-function-noselect func t))) |
| 3204 | (unless (cdr loc) | 3232 | (unless (cdr loc) |
| @@ -3206,13 +3234,16 @@ go to the end of the last sexp, or if that is the same point, then step." | |||
| 3206 | (with-current-buffer (car loc) | 3234 | (with-current-buffer (car loc) |
| 3207 | (goto-char (cdr loc)) | 3235 | (goto-char (cdr loc)) |
| 3208 | (edebug-eval-top-level-form) | 3236 | (edebug-eval-top-level-form) |
| 3209 | func)))))) | 3237 | (list func))))))) |
| 3210 | 3238 | ||
| 3211 | (defun edebug-instrument-callee () | 3239 | (defun edebug-instrument-callee () |
| 3212 | "Instrument the definition of the function or macro about to be called. | 3240 | "Instrument the definition of the function or macro about to be called. |
| 3213 | Do this when stopped before the form or it will be too late. | 3241 | Do this when stopped before the form or it will be too late. |
| 3214 | One side effect of using this command is that the next time the | 3242 | One side effect of using this command is that the next time the |
| 3215 | function or macro is called, Edebug will be called there as well." | 3243 | function or macro is called, Edebug will be called there as well. |
| 3244 | If the callee is a generic function, Edebug will instrument all | ||
| 3245 | the methods, not just the one which is about to be called. Return | ||
| 3246 | the list of symbols which were instrumented." | ||
| 3216 | (interactive) | 3247 | (interactive) |
| 3217 | (if (not (looking-at "(")) | 3248 | (if (not (looking-at "(")) |
| 3218 | (error "You must be before a list form") | 3249 | (error "You must be before a list form") |
| @@ -3227,15 +3258,15 @@ function or macro is called, Edebug will be called there as well." | |||
| 3227 | 3258 | ||
| 3228 | 3259 | ||
| 3229 | (defun edebug-step-in () | 3260 | (defun edebug-step-in () |
| 3230 | "Step into the definition of the function or macro about to be called. | 3261 | "Step into the definition of the function, macro or method about to be called. |
| 3231 | This first does `edebug-instrument-callee' to ensure that it is | 3262 | This first does `edebug-instrument-callee' to ensure that it is |
| 3232 | instrumented. Then it does `edebug-on-entry' and switches to `go' mode." | 3263 | instrumented. Then it does `edebug-on-entry' and switches to `go' mode." |
| 3233 | (interactive) | 3264 | (interactive) |
| 3234 | (let ((func (edebug-instrument-callee))) | 3265 | (let ((funcs (edebug-instrument-callee))) |
| 3235 | (if func | 3266 | (if funcs |
| 3236 | (progn | 3267 | (progn |
| 3237 | (edebug-on-entry func 'temp) | 3268 | (mapc (lambda (func) (edebug-on-entry func 'temp)) funcs) |
| 3238 | (edebug-go-mode nil))))) | 3269 | (edebug-go-mode nil))))) |
| 3239 | 3270 | ||
| 3240 | (defun edebug-on-entry (function &optional flag) | 3271 | (defun edebug-on-entry (function &optional flag) |
| 3241 | "Cause Edebug to stop when FUNCTION is called. | 3272 | "Cause Edebug to stop when FUNCTION is called. |
diff --git a/lisp/emacs-lisp/eieio-compat.el b/lisp/emacs-lisp/eieio-compat.el index fe65ae02623..e6e6d118709 100644 --- a/lisp/emacs-lisp/eieio-compat.el +++ b/lisp/emacs-lisp/eieio-compat.el | |||
| @@ -105,10 +105,10 @@ Summary: | |||
| 105 | (declare (doc-string 3) (obsolete cl-defmethod "25.1") | 105 | (declare (doc-string 3) (obsolete cl-defmethod "25.1") |
| 106 | (debug | 106 | (debug |
| 107 | (&define ; this means we are defining something | 107 | (&define ; this means we are defining something |
| 108 | [&or symbolp ("setf" symbolp)] | 108 | [&or name ("setf" name :name setf)] |
| 109 | ;; ^^ This is the methods symbol | 109 | ;; ^^ This is the methods symbol |
| 110 | [ &optional symbolp ] ; this is key :before etc | 110 | [ &optional symbolp ] ; this is key :before etc |
| 111 | listp ; arguments | 111 | cl-generic-method-args ; arguments |
| 112 | [ &optional stringp ] ; documentation string | 112 | [ &optional stringp ] ; documentation string |
| 113 | def-body ; part to be debugged | 113 | def-body ; part to be debugged |
| 114 | ))) | 114 | ))) |
diff --git a/lisp/subr.el b/lisp/subr.el index 02e79932233..8d5d2a779c6 100644 --- a/lisp/subr.el +++ b/lisp/subr.el | |||
| @@ -2026,6 +2026,25 @@ definition, variable definition, or face definition only." | |||
| 2026 | (setq files (cdr files))) | 2026 | (setq files (cdr files))) |
| 2027 | file))) | 2027 | file))) |
| 2028 | 2028 | ||
| 2029 | (defun method-files (method) | ||
| 2030 | "Return a list of files where METHOD is defined by `cl-defmethod'. | ||
| 2031 | The list will have entries of the form (FILE . (METHOD ...)) | ||
| 2032 | where (METHOD ...) contains the qualifiers and specializers of | ||
| 2033 | the method and is a suitable argument for | ||
| 2034 | `find-function-search-for-symbol'. Filenames are absolute." | ||
| 2035 | (let ((files load-history) | ||
| 2036 | result) | ||
| 2037 | (while files | ||
| 2038 | (let ((defs (cdr (car files)))) | ||
| 2039 | (while defs | ||
| 2040 | (let ((def (car defs))) | ||
| 2041 | (if (and (eq (car-safe def) 'cl-defmethod) | ||
| 2042 | (eq (cadr def) method)) | ||
| 2043 | (push (cons (car (car files)) (cdr def)) result))) | ||
| 2044 | (setq defs (cdr defs)))) | ||
| 2045 | (setq files (cdr files))) | ||
| 2046 | result)) | ||
| 2047 | |||
| 2029 | (defun locate-library (library &optional nosuffix path interactive-call) | 2048 | (defun locate-library (library &optional nosuffix path interactive-call) |
| 2030 | "Show the precise file name of Emacs library LIBRARY. | 2049 | "Show the precise file name of Emacs library LIBRARY. |
| 2031 | LIBRARY should be a relative file name of the library, a string. | 2050 | LIBRARY should be a relative file name of the library, a string. |
diff --git a/test/lisp/subr-tests.el b/test/lisp/subr-tests.el index 0d243cc5d8c..8fa258d12ed 100644 --- a/test/lisp/subr-tests.el +++ b/test/lisp/subr-tests.el | |||
| @@ -291,5 +291,29 @@ cf. Bug#25477." | |||
| 291 | (should-error (eval '(dolist "foo") t) | 291 | (should-error (eval '(dolist "foo") t) |
| 292 | :type 'wrong-type-argument)) | 292 | :type 'wrong-type-argument)) |
| 293 | 293 | ||
| 294 | (require 'cl-generic) | ||
| 295 | (cl-defgeneric subr-tests--generic (x)) | ||
| 296 | (cl-defmethod subr-tests--generic ((x string)) | ||
| 297 | (message "%s is a string" x)) | ||
| 298 | (cl-defmethod subr-tests--generic ((x integer)) | ||
| 299 | (message "%s is a number" x)) | ||
| 300 | (cl-defgeneric subr-tests--generic-without-methods (x y)) | ||
| 301 | (defvar subr-tests--this-file (or load-file-name buffer-file-name)) | ||
| 302 | |||
| 303 | (ert-deftest subr-tests--method-files--finds-methods () | ||
| 304 | "`method-files' returns a list of files and methods for a generic function." | ||
| 305 | (let ((retval (method-files 'subr-tests--generic))) | ||
| 306 | (should (equal (length retval) 2)) | ||
| 307 | (mapc (lambda (x) | ||
| 308 | (should (equal (car x) subr-tests--this-file)) | ||
| 309 | (should (equal (cadr x) 'subr-tests--generic))) | ||
| 310 | retval) | ||
| 311 | (should-not (equal (nth 0 retval) (nth 1 retval))))) | ||
| 312 | |||
| 313 | (ert-deftest subr-tests--method-files--nonexistent-methods () | ||
| 314 | "`method-files' returns nil if asked to find a method which doesn't exist." | ||
| 315 | (should-not (method-files 'subr-tests--undefined-generic)) | ||
| 316 | (should-not (method-files 'subr-tests--generic-without-methods))) | ||
| 317 | |||
| 294 | (provide 'subr-tests) | 318 | (provide 'subr-tests) |
| 295 | ;;; subr-tests.el ends here | 319 | ;;; subr-tests.el ends here |