From 505a2039627350fe7ae0b990d48cd035d8184949 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Wed, 9 Nov 2022 01:00:16 +0800 Subject: [PATCH] `prefer-regexp-test`: Use suggestions if not sure regexp without `g` flag (#1954) --- docs/rules/prefer-regexp-test.md | 2 +- readme.md | 2 +- rules/prefer-regexp-test.js | 44 ++- test/prefer-regexp-test.mjs | 55 ++- test/snapshots/prefer-regexp-test.mjs.md | 386 +++++++++++---------- test/snapshots/prefer-regexp-test.mjs.snap | Bin 2810 -> 3018 bytes 6 files changed, 258 insertions(+), 231 deletions(-) diff --git a/docs/rules/prefer-regexp-test.md b/docs/rules/prefer-regexp-test.md index 06096169b8..ca857ab78a 100644 --- a/docs/rules/prefer-regexp-test.md +++ b/docs/rules/prefer-regexp-test.md @@ -2,7 +2,7 @@ ✅ This rule is enabled in the `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs). -🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). +🔧💡 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix) and manually fixable by [editor suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions). diff --git a/readme.md b/readme.md index faa635af74..247137dc0a 100644 --- a/readme.md +++ b/readme.md @@ -137,7 +137,7 @@ Use a [preset config](#preset-configs) or configure each rules in `package.json` | [prefer-prototype-methods](docs/rules/prefer-prototype-methods.md) | Prefer borrowing methods from the prototype instead of the instance. | ✅ | 🔧 | | | [prefer-query-selector](docs/rules/prefer-query-selector.md) | Prefer `.querySelector()` over `.getElementById()`, `.querySelectorAll()` over `.getElementsByClassName()` and `.getElementsByTagName()`. | ✅ | 🔧 | | | [prefer-reflect-apply](docs/rules/prefer-reflect-apply.md) | Prefer `Reflect.apply()` over `Function#apply()`. | ✅ | 🔧 | | -| [prefer-regexp-test](docs/rules/prefer-regexp-test.md) | Prefer `RegExp#test()` over `String#match()` and `RegExp#exec()`. | ✅ | 🔧 | | +| [prefer-regexp-test](docs/rules/prefer-regexp-test.md) | Prefer `RegExp#test()` over `String#match()` and `RegExp#exec()`. | ✅ | 🔧 | 💡 | | [prefer-set-has](docs/rules/prefer-set-has.md) | Prefer `Set#has()` over `Array#includes()` when checking for existence or non-existence. | ✅ | 🔧 | 💡 | | [prefer-spread](docs/rules/prefer-spread.md) | Prefer the spread operator over `Array.from(…)`, `Array#concat(…)`, `Array#slice()` and `String#split('')`. | ✅ | 🔧 | 💡 | | [prefer-string-replace-all](docs/rules/prefer-string-replace-all.md) | Prefer `String#replaceAll()` over regex searches with the global flag. | | 🔧 | | diff --git a/rules/prefer-regexp-test.js b/rules/prefer-regexp-test.js index 68164485df..5a2e1ebd00 100644 --- a/rules/prefer-regexp-test.js +++ b/rules/prefer-regexp-test.js @@ -8,9 +8,11 @@ const shouldAddParenthesesToMemberExpressionObject = require('./utils/should-add const REGEXP_EXEC = 'regexp-exec'; const STRING_MATCH = 'string-match'; +const SUGGESTION = 'suggestion'; const messages = { [REGEXP_EXEC]: 'Prefer `.test(…)` over `.exec(…)`.', [STRING_MATCH]: 'Prefer `RegExp#test(…)` over `String#match(…)`.', + [SUGGESTION]: 'Switch to `RegExp#test(…)`.', }; const cases = [ @@ -75,6 +77,21 @@ const isRegExpNode = node => && node.callee.name === 'RegExp' ); +const isRegExpWithoutGFlag = (node, scope) => { + const staticResult = getStaticValue(node, scope); + + // Don't know if there is `g` flag + if (!staticResult) { + return false; + } + + const {value} = staticResult; + return ( + Object.prototype.toString.call(value) === '[object RegExp]' + && !value.flags.includes('g') + ); +}; + /** @param {import('eslint').Rule.RuleContext} context */ const create = context => Object.fromEntries( cases.map(checkCase => [ @@ -97,20 +114,22 @@ const create = context => Object.fromEntries( messageId: type, }; - if (!isRegExpNode(regexpNode)) { - const staticResult = getStaticValue(regexpNode, context.getScope()); - if (staticResult) { - const {value} = staticResult; - if ( - Object.prototype.toString.call(value) !== '[object RegExp]' - || value.flags.includes('g') - ) { - return problem; - } - } + const fixFunction = fixer => fix(fixer, nodes, context.getSourceCode()); + + if ( + isRegExpNode(regexpNode) + || isRegExpWithoutGFlag(regexpNode, context.getScope()) + ) { + problem.fix = fixFunction; + } else { + problem.suggest = [ + { + messageId: SUGGESTION, + fix: fixFunction, + }, + ]; } - problem.fix = fixer => fix(fixer, nodes, context.getSourceCode()); return problem; }, ]), @@ -125,6 +144,7 @@ module.exports = { description: 'Prefer `RegExp#test()` over `String#match()` and `RegExp#exec()`.', }, fixable: 'code', + hasSuggestions: true, messages, }, }; diff --git a/test/prefer-regexp-test.mjs b/test/prefer-regexp-test.mjs index c6a53fab6a..66c73d167e 100644 --- a/test/prefer-regexp-test.mjs +++ b/test/prefer-regexp-test.mjs @@ -3,7 +3,7 @@ import {getTester} from './utils/test.mjs'; const {test} = getTester(import.meta); -test({ +test.snapshot({ valid: [ 'const bar = !re.test(foo)', // Not `boolean` @@ -41,38 +41,33 @@ test({ 'if (foo.match(1n)) {}', 'if (foo.match(true)) {}', ], - invalid: [], -}); - -test.snapshot({ - valid: [], invalid: [ // `String#match()` - 'const bar = !foo.match(re)', - 'const bar = Boolean(foo.match(re))', - 'if (foo.match(re)) {}', - 'const bar = foo.match(re) ? 1 : 2', - 'while (foo.match(re)) foo = foo.slice(1);', - 'do {foo = foo.slice(1)} while (foo.match(re));', - 'for (; foo.match(re); ) foo = foo.slice(1);', + 'const re = /a/; const bar = !foo.match(re)', + 'const re = /a/; const bar = Boolean(foo.match(re))', + 'const re = /a/; if (foo.match(re)) {}', + 'const re = /a/; const bar = foo.match(re) ? 1 : 2', + 'const re = /a/; while (foo.match(re)) foo = foo.slice(1);', + 'const re = /a/; do {foo = foo.slice(1)} while (foo.match(re));', + 'const re = /a/; for (; foo.match(re); ) foo = foo.slice(1);', // `RegExp#exec()` - 'const bar = !re.exec(foo)', - 'const bar = Boolean(re.exec(foo))', - 'if (re.exec(foo)) {}', - 'const bar = re.exec(foo) ? 1 : 2', - 'while (re.exec(foo)) foo = foo.slice(1);', - 'do {foo = foo.slice(1)} while (re.exec(foo));', - 'for (; re.exec(foo); ) foo = foo.slice(1);', + 'const re = /a/; const bar = !re.exec(foo)', + 'const re = /a/; const bar = Boolean(re.exec(foo))', + 'const re = /a/; if (re.exec(foo)) {}', + 'const re = /a/; const bar = re.exec(foo) ? 1 : 2', + 'const re = /a/; while (re.exec(foo)) foo = foo.slice(1);', + 'const re = /a/; do {foo = foo.slice(1)} while (re.exec(foo));', + 'const re = /a/; for (; re.exec(foo); ) foo = foo.slice(1);', // Parentheses - 'if ((0, foo).match(re)) {}', - 'if ((0, foo).match((re))) {}', - 'if ((foo).match(re)) {}', - 'if ((foo).match((re))) {}', + 'const re = /a/; if ((0, foo).match(re)) {}', + 'const re = /a/; if ((0, foo).match((re))) {}', + 'const re = /a/; if ((foo).match(re)) {}', + 'const re = /a/; if ((foo).match((re))) {}', 'if (foo.match(/re/)) {}', - 'if (foo.match(bar)) {}', - 'if (foo.match(bar.baz)) {}', + 'const re = /a/; if (foo.match(re)) {}', + 'const bar = {bar: /a/}; if (foo.match(bar.baz)) {}', 'if (foo.match(bar.baz())) {}', 'if (foo.match(new RegExp("re", "g"))) {}', 'if (foo.match(new SomeRegExp())) {}', @@ -89,14 +84,14 @@ test.snapshot({ 'if ((foo).match(new SomeRegExp)) {}', 'if ((foo).match(bar?.baz)) {}', 'if ((foo).match(bar?.baz())) {}', - 'if ((foo).match(bar || baz)) {}', + 'const bar = false; const baz = /a/; if ((foo).match(bar || baz)) {}', outdent` async function a() { if ((foo).match(await bar())) {} } `, // Should not need handle ASI problem - 'if (foo.match([re][0])) {}', + 'const re = [/a/]; if (foo.match([re][0])) {}', // Comments outdent` @@ -151,6 +146,10 @@ test.snapshot({ const regex = /weird/gyi; if (regex.exec(foo)); `, + outdent` + let re = new RegExp('foo', 'g'); + if(str.match(re)); + `, ], }); diff --git a/test/snapshots/prefer-regexp-test.mjs.md b/test/snapshots/prefer-regexp-test.mjs.md index 48766edb47..4838a18635 100644 --- a/test/snapshots/prefer-regexp-test.mjs.md +++ b/test/snapshots/prefer-regexp-test.mjs.md @@ -5,291 +5,291 @@ The actual snapshot is saved in `prefer-regexp-test.mjs.snap`. Generated by [AVA](https://avajs.dev). ## Invalid #1 - 1 | const bar = !foo.match(re) + 1 | const re = /a/; const bar = !foo.match(re) > Output `␊ - 1 | const bar = !re.test(foo)␊ + 1 | const re = /a/; const bar = !re.test(foo)␊ ` > Error 1/1 `␊ - > 1 | const bar = !foo.match(re)␊ - | ^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + > 1 | const re = /a/; const bar = !foo.match(re)␊ + | ^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ ` ## Invalid #2 - 1 | const bar = Boolean(foo.match(re)) + 1 | const re = /a/; const bar = Boolean(foo.match(re)) > Output `␊ - 1 | const bar = Boolean(re.test(foo))␊ + 1 | const re = /a/; const bar = Boolean(re.test(foo))␊ ` > Error 1/1 `␊ - > 1 | const bar = Boolean(foo.match(re))␊ - | ^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + > 1 | const re = /a/; const bar = Boolean(foo.match(re))␊ + | ^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ ` ## Invalid #3 - 1 | if (foo.match(re)) {} + 1 | const re = /a/; if (foo.match(re)) {} > Output `␊ - 1 | if (re.test(foo)) {}␊ + 1 | const re = /a/; if (re.test(foo)) {}␊ ` > Error 1/1 `␊ - > 1 | if (foo.match(re)) {}␊ - | ^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + > 1 | const re = /a/; if (foo.match(re)) {}␊ + | ^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ ` ## Invalid #4 - 1 | const bar = foo.match(re) ? 1 : 2 + 1 | const re = /a/; const bar = foo.match(re) ? 1 : 2 > Output `␊ - 1 | const bar = re.test(foo) ? 1 : 2␊ + 1 | const re = /a/; const bar = re.test(foo) ? 1 : 2␊ ` > Error 1/1 `␊ - > 1 | const bar = foo.match(re) ? 1 : 2␊ - | ^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + > 1 | const re = /a/; const bar = foo.match(re) ? 1 : 2␊ + | ^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ ` ## Invalid #5 - 1 | while (foo.match(re)) foo = foo.slice(1); + 1 | const re = /a/; while (foo.match(re)) foo = foo.slice(1); > Output `␊ - 1 | while (re.test(foo)) foo = foo.slice(1);␊ + 1 | const re = /a/; while (re.test(foo)) foo = foo.slice(1);␊ ` > Error 1/1 `␊ - > 1 | while (foo.match(re)) foo = foo.slice(1);␊ - | ^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + > 1 | const re = /a/; while (foo.match(re)) foo = foo.slice(1);␊ + | ^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ ` ## Invalid #6 - 1 | do {foo = foo.slice(1)} while (foo.match(re)); + 1 | const re = /a/; do {foo = foo.slice(1)} while (foo.match(re)); > Output `␊ - 1 | do {foo = foo.slice(1)} while (re.test(foo));␊ + 1 | const re = /a/; do {foo = foo.slice(1)} while (re.test(foo));␊ ` > Error 1/1 `␊ - > 1 | do {foo = foo.slice(1)} while (foo.match(re));␊ - | ^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + > 1 | const re = /a/; do {foo = foo.slice(1)} while (foo.match(re));␊ + | ^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ ` ## Invalid #7 - 1 | for (; foo.match(re); ) foo = foo.slice(1); + 1 | const re = /a/; for (; foo.match(re); ) foo = foo.slice(1); > Output `␊ - 1 | for (; re.test(foo); ) foo = foo.slice(1);␊ + 1 | const re = /a/; for (; re.test(foo); ) foo = foo.slice(1);␊ ` > Error 1/1 `␊ - > 1 | for (; foo.match(re); ) foo = foo.slice(1);␊ - | ^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + > 1 | const re = /a/; for (; foo.match(re); ) foo = foo.slice(1);␊ + | ^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ ` ## Invalid #8 - 1 | const bar = !re.exec(foo) + 1 | const re = /a/; const bar = !re.exec(foo) > Output `␊ - 1 | const bar = !re.test(foo)␊ + 1 | const re = /a/; const bar = !re.test(foo)␊ ` > Error 1/1 `␊ - > 1 | const bar = !re.exec(foo)␊ - | ^^^^ Prefer \`.test(…)\` over \`.exec(…)\`.␊ + > 1 | const re = /a/; const bar = !re.exec(foo)␊ + | ^^^^ Prefer \`.test(…)\` over \`.exec(…)\`.␊ ` ## Invalid #9 - 1 | const bar = Boolean(re.exec(foo)) + 1 | const re = /a/; const bar = Boolean(re.exec(foo)) > Output `␊ - 1 | const bar = Boolean(re.test(foo))␊ + 1 | const re = /a/; const bar = Boolean(re.test(foo))␊ ` > Error 1/1 `␊ - > 1 | const bar = Boolean(re.exec(foo))␊ - | ^^^^ Prefer \`.test(…)\` over \`.exec(…)\`.␊ + > 1 | const re = /a/; const bar = Boolean(re.exec(foo))␊ + | ^^^^ Prefer \`.test(…)\` over \`.exec(…)\`.␊ ` ## Invalid #10 - 1 | if (re.exec(foo)) {} + 1 | const re = /a/; if (re.exec(foo)) {} > Output `␊ - 1 | if (re.test(foo)) {}␊ + 1 | const re = /a/; if (re.test(foo)) {}␊ ` > Error 1/1 `␊ - > 1 | if (re.exec(foo)) {}␊ - | ^^^^ Prefer \`.test(…)\` over \`.exec(…)\`.␊ + > 1 | const re = /a/; if (re.exec(foo)) {}␊ + | ^^^^ Prefer \`.test(…)\` over \`.exec(…)\`.␊ ` ## Invalid #11 - 1 | const bar = re.exec(foo) ? 1 : 2 + 1 | const re = /a/; const bar = re.exec(foo) ? 1 : 2 > Output `␊ - 1 | const bar = re.test(foo) ? 1 : 2␊ + 1 | const re = /a/; const bar = re.test(foo) ? 1 : 2␊ ` > Error 1/1 `␊ - > 1 | const bar = re.exec(foo) ? 1 : 2␊ - | ^^^^ Prefer \`.test(…)\` over \`.exec(…)\`.␊ + > 1 | const re = /a/; const bar = re.exec(foo) ? 1 : 2␊ + | ^^^^ Prefer \`.test(…)\` over \`.exec(…)\`.␊ ` ## Invalid #12 - 1 | while (re.exec(foo)) foo = foo.slice(1); + 1 | const re = /a/; while (re.exec(foo)) foo = foo.slice(1); > Output `␊ - 1 | while (re.test(foo)) foo = foo.slice(1);␊ + 1 | const re = /a/; while (re.test(foo)) foo = foo.slice(1);␊ ` > Error 1/1 `␊ - > 1 | while (re.exec(foo)) foo = foo.slice(1);␊ - | ^^^^ Prefer \`.test(…)\` over \`.exec(…)\`.␊ + > 1 | const re = /a/; while (re.exec(foo)) foo = foo.slice(1);␊ + | ^^^^ Prefer \`.test(…)\` over \`.exec(…)\`.␊ ` ## Invalid #13 - 1 | do {foo = foo.slice(1)} while (re.exec(foo)); + 1 | const re = /a/; do {foo = foo.slice(1)} while (re.exec(foo)); > Output `␊ - 1 | do {foo = foo.slice(1)} while (re.test(foo));␊ + 1 | const re = /a/; do {foo = foo.slice(1)} while (re.test(foo));␊ ` > Error 1/1 `␊ - > 1 | do {foo = foo.slice(1)} while (re.exec(foo));␊ - | ^^^^ Prefer \`.test(…)\` over \`.exec(…)\`.␊ + > 1 | const re = /a/; do {foo = foo.slice(1)} while (re.exec(foo));␊ + | ^^^^ Prefer \`.test(…)\` over \`.exec(…)\`.␊ ` ## Invalid #14 - 1 | for (; re.exec(foo); ) foo = foo.slice(1); + 1 | const re = /a/; for (; re.exec(foo); ) foo = foo.slice(1); > Output `␊ - 1 | for (; re.test(foo); ) foo = foo.slice(1);␊ + 1 | const re = /a/; for (; re.test(foo); ) foo = foo.slice(1);␊ ` > Error 1/1 `␊ - > 1 | for (; re.exec(foo); ) foo = foo.slice(1);␊ - | ^^^^ Prefer \`.test(…)\` over \`.exec(…)\`.␊ + > 1 | const re = /a/; for (; re.exec(foo); ) foo = foo.slice(1);␊ + | ^^^^ Prefer \`.test(…)\` over \`.exec(…)\`.␊ ` ## Invalid #15 - 1 | if ((0, foo).match(re)) {} + 1 | const re = /a/; if ((0, foo).match(re)) {} > Output `␊ - 1 | if ((re).test((0, foo))) {}␊ + 1 | const re = /a/; if ((re).test((0, foo))) {}␊ ` > Error 1/1 `␊ - > 1 | if ((0, foo).match(re)) {}␊ - | ^^^^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + > 1 | const re = /a/; if ((0, foo).match(re)) {}␊ + | ^^^^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ ` ## Invalid #16 - 1 | if ((0, foo).match((re))) {} + 1 | const re = /a/; if ((0, foo).match((re))) {} > Output `␊ - 1 | if ((re).test((0, foo))) {}␊ + 1 | const re = /a/; if ((re).test((0, foo))) {}␊ ` > Error 1/1 `␊ - > 1 | if ((0, foo).match((re))) {}␊ - | ^^^^^^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + > 1 | const re = /a/; if ((0, foo).match((re))) {}␊ + | ^^^^^^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ ` ## Invalid #17 - 1 | if ((foo).match(re)) {} + 1 | const re = /a/; if ((foo).match(re)) {} > Output `␊ - 1 | if ((re).test(foo)) {}␊ + 1 | const re = /a/; if ((re).test(foo)) {}␊ ` > Error 1/1 `␊ - > 1 | if ((foo).match(re)) {}␊ - | ^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + > 1 | const re = /a/; if ((foo).match(re)) {}␊ + | ^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ ` ## Invalid #18 - 1 | if ((foo).match((re))) {} + 1 | const re = /a/; if ((foo).match((re))) {} > Output `␊ - 1 | if ((re).test((foo))) {}␊ + 1 | const re = /a/; if ((re).test((foo))) {}␊ ` > Error 1/1 `␊ - > 1 | if ((foo).match((re))) {}␊ - | ^^^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + > 1 | const re = /a/; if ((foo).match((re))) {}␊ + | ^^^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ ` ## Invalid #19 @@ -309,51 +309,47 @@ Generated by [AVA](https://avajs.dev). ` ## Invalid #20 - 1 | if (foo.match(bar)) {} + 1 | const re = /a/; if (foo.match(re)) {} > Output `␊ - 1 | if (bar.test(foo)) {}␊ + 1 | const re = /a/; if (re.test(foo)) {}␊ ` > Error 1/1 `␊ - > 1 | if (foo.match(bar)) {}␊ - | ^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + > 1 | const re = /a/; if (foo.match(re)) {}␊ + | ^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ ` ## Invalid #21 - 1 | if (foo.match(bar.baz)) {} - -> Output - - `␊ - 1 | if (bar.baz.test(foo)) {}␊ - ` + 1 | const bar = {bar: /a/}; if (foo.match(bar.baz)) {} > Error 1/1 `␊ - > 1 | if (foo.match(bar.baz)) {}␊ - | ^^^^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + > 1 | const bar = {bar: /a/}; if (foo.match(bar.baz)) {}␊ + | ^^^^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to \`RegExp#test(…)\`.␊ + 1 | const bar = {bar: /a/}; if (bar.baz.test(foo)) {}␊ ` ## Invalid #22 1 | if (foo.match(bar.baz())) {} -> Output - - `␊ - 1 | if (bar.baz().test(foo)) {}␊ - ` - > Error 1/1 `␊ > 1 | if (foo.match(bar.baz())) {}␊ | ^^^^^^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to \`RegExp#test(…)\`.␊ + 1 | if (bar.baz().test(foo)) {}␊ ` ## Invalid #23 @@ -375,81 +371,71 @@ Generated by [AVA](https://avajs.dev). ## Invalid #24 1 | if (foo.match(new SomeRegExp())) {} -> Output - - `␊ - 1 | if (new SomeRegExp().test(foo)) {}␊ - ` - > Error 1/1 `␊ > 1 | if (foo.match(new SomeRegExp())) {}␊ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to \`RegExp#test(…)\`.␊ + 1 | if (new SomeRegExp().test(foo)) {}␊ ` ## Invalid #25 1 | if (foo.match(new SomeRegExp)) {} -> Output - - `␊ - 1 | if ((new SomeRegExp).test(foo)) {}␊ - ` - > Error 1/1 `␊ > 1 | if (foo.match(new SomeRegExp)) {}␊ | ^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to \`RegExp#test(…)\`.␊ + 1 | if ((new SomeRegExp).test(foo)) {}␊ ` ## Invalid #26 1 | if (foo.match(bar?.baz)) {} -> Output - - `␊ - 1 | if (bar?.baz.test(foo)) {}␊ - ` - > Error 1/1 `␊ > 1 | if (foo.match(bar?.baz)) {}␊ | ^^^^^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to \`RegExp#test(…)\`.␊ + 1 | if (bar?.baz.test(foo)) {}␊ ` ## Invalid #27 1 | if (foo.match(bar?.baz())) {} -> Output - - `␊ - 1 | if (bar?.baz().test(foo)) {}␊ - ` - > Error 1/1 `␊ > 1 | if (foo.match(bar?.baz())) {}␊ | ^^^^^^^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to \`RegExp#test(…)\`.␊ + 1 | if (bar?.baz().test(foo)) {}␊ ` ## Invalid #28 1 | if (foo.match(bar || baz)) {} -> Output - - `␊ - 1 | if ((bar || baz).test(foo)) {}␊ - ` - > Error 1/1 `␊ > 1 | if (foo.match(bar || baz)) {}␊ | ^^^^^^^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to \`RegExp#test(…)\`.␊ + 1 | if ((bar || baz).test(foo)) {}␊ ` ## Invalid #29 @@ -457,14 +443,6 @@ Generated by [AVA](https://avajs.dev). 2 | if (foo.match(await bar())) {} 3 | } -> Output - - `␊ - 1 | async function a() {␊ - 2 | if ((await bar()).test(foo)) {}␊ - 3 | }␊ - ` - > Error 1/1 `␊ @@ -472,6 +450,12 @@ Generated by [AVA](https://avajs.dev). > 2 | if (foo.match(await bar())) {}␊ | ^^^^^^^^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ 3 | }␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to \`RegExp#test(…)\`.␊ + 1 | async function a() {␊ + 2 | if ((await bar()).test(foo)) {}␊ + 3 | }␊ ` ## Invalid #30 @@ -493,65 +477,59 @@ Generated by [AVA](https://avajs.dev). ## Invalid #31 1 | if ((foo).match(new SomeRegExp)) {} -> Output - - `␊ - 1 | if ((new SomeRegExp).test(foo)) {}␊ - ` - > Error 1/1 `␊ > 1 | if ((foo).match(new SomeRegExp)) {}␊ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to \`RegExp#test(…)\`.␊ + 1 | if ((new SomeRegExp).test(foo)) {}␊ ` ## Invalid #32 1 | if ((foo).match(bar?.baz)) {} -> Output - - `␊ - 1 | if ((bar?.baz).test(foo)) {}␊ - ` - > Error 1/1 `␊ > 1 | if ((foo).match(bar?.baz)) {}␊ | ^^^^^^^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to \`RegExp#test(…)\`.␊ + 1 | if ((bar?.baz).test(foo)) {}␊ ` ## Invalid #33 1 | if ((foo).match(bar?.baz())) {} -> Output - - `␊ - 1 | if ((bar?.baz()).test(foo)) {}␊ - ` - > Error 1/1 `␊ > 1 | if ((foo).match(bar?.baz())) {}␊ | ^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to \`RegExp#test(…)\`.␊ + 1 | if ((bar?.baz()).test(foo)) {}␊ ` ## Invalid #34 - 1 | if ((foo).match(bar || baz)) {} + 1 | const bar = false; const baz = /a/; if ((foo).match(bar || baz)) {} > Output `␊ - 1 | if ((bar || baz).test(foo)) {}␊ + 1 | const bar = false; const baz = /a/; if ((bar || baz).test(foo)) {}␊ ` > Error 1/1 `␊ - > 1 | if ((foo).match(bar || baz)) {}␊ - | ^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + > 1 | const bar = false; const baz = /a/; if ((foo).match(bar || baz)) {}␊ + | ^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ ` ## Invalid #35 @@ -559,14 +537,6 @@ Generated by [AVA](https://avajs.dev). 2 | if ((foo).match(await bar())) {} 3 | } -> Output - - `␊ - 1 | async function a() {␊ - 2 | if ((await bar()).test(foo)) {}␊ - 3 | }␊ - ` - > Error 1/1 `␊ @@ -574,22 +544,26 @@ Generated by [AVA](https://avajs.dev). > 2 | if ((foo).match(await bar())) {}␊ | ^^^^^^^^^^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ 3 | }␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to \`RegExp#test(…)\`.␊ + 1 | async function a() {␊ + 2 | if ((await bar()).test(foo)) {}␊ + 3 | }␊ ` ## Invalid #36 - 1 | if (foo.match([re][0])) {} - -> Output - - `␊ - 1 | if ([re][0].test(foo)) {}␊ - ` + 1 | const re = [/a/]; if (foo.match([re][0])) {} > Error 1/1 `␊ - > 1 | if (foo.match([re][0])) {}␊ - | ^^^^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + > 1 | const re = [/a/]; if (foo.match([re][0])) {}␊ + | ^^^^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to \`RegExp#test(…)\`.␊ + 1 | const re = [/a/]; if ([re][0].test(foo)) {}␊ ` ## Invalid #37 @@ -604,21 +578,6 @@ Generated by [AVA](https://avajs.dev). 9 | ) {} 10 | } -> Output - - `␊ - 1 | async function a() {␊ - 2 | if (␊ - 3 | /* 1 */ (await /* 6 */ bar()) /* 2 */␊ - 4 | ./* 3 */ test /* 4 */ (␊ - 5 | /* 5 */ foo() /* 7 */␊ - 6 | ,␊ - 7 | /* 8 */␊ - 8 | )␊ - 9 | ) {}␊ - 10 | }␊ - ` - > Error 1/1 `␊ @@ -638,6 +597,19 @@ Generated by [AVA](https://avajs.dev). | ^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ 9 | ) {}␊ 10 | }␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to \`RegExp#test(…)\`.␊ + 1 | async function a() {␊ + 2 | if (␊ + 3 | /* 1 */ (await /* 6 */ bar()) /* 2 */␊ + 4 | ./* 3 */ test /* 4 */ (␊ + 5 | /* 5 */ foo() /* 7 */␊ + 6 | ,␊ + 7 | /* 8 */␊ + 8 | )␊ + 9 | ) {}␊ + 10 | }␊ ` ## Invalid #38 @@ -652,6 +624,12 @@ Generated by [AVA](https://avajs.dev). > 2 | if (foo.match(string)) {␊ | ^^^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ 3 | }␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to \`RegExp#test(…)\`.␊ + 1 | const string = '[.!?]\\s*$';␊ + 2 | if (string.test(foo)) {␊ + 3 | }␊ ` ## Invalid #39 @@ -676,17 +654,15 @@ Generated by [AVA](https://avajs.dev). ## Invalid #40 1 | if (foo.match(unknown)) {} -> Output - - `␊ - 1 | if (unknown.test(foo)) {}␊ - ` - > Error 1/1 `␊ > 1 | if (foo.match(unknown)) {}␊ | ^^^^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to \`RegExp#test(…)\`.␊ + 1 | if (unknown.test(foo)) {}␊ ` ## Invalid #41 @@ -875,6 +851,11 @@ Generated by [AVA](https://avajs.dev). 1 | const regex = /weird/g;␊ > 2 | if (foo.match(regex));␊ | ^^^^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to \`RegExp#test(…)\`.␊ + 1 | const regex = /weird/g;␊ + 2 | if (regex.test(foo));␊ ` ## Invalid #53 @@ -887,6 +868,11 @@ Generated by [AVA](https://avajs.dev). 1 | const regex = /weird/g;␊ > 2 | if (regex.exec(foo));␊ | ^^^^ Prefer \`.test(…)\` over \`.exec(…)\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to \`RegExp#test(…)\`.␊ + 1 | const regex = /weird/g;␊ + 2 | if (regex.test(foo));␊ ` ## Invalid #54 @@ -918,4 +904,26 @@ Generated by [AVA](https://avajs.dev). 1 | const regex = /weird/gyi;␊ > 2 | if (regex.exec(foo));␊ | ^^^^ Prefer \`.test(…)\` over \`.exec(…)\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to \`RegExp#test(…)\`.␊ + 1 | const regex = /weird/gyi;␊ + 2 | if (regex.test(foo));␊ + ` + +## Invalid #56 + 1 | let re = new RegExp('foo', 'g'); + 2 | if(str.match(re)); + +> Error 1/1 + + `␊ + 1 | let re = new RegExp('foo', 'g');␊ + > 2 | if(str.match(re));␊ + | ^^^^^^^^^^^^^ Prefer \`RegExp#test(…)\` over \`String#match(…)\`.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to \`RegExp#test(…)\`.␊ + 1 | let re = new RegExp('foo', 'g');␊ + 2 | if(re.test(str));␊ ` diff --git a/test/snapshots/prefer-regexp-test.mjs.snap b/test/snapshots/prefer-regexp-test.mjs.snap index b29c719abe9cae07044322d99d0dd045926cbfe8..dc06b67f28ace0f8d25e2c694322ec7320950ac2 100644 GIT binary patch literal 3018 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 MA9e8g61Ok_0Q>`TBme*a