[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/relint 7e6b8bf 14/23: Add tests
From: |
Mattias Engdegård |
Subject: |
[elpa] externals/relint 7e6b8bf 14/23: Add tests |
Date: |
Sun, 29 Sep 2019 15:34:53 -0400 (EDT) |
branch: externals/relint
commit 7e6b8bf8404c8f14bc631538bf2c4ce05c994828
Author: Mattias Engdegård <address@hidden>
Commit: Mattias Engdegård <address@hidden>
Add tests
---
relint-test.el | 124 +++++++++++++++++++++++++++++++++++++++++++++++++
test/1.elisp | 87 ++++++++++++++++++++++++++++++++++
test/1.expected | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
test/2.elisp | 42 +++++++++++++++++
test/2.expected | 120 +++++++++++++++++++++++++++++++++++++++++++++++
test/3.elisp | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
test/3.expected | 36 ++++++++++++++
test/4.elisp | 47 +++++++++++++++++++
test/4.expected | 51 ++++++++++++++++++++
test/5.elisp | 27 +++++++++++
test/5.expected | 12 +++++
test/6.elisp | 46 ++++++++++++++++++
test/6.expected | 32 +++++++++++++
13 files changed, 903 insertions(+)
diff --git a/relint-test.el b/relint-test.el
new file mode 100644
index 0000000..95460e5
--- /dev/null
+++ b/relint-test.el
@@ -0,0 +1,124 @@
+;;; relint-test.el --- Tests for relint.el -*- lexical-binding: t -*-
+
+;; Copyright (C) 2019 Free Software Foundation, Inc.
+
+;; Author: Mattias Engdegård <address@hidden>
+
+;; This program 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 of the License, or
+;; (at your option) any later version.
+
+;; This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+
+(require 'relint)
+(require 'ert)
+
+;; Required for some of the source in test/
+(require 'subr-x)
+
+(defconst relint-test--this-file
+ (or load-file-name buffer-file-name))
+
+;; A nonsense test value to exercise the location code.
+(defconst relint-test--value
+ '(t1 t2
+ 13 -7 14.0 1.0e+INF "ab\"\\" ?a ?? ?\\ ?\( ?\)
+ [xy 4 uv]
+ (t3 (((t4) t5) (t6))
+ ('t7 't8 '(t9 () (()))))
+ (r . s)
+ (t10 t11 . t12)
+ #'zz
+ '''qqq
+ `(t13 ,t14 ,(t15 t16) ,@t17 ,@(t18 t19))))
+
+(defun relint-test--enumerate-nodes (form path)
+ "List of (NODE . PATH-TO-NODE) for each node in FORM, starting at PATH.
+A node is either an atom or a list, but not a proper tail of a list."
+ (if (consp form)
+ (let ((r (list (cons form path)))
+ (i 0))
+ (while (consp form)
+ (let ((node (car form)))
+ (setq r (append
+ (reverse (relint-test--enumerate-nodes node (cons i
path)))
+ r))
+ (setq i (1+ i))
+ (setq form (cdr form))))
+ (reverse r))
+ (list (cons form path))))
+
+(defun relint-test--find-toplevel-form (pred)
+ "Find first toplevel form satisfying PRED. Return (FORM . POSITION)."
+ (catch 'found
+ (while t
+ (let* ((pos (point))
+ (form (read (current-buffer))))
+ (when (funcall pred form)
+ (throw 'found (cons form pos)))))))
+
+(ert-deftest relint-find-pos ()
+ "Test the mechanism that computes a position from a toplevel-position
+and a path."
+ (with-temp-buffer
+ (emacs-lisp-mode)
+ (insert-file-contents relint-test--this-file)
+ (let* ((form-pos (relint-test--find-toplevel-form
+ (lambda (x) (pcase x
+ (`(defconst relint-test--value . ,_) t)))))
+ (toplevel-form (car form-pos))
+ (toplevel-pos (cdr form-pos)))
+ ;; We have a toplevel form and position. Enumerate its parts.
+ (dolist (item (relint-test--enumerate-nodes toplevel-form nil))
+ (let* ((node (car item))
+ (path (cdr item))
+ (pos-line-col (relint--pos-line-col-from-toplevel-pos-path
+ toplevel-pos path))
+ (pos (nth 0 pos-line-col)))
+ ;; Skip sugared items; they cannot be read in isolation.
+ (unless (memq node '(quote function \` \, \,@))
+ (goto-char pos)
+ ;; The position should not be at whitespace or comment.
+ (should-not (looking-at (rx (any " \t\n;"))))
+ ;; Read what we find at the position to check.
+ (let ((thing-here (read (current-buffer))))
+ (should (equal thing-here node)))))))))
+
+(defun relint-test--insert-file (file)
+ (insert-file-contents
+ (expand-file-name file (file-name-directory relint-test--this-file))))
+
+(defun relint-test--scan-file (file)
+ "Scan FILE and return the results as a string."
+ (with-temp-buffer
+ (relint--scan-buffer (find-file-noselect file t) (current-buffer) t)
+ (buffer-string)))
+
+(defun relint-test--read-file (file)
+ (with-temp-buffer
+ (relint-test--insert-file file)
+ (buffer-string)))
+
+;; The scan tests are divided more-or-less arbitarily into chunks
+;; instead of having one big file, to make it easier to find errors.
+
+(defmacro relint-test--deftest (basename)
+ (let* ((testfile (concat "test/" basename ".elisp"))
+ (expected (concat "test/" basename ".expected"))
+ (name (intern (concat "relint-check-file-" basename))))
+ `(ert-deftest ,name ()
+ (should (equal (relint-test--scan-file ,testfile)
+ (relint-test--read-file ,expected))))))
+
+(dolist (f (directory-files
+ (concat (file-name-directory relint-test--this-file) "/test")
+ nil (rx ".elisp" eos)))
+ (let ((base (string-remove-suffix ".elisp" f)))
+ (eval `(relint-test--deftest ,base))))
diff --git a/test/1.elisp b/test/1.elisp
new file mode 100644
index 0000000..982803d
--- /dev/null
+++ b/test/1.elisp
@@ -0,0 +1,87 @@
+;;; Relint test file 1 -*- emacs-lisp -*-
+
+;; Test variable name heuristics for detecting regexps.
+(defconst innocent-thing "+bad**regexp^")
+
+(defconst bad-regexp "[AA]")
+(defconst bad-regex "[AA]")
+(defconst bad-re "[AA]")
+(defconst bad-pattern "[AA]")
+
+(defconst bad-regexps '("a" "+a" "a"))
+(defconst bad-regexes '("b" "+b" "b"))
+(defconst bad-regexp-list '("c" "+c" "c"))
+(defconst bad-regex-list '("d" "+d" "d"))
+(defconst bad-re-list '("e" "+e" "e"))
+
+(defconst bad-regexp-alist '((a . "**") ("??" . a) (".^" . "$.")))
+(defconst bad-regex-alist '((a . "**") ("??" . a) (".^" . "$.")))
+(defconst bad-re-alist '((a . "**") ("??" . a) (".^" . "$.")))
+(defconst bad-pattern-alist '((a . "**") ("??" . a) (".^" . "$.")))
+
+(defconst bad-mode-alist '((a . "**") ("??" . a) (".^" . "$.")))
+
+(defconst bad-rules-list '((eins
+ (this . that)
+ (regexp . "$$"))
+ (zwei
+ (tata . toto)
+ (regexp . "[a-Z]"))))
+
+(defconst bad-font-lock-keywords '(("[xx]" . tag) "[yy]"))
+
+;; Test variable doc string heuristics.
+(defconst bad-var-1 "a^"
+ "Regexp, or something.")
+(defvar bad-var-2 "[zz]"
+ "A regular expression with flaws.")
+(defcustom bad-var-3 "[o-O]"
+ "This regexp looks at you."
+ :group 'relint-test
+ :type 'string)
+
+;; Test defcustom type heuristics.
+(defcustom bad-custom-1 "[nn]"
+ "Doc"
+ :group 'relint-test
+ :type 'regexp)
+(defcustom bad-custom-2 "[ss]"
+ "Doc"
+ :group 'relint-test
+ :type '(regexp :tag "tag"))
+(defcustom bad-custom-3-regexp nil
+ "Doc"
+ :group 'relint-test
+ :type '(choice (const :tag "*" "+a+")
+ (radio (const "*b*")
+ (const "^c^"))))
+(defcustom bad-custom-4-regexp nil
+ "Doc"
+ :group 'relint-test
+ :type 'string
+ :options '("a" "+b"))
+(defcustom bad-custom-5 '(("a" . tata) ("^x^" . toto))
+ "Doc"
+ :group 'relint-test
+ :type '(alist :key-type regexp :value-type symbol))
+(defcustom bad-custom-6 '((toto . "humbug") (tata . "[[:bah:]]"))
+ "Doc"
+ :group 'relint-test
+ :type '(alist :key-type symbol :value-type regexp))
+(defcustom bad-custom-7 '("aa" "[aa]")
+ "Doc"
+ :group 'relint-test
+ :type '(repeat regexp))
+
+;; Special case.
+(defvar compilation-error-regexp-alist-alist
+ '((aa "a^a" 1 2)
+ (bb "b$b" 3 4)))
+
+(define-generic-mode my-mode
+ nil
+ nil
+ '(("1^" bla)
+ ("2^" argl))
+ '("a" "b++" "c")
+ nil)
diff --git a/test/1.expected b/test/1.expected
new file mode 100644
index 0000000..6e84f44
--- /dev/null
+++ b/test/1.expected
@@ -0,0 +1,142 @@
+1.elisp:6:22: In bad-regexp: Duplicated `A' inside character alternative (pos
2)
+ "[AA]"
+ ..^
+1.elisp:7:21: In bad-regex: Duplicated `A' inside character alternative (pos 2)
+ "[AA]"
+ ..^
+1.elisp:8:18: In bad-re: Duplicated `A' inside character alternative (pos 2)
+ "[AA]"
+ ..^
+1.elisp:9:23: In bad-pattern: Duplicated `A' inside character alternative (pos
2)
+ "[AA]"
+ ..^
+1.elisp:11:23: In bad-regexps: Unescaped literal `+' (pos 0)
+ "+a"
+ ^
+1.elisp:12:23: In bad-regexes: Unescaped literal `+' (pos 0)
+ "+b"
+ ^
+1.elisp:13:27: In bad-regexp-list: Unescaped literal `+' (pos 0)
+ "+c"
+ ^
+1.elisp:14:26: In bad-regex-list: Unescaped literal `+' (pos 0)
+ "+d"
+ ^
+1.elisp:15:23: In bad-re-list: Unescaped literal `+' (pos 0)
+ "+e"
+ ^
+1.elisp:17:28: In bad-regexp-alist: Unescaped literal `*' (pos 0)
+ "**"
+ ^
+1.elisp:17:28: In bad-regexp-alist: Unescaped literal `?' (pos 0)
+ "??"
+ ^
+1.elisp:17:28: In bad-regexp-alist: Unescaped literal `^' (pos 1)
+ ".^"
+ .^
+1.elisp:17:28: In bad-regexp-alist: Unescaped literal `$' (pos 0)
+ "$."
+ ^
+1.elisp:18:27: In bad-regex-alist: Unescaped literal `*' (pos 0)
+ "**"
+ ^
+1.elisp:18:27: In bad-regex-alist: Unescaped literal `?' (pos 0)
+ "??"
+ ^
+1.elisp:18:27: In bad-regex-alist: Unescaped literal `^' (pos 1)
+ ".^"
+ .^
+1.elisp:18:27: In bad-regex-alist: Unescaped literal `$' (pos 0)
+ "$."
+ ^
+1.elisp:19:24: In bad-re-alist: Unescaped literal `*' (pos 0)
+ "**"
+ ^
+1.elisp:19:24: In bad-re-alist: Unescaped literal `?' (pos 0)
+ "??"
+ ^
+1.elisp:19:24: In bad-re-alist: Unescaped literal `^' (pos 1)
+ ".^"
+ .^
+1.elisp:19:24: In bad-re-alist: Unescaped literal `$' (pos 0)
+ "$."
+ ^
+1.elisp:20:29: In bad-pattern-alist: Unescaped literal `*' (pos 0)
+ "**"
+ ^
+1.elisp:20:29: In bad-pattern-alist: Unescaped literal `?' (pos 0)
+ "??"
+ ^
+1.elisp:20:29: In bad-pattern-alist: Unescaped literal `^' (pos 1)
+ ".^"
+ .^
+1.elisp:20:29: In bad-pattern-alist: Unescaped literal `$' (pos 0)
+ "$."
+ ^
+1.elisp:22:26: In bad-mode-alist: Unescaped literal `?' (pos 0)
+ "??"
+ ^
+1.elisp:22:26: In bad-mode-alist: Unescaped literal `^' (pos 1)
+ ".^"
+ .^
+1.elisp:24:26: In bad-rules-list (eins): Unescaped literal `$' (pos 0)
+ "$$"
+ ^
+1.elisp:24:26: In bad-rules-list (zwei): Reversed range `a-Z' matches nothing
(pos 1)
+ "[a-Z]"
+ .^
+1.elisp:31:34: In bad-font-lock-keywords (tag): Duplicated `x' inside
character alternative (pos 2)
+ "[xx]"
+ ..^
+1.elisp:31:34: In bad-font-lock-keywords: Duplicated `y' inside character
alternative (pos 2)
+ "[yy]"
+ ..^
+1.elisp:34:21: In bad-var-1: Unescaped literal `^' (pos 1)
+ "a^"
+ .^
+1.elisp:36:19: In bad-var-2: Duplicated `z' inside character alternative (pos
2)
+ "[zz]"
+ ..^
+1.elisp:38:22: In bad-var-3: Reversed range `o-O' matches nothing (pos 1)
+ "[o-O]"
+ .^
+1.elisp:44:25: In bad-custom-1: Duplicated `n' inside character alternative
(pos 2)
+ "[nn]"
+ ..^
+1.elisp:48:25: In bad-custom-2: Duplicated `s' inside character alternative
(pos 2)
+ "[ss]"
+ ..^
+1.elisp:55:9: In bad-custom-3-regexp: Unescaped literal `+' (pos 0)
+ "+a+"
+ ^
+1.elisp:55:9: In bad-custom-3-regexp: Unescaped literal `*' (pos 0)
+ "*b*"
+ ^
+1.elisp:55:9: In bad-custom-3-regexp: Unescaped literal `^' (pos 2)
+ "^c^"
+ ..^
+1.elisp:62:12: In bad-custom-4-regexp: Unescaped literal `+' (pos 0)
+ "+b"
+ ^
+1.elisp:63:25: In bad-custom-5: Unescaped literal `^' (pos 2)
+ "^x^"
+ ..^
+1.elisp:67:25: In bad-custom-6: Error: No character class ‘[:bah:]’:
"[[:bah:]]"
+1.elisp:71:25: In bad-custom-7: Duplicated `a' inside character alternative
(pos 2)
+ "[aa]"
+ ..^
+1.elisp:78:3: In compilation-error-regexp-alist-alist (aa): Unescaped literal
`^' (pos 1)
+ "a^a"
+ .^
+1.elisp:78:3: In compilation-error-regexp-alist-alist (bb): Unescaped literal
`$' (pos 1)
+ "b$b"
+ .^
+1.elisp:84:3: In define-generic-mode my-mode: Unescaped literal `^' (pos 1)
+ "1^"
+ .^
+1.elisp:84:3: In define-generic-mode my-mode: Unescaped literal `^' (pos 1)
+ "2^"
+ .^
+1.elisp:86:3: In define-generic-mode my-mode: Repetition of repetition (pos 2)
+ "b++"
+ ..^
diff --git a/test/2.elisp b/test/2.elisp
new file mode 100644
index 0000000..088b588
--- /dev/null
+++ b/test/2.elisp
@@ -0,0 +1,42 @@
+;;; Relint test file 2 -*- emacs-lisp -*-
+
+;; Test regexp detection in arguments of known regexp-detecting functions.
+(defun f1 (s)
+ (looking-at "[aa]")
+ (re-search-forward "[bb]")
+ (re-search-backward "[cc]")
+ (string-match "[dd]" s)
+ (string-match-p "[ee]" s)
+ (looking-at-p "[ff]")
+ (looking-back "[gg]" nil)
+ (replace-regexp-in-string "[hh]" "x" s)
+ (query-replace-regexp "[jj]" s)
+ (posix-looking-at "[kk]")
+ (posix-search-backward "[ll]")
+ (posix-search-forward "[mm]")
+ (posix-string-match "[nn]" s)
+ (load-history-filename-element "[oo]")
+ (kill-matching-buffers "[pp]")
+ (keep-lines "[qq]")
+ (flush-lines "[rr]")
+ (how-many "[ss]")
+ (split-string s "[tt]" nil "[uu]")
+ (split-string-and-unquote s "[vv]")
+ (string-trim-left s "[ww]")
+ (string-trim-right s "[xx]")
+ (string-trim s "[yy]" "[zz]"))
+
+;; Test argument names as means of detecting regexps.
+(defun f2 (x1 my-regexp x2 my-regex x3 my-re x4 my-pattern x5 re)
+ (list x1 my-regexp x1 my-regex x3 my-re x4 my-pattern x5 re))
+
+(defsubst s2 (x1 my-regexp x2 my-regex x3 my-re x4 my-pattern x5 re)
+ (list x1 my-regexp x1 my-regex x3 my-re x4 my-pattern x5 re))
+
+(defmacro m2 (x1 my-regexp x2 my-regex x3 my-re x4 my-pattern x5 re)
+ (list 'quote (list x1 my-regexp x1 my-regex x3 my-re x4 my-pattern x5 re)))
+
+(defun f4 (s)
+ (f2 "[AA]" "[BB]" "[CC]" "[DD]" "[EE]" "[FF]" "[GG]" "[HH]" "[II]" "[JJ]")
+ (s2 "[AA]" "[BB]" "[CC]" "[DD]" "[EE]" "[FF]" "[GG]" "[HH]" "[II]" "[JJ]")
+ (m2 "[AA]" "[BB]" "[CC]" "[DD]" "[EE]" "[FF]" "[GG]" "[HH]" "[II]" "[JJ]"))
diff --git a/test/2.expected b/test/2.expected
new file mode 100644
index 0000000..80a819a
--- /dev/null
+++ b/test/2.expected
@@ -0,0 +1,120 @@
+2.elisp:5:15: In call to looking-at: Duplicated `a' inside character
alternative (pos 2)
+ "[aa]"
+ ..^
+2.elisp:6:22: In call to re-search-forward: Duplicated `b' inside character
alternative (pos 2)
+ "[bb]"
+ ..^
+2.elisp:7:23: In call to re-search-backward: Duplicated `c' inside character
alternative (pos 2)
+ "[cc]"
+ ..^
+2.elisp:8:17: In call to string-match: Duplicated `d' inside character
alternative (pos 2)
+ "[dd]"
+ ..^
+2.elisp:9:19: In call to string-match-p: Duplicated `e' inside character
alternative (pos 2)
+ "[ee]"
+ ..^
+2.elisp:10:17: In call to looking-at-p: Duplicated `f' inside character
alternative (pos 2)
+ "[ff]"
+ ..^
+2.elisp:11:17: In call to looking-back: Duplicated `g' inside character
alternative (pos 2)
+ "[gg]"
+ ..^
+2.elisp:12:29: In call to replace-regexp-in-string: Duplicated `h' inside
character alternative (pos 2)
+ "[hh]"
+ ..^
+2.elisp:13:25: In call to query-replace-regexp: Duplicated `j' inside
character alternative (pos 2)
+ "[jj]"
+ ..^
+2.elisp:14:21: In call to posix-looking-at: Duplicated `k' inside character
alternative (pos 2)
+ "[kk]"
+ ..^
+2.elisp:15:26: In call to posix-search-backward: Duplicated `l' inside
character alternative (pos 2)
+ "[ll]"
+ ..^
+2.elisp:16:25: In call to posix-search-forward: Duplicated `m' inside
character alternative (pos 2)
+ "[mm]"
+ ..^
+2.elisp:17:23: In call to posix-string-match: Duplicated `n' inside character
alternative (pos 2)
+ "[nn]"
+ ..^
+2.elisp:18:34: In call to load-history-filename-element: Duplicated `o' inside
character alternative (pos 2)
+ "[oo]"
+ ..^
+2.elisp:19:26: In call to kill-matching-buffers: Duplicated `p' inside
character alternative (pos 2)
+ "[pp]"
+ ..^
+2.elisp:20:15: In call to keep-lines: Duplicated `q' inside character
alternative (pos 2)
+ "[qq]"
+ ..^
+2.elisp:21:16: In call to flush-lines: Duplicated `r' inside character
alternative (pos 2)
+ "[rr]"
+ ..^
+2.elisp:22:13: In call to how-many: Duplicated `s' inside character
alternative (pos 2)
+ "[ss]"
+ ..^
+2.elisp:23:19: In call to split-string: Duplicated `t' inside character
alternative (pos 2)
+ "[tt]"
+ ..^
+2.elisp:23:30: In call to split-string: Duplicated `u' inside character
alternative (pos 2)
+ "[uu]"
+ ..^
+2.elisp:24:31: In call to split-string-and-unquote: Duplicated `v' inside
character alternative (pos 2)
+ "[vv]"
+ ..^
+2.elisp:25:23: In call to string-trim-left: Duplicated `w' inside character
alternative (pos 2)
+ "[ww]"
+ ..^
+2.elisp:26:24: In call to string-trim-right: Duplicated `x' inside character
alternative (pos 2)
+ "[xx]"
+ ..^
+2.elisp:27:18: In call to string-trim: Duplicated `y' inside character
alternative (pos 2)
+ "[yy]"
+ ..^
+2.elisp:27:25: In call to string-trim: Duplicated `z' inside character
alternative (pos 2)
+ "[zz]"
+ ..^
+2.elisp:40:14: In call to f2: Duplicated `B' inside character alternative (pos
2)
+ "[BB]"
+ ..^
+2.elisp:40:28: In call to f2: Duplicated `D' inside character alternative (pos
2)
+ "[DD]"
+ ..^
+2.elisp:40:42: In call to f2: Duplicated `F' inside character alternative (pos
2)
+ "[FF]"
+ ..^
+2.elisp:40:56: In call to f2: Duplicated `H' inside character alternative (pos
2)
+ "[HH]"
+ ..^
+2.elisp:40:70: In call to f2: Duplicated `J' inside character alternative (pos
2)
+ "[JJ]"
+ ..^
+2.elisp:41:14: In call to s2: Duplicated `B' inside character alternative (pos
2)
+ "[BB]"
+ ..^
+2.elisp:41:28: In call to s2: Duplicated `D' inside character alternative (pos
2)
+ "[DD]"
+ ..^
+2.elisp:41:42: In call to s2: Duplicated `F' inside character alternative (pos
2)
+ "[FF]"
+ ..^
+2.elisp:41:56: In call to s2: Duplicated `H' inside character alternative (pos
2)
+ "[HH]"
+ ..^
+2.elisp:41:70: In call to s2: Duplicated `J' inside character alternative (pos
2)
+ "[JJ]"
+ ..^
+2.elisp:42:14: In call to m2: Duplicated `B' inside character alternative (pos
2)
+ "[BB]"
+ ..^
+2.elisp:42:28: In call to m2: Duplicated `D' inside character alternative (pos
2)
+ "[DD]"
+ ..^
+2.elisp:42:42: In call to m2: Duplicated `F' inside character alternative (pos
2)
+ "[FF]"
+ ..^
+2.elisp:42:56: In call to m2: Duplicated `H' inside character alternative (pos
2)
+ "[HH]"
+ ..^
+2.elisp:42:70: In call to m2: Duplicated `J' inside character alternative (pos
2)
+ "[JJ]"
+ ..^
diff --git a/test/3.elisp b/test/3.elisp
new file mode 100644
index 0000000..64763a2
--- /dev/null
+++ b/test/3.elisp
@@ -0,0 +1,137 @@
+;;; Relint test file 3 -*- emacs-lisp -*-
+
+;;; Test evaluation of built-in functions
+
+(defun test-eval-num ()
+ (looking-at
+ (format
+ "%c"
+ (+ (string-to-number (number-to-string ?*)) ; <-- bad regexp
+ (1+ 0)
+ (1- 0)
+ (* 2 2)
+ (- (/ 8 2))
+ (% 5 3)
+ (- (mod 5 3))
+ (max 3 4)
+ (min -3 -4)
+ (abs 0)
+ (ash 0 -2)
+ (lsh 0 1)
+ (logand 5 10)
+ (logior 0 0)
+ (logxor 3 3)
+ (- (length "abc") (safe-length '(1 2 3)))
+ (and (= 3 3)
+ (< 2 3)
+ (> 4 3)
+ (/= 1 2)
+ (<= 1 1 2)
+ (>= 2 1 1)
+ 0)))))
+
+(defun test-eval-bool ()
+ (looking-at
+ (if (and t (not t))
+ (/ 1 0)
+ (or nil
+ (and (consp '(a))
+ (atom 'a)
+ (stringp "s")
+ (symbolp 'a)
+ (listp nil)
+ (nlistp 0)
+ (booleanp t)
+ (integerp 3)
+ (numberp -5)
+ (natnump 1)
+ (characterp ?A)
+ (zerop 0)
+ (sequencep nil)
+ (vectorp [])
+ (arrayp [])
+ (eq 'a 'a)
+ (eql 2 2)
+ (equal '(1 2) '(1 2))
+ (string-equal "ab" "ab")
+ (string= "ab" "ab")
+ (string-lessp "ab" "bc")
+ (string< "ab" "bc")
+ (string-greaterp "bc" "ab")
+ (string> "bc" "ab")
+ (char-equal ?! ?!)
+ (string-match "b" "abc")
+ (string-match-p "b" "abc")
+ (string-prefix-p "A" "AB")
+ (string-suffix-p "B" "AB")
+ (string-blank-p " ")
+ (identity "++")))))) ; <--- bad regexp
+
+(defun test-eval-str ()
+ (looking-at
+ (substring
+ (concat
+ (format "%s!" "[QQ]") ; 1 <--- bad regexp
+ (string ?a ?b) ; 2
+ (make-string 3 ?x) ; 3
+ (symbol-name 'bof) ; 3
+ (string-to-unibyte (string-to-multibyte "AB")) ; 2
+ (string-join (split-string "I think") "-") ; 7
+ (string-trim-left " mm") ; 2
+ (string-trim-right "nn ") ; 2
+ (string-trim " kk ") ; 2
+ (string-remove-prefix "A" "AB") ; 1
+ (string-remove-suffix "A" "AB") ; 2
+ (char-to-string (string-to-char "t")) ; 1
+ (wildcard-to-regexp "az") ; 6
+ (upcase (downcase (capitalize "zzA"))) ; 3
+ (combine-and-quote-strings
+ (split-string-and-unquote "oh dear"))) ; 7
+ 0 (- (+ 1 2 3 3 2 7 2 2 2 1 2 1 6 3 7)))))
+
+(defun test-eval-list ()
+ (looking-at
+ (nth (+ 1 1 2 1 2 1 1 2 2 2 2 2 2 2 2 2 1 1 2 3 3 2 1 4 2 1 2 2)
+ (append
+ (cons 'a nil) ; 1
+ (cadr '(a (b))) ; 1
+ (cdar '((b c d))) ; 2
+ (caar '(((e)))) ; 1
+ (cddr '(d e f g)) ; 2
+ (car-safe '((h))) ; 1
+ (cdr-safe '(g h)) ; 1
+ (nthcdr 2 '(g h i j)) ; 2
+ (member "B" '("A" "B" "C")) ; 2
+ (memq 'k '(j k l)) ; 2
+ (memql 2 '(1 2 3)) ; 2
+ (member-ignore-case "d" '("C" "D" "E")) ; 2
+ (remove "!" '("F" "!" "G" "!")) ; 2
+ (remq 'a '(m a n a)) ; 2
+ (assoc "p" '(("p" 1) ("q" 2))) ; 2
+ (assq 'r '((q 1) (r 2))) ; 2
+ (car (rassoc 2 '(((u) . 1) ((v) . 2)))) ; 1
+ (car (rassq 'x '(((w) . x) ((u) . y)))) ; 1
+ (butlast '(z x y)) ; 2
+ (number-sequence 1 3) ; 3
+ (make-list 3 'M) ; 3
+ (reverse '(9 8)) ; 2
+ (last '(a b c)) ; 1
+ (nconc (list 1 2) (list 3 4)) ; 4
+ (delete 3 (list 1 2 3)) ; 2
+ (delq 'x (list 'x 'y)) ; 1
+ (nreverse (list 1 2)) ; 2
+ (nbutlast (list 1 2 3)) ; 2
+ (list "^m^"))))) ; <-- bad regexp
+
+(defvar my-unknown (list (unknown-fun) "[11]"))
+
+;; Test partial list evaluation: skip elements that cannot be computed
+(defvar another-bad-regexp-list
+ (append
+ my-unknown
+ (cons "[22]" my-unknown)
+ (cons my-unknown '(my-unknown "[33]"))
+ (purecopy (reverse (list my-unknown "[44]")))
+ (copy-sequence (remove my-unknown (list my-unknown "[55]")))
+ (copy-alist (remq my-unknown (list my-unknown "[66]")))
+ (delete-dups (list my-unknown "[77]" my-unknown "[77]"))))
diff --git a/test/3.expected b/test/3.expected
new file mode 100644
index 0000000..9e14570
--- /dev/null
+++ b/test/3.expected
@@ -0,0 +1,36 @@
+3.elisp:7:4: In call to looking-at: Unescaped literal `*' (pos 0)
+ "*"
+ ^
+3.elisp:35:4: In call to looking-at: Unescaped literal `+' (pos 0)
+ "++"
+ ^
+3.elisp:72:4: In call to looking-at: Duplicated `Q' inside character
alternative (pos 2)
+ "[QQ]"
+ ..^
+3.elisp:94:4: In call to looking-at: Unescaped literal `^' (pos 2)
+ "^m^"
+ ..^
+3.elisp:130:3: In another-bad-regexp-list: Duplicated `1' inside character
alternative (pos 2)
+ "[11]"
+ ..^
+3.elisp:130:3: In another-bad-regexp-list: Duplicated `2' inside character
alternative (pos 2)
+ "[22]"
+ ..^
+3.elisp:130:3: In another-bad-regexp-list: Duplicated `1' inside character
alternative (pos 2)
+ "[11]"
+ ..^
+3.elisp:130:3: In another-bad-regexp-list: Duplicated `3' inside character
alternative (pos 2)
+ "[33]"
+ ..^
+3.elisp:130:3: In another-bad-regexp-list: Duplicated `4' inside character
alternative (pos 2)
+ "[44]"
+ ..^
+3.elisp:130:3: In another-bad-regexp-list: Duplicated `5' inside character
alternative (pos 2)
+ "[55]"
+ ..^
+3.elisp:130:3: In another-bad-regexp-list: Duplicated `6' inside character
alternative (pos 2)
+ "[66]"
+ ..^
+3.elisp:130:3: In another-bad-regexp-list: Duplicated `7' inside character
alternative (pos 2)
+ "[77]"
+ ..^
diff --git a/test/4.elisp b/test/4.elisp
new file mode 100644
index 0000000..4dc8a94
--- /dev/null
+++ b/test/4.elisp
@@ -0,0 +1,47 @@
+;;; Relint test file 4 -*- emacs-lisp -*-
+
+;; Test user-defined function application.
+(defun my-char-seq-str (start end)
+ (if (<= start end)
+ (concat (string start) (my-char-seq-str (1+ start) end))
+ ""))
+
+(defun test-fun-call ()
+ (looking-at (my-char-seq-str ?$ ?*))) ; "$%&'()*"
+
+;; Test higher-order functions.
+(defun test-hof ()
+ (looking-at (apply 'string '(?+ ?x)))
+ (looking-at (funcall #'string ?*))
+ (looking-at (mapconcat #'identity '("x" "^") "-"))
+ (looking-at (if (cl-some 'numberp '('a 2 nil)) "[AA]" ""))
+ (looking-at (if (cl-every 'numberp '('a 2 nil)) "" "[BB]"))
+ (looking-at (apply 'string (mapcar (lambda (c) (1+ c)) '(?* ?a))))
+ (looking-at (apply 'string (mapcan (lambda (c) (list ?+)) '(1 2))))
+ (looking-at (apply 'string (sort (list ?$ ?+ ?a) #'<))))
+
+;; Test higher-order functions with keywords.
+(defun test-hof-kw ()
+ (looking-at (cl-reduce #'concat '(?* ?a ?b) :key #'char-to-string))
+ (looking-at (concat "+" (cl-find "c" '("a" "b" "c") :test #'equal))))
+
+;; Test rx
+(defvar my-sub-rx '(one-or-more nonl))
+
+(defun test-rx ()
+ (looking-at (concat (rx (eval my-sub-rx)
+ (literal (string ?a ?b))
+ (regexp (concat "c" "+"))
+ (regex (string ?d)))
+ "^")))
+
+;; Test macro expansion
+(defmacro my-macro (x)
+ (list 'string ?a x))
+
+(defun test-macro ()
+ (looking-at (my-macro ?^))
+ (looking-at (when t "b++"))
+ (looking-at (unless nil "c++"))
+ (looking-at (string-join `("a" ,@(list "$") ,"b")))
+ (looking-at (pcase 'a ((pred symbolp) "d++"))))
diff --git a/test/4.expected b/test/4.expected
new file mode 100644
index 0000000..d051130
--- /dev/null
+++ b/test/4.expected
@@ -0,0 +1,51 @@
+4.elisp:10:15: In call to looking-at: Unescaped literal `$' (pos 0)
+ "$%&'()*"
+ ^
+4.elisp:14:15: In call to looking-at: Unescaped literal `+' (pos 0)
+ "+x"
+ ^
+4.elisp:15:15: In call to looking-at: Unescaped literal `*' (pos 0)
+ "*"
+ ^
+4.elisp:16:15: In call to looking-at: Unescaped literal `^' (pos 2)
+ "x-^"
+ ..^
+4.elisp:17:15: In call to looking-at: Duplicated `A' inside character
alternative (pos 2)
+ "[AA]"
+ ..^
+4.elisp:18:15: In call to looking-at: Duplicated `B' inside character
alternative (pos 2)
+ "[BB]"
+ ..^
+4.elisp:19:15: In call to looking-at: Unescaped literal `+' (pos 0)
+ "+b"
+ ^
+4.elisp:20:15: In call to looking-at: Unescaped literal `+' (pos 0)
+ "++"
+ ^
+4.elisp:21:15: In call to looking-at: Unescaped literal `$' (pos 0)
+ "$+a"
+ ^
+4.elisp:25:15: In call to looking-at: Unescaped literal `*' (pos 0)
+ "*ab"
+ ^
+4.elisp:26:15: In call to looking-at: Unescaped literal `+' (pos 0)
+ "+c"
+ ^
+4.elisp:32:15: In call to looking-at: Unescaped literal `^' (pos 13)
+ ".+ab\\(?:c+\\)d^"
+ ...............^
+4.elisp:43:15: In call to looking-at: Unescaped literal `^' (pos 1)
+ "a^"
+ .^
+4.elisp:44:15: In call to looking-at: Repetition of repetition (pos 2)
+ "b++"
+ ..^
+4.elisp:45:15: In call to looking-at: Repetition of repetition (pos 2)
+ "c++"
+ ..^
+4.elisp:46:15: In call to looking-at: Unescaped literal `$' (pos 1)
+ "a$b"
+ .^
+4.elisp:47:15: In call to looking-at: Repetition of repetition (pos 2)
+ "d++"
+ ..^
diff --git a/test/5.elisp b/test/5.elisp
new file mode 100644
index 0000000..406ef69
--- /dev/null
+++ b/test/5.elisp
@@ -0,0 +1,27 @@
+;;; Relint test file 5 -*- emacs-lisp -*-
+
+;; Test let bindings
+(defun test-let-inside (x y)
+ (looking-at (let ((x "a")
+ (y "b"))
+ (let* ((y "^")
+ (z (concat x y)))
+ z))))
+
+(defun test-let-outside (x y)
+ (let ((x "A")
+ (y "B"))
+ (let* ((y "^")
+ (z (concat x y)))
+ (looking-at z))))
+
+;; Test setq
+(defun test-setq-inside (x)
+ (looking-at (setq x "[AA]")))
+
+(defun test-setq-outside (x c)
+ (setq x "[")
+ (let ((y "B")
+ (z "M"))
+ (setq z "B")
+ (looking-at (concat x y z "]"))))
diff --git a/test/5.expected b/test/5.expected
new file mode 100644
index 0000000..75c59ea
--- /dev/null
+++ b/test/5.expected
@@ -0,0 +1,12 @@
+5.elisp:5:15: In call to looking-at: Unescaped literal `^' (pos 1)
+ "a^"
+ .^
+5.elisp:16:19: In call to looking-at: Unescaped literal `^' (pos 1)
+ "A^"
+ .^
+5.elisp:20:15: In call to looking-at: Duplicated `A' inside character
alternative (pos 2)
+ "[AA]"
+ ..^
+5.elisp:27:17: In call to looking-at: Duplicated `B' inside character
alternative (pos 2)
+ "[BB]"
+ ..^
diff --git a/test/6.elisp b/test/6.elisp
new file mode 100644
index 0000000..e88e7c8
--- /dev/null
+++ b/test/6.elisp
@@ -0,0 +1,46 @@
+;;; Relint test file 6 -*- emacs-lisp -*-
+
+;; Test mixup of regexp and character alternative
+(defun myrefun (things)
+ (if (listp things)
+ (regexp-opt things)
+ things))
+
+(defun test-mixup (x y z)
+ (list
+ (concat "a*[^" some-regexp "]")
+ (concat stuff "[" (regexp-quote x) "]" stuff)
+ (concat "[" (myrefun y) "]")
+ (concat "[" (rx ?a "b" nonl) "]")
+ (concat "[" (rx-to-string z) "]")
+ (format "^%s[%s]*%s$" stuff some-regex stuff)
+ (format "[%s]" (regexp-quote x))
+ (format "[%s]" (myrefun y))
+ (format "[%s]" (rx ?a "b" nonl))
+ (format "[%s]" (rx-to-string z))))
+
+;; Test skip-chars
+(defun test-skip-chars (x y z)
+ (skip-chars-forward "[a-z]")
+ (skip-chars-backward "aa")
+ (skip-chars-forward some-re)
+ (skip-chars-backward (regexp-quote x))
+ (skip-chars-forward (myrefun y))
+ (skip-chars-backward (rx "abc"))
+ (skip-chars-forward (rx-to-string z)))
+
+;; Test skip-syntax
+(defun test-skip-syntax (x y z)
+ (skip-syntax-forward "\\s-")
+ (skip-syntax-backward "- ")
+ (skip-syntax-forward some-re)
+ (skip-syntax-backward (regexp-quote x))
+ (skip-syntax-forward (myrefun y))
+ (skip-syntax-backward (rx "w-"))
+ (skip-syntax-forward (rx-to-string z)))
+
+;; Test suppression
+(defun test-suppression ()
+ ;; relint suppression: Unescaped literal `$'
+ ;; relint suppression: Duplicated `a'
+ (looking-at "$[aa]"))
diff --git a/test/6.expected b/test/6.expected
new file mode 100644
index 0000000..6d8c83a
--- /dev/null
+++ b/test/6.expected
@@ -0,0 +1,32 @@
+6.elisp:11:19: Value from `some-regexp' cannot be spliced into `[...]'
+6.elisp:12:22: Value from `regexp-quote' cannot be spliced into `[...]'
+6.elisp:13:16: Value from `regexp-opt' cannot be spliced into `[...]'
+6.elisp:14:16: Value from `rx' cannot be spliced into `[...]'
+6.elisp:15:16: Value from `rx-to-string' cannot be spliced into `[...]'
+6.elisp:16:32: Value from `some-regex' cannot be spliced into `[...]'
+6.elisp:17:19: Value from `regexp-quote' cannot be spliced into `[...]'
+6.elisp:18:19: Value from `regexp-opt' cannot be spliced into `[...]'
+6.elisp:19:19: Value from `rx' cannot be spliced into `[...]'
+6.elisp:20:19: Value from `rx-to-string' cannot be spliced into `[...]'
+6.elisp:24:23: In call to skip-chars-forward: Suspect skip set framed in
`[...]' (pos 0)
+ "[a-z]"
+ ^
+6.elisp:25:24: In call to skip-chars-backward: Duplicated character `a' (pos 1)
+ "aa"
+ .^
+6.elisp:26:23: `some-re' cannot be used for arguments to `skip-chars-forward'
+6.elisp:27:24: `regexp-quote' cannot be used for arguments to
`skip-chars-backward'
+6.elisp:28:23: `regexp-opt' cannot be used for arguments to
`skip-chars-forward'
+6.elisp:29:24: `rx' cannot be used for arguments to `skip-chars-backward'
+6.elisp:30:23: `rx-to-string' cannot be used for arguments to
`skip-chars-forward'
+6.elisp:34:24: In call to skip-syntax-forward: Invalid char `s' in syntax
string (pos 1)
+ "\\s-"
+ ..^
+6.elisp:35:25: In call to skip-syntax-backward: Duplicated syntax code ` '
(pos 1)
+ "- "
+ .^
+6.elisp:36:24: `some-re' cannot be used for arguments to `skip-syntax-forward'
+6.elisp:37:25: `regexp-quote' cannot be used for arguments to
`skip-syntax-backward'
+6.elisp:38:24: `regexp-opt' cannot be used for arguments to
`skip-syntax-forward'
+6.elisp:39:25: `rx' cannot be used for arguments to `skip-syntax-backward'
+6.elisp:40:24: `rx-to-string' cannot be used for arguments to
`skip-syntax-forward'
- [elpa] externals/relint 5137ec6 11/23: Evaluate keywords correctly, (continued)
- [elpa] externals/relint 5137ec6 11/23: Evaluate keywords correctly, Mattias Engdegård, 2019/09/29
- [elpa] externals/relint 6a07508 10/23: Handle rx `eval' form correctly, Mattias Engdegård, 2019/09/29
- [elpa] externals/relint 3a7e82a 05/23: Track some mutation of local variables in phase 2, Mattias Engdegård, 2019/09/29
- [elpa] externals/relint bc1b5a8 16/23: Add word-search-regexp to the list of regexp generating functions, Mattias Engdegård, 2019/09/29
- [elpa] externals/relint b890b5a 15/23: Track mutation in push and lambda in phase 2, Mattias Engdegård, 2019/09/29
- [elpa] externals/relint 02c5dd2 13/23: Prepare for easier testability, Mattias Engdegård, 2019/09/29
- [elpa] externals/relint 60d5627 21/23: Lazy evaluation of global variables, Mattias Engdegård, 2019/09/29
- [elpa] externals/relint b0f0bee 23/23: Increment version to 1.11, Mattias Engdegård, 2019/09/29
- [elpa] externals/relint 501f87b 20/23: Evaluate `prog1' and `prog2', Mattias Engdegård, 2019/09/29
- [elpa] externals/relint 6212b6f 22/23: Evaluate more functions and macros, Mattias Engdegård, 2019/09/29
- [elpa] externals/relint 7e6b8bf 14/23: Add tests,
Mattias Engdegård <=