;;; check-declare.el --- Check declare-function statements
-;; Copyright (C) 2007, 2008 Free Software Foundation, Inc.
+;; Copyright (C) 2007-2014 Free Software Foundation, Inc.
;; Author: Glenn Morris <rgm@gnu.org>
;; Keywords: lisp, tools, maint
;; This file is part of GNU Emacs.
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING. If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; checks that all such statements in a file or directory are accurate.
;; The entry points are `check-declare-file' and `check-declare-directory'.
-;; For more information, see Info node `elisp(Declaring Functions)'.
+;; For more information, see Info node `(elisp)Declaring Functions'.
;;; TODO:
;; 1. Warn about functions marked as obsolete, eg
;; password-read-and-add in smime.el.
+;; 2. defmethod, defclass argument checking.
+;; 3. defclass also defines -p and -child-p.
;;; Code:
(defun check-declare-locate (file basefile)
"Return the full path of FILE.
-Expands files with a \".c\" extension relative to the Emacs
+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.
The returned file might not exist. If FILE has an \"ext:\" prefix, so does
(if ext
(setq file (substring file 4)))
(setq file
- (if (string-equal "c" (file-name-extension file))
+ (if (member (file-name-extension file) '("c" "m"))
(expand-file-name file (expand-file-name "src" source-directory))
- (if (setq tfile (locate-library (file-name-nondirectory file)))
+ (if (setq tfile (locate-library file))
(progn
(setq tfile
(replace-regexp-in-string "\\.elc\\'" ".el" tfile))
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 fnfile fn arglist fileonly)
+ alist form len fn fnfile arglist fileonly)
(message "%s" m)
(with-temp-buffer
(insert-file-contents file)
- (while (re-search-forward
- "^[ \t]*(declare-function[ \t]+\\(\\S-+\\)[ \t]+\
-\"\\(\\S-+\\)\"" nil t)
- (setq fn (match-string 1)
- fnfile (match-string 2)
- fnfile (check-declare-locate fnfile (expand-file-name file))
- arglist (progn
- (skip-chars-forward " \t\n")
- ;; Use `t' to distinguish no arglist
- ;; specified from an empty one.
- (if (looking-at "\\((\\|nil\\|t\\)")
- (read (current-buffer))
- t))
- fileonly (progn
- (skip-chars-forward " \t\n")
- (if (looking-at "\\(t\\|'\\sw+\\)")
- (match-string 1)))
- alist (cons (list fnfile fn arglist fileonly) alist))))
+ ;; 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))))
+ ;; Exclude element of byte-compile-initial-macro-environment.
+ (or (listp (cdr form)) (setq form nil))
+ (> (setq len (length form)) 2)
+ (< len 6)
+ (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)))
+ ;; 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)
alist))
(when full
(setq l 0)
(dolist (e errlist)
- (setq l (1+ l))))
+ (setq l (+ l (1- (length e))))))
(format "%d problem%s found" l (if (= l 1) "" "s")))
"OK"))
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 (string-equal "c" (file-name-extension fnfile)))
+ (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-exists-p fnfile)
+ (if (file-regular-p fnfile)
(with-temp-buffer
(insert-file-contents fnfile)
;; defsubst's don't _have_ to be known at compile time.
(setq re (format (if cflag
"^[ \t]*\\(DEFUN\\)[ \t]*([ \t]*\"%s\""
"^[ \t]*(\\(fset[ \t]+'\\|\
-def\\(?:un\\|subst\\|foo\\|\
-ine-\\(?:derived\\|generic\\|\\(?:global\\(?:ized\\)?-\\)?minor\\)-mode\
-\\|\\(?:ine-obsolete-function-\\)?alias[ \t]+'\\)\\)\
+def\\(?:un\\|subst\\|foo\\|method\\|class\\|\
+ine-\\(?:derived\\|generic\\|\\(?:global\\(?:ized\\)?-\\)?minor\\)-mode\\|\
+\\(?:ine-obsolete-function-\\)?alias[ \t]+'\\|\
+ine-overloadable-function\\)\\)\
\[ \t]*%s\\([ \t;]+\\|$\\)")
(regexp-opt (mapcar 'cadr fnlist) t)))
(while (re-search-forward re nil t)
;; sig = 'err means we could not find an arglist.
sig (cond (cflag
(or
- (when (re-search-forward "," nil t 3)
+ (when (search-forward "," nil t 3)
(skip-chars-forward " \t\n")
;; Assuming minargs and maxargs on same line.
(when (looking-at "\\([0-9]+\\)[ \t]*,[ \t]*\
type)
'obsolete)
;; Can't easily check arguments in these cases.
- ((string-match "\\`\\(defalias\\|fset\\)\\>" type)
+ ((string-match "\\`\\(def\\(alias\\|\
+method\\|class\\)\\|fset\\)\\>" type)
t)
((looking-at "\\((\\|nil\\)")
(byte-compile-arglist-signature
;;;###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. For this to
-work correctly, the statements must adhere to the format
-described in the documentation of `declare-function'."
+Returns non-nil if any false statements are found."
(interactive "DDirectory to check: ")
(or (file-directory-p (setq root (expand-file-name root)))
(error "Directory `%s' not found" root))
(provide 'check-declare)
-;; arch-tag: a4d6cdc4-deb7-4502-b327-0e4ef3d82d96
;;; check-declare.el ends here.