diff options
| author | Philipp Stephani | 2016-12-30 18:00:54 +0100 |
|---|---|---|
| committer | Philipp Stephani | 2016-12-31 17:30:46 +0100 |
| commit | 4bbd5424a2290ab4bd88c0af602b7aa7d53a407e (patch) | |
| tree | c6928916fdb29df58d509e519953adc1fe21f6a3 | |
| parent | 8a165813cb9321a8979ac6d98530f5e4392fb879 (diff) | |
| download | emacs-4bbd5424a2290ab4bd88c0af602b7aa7d53a407e.tar.gz emacs-4bbd5424a2290ab4bd88c0af602b7aa7d53a407e.zip | |
Checkdoc: use syntax functions instead of regex
In checkdoc.el, get rid of the error-prone regex to find definition
forms, and use existing syntax-based navigation functions instead.
This fixes a corner case with one-argument `defvar' forms.
* lisp/emacs-lisp/checkdoc.el (checkdoc--next-docstring): New function.
(checkdoc-next-docstring, checkdoc-defun): Use it.
* test/lisp/emacs-lisp/checkdoc-tests.el (checkdoc-tests--next-docstring):
Add unit test.
| -rw-r--r-- | lisp/emacs-lisp/checkdoc.el | 59 | ||||
| -rw-r--r-- | test/lisp/emacs-lisp/checkdoc-tests.el | 13 |
2 files changed, 44 insertions, 28 deletions
diff --git a/lisp/emacs-lisp/checkdoc.el b/lisp/emacs-lisp/checkdoc.el index 2c8bc020d38..55978ddd384 100644 --- a/lisp/emacs-lisp/checkdoc.el +++ b/lisp/emacs-lisp/checkdoc.el | |||
| @@ -294,12 +294,6 @@ problem discovered. This is useful for adding additional checks.") | |||
| 294 | (defvar checkdoc-diagnostic-buffer "*Style Warnings*" | 294 | (defvar checkdoc-diagnostic-buffer "*Style Warnings*" |
| 295 | "Name of warning message buffer.") | 295 | "Name of warning message buffer.") |
| 296 | 296 | ||
| 297 | (defvar checkdoc-defun-regexp | ||
| 298 | "^(def\\(un\\|var\\|custom\\|macro\\|const\\|subst\\|advice\\)\ | ||
| 299 | \\s-+\\(\\(\\sw\\|\\s_\\)+\\)[ \t\n]*" | ||
| 300 | "Regular expression used to identify a defun. | ||
| 301 | A search leaves the cursor in front of the parameter list.") | ||
| 302 | |||
| 303 | (defcustom checkdoc-verb-check-experimental-flag t | 297 | (defcustom checkdoc-verb-check-experimental-flag t |
| 304 | "Non-nil means to attempt to check the voice of the doc string. | 298 | "Non-nil means to attempt to check the voice of the doc string. |
| 305 | This check keys off some words which are commonly misused. See the | 299 | This check keys off some words which are commonly misused. See the |
| @@ -938,13 +932,31 @@ is the starting location. If this is nil, `point-min' is used instead." | |||
| 938 | (defun checkdoc-next-docstring () | 932 | (defun checkdoc-next-docstring () |
| 939 | "Move to the next doc string after point, and return t. | 933 | "Move to the next doc string after point, and return t. |
| 940 | Return nil if there are no more doc strings." | 934 | Return nil if there are no more doc strings." |
| 941 | (if (not (re-search-forward checkdoc-defun-regexp nil t)) | 935 | (let (found) |
| 942 | nil | 936 | (while (and (not (setq found (checkdoc--next-docstring))) |
| 943 | ;; search drops us after the identifier. The next sexp is either | 937 | (beginning-of-defun -1))) |
| 944 | ;; the argument list or the value of the variable. skip it. | 938 | found)) |
| 945 | (forward-sexp 1) | 939 | |
| 946 | (skip-chars-forward " \n\t") | 940 | (defun checkdoc--next-docstring () |
| 947 | t)) | 941 | "When looking at a definition with a doc string, find it. |
| 942 | Move to the next doc string after point, and return t. When not | ||
| 943 | looking at a definition containing a doc string, return nil and | ||
| 944 | don't move point." | ||
| 945 | (pcase (save-excursion (condition-case nil | ||
| 946 | (read (current-buffer)) | ||
| 947 | ;; Conservatively skip syntax errors. | ||
| 948 | (invalid-read-syntax))) | ||
| 949 | (`(,(or 'defun 'defvar 'defcustom 'defmacro 'defconst 'defsubst 'defadvice) | ||
| 950 | ,(pred symbolp) | ||
| 951 | ;; Require an initializer, i.e. ignore single-argument `defvar' | ||
| 952 | ;; forms, which never have a doc string. | ||
| 953 | ,_ . ,_) | ||
| 954 | (down-list) | ||
| 955 | ;; Skip over function or macro name, symbol to be defined, and | ||
| 956 | ;; initializer or argument list. | ||
| 957 | (forward-sexp 3) | ||
| 958 | (skip-chars-forward " \n\t") | ||
| 959 | t))) | ||
| 948 | 960 | ||
| 949 | ;;;###autoload | 961 | ;;;###autoload |
| 950 | (defun checkdoc-comments (&optional take-notes) | 962 | (defun checkdoc-comments (&optional take-notes) |
| @@ -1027,21 +1039,12 @@ space at the end of each line." | |||
| 1027 | (interactive) | 1039 | (interactive) |
| 1028 | (save-excursion | 1040 | (save-excursion |
| 1029 | (beginning-of-defun) | 1041 | (beginning-of-defun) |
| 1030 | (if (not (looking-at checkdoc-defun-regexp)) | 1042 | (when (checkdoc--next-docstring) |
| 1031 | ;; I found this more annoying than useful. | ||
| 1032 | ;;(if (not no-error) | ||
| 1033 | ;; (message "Cannot check this sexp's doc string.")) | ||
| 1034 | nil | ||
| 1035 | ;; search drops us after the identifier. The next sexp is either | ||
| 1036 | ;; the argument list or the value of the variable. skip it. | ||
| 1037 | (goto-char (match-end 0)) | ||
| 1038 | (forward-sexp 1) | ||
| 1039 | (skip-chars-forward " \n\t") | ||
| 1040 | (let* ((checkdoc-spellcheck-documentation-flag | 1043 | (let* ((checkdoc-spellcheck-documentation-flag |
| 1041 | (car (memq checkdoc-spellcheck-documentation-flag | 1044 | (car (memq checkdoc-spellcheck-documentation-flag |
| 1042 | '(defun t)))) | 1045 | '(defun t)))) |
| 1043 | (beg (save-excursion (beginning-of-defun) (point))) | 1046 | (beg (save-excursion (beginning-of-defun) (point))) |
| 1044 | (end (save-excursion (end-of-defun) (point)))) | 1047 | (end (save-excursion (end-of-defun) (point)))) |
| 1045 | (dolist (fun (list #'checkdoc-this-string-valid | 1048 | (dolist (fun (list #'checkdoc-this-string-valid |
| 1046 | (lambda () (checkdoc-message-text-search beg end)) | 1049 | (lambda () (checkdoc-message-text-search beg end)) |
| 1047 | (lambda () (checkdoc-rogue-space-check-engine beg end)))) | 1050 | (lambda () (checkdoc-rogue-space-check-engine beg end)))) |
| @@ -1049,8 +1052,8 @@ space at the end of each line." | |||
| 1049 | (if msg (if no-error | 1052 | (if msg (if no-error |
| 1050 | (message "%s" (checkdoc-error-text msg)) | 1053 | (message "%s" (checkdoc-error-text msg)) |
| 1051 | (user-error "%s" (checkdoc-error-text msg)))))) | 1054 | (user-error "%s" (checkdoc-error-text msg)))))) |
| 1052 | (if (called-interactively-p 'interactive) | 1055 | (if (called-interactively-p 'interactive) |
| 1053 | (message "Checkdoc: done.")))))) | 1056 | (message "Checkdoc: done.")))))) |
| 1054 | 1057 | ||
| 1055 | ;;; Ispell interface for forcing a spell check | 1058 | ;;; Ispell interface for forcing a spell check |
| 1056 | ;; | 1059 | ;; |
diff --git a/test/lisp/emacs-lisp/checkdoc-tests.el b/test/lisp/emacs-lisp/checkdoc-tests.el index 18b5a499e08..02db88c17e2 100644 --- a/test/lisp/emacs-lisp/checkdoc-tests.el +++ b/test/lisp/emacs-lisp/checkdoc-tests.el | |||
| @@ -37,4 +37,17 @@ | |||
| 37 | (insert "(defun foo())") | 37 | (insert "(defun foo())") |
| 38 | (should-error (checkdoc-defun) :type 'user-error))) | 38 | (should-error (checkdoc-defun) :type 'user-error))) |
| 39 | 39 | ||
| 40 | (ert-deftest checkdoc-tests--next-docstring () | ||
| 41 | "Checks that the one-argument form of `defvar' works. | ||
| 42 | See the comments in Bug#24998." | ||
| 43 | (with-temp-buffer | ||
| 44 | (emacs-lisp-mode) | ||
| 45 | (insert "(defvar foo) | ||
| 46 | \(defvar foo bar \"baz\") | ||
| 47 | \(require 'foo)") | ||
| 48 | (goto-char (point-min)) | ||
| 49 | (should (checkdoc-next-docstring)) | ||
| 50 | (should (looking-at-p "\"baz\")")) | ||
| 51 | (should-not (checkdoc-next-docstring)))) | ||
| 52 | |||
| 40 | ;;; checkdoc-tests.el ends here | 53 | ;;; checkdoc-tests.el ends here |