aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJonathan Yavner2004-07-17 17:06:26 +0000
committerJonathan Yavner2004-07-17 17:06:26 +0000
commit3e39672fd35c40e1dedc8213858a3ac424a31824 (patch)
treeb2bd9cef4b59d03d117283c85a1a3759f0d29419
parent3751eb00d78a352422c67fc2285eb38ee5c56b92 (diff)
downloademacs-3e39672fd35c40e1dedc8213858a3ac424a31824.tar.gz
emacs-3e39672fd35c40e1dedc8213858a3ac424a31824.zip
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.
-rw-r--r--lisp/ChangeLog10
-rw-r--r--lisp/emacs-lisp/testcover.el223
2 files changed, 160 insertions, 73 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 2b249918d7f..cf0600b7605 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,13 @@
12004-07-17 Jonathan Yavner <jyavner@member.fsf.org>
2
3 * emacs-lisp/testcover.el: New category "potentially-1valued" for
4 functions that are not erroneous if either 1-valued or
5 multi-valued. Detect functions in this class.
6 (testcover-1value-functions, testcover-compose-functions,
7 testcover-progn-functions) Added some additional functions to lists.
8 (testcover-mark): Bugfix when marking up the definition for an
9 empty function.
10
12004-07-17 Richard M. Stallman <rms@gnu.org> 112004-07-17 Richard M. Stallman <rms@gnu.org>
2 12
3 * replace.el (occur-read-primary-args): Pass default to read-from-minibuffer. 13 * replace.el (occur-read-primary-args): Pass default to read-from-minibuffer.
diff --git a/lisp/emacs-lisp/testcover.el b/lisp/emacs-lisp/testcover.el
index 547e2cbd32d..23e9a54b1bb 100644
--- a/lisp/emacs-lisp/testcover.el
+++ b/lisp/emacs-lisp/testcover.el
@@ -38,9 +38,9 @@
38;; instrumentation callbacks, then replace edebug's callbacks with ours. 38;; instrumentation callbacks, then replace edebug's callbacks with ours.
39;; * To show good coverage, we want to see two values for every form, except 39;; * To show good coverage, we want to see two values for every form, except
40;; functions that always return the same value and `defconst' variables 40;; functions that always return the same value and `defconst' variables
41;; need show only value for good coverage. To avoid the brown splotch, the 41;; need show only one value for good coverage. To avoid the brown
42;; definitions for constants and 1-valued functions must precede the 42;; splotch, the definitions for constants and 1-valued functions must
43;; references. 43;; precede the references.
44;; * Use the macro `1value' in your Lisp code to mark spots where the local 44;; * Use the macro `1value' in your Lisp code to mark spots where the local
45;; code environment causes a function or variable to always have the same 45;; code environment causes a function or variable to always have the same
46;; value, but the function or variable is not intrinsically 1-valued. 46;; value, but the function or variable is not intrinsically 1-valued.
@@ -55,12 +55,14 @@
55;; call has the same value! Also, equal thinks two strings are the same 55;; call has the same value! Also, equal thinks two strings are the same
56;; if they differ only in properties. 56;; if they differ only in properties.
57;; * Because we have only a "1value" class and no "always nil" class, we have 57;; * Because we have only a "1value" class and no "always nil" class, we have
58;; to treat as 1-valued any `and' whose last term is 1-valued, in case the 58;; to treat as potentially 1-valued any `and' whose last term is 1-valued,
59;; last term is always nil. Example: 59;; in case the last term is always nil. Example:
60;; (and (< (point) 1000) (forward-char 10)) 60;; (and (< (point) 1000) (forward-char 10))
61;; This form always returns nil. Similarly, `if' and `cond' are 61;; This form always returns nil. Similarly, `or', `if', and `cond' are
62;; treated as 1-valued if all clauses are, in case those values are 62;; treated as potentially 1-valued if all clauses are, in case those
63;; always nil. 63;; values are always nil. Unlike truly 1-valued functions, it is not an
64;; error if these "potentially" 1-valued forms actually return differing
65;; values.
64 66
65(require 'edebug) 67(require 'edebug)
66(provide 'testcover) 68(provide 'testcover)
@@ -86,12 +88,14 @@ these. This list is quite incomplete!"
86 88
87(defcustom testcover-1value-functions 89(defcustom testcover-1value-functions
88 '(backward-char barf-if-buffer-read-only beginning-of-line 90 '(backward-char barf-if-buffer-read-only beginning-of-line
89 buffer-disable-undo buffer-enable-undo current-global-map deactivate-mark 91 buffer-disable-undo buffer-enable-undo current-global-map
90 delete-char delete-region ding error forward-char function* insert 92 deactivate-mark delete-backward-char delete-char delete-region ding
91 insert-and-inherit kill-all-local-variables lambda mapc narrow-to-region 93 forward-char function* insert insert-and-inherit kill-all-local-variables
92 noreturn push-mark put-text-property run-hooks set-text-properties signal 94 kill-line kill-paragraph kill-region kill-sexp lambda
93 substitute-key-definition suppress-keymap throw undo use-local-map while 95 minibuffer-complete-and-exit narrow-to-region next-line push-mark
94 widen yank) 96 put-text-property run-hooks set-match-data signal
97 substitute-key-definition suppress-keymap undo use-local-map while widen
98 yank)
95 "Functions that always return the same value. No brown splotch is shown 99 "Functions that always return the same value. No brown splotch is shown
96for these. This list is quite incomplete! Notes: Nobody ever changes the 100for these. This list is quite incomplete! Notes: Nobody ever changes the
97current global map. The macro `lambda' is self-evaluating, hence always 101current global map. The macro `lambda' is self-evaluating, hence always
@@ -108,9 +112,9 @@ them as having returned nil just before calling them."
108 :type 'hook) 112 :type 'hook)
109 113
110(defcustom testcover-compose-functions 114(defcustom testcover-compose-functions
111 '(+ - * / length list make-keymap make-sparse-keymap message propertize 115 '(+ - * / = append length list make-keymap make-sparse-keymap
112 replace-regexp-in-string run-with-idle-timer 116 mapcar message propertize replace-regexp-in-string
113 set-buffer-modified-p) 117 run-with-idle-timer set-buffer-modified-p)
114 "Functions that are 1-valued if all their args are either constants or 118 "Functions that are 1-valued if all their args are either constants or
115calls to one of the `testcover-1value-functions', so if that's true then no 119calls to one of the `testcover-1value-functions', so if that's true then no
116brown splotch is shown for these. This list is quite incomplete! Most 120brown splotch is shown for these. This list is quite incomplete! Most
@@ -119,16 +123,16 @@ side-effect-free functions should be here."
119 :type 'hook) 123 :type 'hook)
120 124
121(defcustom testcover-progn-functions 125(defcustom testcover-progn-functions
122 '(define-key fset function goto-char or overlay-put progn save-current-buffer 126 '(define-key fset function goto-char mapc overlay-put progn
123 save-excursion save-match-data save-restriction save-selected-window 127 save-current-buffer save-excursion save-match-data
124 save-window-excursion set set-default setq setq-default 128 save-restriction save-selected-window save-window-excursion
125 with-output-to-temp-buffer with-syntax-table with-temp-buffer 129 set set-default set-marker-insertion-type setq setq-default
126 with-temp-file with-temp-message with-timeout) 130 with-current-buffer with-output-to-temp-buffer with-syntax-table
131 with-temp-buffer with-temp-file with-temp-message with-timeout)
127 "Functions whose return value is the same as their last argument. No 132 "Functions whose return value is the same as their last argument. No
128brown splotch is shown for these if the last argument is a constant or a 133brown splotch is shown for these if the last argument is a constant or a
129call to one of the `testcover-1value-functions'. This list is probably 134call to one of the `testcover-1value-functions'. This list is probably
130incomplete! Note: `or' is here in case the last argument is a function that 135incomplete!"
131always returns nil."
132 :group 'testcover 136 :group 'testcover
133 :type 'hook) 137 :type 'hook)
134 138
@@ -140,6 +144,11 @@ call to one of the `testcover-1value-functions'."
140 :group 'testcover 144 :group 'testcover
141 :type 'hook) 145 :type 'hook)
142 146
147(defcustom testcover-potentially-1value-functions
148 '(add-hook and beep or remove-hook unless when)
149 "Functions that are potentially 1-valued. No brown splotch if actually
1501-valued, no error if actually multi-valued.")
151
143(defface testcover-nohits-face 152(defface testcover-nohits-face
144 '((t (:background "DeepPink2"))) 153 '((t (:background "DeepPink2")))
145 "Face for forms that had no hits during coverage test" 154 "Face for forms that had no hits during coverage test"
@@ -161,7 +170,11 @@ call to one of the `testcover-1value-functions'."
161 170
162(defvar testcover-module-1value-functions nil 171(defvar testcover-module-1value-functions nil
163 "Symbols declared with defun in the last file processed by 172 "Symbols declared with defun in the last file processed by
164`testcover-start', whose functions always return the same value.") 173`testcover-start', whose functions should always return the same value.")
174
175(defvar testcover-module-potentially-1value-functions nil
176 "Symbols declared with defun in the last file processed by
177`testcover-start', whose functions might always return the same value.")
165 178
166(defvar testcover-vector nil 179(defvar testcover-vector nil
167 "Locally bound to coverage vector for function in progress.") 180 "Locally bound to coverage vector for function in progress.")
@@ -206,25 +219,32 @@ non-nil, byte-compiles each function after instrumenting."
206 x)) 219 x))
207 220
208(defun testcover-reinstrument (form) 221(defun testcover-reinstrument (form)
209 "Reinstruments FORM to use testcover instead of edebug. This function 222 "Reinstruments FORM to use testcover instead of edebug. This
210modifies the list that FORM points to. Result is non-nil if FORM will 223function modifies the list that FORM points to. Result is nil if
211always return the same value." 224FORM should return multiple vlues, t if should always return same
225value, 'maybe if either is acceptable."
212 (let ((fun (car-safe form)) 226 (let ((fun (car-safe form))
213 id) 227 id val)
214 (cond 228 (cond
215 ((not fun) ;Atom 229 ((not fun) ;Atom
216 (or (not (symbolp form)) 230 (when (or (not (symbolp form))
217 (memq form testcover-constants) 231 (memq form testcover-constants)
218 (memq form testcover-module-constants))) 232 (memq form testcover-module-constants))
219 ((consp fun) ;Embedded list 233 t))
234 ((consp fun) ;Embedded list
220 (testcover-reinstrument fun) 235 (testcover-reinstrument fun)
221 (testcover-reinstrument-list (cdr form)) 236 (testcover-reinstrument-list (cdr form))
222 nil) 237 nil)
223 ((or (memq fun testcover-1value-functions) 238 ((or (memq fun testcover-1value-functions)
224 (memq fun testcover-module-1value-functions)) 239 (memq fun testcover-module-1value-functions))
225 ;;Always return same value 240 ;;Should always return same value
226 (testcover-reinstrument-list (cdr form)) 241 (testcover-reinstrument-list (cdr form))
227 t) 242 t)
243 ((or (memq fun testcover-potentially-1value-functions)
244 (memq fun testcover-module-potentially-1value-functions))
245 ;;Might always return same value
246 (testcover-reinstrument-list (cdr form))
247 'maybe)
228 ((memq fun testcover-progn-functions) 248 ((memq fun testcover-progn-functions)
229 ;;1-valued if last argument is 249 ;;1-valued if last argument is
230 (testcover-reinstrument-list (cdr form))) 250 (testcover-reinstrument-list (cdr form)))
@@ -233,11 +253,9 @@ always return the same value."
233 (testcover-reinstrument-list (cddr form)) 253 (testcover-reinstrument-list (cddr form))
234 (testcover-reinstrument (cadr form))) 254 (testcover-reinstrument (cadr form)))
235 ((memq fun testcover-compose-functions) 255 ((memq fun testcover-compose-functions)
236 ;;1-valued if all arguments are 256 ;;1-valued if all arguments are. Potentially 1-valued if all
237 (setq id t) 257 ;;arguments are either definitely or potentially.
238 (mapc #'(lambda (x) (setq id (or (testcover-reinstrument x) id))) 258 (testcover-reinstrument-compose (cdr form) 'testcover-reinstrument))
239 (cdr form))
240 id)
241 ((eq fun 'edebug-enter) 259 ((eq fun 'edebug-enter)
242 ;;(edebug-enter 'SYM ARGS #'(lambda nil FORMS)) 260 ;;(edebug-enter 'SYM ARGS #'(lambda nil FORMS))
243 ;; => (testcover-enter 'SYM #'(lambda nil FORMS)) 261 ;; => (testcover-enter 'SYM #'(lambda nil FORMS))
@@ -252,33 +270,44 @@ always return the same value."
252 (aset testcover-vector (cadr (cadr form)) 'ok-coverage)) 270 (aset testcover-vector (cadr (cadr form)) 'ok-coverage))
253 (setq id (nth 2 form)) 271 (setq id (nth 2 form))
254 (setcdr form (nthcdr 2 form)) 272 (setcdr form (nthcdr 2 form))
273 (setq val (testcover-reinstrument (nth 2 form)))
274 (if (eq val t)
275 (setcar form 'testcover-1value)
276 (setcar form 'testcover-after))
277 (when val
278 ;;1-valued or potentially 1-valued
279 (aset testcover-vector id '1value))
255 (cond 280 (cond
256 ((memq (car-safe (nth 2 form)) testcover-noreturn-functions) 281 ((memq (car-safe (nth 2 form)) testcover-noreturn-functions)
257 ;;This function won't return, so set the value in advance 282 ;;This function won't return, so set the value in advance
258 ;;(edebug-after (edebug-before XXX) YYY FORM) 283 ;;(edebug-after (edebug-before XXX) YYY FORM)
259 ;; => (progn (edebug-after YYY nil) FORM) 284 ;; => (progn (edebug-after YYY nil) FORM)
285 (setcar (cdr form) `(,(car form) ,id nil))
260 (setcar form 'progn) 286 (setcar form 'progn)
261 (setcar (cdr form) `(testcover-after ,id nil))) 287 (aset testcover-vector id '1value)
288 (setq val t))
262 ((eq (car-safe (nth 2 form)) '1value) 289 ((eq (car-safe (nth 2 form)) '1value)
263 ;;This function is always supposed to return the same value 290 ;;This function is always supposed to return the same value
264 (setcar form 'testcover-1value)) 291 (setq val t)
265 (t 292 (aset testcover-vector id '1value)
266 (setcar form 'testcover-after))) 293 (setcar form 'testcover-1value)))
267 (when (testcover-reinstrument (nth 2 form)) 294 val)
268 (aset testcover-vector id '1value)))
269 ((eq fun 'defun) 295 ((eq fun 'defun)
270 (if (testcover-reinstrument-list (nthcdr 3 form)) 296 (setq val (testcover-reinstrument-list (nthcdr 3 form)))
271 (push (cadr form) testcover-module-1value-functions))) 297 (when (eq val t)
272 ((eq fun 'defconst) 298 (push (cadr form) testcover-module-1value-functions))
299 (when (eq val 'maybe)
300 (push (cadr form) testcover-module-potentially-1value-functions)))
301 ((memq fun '(defconst defcustom))
273 ;;Define this symbol as 1-valued 302 ;;Define this symbol as 1-valued
274 (push (cadr form) testcover-module-constants) 303 (push (cadr form) testcover-module-constants)
275 (testcover-reinstrument-list (cddr form))) 304 (testcover-reinstrument-list (cddr form)))
276 ((memq fun '(dotimes dolist)) 305 ((memq fun '(dotimes dolist))
277 ;;Always returns third value from SPEC 306 ;;Always returns third value from SPEC
278 (testcover-reinstrument-list (cddr form)) 307 (testcover-reinstrument-list (cddr form))
279 (setq fun (testcover-reinstrument-list (cadr form))) 308 (setq val (testcover-reinstrument-list (cadr form)))
280 (if (nth 2 (cadr form)) 309 (if (nth 2 (cadr form))
281 fun 310 val
282 ;;No third value, always returns nil 311 ;;No third value, always returns nil
283 t)) 312 t))
284 ((memq fun '(let let*)) 313 ((memq fun '(let let*))
@@ -286,23 +315,23 @@ always return the same value."
286 (mapc 'testcover-reinstrument-list (cadr form)) 315 (mapc 'testcover-reinstrument-list (cadr form))
287 (testcover-reinstrument-list (cddr form))) 316 (testcover-reinstrument-list (cddr form)))
288 ((eq fun 'if) 317 ((eq fun 'if)
289 ;;1-valued if both THEN and ELSE clauses are 318 ;;Potentially 1-valued if both THEN and ELSE clauses are
290 (testcover-reinstrument (cadr form)) 319 (testcover-reinstrument (cadr form))
291 (let ((then (testcover-reinstrument (nth 2 form))) 320 (let ((then (testcover-reinstrument (nth 2 form)))
292 (else (testcover-reinstrument-list (nthcdr 3 form)))) 321 (else (testcover-reinstrument-list (nthcdr 3 form))))
293 (and then else))) 322 (and then else 'maybe)))
294 ((memq fun '(when unless and))
295 ;;1-valued if last clause of BODY is
296 (testcover-reinstrument-list (cdr form)))
297 ((eq fun 'cond) 323 ((eq fun 'cond)
298 ;;1-valued if all clauses are 324 ;;Potentially 1-valued if all clauses are
299 (testcover-reinstrument-clauses (cdr form))) 325 (when (testcover-reinstrument-compose (cdr form)
326 'testcover-reinstrument-list)
327 'maybe))
300 ((eq fun 'condition-case) 328 ((eq fun 'condition-case)
301 ;;1-valued if BODYFORM is and all HANDLERS are 329 ;;Potentially 1-valued if BODYFORM is and all HANDLERS are
302 (let ((body (testcover-reinstrument (nth 2 form))) 330 (let ((body (testcover-reinstrument (nth 2 form)))
303 (errs (testcover-reinstrument-clauses (mapcar #'cdr 331 (errs (testcover-reinstrument-compose
304 (nthcdr 3 form))))) 332 (mapcar #'cdr (nthcdr 3 form))
305 (and body errs))) 333 'testcover-reinstrument-list)))
334 (and body errs 'maybe)))
306 ((eq fun 'quote) 335 ((eq fun 'quote)
307 ;;Don't reinstrument what's inside! 336 ;;Don't reinstrument what's inside!
308 ;;This doesn't apply within a backquote 337 ;;This doesn't apply within a backquote
@@ -317,16 +346,55 @@ always return the same value."
317 (let ((testcover-1value-functions 346 (let ((testcover-1value-functions
318 (remq 'quote testcover-1value-functions))) 347 (remq 'quote testcover-1value-functions)))
319 (testcover-reinstrument (cadr form)))) 348 (testcover-reinstrument (cadr form))))
320 ((memq fun '(1value noreturn)) 349 ((eq fun '1value)
321 ;;Hack - pretend the arg is 1-valued here 350 ;;Hack - pretend the arg is 1-valued here
322 (if (symbolp (cadr form)) ;A pseudoconstant variable 351 (cond
323 t 352 ((symbolp (cadr form))
353 ;;A pseudoconstant variable
354 t)
355 ((and (eq (car (cadr form)) 'edebug-after)
356 (symbolp (nth 3 (cadr form))))
357 ;;Reference to pseudoconstant
358 (aset testcover-vector (nth 2 (cadr form)) '1value)
359 (setcar (cdr form) `(testcover-1value ,(nth 2 (cadr form))
360 ,(nth 3 (cadr form))))
361 t)
362 (t
324 (if (eq (car (cadr form)) 'edebug-after) 363 (if (eq (car (cadr form)) 'edebug-after)
325 (setq id (car (nth 3 (cadr form)))) 364 (setq id (car (nth 3 (cadr form))))
326 (setq id (car (cadr form)))) 365 (setq id (car (cadr form))))
327 (let ((testcover-1value-functions 366 (let ((testcover-1value-functions
328 (cons id testcover-1value-functions))) 367 (cons id testcover-1value-functions)))
329 (testcover-reinstrument (cadr form))))) 368 (testcover-reinstrument (cadr form))))))
369 ((eq fun 'noreturn)
370 ;;Hack - pretend the arg has no return
371 (cond
372 ((symbolp (cadr form))
373 ;;A pseudoconstant variable
374 'maybe)
375 ((and (eq (car (cadr form)) 'edebug-after)
376 (symbolp (nth 3 (cadr form))))
377 ;;Reference to pseudoconstant
378 (aset testcover-vector (nth 2 (cadr form)) '1value)
379 (setcar (cdr form) `(progn (testcover-after ,(nth 2 (cadr form)) nil)
380 ,(nth 3 (cadr form))))
381 'maybe)
382 (t
383 (if (eq (car (cadr form)) 'edebug-after)
384 (setq id (car (nth 3 (cadr form))))
385 (setq id (car (cadr form))))
386 (let ((testcover-noreturn-functions
387 (cons id testcover-noreturn-functions)))
388 (testcover-reinstrument (cadr form))))))
389 ((and (eq fun 'apply)
390 (eq (car-safe (cadr form)) 'quote)
391 (symbolp (cadr (cadr form))))
392 ;;Apply of a constant symbol. Process as 1value or noreturn
393 ;;depending on symbol.
394 (setq fun (cons (cadr (cadr form)) (cddr form))
395 val (testcover-reinstrument fun))
396 (setcdr (cdr form) (cdr fun))
397 val)
330 (t ;Some other function or weird thing 398 (t ;Some other function or weird thing
331 (testcover-reinstrument-list (cdr form)) 399 (testcover-reinstrument-list (cdr form))
332 nil)))) 400 nil))))
@@ -341,13 +409,22 @@ always be nil, so we return t for 1-valued."
341 (setq result (testcover-reinstrument (pop list)))) 409 (setq result (testcover-reinstrument (pop list))))
342 result)) 410 result))
343 411
344(defun testcover-reinstrument-clauses (clauselist) 412(defun testcover-reinstrument-compose (list fun)
345 "Reinstrument each list in CLAUSELIST. 413 "For a compositional function, the result is 1-valued if all
346Result is t if every clause is 1-valued." 414arguments are, potentially 1-valued if all arguments are either
415definitely or potentially 1-valued, and multi-valued otherwise.
416FUN should be `testcover-reinstrument' for compositional functions,
417 `testcover-reinstrument-list' for clauses in a `cond'."
347 (let ((result t)) 418 (let ((result t))
348 (mapc #'(lambda (x) 419 (mapc #'(lambda (x)
349 (setq result (and (testcover-reinstrument-list x) result))) 420 (setq x (funcall fun x))
350 clauselist) 421 (cond
422 ((eq result t)
423 (setq result x))
424 ((eq result 'maybe)
425 (when (not x)
426 (setq result nil)))))
427 list)
351 result)) 428 result))
352 429
353(defun testcover-end (buffer) 430(defun testcover-end (buffer)
@@ -387,7 +464,7 @@ same value during coverage testing."
387 (aset testcover-vector idx (cons '1value val))) 464 (aset testcover-vector idx (cons '1value val)))
388 ((not (and (eq (car-safe (aref testcover-vector idx)) '1value) 465 ((not (and (eq (car-safe (aref testcover-vector idx)) '1value)
389 (equal (cdr (aref testcover-vector idx)) val))) 466 (equal (cdr (aref testcover-vector idx)) val)))
390 (error "Value of form marked with `1value' does vary."))) 467 (error "Value of form marked with `1value' does vary: %s" val)))
391 val) 468 val)
392 469
393 470
@@ -415,7 +492,7 @@ eliminated by adding more test cases."
415 ov j item) 492 ov j item)
416 (or (and def-mark points coverage) 493 (or (and def-mark points coverage)
417 (error "Missing edebug data for function %s" def)) 494 (error "Missing edebug data for function %s" def))
418 (when len 495 (when (> len 0)
419 (set-buffer (marker-buffer def-mark)) 496 (set-buffer (marker-buffer def-mark))
420 (mapc 'delete-overlay 497 (mapc 'delete-overlay
421 (overlays-in def-mark (+ def-mark (aref points (1- len)) 1))) 498 (overlays-in def-mark (+ def-mark (aref points (1- len)) 1)))