From 8857217dc6c1cfb8d3af4a2f00a99f480145ffd8 Mon Sep 17 00:00:00 2001 From: oOBoomberOo Date: Sat, 9 Jan 2021 17:25:56 +0900 Subject: [PATCH 1/7] Continue the work from `JaniM:better-prefer-t-regex` --- rules/prefer-t-regex.js | 203 +++++++++++++++++++++++++++++----------- test/prefer-t-regex.js | 40 +++++++- 2 files changed, 189 insertions(+), 54 deletions(-) diff --git a/rules/prefer-t-regex.js b/rules/prefer-t-regex.js index 3418024d..340c9f83 100644 --- a/rules/prefer-t-regex.js +++ b/rules/prefer-t-regex.js @@ -13,75 +13,172 @@ const create = context => { 'falsy' ]); + const equalityTests = new Set([ + 'is', + 'deepEqual' + ]); + + // Find the latest reference to the given identifier's name. const findReference = name => { const reference = context.getScope().references.find(reference => reference.identifier.name === name); + + if (reference === undefined) { + return null; + } + const definitions = reference.resolved.defs; + + if (definitions.length === 0) { + return null; + } + return definitions[definitions.length - 1].node; }; + // Recursively find the "origin" node of the given node. + // Note: Due to some limitation, this function will only find the reference up to two layer. + // + // So the following code will only find the reference in this order: c → b → a + // and it will have no knowledge of the number '0'. + // (assuming we run this function on the identifier c) + // ``` + // let a = 0; + // let b = a; + // let c = b; + // ``` + const findRootReference = node => { + if (node.type === 'Identifier') { + const reference = findReference(node.name); + + if (reference) { + return findRootReference(reference.init); + } + + return node; + } + + if (node.type === 'CallExpression' || node.type === 'NewExpression') { + return findRootReference(node.callee); + } + + // I'm aware that there are other type of Node as well but the scope of the lint shouldn't need those. + return node; + }; + + // Determine if the given node is a regex expression. + // There are two ways to create regex expressions in JavaScript, the first being Regex Literal and the second being RegExp class. + // - The first way can be easily lookup using .regex field in the node. + // - The second way can't really be lookup so I just check the name of a class constructor/function and check if it's matches. + const isRegExp = lookup => { + if (lookup.regex) { + return true; + } + + // Look up references in case it's a variable or RegExp declaration. + const reference = findRootReference(lookup); + + if (reference) { + return reference.regex || reference.name === 'RegExp'; + } + + return false; + }; + + const booleanHandler = node => { + const firstArg = node.arguments[0]; + + // First argument is a call expression + const isFunctionCall = firstArg.type === 'CallExpression'; + if (!isFunctionCall || !firstArg.callee.property) { + return; + } + + const {name} = firstArg.callee.property; + let lookup = {}; + let variable = {}; + + if (name === 'test') { + // `lookup.test(variable)` + lookup = firstArg.callee.object; + variable = firstArg.arguments[0]; + } else if (['search', 'match'].includes(name)) { + // `variable.match(lookup)` + lookup = firstArg.arguments[0]; + variable = firstArg.callee.object; + } + + if (!isRegExp(lookup)) { + return; + } + + const assertion = ['true', 'truthy'].includes(node.callee.property.name) ? 'regex' : 'notRegex'; + + const fix = fixer => { + const source = context.getSourceCode(); + return [ + fixer.replaceText(node.callee.property, assertion), + fixer.replaceText(firstArg, `${source.getText(variable)}, ${source.getText(lookup)}`) + ]; + }; + + context.report({ + node, + message: `Prefer using the \`t.${assertion}()\` assertion.`, + fix + }); + }; + + const equalityHandler = node => { + const [firstArg, secondArg] = node.arguments; + + const firstArgumentIsRegex = isRegExp(firstArg); + const secondArgumentIsRegex = isRegExp(secondArg); + + // If both are regex, or neither are, the expression is ok + if (firstArgumentIsRegex === secondArgumentIsRegex) { + return; + } + + const matchee = secondArgumentIsRegex ? firstArg : secondArg; + const regex = secondArgumentIsRegex ? secondArg : firstArg; + + const assertion = 'regex'; + + const fix = fixer => { + const source = context.getSourceCode(); + return [ + fixer.replaceText(node.callee.property, assertion), + fixer.replaceText(firstArg, `${source.getText(matchee)}`), + fixer.replaceText(secondArg, `${source.getText(regex)}`) + ]; + }; + + context.report({ + node, + message: `Prefer using the \`t.${assertion}()\` assertion.`, + fix + }); + }; + return ava.merge({ CallExpression: visitIf([ ava.isInTestFile, ava.isInTestNode ])(node => { - // Call a boolean assertion, for example, `t.true`, `t.false`, … - const isBooleanAssertion = node.callee.type === 'MemberExpression' && - booleanTests.has(node.callee.property.name) && + const isAssertion = node.callee.type === 'MemberExpression' && util.getNameOfRootNodeObject(node.callee) === 't'; - if (!isBooleanAssertion) { - return; - } - - const firstArg = node.arguments[0]; + const isBooleanAssertion = isAssertion && + booleanTests.has(node.callee.property.name); - // First argument is a call expression - const isFunctionCall = firstArg.type === 'CallExpression'; - if (!isFunctionCall || !firstArg.callee.property) { - return; - } + const isEqualityAssertion = isAssertion && + equalityTests.has(node.callee.property.name); - const {name} = firstArg.callee.property; - let lookup = {}; - let variable = {}; - - if (name === 'test') { - // `lookup.test(variable)` - lookup = firstArg.callee.object; - variable = firstArg.arguments[0]; - } else if (['search', 'match'].includes(name)) { - // `variable.match(lookup)` - lookup = firstArg.arguments[0]; - variable = firstArg.callee.object; + if (isBooleanAssertion) { + booleanHandler(node); + } else if (isEqualityAssertion) { + equalityHandler(node); } - - let isRegExp = lookup.regex; - - // It's not a regexp but an identifier - if (!isRegExp && lookup.type === 'Identifier') { - const reference = findReference(lookup.name); - isRegExp = reference.init.regex; - } - - if (!isRegExp) { - return; - } - - const assertion = ['true', 'truthy'].includes(node.callee.property.name) ? 'regex' : 'notRegex'; - - const fix = fixer => { - const source = context.getSourceCode(); - return [ - fixer.replaceText(node.callee.property, assertion), - fixer.replaceText(firstArg, `${source.getText(variable)}, ${source.getText(lookup)}`) - ]; - }; - - context.report({ - node, - message: `Prefer using the \`t.${assertion}()\` assertion.`, - fix - }); }) }); }; diff --git a/test/prefer-t-regex.js b/test/prefer-t-regex.js index 553a14bb..6334e140 100644 --- a/test/prefer-t-regex.js +++ b/test/prefer-t-regex.js @@ -17,12 +17,14 @@ ruleTester.run('prefer-t-regex', rule, { valid: [ header + 'test(t => t.regex("foo", /\\d+/));', header + 'test(t => t.regex(foo(), /\\d+/));', - header + 'test(t => t.is(/\\d+/.test("foo")), true);', + header + 'test(t => t.is(/\\d+/.test("foo"), true));', header + 'test(t => t.true(1 === 1));', header + 'test(t => t.true(foo.bar()));', header + 'const a = /\\d+/;\ntest(t => t.truthy(a));', header + 'const a = "not a regexp";\ntest(t => t.true(a.test("foo")));', header + 'test("main", t => t.true(foo()));', + header + 'test(t => t.regex(foo, new RegExp("\\d+")));', + header + 'test(t => t.regex(foo, RegExp("\\d+")));', // Shouldn't be triggered since it's not a test file 'test(t => t.true(/\\d+/.test("foo")));' ], @@ -61,6 +63,42 @@ ruleTester.run('prefer-t-regex', rule, { code: header + 'const reg = /\\d+/;\ntest(t => t.true(reg.test(foo.bar())));', output: header + 'const reg = /\\d+/;\ntest(t => t.regex(foo.bar(), reg));', errors: errors('regex') + }, + // The same as the above tests but with `RegExp()` object instead of a regex literal + { + code: header + 'test(t => t.true(new RegExp("\\d+").test("foo")));', + output: header + 'test(t => t.regex("foo", new RegExp("\\d+")));', + errors: errors('regex') + }, + { + code: header + 'test(t => t.false(foo.search(new RegExp("\\d+"))));', + output: header + 'test(t => t.notRegex(foo, new RegExp("\\d+")));', + errors: errors('notRegex') + }, + { + code: header + 'const regexp = RegExp("\\d+");\ntest(t => t.true(foo.search(regexp)));', + output: header + 'const regexp = RegExp("\\d+");\ntest(t => t.regex(foo, regexp));', + errors: errors('regex') + }, + { + code: header + 'test(t => t.truthy(foo.match(new RegExp("\\d+"))));', + output: header + 'test(t => t.regex(foo, new RegExp("\\d+")));', + errors: errors('regex') + }, + { + code: header + 'test(t => t.false(RegExp("\\d+").test("foo")));', + output: header + 'test(t => t.notRegex("foo", RegExp("\\d+")));', + errors: errors('notRegex') + }, + { + code: header + 'test(t => t.true(new RegExp("\\d+").test(foo())));', + output: header + 'test(t => t.regex(foo(), new RegExp("\\d+")));', + errors: errors('regex') + }, + { + code: header + 'const reg = RegExp("\\d+");\ntest(t => t.true(reg.test(foo.bar())));', + output: header + 'const reg = RegExp("\\d+");\ntest(t => t.regex(foo.bar(), reg));', + errors: errors('regex') } ] }); From 64e6dd41ede0c8fbae417127a0b6795a870807c8 Mon Sep 17 00:00:00 2001 From: oOBoomberOo Date: Sat, 9 Jan 2021 17:50:23 +0900 Subject: [PATCH 2/7] Fix integration test --- rules/prefer-t-regex.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/rules/prefer-t-regex.js b/rules/prefer-t-regex.js index 340c9f83..248d27ca 100644 --- a/rules/prefer-t-regex.js +++ b/rules/prefer-t-regex.js @@ -22,17 +22,15 @@ const create = context => { const findReference = name => { const reference = context.getScope().references.find(reference => reference.identifier.name === name); - if (reference === undefined) { - return null; - } + if (reference && reference.resolved) { + const definitions = reference.resolved.defs; - const definitions = reference.resolved.defs; + if (definitions.length === 0) { + return null; + } - if (definitions.length === 0) { - return null; + return definitions[definitions.length - 1].node; } - - return definitions[definitions.length - 1].node; }; // Recursively find the "origin" node of the given node. @@ -50,7 +48,7 @@ const create = context => { if (node.type === 'Identifier') { const reference = findReference(node.name); - if (reference) { + if (reference && reference.init) { return findRootReference(reference.init); } From b8e6cb05128ca4006516f441757db882116e00c9 Mon Sep 17 00:00:00 2001 From: oOBoomberOo Date: Wed, 13 Jan 2021 12:23:13 +0900 Subject: [PATCH 3/7] Add more test coverage --- rules/prefer-t-regex.js | 37 +++++++++++++------- test/prefer-t-regex.js | 75 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 99 insertions(+), 13 deletions(-) diff --git a/rules/prefer-t-regex.js b/rules/prefer-t-regex.js index 248d27ca..d2dd4daf 100644 --- a/rules/prefer-t-regex.js +++ b/rules/prefer-t-regex.js @@ -59,6 +59,10 @@ const create = context => { return findRootReference(node.callee); } + if (node.type === 'MemberExpression') { + return findRootReference(node.object); + } + // I'm aware that there are other type of Node as well but the scope of the lint shouldn't need those. return node; }; @@ -78,8 +82,6 @@ const create = context => { if (reference) { return reference.regex || reference.name === 'RegExp'; } - - return false; }; const booleanHandler = node => { @@ -140,22 +142,33 @@ const create = context => { const matchee = secondArgumentIsRegex ? firstArg : secondArg; const regex = secondArgumentIsRegex ? secondArg : firstArg; - const assertion = 'regex'; - - const fix = fixer => { + const booleanFixer = assertion => fixer => { const source = context.getSourceCode(); return [ fixer.replaceText(node.callee.property, assertion), - fixer.replaceText(firstArg, `${source.getText(matchee)}`), - fixer.replaceText(secondArg, `${source.getText(regex)}`) + fixer.replaceText(firstArg, `${source.getText(regex.arguments[0])}`), + fixer.replaceText(secondArg, `${source.getText(regex.callee.object)}`) ]; }; - context.report({ - node, - message: `Prefer using the \`t.${assertion}()\` assertion.`, - fix - }); + // Only fix a statically verifiable equality + if (regex && matchee.type === 'Literal') { + let assertion; + + if (matchee.raw === 'true') { + assertion = 'regex'; + } else if (matchee.raw === 'false') { + assertion = 'notRegex'; + } else { + return; + } + + context.report({ + node, + message: `Prefer using the \`t.${assertion}()\` assertion.`, + fix: booleanFixer(assertion) + }); + } }; return ava.merge({ diff --git a/test/prefer-t-regex.js b/test/prefer-t-regex.js index 6334e140..7eb917e1 100644 --- a/test/prefer-t-regex.js +++ b/test/prefer-t-regex.js @@ -17,14 +17,21 @@ ruleTester.run('prefer-t-regex', rule, { valid: [ header + 'test(t => t.regex("foo", /\\d+/));', header + 'test(t => t.regex(foo(), /\\d+/));', - header + 'test(t => t.is(/\\d+/.test("foo"), true));', + header + 'test(t => t.is(/\\d+/.test("foo"), foo));', + header + 'test(t => t.is(RegExp("\\d+").test("foo"), foo));', + header + 'test(t => t.is(RegExp(/\\d+/).test("foo"), foo));', + header + 'test(t => t.is(/\\d+/, /\\w+/));', + header + 'test(t => t.is(123, /\\d+/));', header + 'test(t => t.true(1 === 1));', header + 'test(t => t.true(foo.bar()));', + header + 'test(t => t.is(foo, true));', header + 'const a = /\\d+/;\ntest(t => t.truthy(a));', header + 'const a = "not a regexp";\ntest(t => t.true(a.test("foo")));', header + 'test("main", t => t.true(foo()));', header + 'test(t => t.regex(foo, new RegExp("\\d+")));', header + 'test(t => t.regex(foo, RegExp("\\d+")));', + header + 'test(t => t.regex(foo, new RegExp(/\\d+/)));', + header + 'test(t => t.regex(foo, RegExp(/\\d+/)));', // Shouldn't be triggered since it's not a test file 'test(t => t.true(/\\d+/.test("foo")));' ], @@ -59,6 +66,16 @@ ruleTester.run('prefer-t-regex', rule, { output: header + 'test(t => t.regex(foo(), /\\d+/));', errors: errors('regex') }, + { + code: header + 'test(t => t.is(/\\d+/.test(foo), true));', + output: header + 'test(t => t.regex(foo, /\\d+/));', + errors: errors('regex') + }, + { + code: header + 'test(t => t.is(/\\d+/.test(foo), false));', + output: header + 'test(t => t.notRegex(foo, /\\d+/));', + errors: errors('notRegex') + }, { code: header + 'const reg = /\\d+/;\ntest(t => t.true(reg.test(foo.bar())));', output: header + 'const reg = /\\d+/;\ntest(t => t.regex(foo.bar(), reg));', @@ -95,10 +112,66 @@ ruleTester.run('prefer-t-regex', rule, { output: header + 'test(t => t.regex(foo(), new RegExp("\\d+")));', errors: errors('regex') }, + { + code: header + 'test(t => t.is(new RegExp("\\d+").test(foo), true));', + output: header + 'test(t => t.regex(foo, new RegExp("\\d+")));', + errors: errors('regex') + }, + { + code: header + 'test(t => t.is(new RegExp("\\d+").test(foo), false));', + output: header + 'test(t => t.notRegex(foo, new RegExp("\\d+")));', + errors: errors('notRegex') + }, { code: header + 'const reg = RegExp("\\d+");\ntest(t => t.true(reg.test(foo.bar())));', output: header + 'const reg = RegExp("\\d+");\ntest(t => t.regex(foo.bar(), reg));', errors: errors('regex') + }, + // The same as the above tests but with regex literal instead of string regex + { + code: header + 'test(t => t.true(new RegExp(/\\d+/).test("foo")));', + output: header + 'test(t => t.regex("foo", new RegExp(/\\d+/)));', + errors: errors('regex') + }, + { + code: header + 'test(t => t.false(foo.search(new RegExp(/\\d+/))));', + output: header + 'test(t => t.notRegex(foo, new RegExp(/\\d+/)));', + errors: errors('notRegex') + }, + { + code: header + 'const regexp = RegExp(/\\d+/);\ntest(t => t.true(foo.search(regexp)));', + output: header + 'const regexp = RegExp(/\\d+/);\ntest(t => t.regex(foo, regexp));', + errors: errors('regex') + }, + { + code: header + 'test(t => t.truthy(foo.match(new RegExp(/\\d+/))));', + output: header + 'test(t => t.regex(foo, new RegExp(/\\d+/)));', + errors: errors('regex') + }, + { + code: header + 'test(t => t.false(RegExp(/\\d+/).test("foo")));', + output: header + 'test(t => t.notRegex("foo", RegExp(/\\d+/)));', + errors: errors('notRegex') + }, + { + code: header + 'test(t => t.true(new RegExp(/\\d+/).test(foo())));', + output: header + 'test(t => t.regex(foo(), new RegExp(/\\d+/)));', + errors: errors('regex') + }, + { + code: header + 'test(t => t.is(new RegExp(/\\d+/).test(foo), true));', + output: header + 'test(t => t.regex(foo, new RegExp(/\\d+/)));', + errors: errors('regex') + }, + { + code: header + 'test(t => t.is(new RegExp(/\\d+/).test(foo), false));', + output: header + 'test(t => t.notRegex(foo, new RegExp(/\\d+/)));', + errors: errors('notRegex') + }, + { + code: header + 'const reg = RegExp(/\\d+/);\ntest(t => t.true(reg.test(foo.bar())));', + output: header + 'const reg = RegExp(/\\d+/);\ntest(t => t.regex(foo.bar(), reg));', + errors: errors('regex') } ] }); From 97c1efd86d9ef55c11371d9b4d419161da87a1dd Mon Sep 17 00:00:00 2001 From: oOBoomberOo Date: Sun, 17 Jan 2021 14:08:41 +0900 Subject: [PATCH 4/7] Clarify comments --- rules/prefer-t-regex.js | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/rules/prefer-t-regex.js b/rules/prefer-t-regex.js index d2dd4daf..e958bcdb 100644 --- a/rules/prefer-t-regex.js +++ b/rules/prefer-t-regex.js @@ -34,15 +34,19 @@ const create = context => { }; // Recursively find the "origin" node of the given node. - // Note: Due to some limitation, this function will only find the reference up to two layer. + // Note: + // context.getScope() doesn't contain information about + // the outer scope so in most case this function will only + // find the reference directly above the current scope. // - // So the following code will only find the reference in this order: c → b → a + // So the following code will only find the reference in this order: y -> x // and it will have no knowledge of the number '0'. - // (assuming we run this function on the identifier c) + // (assuming we run this function on the identifier y) // ``` - // let a = 0; - // let b = a; - // let c = b; + // const test = require('ava'); + // let x = 0; + // let y = x; + // test(t => t.is(y, 0)); // ``` const findRootReference = node => { if (node.type === 'Identifier') { @@ -63,14 +67,13 @@ const create = context => { return findRootReference(node.object); } - // I'm aware that there are other type of Node as well but the scope of the lint shouldn't need those. return node; }; // Determine if the given node is a regex expression. - // There are two ways to create regex expressions in JavaScript, the first being Regex Literal and the second being RegExp class. - // - The first way can be easily lookup using .regex field in the node. - // - The second way can't really be lookup so I just check the name of a class constructor/function and check if it's matches. + // There are two ways to create regex expressions in JavaScript: Regex Literal and RegExp class. + // 1. Regex Literal can be easily lookup using .regex field in the node. + // 2. RegExp class can't be lookup so the function just checks for the name 'RegExp'. const isRegExp = lookup => { if (lookup.regex) { return true; @@ -87,7 +90,6 @@ const create = context => { const booleanHandler = node => { const firstArg = node.arguments[0]; - // First argument is a call expression const isFunctionCall = firstArg.type === 'CallExpression'; if (!isFunctionCall || !firstArg.callee.property) { return; @@ -98,11 +100,11 @@ const create = context => { let variable = {}; if (name === 'test') { - // `lookup.test(variable)` + // Represent: `lookup.test(variable)` lookup = firstArg.callee.object; variable = firstArg.arguments[0]; } else if (['search', 'match'].includes(name)) { - // `variable.match(lookup)` + // Represent: `variable.match(lookup)` lookup = firstArg.arguments[0]; variable = firstArg.callee.object; } From 96916051c302bce64fe16536e6739d7a6975dd4c Mon Sep 17 00:00:00 2001 From: oOBoomberOo Date: Sun, 17 Jan 2021 14:11:02 +0900 Subject: [PATCH 5/7] Remove unnecessary null check. findRootReference() already return non-null reference. --- rules/prefer-t-regex.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/rules/prefer-t-regex.js b/rules/prefer-t-regex.js index e958bcdb..bf0a3383 100644 --- a/rules/prefer-t-regex.js +++ b/rules/prefer-t-regex.js @@ -81,10 +81,7 @@ const create = context => { // Look up references in case it's a variable or RegExp declaration. const reference = findRootReference(lookup); - - if (reference) { - return reference.regex || reference.name === 'RegExp'; - } + return reference.regex || reference.name === 'RegExp'; }; const booleanHandler = node => { From 33d2e28bef3af2fd1bd1c5b1b75b7667146cdfe1 Mon Sep 17 00:00:00 2001 From: oOBoomberOo Date: Sun, 17 Jan 2021 14:12:04 +0900 Subject: [PATCH 6/7] Remove null return as per sindresorhus's preferences --- rules/prefer-t-regex.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/prefer-t-regex.js b/rules/prefer-t-regex.js index bf0a3383..351cc3f8 100644 --- a/rules/prefer-t-regex.js +++ b/rules/prefer-t-regex.js @@ -26,7 +26,7 @@ const create = context => { const definitions = reference.resolved.defs; if (definitions.length === 0) { - return null; + return; } return definitions[definitions.length - 1].node; From 967600fed03d559abed96424174e16cd963bfe26 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Sun, 17 Jan 2021 14:59:53 +0700 Subject: [PATCH 7/7] Update prefer-t-regex.js --- rules/prefer-t-regex.js | 46 ++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/rules/prefer-t-regex.js b/rules/prefer-t-regex.js index 351cc3f8..0e25d366 100644 --- a/rules/prefer-t-regex.js +++ b/rules/prefer-t-regex.js @@ -33,21 +33,22 @@ const create = context => { } }; - // Recursively find the "origin" node of the given node. - // Note: - // context.getScope() doesn't contain information about - // the outer scope so in most case this function will only - // find the reference directly above the current scope. - // - // So the following code will only find the reference in this order: y -> x - // and it will have no knowledge of the number '0'. - // (assuming we run this function on the identifier y) - // ``` - // const test = require('ava'); - // let x = 0; - // let y = x; - // test(t => t.is(y, 0)); - // ``` + /* + Recursively find the "origin" node of the given node. + + Note: `context.getScope()` doesn't contain information about the outer scope so in most cases this function will only find the reference directly above the current scope. So the following code will only find the reference in this order: y -> x, and it will have no knowledge of the number `0`. (assuming we run this function on the identifier `y`) + + ``` + const test = require('ava'); + + let x = 0; + let y = x; + + test(t => { + t.is(y, 0); + }); + ``` + */ const findRootReference = node => { if (node.type === 'Identifier') { const reference = findReference(node.name); @@ -70,10 +71,13 @@ const create = context => { return node; }; - // Determine if the given node is a regex expression. - // There are two ways to create regex expressions in JavaScript: Regex Literal and RegExp class. - // 1. Regex Literal can be easily lookup using .regex field in the node. - // 2. RegExp class can't be lookup so the function just checks for the name 'RegExp'. + /* + Determine if the given node is a regex expression. + + There are two ways to create regex expressions in JavaScript: Regex literal and `RegExp` class. + 1. Regex literal can be easily looked up using the `.regex` property on the node. + 2. `RegExp` class can't be looked up so the function just checks for the name `RegExp`. + */ const isRegExp = lookup => { if (lookup.regex) { return true; @@ -133,7 +137,7 @@ const create = context => { const firstArgumentIsRegex = isRegExp(firstArg); const secondArgumentIsRegex = isRegExp(secondArg); - // If both are regex, or neither are, the expression is ok + // If both are regex, or neither are, the expression is ok. if (firstArgumentIsRegex === secondArgumentIsRegex) { return; } @@ -150,7 +154,7 @@ const create = context => { ]; }; - // Only fix a statically verifiable equality + // Only fix a statically verifiable equality. if (regex && matchee.type === 'Literal') { let assertion;