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.
This commit is contained in:
Gemini Lasswell 2017-05-13 11:35:49 -07:00 committed by Dmitry Gutov
parent 10037e4be2
commit e6f64df9c2
5 changed files with 89 additions and 15 deletions

View file

@ -1607,6 +1607,7 @@ expressions; a `progn' form will be returned enclosing these forms."
;; Less frequently used:
;; (function . edebug-match-function)
(lambda-expr . edebug-match-lambda-expr)
(cl-generic-method-args . edebug-match-cl-generic-method-args)
(&not . edebug-match-&not)
(&key . edebug-match-&key)
(place . edebug-match-place)
@ -1900,6 +1901,16 @@ expressions; a `progn' form will be returned enclosing these forms."
spec))
nil)
(defun edebug-match-cl-generic-method-args (cursor)
(let ((args (edebug-top-element-required cursor "Expected arguments")))
(if (not (consp args))
(edebug-no-match cursor "List expected"))
;; Append the arguments to edebug-def-name.
(setq edebug-def-name
(intern (format "%s %s" edebug-def-name args)))
(edebug-move-cursor cursor)
(list args)))
(defun edebug-match-arg (cursor)
;; set the def-args bound in edebug-defining-form
(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."
)))))
(defun edebug-instrument-function (func)
;; Func should be a function symbol.
;; Return the function symbol, or nil if not instrumented.
"Instrument the function or generic method FUNC.
Return the list of function symbols which were instrumented.
This may be simply (FUNC) for a normal function, or a list of
generated symbols for methods. If a function or method to
instrument cannot be found, signal an error."
(let ((func-marker (get func 'edebug)))
(cond
((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."
(with-current-buffer (marker-buffer func-marker)
(goto-char func-marker)
(edebug-eval-top-level-form)
func))
(list func)))
((consp func-marker)
(message "%s is already instrumented." func)
func)
(list func))
((get func 'cl--generic)
(let ((method-defs (method-files func))
symbols)
(unless method-defs
(error "Could not find any method definitions for %s" func))
(pcase-dolist (`(,file . ,spec) method-defs)
(let* ((loc (find-function-search-for-symbol spec 'cl-defmethod file)))
(unless (cdr loc)
(error "Could not find the definition for %s in its file" spec))
(with-current-buffer (car loc)
(goto-char (cdr loc))
(edebug-eval-top-level-form)
(push (edebug-form-data-symbol) symbols))))
symbols))
(t
(let ((loc (find-function-noselect func t)))
(unless (cdr loc)
@ -3206,13 +3234,16 @@ go to the end of the last sexp, or if that is the same point, then step."
(with-current-buffer (car loc)
(goto-char (cdr loc))
(edebug-eval-top-level-form)
func))))))
(list func)))))))
(defun edebug-instrument-callee ()
"Instrument the definition of the function or macro about to be called.
Do this when stopped before the form or it will be too late.
One side effect of using this command is that the next time the
function or macro is called, Edebug will be called there as well."
function or macro is called, Edebug will be called there as well.
If the callee is a generic function, Edebug will instrument all
the methods, not just the one which is about to be called. Return
the list of symbols which were instrumented."
(interactive)
(if (not (looking-at "("))
(error "You must be before a list form")
@ -3227,15 +3258,15 @@ function or macro is called, Edebug will be called there as well."
(defun edebug-step-in ()
"Step into the definition of the function or macro about to be called.
"Step into the definition of the function, macro or method about to be called.
This first does `edebug-instrument-callee' to ensure that it is
instrumented. Then it does `edebug-on-entry' and switches to `go' mode."
(interactive)
(let ((func (edebug-instrument-callee)))
(if func
(let ((funcs (edebug-instrument-callee)))
(if funcs
(progn
(edebug-on-entry func 'temp)
(edebug-go-mode nil)))))
(mapc (lambda (func) (edebug-on-entry func 'temp)) funcs)
(edebug-go-mode nil)))))
(defun edebug-on-entry (function &optional flag)
"Cause Edebug to stop when FUNCTION is called.