elint-log-buffer
"*Elint*" 55 "The buffer in which to log lint messages." 60 (defcustomelint-scan-preloaded t
61 "Non-nil means to scan `preloaded-file-list' when initializing. 62 Otherwise, just scan the DOC file for functions and variables. 63 This is faster, but less accurate, since it misses undocumented features. 64 This may result in spurious warnings about unknown functions, etc." 70 (defcustomelint-ignored-warnings nil
71 "If non-nil, a list of issue types that Elint should ignore. 72 This is useful if Elint has trouble understanding your code and 73 you need to suppress lots of spurious warnings. The valid list elements 74 are as follows, and suppress messages about the indicated features: 75 undefined-functions - calls to unknown functions 76 unbound-reference - reference to unknown variables 77 unbound-assignment - assignment to unknown variables 78 macro-expansions - failure to expand macros 79 empty-let - let-bindings with empty variable lists" 80 :type
'(choice (const :tag
"Don't suppress any warnings"nil
) 81 (repeat :tag
"List of issues to ignore" 82 (choice (constundefined-functions
83 :tag
"Calls to unknown functions") 84 (constunbound-reference
85 :tag
"Reference to unknown variables") 86 (constunbound-assignment
87 :tag
"Assignment to unknown variables") 88 (constmacro-expansion
89 :tag
"Failure to expand macros") 91 :tag
"Let-binding with empty varlist")))) 92 :safe
(lambda (value) (or (nullvalue
) 108 (defcustomelint-directory-skip-re
"\\(ldefs-boot.*\\|loaddefs\\)\\.el\\'" 109 "If nil, a regexp matching files to skip when linting a directory." 110 :type
'(choice (const :tag
"Lint all files"nil
) 111 (regexp :tag
"Regexp to skip")) 112 :safe
'string-or-null-p
120 (defconstelint-standard-variables
121 ;; Most of these are defined in C with no documentation. 122 ;; FIXME I don't see why they shouldn't just get doc-strings. 123 '(vc-modelocal-write-file-hooks activate-menubar-hook buffer-name-history
124coding-system-history extended-command-history
125yes-or-no-p-history
) 126 "Standard variables, excluding `elint-builtin-variables'. 127 These are variables that we cannot detect automatically for some reason.") 129 (defvarelint-builtin-variables nil
130 "List of built-in variables. Set by `elint-initialize'. 131 This is actually all those documented in the DOC file, which includes 132 built-in variables and those from dumped Lisp files.") 134 (defvarelint-autoloaded-variables nil
135 "List of `loaddefs.el' variables. Set by `elint-initialize'.") 137 (defvarelint-preloaded-env nil
138 "Environment defined by the preloaded (dumped) Lisp files. 139 Set by `elint-initialize', if `elint-scan-preloaded' is non-nil.") 141 (defconstelint-unknown-builtin-args
142 ;; encode-time allows extra arguments for use with decode-time. 143 ;; For some reason, some people seem to like to use them in other cases. 144 '((encode-timesecond minute hour day month year
&rest zone
)) 145 "Those built-ins for which we can't find arguments, if any.") 147 (defvarelint-extra-errors
'(file-lockedfile-supersession ftp-error
) 148 "Errors without `error-message' or `error-conditions' properties.") 150 (defconstelint-preloaded-skip-re
151 (regexp-opt '("loaddefs.el" "loadup.el" "cus-start" "language/" 152 "eucjp-ms" "mule-conf" "/characters" "/charprop" 154 "Regexp matching elements of `preloaded-file-list' to ignore. 155 We ignore them because they contain no definitions of use to Elint.") 161 (defsubstelint-make-top-form
(formpos
) 163 FORM is the form, and POS is the point where it starts in the buffer." 166 (defsubstelint-top-form-form
(top-form) 167 "Extract the form from a TOP-FORM." 170 (defsubstelint-top-form-pos
(top-form) 171 "Extract the position from a TOP-FORM." 178 (defsubstelint-make-env
() 179 "Create an empty environment." 180 (list (listnil
)nil nil
)) 182 (defsubstelint-env-add-env
(envnewenv
) 183 "Augment ENV with NEWENV. 184 None of them is modified, and the new env is returned." 185 (list (append (carenv
) (carnewenv
)) 186 (append (cadrenv
) (cadrnewenv
)) 187 (append (car (cdr (cdrenv
))) (car (cdr (cdrnewenv
)))))) 189 (defsubstelint-env-add-var
(envvar
) 190 "Augment ENV with the variable VAR. 191 The new environment is returned, the old is unmodified." 192 (cons (cons (listvar
) (carenv
)) (cdrenv
))) 194 (defsubstelint-env-add-global-var
(envvar
) 195 "Augment ENV with the variable VAR. 196 ENV is modified so VAR is seen everywhere. 198 (nconc (carenv
) (list (listvar
))) 201 (defsubstelint-env-find-var
(envvar
) 202 "Non-nil if ENV contains the variable VAR. 203 Actually, a list with VAR as a single element is returned." 204 (assqvar
(carenv
))) 206 (defsubstelint-env-add-func
(envfunc args
) 207 "Augment ENV with the function FUNC, which has the arguments ARGS. 208 The new environment is returned, the old is unmodified." 210 (cons (listfunc args
) (cadrenv
)) 211 (car (cdr (cdrenv
))))) 213 (defsubstelint-env-find-func
(envfunc
) 214 "Non-nil if ENV contains the function FUNC. 215 Actually, a list of (FUNC ARGS) is returned." 216 (assqfunc
(cadrenv
))) 218 (defsubstelint-env-add-macro
(envmacro def
) 219 "Augment ENV with the macro named MACRO. 220 DEF is the macro definition (a lambda expression or similar). 221 The new environment is returned, the old is unmodified." 224 (cons (consmacro def
) (car (cdr (cdrenv
)))))) 226 (defsubstelint-env-macro-env
(env) 227 "Return the macro environment of ENV. 228 This environment can be passed to `macroexpand'." 229 (car (cdr (cdrenv
)))) 231 (defsubstelint-env-macrop
(envmacro
) 232 "Non-nil if ENV contains MACRO." 233 (assqmacro
(elint-env-macro-envenv
))) 240 (defun elint-file (file) 241 "Lint the file FILE." 242 (interactive "fElint file: ") 243 (setqfile
(expand-file-namefile
)) 244 (orelint-builtin-variables
246 (let ((dir (file-name-directoryfile
))) 247 (let ((default-directorydir
)) 249 (elint-set-mode-linet
) 250 (with-current-bufferelint-log-buffer
251 (unless (string-equaldefault-directory dir
) 252 (elint-log-message (format-message "\f\nLeaving directory `%s'" 253default-directory
)t
) 254 (elint-log-message (format-message "Entering directory `%s'"dir
)t
) 255 (setqdefault-directory dir
)))) 256 (let ((str (format "Linting file %s"file
))) 257 (message "%s..."str
) 259 (elint-log-message (format "\f\n%s at %s"str
(current-time-string))t
)) 260 ;; elint-current-buffer clears log. 262 (insert-file-contentsfile
) 263 (let ((buffer-file-namefile
) 264 (max-lisp-eval-depth (max 1000max-lisp-eval-depth
))) 265 (with-syntax-tableemacs-lisp-mode-syntax-table
266 (mapc 'elint-top-form
(elint-update-env))))) 267 (elint-set-mode-line) 268 (message "%s...done"str
))) 270 ;; cf byte-recompile-directory. 272 (defun elint-directory (directory) 273 "Lint all the .el files in DIRECTORY. 274 A complicated directory may require a lot of memory." 275 (interactive "DElint directory: ") 276 (let ((elint-runningt
)) 277 (dolist (file (directory-filesdirectory t
)) 278 ;; Bytecomp has emacs-lisp-file-regexp. 279 (when (and (string-match "\\.el\\'"file
) 280 (file-readable-pfile
) 281 (not (auto-save-file-name-pfile
))) 282 (if (string-matchelint-directory-skip-re file
) 283 (message "Skipping file %s"file
) 284 (elint-filefile
))))) 285 (elint-set-mode-line)) 288 (defun elint-current-buffer () 289 "Lint the current buffer. 290 If necessary, this first calls `elint-initialize'." 292 (orelint-builtin-variables
294 (elint-clear-log (format "Linting %s" (or (buffer-file-name) 297 (elint-set-mode-linet
) 298 (mapc 'elint-top-form
(elint-update-env)) 299 ;; Tell the user we're finished. This is terribly kludgy: we set 300 ;; elint-top-form-logged so elint-log-message doesn't print the 301 ;; ** top form ** header... 302 (elint-set-mode-line) 303 (elint-log-message "\nLinting finished.\n"t
)) 307 (defun elint-defun () 308 "Lint the function at point. 309 If necessary, this first calls `elint-initialize'." 311 (orelint-builtin-variables
314 (or (beginning-of-defun) (error "Lint what?")) 316 (def (read (current-buffer)))) 319 (elint-top-form (elint-make-top-formdef pos
))))) 322 ;;; Top form functions 325 (defvarelint-buffer-env nil
326 "The environment of an elisp buffer. 327 Will be local in linted buffers.") 329 (defvarelint-buffer-forms nil
330 "The top forms in a buffer. 331 Will be local in linted buffers.") 333 (defvarelint-last-env-time nil
334 "The last time the buffers env was updated. 335 Is measured in buffer-modified-ticks and is local in linted buffers.") 337 ;; This is a minor optimization. It is local to every buffer, and so 338 ;; does not prevent recursive requires. It does not list the requires 340 (defvarelint-features nil
341 "List of all libraries this buffer has required, or that have been provided.") 343 (defun elint-update-env () 344 "Update the elint environment in the current buffer. 345 Don't do anything if the buffer hasn't been changed since this 346 function was called the last time. 348 (if (and (local-variable-p 'elint-buffer-env
(current-buffer)) 349 (local-variable-p 'elint-buffer-forms
(current-buffer)) 350 (local-variable-p 'elint-last-env-time
(current-buffer)) 351 (= (buffer-modified-tick)elint-last-env-time
)) 355 (set (make-local-variable 'elint-buffer-forms
) (elint-get-top-forms)) 356 (set (make-local-variable 'elint-features
)nil
) 357 (set (make-local-variable 'elint-buffer-env
) 358 (elint-init-envelint-buffer-forms
)) 359 (ifelint-preloaded-env
360 ;; FIXME: This doesn't do anything! Should we setq the result to 362 (elint-env-add-envelint-preloaded-env elint-buffer-env
)) 363 (set (make-local-variable 'elint-last-env-time
) (buffer-modified-tick)) 364elint-buffer-forms
)) 366 (defun elint-get-top-forms () 367 "Collect all the top forms in the current buffer." 370 (goto-char (point-min)) 371 (while (elint-find-next-top-form) 372 (let ((elint-current-pos (point))) 373 ;; non-list check could be here too. errors may be out of seq. 374 ;; quoted check cannot be elsewhere, since quotes skipped. 375 (if (looking-back "'" (1- (point))) 376 ;; Eg cust-print.el uses ' as a comment syntax. 377 (elint-warning "Skipping quoted form `%c%.20s...'"?
\' 378 (read (current-buffer))) 381 (elint-make-top-form (read (current-buffer)) 385 (goto-charelint-current-pos
) 386 (error "Missing `)' in top form: %s" 387 (buffer-substringelint-current-pos
388 (line-end-position)))))))) 391 (defun elint-find-next-top-form () 392 "Find the next top form from point. 393 Return nil if there are no more forms, t otherwise." 394 (parse-partial-sexp (point) (point-max)nil t
) 397 (defvarelint-env
) ; from elint-init-env 399 (defun elint-init-form (form) 400 "Process FORM, adding to ELINT-ENV if recognized." 402 ;; Eg nnmaildir seems to use [] as a form of comment syntax. 404 (elint-warning "Skipping non-list form `%s'"form
)) 405 ;; Add defined variable 406 ((memq (carform
) '(defvardefconst defcustom
)) 407 (setqelint-env
(elint-env-add-varelint-env
(cadrform
)))) 409 ((memq (carform
) '(defun defsubst)) 410 (setqelint-env
(elint-env-add-funcelint-env
(cadrform
) (nth 2form
)))) 411 ;; FIXME needs a handler to say second arg is not a variable when we come 413 ((eq (carform
) 'define-derived-mode
) 414 (setqelint-env
(elint-env-add-funcelint-env
(cadrform
) ()) 415elint-env
(elint-env-add-varelint-env
(cadrform
)) 416elint-env
(elint-env-add-varelint-env
417 (intern (format "%s-map" (cadrform
)))))) 418 ((eq (carform
) 'define-minor-mode
) 419 (setqelint-env
(elint-env-add-funcelint-env
(cadrform
) '(&optional arg
)) 421elint-env
(elint-env-add-varelint-env
(cadrform
)))) 422 ((and (eq (carform
) 'easy-menu-define
) 424 (setqelint-env
(elint-env-add-funcelint-env
(cadrform
) '(event)) 425elint-env
(elint-env-add-varelint-env
(cadrform
)))) 426 ;; FIXME it would be nice to check the autoloads are correct. 427 ((eq (carform
) 'autoload
) 428 (setqelint-env
(elint-env-add-funcelint-env
(cadr (cadrform
)) 'unknown
))) 429 ((eq (carform
) 'declare-function
) 430 (setqelint-env
(elint-env-add-func 431elint-env
(cadrform
) 432 (if (or (< (lengthform
) 4) 433 (eq (nth 3form
)t
) 434 (unless (stringp (nth 2form
)) 435 (elint-error "Malformed declaration for `%s'" 440 ((and (eq (carform
) 'defalias
) (listp (nth 2form
))) 441 ;; If the alias points to something already in the environment, 442 ;; add the alias to the environment with the same arguments. 443 ;; FIXME symbol-function, eg backquote.el? 444 (let ((def (elint-env-find-funcelint-env
(cadr (nth 2form
))))) 445 (setqelint-env
(elint-env-add-funcelint-env
(cadr (cadrform
)) 446 (ifdef
(cadrdef
) 'unknown
))))) 447 ;; Add macro, both as a macro and as a function 448 ((eq (carform
) 'defmacro
) 449 (setqelint-env
(elint-env-add-macroelint-env
(cadrform
) 450 (cons 'lambda
(cddrform
))) 451elint-env
(elint-env-add-funcelint-env
(cadrform
) (nth 2form
)))) 452 ((and (eq (carform
) 'put
) 454 (eq (car-safe (cadrform
)) 'quote
) 455 (equal (nth 2form
) '(quoteerror-conditions
))) 456 (set (make-local-variable 'elint-extra-errors
) 457 (cons (cadr (cadrform
))elint-extra-errors
))) 458 ((eq (carform
) 'provide
) 459 (add-to-list 'elint-features
(eval (cadrform
)))) 460 ;; Import variable definitions 461 ((memq (carform
) '(requirecc-require cc-require-when-compile
)) 462 (let ((name (eval (cadrform
))) 463 (file (eval (nth 2form
))) 464 (elint-doing-cl (bound-and-true-pelint-doing-cl
))) 465 (unless (memqname elint-features
) 466 (add-to-list 'elint-features name
) 467 ;; cl loads cl-macs in an opaque manner. 468 ;; Since cl-macs requires cl, we can just process cl-macs. 469 ;; FIXME: AFAIK, `cl' now behaves properly and does not need any 470 ;; special treatment any more. Can someone who understands this 471 ;; code confirm? --Stef 472 (and (eqname
'cl
) (notelint-doing-cl
) 473 ;; We need cl if elint-form is to be able to expand cl macros. 477elint-doing-cl t
)) ; blech 478 (setqelint-env
(elint-add-required-envelint-env name file
)))))) 481 (defun elint-init-env (forms) 482 "Initialize the environment from FORMS." 483 (let ((elint-env (elint-make-env)) 486 (setqform
(elint-top-form-form (carforms
)) 488 ;; FIXME eval-when-compile should be treated differently (macros). 489 ;; Could bind something that makes elint-init-form only check 491 (if (memq (car-safeform
) 492 '(eval-and-compileeval-when-compile progn prog1 prog2
494 (mapc 'elint-init-form
(cdrform
)) 495 (elint-init-formform
))) 498 (defun elint-add-required-env (envname file
) 499 "Augment ENV with the variables defined by feature NAME in FILE." 501 (let* ((libname (if (stringpfile
) 503 (symbol-namename
))) 505 ;; First try to find .el files, then the raw name 506 (lib1 (locate-library (concatlibname
".el")t
)) 507 (lib (orlib1
(locate-librarylibname t
)))) 508 ;; Clear the messages :-/ 509 ;; (Messes up the "Initializing elint..." message.) 512 (with-current-buffer (find-file-noselectlib
) 513 ;; FIXME this doesn't use a temp buffer, because it 514 ;; stores the result in buffer-local variables so that 517 (setqenv
(elint-env-add-envenv elint-buffer-env
))) 518 ;;; (with-temp-buffer 519 ;;; (insert-file-contents lib) 520 ;;; (with-syntax-table emacs-lisp-mode-syntax-table 521 ;;; (elint-update-env)) 522 ;;; (setq env (elint-env-add-env env elint-buffer-env)))) 523 ;;(message "%s" (format "Elint processed (require '%s)" name)) 524 (error "%s.el not found in load-path"libname
))) 526 (message "Can't get variables from require'd library %s: %s" 527name
(error-message-stringerr
)))) 530 (defvarelint-top-form nil
531 "The currently linted top form, or nil.") 533 (defvarelint-top-form-logged nil
534 "The value t if the currently linted top form has been mentioned in the log buffer.") 536 (defun elint-top-form (form) 538 (let ((elint-top-formform
) 539 (elint-top-form-loggednil
) 540 (elint-current-pos (elint-top-form-posform
))) 541 (elint-form (elint-top-form-formform
)elint-buffer-env
))) 544 ;;; General form linting functions 547 (defconstelint-special-forms
548 '((let. elint-check-let-form
) 549 (let*. elint-check-let-form
) 550 (setq. elint-check-setq-form
) 551 (quote. elint-check-quote-form
) 552 (function. elint-check-quote-form
) 553 (cond. elint-check-cond-form
) 554 (lambda. elint-check-defun-form
) 555 (function. elint-check-function-form
) 556 (setq-default. elint-check-setq-form
) 557 (defalias. elint-check-defalias-form
) 558 (defun. elint-check-defun-form
) 559 (defsubst. elint-check-defun-form
) 560 (defmacro. elint-check-defun-form
) 561 (defvar. elint-check-defvar-form
) 562 (defconst. elint-check-defvar-form
) 563 (defcustom. elint-check-defcustom-form
) 564 (macro. elint-check-macro-form
) 565 (condition-case. elint-check-condition-case-form
) 566 (if. elint-check-conditional-form
) 567 (when. elint-check-conditional-form
) 568 (unless. elint-check-conditional-form
) 569 (and. elint-check-conditional-form
) 570 (or. elint-check-conditional-form
)) 571 "Functions to call when some special form should be linted.") 573 (defun elint-form (formenv
&optional nohandler
) 574 "Lint FORM in the environment ENV. 575 Optional argument NOHANDLER non-nil means ignore `elint-special-forms'. 576 Returns the environment created by the form." 579 (let ((func (cdr (assq (carform
)elint-special-forms
)))) 580 (if (andfunc
(notnohandler
)) 582 (funcallfunc form env
) 584 (let* ((func (carform
)) 585 (args (elint-get-argsfunc env
)) 588 ((eqargs
'undefined
) 590 (or (memq 'undefined-functions elint-ignored-warnings
) 591 (elint-error "Call to undefined function: %s"func
))) 593 ((eqargs
'unknown
)nil
) 595 (t (setqargsok
(elint-match-argsform args
)))) 598 (if (elint-env-macropenv func
) 599 ;; Macro defined in buffer, expand it 601 ;; FIXME error if macro uses macro, eg bytecomp.el. 604 (macroexpandform
(elint-env-macro-envenv
))env
) 606 (or (memq 'macro-expansion elint-ignored-warnings
) 607 (elint-error "Elint failed to expand macro: %s"func
)) 611 (let ((fcode (if (symbolpfunc
) 613 (indirect-functionfunc
)) 615 (if (and (listpfcode
) (eq (carfcode
) 'macro
)) 616 ;; Macro defined outside buffer 618 (elint-form (macroexpandform
)env
) 620 ;; Function, lint its parameters 621 (elint-forms (cdrform
)env
)))))))) 623 ;; :foo variables are quoted 624 (and (/= (aref (symbol-nameform
) 0)?
:) 625 (not (memq 'unbound-reference elint-ignored-warnings
)) 626 (elint-unbound-variableform env
) 627 (elint-warning "Reference to unbound symbol: %s"form
)) 632 (defun elint-forms (formsenv
) 633 "Lint the FORMS, accumulating an environment, starting with ENV." 634 ;; grumblegrumbletailrecursiongrumblegrumble 636 (dolist (fforms env
) 637 (setqenv
(elint-formf env
))) 639 (elint-error "Elint failed to parse form: %s"forms
) 642 (defvarelint-bound-variable nil
643 "Name of a temporarily bound symbol.") 645 (defun elint-unbound-variable (varenv
) 646 "Return t if VAR is unbound in ENV." 647 ;; #1063 suggests adding (symbol-file var) here, but I don't think 648 ;; this is right, because it depends on what files you happen to have 649 ;; loaded at the time, which might not be the same when the code runs. 650 ;; It also suggests adding: 651 ;; (numberp (get var 'variable-documentation)) 652 ;; (numberp (cdr-safe (get var 'variable-documentation))) 653 ;; but this is not needed now elint-scan-doc-file exists. 654 (not (or (memqvar
'(nilt
)) 655 (eqvar elint-bound-variable
) 656 (elint-env-find-varenv var
) 657 (memqvar elint-builtin-variables
) 658 (memqvar elint-autoloaded-variables
) 659 (memqvar elint-standard-variables
)))) 662 ;;; Function argument checking 665 (defun elint-match-args (arglistargpattern
) 666 "Match ARGLIST against ARGPATTERN." 673 ((and (nullal
) (nullap
))nil
) 674 ((eq (carap
) '&optional
) 675 (setqstate
'optional
) 678 ((eq (carap
) '&rest
) 680 ((or (and (eqstate
'all
) (or (nullal
) (nullap
))) 681 (and (eqstate
'optional
) (andal
(nullap
)))) 682 (elint-error "Wrong number of args: %s, %s"arglist argpattern
) 685 ((and (eqstate
'optional
) (nullal
)) 687 (t (setqal
(cdral
) 692 (defvarelint-bound-function nil
693 "Name of a temporarily bound function symbol.") 695 (defun elint-get-args (funcenv
) 696 "Find the args of FUNC in ENV. 697 Returns `unknown' if we couldn't find arguments." 698 (let ((f (elint-env-find-funcenv func
))) 702 (if (eqfunc elint-bound-function
) 705 (let ((fcode (indirect-functionfunc
))) 707 ;; FIXME builtins with no args have args = nil. 708 (or (getfunc
'elint-args
) 'unknown
) 709 (elint-find-args-in-codefcode
))) 711 (elint-find-args-in-codefunc
))))) 713 (defun elint-find-args-in-code (code) 714 "Extract the arguments from CODE. 715 CODE can be a lambda expression, a macro, or byte-compiled code." 716 (let ((args (help-function-arglistcode
))) 717 (if (listpargs
)args
'unknown
))) 720 ;;; Functions to check some special forms 723 (defun elint-check-cond-form (formenv
) 724 "Lint a cond FORM in ENV." 725 (dolist (f (cdrform
)) 727 (let ((test (carf
))) 728 (cond ((equaltest
'(featurep (quotexemacs
)))) 729 ((equaltest
'(not (featurep (quoteemacs
))))) 730 ;; FIXME (and (boundp 'foo) 731 ((and (eq (car-safetest
) 'fboundp
) 733 (eq (car-safe (cadrtest
)) 'quote
)) 734 (let ((elint-bound-function (cadr (cadrtest
)))) 735 (elint-formsf env
))) 736 ((and (eq (car-safetest
) 'boundp
) 738 (eq (car-safe (cadrtest
)) 'quote
)) 739 (let ((elint-bound-variable (cadr (cadrtest
)))) 740 (elint-formsf env
))) 741 (t (elint-formsf env
)))) 742 (elint-error "cond clause should be a list: %s"f
))) 745 (defun elint-check-defun-form (formenv
) 746 "Lint a defun/defmacro/lambda FORM in ENV." 747 (setqform
(if (eq (carform
) 'lambda
) (cdrform
) (cddrform
))) 749 (or (memqp
'(&optional
&rest
)) 750 (setqenv
(elint-env-add-varenv p
)))) 752 (elint-forms (cdrform
)env
)) 754 (defun elint-check-defalias-form (formenv
) 755 "Lint a defalias FORM in ENV." 756 (let ((alias (cadrform
)) 757 (target (nth 2form
))) 758 (and (eq (car-safealias
) 'quote
) 759 (eq (car-safetarget
) 'quote
) 760 (eq (elint-get-args (cadrtarget
)env
) 'undefined
) 761 (elint-warning "Alias `%s' has unknown target `%s'" 762 (cadralias
) (cadrtarget
)))) 763 (elint-formform env t
)) 765 (defun elint-check-let-form (formenv
) 766 "Lint the let/let* FORM in ENV." 767 (let ((varlist (cadrform
))) 769 (if (> (lengthform
) 2) 770 ;; An empty varlist is not really an error. Eg some cl macros 771 ;; can expand to such a form. 773 (or (memq 'empty-let elint-ignored-warnings
) 774 (elint-warning "Empty varlist in let: %s"form
)) 775 ;; Lint the body forms 776 (elint-forms (cddrform
)env
)) 777 (elint-error "Malformed let: %s"form
) 779 ;; Check for (let (a (car b)) ...) type of error 780 (if (and (= (lengthvarlist
) 2) 781 (symbolp (carvarlist
)) 782 (listp (car (cdrvarlist
))) 783 (fboundp (car (car (cdrvarlist
))))) 784 (elint-warning "Suspect varlist: %s"form
)) 785 ;; Add variables to environment, and check the init values 790 (setqnewenv
(elint-env-add-varnewenv s
))) 791 ((and (consps
) (<= (lengths
) 2)) 792 (elint-form (cadrs
) 793 (if (eq (carform
) 'let
) 797 (elint-env-add-varnewenv
(cars
)))) 799 "Malformed `let' declaration: %s"s
)))) 802 ;; Lint the body forms 803 (elint-forms (cddrform
)newenv
))))) 805 (defun elint-check-setq-form (formenv
) 806 "Lint the setq FORM in ENV." 807 (or (= (mod (lengthform
) 2) 1) 808 ;; (setq foo) is valid and equivalent to (setq foo nil). 809 (elint-warning "Missing value in setq: %s"form
)) 812 (setqform
(cdrform
)) 814 (setqsym
(carform
) 815val
(car (cdrform
)) 816form
(cdr (cdrform
))) 818 (and (not (memq 'unbound-assignment elint-ignored-warnings
)) 819 (elint-unbound-variablesym newenv
) 820 (elint-warning "Setting previously unbound symbol: %s"sym
)) 821 (elint-error "Setting non-symbol in setq: %s"sym
)) 822 (elint-formval newenv
) 824 (setqnewenv
(elint-env-add-varnewenv sym
)))) 827 (defun elint-check-defvar-form (formenv
) 828 "Lint the defvar/defconst FORM in ENV." 829 (if (or (= (lengthform
) 2) 831 ;; Eg the defcalcmodevar macro can expand with a nil doc-string. 832 (and (= (lengthform
) 4) (string-or-null-p (nth 3form
)))) 833 (elint-env-add-global-var (elint-form (nth 2form
)env
) 835 (elint-error "Malformed variable declaration: %s"form
) 838 (defun elint-check-defcustom-form (formenv
) 839 "Lint the defcustom FORM in ENV." 840 (if (and (> (lengthform
) 3) 841 ;; even no. of keyword/value args ? 842 (zerop (logand (lengthform
) 1))) 843 (elint-env-add-global-var (elint-form (nth 2form
)env
) 845 (elint-error "Malformed variable declaration: %s"form
) 848 (defun elint-check-function-form (formenv
) 849 "Lint the function FORM in ENV." 850 (let ((func (car (cdr-safeform
)))) 853 (or (elint-env-find-funcenv func
) 854 ;; FIXME potentially bogus, since it uses the current 855 ;; environment rather than a clean one. 857 (elint-warning "Reference to undefined function: %s"form
)) 859 ((and (conspfunc
) (memq (carfunc
) '(lambdamacro
))) 860 (elint-formfunc env
)) 861 ((stringpfunc
)env
) 862 (t (elint-error "Not a function object: %s"form
) 865 (defun elint-check-quote-form (formenv
) 866 "Lint the quote FORM in ENV." 869 (defun elint-check-macro-form (formenv
) 870 "Check the macro FORM in ENV." 871 (elint-check-function-form (list (carform
) (cdrform
))env
)) 873 (defun elint-check-condition-case-form (formenv
) 874 "Check the `condition-case' FORM in ENV." 876 (if (< (lengthform
) 3) 877 (elint-error "Malformed condition-case: %s"form
) 878 (or (symbolp (cadrform
)) 879 (elint-warning "First parameter should be a symbol: %s"form
)) 880 (setqresenv
(elint-form (nth 2form
)env
)) 881 (let ((newenv (elint-env-add-varenv
(cadrform
))) 883 (dolist (err (nthcdr 3form
)) 884 (setqerrlist
(carerr
)) 886 (or (gets
'error-conditions
) 887 (gets
'error-message
) 888 (memqs elint-extra-errors
) 890 "Not an error symbol in error handler: %s"s
))) 892 ((symbolperrlist
) (listerrlist
)) 893 ((listperrlist
)errlist
) 894 (t (elint-error "Bad error list in error handler: %s" 897 (elint-forms (cdrerr
)newenv
)))) 900 ;; For the featurep parts, an alternative is to have 901 ;; elint-get-top-forms skip the irrelevant branches. 902 (defun elint-check-conditional-form (formenv
) 903 "Check the when/unless/and/or FORM in ENV. 904 Does basic handling of `featurep' tests." 905 (let ((func (carform
)) 908 ;; Misses things like (and t (featurep 'xemacs)) 909 ;; Check byte-compile-maybe-guarded. 910 (cond ((and (memqfunc
'(whenand
)) 911 (eq (car-safetest
) 'boundp
) 913 (eq (car-safe (cadrtest
)) 'quote
)) 914 ;; Cf elint-check-let-form, which modifies the whole ENV. 915 (let ((elint-bound-variable (cadr (cadrtest
)))) 916 (elint-formform env t
))) 917 ((and (memqfunc
'(whenand
)) 918 (eq (car-safetest
) 'fboundp
) 920 (eq (car-safe (cadrtest
)) 'quote
)) 921 (let ((elint-bound-function (cadr (cadrtest
)))) 922 (elint-formform env t
))) 923 ;; Let's not worry about (if (not (boundp... 924 ((and (eqfunc
'if
) 925 (eq (car-safetest
) 'boundp
) 927 (eq (car-safe (cadrtest
)) 'quote
)) 928 (let ((elint-bound-variable (cadr (cadrtest
)))) 929 (elint-form (nth 2form
)env
)) 930 (dolist (f (nthcdr 3form
)) 931 (elint-formf env
))) 932 ((and (eqfunc
'if
) 933 (eq (car-safetest
) 'fboundp
) 935 (eq (car-safe (cadrtest
)) 'quote
)) 936 (let ((elint-bound-function (cadr (cadrtest
)))) 937 (elint-form (nth 2form
)env
)) 938 (dolist (f (nthcdr 3form
)) 939 (elint-formf env
))) 940 ((and (memqfunc
'(whenand
)) ; skip all 942 (membertest
'((featurep (quotexemacs
)) 943 (not (featurep (quoteemacs
))))) 944 (and (eq (car-safetest
) 'and
) 945 (equal (car-safe (cdrtest
)) 946 '(featurep (quotexemacs
))))))) 947 ((and (memqfunc
'(unlessor
)) 948 (equaltest
'(featurep (quoteemacs
))))) 949 ((and (eqfunc
'if
) 950 (or (nulltest
) ; eg custom-browse-insert-prefix 951 (membertest
'((featurep (quotexemacs
)) 952 (not (featurep (quoteemacs
))))) 953 (and (eq (car-safetest
) 'and
) 954 (equal (car-safe (cdrtest
)) 955 '(featurep (quotexemacs
)))))) 956 (dolist (f (nthcdr 3form
)) 957 (elint-formf env
))) ; lint the else branch 958 ((and (eqfunc
'if
) 959 (equaltest
'(featurep (quoteemacs
)))) 960 (elint-form (nth 2form
)env
)) ; lint the if branch 961 ;; Process conditional as normal, without handler. 963 (elint-formform env t
)))) 967 ;;; Message functions 970 (defvarelint-current-pos
) ; dynamically bound in elint-top-form 972 (defun elint-log (typestring args
) 973 (elint-log-message (format "%s:%d:%s: %s" 974 (let ((f (buffer-file-name))) 976 (file-name-nondirectoryf
) 978 (if (boundp 'elint-current-pos
) 980 (goto-charelint-current-pos
) 981 (1+ (count-lines (point-min) 982 (line-beginning-position)))) 983 0) ; unknown position 985 (apply #'format-message string args
)))) 987 (defun elint-error (string &rest args
) 988 "Report a linting error. 989 STRING and ARGS are thrown on `format' to get the message." 990 (elint-log "Error"string args
)) 992 (defun elint-warning (string &rest args
) 993 "Report a linting warning. 995 (elint-log "Warning"string args
)) 997 (defun elint-output (string) 998 "Print or insert STRING, depending on value of `noninteractive'." 1000 (message "%s"string
) 1001 (insertstring
"\n"))) 1003 (defun elint-log-message (errstr &optional top
) 1004 "Insert ERRSTR last in the lint log buffer. 1005 Optional argument TOP non-nil means pretend `elint-top-form-logged' is non-nil." 1006 (with-current-buffer (elint-get-log-buffer) 1007 (goto-char (point-max)) 1008 (let ((inhibit-read-onlyt
)) 1009 (or (bolp) (newline)) 1010 ;; Do we have to say where we are? 1011 (unless (orelint-top-form-logged top
) 1012 (let* ((form (elint-top-form-formelint-top-form
)) 1015 ((memqtop
'(defun defsubst)) 1016 (format "\nIn function %s:" (cadrform
))) 1017 ((eqtop
'defmacro
) 1018 (format "\nIn macro %s:" (cadrform
))) 1019 ((memqtop
'(defvardefconst
)) 1020 (format "\nIn variable %s:" (cadrform
))) 1021 (t "\nIn top level expression:")))) 1022 (setqelint-top-form-logged t
)) 1023 (elint-outputerrstr
)))) 1025 (defun elint-clear-log (&optional header
) 1026 "Clear the lint log buffer. 1027 Insert HEADER followed by a blank line if non-nil." 1028 (let ((dirdefault-directory
)) 1029 (with-current-buffer (elint-get-log-buffer) 1030 (setqdefault-directory dir
) 1031 (let ((inhibit-read-onlyt
)) 1033 (ifheader
(insertheader
"\n")))))) 1035 (defun elint-display-log () 1036 "Display the lint log buffer." 1037 (let ((pop-up-windowst
)) 1038 (display-buffer (elint-get-log-buffer)) 1041 (defvarelint-running
) 1043 (defun elint-set-mode-line (&optional on
) 1044 "Set the mode-line-process of the Elint log buffer." 1045 (with-current-buffer (elint-get-log-buffer) 1046 (and (eqmajor-mode
'compilation-mode
) 1047 (setqmode-line-process
1048 (list (if (oron
(bound-and-true-pelint-running
)) 1049 (propertize ":run" 'face
'compilation-warning
) 1050 (propertize ":finished" 'face
'compilation-info
))))))) 1052 (defun elint-get-log-buffer () 1053 "Return a log buffer for elint." 1054 (or (get-bufferelint-log-buffer
) 1055 (with-current-buffer (get-buffer-createelint-log-buffer
) 1056 (or (eqmajor-mode
'compilation-mode
) 1058 (setqbuffer-undo-list t
) 1062 ;;; Initializing code 1065 (defun elint-put-function-args (funcargs
) 1066 "Mark function FUNC as having argument list ARGS." 1067 (and (symbolpfunc
) 1069 (not (eqargs
'unknown
)) 1070 (putfunc
'elint-args args
))) 1073 (defun elint-initialize (&optional reinit
) 1075 If elint is already initialized, this does nothing, unless 1076 optional prefix argument REINIT is non-nil." 1078 (if (andelint-builtin-variables
(notreinit
)) 1079 (message "Elint is already initialized") 1080 (message "Initializing elint...") 1081 (setqelint-builtin-variables
(elint-scan-doc-file) 1082elint-autoloaded-variables
(elint-find-autoloaded-variables)) 1083 (mapc (lambda (x) (elint-put-function-args (carx
) (cdrx
))) 1084 (elint-find-builtin-args)) 1085 (ifelint-unknown-builtin-args
1086 (mapc (lambda (x) (elint-put-function-args (carx
) (cdrx
))) 1087elint-unknown-builtin-args
)) 1088 (whenelint-scan-preloaded
1089 (dolist (libpreloaded-file-list
) 1090 ;; Skip files that contain nothing of use to us. 1091 (unless (string-matchelint-preloaded-skip-re lib
) 1092 (setqelint-preloaded-env
1093 (elint-add-required-envelint-preloaded-env nil lib
))))) 1094 (message "Initializing elint...done"))) 1097 ;; This includes all the built-in and dumped things with documentation. 1098 (defun elint-scan-doc-file () 1099 "Scan the DOC file for function and variables. 1100 Marks the function with their arguments, and returns a list of variables." 1101 ;; Cribbed from help-fns.el. 1102 (let ((docbuf " *DOC*") 1105 (if (get-bufferdocbuf
) 1107 (set-bufferdocbuf
) 1108 (goto-char (point-min))) 1109 (set-buffer (get-buffer-createdocbuf
)) 1110 (insert-file-contents-literally 1111 (expand-file-nameinternal-doc-file-name doc-directory
))) 1112 (while (re-search-forward "\x1f\\([VF]\\)"nil t
) 1113 (when (setqsym
(intern-soft (buffer-substring (point) 1114 (line-end-position)))) 1115 (if (string-equal (match-string 1) "V") 1116 ;; Excludes platform-specific stuff not relevant to the 1117 ;; running platform. 1118 (if (boundpsym
) (setqvars
(conssym vars
))) 1120 (when (fboundpsym
) 1121 (when (re-search-forward "\\(^(fn.*)\\)?\x1f"nil t
) 1123 ;; FIXME distinguish no args from not found. 1124 (and (setqargs
(match-string 1)) 1128 (replace-regexp-in-string "^(fn ?" "("args
)))) 1129 (elint-put-function-argssym args
)))))))) 1132 (defun elint-find-autoloaded-variables () 1133 "Return a list of all autoloaded variables." 1136 (insert-file-contents (locate-library "loaddefs.el")) 1137 (while (re-search-forward "^(defvar \\([[:alnum:]_-]+\\)"nil t
) 1138 (and (setqvar
(intern-soft (match-string 1))) 1140 (setqvars
(consvar vars
))))) 1143 (defun elint-find-builtins () 1144 "Return a list of all built-in functions." 1146 (mapatoms (lambda (s) (and (subrp (symbol-functions
)) 1150 (defun elint-find-builtin-args (&optional list
) 1151 "Return a list of the built-in functions and their arguments. 1152 If LIST is nil, call `elint-find-builtins' to get a list of all built-in 1153 functions, otherwise use LIST. 1155 Each function is represented by a cons cell: 1156 \(function-symbol . args) 1157 If no documentation could be found args will be `unknown'." 1159 (let ((doc (documentationf t
))) 1161 (string-match "\n\n(fn\\(.*)\\)\\'"doc
) 1163 ;; "BODY...)" -> "&rest BODY)". 1164 (read (replace-regexp-in-string 1165 "\\([^ ]+\\)\\.\\.\\.)\\'" "&rest \\1)" 1166 (format "(%s %s"f
(match-string 1doc
))t
)))) 1167 (consf
'unknown
)))) 1168 (orlist
(elint-find-builtins)))) 1172 ;;; elint.el ends hereRetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4