From 1c309ebca4a81a0faf397103dbc621019dea8c9c Mon Sep 17 00:00:00 2001 From: Milos Djermanovic Date: Tue, 26 Jan 2021 02:57:57 +0100 Subject: [PATCH] Update: fix no-invalid-regexp false negatives with no flags specified (#14018) --- lib/rules/no-invalid-regexp.js | 43 +++++++++++++++++---- tests/lib/rules/no-invalid-regexp.js | 56 ++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 8 deletions(-) diff --git a/lib/rules/no-invalid-regexp.js b/lib/rules/no-invalid-regexp.js index 6136ebb9e0b..94ad5ba6d5c 100644 --- a/lib/rules/no-invalid-regexp.js +++ b/lib/rules/no-invalid-regexp.js @@ -69,6 +69,28 @@ module.exports = { return node && node.type === "Literal" && typeof node.value === "string"; } + /** + * Gets flags of a regular expression created by the given `RegExp()` or `new RegExp()` call + * Examples: + * new RegExp(".") // => "" + * new RegExp(".", "gu") // => "gu" + * new RegExp(".", flags) // => null + * @param {ASTNode} node `CallExpression` or `NewExpression` node + * @returns {string|null} flags if they can be determined, `null` otherwise + * @private + */ + function getFlags(node) { + if (node.arguments.length < 2) { + return ""; + } + + if (isString(node.arguments[1])) { + return node.arguments[1].value; + } + + return null; + } + /** * Check syntax error in a given pattern. * @param {string} pattern The RegExp pattern to validate. @@ -104,18 +126,23 @@ module.exports = { return; } const pattern = node.arguments[0].value; - let flags = isString(node.arguments[1]) ? node.arguments[1].value : ""; + let flags = getFlags(node); - if (allowedFlags) { + if (flags && allowedFlags) { flags = flags.replace(allowedFlags, ""); } - // If flags are unknown, check both are errored or not. - const message = validateRegExpFlags(flags) || ( - flags - ? validateRegExpPattern(pattern, flags.indexOf("u") !== -1) - : validateRegExpPattern(pattern, true) && validateRegExpPattern(pattern, false) - ); + const message = + ( + flags && validateRegExpFlags(flags) + ) || + ( + + // If flags are unknown, report the regex only if its pattern is invalid both with and without the "u" flag + flags === null + ? validateRegExpPattern(pattern, true) && validateRegExpPattern(pattern, false) + : validateRegExpPattern(pattern, flags.includes("u")) + ); if (message) { context.report({ diff --git a/tests/lib/rules/no-invalid-regexp.js b/tests/lib/rules/no-invalid-regexp.js index 485ce1e41fd..6484db816db 100644 --- a/tests/lib/rules/no-invalid-regexp.js +++ b/tests/lib/rules/no-invalid-regexp.js @@ -39,6 +39,20 @@ ruleTester.run("no-invalid-regexp", rule, { "new RegExp('(?b)\\k', 'u')", "new RegExp('\\\\p{Letter}', 'u')", + // unknown flags + "RegExp('{', flags)", // valid without the "u" flag + "new RegExp('{', flags)", // valid without the "u" flag + "RegExp('\\\\u{0}*', flags)", // valid with the "u" flag + "new RegExp('\\\\u{0}*', flags)", // valid with the "u" flag + { + code: "RegExp('{', flags)", // valid without the "u" flag + options: [{ allowConstructorFlags: ["u"] }] + }, + { + code: "RegExp('\\\\u{0}*', flags)", // valid with the "u" flag + options: [{ allowConstructorFlags: ["a"] }] + }, + // ES2020 "new RegExp('(?<\\\\ud835\\\\udc9c>.)', 'g')", "new RegExp('(?<\\\\u{1d49c}>.)', 'g')", @@ -165,6 +179,48 @@ ruleTester.run("no-invalid-regexp", rule, { type: "NewExpression" }] }, + { + code: String.raw`RegExp('\\u{0}*');`, + errors: [{ + messageId: "regexMessage", + data: { message: "Invalid regular expression: /\\u{0}*/: Nothing to repeat" }, + type: "CallExpression" + }] + }, + { + code: String.raw`new RegExp('\\u{0}*');`, + errors: [{ + messageId: "regexMessage", + data: { message: "Invalid regular expression: /\\u{0}*/: Nothing to repeat" }, + type: "NewExpression" + }] + }, + { + code: String.raw`new RegExp('\\u{0}*', '');`, + errors: [{ + messageId: "regexMessage", + data: { message: "Invalid regular expression: /\\u{0}*/: Nothing to repeat" }, + type: "NewExpression" + }] + }, + { + code: String.raw`new RegExp('\\u{0}*', 'a');`, + options: [{ allowConstructorFlags: ["a"] }], + errors: [{ + messageId: "regexMessage", + data: { message: "Invalid regular expression: /\\u{0}*/: Nothing to repeat" }, + type: "NewExpression" + }] + }, + { + code: String.raw`RegExp('\\u{0}*');`, + options: [{ allowConstructorFlags: ["a"] }], + errors: [{ + messageId: "regexMessage", + data: { message: "Invalid regular expression: /\\u{0}*/: Nothing to repeat" }, + type: "CallExpression" + }] + }, // https://github.com/eslint/eslint/issues/10861 {