‘make check-declare’ now chatters less

* etc/NEWS: Document this.
* lisp/emacs-lisp/check-declare.el (check-declare-locate):
Return relative names, not absolute.
(check-declare-scan, check-declare-verify, check-declare-warn)
(check-declare-file, check-declare-directory):
Generate less chatter.  Use relative file names rather than
absolute.  Don’t give up on computing a good file name for a
diagnostic merely because the function name was bad.  Make
malformed declarations more noticeable.  Don’t warn about
"ext:..." declarations if check-declare-ext-errors is nil.
(check-declare-errmsg): Remove.
(check-declare-warn): New optional arg LINE.
(check-declare-files): Put status into mode line rather than
chattering.
This commit is contained in:
Paul Eggert 2016-05-18 00:48:35 -07:00
parent b4d1cddc1b
commit 07ebcf1bd9
2 changed files with 63 additions and 83 deletions

View file

@ -43,7 +43,7 @@
"Name of buffer used to display any `check-declare' warnings.")
(defun check-declare-locate (file basefile)
"Return the full path of FILE.
"Return the relative name of FILE.
Expands files with a \".c\" or \".m\" extension relative to the Emacs
\"src/\" directory. Otherwise, `locate-library' searches for FILE.
If that fails, expands FILE relative to BASEFILE's directory part.
@ -70,6 +70,7 @@ the result."
(string-match "\\.el\\'" tfile))
tfile
(concat tfile ".el")))))
(setq file (file-relative-name file))
(if ext (concat "ext:" file)
file)))
@ -80,49 +81,40 @@ where only the first two elements need be present. This claims that FNFILE
defines FN, with ARGLIST. FILEONLY non-nil means only check that FNFILE
exists, not that it defines FN. This is for function definitions that we
don't know how to recognize (e.g. some macros)."
(let ((m (format "Scanning %s..." file))
alist form len fn fnfile arglist fileonly)
(message "%s" m)
(let (alist)
(with-temp-buffer
(insert-file-contents file)
;; FIXME we could theoretically be inside a string.
(while (re-search-forward "^[ \t]*\\((declare-function\\)[ \t\n]" nil t)
(goto-char (match-beginning 1))
(if (and (setq form (ignore-errors (read (current-buffer))))
(let ((pos (match-beginning 1)))
(goto-char pos)
(let ((form (ignore-errors (read (current-buffer))))
len fn formfile fnfile arglist fileonly)
(if (and
;; Exclude element of byte-compile-initial-macro-environment.
(or (listp (cdr form)) (setq form nil))
(> (setq len (length form)) 2)
(< len 6)
(setq formfile (nth 2 form))
(symbolp (setq fn (cadr form)))
(setq fn (symbol-name fn)) ; later we use as a search string
(stringp (setq fnfile (nth 2 form)))
(setq fnfile (check-declare-locate fnfile
(expand-file-name file)))
(stringp formfile)
(setq fnfile (check-declare-locate formfile file))
;; Use t to distinguish unspecified arglist from empty one.
(or (eq t (setq arglist (if (> len 3)
(nth 3 form)
t)))
(listp arglist))
(symbolp (setq fileonly (nth 4 form))))
(setq alist (cons (list fnfile fn arglist fileonly) alist))
;; FIXME make this more noticeable.
(if form (message "Malformed declaration for `%s'" (cadr form))))))
(message "%sdone" m)
(setq alist (cons (list fnfile fn arglist fileonly) alist))
(when form
(check-declare-warn file (or fn "unknown function")
(if (stringp formfile) formfile
"unknown file")
"Malformed declaration"
(line-number-at-pos pos))))))))
alist))
(defun check-declare-errmsg (errlist &optional full)
"Return a string with the number of errors in ERRLIST, if any.
Normally just counts the number of elements in ERRLIST.
With optional argument FULL, sums the number of elements in each element."
(if errlist
(let ((l (length errlist)))
(when full
(setq l 0)
(dolist (e errlist)
(setq l (+ l (1- (length e))))))
(format "%d problem%s found" l (if (= l 1) "" "s")))
"OK"))
(autoload 'byte-compile-arglist-signature "bytecomp")
(defgroup check-declare nil
@ -144,11 +136,9 @@ to only check that FNFILE exists, not that it actually defines FN.
Returns nil if all claims are found to be true, otherwise a list
of errors with elements of the form \(FILE FN TYPE), where TYPE
is a string giving details of the error."
(let ((m (format "Checking %s..." fnfile))
(cflag (member (file-name-extension fnfile) '("c" "m")))
(let ((cflag (member (file-name-extension fnfile) '("c" "m")))
(ext (string-match "^ext:" fnfile))
re fn sig siglist arglist type errlist minargs maxargs)
(message "%s" m)
(if ext
(setq fnfile (substring fnfile 4)))
(if (file-regular-p fnfile)
@ -216,7 +206,8 @@ fset\\|\\(?:cl-\\)?defmethod\\)\\>" type)
(setq arglist (nth 2 e)
type
(if (not re)
"file not found"
(when (or check-declare-ext-errors (not ext))
"file not found")
(if (not (setq sig (assoc (cadr e) siglist)))
(unless (nth 3 e) ; fileonly
"function not found")
@ -235,13 +226,6 @@ fset\\|\\(?:cl-\\)?defmethod\\)\\>" type)
"arglist mismatch")))))
(when type
(setq errlist (cons (list (car e) (cadr e) type) errlist))))
(message "%s%s" m
(if (or re (or check-declare-ext-errors
(not ext)))
(check-declare-errmsg errlist)
(progn
(setq errlist nil)
"skipping external file")))
errlist))
(defun check-declare-sort (alist)
@ -258,30 +242,27 @@ Returned list has elements FNFILE (FILE ...)."
(setq sort (cons (list fnfile (cons file rest)) sort)))))
sort))
(defun check-declare-warn (file fn fnfile type)
(defun check-declare-warn (file fn fnfile type &optional line)
"Warn that FILE made a false claim about FN in FNFILE.
TYPE is a string giving the nature of the error. Warning is displayed in
`check-declare-warning-buffer'."
TYPE is a string giving the nature of the error.
Optional LINE is the claim's line number; otherwise, search for the claim.
Display warning in `check-declare-warning-buffer'."
(let ((warning-prefix-function
(lambda (level entry)
(let ((line 0)
(col 0))
(insert
(with-current-buffer (find-file-noselect file)
(goto-char (point-min))
(when (re-search-forward
(format "(declare-function[ \t\n]+%s" fn) nil t)
(goto-char (match-beginning 0))
(setq line (line-number-at-pos))
(setq col (1+ (current-column))))
(format "%s:%d:%d:"
(file-name-nondirectory file)
line col))))
(insert (format "%s:%d:" (file-relative-name file) (or line 0)))
entry))
(warning-fill-prefix " "))
(unless line
(with-current-buffer (find-file-noselect file)
(goto-char (point-min))
(when (and (not line)
(re-search-forward
(format "(declare-function[ \t\n]+%s" fn) nil t))
(goto-char (match-beginning 0))
(setq line (line-number-at-pos)))))
(display-warning 'check-declare
(format-message "said `%s' was defined in %s: %s"
fn (file-name-nondirectory fnfile) type)
fn (file-relative-name fnfile) type)
nil check-declare-warning-buffer)))
(declare-function compilation-forget-errors "compile" ())
@ -289,7 +270,18 @@ TYPE is a string giving the nature of the error. Warning is displayed in
(defun check-declare-files (&rest files)
"Check veracity of all `declare-function' statements in FILES.
Return a list of any errors found."
(let (alist err errlist)
(if (get-buffer check-declare-warning-buffer)
(kill-buffer check-declare-warning-buffer))
(let ((buf (get-buffer-create check-declare-warning-buffer))
alist err errlist)
(with-current-buffer buf
(unless (derived-mode-p 'compilation-mode)
(compilation-mode))
(setq mode-line-process
'(:propertize ":run" face compilation-mode-line-run))
(let ((inhibit-read-only t))
(insert "\f\n"))
(compilation-forget-errors))
(dolist (file files)
(setq alist (cons (cons file (check-declare-scan file)) alist)))
;; Sort so that things are ordered by the files supposed to
@ -298,19 +290,15 @@ Return a list of any errors found."
(if (setq err (check-declare-verify (car e) (cdr e)))
(setq errlist (cons (cons (car e) err) errlist))))
(setq errlist (nreverse errlist))
(if (get-buffer check-declare-warning-buffer)
(kill-buffer check-declare-warning-buffer))
(with-current-buffer (get-buffer-create check-declare-warning-buffer)
(unless (derived-mode-p 'compilation-mode)
(compilation-mode))
(let ((inhibit-read-only t))
(insert "\f\n"))
(compilation-forget-errors))
;; Sort back again so that errors are ordered by the files
;; containing the declare-function statements.
(dolist (e (check-declare-sort errlist))
(dolist (f (cdr e))
(check-declare-warn (car e) (cadr f) (car f) (nth 2 f))))
(with-current-buffer buf
(setq mode-line-process
'(:propertize ":exit" face compilation-mode-line-run))
(force-mode-line-update))
errlist))
;;;###autoload
@ -320,34 +308,22 @@ See `check-declare-directory' for more information."
(interactive "fFile to check: ")
(or (file-exists-p file)
(error "File `%s' not found" file))
(let ((m (format "Checking %s..." file))
errlist)
(message "%s" m)
(setq errlist (check-declare-files file))
(message "%s%s" m (check-declare-errmsg errlist))
errlist))
(check-declare-files file))
;;;###autoload
(defun check-declare-directory (root)
"Check veracity of all `declare-function' statements under directory ROOT.
Returns non-nil if any false statements are found."
(interactive "DDirectory to check: ")
(or (file-directory-p (setq root (expand-file-name root)))
(setq root (directory-file-name (file-relative-name root)))
(or (file-directory-p root)
(error "Directory `%s' not found" root))
(let ((m "Checking `declare-function' statements...")
(m2 "Finding files with declarations...")
errlist files)
(message "%s" m)
(message "%s" m2)
(setq files (process-lines find-program root
"-name" "*.el"
"-exec" grep-program
"-l" "^[ \t]*(declare-function" "{}" "+"))
(message "%s%d found" m2 (length files))
(let ((files (process-lines find-program root
"-name" "*.el"
"-exec" grep-program
"-l" "^[ \t]*(declare-function" "{}" "+")))
(when files
(setq errlist (apply 'check-declare-files files))
(message "%s%s" m (check-declare-errmsg errlist t))
errlist)))
(apply #'check-declare-files files))))
(provide 'check-declare)