branch: externals/relint
commit 7abad19169cf79d12708e6e93b53c44ab2e32ad6
Author: Mattias EngdegÄrd <matti...@acm.org>
Commit: Mattias EngdegÄrd <matti...@acm.org>

    Adapt to xr changes: ranged diagnostics with severity field
---
 README           |   7 +-
 relint-test.el   |  19 +++---
 relint.el        | 192 ++++++++++++++++++++++++++++++++++++++-----------------
 test/1.expected  | 119 +++++++++++++++++-----------------
 test/10.expected |  14 ++--
 test/11.expected |  34 +++++-----
 test/12.expected |  26 ++++----
 test/13.expected |  52 +++++++--------
 test/14.expected |  14 ++--
 test/2.expected  | 120 +++++++++++++++++-----------------
 test/3.expected  |   4 +-
 test/4.expected  |   2 +-
 test/6.expected  |   6 +-
 test/9.expected  |  60 ++++++++---------
 14 files changed, 373 insertions(+), 296 deletions(-)

diff --git a/README b/README
index 8c2ba342ab..0c7bbc874d 100644
--- a/README
+++ b/README
@@ -121,6 +121,7 @@ skip-syntax-forward and skip-syntax-backward.
     ranges are caused by a misplaced hyphen.
 
   - Character 'B' included in range 'A-C'
+  - Range 'A-C' includes character 'B'
 
     A range includes a character that also occurs individually. This
     is often caused by a misplaced hyphen.
@@ -206,14 +207,14 @@ skip-syntax-forward and skip-syntax-backward.
     the repeated sequence, resulting in a*\(?:c[ab]+\)* in the example
     above.
 
-  - End-of-line anchor followed by non-newline
-  - Non-newline followed by line-start anchor
+  - Non-newline follows end-of-line anchor
+  - Line-start anchor follows non-newline
 
     A pattern that does not match a newline occurs right after an
     end-of-line anchor ($) or before a line-start anchor (^).
     This combination can never match.
 
-  - End-of-text anchor followed by non-empty pattern
+  - Non-empty pattern follows end-of-text anchor
 
     A pattern that only matches a non-empty string occurs right after
     an end-of-text anchor (\'). This combination can never match.
diff --git a/relint-test.el b/relint-test.el
index 0b1999cefb..fd7f3c3e69 100644
--- a/relint-test.el
+++ b/relint-test.el
@@ -138,13 +138,13 @@ and a path."
           (should (equal
                    (relint-buffer buf)
                    '(("In call to looking-at: Repetition of repetition"
-                      20 28 "broken**regexp" 7 warning)
+                      20 28 28 "broken**regexp" 7 7 warning)
                      ("In call to looking-at: Unescaped literal `^'"
-                      50 nil "^^" 1 warning)
+                      50 nil nil "^^" 1 1 warning)
                      ("In call to looking-at: Duplicated `g' inside character 
alternative"
-                      82 105 "abcdef[gg]" 8 warning)
+                      82 105 105 "abcdef[gg]" 8 8 warning)
                      ("In call to string-match: Unterminated character 
alternative"
-                      125 nil "[xy" nil error)))))
+                      125 126 128 "[xy" 0 2 error)))))
       (kill-buffer buf))))
 
 (ert-deftest relint-buffer-huge ()
@@ -161,8 +161,9 @@ and a path."
              (insert "(message \"goodbye\\! everyone!\")\n")
              (let ((text-quoting-style 'grave))
                (relint-buffer (current-buffer))))
-           '(("Ineffective string escape `\\?'" 16 nil nil nil warning)
-             ("Ineffective string escape `\\!'" 1288960 nil nil nil 
warning)))))
+           '(("Ineffective string escape `\\?'" nil 16 17 nil nil nil warning)
+             ("Ineffective string escape `\\!'" nil 1288960 1288961 nil nil nil
+              warning)))))
 
 (ert-deftest relint-bad-hex-escape ()
   ;; Test the bad \x character escape warning. We do this separately because
@@ -182,9 +183,9 @@ and a path."
                      ;; Ignore 'invalid escape char syntax' error.
                      (remove (assoc err diags) diags)
                      '(("Character escape `\\x' not followed by hex digit"
-                        15 nil nil nil warning)
+                        nil 15 16  nil nil nil warning)
                        ("Character escape `\\x' not followed by hex digit"
-                        19 nil nil nil warning)
+                        nil 19 20 nil nil nil warning)
                        )))))
       (kill-buffer buf))))
 
@@ -203,6 +204,6 @@ and a path."
             "In call to looking-at: Possibly mistyped `:?' at start of group"))
       (should (equal warnings
                      (and checks
-                          `((,msg 13 17 "\\(:?xy\\)+" 2 warning))))))))
+                          `((,msg 13 17 18 "\\(:?xy\\)+" 2 3 warning))))))))
 
 (provide 'relint-test)
diff --git a/relint.el b/relint.el
index 5f4e8d67cd..082d3fd97f 100644
--- a/relint.el
+++ b/relint.el
@@ -140,13 +140,18 @@ list indices to follow to target)."
       (goto-char (match-end 0)))
     (point)))
 
-(defun relint--string-pos (pos n)
+(defun relint--string-pos (pos n endp)
   "Position of character N in a string expression at POS,
-or nil if no position could be determined."
+or nil if no position could be determined.
+If ENDP is true, use the last position of the character, otherwise the first,
+in case it occupies more than one position in the buffer."
   (save-excursion
     (goto-char pos)
     (pcase (read (current-buffer))
-      ((pred stringp) (relint--literal-string-pos pos n))
+      ((pred stringp)
+       (if endp
+           (1- (relint--literal-string-pos pos (1+ n)))
+         (relint--literal-string-pos pos n)))
       (`(concat . ,args)
        ;; Find out in which argument the sought position is.
        (let ((index 1))
@@ -157,7 +162,9 @@ or nil if no position could be determined."
          (and args (stringp (car args))
               (let ((string-pos
                      (relint--pos-from-start-pos-path pos (list index))))
-                (relint--literal-string-pos string-pos n))))))))
+                (if endp
+                    (1- (relint--literal-string-pos string-pos (1+ n)))
+                  (relint--literal-string-pos string-pos n)))))))))
 
 (defun relint--suppression (pos message)
   "Whether there is a suppression for MESSAGE at POS."
@@ -193,23 +200,41 @@ or nil if no position could be determined."
       (relint--add-to-error-buffer error-buffer (concat string "\n"))
     (message "%s" string)))
 
+(defun relint--col-at-pos (pos)
+ (save-excursion
+   (goto-char pos)
+   (1+ (current-column))))
+
 (cl-defun relint--output-report (error-buffer file
-                                 (message expr-pos error-pos
-                                  str str-idx severity))
-  (let* ((pos (or error-pos expr-pos))
-         (line (line-number-at-pos pos t))
-         (col (save-excursion
-                (goto-char pos)
-                (1+ (current-column)))))
+                                 (message expr-pos beg-pos end-pos
+                                  str beg-idx end-idx severity))
+  (let* ((beg (or beg-pos expr-pos))
+         (end end-pos)
+         (beg-line (line-number-at-pos beg t))
+         (end-line (cond ((eq beg end) beg-line)
+                         (end (line-number-at-pos end t))))
+         (beg-col (relint--col-at-pos beg))
+         (end-col (cond ((eq beg end) beg-col)
+                        (end (relint--col-at-pos end))))
+         (loc-str (cond
+                   ((and end-line (< beg-line end-line))
+                    (format "%d.%d-%d.%d" beg-line beg-col end-line end-col))
+                   (end-col
+                    (format "%d:%d-%d" beg-line beg-col end-col))
+                   (t
+                    (format "%d:%d" beg-line beg-col)))))
     (relint--output-message
      error-buffer
      (concat
-      (format "%s:%d:%d: " file line col)
+      (format "%s:%s: " file loc-str)
       (and (eq severity 'error) "error: ")
       message
-      (and str-idx (format " (pos %d)" str-idx))
+      (cond ((and beg-idx end-idx (< beg-idx end-idx))
+             (format " (pos %d..%d)" beg-idx end-idx))
+            (beg-idx (format " (pos %d)" beg-idx)))
       (and str     (format "\n  %s" (relint--quote-string str)))
-      (and str-idx (format "\n   %s" (relint--caret-string str str-idx)))))))
+      (and beg-idx (format "\n   %s" (relint--caret-string
+                                      str beg-idx end-idx)))))))
   
 (defun relint--output-complaints (buffer file complaints error-buffer)
   (with-current-buffer buffer
@@ -217,21 +242,45 @@ or nil if no position could be determined."
       (relint--output-report error-buffer file complaint))))
 
 (defvar relint--suppression-count)
-(defvar relint--complaints)
-
-(defun relint--report (start-pos path message str str-idx severity)
+(defvar relint--complaints
+  ;; list of (MESSAGE EXPR-POS BEG-POS END-POS STR BEG-IDX END-IDX SEVERITY)
+  ;; MESSAGE  string of message
+  ;; EXPR-POS position of expression or nil
+  ;; BEG-POS  exact first position of error or nil
+  ;; END-POS  exact last position of error or nil
+  ;; STR      string that is the subject of message
+  ;; BEG-IDX  starting index into STR or nil
+  ;; END-IDX  ending index into STR or nil
+  ;; SEVERITY `error' or `warning'
+  )
+
+(defun relint--report (message expr-pos beg-pos end-pos
+                       str beg-idx end-idx severity)
+  (if (relint--suppression (or expr-pos beg-pos) message)
+      (setq relint--suppression-count (1+ relint--suppression-count))
+    (push (list message expr-pos beg-pos end-pos str beg-idx end-idx severity)
+          relint--complaints)))
+
+(defun relint--report-at-path (start-pos path msg str beg-idx end-idx severity)
   (let* ((expr-pos (relint--pos-from-start-pos-path start-pos path))
-         (error-pos (and str-idx (relint--string-pos expr-pos str-idx))))
-    (if (relint--suppression expr-pos message)
-        (setq relint--suppression-count (1+ relint--suppression-count))
-      (push (list message expr-pos error-pos str str-idx severity)
-            relint--complaints))))
+         (beg-pos (and beg-idx (relint--string-pos expr-pos beg-idx nil)))
+         (end-pos (and end-idx (relint--string-pos expr-pos end-idx t))))
+    (relint--report msg expr-pos beg-pos end-pos str beg-idx end-idx 
severity)))
+
+;; FIXME: need a way to report a diagnostic for an interval-location
+;; whose extent is a Lisp expression!
+
+(defun relint--warn-at (beg-pos end-pos message)
+  (relint--report message nil beg-pos end-pos nil nil nil 'warning))
 
 (defun relint--warn (start-pos path message &optional str str-idx)
-  (relint--report start-pos path message str str-idx 'warning))
+  (relint--report-at-path start-pos path message str str-idx str-idx 'warning))
+
+(defun relint--err-at (pos message)
+  (relint--report message nil pos nil nil nil nil 'error))
 
 (defun relint--err (start-pos path message &optional str str-idx)
-  (relint--report start-pos path message str str-idx 'error))
+  (relint--report-at-path start-pos path message str str-idx str-idx 'error))
 
 (defun relint--escape-string (str escape-printable)
   (replace-regexp-in-string
@@ -254,10 +303,17 @@ or nil if no position could be determined."
 (defun relint--quote-string (str)
   (concat "\"" (relint--escape-string str t) "\""))
 
-(defun relint--caret-string (string pos)
-  (let ((quoted-pos
-         (length (relint--escape-string (substring string 0 pos) t))))
-    (concat (make-string quoted-pos ?.) "^")))
+(defun relint--caret-string (string beg end)
+  (let* ((beg-col
+          (length (relint--escape-string (substring string 0 beg) t)))
+         (end-col
+          (if end
+              ;; handle escaped chars such as \n
+              (1- (length (relint--escape-string
+                           (substring string 0 (1+ end)) t)))
+            beg-col)))
+    (concat (make-string beg-col ?.)
+            (make-string (- end-col beg-col -1) ?^))))
 
 (defun relint--expand-name (name)
   (pcase-exhaustive name
@@ -278,9 +334,13 @@ or nil if no position could be determined."
                          string nil)
             nil))))
     (dolist (c complaints)
-      (relint--warn pos path
-                    (format "In %s: %s" (relint--expand-name name) (cdr c))
-                    string (car c)))))
+      (let* ((beg (nth 0 c))
+             (end (nth 1 c))
+             (msg (nth 2 c))
+             (severity (nth 3 c)))
+        (relint--report-at-path
+         pos path (format "In %s: %s" (relint--expand-name name) msg)
+         string beg end severity)))))
 
 (defun relint--check-skip-set (skip-set-string name pos path)
   (relint--check-string skip-set-string #'xr-skip-set-lint name pos path))
@@ -793,6 +853,7 @@ not be evaluated safely."
 
        ;; sort: accept missing items in the list argument.
        ((eq head 'sort)
+        ;; FIXME: handle new-style `sort' args
         (let* ((arg (relint--eval-list (car body)))
                (seq (cond ((listp arg) (remq nil arg))
                           ((sequencep arg) (copy-sequence arg))
@@ -2392,10 +2453,10 @@ Return a list of (FORM . STARTING-POSITION)."
              (goto-char pos)
              (forward-sexp 1))
             (t
-             (relint--err (point) nil (prin1-to-string err))
+             (relint--err-at (point) (prin1-to-string err))
              (setq keep-going nil))))
           (error
-           (relint--err (point) nil (prin1-to-string err))
+           (relint--err-at (point) (prin1-to-string err))
            (setq keep-going nil)))
         (when (consp form)
           (push (cons form pos) forms))))
@@ -2444,13 +2505,13 @@ STRING-START is the start of the string literal (first 
double quote)."
     (unless (or (bolp)
                 (and (memq c '(?\( ?\) ?\[ ?\] ?\'))
                      (relint--in-doc-string-p string-start)))
-      (relint--warn (point) nil
-                    (if (eq c ?x)
-                        (format-message
-                         "Character escape `\\x' not followed by hex digit")
-                      (format-message
-                       "Ineffective string escape `\\%s'"
-                       (relint--escape-string (char-to-string c) nil)))))))
+      (relint--warn-at (point) (1+ (point))
+                       (if (eq c ?x)
+                           (format-message
+                            "Character escape `\\x' not followed by hex digit")
+                         (format-message
+                          "Ineffective string escape `\\%s'"
+                          (relint--escape-string (char-to-string c) nil)))))))
 
 (defun relint--check-for-misplaced-backslashes ()
   "Check for misplaced backslashes in the current buffer."
@@ -2480,6 +2541,18 @@ STRING-START is the start of the string literal (first 
double quote)."
         (unless (eobp)
           (forward-char 1))))))
 
+(defalias 'relint--sort-with-key
+  (if (>= emacs-major-version 30)
+      (lambda (key-fun list)
+        (with-suppressed-warnings ((callargs sort))  ; hush emacs <30
+          (sort list :key key-fun :in-place t)))
+    (lambda (key-fun list) 
+      (mapcar #'cdr (sort (mapcar (lambda (x) (cons (funcall key-fun x) x))
+                                  list)
+                          #'car-less-than-car))))
+  "Sort LIST using KEY-FUN for each element as the key.
+The keys are sorted numerically, in ascending order.")
+
 (defun relint--scan-current-buffer ()
   (let* ((relint--suppression-count 0)
          (relint--complaints nil)
@@ -2501,16 +2574,13 @@ STRING-START is the start of the string literal (first 
double quote)."
     (relint--check-for-misplaced-backslashes)
     (let ((complaints (nreverse relint--complaints)))
       (cons
-       (sort complaints
-             ;; Sort by error position if available, expression position
-             ;; otherwise.
-             (lambda (a b)
-               (let ((expr-pos-a (nth 1 a))
-                     (expr-pos-b (nth 1 b))
-                     (error-pos-a (nth 2 a))
-                     (error-pos-b (nth 2 b)))
-                 (< (or error-pos-a expr-pos-a)
-                    (or error-pos-b expr-pos-b)))))
+       (relint--sort-with-key
+        ;; Sort by error position if available, expression position otherwise.
+        (lambda (x)
+          (let ((expr-pos (nth 1 x))
+                (error-pos (nth 2 x)))
+            (or error-pos expr-pos)))
+        complaints)
        relint--suppression-count))))
 
 (defvar relint-last-target nil
@@ -2614,7 +2684,7 @@ TARGET is the file or directory to use for a repeated 
run."
 (defun relint--scan-buffer (buffer)
   "Scan BUFFER; return (COMPLAINTS . SUPPRESSED) where
 COMPLAINTS is a list of (unsuppressed) diagnostics each on the form
-   (MESSAGE EXPR-POS ERROR-POS STRING STRING-IDX SEVERITY)
+   (MESSAGE EXPR-POS BEG-POS END-POS STRING BEG-IDX END-IDX SEVERITY)
 and SUPPRESSED is the number of suppressed diagnostics."
   (with-current-buffer buffer
     (unless (derived-mode-p 'emacs-lisp-mode)
@@ -2665,15 +2735,19 @@ The buffer must be in emacs-lisp-mode."
   "Scan BUFFER for regexp errors. Return list of diagnostics.
 Each element in the returned list has the form
 
-  (MESSAGE EXPR-POS ERROR-POS STRING STRING-IDX SEVERITY),
+  (MESSAGE EXPR-POS BEG-POS END-POS STRING BEG-IDX END-IDX SEVERITY)
+
+where
+
+  MESSAGE is the message string
+  EXPR-POS the location of the flawed expression or nil
+  BEG-POS and END-POS the exact boundaries of the error or nil if unavailable
+  STRING is nil or a string to which the message pertains
+  BEG-IDX and END-IDX are bounds in STRING or nil,
+  and SEVERITY is `error' or `warning'.
 
-where MESSAGE is the message string, EXPR-POS the location of the
-flawed expression, ERROR-POS the exact position of the error or
-nil if unavailable, STRING is nil or a string to which the
-message pertains, STRING-IDX is nil or an index into STRING,
-and SEVERITY is `error' or `warning'.
-The intent is that ERROR-POS is the position in the buffer that
-corresponds to STRING at STRING-IDX, if such a location can be
+The intent is that BEG-POS..END-POS is the buffer range that
+corresponds to STRING at BEG-IDX..END-IDX, if such a location can be
 determined."
   (car (relint--scan-buffer buffer)))
 
diff --git a/test/1.expected b/test/1.expected
index d2bad60c4d..b7466aa10d 100644
--- a/test/1.expected
+++ b/test/1.expected
@@ -1,121 +1,121 @@
-1.elisp:6:25: In bad-regexp: Duplicated `A' inside character alternative (pos 
2)
+1.elisp:6:25-25: In bad-regexp: Duplicated `A' inside character alternative 
(pos 2)
   "[AA]"
    ..^
-1.elisp:7:24: In bad-regex: Duplicated `A' inside character alternative (pos 2)
+1.elisp:7:24-24: In bad-regex: Duplicated `A' inside character alternative 
(pos 2)
   "[AA]"
    ..^
-1.elisp:8:21: In bad-re: Duplicated `A' inside character alternative (pos 2)
+1.elisp:8:21-21: In bad-re: Duplicated `A' inside character alternative (pos 2)
   "[AA]"
    ..^
-1.elisp:9:26: In bad-pattern: Duplicated `A' inside character alternative (pos 
2)
+1.elisp:9:26-26: In bad-pattern: Duplicated `A' inside character alternative 
(pos 2)
   "[AA]"
    ..^
-1.elisp:11:30: In bad-regexps: Unescaped literal `+' (pos 0)
+1.elisp:11:30-30: In bad-regexps: Unescaped literal `+' (pos 0)
   "+a"
    ^
-1.elisp:12:30: In bad-regexes: Unescaped literal `+' (pos 0)
+1.elisp:12:30-30: In bad-regexes: Unescaped literal `+' (pos 0)
   "+b"
    ^
-1.elisp:13:34: In bad-regexp-list: Unescaped literal `+' (pos 0)
+1.elisp:13:34-34: In bad-regexp-list: Unescaped literal `+' (pos 0)
   "+c"
    ^
-1.elisp:14:33: In bad-regex-list: Unescaped literal `+' (pos 0)
+1.elisp:14:33-33: In bad-regex-list: Unescaped literal `+' (pos 0)
   "+d"
    ^
-1.elisp:15:30: In bad-re-list: Unescaped literal `+' (pos 0)
+1.elisp:15:30-30: In bad-re-list: Unescaped literal `+' (pos 0)
   "+e"
    ^
-1.elisp:17:36: In bad-regexp-alist: Unescaped literal `*' (pos 0)
+1.elisp:17:36-36: In bad-regexp-alist: Unescaped literal `*' (pos 0)
   "**"
    ^
-1.elisp:17:43: In bad-regexp-alist: Unescaped literal `?' (pos 0)
+1.elisp:17:43-43: In bad-regexp-alist: Unescaped literal `?' (pos 0)
   "??"
    ^
-1.elisp:17:55: In bad-regexp-alist: Unescaped literal `^' (pos 1)
+1.elisp:17:55-55: In bad-regexp-alist: Unescaped literal `^' (pos 1)
   ".^"
    .^
-1.elisp:17:61: In bad-regexp-alist: Unescaped literal `$' (pos 0)
+1.elisp:17:61-61: In bad-regexp-alist: Unescaped literal `$' (pos 0)
   "$."
    ^
-1.elisp:18:35: In bad-regex-alist: Unescaped literal `*' (pos 0)
+1.elisp:18:35-35: In bad-regex-alist: Unescaped literal `*' (pos 0)
   "**"
    ^
-1.elisp:18:42: In bad-regex-alist: Unescaped literal `?' (pos 0)
+1.elisp:18:42-42: In bad-regex-alist: Unescaped literal `?' (pos 0)
   "??"
    ^
-1.elisp:18:54: In bad-regex-alist: Unescaped literal `^' (pos 1)
+1.elisp:18:54-54: In bad-regex-alist: Unescaped literal `^' (pos 1)
   ".^"
    .^
-1.elisp:18:60: In bad-regex-alist: Unescaped literal `$' (pos 0)
+1.elisp:18:60-60: In bad-regex-alist: Unescaped literal `$' (pos 0)
   "$."
    ^
-1.elisp:19:32: In bad-re-alist: Unescaped literal `*' (pos 0)
+1.elisp:19:32-32: In bad-re-alist: Unescaped literal `*' (pos 0)
   "**"
    ^
-1.elisp:19:39: In bad-re-alist: Unescaped literal `?' (pos 0)
+1.elisp:19:39-39: In bad-re-alist: Unescaped literal `?' (pos 0)
   "??"
    ^
-1.elisp:19:51: In bad-re-alist: Unescaped literal `^' (pos 1)
+1.elisp:19:51-51: In bad-re-alist: Unescaped literal `^' (pos 1)
   ".^"
    .^
-1.elisp:19:57: In bad-re-alist: Unescaped literal `$' (pos 0)
+1.elisp:19:57-57: In bad-re-alist: Unescaped literal `$' (pos 0)
   "$."
    ^
-1.elisp:20:37: In bad-pattern-alist: Unescaped literal `*' (pos 0)
+1.elisp:20:37-37: In bad-pattern-alist: Unescaped literal `*' (pos 0)
   "**"
    ^
-1.elisp:20:44: In bad-pattern-alist: Unescaped literal `?' (pos 0)
+1.elisp:20:44-44: In bad-pattern-alist: Unescaped literal `?' (pos 0)
   "??"
    ^
-1.elisp:20:56: In bad-pattern-alist: Unescaped literal `^' (pos 1)
+1.elisp:20:56-56: In bad-pattern-alist: Unescaped literal `^' (pos 1)
   ".^"
    .^
-1.elisp:20:62: In bad-pattern-alist: Unescaped literal `$' (pos 0)
+1.elisp:20:62-62: In bad-pattern-alist: Unescaped literal `$' (pos 0)
   "$."
    ^
-1.elisp:22:41: In bad-mode-alist: Unescaped literal `?' (pos 0)
+1.elisp:22:41-41: In bad-mode-alist: Unescaped literal `?' (pos 0)
   "??"
    ^
-1.elisp:22:53: In bad-mode-alist: Unescaped literal `^' (pos 1)
+1.elisp:22:53-53: In bad-mode-alist: Unescaped literal `^' (pos 1)
   ".^"
    .^
-1.elisp:26:40: In bad-rules-list (eins): Unescaped literal `$' (pos 0)
+1.elisp:26:40-40: In bad-rules-list (eins): Unescaped literal `$' (pos 0)
   "$$"
    ^
-1.elisp:29:41: In bad-rules-list (zwei): Reversed range `a-Z' matches nothing 
(pos 1)
+1.elisp:29:41-43: In bad-rules-list (zwei): Reversed range `a-Z' matches 
nothing (pos 1..3)
   "[a-Z]"
-   .^
-1.elisp:31:40: In bad-font-lock-keywords (tag): Duplicated `x' inside 
character alternative (pos 2)
+   .^^^
+1.elisp:31:40-40: In bad-font-lock-keywords (tag): Duplicated `x' inside 
character alternative (pos 2)
   "[xx]"
    ..^
-1.elisp:31:54: In bad-font-lock-keywords: Duplicated `y' inside character 
alternative (pos 2)
+1.elisp:31:54-54: In bad-font-lock-keywords: Duplicated `y' inside character 
alternative (pos 2)
   "[yy]"
    ..^
-1.elisp:32:45: In more-font-lock-keywords-bad (tag): Duplicated `u' inside 
character alternative (pos 2)
+1.elisp:32:45-45: In more-font-lock-keywords-bad (tag): Duplicated `u' inside 
character alternative (pos 2)
   "[uu]"
    ..^
-1.elisp:32:59: In more-font-lock-keywords-bad: Duplicated `v' inside character 
alternative (pos 2)
+1.elisp:32:59-59: In more-font-lock-keywords-bad: Duplicated `v' inside 
character alternative (pos 2)
   "[vv]"
    ..^
-1.elisp:33:39: In font-lock-add-keywords (tag): Duplicated `s' inside 
character alternative (pos 2)
+1.elisp:33:39-39: In font-lock-add-keywords (tag): Duplicated `s' inside 
character alternative (pos 2)
   "[ss]"
    ..^
-1.elisp:33:53: In font-lock-add-keywords: Duplicated `t' inside character 
alternative (pos 2)
+1.elisp:33:53-53: In font-lock-add-keywords: Duplicated `t' inside character 
alternative (pos 2)
   "[tt]"
    ..^
-1.elisp:36:23: In bad-var-1: Unescaped literal `^' (pos 1)
+1.elisp:36:23-23: In bad-var-1: Unescaped literal `^' (pos 1)
   "a^"
    .^
-1.elisp:38:22: In bad-var-2: Duplicated `z' inside character alternative (pos 
2)
+1.elisp:38:22-22: In bad-var-2: Duplicated `z' inside character alternative 
(pos 2)
   "[zz]"
    ..^
-1.elisp:40:24: In bad-var-3: Reversed range `o-O' matches nothing (pos 1)
+1.elisp:40:24-26: In bad-var-3: Reversed range `o-O' matches nothing (pos 1..3)
   "[o-O]"
-   .^
-1.elisp:46:28: In bad-custom-1: Duplicated `n' inside character alternative 
(pos 2)
+   .^^^
+1.elisp:46:28-28: In bad-custom-1: Duplicated `n' inside character alternative 
(pos 2)
   "[nn]"
    ..^
-1.elisp:50:28: In bad-custom-2: Duplicated `s' inside character alternative 
(pos 2)
+1.elisp:50:28-28: In bad-custom-2: Duplicated `s' inside character alternative 
(pos 2)
   "[ss]"
    ..^
 1.elisp:57:9: In bad-custom-3-regexp: Unescaped literal `+' (pos 0)
@@ -127,15 +127,16 @@
 1.elisp:57:9: In bad-custom-3-regexp: Unescaped literal `^' (pos 2)
   "^c^"
    ..^
-1.elisp:64:19: In bad-custom-4-regexp: Unescaped literal `+' (pos 0)
+1.elisp:64:19-19: In bad-custom-4-regexp: Unescaped literal `+' (pos 0)
   "+b"
    ^
-1.elisp:65:44: In bad-custom-5: Unescaped literal `^' (pos 2)
+1.elisp:65:44-44: In bad-custom-5: Unescaped literal `^' (pos 2)
   "^x^"
    ..^
-1.elisp:69:53: error: In bad-custom-6: No character class `[:bah:]'
+1.elisp:69:55-61: error: In bad-custom-6: No character class `[:bah:]' (pos 
1..7)
   "[[:bah:]]"
-1.elisp:73:35: In bad-custom-7: Duplicated `a' inside character alternative 
(pos 2)
+   .^^^^^^^
+1.elisp:73:35-35: In bad-custom-7: Duplicated `a' inside character alternative 
(pos 2)
   "[aa]"
    ..^
 1.elisp:80:9: In bad-custom-8: Duplicated `1' inside character alternative 
(pos 2)
@@ -150,42 +151,42 @@
 1.elisp:89:9: In bad-custom-10: Duplicated `4' inside character alternative 
(pos 2)
   "[44]"
    ..^
-1.elisp:93:11: In compilation-error-regexp-alist-alist (aa): Unescaped literal 
`^' (pos 1)
+1.elisp:93:11-11: In compilation-error-regexp-alist-alist (aa): Unescaped 
literal `^' (pos 1)
   "a^a"
    .^
-1.elisp:94:11: In compilation-error-regexp-alist-alist (bb): Unescaped literal 
`$' (pos 1)
+1.elisp:94:11-11: In compilation-error-regexp-alist-alist (bb): Unescaped 
literal `$' (pos 1)
   "b$b"
    .^
-1.elisp:99:8: In define-generic-mode my-mode: Unescaped literal `^' (pos 1)
+1.elisp:99:8-8: In define-generic-mode my-mode: Unescaped literal `^' (pos 1)
   "1^"
    .^
-1.elisp:100:8: In define-generic-mode my-mode: Unescaped literal `^' (pos 1)
+1.elisp:100:8-8: In define-generic-mode my-mode: Unescaped literal `^' (pos 1)
   "2^"
    .^
-1.elisp:101:12: In define-generic-mode my-mode: Repetition of repetition (pos 
2)
+1.elisp:101:12-12: In define-generic-mode my-mode: Repetition of repetition 
(pos 2)
   "b++"
    ..^
-1.elisp:107:6: In call to syntax-propertize-rules: Unescaped literal `$' (pos 
0)
+1.elisp:107:6-6: In call to syntax-propertize-rules: Unescaped literal `$' 
(pos 0)
   "$1$"
    ^
-1.elisp:108:8: In call to syntax-propertize-rules: Unescaped literal `^' (pos 
2)
+1.elisp:108:8-8: In call to syntax-propertize-rules: Unescaped literal `^' 
(pos 2)
   "^2^"
    ..^
-1.elisp:113:6: In call to syntax-propertize-precompile-rules: Unescaped 
literal `$' (pos 0)
+1.elisp:113:6-6: In call to syntax-propertize-precompile-rules: Unescaped 
literal `$' (pos 0)
   "$3$"
    ^
-1.elisp:114:8: In call to syntax-propertize-precompile-rules: Unescaped 
literal `^' (pos 2)
+1.elisp:114:8-8: In call to syntax-propertize-precompile-rules: Unescaped 
literal `^' (pos 2)
   "^4^"
    ..^
-1.elisp:116:50: In my-ts--font-lock-rules: Duplicated `5' inside character 
alternative (pos 2)
+1.elisp:116:50-50: In my-ts--font-lock-rules: Duplicated `5' inside character 
alternative (pos 2)
   "[55]"
    ..^
-1.elisp:117:54: In my-ts-mode-font-lock-rules: Duplicated `6' inside character 
alternative (pos 2)
+1.elisp:117:54-54: In my-ts-mode-font-lock-rules: Duplicated `6' inside 
character alternative (pos 2)
   "[66]"
    ..^
-1.elisp:121:16: Suspect range `+-/' (pos 0)
+1.elisp:121:16-16: Suspect range `+-/' (pos 0)
   "+-/"
    ^
-1.elisp:124:16: Suspect range `+-/' (pos 0)
+1.elisp:124:16-16: Suspect range `+-/' (pos 0)
   "+-/"
    ^
diff --git a/test/10.expected b/test/10.expected
index c4af0ed618..625e9f7b9c 100644
--- a/test/10.expected
+++ b/test/10.expected
@@ -1,10 +1,10 @@
-10.elisp:7:17: In test-1-regexp-list: Duplicated `a' inside character 
alternative (pos 2)
+10.elisp:7:17-17: In test-1-regexp-list: Duplicated `a' inside character 
alternative (pos 2)
   "[aa]"
    ..^
-10.elisp:8:21: In test-1-regexp-list: Duplicated `b' inside character 
alternative (pos 2)
+10.elisp:8:21-21: In test-1-regexp-list: Duplicated `b' inside character 
alternative (pos 2)
   "[bb]"
    ..^
-10.elisp:11:13: In test-2-regexp-alist: Duplicated `c' inside character 
alternative (pos 2)
+10.elisp:11:13-13: In test-2-regexp-alist: Duplicated `c' inside character 
alternative (pos 2)
   "[cc]"
    ..^
 10.elisp:12:5: In test-2-regexp-alist: Duplicated `d' inside character 
alternative (pos 2)
@@ -19,15 +19,15 @@
 10.elisp:14:5: In test-2-regexp-alist: Duplicated `g' inside character 
alternative (pos 2)
   "[gg]"
    ..^
-10.elisp:15:13: In test-2-regexp-alist: Duplicated `h' inside character 
alternative (pos 2)
+10.elisp:15:13-13: In test-2-regexp-alist: Duplicated `h' inside character 
alternative (pos 2)
   "[hh]"
    ..^
-10.elisp:16:24: Single-character range `z-z' (pos 0)
+10.elisp:16:24-24: Single-character range `z-z' (pos 0)
   "z-z"
    ^
-10.elisp:20:9: In test-3-regexp-alist: Duplicated `i' inside character 
alternative (pos 2)
+10.elisp:20:9-9: In test-3-regexp-alist: Duplicated `i' inside character 
alternative (pos 2)
   "[ii]"
    ..^
-10.elisp:20:18: In test-3-regexp-alist: Duplicated `j' inside character 
alternative (pos 2)
+10.elisp:20:18-18: In test-3-regexp-alist: Duplicated `j' inside character 
alternative (pos 2)
   "[jj]"
    ..^
diff --git a/test/11.expected b/test/11.expected
index c48a42b73e..ce666a5a17 100644
--- a/test/11.expected
+++ b/test/11.expected
@@ -1,28 +1,28 @@
-11.elisp:7:23: Duplicated character `c' (pos 2)
+11.elisp:7:23-23: Duplicated character `c' (pos 2)
   "abc"
    ..^
 11.elisp:7:26: Duplicated character `b'
-11.elisp:8:30: Single-character range `c-c' (pos 4)
+11.elisp:8:30-30: Single-character range `c-c' (pos 4)
   "0-9ac-ceg-h3"
    ....^
-11.elisp:8:34: Two-character range `g-h' (pos 8)
+11.elisp:8:34-34: Two-character range `g-h' (pos 8)
   "0-9ac-ceg-h3"
    ........^
-11.elisp:8:37: Character `3' included in range `0-9' (pos 11)
+11.elisp:8:37-37: Character `3' included in range `0-9' (pos 11)
   "0-9ac-ceg-h3"
    ...........^
 11.elisp:9:25: Range `f-t' overlaps previous `a-m'
-11.elisp:9:37: Character `s' included in range `f-t' (pos 1)
+11.elisp:9:37-37: Character `s' included in range `f-t' (pos 1)
   "!s"
    .^
 11.elisp:10:45: Duplicated class `space'
-11.elisp:11:21: Suspect range `)-+' (pos 4)
+11.elisp:11:21-21: Suspect range `)-+' (pos 4)
   "0-9()-+"
    ....^
-11.elisp:12:20: Suspect range `+-.' (pos 3)
+11.elisp:12:20-20: Suspect range `+-.' (pos 3)
   "0-9+-."
    ...^
-11.elisp:15:20: Literal `-' not first or last (pos 3)
+11.elisp:15:20-20: Literal `-' not first or last (pos 3)
   "A-F-K-T"
    ...^
 11.elisp:16:4: In rx `regexp' form: Duplicated `1' inside character 
alternative (pos 2)
@@ -34,7 +34,7 @@
 11.elisp:16:4: Duplicated character `3' (pos 1)
   "33"
    .^
-11.elisp:20:29: Duplicated character `A' (pos 1)
+11.elisp:20:29-29: Duplicated character `A' (pos 1)
   "AA"
    .^
 11.elisp:21:19: Duplicated character `B' (pos 1)
@@ -48,19 +48,19 @@
    .^
 11.elisp:26:7: Duplicated character `z'
 11.elisp:26:7: Duplicated character `y'
-11.elisp:31:25: Character `\177' included in range `\000-\177' (pos 0)
+11.elisp:31:25-28: Character `\177' included in range `\000-\177' (pos 0)
   "\177"
-   ^
-11.elisp:31:32: Character `\240' included in range `\200-\377' (pos 0)
+   ^^^^
+11.elisp:31:32-35: Character `\240' included in range `\200-\377' (pos 0)
   "\240"
-   ^
+   ^^^^
 11.elisp:33:18: Character `m' included in range `a-z'
-11.elisp:34:19: Range `\000-\377' overlaps previous `a-f' (pos 0)
+11.elisp:34:19-22: Range `\000-\377' overlaps previous `a-f' (pos 0)
   "\000-\377"
-   ^
-11.elisp:35:25: Range `\000-\377' overlaps previous `\240-\277' (pos 0)
+   ^^^^
+11.elisp:35:25-28: Range `\000-\377' overlaps previous `\240-\277' (pos 0)
   "\000-\377"
-   ^
+   ^^^^
 11.elisp:37:17: Duplicated rx form in or-pattern: ?A
 11.elisp:37:20: Duplicated rx form in or-pattern: "def"
 11.elisp:38:10: Duplicated rx form in or-pattern: "abc"
diff --git a/test/12.expected b/test/12.expected
index e0ab05bcf4..6253260467 100644
--- a/test/12.expected
+++ b/test/12.expected
@@ -1,39 +1,39 @@
-12.elisp:9:6: In define-generic-mode my-mode: Possibly unescaped `.' in 
file-matching regexp (pos 0)
+12.elisp:9:6-6: In define-generic-mode my-mode: Possibly unescaped `.' in 
file-matching regexp (pos 0)
   ".aa\\'"
    ^
-12.elisp:9:20: In define-generic-mode my-mode: Use \' instead of $ in 
file-matching regexp (pos 4)
+12.elisp:9:20-20: In define-generic-mode my-mode: Use \' instead of $ in 
file-matching regexp (pos 4)
   "\\.bb$"
    .....^
-12.elisp:9:24: In define-generic-mode my-mode: Use \` instead of ^ in 
file-matching regexp (pos 0)
+12.elisp:9:24-24: In define-generic-mode my-mode: Use \` instead of ^ in 
file-matching regexp (pos 0)
   "^cc.*dd"
    ^
-12.elisp:11:39: In add-to-list: Use \' instead of $ in file-matching regexp 
(pos 4)
+12.elisp:11:39-39: In add-to-list: Use \' instead of $ in file-matching regexp 
(pos 4)
   "\\.ee$"
    .....^
-12.elisp:12:10: In auto-mode-alist: Possibly unescaped `.' in file-matching 
regexp (pos 0)
+12.elisp:12:10-10: In auto-mode-alist: Possibly unescaped `.' in file-matching 
regexp (pos 0)
   ".ff\\'"
    ^
-12.elisp:13:32: In auto-mode-alist: Possibly unescaped `.' in file-matching 
regexp (pos 0)
+12.elisp:13:32-32: In auto-mode-alist: Possibly unescaped `.' in file-matching 
regexp (pos 0)
   ".gg\\'"
    ^
-12.elisp:14:35: In auto-mode-alist: Possibly unescaped `.' in file-matching 
regexp (pos 0)
+12.elisp:14:35-35: In auto-mode-alist: Possibly unescaped `.' in file-matching 
regexp (pos 0)
   ".hh\\'"
    ^
-12.elisp:15:35: In auto-mode-alist: Possibly unescaped `.' in file-matching 
regexp (pos 0)
+12.elisp:15:35-35: In auto-mode-alist: Possibly unescaped `.' in file-matching 
regexp (pos 0)
   ".ii\\'"
    ^
-12.elisp:21:27: In call to directory-files: Possibly unescaped `.' in 
file-matching regexp (pos 0)
+12.elisp:21:27-27: In call to directory-files: Possibly unescaped `.' in 
file-matching regexp (pos 0)
   ".txt\\'"
    ^
-12.elisp:22:48: In call to directory-files-and-attributes: Use \' instead of $ 
in file-matching regexp (pos 5)
+12.elisp:22:48-48: In call to directory-files-and-attributes: Use \' instead 
of $ in file-matching regexp (pos 5)
   "\\.pas$"
    ......^
-12.elisp:23:35: In call to directory-files-recursively: Use \` instead of ^ in 
file-matching regexp (pos 0)
+12.elisp:23:35-35: In call to directory-files-recursively: Use \` instead of ^ 
in file-matching regexp (pos 0)
   "^abc"
    ^
-12.elisp:24:43: In call to modify-coding-system-alist: Use \' instead of $ in 
file-matching regexp (pos 4)
+12.elisp:24:43-43: In call to modify-coding-system-alist: Use \' instead of $ 
in file-matching regexp (pos 4)
   "\\.ml$"
    .....^
-12.elisp:25:41: In call to modify-coding-system-alist: Unescaped literal `+' 
(pos 0)
+12.elisp:25:41-41: In call to modify-coding-system-alist: Unescaped literal 
`+' (pos 0)
   "+xx$"
    ^
diff --git a/test/13.expected b/test/13.expected
index 27298a44ca..0048c1d310 100644
--- a/test/13.expected
+++ b/test/13.expected
@@ -1,30 +1,30 @@
-13.elisp:8:14: Ineffective string escape `\{'
-13.elisp:8:17: Ineffective string escape `\}'
-13.elisp:8:20: Ineffective string escape `\|'
-13.elisp:8:23: Ineffective string escape `\`'
-13.elisp:8:26: Ineffective string escape `\?'
-13.elisp:8:29: Ineffective string escape `\*'
-13.elisp:8:32: Ineffective string escape `\.'
-13.elisp:8:35: Ineffective string escape `\+'
-13.elisp:8:38: Ineffective string escape `\;'
-13.elisp:8:41: Ineffective string escape `\q'
-13.elisp:10:5: Ineffective string escape `\m'
-13.elisp:11:18: Ineffective string escape `\('
-13.elisp:11:21: Ineffective string escape `\)'
-13.elisp:11:24: Ineffective string escape `\['
-13.elisp:11:27: Ineffective string escape `\]'
-13.elisp:11:30: Ineffective string escape `\''
-13.elisp:12:17: Ineffective string escape `\;'
-13.elisp:18:23: In call to re-search-forward: Unescaped literal `$' (pos 0)
+13.elisp:8:14-15: Ineffective string escape `\{'
+13.elisp:8:17-18: Ineffective string escape `\}'
+13.elisp:8:20-21: Ineffective string escape `\|'
+13.elisp:8:23-24: Ineffective string escape `\`'
+13.elisp:8:26-27: Ineffective string escape `\?'
+13.elisp:8:29-30: Ineffective string escape `\*'
+13.elisp:8:32-33: Ineffective string escape `\.'
+13.elisp:8:35-36: Ineffective string escape `\+'
+13.elisp:8:38-39: Ineffective string escape `\;'
+13.elisp:8:41-42: Ineffective string escape `\q'
+13.elisp:10:5-6: Ineffective string escape `\m'
+13.elisp:11:18-19: Ineffective string escape `\('
+13.elisp:11:21-22: Ineffective string escape `\)'
+13.elisp:11:24-25: Ineffective string escape `\['
+13.elisp:11:27-28: Ineffective string escape `\]'
+13.elisp:11:30-31: Ineffective string escape `\''
+13.elisp:12:17-18: Ineffective string escape `\;'
+13.elisp:18:23-24: In call to re-search-forward: Unescaped literal `$' (pos 0)
   "$.x++"
    ^
-13.elisp:18:23: Ineffective string escape `\$'
-13.elisp:18:25: Ineffective string escape `\.'
-13.elisp:18:28: Ineffective string escape `\+'
-13.elisp:18:30: In call to re-search-forward: Repetition of repetition (pos 4)
+13.elisp:18:23-24: Ineffective string escape `\$'
+13.elisp:18:25-26: Ineffective string escape `\.'
+13.elisp:18:28-29: Ineffective string escape `\+'
+13.elisp:18:30-31: In call to re-search-forward: Repetition of repetition (pos 
4)
   "$.x++"
    ....^
-13.elisp:18:30: Ineffective string escape `\+'
-13.elisp:19:16: Ineffective string escape `\q'
-13.elisp:22:16: Ineffective string escape `\8'
-13.elisp:22:19: Ineffective string escape `\9'
+13.elisp:18:30-31: Ineffective string escape `\+'
+13.elisp:19:16-17: Ineffective string escape `\q'
+13.elisp:22:16-17: Ineffective string escape `\8'
+13.elisp:22:19-20: Ineffective string escape `\9'
diff --git a/test/14.expected b/test/14.expected
index 583def148d..d78367ce96 100644
--- a/test/14.expected
+++ b/test/14.expected
@@ -1,30 +1,30 @@
-14.elisp:13:23: In call to treesit-font-lock-rules: Unescaped literal `+' (pos 
0)
+14.elisp:13:23-23: In call to treesit-font-lock-rules: Unescaped literal `+' 
(pos 0)
   "+f+"
    ^
 14.elisp:14:6: In call to treesit-font-lock-rules: Unescaped literal `+' (pos 
0)
   "+g+"
    ^
-14.elisp:29:32: In call to treesit-range-rules: Unescaped literal `+' (pos 0)
+14.elisp:29:32-32: In call to treesit-range-rules: Unescaped literal `+' (pos 
0)
   "+o+"
    ^
 14.elisp:30:6: In call to treesit-range-rules: Unescaped literal `+' (pos 0)
   "+p+"
    ^
-14.elisp:33:39: In call to treesit-query-expand: Unescaped literal `+' (pos 0)
+14.elisp:33:39-39: In call to treesit-query-expand: Unescaped literal `+' (pos 
0)
   "+r+"
    ^
 14.elisp:37:4: In call to treesit-query-compile: Unescaped literal `+' (pos 0)
   "+t+"
    ^
-14.elisp:39:45: In call to treesit-node-top-level: Unescaped literal `+' (pos 
0)
+14.elisp:39:45-45: In call to treesit-node-top-level: Unescaped literal `+' 
(pos 0)
   "+u+"
    ^
-14.elisp:40:44: In call to treesit-query-capture: Unescaped literal `+' (pos 0)
+14.elisp:40:44-44: In call to treesit-query-capture: Unescaped literal `+' 
(pos 0)
   "+v+"
    ^
-14.elisp:41:42: In call to treesit-query-range: Unescaped literal `+' (pos 0)
+14.elisp:41:42-42: In call to treesit-query-range: Unescaped literal `+' (pos 
0)
   "+w+"
    ^
-14.elisp:42:43: In call to treesit-query-string: Unescaped literal `+' (pos 0)
+14.elisp:42:43-43: In call to treesit-query-string: Unescaped literal `+' (pos 
0)
   "+x+"
    ^
diff --git a/test/2.expected b/test/2.expected
index 7d36356b57..ec000b61ec 100644
--- a/test/2.expected
+++ b/test/2.expected
@@ -1,177 +1,177 @@
-2.elisp:5:18: In call to looking-at: Duplicated `a' inside character 
alternative (pos 2)
+2.elisp:5:18-18: In call to looking-at: Duplicated `a' inside character 
alternative (pos 2)
   "[aa]"
    ..^
-2.elisp:6:25: In call to re-search-forward: Duplicated `b' inside character 
alternative (pos 2)
+2.elisp:6:25-25: In call to re-search-forward: Duplicated `b' inside character 
alternative (pos 2)
   "[bb]"
    ..^
-2.elisp:7:26: In call to re-search-backward: Duplicated `c' inside character 
alternative (pos 2)
+2.elisp:7:26-26: In call to re-search-backward: Duplicated `c' inside 
character alternative (pos 2)
   "[cc]"
    ..^
-2.elisp:8:29: In call to search-forward-regexp: Duplicated `B' inside 
character alternative (pos 2)
+2.elisp:8:29-29: In call to search-forward-regexp: Duplicated `B' inside 
character alternative (pos 2)
   "[BB]"
    ..^
-2.elisp:9:29: In call to search-forward-regexp: Duplicated `C' inside 
character alternative (pos 2)
+2.elisp:9:29-29: In call to search-forward-regexp: Duplicated `C' inside 
character alternative (pos 2)
   "[CC]"
    ..^
-2.elisp:10:20: In call to string-match: Duplicated `d' inside character 
alternative (pos 2)
+2.elisp:10:20-20: In call to string-match: Duplicated `d' inside character 
alternative (pos 2)
   "[dd]"
    ..^
-2.elisp:11:22: In call to string-match-p: Duplicated `e' inside character 
alternative (pos 2)
+2.elisp:11:22-22: In call to string-match-p: Duplicated `e' inside character 
alternative (pos 2)
   "[ee]"
    ..^
-2.elisp:12:20: In call to looking-at-p: Duplicated `f' inside character 
alternative (pos 2)
+2.elisp:12:20-20: In call to looking-at-p: Duplicated `f' inside character 
alternative (pos 2)
   "[ff]"
    ..^
-2.elisp:13:20: In call to looking-back: Duplicated `g' inside character 
alternative (pos 2)
+2.elisp:13:20-20: In call to looking-back: Duplicated `g' inside character 
alternative (pos 2)
   "[gg]"
    ..^
-2.elisp:14:32: In call to replace-regexp-in-string: Duplicated `h' inside 
character alternative (pos 2)
+2.elisp:14:32-32: In call to replace-regexp-in-string: Duplicated `h' inside 
character alternative (pos 2)
   "[hh]"
    ..^
-2.elisp:15:28: In call to query-replace-regexp: Duplicated `j' inside 
character alternative (pos 2)
+2.elisp:15:28-28: In call to query-replace-regexp: Duplicated `j' inside 
character alternative (pos 2)
   "[jj]"
    ..^
-2.elisp:16:24: In call to posix-looking-at: Duplicated `k' inside character 
alternative (pos 2)
+2.elisp:16:24-24: In call to posix-looking-at: Duplicated `k' inside character 
alternative (pos 2)
   "[kk]"
    ..^
-2.elisp:17:29: In call to posix-search-backward: Duplicated `l' inside 
character alternative (pos 2)
+2.elisp:17:29-29: In call to posix-search-backward: Duplicated `l' inside 
character alternative (pos 2)
   "[ll]"
    ..^
-2.elisp:18:28: In call to posix-search-forward: Duplicated `m' inside 
character alternative (pos 2)
+2.elisp:18:28-28: In call to posix-search-forward: Duplicated `m' inside 
character alternative (pos 2)
   "[mm]"
    ..^
-2.elisp:19:26: In call to posix-string-match: Duplicated `n' inside character 
alternative (pos 2)
+2.elisp:19:26-26: In call to posix-string-match: Duplicated `n' inside 
character alternative (pos 2)
   "[nn]"
    ..^
-2.elisp:20:37: In call to load-history-filename-element: Duplicated `o' inside 
character alternative (pos 2)
+2.elisp:20:37-37: In call to load-history-filename-element: Duplicated `o' 
inside character alternative (pos 2)
   "[oo]"
    ..^
-2.elisp:21:29: In call to kill-matching-buffers: Duplicated `p' inside 
character alternative (pos 2)
+2.elisp:21:29-29: In call to kill-matching-buffers: Duplicated `p' inside 
character alternative (pos 2)
   "[pp]"
    ..^
-2.elisp:22:18: In call to keep-lines: Duplicated `q' inside character 
alternative (pos 2)
+2.elisp:22:18-18: In call to keep-lines: Duplicated `q' inside character 
alternative (pos 2)
   "[qq]"
    ..^
-2.elisp:23:19: In call to flush-lines: Duplicated `r' inside character 
alternative (pos 2)
+2.elisp:23:19-19: In call to flush-lines: Duplicated `r' inside character 
alternative (pos 2)
   "[rr]"
    ..^
-2.elisp:24:16: In call to how-many: Duplicated `s' inside character 
alternative (pos 2)
+2.elisp:24:16-16: In call to how-many: Duplicated `s' inside character 
alternative (pos 2)
   "[ss]"
    ..^
-2.elisp:25:22: In call to split-string: Duplicated `t' inside character 
alternative (pos 2)
+2.elisp:25:22-22: In call to split-string: Duplicated `t' inside character 
alternative (pos 2)
   "[tt]"
    ..^
-2.elisp:25:33: In call to split-string: Duplicated `u' inside character 
alternative (pos 2)
+2.elisp:25:33-33: In call to split-string: Duplicated `u' inside character 
alternative (pos 2)
   "[uu]"
    ..^
-2.elisp:26:34: In call to split-string-and-unquote: Duplicated `v' inside 
character alternative (pos 2)
+2.elisp:26:34-34: In call to split-string-and-unquote: Duplicated `v' inside 
character alternative (pos 2)
   "[vv]"
    ..^
-2.elisp:27:26: In call to string-trim-left: Duplicated `w' inside character 
alternative (pos 2)
+2.elisp:27:26-26: In call to string-trim-left: Duplicated `w' inside character 
alternative (pos 2)
   "[ww]"
    ..^
-2.elisp:28:27: In call to string-trim-right: Duplicated `x' inside character 
alternative (pos 2)
+2.elisp:28:27-27: In call to string-trim-right: Duplicated `x' inside 
character alternative (pos 2)
   "[xx]"
    ..^
-2.elisp:29:21: In call to string-trim: Duplicated `y' inside character 
alternative (pos 2)
+2.elisp:29:21-21: In call to string-trim: Duplicated `y' inside character 
alternative (pos 2)
   "[yy]"
    ..^
-2.elisp:29:28: In call to string-trim: Duplicated `z' inside character 
alternative (pos 2)
+2.elisp:29:28-28: In call to string-trim: Duplicated `z' inside character 
alternative (pos 2)
   "[zz]"
    ..^
-2.elisp:30:27: In call to directory-files: Unescaped literal `+' (pos 0)
+2.elisp:30:27-27: In call to directory-files: Unescaped literal `+' (pos 0)
   "+1"
    ^
-2.elisp:31:42: In call to directory-files-and-attributes: Unescaped literal 
`+' (pos 0)
+2.elisp:31:42-42: In call to directory-files-and-attributes: Unescaped literal 
`+' (pos 0)
   "+2"
    ^
-2.elisp:32:35: In call to directory-files-recursively: Unescaped literal `+' 
(pos 0)
+2.elisp:32:35-35: In call to directory-files-recursively: Unescaped literal 
`+' (pos 0)
   "+3"
    ^
-2.elisp:33:27: In call to delete-matching-lines: Unescaped literal `+' (pos 0)
+2.elisp:33:27-27: In call to delete-matching-lines: Unescaped literal `+' (pos 
0)
   "+4"
    ^
-2.elisp:34:31: In call to delete-non-matching-lines: Unescaped literal `+' 
(pos 0)
+2.elisp:34:31-31: In call to delete-non-matching-lines: Unescaped literal `+' 
(pos 0)
   "+5"
    ^
-2.elisp:35:19: In call to count-matches: Unescaped literal `+' (pos 0)
+2.elisp:35:19-19: In call to count-matches: Unescaped literal `+' (pos 0)
   "+6"
    ^
-2.elisp:36:34: In call to treesit-induce-sparse-tree: Unescaped literal `+' 
(pos 0)
+2.elisp:36:34-34: In call to treesit-induce-sparse-tree: Unescaped literal `+' 
(pos 0)
   "+7"
    ^
-2.elisp:37:30: In call to treesit-search-forward: Unescaped literal `+' (pos 0)
+2.elisp:37:30-30: In call to treesit-search-forward: Unescaped literal `+' 
(pos 0)
   "+8"
    ^
-2.elisp:38:35: In call to treesit-search-forward-goto: Unescaped literal `+' 
(pos 0)
+2.elisp:38:35-35: In call to treesit-search-forward-goto: Unescaped literal 
`+' (pos 0)
   "+9"
    ^
-2.elisp:39:30: In call to treesit-search-subtree: Unescaped literal `+' (pos 0)
+2.elisp:39:30-30: In call to treesit-search-subtree: Unescaped literal `+' 
(pos 0)
   "+10"
    ^
-2.elisp:52:17: In call to f2: Duplicated `B' inside character alternative (pos 
2)
+2.elisp:52:17-17: In call to f2: Duplicated `B' inside character alternative 
(pos 2)
   "[BB]"
    ..^
-2.elisp:52:31: In call to f2: Duplicated `D' inside character alternative (pos 
2)
+2.elisp:52:31-31: In call to f2: Duplicated `D' inside character alternative 
(pos 2)
   "[DD]"
    ..^
-2.elisp:52:45: In call to f2: Duplicated `F' inside character alternative (pos 
2)
+2.elisp:52:45-45: In call to f2: Duplicated `F' inside character alternative 
(pos 2)
   "[FF]"
    ..^
-2.elisp:52:59: In call to f2: Duplicated `H' inside character alternative (pos 
2)
+2.elisp:52:59-59: In call to f2: Duplicated `H' inside character alternative 
(pos 2)
   "[HH]"
    ..^
-2.elisp:52:73: In call to f2: Duplicated `J' inside character alternative (pos 
2)
+2.elisp:52:73-73: In call to f2: Duplicated `J' inside character alternative 
(pos 2)
   "[JJ]"
    ..^
-2.elisp:53:17: In call to s2: Duplicated `B' inside character alternative (pos 
2)
+2.elisp:53:17-17: In call to s2: Duplicated `B' inside character alternative 
(pos 2)
   "[BB]"
    ..^
-2.elisp:53:31: In call to s2: Duplicated `D' inside character alternative (pos 
2)
+2.elisp:53:31-31: In call to s2: Duplicated `D' inside character alternative 
(pos 2)
   "[DD]"
    ..^
-2.elisp:53:45: In call to s2: Duplicated `F' inside character alternative (pos 
2)
+2.elisp:53:45-45: In call to s2: Duplicated `F' inside character alternative 
(pos 2)
   "[FF]"
    ..^
-2.elisp:53:59: In call to s2: Duplicated `H' inside character alternative (pos 
2)
+2.elisp:53:59-59: In call to s2: Duplicated `H' inside character alternative 
(pos 2)
   "[HH]"
    ..^
-2.elisp:53:73: In call to s2: Duplicated `J' inside character alternative (pos 
2)
+2.elisp:53:73-73: In call to s2: Duplicated `J' inside character alternative 
(pos 2)
   "[JJ]"
    ..^
-2.elisp:54:17: In call to m2: Duplicated `B' inside character alternative (pos 
2)
+2.elisp:54:17-17: In call to m2: Duplicated `B' inside character alternative 
(pos 2)
   "[BB]"
    ..^
-2.elisp:54:31: In call to m2: Duplicated `D' inside character alternative (pos 
2)
+2.elisp:54:31-31: In call to m2: Duplicated `D' inside character alternative 
(pos 2)
   "[DD]"
    ..^
-2.elisp:54:45: In call to m2: Duplicated `F' inside character alternative (pos 
2)
+2.elisp:54:45-45: In call to m2: Duplicated `F' inside character alternative 
(pos 2)
   "[FF]"
    ..^
-2.elisp:54:59: In call to m2: Duplicated `H' inside character alternative (pos 
2)
+2.elisp:54:59-59: In call to m2: Duplicated `H' inside character alternative 
(pos 2)
   "[HH]"
    ..^
-2.elisp:54:73: In call to m2: Duplicated `J' inside character alternative (pos 
2)
+2.elisp:54:73-73: In call to m2: Duplicated `J' inside character alternative 
(pos 2)
   "[JJ]"
    ..^
-2.elisp:62:17: In call to f5: Duplicated `b' inside character alternative (pos 
2)
+2.elisp:62:17-17: In call to f5: Duplicated `b' inside character alternative 
(pos 2)
   "[bb]"
    ..^
-2.elisp:62:24: In call to f5: Duplicated `c' inside character alternative (pos 
2)
+2.elisp:62:24-24: In call to f5: Duplicated `c' inside character alternative 
(pos 2)
   "[cc]"
    ..^
-2.elisp:62:31: In call to f5: Duplicated `d' inside character alternative (pos 
2)
+2.elisp:62:31-31: In call to f5: Duplicated `d' inside character alternative 
(pos 2)
   "[dd]"
    ..^
-2.elisp:65:26: In :regexp parameter: Duplicated `1' inside character 
alternative (pos 2)
+2.elisp:65:26-26: In :regexp parameter: Duplicated `1' inside character 
alternative (pos 2)
   "[11]"
    ..^
-2.elisp:66:20: In :regex parameter: Duplicated `2' inside character 
alternative (pos 2)
+2.elisp:66:20-20: In :regex parameter: Duplicated `2' inside character 
alternative (pos 2)
   "[22]"
    ..^
-2.elisp:69:31: In call to sort-regexp-fields: Unescaped literal `$' (pos 3)
+2.elisp:69:31-31: In call to sort-regexp-fields: Unescaped literal `$' (pos 3)
   "^.*$x"
    ...^
-2.elisp:70:35: In call to sort-regexp-fields: Escaped non-special character 
`%' (pos 0)
+2.elisp:70:35-37: In call to sort-regexp-fields: Escaped non-special character 
`%' (pos 0..1)
   "\\%"
-   ^
+   ^^^
diff --git a/test/3.expected b/test/3.expected
index 2a5d99c08f..d79ea7c7a8 100644
--- a/test/3.expected
+++ b/test/3.expected
@@ -34,10 +34,10 @@
 3.elisp:137:4: In another-bad-regexp-list: Duplicated `7' inside character 
alternative (pos 2)
   "[77]"
    ..^
-3.elisp:141:13: In call to looking-at: Unescaped literal `+' (pos 0)
+3.elisp:141:13-13: In call to looking-at: Unescaped literal `+' (pos 0)
   "+abcdefgh"
    ^
-3.elisp:157:13: In call to looking-at: Unescaped literal `?' (pos 0)
+3.elisp:157:13-13: In call to looking-at: Unescaped literal `?' (pos 0)
   "?abc"
    ^
 3.elisp:164:4: In call to looking-at: Duplicated `A' inside character 
alternative (pos 2)
diff --git a/test/4.expected b/test/4.expected
index c5d7ff57e1..a7d3a1d79e 100644
--- a/test/4.expected
+++ b/test/4.expected
@@ -28,7 +28,7 @@
 4.elisp:25:15: In call to looking-at: Unescaped literal `*' (pos 0)
   "*ab"
    ^
-4.elisp:26:24: In call to looking-at: Unescaped literal `+' (pos 0)
+4.elisp:26:24-24: In call to looking-at: Unescaped literal `+' (pos 0)
   "+c"
    ^
 4.elisp:43:15: In call to looking-at: Unescaped literal `^' (pos 1)
diff --git a/test/6.expected b/test/6.expected
index e8851d53e4..cfbbf98e08 100644
--- a/test/6.expected
+++ b/test/6.expected
@@ -9,10 +9,10 @@
 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:23:19: Value from `regexp-quote' cannot be spliced into `[...]'
-6.elisp:28:24: In call to skip-chars-forward: Suspect skip set framed in 
`[...]' (pos 0)
+6.elisp:28:24-28: In call to skip-chars-forward: Suspect skip set framed in 
`[...]' (pos 0..4)
   "[a-z]"
-   ^
-6.elisp:29:26: In call to skip-chars-backward: Duplicated character `a' (pos 1)
+   ^^^^^
+6.elisp:29:26-26: In call to skip-chars-backward: Duplicated character `a' 
(pos 1)
   "aa"
    .^
 6.elisp:30:23: `some-re' cannot be used for arguments to `skip-chars-forward'
diff --git a/test/9.expected b/test/9.expected
index df8f7e682a..50f8ea7498 100644
--- a/test/9.expected
+++ b/test/9.expected
@@ -1,46 +1,46 @@
-9.elisp:6:33: In page-delimiter: Duplicated `a' inside character alternative 
(pos 2)
+9.elisp:6:33-33: In page-delimiter: Duplicated `a' inside character 
alternative (pos 2)
   "[aa]"
    ..^
-9.elisp:7:37: In paragraph-separate: Duplicated `b' inside character 
alternative (pos 2)
+9.elisp:7:37-37: In paragraph-separate: Duplicated `b' inside character 
alternative (pos 2)
   "[bb]"
    ..^
-9.elisp:8:34: In paragraph-start: Duplicated `c' inside character alternative 
(pos 2)
+9.elisp:8:34-34: In paragraph-start: Duplicated `c' inside character 
alternative (pos 2)
   "[cc]"
    ..^
-9.elisp:9:31: In sentence-end: Duplicated `d' inside character alternative 
(pos 2)
+9.elisp:9:31-31: In sentence-end: Duplicated `d' inside character alternative 
(pos 2)
   "[dd]"
    ..^
-9.elisp:10:37: In comment-start-skip: Duplicated `e' inside character 
alternative (pos 2)
+9.elisp:10:37-37: In comment-start-skip: Duplicated `e' inside character 
alternative (pos 2)
   "[ee]"
    ..^
-9.elisp:11:35: In comment-end-skip: Duplicated `f' inside character 
alternative (pos 2)
+9.elisp:11:35-35: In comment-end-skip: Duplicated `f' inside character 
alternative (pos 2)
   "[ff]"
    ..^
-9.elisp:13:25: In sentence-end: Duplicated `g' inside character alternative 
(pos 2)
+9.elisp:13:25-25: In sentence-end: Duplicated `g' inside character alternative 
(pos 2)
   "[gg]"
    ..^
-9.elisp:14:50: In paragraph-start: Duplicated `h' inside character alternative 
(pos 2)
+9.elisp:14:50-50: In paragraph-start: Duplicated `h' inside character 
alternative (pos 2)
   "[hh]"
    ..^
-9.elisp:16:32: In paragraph-separate: Duplicated `i' inside character 
alternative (pos 2)
+9.elisp:16:32-32: In paragraph-separate: Duplicated `i' inside character 
alternative (pos 2)
   "[ii]"
    ..^
-9.elisp:17:28: In page-delimiter: Duplicated `j' inside character alternative 
(pos 2)
+9.elisp:17:28-28: In page-delimiter: Duplicated `j' inside character 
alternative (pos 2)
   "[jj]"
    ..^
-9.elisp:18:35: In comment-start-skip: Duplicated `k' inside character 
alternative (pos 2)
+9.elisp:18:35-35: In comment-start-skip: Duplicated `k' inside character 
alternative (pos 2)
   "[kk]"
    ..^
-9.elisp:19:33: In comment-end-skip: Duplicated `l' inside character 
alternative (pos 2)
+9.elisp:19:33-33: In comment-end-skip: Duplicated `l' inside character 
alternative (pos 2)
   "[ll]"
    ..^
-9.elisp:22:39: In treesit-sentence-type-regexp: Unescaped literal `+' (pos 0)
+9.elisp:22:39-39: In treesit-sentence-type-regexp: Unescaped literal `+' (pos 
0)
   "+0"
    ^
-9.elisp:23:35: In treesit-sexp-type-regexp: Unescaped literal `+' (pos 0)
+9.elisp:23:35-35: In treesit-sexp-type-regexp: Unescaped literal `+' (pos 0)
   "+1"
    ^
-9.elisp:36:28: In imenu-generic-expression: Unescaped literal `+' (pos 0)
+9.elisp:36:28-28: In imenu-generic-expression: Unescaped literal `+' (pos 0)
   "+a+"
    ^
 9.elisp:37:34: In imenu-generic-expression: Unescaped literal `+' (pos 0)
@@ -49,52 +49,52 @@
 9.elisp:38:45: In treesit-simple-imenu-settings: Unescaped literal `+' (pos 0)
   "+c+"
    ^
-9.elisp:41:21: In treesit-simple-imenu-settings: Unescaped literal `+' (pos 0)
+9.elisp:41:21-21: In treesit-simple-imenu-settings: Unescaped literal `+' (pos 
0)
   "+d+"
    ^
-9.elisp:44:40: In font-lock-keywords (tag): Duplicated `m' inside character 
alternative (pos 2)
+9.elisp:44:40-40: In font-lock-keywords (tag): Duplicated `m' inside character 
alternative (pos 2)
   "[mm]"
    ..^
-9.elisp:45:34: In font-lock-keywords (tag): Duplicated `n' inside character 
alternative (pos 2)
+9.elisp:45:34-34: In font-lock-keywords (tag): Duplicated `n' inside character 
alternative (pos 2)
   "[nn]"
    ..^
-9.elisp:46:56: In font-lock-keywords (tag): Duplicated `o' inside character 
alternative (pos 2)
+9.elisp:46:56-56: In font-lock-keywords (tag): Duplicated `o' inside character 
alternative (pos 2)
   "[oo]"
    ..^
-9.elisp:52:9: In my-font-lock-keywords-2 (beta): Duplicated `q' inside 
character alternative (pos 2)
+9.elisp:52:9-9: In my-font-lock-keywords-2 (beta): Duplicated `q' inside 
character alternative (pos 2)
   "[qq]"
    ..^
 9.elisp:56:9: In font-lock-defaults (alpha): Duplicated `p' inside character 
alternative (pos 2)
   "[pp]"
    ..^
-9.elisp:62:23: In treesit-simple-indent-rules (a): Unescaped literal `+' (pos 
0)
+9.elisp:62:23-23: In treesit-simple-indent-rules (a): Unescaped literal `+' 
(pos 0)
   "+e+"
    ^
-9.elisp:62:29: In treesit-simple-indent-rules (a): Unescaped literal `+' (pos 
0)
+9.elisp:62:29-29: In treesit-simple-indent-rules (a): Unescaped literal `+' 
(pos 0)
   "+f+"
    ^
-9.elisp:62:35: In treesit-simple-indent-rules (a): Unescaped literal `+' (pos 
0)
+9.elisp:62:35-35: In treesit-simple-indent-rules (a): Unescaped literal `+' 
(pos 0)
   "+g+"
    ^
-9.elisp:63:24: In treesit-simple-indent-rules (a): Unescaped literal `+' (pos 
0)
+9.elisp:63:24-24: In treesit-simple-indent-rules (a): Unescaped literal `+' 
(pos 0)
   "+h+"
    ^
-9.elisp:63:30: In treesit-simple-indent-rules (a): Unescaped literal `+' (pos 
0)
+9.elisp:63:30-30: In treesit-simple-indent-rules (a): Unescaped literal `+' 
(pos 0)
   "+i+"
    ^
-9.elisp:63:36: In treesit-simple-indent-rules (a): Unescaped literal `+' (pos 
0)
+9.elisp:63:36-36: In treesit-simple-indent-rules (a): Unescaped literal `+' 
(pos 0)
   "+j+"
    ^
-9.elisp:64:27: In treesit-simple-indent-rules (b): Unescaped literal `+' (pos 
0)
+9.elisp:64:27-27: In treesit-simple-indent-rules (b): Unescaped literal `+' 
(pos 0)
   "+k+"
    ^
-9.elisp:65:25: In treesit-simple-indent-rules (b): Unescaped literal `+' (pos 
0)
+9.elisp:65:25-25: In treesit-simple-indent-rules (b): Unescaped literal `+' 
(pos 0)
   "+l+"
    ^
-9.elisp:66:26: In treesit-simple-indent-rules (b): Unescaped literal `+' (pos 
0)
+9.elisp:66:26-26: In treesit-simple-indent-rules (b): Unescaped literal `+' 
(pos 0)
   "+m+"
    ^
-9.elisp:69:36: In treesit-defun-type-regexp: Unescaped literal `+' (pos 0)
+9.elisp:69:36-36: In treesit-defun-type-regexp: Unescaped literal `+' (pos 0)
   "+n+"
    ^
 9.elisp:70:35: In treesit-defun-type-regexp: Unescaped literal `+' (pos 0)

Reply via email to