Repository: ota-meshi/eslint-plugin-regexp
Pull request #296 (2021-08-12 13:16:41)

Update `regexp-ast-analysis@0.2.4`
I found and fixed a nasty bug in `regexp-ast-analysis` today. All `getFirst*Char*` functions could potentially take a long time to when (directly or indirectly) dealing with word boundary assertions. So one call to `getFirstCharAfter` would take a second or longer for the right regex.


Patch:
diff --git a/lib/rules/match-any.ts b/lib/rules/match-any.ts
index f362dfe..9a312af 100644
--- a/lib/rules/match-any.ts
+++ b/lib/rules/match-any.ts
@@ -5,6 +5,7 @@ import type { RegExpContext } from "../utils"
 import { createRule, defineRegexpVisitor } from "../utils"
 import { isRegexpLiteral } from "../utils/ast-utils/utils"
 import { matchesAllCharacters } from "regexp-ast-analysis"
+import { mention } from "../utils/mention"
 
 const OPTION_SS1 = "[\\s\\S]" as const
 const OPTION_SS2 = "[\\S\\s]" as const
@@ -47,7 +48,7 @@ export default createRule("match-any", {
             },
         ],
         messages: {
-            unexpected: "Unexpected using '{{expr}}' to match any character.",
+            unexpected: "Unexpected using {{expr}} to match any character.",
         },
         type: "suggestion", // "problem",
     },
@@ -145,7 +146,7 @@ export default createRule("match-any", {
                             loc: getRegexpLocation(csNode),
                             messageId: "unexpected",
                             data: {
-                                expr: ".",
+                                expr: mention(csNode),
                             },
                             fix(fixer) {
                                 return fix(fixer, regexpContext, csNode)
@@ -163,7 +164,7 @@ export default createRule("match-any", {
                             loc: getRegexpLocation(ccNode),
                             messageId: "unexpected",
                             data: {
-                                expr: ccNode.raw,
+                                expr: mention(ccNode),
                             },
                             fix(fixer) {
                                 return fix(fixer, regexpContext, ccNode)
diff --git a/lib/rules/no-dupe-disjunctions.ts b/lib/rules/no-dupe-disjunctions.ts
index 5a35477..b20d3f8 100644
--- a/lib/rules/no-dupe-disjunctions.ts
+++ b/lib/rules/no-dupe-disjunctions.ts
@@ -30,6 +30,7 @@ import {
 import { RegExpParser } from "regexpp"
 import { UsageOfPattern } from "../utils/get-usage-of-pattern"
 import { canReorder } from "../utils/reorder-alternatives"
+import { mention } from "../utils/mention"
 
 type ParentNode = Group | CapturingGroup | Pattern | LookaroundAssertion
 
@@ -714,13 +715,13 @@ export default createRule("no-dupe-disjunctions", {
             duplicate:
                 "Unexpected duplicate alternative. This alternative can be removed.{{cap}}{{exp}}",
             subset:
-                "Unexpected useless alternative. This alternative is a strict subset of '{{others}}' and can be removed.{{cap}}{{exp}}",
+                "Unexpected useless alternative. This alternative is a strict subset of {{others}} and can be removed.{{cap}}{{exp}}",
             prefixSubset:
-                "Unexpected useless alternative. This alternative is already covered by '{{others}}' and can be removed.{{cap}}",
+                "Unexpected useless alternative. This alternative is already covered by {{others}} and can be removed.{{cap}}",
             superset:
-                "Unexpected superset. This alternative is a superset of '{{others}}'. It might be possible to remove the other alternative(s).{{cap}}{{exp}}",
+                "Unexpected superset. This alternative is a superset of {{others}}. It might be possible to remove the other alternative(s).{{cap}}{{exp}}",
             overlap:
-                "Unexpected overlap. This alternative overlaps with '{{others}}'. The overlap is '{{expr}}'.{{cap}}{{exp}}",
+                "Unexpected overlap. This alternative overlaps with {{others}}. The overlap is {{expr}}.{{cap}}{{exp}}",
         },
         type: "suggestion", // "problem",
     },
@@ -936,7 +937,9 @@ export default createRule("no-dupe-disjunctions", {
                     ? " Careful! This alternative contains capturing groups which might be difficult to remove."
                     : ""
 
-                const others = result.others.map((a) => a.raw).join("|")
+                const others = mention(
+                    result.others.map((a) => a.raw).join("|"),
+                )
 
                 switch (result.type) {
                     case "Duplicate":
@@ -984,7 +987,9 @@ export default createRule("no-dupe-disjunctions", {
                                 exp,
                                 cap,
                                 others,
-                                expr: faToSource(result.overlap, flags),
+                                expr: mention(
+                                    faToSource(result.overlap, flags),
+                                ),
                             },
                         })
                         break
diff --git a/lib/rules/no-super-linear-backtracking.ts b/lib/rules/no-super-linear-backtracking.ts
index b2d655c..11c8f21 100644
--- a/lib/rules/no-super-linear-backtracking.ts
+++ b/lib/rules/no-super-linear-backtracking.ts
@@ -5,6 +5,7 @@ import { UsageOfPattern } from "../utils/get-usage-of-pattern"
 import type { ParsedLiteral } from "scslre"
 import { analyse } from "scslre"
 import type { Position, SourceLocation } from "estree"
+import { mention } from "../utils/mention"
 
 /**
  * Returns the combined source location of the two given locations.
@@ -74,11 +75,11 @@ export default createRule("no-super-linear-backtracking", {
         ],
         messages: {
             self:
-                "This quantifier can reach itself via the loop '{{parent}}'." +
+                "This quantifier can reach itself via the loop {{parent}}." +
                 " Using any string accepted by {{attack}}, this can be exploited to cause at least polynomial backtracking." +
                 "{{exp}}",
             trade:
-                "The quantifier '{{start}}' can exchange characters with '{{end}}'." +
+                "The quantifier {{start}} can exchange characters with {{end}}." +
                 " Using any string accepted by {{attack}}, this can be exploited to cause at least polynomial backtracking." +
                 "{{exp}}",
         },
@@ -134,7 +135,7 @@ export default createRule("no-super-linear-backtracking", {
                         data: {
                             exp,
                             attack,
-                            parent: report.parentQuant.raw,
+                            parent: mention(report.parentQuant),
                         },
                         fix,
                     })
@@ -149,8 +150,8 @@ export default createRule("no-super-linear-backtracking", {
                         data: {
                             exp,
                             attack,
-                            start: report.startQuant.raw,
-                            end: report.endQuant.raw,
+                            start: mention(report.startQuant),
+                            end: mention(report.endQuant),
                         },
                         fix,
                     })
diff --git a/lib/rules/no-useless-assertions.ts b/lib/rules/no-useless-assertions.ts
index df813d0..452178a 100644
--- a/lib/rules/no-useless-assertions.ts
+++ b/lib/rules/no-useless-assertions.ts
@@ -16,26 +16,27 @@ import {
     hasSomeDescendant,
     isPotentiallyEmpty,
 } from "regexp-ast-analysis"
+import { mention } from "../utils/mention"
 
 const messages = {
     alwaysRejectByChar:
-        "'{{assertion}}' will always reject because it is {{followedOrPreceded}} by a character.",
+        "{{assertion}} will always reject because it is {{followedOrPreceded}} by a character.",
     alwaysRejectByNonLineTerminator:
-        "'{{assertion}}' will always reject because it is {{followedOrPreceded}} by a non-line-terminator character.",
+        "{{assertion}} will always reject because it is {{followedOrPreceded}} by a non-line-terminator character.",
     alwaysAcceptByLineTerminator:
-        "'{{assertion}}' will always accept because it is {{followedOrPreceded}} by a line-terminator character.",
+        "{{assertion}} will always accept because it is {{followedOrPreceded}} by a line-terminator character.",
     alwaysAcceptOrRejectFollowedByWord:
-        "'{{assertion}}' will always {{acceptOrReject}} because it is preceded by a non-word character and followed by a word character.",
+        "{{assertion}} will always {{acceptOrReject}} because it is preceded by a non-word character and followed by a word character.",
     alwaysAcceptOrRejectFollowedByNonWord:
-        "'{{assertion}}' will always {{acceptOrReject}} because it is preceded by a non-word character and followed by a non-word character.",
+        "{{assertion}} will always {{acceptOrReject}} because it is preceded by a non-word character and followed by a non-word character.",
     alwaysAcceptOrRejectPrecededByWordFollowedByNonWord:
-        "'{{assertion}}' will always {{acceptOrReject}} because it is preceded by a word character and followed by a non-word character.",
+        "{{assertion}} will always {{acceptOrReject}} because it is preceded by a word character and followed by a non-word character.",
     alwaysAcceptOrRejectPrecededByWordFollowedByWord:
-        "'{{assertion}}' will always {{acceptOrReject}} because it is preceded by a word character and followed by a word character.",
+        "{{assertion}} will always {{acceptOrReject}} because it is preceded by a word character and followed by a word character.",
     alwaysForLookaround:
-        "The {{kind}} '{{assertion}}' will always {{acceptOrReject}}.",
+        "The {{kind}} {{assertion}} will always {{acceptOrReject}}.",
     alwaysForNegativeLookaround:
-        "The negative {{kind}} '{{assertion}}' will always {{acceptOrReject}}.",
+        "The negative {{kind}} {{assertion}} will always {{acceptOrReject}}.",
 }
 
 export default createRule("no-useless-assertions", {
@@ -72,7 +73,7 @@ export default createRule("no-useless-assertions", {
                     loc: getRegexpLocation(assertion),
                     messageId,
                     data: {
-                        assertion: assertion.raw,
+                        assertion: mention(assertion),
                         ...data,
                     },
                 })
diff --git a/lib/rules/no-useless-backreference.ts b/lib/rules/no-useless-backreference.ts
index 2ca89bc..84f5f3f 100644
--- a/lib/rules/no-useless-backreference.ts
+++ b/lib/rules/no-useless-backreference.ts
@@ -12,6 +12,7 @@ import {
     getMatchingDirection,
     isZeroLength,
 } from "regexp-ast-analysis"
+import { mention } from "../utils/mention"
 
 /**
  * Returns whether the list of ancestors from `from` to `to` contains a negated
@@ -89,17 +90,17 @@ export default createRule("no-useless-backreference", {
         schema: [],
         messages: {
             nested:
-                "Backreference '{{ bref }}' will be ignored. It references group '{{ group }}' from within that group.",
+                "Backreference {{ bref }} will be ignored. It references group {{ group }} from within that group.",
             forward:
-                "Backreference '{{ bref }}' will be ignored. It references group '{{ group }}' which appears later in the pattern.",
+                "Backreference {{ bref }} will be ignored. It references group {{ group }} which appears later in the pattern.",
             backward:
-                "Backreference '{{ bref }}' will be ignored. It references group '{{ group }}' which appears before in the same lookbehind.",
+                "Backreference {{ bref }} will be ignored. It references group {{ group }} which appears before in the same lookbehind.",
             disjunctive:
-                "Backreference '{{ bref }}' will be ignored. It references group '{{ group }}' which is in another alternative.",
+                "Backreference {{ bref }} will be ignored. It references group {{ group }} which is in another alternative.",
             intoNegativeLookaround:
-                "Backreference '{{ bref }}' will be ignored. It references group '{{ group }}' which is in a negative lookaround.",
+                "Backreference {{ bref }} will be ignored. It references group {{ group }} which is in a negative lookaround.",
             empty:
-                "Backreference '{{ bref }}' will be ignored. It references group '{{ group }}' which always captures zero characters.",
+                "Backreference {{ bref }} will be ignored. It references group {{ group }} which always captures zero characters.",
         },
         type: "suggestion", // "problem",
     },
@@ -121,8 +122,8 @@ export default createRule("no-useless-backreference", {
                             loc: getRegexpLocation(backRef),
                             messageId,
                             data: {
-                                bref: backRef.raw,
-                                group: backRef.resolved.raw,
+                                bref: mention(backRef),
+                                group: mention(backRef.resolved),
                             },
                         })
                     }
diff --git a/lib/rules/no-useless-lazy.ts b/lib/rules/no-useless-lazy.ts
index 0459baf..e6ca63b 100644
--- a/lib/rules/no-useless-lazy.ts
+++ b/lib/rules/no-useless-lazy.ts
@@ -94,7 +94,7 @@ export default createRule("no-useless-lazy", {
 
                     const matchingDir = getMatchingDirection(qNode)
                     const firstChar = getFirstConsumedChar(
-                        qNode,
+                        qNode.element,
                         matchingDir,
                         flags,
                     )
diff --git a/lib/rules/no-useless-non-capturing-group.ts b/lib/rules/no-useless-non-capturing-group.ts
index 76daafb..bf432c6 100644
--- a/lib/rules/no-useless-non-capturing-group.ts
+++ b/lib/rules/no-useless-non-capturing-group.ts
@@ -125,7 +125,7 @@ export default createRule("no-useless-non-capturing-group", {
 
                     context.report({
                         node,
-                        loc: getRegexpLocation(gNode),
+                        loc: getRegexpLocation(gNode, [0, 3]),
                         messageId: "unexpected",
                         fix: fixReplaceNode(gNode, () => {
                             if (
diff --git a/lib/rules/optimal-lookaround-quantifier.ts b/lib/rules/optimal-lookaround-quantifier.ts
index 3291357..295e9ff 100644
--- a/lib/rules/optimal-lookaround-quantifier.ts
+++ b/lib/rules/optimal-lookaround-quantifier.ts
@@ -3,6 +3,7 @@ import type { Alternative, LookaroundAssertion, Quantifier } from "regexpp/ast"
 import type { RegExpContext } from "../utils"
 import { createRule, defineRegexpVisitor } from "../utils"
 import { hasSomeDescendant } from "regexp-ast-analysis"
+import { mention } from "../utils/mention"
 
 /**
  * Extract invalid quantifiers for lookarounds
@@ -65,9 +66,9 @@ export default createRule("optimal-lookaround-quantifier", {
         schema: [],
         messages: {
             remove:
-                "The quantified expression '{{expr}}' at the {{endOrStart}} of the expression tree should only be matched a constant number of times. The expression can be removed without affecting the lookaround.",
+                "The quantified expression {{expr}} at the {{endOrStart}} of the expression tree should only be matched a constant number of times. The expression can be removed without affecting the lookaround.",
             replacedWith:
-                "The quantified expression '{{expr}}' at the {{endOrStart}} of the expression tree should only be matched a constant number of times. The expression can be replaced with {{replacer}} without affecting the lookaround.",
+                "The quantified expression {{expr}} at the {{endOrStart}} of the expression tree should only be matched a constant number of times. The expression can be replaced with {{replacer}} without affecting the lookaround.",
         },
         type: "problem",
     },
@@ -105,7 +106,7 @@ export default createRule("optimal-lookaround-quantifier", {
                                 messageId:
                                     q.min === 0 ? "remove" : "replacedWith",
                                 data: {
-                                    expr: q.raw,
+                                    expr: mention(q),
                                     endOrStart,
                                     replacer,
                                 },
diff --git a/lib/rules/optimal-quantifier-concatenation.ts b/lib/rules/optimal-quantifier-concatenation.ts
index e4c30fc..1e8e67a 100644
--- a/lib/rules/optimal-quantifier-concatenation.ts
+++ b/lib/rules/optimal-quantifier-concatenation.ts
@@ -19,6 +19,7 @@ import { createRule, defineRegexpVisitor, quantToString } from "../utils"
 import { Chars, hasSomeDescendant } from "regexp-ast-analysis"
 import { getPossiblyConsumedChar } from "../utils/regexp-ast"
 import type { CharSet } from "refa"
+import { mention } from "../utils/mention"
 
 /**
  * Returns whether the given node is or contains a capturing group.
@@ -511,17 +512,17 @@ export default createRule("optimal-quantifier-concatenation", {
         schema: [],
         messages: {
             combine:
-                "'{{left}}' and '{{right}}' can be combined into one quantifier '{{fix}}'.{{cap}}",
+                "{{left}} and {{right}} can be combined into one quantifier {{fix}}.{{cap}}",
             removeLeft:
-                "'{{left}}' can be removed because it is already included by '{{right}}'.{{cap}}",
+                "{{left}} can be removed because it is already included by {{right}}.{{cap}}",
             removeRight:
-                "'{{right}}' can be removed because it is already included by '{{left}}'.{{cap}}",
+                "{{right}} can be removed because it is already included by {{left}}.{{cap}}",
             replace:
-                "'{{left}}' and '{{right}}' can be replaced with '{{fix}}'.{{cap}}",
+                "{{left}} and {{right}} can be replaced with {{fix}}.{{cap}}",
             nestedRemove:
-                "'{{nested}}' can be removed because of '{{dominate}}'.{{cap}}",
+                "{{nested}} can be removed because of {{dominate}}.{{cap}}",
             nestedReplace:
-                "'{{nested}}' can be replaced with '{{fix}}' because of '{{dominate}}'.{{cap}}",
+                "{{nested}} can be replaced with {{fix}} because of {{dominate}}.{{cap}}",
         },
         type: "suggestion",
     },
@@ -562,9 +563,9 @@ export default createRule("optimal-quantifier-concatenation", {
                                 loc: getLoc(left, right, regexpContext),
                                 messageId: replacement.messageId,
                                 data: {
-                                    left: left.raw,
-                                    right: right.raw,
-                                    fix: replacement.raw,
+                                    left: mention(left),
+                                    right: mention(right),
+                                    fix: mention(replacement.raw),
                                     cap,
                                 },
                                 fix: fixReplaceNode(aNode, () => {
@@ -589,9 +590,9 @@ export default createRule("optimal-quantifier-concatenation", {
                                 loc: getRegexpLocation(replacement.nested),
                                 messageId: replacement.messageId,
                                 data: {
-                                    nested: replacement.nested.raw,
-                                    dominate: replacement.dominate.raw,
-                                    fix: replacement.raw,
+                                    nested: mention(replacement.nested),
+                                    dominate: mention(replacement.dominate),
+                                    fix: mention(replacement.raw),
                                     cap,
                                 },
                                 fix: fixReplaceNode(replacement.nested, () => {
diff --git a/lib/rules/prefer-d.ts b/lib/rules/prefer-d.ts
index 59e83a9..b045c4e 100644
--- a/lib/rules/prefer-d.ts
+++ b/lib/rules/prefer-d.ts
@@ -7,6 +7,7 @@ import {
     CP_DIGIT_NINE,
 } from "../utils"
 import { Chars } from "regexp-ast-analysis"
+import { mention } from "../utils/mention"
 
 export default createRule("prefer-d", {
     meta: {
@@ -19,7 +20,7 @@ export default createRule("prefer-d", {
         schema: [],
         messages: {
             unexpected:
-                "Unexpected {{type}} '{{expr}}'. Use '{{instead}}' instead.",
+                "Unexpected {{type}} {{expr}}. Use '{{instead}}' instead.",
         },
         type: "suggestion", // "problem",
     },
@@ -56,7 +57,7 @@ export default createRule("prefer-d", {
                             messageId: "unexpected",
                             data: {
                                 type: "character class",
-                                expr: ccNode.raw,
+                                expr: mention(ccNode),
                                 instead: predefined,
                             },
                             fix: fixReplaceNode(ccNode, predefined),
@@ -83,7 +84,7 @@ export default createRule("prefer-d", {
                             messageId: "unexpected",
                             data: {
                                 type: "character class range",
-                                expr: ccrNode.raw,
+                                expr: mention(ccrNode),
                                 instead,
                             },
                             fix: fixReplaceNode(ccrNode, instead),
diff --git a/lib/rules/prefer-question-quantifier.ts b/lib/rules/prefer-question-quantifier.ts
index 7c3dbce..0be48d4 100644
--- a/lib/rules/prefer-question-quantifier.ts
+++ b/lib/rules/prefer-question-quantifier.ts
@@ -1,3 +1,4 @@
+import { mention } from "../utils/mention"
 import type { Group, Quantifier } from "regexpp/ast"
 import type { RegExpVisitor } from "regexpp/visitor"
 import type { RegExpContext } from "../utils"
@@ -15,7 +16,7 @@ export default createRule("prefer-question-quantifier", {
         messages: {
             unexpected: "Unexpected quantifier '{{expr}}'. Use '?' instead.",
             unexpectedGroup:
-                "Unexpected group '{{expr}}'. Use '{{instead}}' instead.",
+                "Unexpected group {{expr}}. Use '{{instead}}' instead.",
         },
         type: "suggestion", // "problem",
     },
@@ -96,7 +97,7 @@ export default createRule("prefer-question-quantifier", {
                             loc: getRegexpLocation(reportNode),
                             messageId: "unexpectedGroup",
                             data: {
-                                expr: reportNode.raw,
+                                expr: mention(reportNode),
                                 instead,
                             },
                             fix: fixReplaceNode(reportNode, instead),
diff --git a/lib/rules/prefer-range.ts b/lib/rules/prefer-range.ts
index c9cae54..c3ee7b6 100644
--- a/lib/rules/prefer-range.ts
+++ b/lib/rules/prefer-range.ts
@@ -8,6 +8,7 @@ import {
     inRange,
 } from "../utils/char-ranges"
 import type { PatternReplaceRange } from "../utils/ast-utils/pattern-source"
+import { mention } from "../utils/mention"
 
 export default createRule("prefer-range", {
     meta: {
@@ -30,7 +31,7 @@ export default createRule("prefer-range", {
         ],
         messages: {
             unexpected:
-                "Unexpected multiple adjacent characters. Use '{{range}}' instead.",
+                "Unexpected multiple adjacent characters. Use {{range}} instead.",
         },
         type: "suggestion", // "problem",
     },
@@ -161,7 +162,7 @@ export default createRule("prefer-range", {
                                     node,
                                     loc: node.loc!,
                                     messageId: "unexpected",
-                                    data: { range: newText },
+                                    data: { range: mention(newText) },
                                 })
                                 continue
                             }
@@ -171,7 +172,7 @@ export default createRule("prefer-range", {
                                     node,
                                     loc: range.getAstLocation(sourceCode),
                                     messageId: "unexpected",
-                                    data: { range: newText },
+                                    data: { range: mention(newText) },
                                     fix: (fixer) => {
                                         return ranges.map((r, index) => {
                                             if (index === 0) {
diff --git a/lib/rules/prefer-t.ts b/lib/rules/prefer-t.ts
index a91fce8..171ec5d 100644
--- a/lib/rules/prefer-t.ts
+++ b/lib/rules/prefer-t.ts
@@ -1,3 +1,4 @@
+import { mention } from "../utils/mention"
 import type { RegExpVisitor } from "regexpp/visitor"
 import type { RegExpContext } from "../utils"
 import { createRule, defineRegexpVisitor, CP_TAB } from "../utils"
@@ -13,7 +14,7 @@ export default createRule("prefer-t", {
         fixable: "code",
         schema: [],
         messages: {
-            unexpected: "Unexpected character '{{expr}}'. Use '\\t' instead.",
+            unexpected: "Unexpected character {{expr}}. Use '\\t' instead.",
         },
         type: "suggestion", // "problem",
         deprecated: true,
@@ -39,7 +40,7 @@ export default createRule("prefer-t", {
                             loc: getRegexpLocation(cNode),
                             messageId: "unexpected",
                             data: {
-                                expr: cNode.raw,
+                                expr: mention(cNode),
                             },
                             fix: fixReplaceNode(cNode, "\\t"),
                         })
diff --git a/lib/rules/prefer-w.ts b/lib/rules/prefer-w.ts
index 1c08c0b..db66e15 100644
--- a/lib/rules/prefer-w.ts
+++ b/lib/rules/prefer-w.ts
@@ -14,6 +14,7 @@ import {
     CP_LOW_LINE,
 } from "../utils"
 import { Chars } from "regexp-ast-analysis"
+import { mention } from "../utils/mention"
 
 /**
  * Checks if small letter char class range
@@ -71,7 +72,7 @@ export default createRule("prefer-w", {
         schema: [],
         messages: {
             unexpected:
-                "Unexpected {{type}} '{{expr}}'. Use '{{instead}}' instead.",
+                "Unexpected {{type}} {{expr}}. Use '{{instead}}' instead.",
         },
         type: "suggestion", // "problem",
     },
@@ -105,7 +106,7 @@ export default createRule("prefer-w", {
                             messageId: "unexpected",
                             data: {
                                 type: "character class",
-                                expr: ccNode.raw,
+                                expr: mention(ccNode),
                                 instead: predefined,
                             },
                             fix: fixReplaceNode(ccNode, predefined),
@@ -156,9 +157,9 @@ export default createRule("prefer-w", {
                             messageId: "unexpected",
                             data: {
                                 type: "character class ranges",
-                                expr: `[${unexpectedElements
+                                expr: `'[${unexpectedElements
                                     .map((e) => e.raw)
-                                    .join("")}]`,
+                                    .join("")}]'`,
                                 instead: "\\w",
                             },
                             fix(fixer: Rule.RuleFixer) {
diff --git a/lib/rules/strict.ts b/lib/rules/strict.ts
index 5656ecd..7199942 100644
--- a/lib/rules/strict.ts
+++ b/lib/rules/strict.ts
@@ -1,3 +1,4 @@
+import { mention } from "../utils/mention"
 import { RegExpValidator } from "regexpp"
 import type { CharacterClassElement, Element } from "regexpp/ast"
 import type { RegExpVisitor } from "regexpp/visitor"
@@ -46,14 +47,14 @@ export default createRule("strict", {
             invalidControlEscape:
                 "Invalid or incomplete control escape sequence. Either use a valid control escape sequence or escaping the standalone backslash.",
             incompleteEscapeSequence:
-                "Incomplete escape sequence '{{expr}}'. Either use a valid escape sequence or remove the useless escaping.",
+                "Incomplete escape sequence {{expr}}. Either use a valid escape sequence or remove the useless escaping.",
             invalidPropertyEscape:
-                "Invalid property escape sequence '{{expr}}'. Either use a valid property escape sequence or remove the useless escaping.",
+                "Invalid property escape sequence {{expr}}. Either use a valid property escape sequence or remove the useless escaping.",
             incompleteBackreference:
-                "Incomplete backreference '{{expr}}'. Either use a valid backreference or remove the useless escaping.",
-            unescapedSourceCharacter: "Unescaped source character '{{expr}}'.",
+                "Incomplete backreference {{expr}}. Either use a valid backreference or remove the useless escaping.",
+            unescapedSourceCharacter: "Unescaped source character {{expr}}.",
             octalEscape:
-                "Invalid legacy octal escape sequence '{{expr}}'. Use a hexadecimal escape instead.",
+                "Invalid legacy octal escape sequence {{expr}}. Use a hexadecimal escape instead.",
             uselessEscape:
                 "Useless identity escapes with non-syntax characters are forbidden.",
 
@@ -111,7 +112,7 @@ export default createRule("strict", {
                         loc: getRegexpLocation(element),
                         messageId,
                         data: {
-                            expr: element.raw,
+                            expr: mention(element),
                         },
                         suggest: [
                             {
@@ -126,7 +127,7 @@ export default createRule("strict", {
                         loc: getRegexpLocation(element),
                         messageId,
                         data: {
-                            expr: element.raw,
+                            expr: mention(element),
                         },
                         fix: fix ? fixReplaceNode(element, fix) : null,
                     })
diff --git a/lib/utils/mention.ts b/lib/utils/mention.ts
index efa8da0..7b8f698 100644
--- a/lib/utils/mention.ts
+++ b/lib/utils/mention.ts
@@ -28,8 +28,8 @@ export function mentionChar(element: CharacterClassElement): string {
 /**
  * Creates a string that mentions the given character class element.
  */
-export function mention(element: Node): string {
-    return `'${escape(element.raw)}'`
+export function mention(element: Node | string): string {
+    return `'${escape(typeof element === "string" ? element : element.raw)}'`
 }
 
 /** Escape control characters in the given string */
diff --git a/package-lock.json b/package-lock.json
index a27ee46..8b8fd09 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13226,9 +13226,9 @@
             }
         },
         "regexp-ast-analysis": {
-            "version": "0.2.3",
-            "resolved": "https://registry.npmjs.org/regexp-ast-analysis/-/regexp-ast-analysis-0.2.3.tgz",
-            "integrity": "sha512-K/6d90dP1DI68HHdHORj9vVxV5oWkLB8F7LeGs70EdA/h5BIqkrK8SdGy+O9Ii+AVVPprF0Ia6f5UwgI+ahIcw==",
+            "version": "0.2.4",
+            "resolved": "https://registry.npmjs.org/regexp-ast-analysis/-/regexp-ast-analysis-0.2.4.tgz",
+            "integrity": "sha512-8L7kOZQaKPxKKAwGuUZxTQtlO3WZ+tiXy4s6G6PKL6trbOXcZoumwC3AOHHFtI/xoSbNxt7jgLvCnP1UADLWqg==",
             "requires": {
                 "refa": "^0.9.0",
                 "regexpp": "^3.2.0"
diff --git a/package.json b/package.json
index 4ee8279..1c34d55 100644
--- a/package.json
+++ b/package.json
@@ -95,7 +95,7 @@
         "eslint-utils": "^3.0.0",
         "jsdoctypeparser": "^9.0.0",
         "refa": "^0.9.0",
-        "regexp-ast-analysis": "^0.2.3",
+        "regexp-ast-analysis": "^0.2.4",
         "regexpp": "^3.2.0",
         "scslre": "^0.1.6"
     }


Test patch:
diff --git a/tests/lib/rules/no-useless-lazy.ts b/tests/lib/rules/no-useless-lazy.ts
index 9d6029a..58e35c6 100644
--- a/tests/lib/rules/no-useless-lazy.ts
+++ b/tests/lib/rules/no-useless-lazy.ts
@@ -72,6 +72,11 @@ tester.run("no-useless-lazy", rule as any, {
             output: `/a+b+/`,
             errors: [{ messageId: "possessive" }],
         },
+        {
+            code: `/a*?b+/`,
+            output: `/a*b+/`,
+            errors: [{ messageId: "possessive" }],
+        },
         {
             code: `/(?:a|cd)+?(?:b+|zzz)/`,
             output: `/(?:a|cd)+(?:b+|zzz)/`,
diff --git a/tests/lib/rules/no-useless-non-capturing-group.ts b/tests/lib/rules/no-useless-non-capturing-group.ts
index 1f04a44..6ea70ed 100644
--- a/tests/lib/rules/no-useless-non-capturing-group.ts
+++ b/tests/lib/rules/no-useless-non-capturing-group.ts
@@ -87,7 +87,7 @@ tester.run("no-useless-non-capturing-group", rule as any, {
                     line: 1,
                     column: 2,
                     endLine: 1,
-                    endColumn: 10,
+                    endColumn: 5,
                 },
             ],
         },
@@ -100,7 +100,7 @@ tester.run("no-useless-non-capturing-group", rule as any, {
                     line: 1,
                     column: 2,
                     endLine: 1,
-                    endColumn: 12,
+                    endColumn: 5,
                 },
             ],
         },