aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGemini Lasswell2017-05-13 11:35:49 -0700
committerDmitry Gutov2017-05-14 23:32:27 +0300
commite6f64df9c2b443d3385c2c25c29ccd5283d37e3f (patch)
tree487563d05e1133b4dc7147ed48b87c69e6fd4ff7
parent10037e4be2358597125a05db93f6fee551131d83 (diff)
downloademacs-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.el4
-rw-r--r--lisp/emacs-lisp/edebug.el53
-rw-r--r--lisp/emacs-lisp/eieio-compat.el4
-rw-r--r--lisp/subr.el19
-rw-r--r--test/lisp/subr-tests.el24
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 (&not . edebug-match-&not) 1611 (&not . edebug-match-&not)
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. 3201Return the list of function symbols which were instrumented.
3202This may be simply (FUNC) for a normal function, or a list of
3203generated symbols for methods. If a function or method to
3204instrument 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.
3213Do this when stopped before the form or it will be too late. 3241Do this when stopped before the form or it will be too late.
3214One side effect of using this command is that the next time the 3242One side effect of using this command is that the next time the
3215function or macro is called, Edebug will be called there as well." 3243function or macro is called, Edebug will be called there as well.
3244If the callee is a generic function, Edebug will instrument all
3245the methods, not just the one which is about to be called. Return
3246the 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.
3231This first does `edebug-instrument-callee' to ensure that it is 3262This first does `edebug-instrument-callee' to ensure that it is
3232instrumented. Then it does `edebug-on-entry' and switches to `go' mode." 3263instrumented. 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'.
2031The list will have entries of the form (FILE . (METHOD ...))
2032where (METHOD ...) contains the qualifiers and specializers of
2033the 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.
2031LIBRARY should be a relative file name of the library, a string. 2050LIBRARY 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