Repository: ota-meshi/eslint-plugin-regexp
Pull request #628 (2023-10-01 14:02:36)

Add support for `v` flag to `regexp/match-any`
Changes:
- Fixed that character classes with strings were lost their strings after fixing. E.g. `[\q{abc}\s\S]` -> `[\s\S]`.
- Check expression character classes as well.

changeset-bot[bot]: ###  ⚠️  No Changeset found

Latest commit: a25d396ebeb7db0d878ba8dc06fb42b0682d5ab1

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/ota-meshi/eslint-plugin-regexp/new/match-any-v?filename=.changeset/lemon-goats-look.md&value=---%0A%22eslint-plugin-regexp%22%3A%20patch%0A---%0A%0AAdd%20support%20for%20%60v%60%20flag%20to%20%60regexp%2Fmatch-any%60%0A)



Patch:
diff --git a/.changeset/dry-horses-punch.md b/.changeset/dry-horses-punch.md
new file mode 100644
index 0000000..4307a85
--- /dev/null
+++ b/.changeset/dry-horses-punch.md
@@ -0,0 +1,5 @@
+---
+"eslint-plugin-regexp": patch
+---
+
+Fixed incorrect default of `ignoreSticky` option in `regexp/no-super-linear-move`
diff --git a/.changeset/lemon-goats-look.md b/.changeset/lemon-goats-look.md
new file mode 100644
index 0000000..793939e
--- /dev/null
+++ b/.changeset/lemon-goats-look.md
@@ -0,0 +1,5 @@
+---
+"eslint-plugin-regexp": minor
+---
+
+Add support for `v` flag to `regexp/match-any`
diff --git a/.changeset/thin-insects-cover.md b/.changeset/thin-insects-cover.md
new file mode 100644
index 0000000..fcd6b3d
--- /dev/null
+++ b/.changeset/thin-insects-cover.md
@@ -0,0 +1,5 @@
+---
+"eslint-plugin-regexp": patch
+---
+
+Add more `v` flag tests
diff --git a/lib/rules/match-any.ts b/lib/rules/match-any.ts
index 6e694bd..6b61554 100644
--- a/lib/rules/match-any.ts
+++ b/lib/rules/match-any.ts
@@ -2,12 +2,13 @@ import type { RegExpVisitor } from "@eslint-community/regexpp/visitor"
 import type { Rule } from "eslint"
 import type {
     CharacterClass,
-    Node as RegExpNode,
+    ExpressionCharacterClass,
+    Node,
 } from "@eslint-community/regexpp/ast"
 import type { RegExpContext } from "../utils"
 import { createRule, defineRegexpVisitor } from "../utils"
 import { isRegexpLiteral } from "../utils/ast-utils/utils"
-import { matchesAllCharacters } from "regexp-ast-analysis"
+import { matchesAllCharacters, hasStrings } from "regexp-ast-analysis"
 import { mention } from "../utils/mention"
 
 const OPTION_SS1 = "[\\s\\S]" as const
@@ -72,7 +73,7 @@ export default createRule("match-any", {
         function fix(
             fixer: Rule.RuleFixer,
             { node, flags, patternSource }: RegExpContext,
-            regexpNode: RegExpNode,
+            regexpNode: Node,
         ): null | Rule.Fix | Rule.Fix[] {
             if (!preference) {
                 return null
@@ -134,6 +135,28 @@ export default createRule("match-any", {
         ): RegExpVisitor.Handlers {
             const { node, flags, getRegexpLocation } = regexpContext
 
+            function onClass(
+                ccNode: CharacterClass | ExpressionCharacterClass,
+            ) {
+                if (
+                    matchesAllCharacters(ccNode, flags) &&
+                    !hasStrings(ccNode, flags) &&
+                    !allows.has(ccNode.raw as never)
+                ) {
+                    context.report({
+                        node,
+                        loc: getRegexpLocation(ccNode),
+                        messageId: "unexpected",
+                        data: {
+                            expr: mention(ccNode),
+                        },
+                        fix(fixer) {
+                            return fix(fixer, regexpContext, ccNode)
+                        },
+                    })
+                }
+            }
+
             return {
                 onCharacterSetEnter(csNode) {
                     if (
@@ -154,24 +177,8 @@ export default createRule("match-any", {
                         })
                     }
                 },
-                onCharacterClassEnter(ccNode: CharacterClass) {
-                    if (
-                        matchesAllCharacters(ccNode, flags) &&
-                        !allows.has(ccNode.raw as never)
-                    ) {
-                        context.report({
-                            node,
-                            loc: getRegexpLocation(ccNode),
-                            messageId: "unexpected",
-                            data: {
-                                expr: mention(ccNode),
-                            },
-                            fix(fixer) {
-                                return fix(fixer, regexpContext, ccNode)
-                            },
-                        })
-                    }
-                },
+                onCharacterClassEnter: onClass,
+                onExpressionCharacterClassEnter: onClass,
             }
         }
 
diff --git a/lib/rules/no-super-linear-move.ts b/lib/rules/no-super-linear-move.ts
index bee3072..90c3667 100644
--- a/lib/rules/no-super-linear-move.ts
+++ b/lib/rules/no-super-linear-move.ts
@@ -146,7 +146,7 @@ export default createRule("no-super-linear-move", {
     create(context) {
         const reportUncertain =
             (context.options[0]?.report ?? "certain") === "potential"
-        const ignoreSticky = context.options[0]?.ignoreSticky ?? false
+        const ignoreSticky = context.options[0]?.ignoreSticky ?? true
         const ignorePartial = context.options[0]?.ignorePartial ?? true
 
         function getScslreReports(


Test patch:
diff --git a/tests/lib/rules/match-any.ts b/tests/lib/rules/match-any.ts
index c2f103a..bd8c84a 100644
--- a/tests/lib/rules/match-any.ts
+++ b/tests/lib/rules/match-any.ts
@@ -3,7 +3,7 @@ import rule from "../../../lib/rules/match-any"
 
 const tester = new RuleTester({
     parserOptions: {
-        ecmaVersion: 2020,
+        ecmaVersion: "latest",
         sourceType: "module",
     },
 })
@@ -41,6 +41,7 @@ tester.run("match-any", rule as any, {
         "/[^\\p{ASCII}\\P{ASCII}]/u",
         "/[^\\P{ASCII}\\p{ASCII}]/u",
         "/[^\\s\\S\\0-\\uFFFF]/",
+        String.raw`/[\S\s\q{abc}]/v`,
     ],
     invalid: [
         {
@@ -55,6 +56,25 @@ tester.run("match-any", rule as any, {
                 },
             ],
         },
+        {
+            code: String.raw`/[\S\s]/v`,
+            output: String.raw`/[\s\S]/v`,
+            errors: ["Unexpected using '[\\S\\s]' to match any character."],
+        },
+        {
+            code: String.raw`/[\S\s\q{a|b|c}]/v`,
+            output: String.raw`/[\s\S]/v`,
+            errors: [
+                "Unexpected using '[\\S\\s\\q{a|b|c}]' to match any character.",
+            ],
+        },
+        {
+            code: String.raw`/[[\S\s\q{abc}]--\q{abc}]/v`,
+            output: String.raw`/[\s\S]/v`,
+            errors: [
+                "Unexpected using '[[\\S\\s\\q{abc}]--\\q{abc}]' to match any character.",
+            ],
+        },
         {
             code: "/[^]/",
             output: "/[\\s\\S]/",
diff --git a/tests/lib/rules/no-useless-assertions.ts b/tests/lib/rules/no-useless-assertions.ts
index 63f3e9f..5dedfa4 100644
--- a/tests/lib/rules/no-useless-assertions.ts
+++ b/tests/lib/rules/no-useless-assertions.ts
@@ -3,7 +3,7 @@ import rule from "../../../lib/rules/no-useless-assertions"
 
 const tester = new RuleTester({
     parserOptions: {
-        ecmaVersion: 2020,
+        ecmaVersion: "latest",
         sourceType: "module",
     },
 })
@@ -67,6 +67,15 @@ tester.run("no-useless-assertions", rule as any, {
                 },
             ],
         },
+        {
+            code: String.raw`/a\b[\q{foo}]/v`,
+            errors: [
+                {
+                    message:
+                        "'\\b' will always reject because it is preceded by a word character and followed by a word character.",
+                },
+            ],
+        },
         {
             code: String.raw`/,\b,/`,
             errors: [
diff --git a/tests/lib/rules/no-useless-backreference.ts b/tests/lib/rules/no-useless-backreference.ts
index c6363a3..bf79eb5 100644
--- a/tests/lib/rules/no-useless-backreference.ts
+++ b/tests/lib/rules/no-useless-backreference.ts
@@ -3,7 +3,7 @@ import rule from "../../../lib/rules/no-useless-backreference"
 
 const tester = new RuleTester({
     parserOptions: {
-        ecmaVersion: 2020,
+        ecmaVersion: "latest",
         sourceType: "module",
     },
 })
@@ -31,6 +31,10 @@ tester.run("no-useless-backreference", rule as any, {
             code: "/(\\b)a\\1/",
             errors: [{ messageId: "empty" }],
         },
+        {
+            code: "/([\\q{}])a\\1/v",
+            errors: [{ messageId: "empty" }],
+        },
         {
             code: "/(\\b|a{0})a\\1/",
             errors: [{ messageId: "empty" }],
diff --git a/tests/lib/rules/no-useless-flag.ts b/tests/lib/rules/no-useless-flag.ts
index 004f68e..cb1bbe9 100644
--- a/tests/lib/rules/no-useless-flag.ts
+++ b/tests/lib/rules/no-useless-flag.ts
@@ -6,7 +6,7 @@ import { rules } from "../../../lib/utils/rules"
 
 const tester = new RuleTester({
     parserOptions: {
-        ecmaVersion: 2020,
+        ecmaVersion: "latest",
         sourceType: "module",
     },
 })
@@ -218,6 +218,8 @@ tester.run("no-useless-flag", rule as any, {
         const orig = /\w/i; // eslint-disable-line
         const clone = new RegExp(orig);
         `,
+        String.raw`/a/u`,
+        String.raw`/a/v`,
     ],
     invalid: [
         // i
diff --git a/tests/lib/rules/no-useless-lazy.ts b/tests/lib/rules/no-useless-lazy.ts
index 3fbf3ec..da0c5e2 100644
--- a/tests/lib/rules/no-useless-lazy.ts
+++ b/tests/lib/rules/no-useless-lazy.ts
@@ -3,7 +3,7 @@ import rule from "../../../lib/rules/no-useless-lazy"
 
 const tester = new RuleTester({
     parserOptions: {
-        ecmaVersion: 2020,
+        ecmaVersion: "latest",
         sourceType: "module",
     },
 })
@@ -34,6 +34,11 @@ tester.run("no-useless-lazy", rule as any, {
                 },
             ],
         },
+        {
+            code: `/a{1}?/v`,
+            output: `/a{1}/v`,
+            errors: [{ messageId: "constant" }],
+        },
         {
             code: `/a{4}?/`,
             output: `/a{4}/`,
@@ -72,6 +77,11 @@ tester.run("no-useless-lazy", rule as any, {
             output: `/a+b+/`,
             errors: [{ messageId: "possessive" }],
         },
+        {
+            code: String.raw`/[\q{aa|ab}]+?b+/v`,
+            output: String.raw`/[\q{aa|ab}]+b+/v`,
+            errors: [{ messageId: "possessive" }],
+        },
         {
             code: `/a*?b+/`,
             output: `/a*b+/`,
diff --git a/tests/lib/rules/no-useless-non-capturing-group.ts b/tests/lib/rules/no-useless-non-capturing-group.ts
index 6ea70ed..2734196 100644
--- a/tests/lib/rules/no-useless-non-capturing-group.ts
+++ b/tests/lib/rules/no-useless-non-capturing-group.ts
@@ -3,7 +3,7 @@ import rule from "../../../lib/rules/no-useless-non-capturing-group"
 
 const tester = new RuleTester({
     parserOptions: {
-        ecmaVersion: 2020,
+        ecmaVersion: "latest",
         sourceType: "module",
     },
 })
@@ -91,6 +91,19 @@ tester.run("no-useless-non-capturing-group", rule as any, {
                 },
             ],
         },
+        {
+            code: `/(?:abcd)/v.test(str)`,
+            output: `/abcd/v.test(str)`,
+            errors: [
+                {
+                    message: "Unexpected quantifier Non-capturing group.",
+                    line: 1,
+                    column: 2,
+                    endLine: 1,
+                    endColumn: 5,
+                },
+            ],
+        },
         {
             code: `/(?:[abcd])/.test(str)`,
             output: `/[abcd]/.test(str)`,
diff --git a/tests/lib/rules/no-useless-quantifier.ts b/tests/lib/rules/no-useless-quantifier.ts
index ffcea1d..924ab4d 100644
--- a/tests/lib/rules/no-useless-quantifier.ts
+++ b/tests/lib/rules/no-useless-quantifier.ts
@@ -3,7 +3,7 @@ import rule from "../../../lib/rules/no-useless-quantifier"
 
 const tester = new RuleTester({
     parserOptions: {
-        ecmaVersion: 2020,
+        ecmaVersion: "latest",
         sourceType: "module",
     },
 })
@@ -45,6 +45,21 @@ tester.run("no-useless-quantifier", rule as any, {
                 },
             ],
         },
+        {
+            code: String.raw`/(?:[\q{}])+/v`,
+            output: null,
+            errors: [
+                {
+                    messageId: "empty",
+                    suggestions: [
+                        {
+                            messageId: "remove",
+                            output: String.raw`/(?:[\q{}])/v`,
+                        },
+                    ],
+                },
+            ],
+        },
         {
             code: String.raw`/(?:|(?:)){5,9}/`,
             output: null,
diff --git a/tests/lib/rules/no-useless-range.ts b/tests/lib/rules/no-useless-range.ts
index b687abd..a3e9962 100644
--- a/tests/lib/rules/no-useless-range.ts
+++ b/tests/lib/rules/no-useless-range.ts
@@ -3,7 +3,7 @@ import rule from "../../../lib/rules/no-useless-range"
 
 const tester = new RuleTester({
     parserOptions: {
-        ecmaVersion: 2020,
+        ecmaVersion: "latest",
         sourceType: "module",
     },
 })
@@ -23,6 +23,13 @@ tester.run("no-useless-range", rule as any, {
                 },
             ],
         },
+        {
+            code: `/[a-a]/v`,
+            output: `/[a]/v`,
+            errors: [
+                "Unexpected unnecessary character ranges. The hyphen is unnecessary.",
+            ],
+        },
         {
             code: `/[a-b]/`,
             output: `/[ab]/`,
@@ -35,6 +42,13 @@ tester.run("no-useless-range", rule as any, {
                 },
             ],
         },
+        {
+            code: `/[a-b]/v`,
+            output: `/[ab]/v`,
+            errors: [
+                "Unexpected unnecessary character ranges. The hyphen is unnecessary.",
+            ],
+        },
         {
             code: `/[a-a-c-c]/`,
             output: `/[a\\-c]/`,
diff --git a/tests/lib/rules/no-useless-two-nums-quantifier.ts b/tests/lib/rules/no-useless-two-nums-quantifier.ts
index ad6c683..936700c 100644
--- a/tests/lib/rules/no-useless-two-nums-quantifier.ts
+++ b/tests/lib/rules/no-useless-two-nums-quantifier.ts
@@ -3,7 +3,7 @@ import rule from "../../../lib/rules/no-useless-two-nums-quantifier"
 
 const tester = new RuleTester({
     parserOptions: {
-        ecmaVersion: 2020,
+        ecmaVersion: "latest",
         sourceType: "module",
     },
 })
@@ -42,5 +42,10 @@ tester.run("no-useless-two-nums-quantifier", rule as any, {
             output: "/a{100}?/",
             errors: ["Unexpected quantifier '{100,100}'."],
         },
+        {
+            code: "/a{100,100}?/v",
+            output: "/a{100}?/v",
+            errors: 1,
+        },
     ],
 })
diff --git a/tests/lib/rules/no-zero-quantifier.ts b/tests/lib/rules/no-zero-quantifier.ts
index 7247309..f3bdbb3 100644
--- a/tests/lib/rules/no-zero-quantifier.ts
+++ b/tests/lib/rules/no-zero-quantifier.ts
@@ -3,7 +3,7 @@ import rule from "../../../lib/rules/no-zero-quantifier"
 
 const tester = new RuleTester({
     parserOptions: {
-        ecmaVersion: 2020,
+        ecmaVersion: "latest",
         sourceType: "module",
     },
 })
@@ -22,6 +22,15 @@ tester.run("no-zero-quantifier", rule as any, {
                 },
             ],
         },
+        {
+            code: `/a{0}/v`,
+            errors: [
+                {
+                    messageId: "unexpected",
+                    suggestions: [{ output: `/(?:)/v` }],
+                },
+            ],
+        },
         {
             code: `/a{0,0}/`,
             errors: [
diff --git a/tests/lib/rules/optimal-lookaround-quantifier.ts b/tests/lib/rules/optimal-lookaround-quantifier.ts
index d3fd173..1b65b4f 100644
--- a/tests/lib/rules/optimal-lookaround-quantifier.ts
+++ b/tests/lib/rules/optimal-lookaround-quantifier.ts
@@ -3,7 +3,7 @@ import rule from "../../../lib/rules/optimal-lookaround-quantifier"
 
 const tester = new RuleTester({
     parserOptions: {
-        ecmaVersion: 2020,
+        ecmaVersion: "latest",
         sourceType: "module",
     },
 })
@@ -22,6 +22,17 @@ tester.run("optimal-lookaround-quantifier", rule as any, {
                 },
             ],
         },
+        {
+            code: `/(?=ba*)/v`,
+            errors: [
+                {
+                    message:
+                        "The quantified expression 'a*' at the end of the expression tree should only be matched a constant number of times. The expression can be removed without affecting the lookaround.",
+                    column: 6,
+                    suggestions: [{ output: `/(?=b)/v` }],
+                },
+            ],
+        },
         {
             code: `/(?=(?:a|b|abc*))/`,
             errors: [
diff --git a/tests/lib/rules/prefer-escape-replacement-dollar-char.ts b/tests/lib/rules/prefer-escape-replacement-dollar-char.ts
index 59795a6..c3bb752 100644
--- a/tests/lib/rules/prefer-escape-replacement-dollar-char.ts
+++ b/tests/lib/rules/prefer-escape-replacement-dollar-char.ts
@@ -3,7 +3,7 @@ import rule from "../../../lib/rules/prefer-escape-replacement-dollar-char"
 
 const tester = new RuleTester({
     parserOptions: {
-        ecmaVersion: 2020,
+        ecmaVersion: "latest",
         sourceType: "module",
     },
 })
@@ -34,6 +34,10 @@ tester.run("prefer-escape-replacement-dollar-char", rule as any, {
                 },
             ],
         },
+        {
+            code: `'€1,234'.replace(/€/v, '$'); // "$1,234"`,
+            errors: 1,
+        },
         {
             code: `'€1,234'.replaceAll(/€/, '$'); // "$1,234"`,
             errors: [
diff --git a/tests/lib/rules/prefer-lookaround.ts b/tests/lib/rules/prefer-lookaround.ts
index f2a3a53..121d0dc 100644
--- a/tests/lib/rules/prefer-lookaround.ts
+++ b/tests/lib/rules/prefer-lookaround.ts
@@ -3,7 +3,7 @@ import rule from "../../../lib/rules/prefer-lookaround"
 
 const tester = new RuleTester({
     parserOptions: {
-        ecmaVersion: 2020,
+        ecmaVersion: "latest",
         sourceType: "module",
     },
 })
@@ -544,6 +544,13 @@ tester.run("prefer-lookaround", rule as any, {
                 },
             ],
         },
+        {
+            code: String.raw`var str = 'Java'.replace(/(?:^|\b|[\q{}])(J)ava/gv, '$1Query')`,
+            output: String.raw`var str = 'Java'.replace(/(?<=(?:^|\b|[\q{}])J)ava/gv, 'Query')`,
+            errors: [
+                "This capturing group can be replaced with a lookbehind assertion ('(?<=(?:^|\\b|[\\q{}])J)').",
+            ],
+        },
         {
             code: `var str = 'JavaScriptCode'.replace(/(?<=Java)(Script)Code/g, '$1Linter')`,
             output: `var str = 'JavaScriptCode'.replace(/(?<=JavaScript)Code/g, 'Linter')`,
diff --git a/tests/lib/rules/prefer-named-backreference.ts b/tests/lib/rules/prefer-named-backreference.ts
index 7baf978..cce7f8f 100644
--- a/tests/lib/rules/prefer-named-backreference.ts
+++ b/tests/lib/rules/prefer-named-backreference.ts
@@ -3,7 +3,7 @@ import rule from "../../../lib/rules/prefer-named-backreference"
 
 const tester = new RuleTester({
     parserOptions: {
-        ecmaVersion: 2020,
+        ecmaVersion: "latest",
         sourceType: "module",
     },
 })
@@ -16,5 +16,10 @@ tester.run("prefer-named-backreference", rule as any, {
             output: `/(?<foo>a)\\k<foo>/`,
             errors: [{ messageId: "unexpected" }],
         },
+        {
+            code: `/(?<foo>a)\\1/v`,
+            output: `/(?<foo>a)\\k<foo>/v`,
+            errors: [{ messageId: "unexpected" }],
+        },
     ],
 })
diff --git a/tests/lib/rules/prefer-named-capture-group.ts b/tests/lib/rules/prefer-named-capture-group.ts
index f99318e..d299c2e 100644
--- a/tests/lib/rules/prefer-named-capture-group.ts
+++ b/tests/lib/rules/prefer-named-capture-group.ts
@@ -3,7 +3,7 @@ import rule from "../../../lib/rules/prefer-named-capture-group"
 
 const tester = new RuleTester({
     parserOptions: {
-        ecmaVersion: 2020,
+        ecmaVersion: "latest",
         sourceType: "module",
     },
 })
@@ -22,5 +22,11 @@ tester.run("prefer-named-capture-group", rule as any, {
                 "Capture group '(foo)' should be converted to a named or non-capturing group.",
             ],
         },
+        {
+            code: String.raw`/(foo)/v`,
+            errors: [
+                "Capture group '(foo)' should be converted to a named or non-capturing group.",
+            ],
+        },
     ],
 })
diff --git a/tests/lib/rules/prefer-named-replacement.ts b/tests/lib/rules/prefer-named-replacement.ts
index 7cc5f2d..3d0949e 100644
--- a/tests/lib/rules/prefer-named-replacement.ts
+++ b/tests/lib/rules/prefer-named-replacement.ts
@@ -3,7 +3,7 @@ import rule from "../../../lib/rules/prefer-named-replacement"
 
 const tester = new RuleTester({
     parserOptions: {
-        ecmaVersion: 2020,
+        ecmaVersion: "latest",
         sourceType: "module",
     },
 })
@@ -34,6 +34,11 @@ tester.run("prefer-named-replacement", rule as any, {
                 },
             ],
         },
+        {
+            code: `"str".replace(/a(?<foo>b)c/v, "_$1_")`,
+            output: `"str".replace(/a(?<foo>b)c/v, "_$<foo>_")`,
+            errors: ["Unexpected indexed reference in replacement string."],
+        },
         {
             code: `"str".replaceAll(/a(?<foo>b)c/, "_$1_")`,
             output: `"str".replaceAll(/a(?<foo>b)c/, "_$<foo>_")`,
diff --git a/tests/lib/rules/prefer-plus-quantifier.ts b/tests/lib/rules/prefer-plus-quantifier.ts
index 3381b3f..b02de2e 100644
--- a/tests/lib/rules/prefer-plus-quantifier.ts
+++ b/tests/lib/rules/prefer-plus-quantifier.ts
@@ -3,7 +3,7 @@ import rule from "../../../lib/rules/prefer-plus-quantifier"
 
 const tester = new RuleTester({
     parserOptions: {
-        ecmaVersion: 2020,
+        ecmaVersion: "latest",
         sourceType: "module",
     },
 })
@@ -44,6 +44,11 @@ tester.run("prefer-plus-quantifier", rule as any, {
                 },
             ],
         },
+        {
+            code: "/(a){1,}/v",
+            output: "/(a)+/v",
+            errors: ["Unexpected quantifier '{1,}'. Use '+' instead."],
+        },
         {
             code: "/(a){1,}?/",
             output: "/(a)+?/",
diff --git a/tests/lib/rules/prefer-quantifier.ts b/tests/lib/rules/prefer-quantifier.ts
index 2de1f81..9ae51f8 100644
--- a/tests/lib/rules/prefer-quantifier.ts
+++ b/tests/lib/rules/prefer-quantifier.ts
@@ -3,7 +3,7 @@ import rule from "../../../lib/rules/prefer-quantifier"
 
 const tester = new RuleTester({
     parserOptions: {
-        ecmaVersion: 2020,
+        ecmaVersion: "latest",
         sourceType: "module",
     },
 })
@@ -47,6 +47,27 @@ tester.run("prefer-quantifier", rule as any, {
                 },
             ],
         },
+        {
+            code: `/  /v`,
+            output: `/ {2}/v`,
+            errors: [
+                "Unexpected consecutive same characters. Use '{2}' instead.",
+            ],
+        },
+        {
+            code: String.raw`/\p{ASCII}\p{ASCII}/u`,
+            output: String.raw`/\p{ASCII}{2}/u`,
+            errors: [
+                "Unexpected consecutive same character class escapes. Use '{2}' instead.",
+            ],
+        },
+        {
+            code: String.raw`/\p{Basic_Emoji}\p{Basic_Emoji}/v`,
+            output: String.raw`/\p{Basic_Emoji}{2}/v`,
+            errors: [
+                "Unexpected consecutive same character class escapes. Use '{2}' instead.",
+            ],
+        },
         {
             code: `/(\\d\\d\\d*)/`,
             output: `/(\\d{2}\\d*)/`,
diff --git a/tests/lib/rules/prefer-question-quantifier.ts b/tests/lib/rules/prefer-question-quantifier.ts
index e481d32..ef7adc0 100644
--- a/tests/lib/rules/prefer-question-quantifier.ts
+++ b/tests/lib/rules/prefer-question-quantifier.ts
@@ -3,7 +3,7 @@ import rule from "../../../lib/rules/prefer-question-quantifier"
 
 const tester = new RuleTester({
     parserOptions: {
-        ecmaVersion: 2020,
+        ecmaVersion: "latest",
         sourceType: "module",
     },
 })
@@ -58,6 +58,11 @@ tester.run("prefer-question-quantifier", rule as any, {
                 },
             ],
         },
+        {
+            code: "/(a){0,1}/v",
+            output: "/(a)?/v",
+            errors: ["Unexpected quantifier '{0,1}'. Use '?' instead."],
+        },
         {
             code: "/(a){0,1}?/",
             output: "/(a)??/",
diff --git a/tests/lib/rules/prefer-range.ts b/tests/lib/rules/prefer-range.ts
index 8a6ba0c..3ea3fae 100644
--- a/tests/lib/rules/prefer-range.ts
+++ b/tests/lib/rules/prefer-range.ts
@@ -3,7 +3,7 @@ import rule from "../../../lib/rules/prefer-range"
 
 const tester = new RuleTester({
     parserOptions: {
-        ecmaVersion: 2020,
+        ecmaVersion: "latest",
         sourceType: "module",
     },
 })
@@ -58,6 +58,8 @@ tester.run("prefer-range", rule as any, {
             code: `/[а-яА-Я][А-Яа-я]/`,
             options: [{ target: ["alphanumeric", "а-я", "А-Я"] }],
         },
+
+        String.raw`/[\q{a|b|c|d|e|f|abcdef}]/v`,
     ],
     invalid: [
         {
@@ -82,6 +84,14 @@ tester.run("prefer-range", rule as any, {
                 "Unexpected multiple adjacent characters. Use 'a-d' instead.",
             ],
         },
+        {
+            code: `/[ABCD abcd]/v`,
+            output: `/[A-D a-d]/v`,
+            errors: [
+                "Unexpected multiple adjacent characters. Use 'A-D' instead.",
+                "Unexpected multiple adjacent characters. Use 'a-d' instead.",
+            ],
+        },
         {
             code: `/[abc-f]/`,
             output: `/[a-f]/`,
diff --git a/tests/lib/rules/prefer-star-quantifier.ts b/tests/lib/rules/prefer-star-quantifier.ts
index 8889165..5259949 100644
--- a/tests/lib/rules/prefer-star-quantifier.ts
+++ b/tests/lib/rules/prefer-star-quantifier.ts
@@ -3,7 +3,7 @@ import rule from "../../../lib/rules/prefer-star-quantifier"
 
 const tester = new RuleTester({
     parserOptions: {
-        ecmaVersion: 2020,
+        ecmaVersion: "latest",
         sourceType: "module",
     },
 })
@@ -44,6 +44,11 @@ tester.run("prefer-star-quantifier", rule as any, {
                 },
             ],
         },
+        {
+            code: "/(a){0,}/v",
+            output: "/(a)*/v",
+            errors: ["Unexpected quantifier '{0,}'. Use '*' instead."],
+        },
         {
             code: "/(a){0,}?/",
             output: "/(a)*?/",
diff --git a/tests/lib/rules/sort-flags.ts b/tests/lib/rules/sort-flags.ts
index e40d54d..3cc77f9 100644
--- a/tests/lib/rules/sort-flags.ts
+++ b/tests/lib/rules/sort-flags.ts
@@ -3,7 +3,7 @@ import rule from "../../../lib/rules/sort-flags"
 
 const tester = new RuleTester({
     parserOptions: {
-        ecmaVersion: 2020,
+        ecmaVersion: "latest",
         sourceType: "module",
     },
 })
@@ -14,10 +14,12 @@ tester.run("sort-flags", rule as any, {
         String.raw`/\w/im`,
         String.raw`/\w/gi`,
         String.raw`/\w/gimsuy`,
+        String.raw`/\w/gimsvy`,
         String.raw`new RegExp("\\w", "i")`,
         String.raw`new RegExp("\\w", "gi")`,
         String.raw`new RegExp("\\w", "gimsuy")`,
         String.raw`new RegExp("\\w", "dgimsuy")`,
+        String.raw`new RegExp("\\w", "dgimsvy")`,
 
         // ignore
         String.raw`
@@ -38,6 +40,11 @@ tester.run("sort-flags", rule as any, {
                 },
             ],
         },
+        {
+            code: String.raw`/\w/yvsimg`,
+            output: String.raw`/\w/gimsvy`,
+            errors: ["The flags 'yvsimg' should be in the order 'gimsvy'."],
+        },
         {
             code: String.raw`new RegExp("\\w", "yusimg")`,
             output: String.raw`new RegExp("\\w", "gimsuy")`,