From 11ab0be908d21843b4dfe5874b1e8776d23fd3f2 Mon Sep 17 00:00:00 2001 From: Milos Djermanovic Date: Mon, 9 Sep 2019 01:55:56 +0200 Subject: [PATCH] Fix: prefer-named-capture-group incorrect locations (fixes #12233) --- lib/rules/prefer-named-capture-group.js | 36 +++-- tests/lib/rules/prefer-named-capture-group.js | 125 ++++++++++++++++++ 2 files changed, 152 insertions(+), 9 deletions(-) diff --git a/lib/rules/prefer-named-capture-group.js b/lib/rules/prefer-named-capture-group.js index 07e69f022c7..dcab0c66867 100644 --- a/lib/rules/prefer-named-capture-group.js +++ b/lib/rules/prefer-named-capture-group.js @@ -50,16 +50,16 @@ module.exports = { /** * Function to check regular expression. * - * @param {string} regex The regular expression to be check. + * @param {string} pattern The regular expression pattern to be check. * @param {ASTNode} node AST node which contains regular expression. * @param {boolean} uFlag Flag indicates whether unicode mode is enabled or not. * @returns {void} */ - function checkRegex(regex, node, uFlag) { + function checkRegex(pattern, node, uFlag) { let ast; try { - ast = parser.parsePattern(regex, 0, regex.length, uFlag); + ast = parser.parsePattern(pattern, 0, pattern.length, uFlag); } catch (_) { // ignore regex syntax errors @@ -69,12 +69,22 @@ module.exports = { regexpp.visitRegExpAST(ast, { onCapturingGroupEnter(group) { if (!group.name) { - const locNode = node.type === "Literal" ? node : node.arguments[0]; + let locNode, rawPattern, loc; + + if (node.type === "Literal") { + locNode = node; + rawPattern = locNode.raw.slice(1, locNode.raw.lastIndexOf("/")); + } else { + locNode = node.arguments[0]; + if (locNode.type === "Literal" && typeof locNode.value === "string") { + rawPattern = locNode.raw.slice(1, -1); + } else if (locNode.type === "TemplateLiteral" && locNode.expressions.length === 0 && locNode.quasis.length === 1) { + rawPattern = locNode.quasis[0].value.raw; + } + } - context.report({ - node, - messageId: "required", - loc: { + if (pattern === rawPattern && locNode.loc.start.line === locNode.loc.end.line) { + loc = { start: { line: locNode.loc.start.line, column: locNode.loc.start.column + group.start + 1 @@ -83,7 +93,15 @@ module.exports = { line: locNode.loc.start.line, column: locNode.loc.start.column + group.end + 1 } - }, + }; + } else { + loc = locNode.loc; + } + + context.report({ + node, + messageId: "required", + loc, data: { group: group.raw } diff --git a/tests/lib/rules/prefer-named-capture-group.js b/tests/lib/rules/prefer-named-capture-group.js index e3a86e9ee30..7947ceef2b2 100644 --- a/tests/lib/rules/prefer-named-capture-group.js +++ b/tests/lib/rules/prefer-named-capture-group.js @@ -70,6 +70,17 @@ ruleTester.run("prefer-named-capture-group", rule, { endColumn: 19 }] }, + { + code: "new RegExp(`a(bc)d`)", + errors: [{ + messageId: "required", + type: "NewExpression", + data: { group: "(bc)" }, + line: 1, + column: 14, + endColumn: 18 + }] + }, { code: "/([0-9]{4})-(\\w{5})/", errors: [ @@ -90,6 +101,120 @@ ruleTester.run("prefer-named-capture-group", rule, { endColumn: 20 } ] + }, + + // For computed, multiline and strings with escape sequences, report the whole arguments[0] location. + { + code: "new RegExp('(' + 'a)')", + errors: [{ + messageId: "required", + type: "NewExpression", + data: { group: "(a)" }, + line: 1, + column: 12, + endColumn: 22 + }] + }, + { + code: "new RegExp('a(bc)d' + 'e')", + errors: [{ + messageId: "required", + type: "NewExpression", + data: { group: "(bc)" }, + line: 1, + column: 12, + endColumn: 26 + }] + }, + { + code: "RegExp('(a)'+'')", + errors: [{ + messageId: "required", + type: "CallExpression", + data: { group: "(a)" }, + line: 1, + column: 8, + endColumn: 16 + }] + }, + { + code: "RegExp( '' + '(ab)')", + errors: [{ + messageId: "required", + type: "CallExpression", + data: { group: "(ab)" }, + line: 1, + column: 9, + endColumn: 20 + }] + }, + { + code: "new RegExp(`(ab)${''}`)", + errors: [{ + messageId: "required", + type: "NewExpression", + data: { group: "(ab)" }, + line: 1, + column: 12, + endColumn: 23 + }] + }, + { + code: "new RegExp(`(a)\n`)", + errors: [{ + messageId: "required", + type: "NewExpression", + data: { group: "(a)" }, + line: 1, + column: 12, + endLine: 2, + endColumn: 2 + }] + }, + { + code: "RegExp(`a(b\nc)d`)", + errors: [{ + messageId: "required", + type: "CallExpression", + data: { group: "(b\nc)" }, + line: 1, + column: 8, + endLine: 2, + endColumn: 5 + }] + }, + { + code: "new RegExp('a(b)\\'')", + errors: [{ + messageId: "required", + type: "NewExpression", + data: { group: "(b)" }, + line: 1, + column: 12, + endColumn: 20 + }] + }, + { + code: "RegExp('(a)\\\\d')", + errors: [{ + messageId: "required", + type: "CallExpression", + data: { group: "(a)" }, + line: 1, + column: 8, + endColumn: 16 + }] + }, + { + code: "RegExp(`\\a(b)`)", + errors: [{ + messageId: "required", + type: "CallExpression", + data: { group: "(b)" }, + line: 1, + column: 8, + endColumn: 15 + }] } ] });