diff --git a/lib/rules/no-invalid-regexp.js b/lib/rules/no-invalid-regexp.js index 9a35743d122..3c42a68e8a3 100644 --- a/lib/rules/no-invalid-regexp.js +++ b/lib/rules/no-invalid-regexp.js @@ -10,7 +10,7 @@ const RegExpValidator = require("@eslint-community/regexpp").RegExpValidator; const validator = new RegExpValidator(); -const validFlags = /[dgimsuy]/gu; +const validFlags = /[dgimsuvy]/gu; const undefined1 = void 0; //------------------------------------------------------------------------------ @@ -108,12 +108,14 @@ module.exports = { /** * Check syntax error in a given pattern. * @param {string} pattern The RegExp pattern to validate. - * @param {boolean} uFlag The Unicode flag. + * @param {Object} flags The RegExp flags to validate. + * @param {boolean} [flags.unicode] The Unicode flag. + * @param {boolean} [flags.unicodeSets] The UnicodeSets flag. * @returns {string|null} The syntax error. */ - function validateRegExpPattern(pattern, uFlag) { + function validateRegExpPattern(pattern, flags) { try { - validator.validatePattern(pattern, undefined1, undefined1, uFlag); + validator.validatePattern(pattern, undefined1, undefined1, flags); return null; } catch (err) { return err.message; @@ -131,10 +133,19 @@ module.exports = { } try { validator.validateFlags(flags); - return null; } catch { return `Invalid flags supplied to RegExp constructor '${flags}'`; } + + /* + * `regexpp` checks the combination of `u` and `v` flags when parsing `Pattern` according to `ecma262`, + * but this rule may check only the flag when the pattern is unidentifiable, so check it here. + * https://tc39.es/ecma262/multipage/text-processing.html#sec-parsepattern + */ + if (flags.includes("u") && flags.includes("v")) { + return "Regex 'u' and 'v' flags cannot be used together"; + } + return null; } return { @@ -166,8 +177,12 @@ module.exports = { // 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")) + ? ( + validateRegExpPattern(pattern, { unicode: true, unicodeSets: false }) && + validateRegExpPattern(pattern, { unicode: false, unicodeSets: true }) && + validateRegExpPattern(pattern, { unicode: false, unicodeSets: false }) + ) + : validateRegExpPattern(pattern, { unicode: flags.includes("u"), unicodeSets: flags.includes("v") }) ); if (message) { diff --git a/package.json b/package.json index d17f2b20169..3833ed0a2b8 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "bugs": "https://github.com/eslint/eslint/issues/", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", + "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.0", "@eslint/js": "8.44.0", "@humanwhocodes/config-array": "^0.11.10", diff --git a/tests/lib/rules/no-invalid-regexp.js b/tests/lib/rules/no-invalid-regexp.js index ceaa8f13a44..dfd662bad27 100644 --- a/tests/lib/rules/no-invalid-regexp.js +++ b/tests/lib/rules/no-invalid-regexp.js @@ -81,6 +81,14 @@ ruleTester.run("no-invalid-regexp", rule, { "new RegExp('\\\\p{Script=Vith}', 'u')", "new RegExp('\\\\p{Script=Vithkuqi}', 'u')", + // ES2024 + "new RegExp('[A--B]', 'v')", + "new RegExp('[A&&B]', 'v')", + "new RegExp('[A--[0-9]]', 'v')", + "new RegExp('[\\\\p{Basic_Emoji}--\\\\q{a|bc|def}]', 'v')", + "new RegExp('[A--B]', flags)", // valid only with `v` flag + "new RegExp('[[]\\\\u{0}*', flags)", // valid only with `u` flag + // allowConstructorFlags { code: "new RegExp('.', 'g')", @@ -288,6 +296,48 @@ ruleTester.run("no-invalid-regexp", rule, { data: { message: "Invalid flags supplied to RegExp constructor 'z'" }, type: "NewExpression" }] + }, + + // ES2024 + { + code: "new RegExp('[[]', 'v');", + errors: [{ + messageId: "regexMessage", + data: { message: "Invalid regular expression: /[[]/v: Unterminated character class" }, + type: "NewExpression" + }] + }, + { + code: "new RegExp('.', 'uv');", + errors: [{ + messageId: "regexMessage", + data: { message: "Regex 'u' and 'v' flags cannot be used together" }, + type: "NewExpression" + }] + }, + { + code: "new RegExp(pattern, 'uv');", + errors: [{ + messageId: "regexMessage", + data: { message: "Regex 'u' and 'v' flags cannot be used together" }, + type: "NewExpression" + }] + }, + { + code: "new RegExp('[A--B]' /* valid only with `v` flag */, 'u')", + errors: [{ + messageId: "regexMessage", + data: { message: "Invalid regular expression: /[A--B]/u: Range out of order in character class" }, + type: "NewExpression" + }] + }, + { + code: "new RegExp('[[]\\\\u{0}*' /* valid only with `u` flag */, 'v')", + errors: [{ + messageId: "regexMessage", + data: { message: "Invalid regular expression: /[[]\\u{0}*/v: Unterminated character class" }, + type: "NewExpression" + }] } ] });