diff lisp/emacs-lisp/elint.el @ 19210:ad26cf3804c3

Initial revision
author Richard M. Stallman <rms@gnu.org>
date Thu, 07 Aug 1997 17:25:43 +0000
parents
children 2ecea967337d
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/emacs-lisp/elint.el	Thu Aug 07 17:25:43 1997 +0000
@@ -0,0 +1,800 @@
+;;; elint.el -- Lint Emacs Lisp
+
+;; Copyright (C) 1997 Free Software Foundation, Inc.
+
+;; Author: Peter Liljenberg <petli@lysator.liu.se>
+;; Created: May 1997
+;; Keywords: lisp
+
+;; This file is part of GNU Emacs.
+
+;; 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 2, 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; 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., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;; This is a linter for Emacs Lisp. Currently, it mainly catches
+;; mispellings and undefined variables, although it can also catch
+;; function calls with the wrong number of arguments.
+
+;; Before using, call `elint-initialize' to set up som argument
+;; data. This takes a while. Then call elint-current-buffer or
+;; elint-defun to lint a buffer or a defun.
+
+;; The linter will try to "include" any require'd libraries to find
+;; the variables defined in those. There is a fair amount of voodoo
+;; involved in this, but it seems to work in normal situations.
+
+;;; History:
+
+;;; To do:
+
+;; * A list of all standard Emacs variables would be nice to have...
+;; * Adding type checking. (Stop that sniggering!)
+
+;;; Code:
+
+(defvar elint-log-buffer "*Elint*"
+  "*The buffer to insert lint messages in.")
+
+;;;
+;;; ADT: top-form
+;;;
+
+(defsubst elint-make-top-form (form pos)
+  "Create a top form.
+FORM is the form, and POS is the point where it starts in the buffer."
+  (cons form pos))
+
+(defsubst elint-top-form-form (top-form)
+  "Extract the form from a TOP-FORM."
+  (car top-form))
+
+(defsubst elint-top-form-pos (top-form)
+  "Extract the position from a TOP-FORM."
+  (cdr top-form))
+
+;;;
+;;; ADT: env
+;;;
+
+(defsubst elint-make-env ()
+  "Create an empty environment."
+  (list (list nil) nil nil))
+
+(defsubst elint-env-add-env (env newenv)
+  "Augment ENV with NEWENV.
+None of them is modified, and the new env is returned."
+  (list (append (car env) (car newenv))
+	(append (car (cdr env)) (car (cdr newenv)))
+	(append (car (cdr (cdr env))) (car (cdr (cdr newenv))))))
+
+(defsubst elint-env-add-var (env var)
+  "Augment ENV with the variable VAR.
+The new environment is returned, the old is unmodified."
+  (cons (cons (list var) (car env)) (cdr env)))
+
+(defsubst elint-env-add-global-var (env var)
+  "Augment ENV with the variable VAR.
+ENV is modified so VAR is seen everywhere.
+ENV is returned."
+  (nconc (car env) (list (list var)))
+  env)
+
+(defsubst elint-env-find-var (env var)
+  "Non-nil if ENV contains the variable VAR.
+Actually, a list with VAR as a single element is returned."
+  (assq var (car env)))
+
+(defsubst elint-env-add-func (env func args)
+  "Augment ENV with the function FUNC, which has the arguments ARGS.
+The new environment is returned, the old is unmodified."
+  (list (car env)
+	(cons (list func args) (car (cdr env)))
+	(car (cdr (cdr env)))))
+
+(defsubst elint-env-find-func (env func)
+  "Non-nil if ENV contains the function FUNC.
+Actually, a list of (FUNC ARGS) is returned."
+  (assq func (car (cdr env))))
+
+(defsubst elint-env-add-macro (env macro def)
+  "Augment ENV with the macro named MACRO.
+DEF is the macro definition (a lambda expression or similar).
+The new environment is returned, the old is unmodified."
+  (list (car env)
+	(car (cdr env))
+	(cons (cons macro def) (car (cdr (cdr env))))))
+
+(defsubst elint-env-macro-env (env)
+  "Return the macro environment of ENV.
+This environment can be passed to `macroexpand'."
+  (car (cdr (cdr env))))
+
+(defsubst elint-env-macrop (env macro)
+  "Non-nil if ENV contains MACRO."
+  (assq macro (elint-env-macro-env env)))
+
+;;;
+;;; User interface
+;;;
+
+(defun elint-current-buffer ()
+  "Lint the current buffer."
+  (interactive)
+  (elint-clear-log (format "Linting %s" (if (buffer-file-name)
+					    (buffer-file-name)
+					  (buffer-name))))
+  (elint-display-log)
+  (mapcar 'elint-top-form (elint-update-env))
+
+  ;; Tell the user we're finished. This is terribly klugy: we set
+  ;; elint-top-form-logged so elint-log-message doesn't print the
+  ;; ** top form ** header...
+  (let ((elint-top-form-logged t))
+    (elint-log-message "\nLinting complete.\n")))
+	
+(defun elint-defun ()
+  "Lint the function at point."
+  (interactive)
+  (save-excursion
+    (if (not (beginning-of-defun))
+	(error "Lint what?"))
+
+    (let ((pos (point))
+	  (def (read (current-buffer))))
+      (elint-display-log)
+
+      (elint-update-env)
+      (elint-top-form (elint-make-top-form def pos)))))
+
+;;;
+;;; Top form functions
+;;;
+
+(defvar elint-buffer-env nil
+  "The environment of a elisp buffer.
+Will be local in linted buffers.")
+
+(defvar elint-buffer-forms nil
+  "The top forms in a buffer.
+Will be local in linted buffers.")
+
+(defvar elint-last-env-time nil
+  "The last time the buffers env was updated.
+Is measured in buffer-modified-ticks and is local in linted buffers.")
+
+(defun elint-update-env ()
+  "Update the elint environment in the current buffer.
+Don't do anything if the buffer hasn't been changed since this
+function was called the last time.
+Returns the forms."
+  (if (and (local-variable-p 'elint-buffer-env (current-buffer))
+	   (local-variable-p 'elint-buffer-forms (current-buffer))
+	   (local-variable-p 'elint-last-env-time (current-buffer))
+	   (= (buffer-modified-tick) elint-last-env-time))
+      ;; Env is up to date
+      elint-buffer-forms
+    ;; Remake env
+    (set (make-local-variable 'elint-buffer-forms) (elint-get-top-forms))
+    (set (make-local-variable 'elint-buffer-env)
+	 (elint-init-env elint-buffer-forms))
+    (set (make-local-variable 'elint-last-env-time) (buffer-modified-tick))
+    elint-buffer-forms))
+       
+(defun elint-get-top-forms ()
+  "Collect all the top forms in the current buffer."
+  (save-excursion
+    (let ((tops nil))
+      (goto-char (point-min))
+      (while (elint-find-next-top-form)
+	(let ((pos (point)))
+	  (condition-case nil
+	      (setq tops (cons
+			  (elint-make-top-form (read (current-buffer)) pos)
+			  tops))
+	    (end-of-file
+	     (goto-char pos)
+	     (end-of-line)
+	     (error "Missing ')' in top form: %s" (buffer-substring pos (point)))))
+	  ))
+      (nreverse tops))))
+
+(defun elint-find-next-top-form ()
+  "Find the next top form from point.
+Returns nil if there are no more forms, T otherwise."
+  (parse-partial-sexp (point) (point-max) nil t)
+  (not (eobp)))
+
+(defun elint-init-env (forms)
+  "Initialise the environment from FORMS."
+  (let ((env (elint-make-env))
+	form)
+    (while forms
+      (setq form (elint-top-form-form (car forms))
+	    forms (cdr forms))
+      (cond
+       ;; Add defined variable
+       ((memq (car form) '(defvar defconst defcustom))
+	(setq env (elint-env-add-var env (car (cdr form)))))
+       ;; Add function
+       ((memq (car form) '(defun defsubst))
+	(setq env (elint-env-add-func env (car (cdr form))
+				      (car (cdr (cdr form))))))
+       ;; Add macro, both as a macro and as a function
+       ((eq (car form) 'defmacro)
+	(setq env (elint-env-add-macro env (car (cdr form))
+				       (cons 'lambda
+					     (cdr (cdr form))))
+	      env (elint-env-add-func env (car (cdr form))
+				      (car (cdr (cdr form))))))
+
+       ;; Import variable definitions
+       ((eq (car form) 'require)
+	(let ((name (eval (car (cdr form))))
+	      (file (eval (car (cdr (cdr form))))))
+	  (setq env (elint-add-required-env env name file))))
+       ))
+    env))
+
+(defun elint-add-required-env (env name file)
+  "Augment ENV with the variables definied by feature NAME in FILE."
+  (condition-case nil
+      (let* ((libname (if (stringp file)
+			  file
+			(symbol-name name)))
+
+	     ;; First try to find .el files, then the raw name
+	     (lib1 (locate-library (concat libname ".el") t))
+	     (lib (if lib1 lib1 (locate-library libname t))))
+	;; Clear the messages :-/
+	(message nil)
+	(if lib
+	    (save-excursion
+	      (set-buffer (find-file-noselect lib))
+	      (elint-update-env)
+	      (setq env (elint-env-add-env env elint-buffer-env)))
+	  (error "dummy error...")))
+    (error
+     (ding)
+     (message "Can't get variables from require'd library %s" name)))
+  env)
+					
+(defun regexp-assoc (regexp alist)
+  "Search for a key matching REGEXP in ALIST."
+  (let ((res nil))
+    (while (and alist (not res))
+      (if (and (stringp (car (car alist)))
+	       (string-match regexp (car (car alist))))
+	  (setq res (car alist))
+	(setq alist (cdr alist))))
+    res))
+
+(defvar elint-top-form nil
+  "The currently linted top form, or nil.")
+
+(defvar elint-top-form-logged nil
+  "T if the currently linted top form has been mentioned in the log buffer.")
+
+(defun elint-top-form (form)
+  "Lint a top FORM."
+  (let ((elint-top-form form)
+	(elint-top-form-logged nil))
+    (elint-form (elint-top-form-form form) elint-buffer-env)))
+
+;;;
+;;; General form linting functions
+;;;
+
+(defconst elint-special-forms
+  '((let . elint-check-let-form)
+    (let* . elint-check-let-form)
+    (setq . elint-check-setq-form)
+    (quote . elint-check-quote-form)
+    (cond . elint-check-cond-form)
+    (lambda . elint-check-defun-form)
+    (function . elint-check-function-form)
+    (setq-default . elint-check-setq-form)
+    (defun . elint-check-defun-form)
+    (defsubst . elint-check-defun-form)
+    (defmacro . elint-check-defun-form)
+    (defvar . elint-check-defvar-form)
+    (defconst . elint-check-defvar-form)
+    (defcustom . elint-check-defvar-form)
+    (macro . elint-check-macro-form)
+    (condition-case . elint-check-condition-case-form))
+  "Functions to call when some special form should be linted.")
+								
+(defun elint-form (form env)
+  "Lint FORM in the environment ENV.
+The environment created by the form is returned."
+  (cond
+   ((consp form)
+    (let ((func (cdr (assq (car form) elint-special-forms))))
+      (if func
+	  ;; Special form
+	  (funcall func form env)
+
+	(let* ((func (car form))
+	       (args (elint-get-args func env))
+	       (argsok t))
+	  (cond
+	   ((eq args 'undefined)
+	    (setq argsok nil)
+	    (elint-error "Call to undefined function: %s" form))
+       
+	   ((eq args 'unknown) nil)
+	 
+	   (t (setq argsok (elint-match-args form args))))
+
+	  ;; Is this a macro?
+	  (if (elint-env-macrop env func)
+	      ;; Macro defined in buffer, expand it
+	      (if argsok
+		  (elint-form (macroexpand form (elint-env-macro-env env)) env)
+		env)
+
+	    (let ((fcode (if (symbolp func)
+			     (if (fboundp func)
+				 (indirect-function func)
+			       nil)
+			   func)))
+	      (if (and (listp fcode) (eq (car fcode) 'macro))
+		  ;; Macro defined outside buffer
+		  (if argsok
+		      (elint-form (macroexpand form) env)
+		    env)
+		;; Function, lint its parameters
+		(elint-forms (cdr form) env))))
+	  ))
+      ))
+   ((symbolp form)
+    ;; :foo variables are quoted
+    (if (and (/= (aref (symbol-name form) 0) ?:)
+	     (elint-unbound-variable form env))
+	(elint-warning "Reference to unbound symbol: %s" form))
+    env)
+
+   (t env)
+   ))
+
+(defun elint-forms (forms env)
+  "Lint the FORMS, accumulating an environment, starting with ENV."
+  ;; grumblegrumbletailrecursiongrumblegrumble
+  (while forms
+    (setq env (elint-form (car forms) env)
+	  forms (cdr forms)))
+  env)
+
+(defun elint-unbound-variable (var env)
+  "T if VAR is unbound in ENV."
+  (not (or (eq var nil)
+	   (eq var t)
+	   (elint-env-find-var env var)
+	   (memq var elint-standard-variables))))
+
+;;;
+;;; Function argument checking
+;;;
+
+(defun elint-match-args (arglist argpattern)
+  "Match ARGLIST against ARGPATTERN."
+
+  (let ((state 'all)
+	(al (cdr arglist))
+	(ap argpattern)
+	(ok t))
+    (while
+	(cond
+	 ((and (null al) (null ap)) nil)
+	 ((eq (car ap) '&optional)
+	  (setq state 'optional)
+	  (setq ap (cdr ap))
+	  t)
+	 ((eq (car ap) '&rest)
+	  nil)
+	 ((or (and (eq state 'all) (or (null al) (null ap)))
+	      (and (eq state 'optional) (and al (null ap))))
+	  (elint-error "Wrong number of args: %s, %s" arglist argpattern)
+	  (setq ok nil)
+	  nil)
+	 ((and (eq state 'optional) (null al))
+	  nil)
+	 (t (setq al (cdr al)
+		  ap (cdr ap))
+	    t)))
+    ok))
+
+(defun elint-get-args (func env)
+  "Find the args of FUNC in ENV.
+Returns `unknown' if we couldn't find arguments."
+  (let ((f (elint-env-find-func env func)))
+    (if f
+	(car (cdr f))
+      (if (symbolp func)
+	  (if (fboundp func)
+	      (let ((fcode (indirect-function func)))
+		(if (subrp fcode)
+		    (let ((args (get func 'elint-args)))
+		      (if args args 'unknown))
+		  (elint-find-args-in-code fcode)))
+	    'undefined)
+	(elint-find-args-in-code func)))))
+
+(defun elint-find-args-in-code (code)
+  "Extract the arguments from CODE.
+CODE can be a lambda expression, a macro, or byte-compiled code."
+  (cond
+   ((byte-code-function-p code)
+    (aref code 0))
+   ((and (listp code) (eq (car code) 'lambda))
+    (car (cdr code)))
+   ((and (listp code) (eq (car code) 'macro))
+    (elint-find-args-in-code (cdr code)))
+   (t 'unknown)))
+
+;;;
+;;; Functions to check some special forms
+;;;
+
+(defun elint-check-cond-form (form env)
+  "Lint a cond FORM in ENV."
+  (setq form (cdr form))
+  (while form
+    (if (consp (car form))
+	(elint-forms  (car form) env)
+      (elint-error "cond clause should be a list: %s" (car form)))
+    (setq form (cdr form)))
+  env)
+
+(defun elint-check-defun-form (form env)
+  "Lint a defun/defmacro/lambda FORM in ENV."
+  (setq form (if (eq (car form) 'lambda) (cdr form) (cdr (cdr form))))
+  (mapcar (function (lambda (p)
+		      (or (memq p '(&optional &rest))
+			  (setq env (elint-env-add-var env p)))
+		      ))
+	  (car form))
+  (elint-forms (cdr form) env))
+
+(defun elint-check-let-form (form env)
+  "Lint the let/let* FORM in ENV."
+  (let ((varlist (car (cdr form))))
+    (if (not varlist)
+	(progn
+	  (elint-error "Missing varlist in let: %s" form)
+	  env)
+
+      ;; Check for (let (a (car b)) ...) type of error
+      (if (and (= (length varlist) 2)
+	       (symbolp (car varlist))
+	       (listp (car (cdr varlist)))
+	       (fboundp (car (car (cdr varlist)))))
+	  (elint-warning "Suspect varlist: %s" form))
+
+      ;; Add variables to environment, and check the init values
+      (let ((newenv env))
+	(mapcar (function (lambda (s)
+			    (cond
+			     ((symbolp s)
+			      (setq newenv (elint-env-add-var newenv s)))
+			     ((and (consp s) (<= (length s) 2))
+			      (elint-form (car (cdr s))
+					  (if (eq (car form) 'let)
+					      env
+					    newenv))
+			      (setq newenv
+				    (elint-env-add-var newenv (car s))))
+			     (t (elint-error
+				 "Malformed `let' declaration: %s" s))
+			     )))
+		varlist)
+
+	;; Lint the body forms
+	(elint-forms (cdr (cdr form)) newenv)
+	))))
+
+(defun elint-check-setq-form (form env)
+  "Lint the setq FORM in ENV."
+  (or (= (mod (length form) 2) 1)
+      (elint-error "Missing value in setq: %s" form))
+
+  (let ((newenv env)
+	sym val)
+    (setq form (cdr form))
+    (while form
+      (setq sym (car form)
+	    val (car (cdr form))
+	    form (cdr (cdr form)))
+      (if (symbolp sym)
+	  (if (elint-unbound-variable sym newenv)
+	      (elint-warning "Setting previously unbound symbol: %s" sym))
+	(elint-error "Setting non-symbol in setq: %s" sym))
+      (elint-form val newenv)
+      (if (symbolp sym)
+	  (setq newenv (elint-env-add-var newenv sym))))
+    newenv))
+			
+(defun elint-check-defvar-form (form env)
+  "Lint the defvar/defconst FORM in ENV."
+  (if (or (= (length form) 2)
+	  (= (length form) 3)
+	  (and (= (length form) 4) (stringp (nth 3 form))))
+          (elint-env-add-global-var (elint-form (nth 2 form) env)
+			     (car (cdr form)))
+    (elint-error "Malformed variable declaration: %s" form)
+    env))
+   
+(defun elint-check-function-form (form env)
+  "Lint the function FORM in ENV."
+  (let ((func (car (cdr-safe form))))
+    (cond
+     ((symbolp func)
+      (or (elint-env-find-func env func)
+	  (fboundp func)
+	  (elint-warning "Reference to undefined function: %s" form))
+      env)
+     ((and (consp func) (memq (car func) '(lambda macro)))
+      (elint-form func env))
+     ((stringp func) env)
+     (t (elint-error "Not a function object: %s" form)
+	env)
+     )))
+
+(defun elint-check-quote-form (form env)
+  "Lint the quote FORM in ENV."
+  env)
+
+(defun elint-check-macro-form (form env)
+  "Check the macro FORM in ENV."
+  (elint-check-function-form (list (car form) (cdr form)) env))
+
+(defun elint-check-condition-case-form (form env)
+  "Check the condition-case FORM in ENV."
+  (let ((resenv env))
+    (if (< (length form) 3)
+	(elint-error "Malformed condition-case: %s" form)
+      (or (symbolp (car (cdr form)))
+	  (elint-warning "First parameter should be a symbol: %s" form))
+      (setq resenv (elint-form (nth 2 form) env))
+
+      (let ((newenv (elint-env-add-var env (car (cdr form))))
+	    (errforms (nthcdr 3 form))
+	    errlist)
+	(while errforms
+	  (setq errlist (car (car errforms)))
+	  (mapcar (function (lambda (s)
+			      (or (get s 'error-conditions)
+				  (get s 'error-message)
+				  (elint-warning
+				   "Not an error symbol in error handler: %s" s))))
+		  (cond
+		   ((symbolp errlist) (list errlist))
+		   ((listp errlist) errlist)
+		   (t (elint-error "Bad error list in error handler: %s"
+				   errlist)
+		      nil))
+		  )
+	  (elint-forms (cdr (car errforms)) newenv)
+	  (setq errforms (cdr errforms))
+	  )))
+    resenv))
+	
+;;;
+;;; Message functions
+;;;
+
+;; elint-error and elint-warning are identical, but they might change
+;; to reflect different seriousness of linting errors
+
+(defun elint-error (string &rest args)
+  "Report an linting error.
+STRING and ARGS are thrown on `format' to get the message."
+  (let ((errstr (apply 'format string args)))
+    (elint-log-message errstr)
+    ))
+    
+(defun elint-warning (string &rest args)
+  "Report an linting warning.
+STRING and ARGS are thrown on `format' to get the message."
+  (let ((errstr (apply 'format string args)))
+    (elint-log-message errstr)
+    ))
+
+(defun elint-log-message (errstr)
+  "Insert ERRSTR last in the lint log buffer."
+  (save-excursion
+    (set-buffer (elint-get-log-buffer))
+    (goto-char (point-max))
+    (or (bolp) (newline))
+
+    ;; Do we have to say where we are?
+    (if elint-top-form-logged
+	nil
+      (insert
+       (let* ((form (elint-top-form-form elint-top-form))
+	      (top (car form)))
+	 (cond
+	  ((memq top '(defun defsubst))
+	   (format "\n** function %s **\n" (car (cdr form))))
+	  ((eq top 'defmacro)
+	   (format "\n** macro %s **\n" (car (cdr form))))
+	  ((memq top '(defvar defconst))
+	   (format "\n** variable %s **\n" (car (cdr form))))
+	  (t "\n** top level expression **\n"))))
+      (setq elint-top-form-logged t))
+
+    (insert errstr)
+    (newline)))
+
+(defun elint-clear-log (&optional header)
+  "Clear the lint log buffer.
+Insert HEADER followed by a blank line if non-nil."
+  (save-excursion
+    (set-buffer (elint-get-log-buffer))
+    (erase-buffer)
+    (if header
+	(progn
+	  (insert header)
+	  (newline))
+      )))
+
+(defun elint-display-log ()
+  "Display the lint log buffer."
+  (let ((pop-up-windows t))
+    (display-buffer (elint-get-log-buffer))
+    (sit-for 0)))
+
+(defun elint-get-log-buffer ()
+  "Return a log buffer for elint."
+  (let ((buf (get-buffer elint-log-buffer)))
+    (if buf
+	buf
+      (let ((oldbuf (current-buffer)))
+	(prog1
+	    (set-buffer (get-buffer-create elint-log-buffer))
+	  (setq truncate-lines t)
+	  (set-buffer oldbuf)))
+      )))
+      
+;;;
+;;; Initializing code
+;;;
+   
+;;;###autoload
+(defun elint-initialize ()
+  "Initialize elint."
+  (interactive)
+  (mapcar (function (lambda (x)
+		      (or (not (symbolp (car x)))
+			  (eq (cdr x) 'unknown)
+			  (put (car x) 'elint-args (cdr x)))))
+	  (elint-find-builtin-args))
+  (mapcar (function (lambda (x)
+		      (put (car x) 'elint-args (cdr x))))
+	  elint-unknown-builtin-args))
+
+
+(defun elint-find-builtins ()
+  "Returns a list of all built-in functions."
+  (let ((subrs nil))
+    (mapatoms (lambda (s) (if (and (fboundp s) (subrp (symbol-function s)))
+			      (setq subrs (cons s subrs)))))
+    subrs
+    ))
+
+(defun elint-find-builtin-args (&optional list)
+  "Returns a list of the built-in functions and their arguments.
+
+If LIST is nil, call `elint-find-builtins' to get a list of all built-in
+functions, otherwise use LIST.
+
+Each functions is represented by a cons cell:
+\(function-symbol . args)
+If no documentation could be found args will be `unknown'."
+
+    (mapcar (function (lambda (f)
+			(let ((doc (documentation f t)))
+			  (if (and doc (string-match "\n\n\\((.*)\\)" doc))
+			      (read (match-string 1 doc))
+			    (cons f 'unknown))
+			  )))
+	    (if list list
+	      (elint-find-builtins))))
+
+;;;
+;;; Data
+;;;
+
+(defconst elint-standard-variables
+  '(abbrev-mode auto-fill-function buffer-auto-save-file-name
+     buffer-backed-up buffer-display-table buffer-file-format
+     buffer-file-name buffer-file-number buffer-file-truename
+     buffer-file-type buffer-invisibility-spec buffer-offer-save
+     buffer-read-only buffer-saved-size buffer-undo-list
+     cache-long-line-scans case-fold-search ctl-arrow comment-column
+     default-directory defun-prompt-regexp fill-column goal-column
+     left-margin local-abbrev-table local-write-file-hooks major-mode
+     mark-active mark-ring minor-modes mode-line-buffer-identification
+     mode-line-format mode-line-modified mode-line-process mode-name
+     overwrite-mode paragraph-separate paragraph-start
+     point-before-scroll require-final-newline selective-display
+     selective-display-ellipses tab-width truncate-lines vc-mode)
+  "Standard buffer local vars.")
+  
+(defconst elint-unknown-builtin-args
+  '((while test &rest forms)
+    (insert-before-markers-and-inherit &rest text)
+    (catch tag &rest body)
+    (and &rest args)
+    (funcall func &rest args)
+    (insert-string &rest args)
+    (insert &rest args)
+    (vconcat &rest args)
+    (run-hook-with-args hook &rest args)
+    (message-or-box string &rest args)
+    (save-window-excursion &rest body)
+    (append &rest args)
+    (logior &rest args)
+    (progn &rest body)
+    (insert-and-inherit &rest args)
+    (message-box string &rest args)
+    (prog2 x y &rest body)
+    (prog1 first &rest body)
+    (ml-provide-prefix-argument prefix form)
+    (insert-before-markers &rest args)
+    (call-process-region start end program &optional delete
+			 destination display &rest args)
+    (concat &rest args)
+    (vector &rest args)
+    (run-hook-with-args-until-success hook &rest args)
+    (track-mouse &rest body)
+    (unwind-protect bodyform &rest unwindforms)
+    (save-restriction &rest body)
+    (ml-prefix-argument-loop)
+    (quote arg)
+    (make-byte-code &rest args)
+    (or &rest args)
+    (cond &rest clauses)
+    (start-process name buffer program &rest args)
+    (run-hook-with-args-until-failure hook &rest args)
+    (if cond then &rest else)
+    (ml-if)
+    (apply function &rest args)
+    (format string &rest args)
+    (encode-time second minute hour day month year zone &rest args)
+    (min &rest args)
+    (logand &rest args)
+    (logxor &rest args)
+    (max &rest args)
+    (list &rest args)
+    (message string &rest args)
+    (defvar symbol init doc)
+    (call-process program &optional infile destination display &rest args)
+    (with-output-to-temp-buffer bufname &rest body)
+    (nconc &rest args)
+    (save-excursion &rest body)
+    (run-hooks &rest hooks)
+    (/ x y &rest zs)
+    (- x &rest y)
+    (+ &rest args)
+    (* &rest args)
+    (interactive &optional args))
+  "Those built-ins for which we can't find arguments.")
+
+(provide 'elint)
+
+;;; elint.el ends here
+