diff options
| author | Glenn Morris | 2009-09-12 02:41:39 +0000 |
|---|---|---|
| committer | Glenn Morris | 2009-09-12 02:41:39 +0000 |
| commit | 6974be68b6a243403ce44033bc0d556b134e75f3 (patch) | |
| tree | 442a0e34517a12d2e4cf4cd6340fb624b017666f | |
| parent | 30194d4d1b45fb4d096b5b98976bad4549127cc1 (diff) | |
| download | emacs-6974be68b6a243403ce44033bc0d556b134e75f3.tar.gz emacs-6974be68b6a243403ce44033bc0d556b134e75f3.zip | |
(elint-file, elint-directory): New autoloaded commands.
(elint-current-buffer): Set mode-line-process.
(elint-init-env): Handle define-derived-mode.
Fix declare-function with unspecified arglist. Guard against odd
defalias statements (eg iso-insert's 8859-1-map).
(elint-add-required-env): Use a temp buffer.
(elint-form): Just print the function/macro name, not the whole form.
Return env unchanged if we fail to parse a macro.
(elint-forms): Guard against parse errors.
(elint-output): New function, to handle batch mode.
(elint-log-message): Add optional argument. Use elint-output.
(elint-set-mode-line): New function.
| -rw-r--r-- | lisp/emacs-lisp/elint.el | 142 |
1 files changed, 111 insertions, 31 deletions
diff --git a/lisp/emacs-lisp/elint.el b/lisp/emacs-lisp/elint.el index d144a66a61a..32011f143f3 100644 --- a/lisp/emacs-lisp/elint.el +++ b/lisp/emacs-lisp/elint.el | |||
| @@ -40,6 +40,9 @@ | |||
| 40 | 40 | ||
| 41 | ;; * List of variables and functions defined in dumped lisp files. | 41 | ;; * List of variables and functions defined in dumped lisp files. |
| 42 | ;; * Adding type checking. (Stop that sniggering!) | 42 | ;; * Adding type checking. (Stop that sniggering!) |
| 43 | ;; * Handle eval-when-compile (eg for requires, being sensitive to the | ||
| 44 | ;; difference between funcs and macros). | ||
| 45 | ;; * Requires within function bodies. | ||
| 43 | 46 | ||
| 44 | ;;; Code: | 47 | ;;; Code: |
| 45 | 48 | ||
| @@ -152,6 +155,50 @@ This environment can be passed to `macroexpand'." | |||
| 152 | ;;; | 155 | ;;; |
| 153 | 156 | ||
| 154 | ;;;###autoload | 157 | ;;;###autoload |
| 158 | (defun elint-file (file) | ||
| 159 | "Lint the file FILE." | ||
| 160 | (interactive "fElint file: ") | ||
| 161 | (setq file (expand-file-name file)) | ||
| 162 | (or elint-builtin-variables | ||
| 163 | (elint-initialize)) | ||
| 164 | (let ((dir (file-name-directory file))) | ||
| 165 | (let ((default-directory dir)) | ||
| 166 | (elint-display-log)) | ||
| 167 | (elint-set-mode-line t) | ||
| 168 | (with-current-buffer elint-log-buffer | ||
| 169 | (unless (string-equal default-directory dir) | ||
| 170 | (elint-log-message (format "\nLeaving directory `%s'" | ||
| 171 | default-directory) t) | ||
| 172 | (elint-log-message (format "Entering directory `%s'" dir) t) | ||
| 173 | (setq default-directory dir)))) | ||
| 174 | (let ((str (format "Linting file %s" file))) | ||
| 175 | (message "%s..." str) | ||
| 176 | (or noninteractive | ||
| 177 | (elint-log-message (format "\n%s at %s" str (current-time-string)) t)) | ||
| 178 | ;; elint-current-buffer clears log. | ||
| 179 | (with-temp-buffer | ||
| 180 | (insert-file-contents file) | ||
| 181 | (let ((buffer-file-name file)) | ||
| 182 | (with-syntax-table emacs-lisp-mode-syntax-table | ||
| 183 | (mapc 'elint-top-form (elint-update-env))))) | ||
| 184 | (elint-set-mode-line) | ||
| 185 | (message "%s...done" str))) | ||
| 186 | |||
| 187 | ;; cf byte-recompile-directory. | ||
| 188 | ;;;###autoload | ||
| 189 | (defun elint-directory (directory) | ||
| 190 | "Lint all the .el files in DIRECTORY." | ||
| 191 | (interactive "DElint directory: ") | ||
| 192 | (let ((elint-running t)) | ||
| 193 | (dolist (file (directory-files directory t)) | ||
| 194 | ;; Bytecomp has emacs-lisp-file-regexp. | ||
| 195 | (when (and (string-match "\\.el\\'" file) | ||
| 196 | (file-readable-p file) | ||
| 197 | (not (auto-save-file-name-p file))) | ||
| 198 | (elint-file file)))) | ||
| 199 | (elint-set-mode-line)) | ||
| 200 | |||
| 201 | ;;;###autoload | ||
| 155 | (defun elint-current-buffer () | 202 | (defun elint-current-buffer () |
| 156 | "Lint the current buffer. | 203 | "Lint the current buffer. |
| 157 | If necessary, this first calls `elint-initalize'." | 204 | If necessary, this first calls `elint-initalize'." |
| @@ -161,12 +208,14 @@ If necessary, this first calls `elint-initalize'." | |||
| 161 | (elint-clear-log (format "Linting %s" (or (buffer-file-name) | 208 | (elint-clear-log (format "Linting %s" (or (buffer-file-name) |
| 162 | (buffer-name)))) | 209 | (buffer-name)))) |
| 163 | (elint-display-log) | 210 | (elint-display-log) |
| 211 | (elint-set-mode-line t) | ||
| 164 | (mapc 'elint-top-form (elint-update-env)) | 212 | (mapc 'elint-top-form (elint-update-env)) |
| 165 | ;; Tell the user we're finished. This is terribly klugy: we set | 213 | ;; Tell the user we're finished. This is terribly klugy: we set |
| 166 | ;; elint-top-form-logged so elint-log-message doesn't print the | 214 | ;; elint-top-form-logged so elint-log-message doesn't print the |
| 167 | ;; ** top form ** header... | 215 | ;; ** top form ** header... |
| 168 | (let ((elint-top-form-logged t)) | 216 | (elint-set-mode-line) |
| 169 | (elint-log-message "\nLinting finished.\n"))) | 217 | (elint-log-message "\nLinting finished.\n" t)) |
| 218 | |||
| 170 | 219 | ||
| 171 | ;;;###autoload | 220 | ;;;###autoload |
| 172 | (defun elint-defun () | 221 | (defun elint-defun () |
| @@ -254,15 +303,19 @@ Return nil if there are no more forms, t otherwise." | |||
| 254 | ;; Add function | 303 | ;; Add function |
| 255 | ((memq (car form) '(defun defsubst)) | 304 | ((memq (car form) '(defun defsubst)) |
| 256 | (setq env (elint-env-add-func env (cadr form) (nth 2 form)))) | 305 | (setq env (elint-env-add-func env (cadr form) (nth 2 form)))) |
| 306 | ((eq (car form) 'define-derived-mode) | ||
| 307 | (setq env (elint-env-add-func env (cadr form) ()) | ||
| 308 | env (elint-env-add-var env (cadr form)))) | ||
| 257 | ;; FIXME it would be nice to check the autoloads are correct. | 309 | ;; FIXME it would be nice to check the autoloads are correct. |
| 258 | ((eq (car form) 'autoload) | 310 | ((eq (car form) 'autoload) |
| 259 | (setq env (elint-env-add-func env (cadr (cadr form)) 'unknown))) | 311 | (setq env (elint-env-add-func env (cadr (cadr form)) 'unknown))) |
| 260 | ((eq (car form) 'declare-function) | 312 | ((eq (car form) 'declare-function) |
| 261 | (setq env (elint-env-add-func env (cadr form) | 313 | (setq env (elint-env-add-func env (cadr form) |
| 262 | (if (> (length form) 3) | 314 | (if (or (< (length form) 4) |
| 263 | (nth 3 form) | 315 | (eq (nth 3 form) t)) |
| 264 | 'unknown)))) | 316 | 'unknown |
| 265 | ((eq (car form) 'defalias) | 317 | (nth 3 form))))) |
| 318 | ((and (eq (car form) 'defalias) (listp (nth 2 form))) | ||
| 266 | ;; If the alias points to something already in the environment, | 319 | ;; If the alias points to something already in the environment, |
| 267 | ;; add the alias to the environment with the same arguments. | 320 | ;; add the alias to the environment with the same arguments. |
| 268 | (let ((def (elint-env-find-func env (cadr (nth 2 form))))) | 321 | (let ((def (elint-env-find-func env (cadr (nth 2 form))))) |
| @@ -295,10 +348,16 @@ Return nil if there are no more forms, t otherwise." | |||
| 295 | (message nil) | 348 | (message nil) |
| 296 | (if lib | 349 | (if lib |
| 297 | (save-excursion | 350 | (save-excursion |
| 298 | (set-buffer (find-file-noselect lib)) | 351 | ;;; (set-buffer (find-file-noselect lib)) |
| 299 | (elint-update-env) | 352 | ;;; (elint-update-env) |
| 300 | (setq env (elint-env-add-env env elint-buffer-env))) | 353 | ;;; (setq env (elint-env-add-env env elint-buffer-env))) |
| 301 | (error "Dummy error"))) | 354 | (with-temp-buffer |
| 355 | (insert-file-contents lib) | ||
| 356 | (with-syntax-table emacs-lisp-mode-syntax-table | ||
| 357 | (elint-update-env)) | ||
| 358 | (setq env (elint-env-add-env env elint-buffer-env)))) | ||
| 359 | ;;(message "Elint processed (require '%s)" name)) | ||
| 360 | (error "Unable to find require'd library %s" name))) | ||
| 302 | (error | 361 | (error |
| 303 | (ding) | 362 | (ding) |
| 304 | (message "Can't get variables from require'd library %s" name))) | 363 | (message "Can't get variables from require'd library %s" name))) |
| @@ -356,7 +415,7 @@ The environment created by the form is returned." | |||
| 356 | (cond | 415 | (cond |
| 357 | ((eq args 'undefined) | 416 | ((eq args 'undefined) |
| 358 | (setq argsok nil) | 417 | (setq argsok nil) |
| 359 | (elint-error "Call to undefined function: %s" form)) | 418 | (elint-error "Call to undefined function: %s" func)) |
| 360 | 419 | ||
| 361 | ((eq args 'unknown) nil) | 420 | ((eq args 'unknown) nil) |
| 362 | 421 | ||
| @@ -371,7 +430,8 @@ The environment created by the form is returned." | |||
| 371 | (elint-form | 430 | (elint-form |
| 372 | (macroexpand form (elint-env-macro-env env)) env) | 431 | (macroexpand form (elint-env-macro-env env)) env) |
| 373 | (error | 432 | (error |
| 374 | (elint-error "Elint failed to expand macro: %s" form))) | 433 | (elint-error "Elint failed to expand macro: %s" func) |
| 434 | env)) | ||
| 375 | env) | 435 | env) |
| 376 | 436 | ||
| 377 | (let ((fcode (if (symbolp func) | 437 | (let ((fcode (if (symbolp func) |
| @@ -397,8 +457,11 @@ The environment created by the form is returned." | |||
| 397 | (defun elint-forms (forms env) | 457 | (defun elint-forms (forms env) |
| 398 | "Lint the FORMS, accumulating an environment, starting with ENV." | 458 | "Lint the FORMS, accumulating an environment, starting with ENV." |
| 399 | ;; grumblegrumbletailrecursiongrumblegrumble | 459 | ;; grumblegrumbletailrecursiongrumblegrumble |
| 400 | (dolist (f forms env) | 460 | (if (listp forms) |
| 401 | (setq env (elint-form f env)))) | 461 | (dolist (f forms env) |
| 462 | (setq env (elint-form f env))) | ||
| 463 | ;; Loop macro? | ||
| 464 | (elint-error "Elint failed to parse form: %s" forms))) | ||
| 402 | 465 | ||
| 403 | (defun elint-unbound-variable (var env) | 466 | (defun elint-unbound-variable (var env) |
| 404 | "T if VAR is unbound in ENV." | 467 | "T if VAR is unbound in ENV." |
| @@ -639,27 +702,33 @@ STRING and ARGS are thrown on `format' to get the message." | |||
| 639 | See `elint-error'." | 702 | See `elint-error'." |
| 640 | (elint-log "Warning" string args)) | 703 | (elint-log "Warning" string args)) |
| 641 | 704 | ||
| 642 | (defun elint-log-message (errstr) | 705 | (defun elint-output (string) |
| 643 | "Insert ERRSTR last in the lint log buffer." | 706 | "Print or insert STRING, depending on value of `noninteractive'." |
| 707 | (if noninteractive | ||
| 708 | (message "%s" string) | ||
| 709 | (insert string "\n"))) | ||
| 710 | |||
| 711 | (defun elint-log-message (errstr &optional top) | ||
| 712 | "Insert ERRSTR last in the lint log buffer. | ||
| 713 | Optional argument TOP non-nil means pretend `elint-top-form-logged' is non-nil." | ||
| 644 | (with-current-buffer (elint-get-log-buffer) | 714 | (with-current-buffer (elint-get-log-buffer) |
| 645 | (goto-char (point-max)) | 715 | (goto-char (point-max)) |
| 646 | (let ((inhibit-read-only t)) | 716 | (let ((inhibit-read-only t)) |
| 647 | (or (bolp) (newline)) | 717 | (or (bolp) (newline)) |
| 648 | ;; Do we have to say where we are? | 718 | ;; Do we have to say where we are? |
| 649 | (unless elint-top-form-logged | 719 | (unless (or elint-top-form-logged top) |
| 650 | (insert | 720 | (let* ((form (elint-top-form-form elint-top-form)) |
| 651 | (let* ((form (elint-top-form-form elint-top-form)) | 721 | (top (car form))) |
| 652 | (top (car form))) | 722 | (elint-output (cond |
| 653 | (cond | 723 | ((memq top '(defun defsubst)) |
| 654 | ((memq top '(defun defsubst)) | 724 | (format "\nIn function %s:" (cadr form))) |
| 655 | (format "\nIn function %s:\n" (cadr form))) | 725 | ((eq top 'defmacro) |
| 656 | ((eq top 'defmacro) | 726 | (format "\nIn macro %s:" (cadr form))) |
| 657 | (format "\nIn macro %s:\n" (cadr form))) | 727 | ((memq top '(defvar defconst)) |
| 658 | ((memq top '(defvar defconst)) | 728 | (format "\nIn variable %s:" (cadr form))) |
| 659 | (format "\nIn variable %s:\n" (cadr form))) | 729 | (t "\nIn top level expression:")))) |
| 660 | (t "\nIn top level expression:\n")))) | ||
| 661 | (setq elint-top-form-logged t)) | 730 | (setq elint-top-form-logged t)) |
| 662 | (insert errstr "\n")))) | 731 | (elint-output errstr)))) |
| 663 | 732 | ||
| 664 | (defun elint-clear-log (&optional header) | 733 | (defun elint-clear-log (&optional header) |
| 665 | "Clear the lint log buffer. | 734 | "Clear the lint log buffer. |
| @@ -677,6 +746,17 @@ Insert HEADER followed by a blank line if non-nil." | |||
| 677 | (display-buffer (elint-get-log-buffer)) | 746 | (display-buffer (elint-get-log-buffer)) |
| 678 | (sit-for 0))) | 747 | (sit-for 0))) |
| 679 | 748 | ||
| 749 | (defvar elint-running) | ||
| 750 | |||
| 751 | (defun elint-set-mode-line (&optional on) | ||
| 752 | "Set the mode-line-process of the Elint log buffer." | ||
| 753 | (with-current-buffer (elint-get-log-buffer) | ||
| 754 | (and (eq major-mode 'compilation-mode) | ||
| 755 | (setq mode-line-process | ||
| 756 | (list (if (or on (bound-and-true-p elint-running)) | ||
| 757 | (propertize ":run" 'face 'compilation-warning) | ||
| 758 | (propertize ":finished" 'face 'compilation-info))))))) | ||
| 759 | |||
| 680 | (defun elint-get-log-buffer () | 760 | (defun elint-get-log-buffer () |
| 681 | "Return a log buffer for elint." | 761 | "Return a log buffer for elint." |
| 682 | (or (get-buffer elint-log-buffer) | 762 | (or (get-buffer elint-log-buffer) |