Mercurial > emacs
changeset 56458:71315b2afe5b
Added some additional functions to the `1-valued', `compose', and progn groups.
Bugfix for marking up the definition for an empty function.
New category "potentially-1valued" for functions that are not erroneous if either 1-valued or multi-valued.
author | Jonathan Yavner <jyavner@member.fsf.org> |
---|---|
date | Sat, 17 Jul 2004 17:01:28 +0000 |
parents | 75852f9fc8d7 |
children | 718cf6b0289c |
files | lisp/emacs-lisp/testcover.el |
diffstat | 1 files changed, 150 insertions(+), 73 deletions(-) [+] |
line wrap: on
line diff
--- a/lisp/emacs-lisp/testcover.el Sat Jul 17 15:00:22 2004 +0000 +++ b/lisp/emacs-lisp/testcover.el Sat Jul 17 17:01:28 2004 +0000 @@ -38,9 +38,9 @@ ;; instrumentation callbacks, then replace edebug's callbacks with ours. ;; * To show good coverage, we want to see two values for every form, except ;; functions that always return the same value and `defconst' variables -;; need show only value for good coverage. To avoid the brown splotch, the -;; definitions for constants and 1-valued functions must precede the -;; references. +;; need show only one value for good coverage. To avoid the brown +;; splotch, the definitions for constants and 1-valued functions must +;; precede the references. ;; * Use the macro `1value' in your Lisp code to mark spots where the local ;; code environment causes a function or variable to always have the same ;; value, but the function or variable is not intrinsically 1-valued. @@ -55,12 +55,14 @@ ;; call has the same value! Also, equal thinks two strings are the same ;; if they differ only in properties. ;; * Because we have only a "1value" class and no "always nil" class, we have -;; to treat as 1-valued any `and' whose last term is 1-valued, in case the -;; last term is always nil. Example: +;; to treat as potentially 1-valued any `and' whose last term is 1-valued, +;; in case the last term is always nil. Example: ;; (and (< (point) 1000) (forward-char 10)) -;; This form always returns nil. Similarly, `if' and `cond' are -;; treated as 1-valued if all clauses are, in case those values are -;; always nil. +;; This form always returns nil. Similarly, `or', `if', and `cond' are +;; treated as potentially 1-valued if all clauses are, in case those +;; values are always nil. Unlike truly 1-valued functions, it is not an +;; error if these "potentially" 1-valued forms actually return differing +;; values. (require 'edebug) (provide 'testcover) @@ -86,12 +88,14 @@ (defcustom testcover-1value-functions '(backward-char barf-if-buffer-read-only beginning-of-line - buffer-disable-undo buffer-enable-undo current-global-map deactivate-mark - delete-char delete-region ding error forward-char function* insert - insert-and-inherit kill-all-local-variables lambda mapc narrow-to-region - noreturn push-mark put-text-property run-hooks set-text-properties signal - substitute-key-definition suppress-keymap throw undo use-local-map while - widen yank) + buffer-disable-undo buffer-enable-undo current-global-map + deactivate-mark delete-backward-char delete-char delete-region ding + forward-char function* insert insert-and-inherit kill-all-local-variables + kill-line kill-paragraph kill-region kill-sexp lambda + minibuffer-complete-and-exit narrow-to-region next-line push-mark + put-text-property run-hooks set-match-data signal + substitute-key-definition suppress-keymap undo use-local-map while widen + yank) "Functions that always return the same value. No brown splotch is shown for these. This list is quite incomplete! Notes: Nobody ever changes the current global map. The macro `lambda' is self-evaluating, hence always @@ -108,9 +112,9 @@ :type 'hook) (defcustom testcover-compose-functions - '(+ - * / length list make-keymap make-sparse-keymap message propertize - replace-regexp-in-string run-with-idle-timer - set-buffer-modified-p) + '(+ - * / = append length list make-keymap make-sparse-keymap + mapcar message propertize replace-regexp-in-string + run-with-idle-timer set-buffer-modified-p) "Functions that are 1-valued if all their args are either constants or calls to one of the `testcover-1value-functions', so if that's true then no brown splotch is shown for these. This list is quite incomplete! Most @@ -119,16 +123,16 @@ :type 'hook) (defcustom testcover-progn-functions - '(define-key fset function goto-char or overlay-put progn save-current-buffer - save-excursion save-match-data save-restriction save-selected-window - save-window-excursion set set-default setq setq-default - with-output-to-temp-buffer with-syntax-table with-temp-buffer - with-temp-file with-temp-message with-timeout) + '(define-key fset function goto-char mapc overlay-put progn + save-current-buffer save-excursion save-match-data + save-restriction save-selected-window save-window-excursion + set set-default set-marker-insertion-type setq setq-default + with-current-buffer with-output-to-temp-buffer with-syntax-table + with-temp-buffer with-temp-file with-temp-message with-timeout) "Functions whose return value is the same as their last argument. No brown splotch is shown for these if the last argument is a constant or a call to one of the `testcover-1value-functions'. This list is probably -incomplete! Note: `or' is here in case the last argument is a function that -always returns nil." +incomplete!" :group 'testcover :type 'hook) @@ -140,6 +144,11 @@ :group 'testcover :type 'hook) +(defcustom testcover-potentially-1value-functions + '(add-hook and beep or remove-hook unless when) + "Functions that are potentially 1-valued. No brown splotch if actually +1-valued, no error if actually multi-valued.") + (defface testcover-nohits-face '((t (:background "DeepPink2"))) "Face for forms that had no hits during coverage test" @@ -161,7 +170,11 @@ (defvar testcover-module-1value-functions nil "Symbols declared with defun in the last file processed by -`testcover-start', whose functions always return the same value.") +`testcover-start', whose functions should always return the same value.") + +(defvar testcover-module-potentially-1value-functions nil + "Symbols declared with defun in the last file processed by +`testcover-start', whose functions might always return the same value.") (defvar testcover-vector nil "Locally bound to coverage vector for function in progress.") @@ -206,25 +219,32 @@ x)) (defun testcover-reinstrument (form) - "Reinstruments FORM to use testcover instead of edebug. This function -modifies the list that FORM points to. Result is non-nil if FORM will -always return the same value." + "Reinstruments FORM to use testcover instead of edebug. This +function modifies the list that FORM points to. Result is nil if +FORM should return multiple vlues, t if should always return same +value, 'maybe if either is acceptable." (let ((fun (car-safe form)) - id) + id val) (cond - ((not fun) ;Atom - (or (not (symbolp form)) - (memq form testcover-constants) - (memq form testcover-module-constants))) - ((consp fun) ;Embedded list + ((not fun) ;Atom + (when (or (not (symbolp form)) + (memq form testcover-constants) + (memq form testcover-module-constants)) + t)) + ((consp fun) ;Embedded list (testcover-reinstrument fun) (testcover-reinstrument-list (cdr form)) nil) ((or (memq fun testcover-1value-functions) (memq fun testcover-module-1value-functions)) - ;;Always return same value + ;;Should always return same value (testcover-reinstrument-list (cdr form)) t) + ((or (memq fun testcover-potentially-1value-functions) + (memq fun testcover-module-potentially-1value-functions)) + ;;Might always return same value + (testcover-reinstrument-list (cdr form)) + 'maybe) ((memq fun testcover-progn-functions) ;;1-valued if last argument is (testcover-reinstrument-list (cdr form))) @@ -233,11 +253,9 @@ (testcover-reinstrument-list (cddr form)) (testcover-reinstrument (cadr form))) ((memq fun testcover-compose-functions) - ;;1-valued if all arguments are - (setq id t) - (mapc #'(lambda (x) (setq id (or (testcover-reinstrument x) id))) - (cdr form)) - id) + ;;1-valued if all arguments are. Potentially 1-valued if all + ;;arguments are either definitely or potentially. + (testcover-reinstrument-compose (cdr form) 'testcover-reinstrument)) ((eq fun 'edebug-enter) ;;(edebug-enter 'SYM ARGS #'(lambda nil FORMS)) ;; => (testcover-enter 'SYM #'(lambda nil FORMS)) @@ -252,33 +270,44 @@ (aset testcover-vector (cadr (cadr form)) 'ok-coverage)) (setq id (nth 2 form)) (setcdr form (nthcdr 2 form)) + (setq val (testcover-reinstrument (nth 2 form))) + (if (eq val t) + (setcar form 'testcover-1value) + (setcar form 'testcover-after)) + (when val + ;;1-valued or potentially 1-valued + (aset testcover-vector id '1value)) (cond ((memq (car-safe (nth 2 form)) testcover-noreturn-functions) ;;This function won't return, so set the value in advance ;;(edebug-after (edebug-before XXX) YYY FORM) ;; => (progn (edebug-after YYY nil) FORM) + (setcar (cdr form) `(,(car form) ,id nil)) (setcar form 'progn) - (setcar (cdr form) `(testcover-after ,id nil))) + (aset testcover-vector id '1value) + (setq val t)) ((eq (car-safe (nth 2 form)) '1value) ;;This function is always supposed to return the same value - (setcar form 'testcover-1value)) - (t - (setcar form 'testcover-after))) - (when (testcover-reinstrument (nth 2 form)) - (aset testcover-vector id '1value))) + (setq val t) + (aset testcover-vector id '1value) + (setcar form 'testcover-1value))) + val) ((eq fun 'defun) - (if (testcover-reinstrument-list (nthcdr 3 form)) - (push (cadr form) testcover-module-1value-functions))) - ((eq fun 'defconst) + (setq val (testcover-reinstrument-list (nthcdr 3 form))) + (when (eq val t) + (push (cadr form) testcover-module-1value-functions)) + (when (eq val 'maybe) + (push (cadr form) testcover-module-potentially-1value-functions))) + ((memq fun '(defconst defcustom)) ;;Define this symbol as 1-valued (push (cadr form) testcover-module-constants) (testcover-reinstrument-list (cddr form))) ((memq fun '(dotimes dolist)) ;;Always returns third value from SPEC (testcover-reinstrument-list (cddr form)) - (setq fun (testcover-reinstrument-list (cadr form))) + (setq val (testcover-reinstrument-list (cadr form))) (if (nth 2 (cadr form)) - fun + val ;;No third value, always returns nil t)) ((memq fun '(let let*)) @@ -286,23 +315,23 @@ (mapc 'testcover-reinstrument-list (cadr form)) (testcover-reinstrument-list (cddr form))) ((eq fun 'if) - ;;1-valued if both THEN and ELSE clauses are + ;;Potentially 1-valued if both THEN and ELSE clauses are (testcover-reinstrument (cadr form)) (let ((then (testcover-reinstrument (nth 2 form))) (else (testcover-reinstrument-list (nthcdr 3 form)))) - (and then else))) - ((memq fun '(when unless and)) - ;;1-valued if last clause of BODY is - (testcover-reinstrument-list (cdr form))) + (and then else 'maybe))) ((eq fun 'cond) - ;;1-valued if all clauses are - (testcover-reinstrument-clauses (cdr form))) + ;;Potentially 1-valued if all clauses are + (when (testcover-reinstrument-compose (cdr form) + 'testcover-reinstrument-list) + 'maybe)) ((eq fun 'condition-case) - ;;1-valued if BODYFORM is and all HANDLERS are + ;;Potentially 1-valued if BODYFORM is and all HANDLERS are (let ((body (testcover-reinstrument (nth 2 form))) - (errs (testcover-reinstrument-clauses (mapcar #'cdr - (nthcdr 3 form))))) - (and body errs))) + (errs (testcover-reinstrument-compose + (mapcar #'cdr (nthcdr 3 form)) + 'testcover-reinstrument-list))) + (and body errs 'maybe))) ((eq fun 'quote) ;;Don't reinstrument what's inside! ;;This doesn't apply within a backquote @@ -317,16 +346,55 @@ (let ((testcover-1value-functions (remq 'quote testcover-1value-functions))) (testcover-reinstrument (cadr form)))) - ((memq fun '(1value noreturn)) + ((eq fun '1value) ;;Hack - pretend the arg is 1-valued here - (if (symbolp (cadr form)) ;A pseudoconstant variable - t + (cond + ((symbolp (cadr form)) + ;;A pseudoconstant variable + t) + ((and (eq (car (cadr form)) 'edebug-after) + (symbolp (nth 3 (cadr form)))) + ;;Reference to pseudoconstant + (aset testcover-vector (nth 2 (cadr form)) '1value) + (setcar (cdr form) `(testcover-1value ,(nth 2 (cadr form)) + ,(nth 3 (cadr form)))) + t) + (t (if (eq (car (cadr form)) 'edebug-after) (setq id (car (nth 3 (cadr form)))) (setq id (car (cadr form)))) (let ((testcover-1value-functions (cons id testcover-1value-functions))) - (testcover-reinstrument (cadr form))))) + (testcover-reinstrument (cadr form)))))) + ((eq fun 'noreturn) + ;;Hack - pretend the arg has no return + (cond + ((symbolp (cadr form)) + ;;A pseudoconstant variable + 'maybe) + ((and (eq (car (cadr form)) 'edebug-after) + (symbolp (nth 3 (cadr form)))) + ;;Reference to pseudoconstant + (aset testcover-vector (nth 2 (cadr form)) '1value) + (setcar (cdr form) `(progn (testcover-after ,(nth 2 (cadr form)) nil) + ,(nth 3 (cadr form)))) + 'maybe) + (t + (if (eq (car (cadr form)) 'edebug-after) + (setq id (car (nth 3 (cadr form)))) + (setq id (car (cadr form)))) + (let ((testcover-noreturn-functions + (cons id testcover-noreturn-functions))) + (testcover-reinstrument (cadr form)))))) + ((and (eq fun 'apply) + (eq (car-safe (cadr form)) 'quote) + (symbolp (cadr (cadr form)))) + ;;Apply of a constant symbol. Process as 1value or noreturn + ;;depending on symbol. + (setq fun (cons (cadr (cadr form)) (cddr form)) + val (testcover-reinstrument fun)) + (setcdr (cdr form) (cdr fun)) + val) (t ;Some other function or weird thing (testcover-reinstrument-list (cdr form)) nil)))) @@ -341,13 +409,22 @@ (setq result (testcover-reinstrument (pop list)))) result)) -(defun testcover-reinstrument-clauses (clauselist) - "Reinstrument each list in CLAUSELIST. -Result is t if every clause is 1-valued." +(defun testcover-reinstrument-compose (list fun) + "For a compositional function, the result is 1-valued if all +arguments are, potentially 1-valued if all arguments are either +definitely or potentially 1-valued, and multi-valued otherwise. +FUN should be `testcover-reinstrument' for compositional functions, + `testcover-reinstrument-list' for clauses in a `cond'." (let ((result t)) (mapc #'(lambda (x) - (setq result (and (testcover-reinstrument-list x) result))) - clauselist) + (setq x (funcall fun x)) + (cond + ((eq result t) + (setq result x)) + ((eq result 'maybe) + (when (not x) + (setq result nil))))) + list) result)) (defun testcover-end (buffer) @@ -387,7 +464,7 @@ (aset testcover-vector idx (cons '1value val))) ((not (and (eq (car-safe (aref testcover-vector idx)) '1value) (equal (cdr (aref testcover-vector idx)) val))) - (error "Value of form marked with `1value' does vary."))) + (error "Value of form marked with `1value' does vary: %s" val))) val) @@ -415,7 +492,7 @@ ov j item) (or (and def-mark points coverage) (error "Missing edebug data for function %s" def)) - (when len + (when (> len 0) (set-buffer (marker-buffer def-mark)) (mapc 'delete-overlay (overlays-in def-mark (+ def-mark (aref points (1- len)) 1)))