From 9ff21fbebf81f480428e674593f0d7bf95fd1e53 Mon Sep 17 00:00:00 2001 From: Mathias Rasmussen Date: Tue, 9 Nov 2021 08:57:08 +0100 Subject: [PATCH 1/7] refactor: Simplify kinds tracking with a Set --- lib/rules/no-misleading-character-class.js | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index 70e31e604f4..68cbc9bb12f 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -129,14 +129,6 @@ module.exports = { * @returns {void} */ function verify(node, pattern, flags) { - const has = { - surrogatePairWithoutUFlag: false, - combiningClass: false, - variationSelector: false, - emojiModifier: false, - regionalIndicatorSymbol: false, - zwj: false - }; let patternNode; try { @@ -152,20 +144,22 @@ module.exports = { return; } + const foundKinds = new Set(); + visitRegExpAST(patternNode, { onCharacterClassEnter(ccNode) { for (const chars of iterateCharacterSequence(ccNode.elements)) { for (const kind of kinds) { - has[kind] = has[kind] || hasCharacterSequence[kind](chars); + if (hasCharacterSequence[kind](chars)) { + foundKinds.add(kind); + } } } } }); - for (const kind of kinds) { - if (has[kind]) { - context.report({ node, messageId: kind }); - } + for (const kind of foundKinds) { + context.report({ node, messageId: kind }); } } From 81f6c2129b3fe3c8ae1ed1d3bd467e4ce38a1869 Mon Sep 17 00:00:00 2001 From: Mathias Rasmussen Date: Tue, 9 Nov 2021 09:25:51 +0100 Subject: [PATCH 2/7] feat: Add fixer for missing regex unicode flag --- lib/rules/no-misleading-character-class.js | 27 ++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index 68cbc9bb12f..8fcc00198c9 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -110,6 +110,8 @@ module.exports = { schema: [], + fixable: "whitespace", + messages: { surrogatePairWithoutUFlag: "Unexpected surrogate pair in character class. Use 'u' flag.", combiningClass: "Unexpected combined character in character class.", @@ -126,9 +128,10 @@ module.exports = { * @param {Node} node The node to report. * @param {string} pattern The regular expression pattern to verify. * @param {string} flags The flags of the regular expression. + * @param {Function} unicodeFixer Fixer for missing "u" flag. * @returns {void} */ - function verify(node, pattern, flags) { + function verify(node, pattern, flags, unicodeFixer) { let patternNode; try { @@ -159,13 +162,17 @@ module.exports = { }); for (const kind of foundKinds) { - context.report({ node, messageId: kind }); + context.report({ + node, + messageId: kind, + fix: kind === "surrogatePairWithoutUFlag" && unicodeFixer + }); } } return { "Literal[regex]"(node) { - verify(node, node.regex.pattern, node.regex.flags); + verify(node, node.regex.pattern, node.regex.flags, fixer => fixer.insertTextAfter(node, "u")); }, "Program"() { const scope = context.getScope(); @@ -184,7 +191,19 @@ module.exports = { const flags = getStringIfConstant(flagsNode, scope); if (typeof pattern === "string") { - verify(node, pattern, flags || ""); + verify(node, pattern, flags || "", fixer => { + if (node.arguments.length === 1) { + return fixer.insertTextAfterRange(patternNode.range, ', "u"'); + } + + if (flagsNode.type === "Literal" || flagsNode.type === "TemplateLiteral") { + const range = [flagsNode.range[0], flagsNode.range[1] - 1]; + + return fixer.insertTextAfterRange(range, "u"); + } + + return null; + }); } } } From 36396bad0b13c864e4ba1f4db212f03ca6622856 Mon Sep 17 00:00:00 2001 From: Mathias Rasmussen Date: Tue, 9 Nov 2021 09:42:33 +0100 Subject: [PATCH 3/7] test: Update tests --- .../rules/no-misleading-character-class.js | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/tests/lib/rules/no-misleading-character-class.js b/tests/lib/rules/no-misleading-character-class.js index a02e5e13e32..eeacc3ed994 100644 --- a/tests/lib/rules/no-misleading-character-class.js +++ b/tests/lib/rules/no-misleading-character-class.js @@ -76,86 +76,112 @@ ruleTester.run("no-misleading-character-class", rule, { // RegExp Literals. { code: "var r = /[πŸ‘]/", + output: "var r = /[πŸ‘]/u", errors: [{ messageId: "surrogatePairWithoutUFlag" }] }, { code: "var r = /[\\uD83D\\uDC4D]/", + output: "var r = /[\\uD83D\\uDC4D]/u", errors: [{ messageId: "surrogatePairWithoutUFlag" }] }, { code: "var r = /[Á]/", + output: null, errors: [{ messageId: "combiningClass" }] }, { code: "var r = /[Á]/u", + output: null, errors: [{ messageId: "combiningClass" }] }, { code: "var r = /[\\u0041\\u0301]/", + output: null, errors: [{ messageId: "combiningClass" }] }, { code: "var r = /[\\u0041\\u0301]/u", + output: null, errors: [{ messageId: "combiningClass" }] }, { code: "var r = /[\\u{41}\\u{301}]/u", + output: null, errors: [{ messageId: "combiningClass" }] }, { code: "var r = /[❇️]/", + output: null, errors: [{ messageId: "combiningClass" }] }, { code: "var r = /[❇️]/u", + output: null, errors: [{ messageId: "combiningClass" }] }, { code: "var r = /[\\u2747\\uFE0F]/", + output: null, errors: [{ messageId: "combiningClass" }] }, { code: "var r = /[\\u2747\\uFE0F]/u", + output: null, errors: [{ messageId: "combiningClass" }] }, { code: "var r = /[\\u{2747}\\u{FE0F}]/u", + output: null, errors: [{ messageId: "combiningClass" }] }, { code: "var r = /[πŸ‘ΆπŸ»]/", + output: "var r = /[πŸ‘ΆπŸ»]/u", errors: [{ messageId: "surrogatePairWithoutUFlag" }] }, { code: "var r = /[πŸ‘ΆπŸ»]/u", + output: null, errors: [{ messageId: "emojiModifier" }] }, { code: "var r = /[\\uD83D\\uDC76\\uD83C\\uDFFB]/u", + output: null, errors: [{ messageId: "emojiModifier" }] }, { code: "var r = /[\\u{1F476}\\u{1F3FB}]/u", + output: null, errors: [{ messageId: "emojiModifier" }] }, { code: "var r = /[πŸ‡―πŸ‡΅]/", + output: "var r = /[πŸ‡―πŸ‡΅]/u", + errors: [{ messageId: "surrogatePairWithoutUFlag" }] + }, + { + code: "var r = /[πŸ‡―πŸ‡΅]/i", + output: "var r = /[πŸ‡―πŸ‡΅]/iu", errors: [{ messageId: "surrogatePairWithoutUFlag" }] }, { code: "var r = /[πŸ‡―πŸ‡΅]/u", + output: null, errors: [{ messageId: "regionalIndicatorSymbol" }] }, { code: "var r = /[\\uD83C\\uDDEF\\uD83C\\uDDF5]/u", + output: null, errors: [{ messageId: "regionalIndicatorSymbol" }] }, { code: "var r = /[\\u{1F1EF}\\u{1F1F5}]/u", + output: null, errors: [{ messageId: "regionalIndicatorSymbol" }] }, { code: "var r = /[πŸ‘¨β€πŸ‘©β€πŸ‘¦]/", + output: "var r = /[πŸ‘¨β€πŸ‘©β€πŸ‘¦]/u", errors: [ { messageId: "surrogatePairWithoutUFlag" }, { messageId: "zwj" } @@ -163,100 +189,134 @@ ruleTester.run("no-misleading-character-class", rule, { }, { code: "var r = /[πŸ‘¨β€πŸ‘©β€πŸ‘¦]/u", + output: null, errors: [{ messageId: "zwj" }] }, { code: "var r = /[\\uD83D\\uDC68\\u200D\\uD83D\\uDC69\\u200D\\uD83D\\uDC66]/u", + output: null, errors: [{ messageId: "zwj" }] }, { code: "var r = /[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]/u", + output: null, errors: [{ messageId: "zwj" }] }, // RegExp constructors. { code: String.raw`var r = new RegExp("[πŸ‘]", "")`, + output: String.raw`var r = new RegExp("[πŸ‘]", "u")`, errors: [{ messageId: "surrogatePairWithoutUFlag" }] }, { code: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "")`, + output: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "u")`, errors: [{ messageId: "surrogatePairWithoutUFlag" }] }, { code: String.raw`var r = new RegExp("[Á]", "")`, + output: null, errors: [{ messageId: "combiningClass" }] }, { code: String.raw`var r = new RegExp("[Á]", "u")`, + output: null, errors: [{ messageId: "combiningClass" }] }, { code: String.raw`var r = new RegExp("[\\u0041\\u0301]", "")`, + output: null, errors: [{ messageId: "combiningClass" }] }, { code: String.raw`var r = new RegExp("[\\u0041\\u0301]", "u")`, + output: null, errors: [{ messageId: "combiningClass" }] }, { code: String.raw`var r = new RegExp("[\\u{41}\\u{301}]", "u")`, + output: null, errors: [{ messageId: "combiningClass" }] }, { code: String.raw`var r = new RegExp("[❇️]", "")`, + output: null, errors: [{ messageId: "combiningClass" }] }, { code: String.raw`var r = new RegExp("[❇️]", "u")`, + output: null, errors: [{ messageId: "combiningClass" }] }, { code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "")`, + output: null, errors: [{ messageId: "combiningClass" }] }, { code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "u")`, + output: null, errors: [{ messageId: "combiningClass" }] }, { code: String.raw`var r = new RegExp("[\\u{2747}\\u{FE0F}]", "u")`, + output: null, errors: [{ messageId: "combiningClass" }] }, { code: String.raw`var r = new RegExp("[πŸ‘ΆπŸ»]", "")`, + output: String.raw`var r = new RegExp("[πŸ‘ΆπŸ»]", "u")`, errors: [{ messageId: "surrogatePairWithoutUFlag" }] }, { code: String.raw`var r = new RegExp("[πŸ‘ΆπŸ»]", "u")`, + output: null, errors: [{ messageId: "emojiModifier" }] }, { code: String.raw`var r = new RegExp("[\\uD83D\\uDC76\\uD83C\\uDFFB]", "u")`, + output: null, errors: [{ messageId: "emojiModifier" }] }, { code: String.raw`var r = new RegExp("[\\u{1F476}\\u{1F3FB}]", "u")`, + output: null, errors: [{ messageId: "emojiModifier" }] }, { code: String.raw`var r = new RegExp("[πŸ‡―πŸ‡΅]", "")`, + output: String.raw`var r = new RegExp("[πŸ‡―πŸ‡΅]", "u")`, + errors: [{ messageId: "surrogatePairWithoutUFlag" }] + }, + { + code: String.raw`var r = new RegExp("[πŸ‡―πŸ‡΅]", "i")`, + output: String.raw`var r = new RegExp("[πŸ‡―πŸ‡΅]", "iu")`, + errors: [{ messageId: "surrogatePairWithoutUFlag" }] + }, + { + code: String.raw`var r = new RegExp("[πŸ‡―πŸ‡΅]")`, + output: String.raw`var r = new RegExp("[πŸ‡―πŸ‡΅]", "u")`, errors: [{ messageId: "surrogatePairWithoutUFlag" }] }, { code: String.raw`var r = new RegExp("[πŸ‡―πŸ‡΅]", "u")`, + output: null, errors: [{ messageId: "regionalIndicatorSymbol" }] }, { code: String.raw`var r = new RegExp("[\\uD83C\\uDDEF\\uD83C\\uDDF5]", "u")`, + output: null, errors: [{ messageId: "regionalIndicatorSymbol" }] }, { code: String.raw`var r = new RegExp("[\\u{1F1EF}\\u{1F1F5}]", "u")`, + output: null, errors: [{ messageId: "regionalIndicatorSymbol" }] }, { code: String.raw`var r = new RegExp("[πŸ‘¨β€πŸ‘©β€πŸ‘¦]", "")`, + output: String.raw`var r = new RegExp("[πŸ‘¨β€πŸ‘©β€πŸ‘¦]", "u")`, errors: [ { messageId: "surrogatePairWithoutUFlag" }, { messageId: "zwj" } @@ -264,33 +324,40 @@ ruleTester.run("no-misleading-character-class", rule, { }, { code: String.raw`var r = new RegExp("[πŸ‘¨β€πŸ‘©β€πŸ‘¦]", "u")`, + output: null, errors: [{ messageId: "zwj" }] }, { code: String.raw`var r = new RegExp("[\\uD83D\\uDC68\\u200D\\uD83D\\uDC69\\u200D\\uD83D\\uDC66]", "u")`, + output: null, errors: [{ messageId: "zwj" }] }, { code: String.raw`var r = new RegExp("[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]", "u")`, + output: null, errors: [{ messageId: "zwj" }] }, { code: String.raw`var r = new globalThis.RegExp("[❇️]", "")`, + output: null, env: { es2020: true }, errors: [{ messageId: "combiningClass" }] }, { code: String.raw`var r = new globalThis.RegExp("[πŸ‘ΆπŸ»]", "u")`, + output: null, env: { es2020: true }, errors: [{ messageId: "emojiModifier" }] }, { code: String.raw`var r = new globalThis.RegExp("[πŸ‡―πŸ‡΅]", "")`, + output: String.raw`var r = new globalThis.RegExp("[πŸ‡―πŸ‡΅]", "u")`, env: { es2020: true }, errors: [{ messageId: "surrogatePairWithoutUFlag" }] }, { code: String.raw`var r = new globalThis.RegExp("[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]", "u")`, + output: null, env: { es2020: true }, errors: [{ messageId: "zwj" }] } From 2f8f0399d691ba3c7facb1209cb6de8cf9eb3a43 Mon Sep 17 00:00:00 2001 From: Mathias Rasmussen Date: Thu, 25 Nov 2021 02:31:51 +0100 Subject: [PATCH 4/7] refactor: Change fix to suggestion --- lib/rules/no-misleading-character-class.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index 8fcc00198c9..feafeed755c 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -108,6 +108,8 @@ module.exports = { url: "https://eslint.org/docs/rules/no-misleading-character-class" }, + hasSuggestions: true, + schema: [], fixable: "whitespace", @@ -117,7 +119,8 @@ module.exports = { combiningClass: "Unexpected combined character in character class.", emojiModifier: "Unexpected modified Emoji in character class.", regionalIndicatorSymbol: "Unexpected national flag in character class.", - zwj: "Unexpected joined character sequence in character class." + zwj: "Unexpected joined character sequence in character class.", + suggestUnicodeFlag: "Add unicode 'u' flag to regex." } }, create(context) { @@ -162,10 +165,19 @@ module.exports = { }); for (const kind of foundKinds) { + let suggest; + + if (kind === "surrogatePairWithoutUFlag") { + suggest = [{ + messageId: "suggestUnicodeFlag", + fix: unicodeFixer + }]; + } + context.report({ node, messageId: kind, - fix: kind === "surrogatePairWithoutUFlag" && unicodeFixer + suggest }); } } From de31e68bca9f6460f44b846fe1b75ba5755ef7f0 Mon Sep 17 00:00:00 2001 From: Mathias Rasmussen Date: Thu, 25 Nov 2021 02:38:28 +0100 Subject: [PATCH 5/7] Revert "test: Update tests" This reverts commit 36396bad0b13c864e4ba1f4db212f03ca6622856. --- .../rules/no-misleading-character-class.js | 67 ------------------- 1 file changed, 67 deletions(-) diff --git a/tests/lib/rules/no-misleading-character-class.js b/tests/lib/rules/no-misleading-character-class.js index eeacc3ed994..a02e5e13e32 100644 --- a/tests/lib/rules/no-misleading-character-class.js +++ b/tests/lib/rules/no-misleading-character-class.js @@ -76,112 +76,86 @@ ruleTester.run("no-misleading-character-class", rule, { // RegExp Literals. { code: "var r = /[πŸ‘]/", - output: "var r = /[πŸ‘]/u", errors: [{ messageId: "surrogatePairWithoutUFlag" }] }, { code: "var r = /[\\uD83D\\uDC4D]/", - output: "var r = /[\\uD83D\\uDC4D]/u", errors: [{ messageId: "surrogatePairWithoutUFlag" }] }, { code: "var r = /[Á]/", - output: null, errors: [{ messageId: "combiningClass" }] }, { code: "var r = /[Á]/u", - output: null, errors: [{ messageId: "combiningClass" }] }, { code: "var r = /[\\u0041\\u0301]/", - output: null, errors: [{ messageId: "combiningClass" }] }, { code: "var r = /[\\u0041\\u0301]/u", - output: null, errors: [{ messageId: "combiningClass" }] }, { code: "var r = /[\\u{41}\\u{301}]/u", - output: null, errors: [{ messageId: "combiningClass" }] }, { code: "var r = /[❇️]/", - output: null, errors: [{ messageId: "combiningClass" }] }, { code: "var r = /[❇️]/u", - output: null, errors: [{ messageId: "combiningClass" }] }, { code: "var r = /[\\u2747\\uFE0F]/", - output: null, errors: [{ messageId: "combiningClass" }] }, { code: "var r = /[\\u2747\\uFE0F]/u", - output: null, errors: [{ messageId: "combiningClass" }] }, { code: "var r = /[\\u{2747}\\u{FE0F}]/u", - output: null, errors: [{ messageId: "combiningClass" }] }, { code: "var r = /[πŸ‘ΆπŸ»]/", - output: "var r = /[πŸ‘ΆπŸ»]/u", errors: [{ messageId: "surrogatePairWithoutUFlag" }] }, { code: "var r = /[πŸ‘ΆπŸ»]/u", - output: null, errors: [{ messageId: "emojiModifier" }] }, { code: "var r = /[\\uD83D\\uDC76\\uD83C\\uDFFB]/u", - output: null, errors: [{ messageId: "emojiModifier" }] }, { code: "var r = /[\\u{1F476}\\u{1F3FB}]/u", - output: null, errors: [{ messageId: "emojiModifier" }] }, { code: "var r = /[πŸ‡―πŸ‡΅]/", - output: "var r = /[πŸ‡―πŸ‡΅]/u", - errors: [{ messageId: "surrogatePairWithoutUFlag" }] - }, - { - code: "var r = /[πŸ‡―πŸ‡΅]/i", - output: "var r = /[πŸ‡―πŸ‡΅]/iu", errors: [{ messageId: "surrogatePairWithoutUFlag" }] }, { code: "var r = /[πŸ‡―πŸ‡΅]/u", - output: null, errors: [{ messageId: "regionalIndicatorSymbol" }] }, { code: "var r = /[\\uD83C\\uDDEF\\uD83C\\uDDF5]/u", - output: null, errors: [{ messageId: "regionalIndicatorSymbol" }] }, { code: "var r = /[\\u{1F1EF}\\u{1F1F5}]/u", - output: null, errors: [{ messageId: "regionalIndicatorSymbol" }] }, { code: "var r = /[πŸ‘¨β€πŸ‘©β€πŸ‘¦]/", - output: "var r = /[πŸ‘¨β€πŸ‘©β€πŸ‘¦]/u", errors: [ { messageId: "surrogatePairWithoutUFlag" }, { messageId: "zwj" } @@ -189,134 +163,100 @@ ruleTester.run("no-misleading-character-class", rule, { }, { code: "var r = /[πŸ‘¨β€πŸ‘©β€πŸ‘¦]/u", - output: null, errors: [{ messageId: "zwj" }] }, { code: "var r = /[\\uD83D\\uDC68\\u200D\\uD83D\\uDC69\\u200D\\uD83D\\uDC66]/u", - output: null, errors: [{ messageId: "zwj" }] }, { code: "var r = /[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]/u", - output: null, errors: [{ messageId: "zwj" }] }, // RegExp constructors. { code: String.raw`var r = new RegExp("[πŸ‘]", "")`, - output: String.raw`var r = new RegExp("[πŸ‘]", "u")`, errors: [{ messageId: "surrogatePairWithoutUFlag" }] }, { code: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "")`, - output: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "u")`, errors: [{ messageId: "surrogatePairWithoutUFlag" }] }, { code: String.raw`var r = new RegExp("[Á]", "")`, - output: null, errors: [{ messageId: "combiningClass" }] }, { code: String.raw`var r = new RegExp("[Á]", "u")`, - output: null, errors: [{ messageId: "combiningClass" }] }, { code: String.raw`var r = new RegExp("[\\u0041\\u0301]", "")`, - output: null, errors: [{ messageId: "combiningClass" }] }, { code: String.raw`var r = new RegExp("[\\u0041\\u0301]", "u")`, - output: null, errors: [{ messageId: "combiningClass" }] }, { code: String.raw`var r = new RegExp("[\\u{41}\\u{301}]", "u")`, - output: null, errors: [{ messageId: "combiningClass" }] }, { code: String.raw`var r = new RegExp("[❇️]", "")`, - output: null, errors: [{ messageId: "combiningClass" }] }, { code: String.raw`var r = new RegExp("[❇️]", "u")`, - output: null, errors: [{ messageId: "combiningClass" }] }, { code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "")`, - output: null, errors: [{ messageId: "combiningClass" }] }, { code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "u")`, - output: null, errors: [{ messageId: "combiningClass" }] }, { code: String.raw`var r = new RegExp("[\\u{2747}\\u{FE0F}]", "u")`, - output: null, errors: [{ messageId: "combiningClass" }] }, { code: String.raw`var r = new RegExp("[πŸ‘ΆπŸ»]", "")`, - output: String.raw`var r = new RegExp("[πŸ‘ΆπŸ»]", "u")`, errors: [{ messageId: "surrogatePairWithoutUFlag" }] }, { code: String.raw`var r = new RegExp("[πŸ‘ΆπŸ»]", "u")`, - output: null, errors: [{ messageId: "emojiModifier" }] }, { code: String.raw`var r = new RegExp("[\\uD83D\\uDC76\\uD83C\\uDFFB]", "u")`, - output: null, errors: [{ messageId: "emojiModifier" }] }, { code: String.raw`var r = new RegExp("[\\u{1F476}\\u{1F3FB}]", "u")`, - output: null, errors: [{ messageId: "emojiModifier" }] }, { code: String.raw`var r = new RegExp("[πŸ‡―πŸ‡΅]", "")`, - output: String.raw`var r = new RegExp("[πŸ‡―πŸ‡΅]", "u")`, - errors: [{ messageId: "surrogatePairWithoutUFlag" }] - }, - { - code: String.raw`var r = new RegExp("[πŸ‡―πŸ‡΅]", "i")`, - output: String.raw`var r = new RegExp("[πŸ‡―πŸ‡΅]", "iu")`, - errors: [{ messageId: "surrogatePairWithoutUFlag" }] - }, - { - code: String.raw`var r = new RegExp("[πŸ‡―πŸ‡΅]")`, - output: String.raw`var r = new RegExp("[πŸ‡―πŸ‡΅]", "u")`, errors: [{ messageId: "surrogatePairWithoutUFlag" }] }, { code: String.raw`var r = new RegExp("[πŸ‡―πŸ‡΅]", "u")`, - output: null, errors: [{ messageId: "regionalIndicatorSymbol" }] }, { code: String.raw`var r = new RegExp("[\\uD83C\\uDDEF\\uD83C\\uDDF5]", "u")`, - output: null, errors: [{ messageId: "regionalIndicatorSymbol" }] }, { code: String.raw`var r = new RegExp("[\\u{1F1EF}\\u{1F1F5}]", "u")`, - output: null, errors: [{ messageId: "regionalIndicatorSymbol" }] }, { code: String.raw`var r = new RegExp("[πŸ‘¨β€πŸ‘©β€πŸ‘¦]", "")`, - output: String.raw`var r = new RegExp("[πŸ‘¨β€πŸ‘©β€πŸ‘¦]", "u")`, errors: [ { messageId: "surrogatePairWithoutUFlag" }, { messageId: "zwj" } @@ -324,40 +264,33 @@ ruleTester.run("no-misleading-character-class", rule, { }, { code: String.raw`var r = new RegExp("[πŸ‘¨β€πŸ‘©β€πŸ‘¦]", "u")`, - output: null, errors: [{ messageId: "zwj" }] }, { code: String.raw`var r = new RegExp("[\\uD83D\\uDC68\\u200D\\uD83D\\uDC69\\u200D\\uD83D\\uDC66]", "u")`, - output: null, errors: [{ messageId: "zwj" }] }, { code: String.raw`var r = new RegExp("[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]", "u")`, - output: null, errors: [{ messageId: "zwj" }] }, { code: String.raw`var r = new globalThis.RegExp("[❇️]", "")`, - output: null, env: { es2020: true }, errors: [{ messageId: "combiningClass" }] }, { code: String.raw`var r = new globalThis.RegExp("[πŸ‘ΆπŸ»]", "u")`, - output: null, env: { es2020: true }, errors: [{ messageId: "emojiModifier" }] }, { code: String.raw`var r = new globalThis.RegExp("[πŸ‡―πŸ‡΅]", "")`, - output: String.raw`var r = new globalThis.RegExp("[πŸ‡―πŸ‡΅]", "u")`, env: { es2020: true }, errors: [{ messageId: "surrogatePairWithoutUFlag" }] }, { code: String.raw`var r = new globalThis.RegExp("[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]", "u")`, - output: null, env: { es2020: true }, errors: [{ messageId: "zwj" }] } From a69a73b60eb01aea980de6fe00e2988aa51f69a4 Mon Sep 17 00:00:00 2001 From: Mathias Rasmussen Date: Sun, 28 Nov 2021 22:51:33 +0100 Subject: [PATCH 6/7] Address review comments Add tests --- lib/rules/no-misleading-character-class.js | 2 - .../rules/no-misleading-character-class.js | 76 ++++++++++++++++--- 2 files changed, 65 insertions(+), 13 deletions(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index feafeed755c..1e74a4ab1d1 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -112,8 +112,6 @@ module.exports = { schema: [], - fixable: "whitespace", - messages: { surrogatePairWithoutUFlag: "Unexpected surrogate pair in character class. Use 'u' flag.", combiningClass: "Unexpected combined character in character class.", diff --git a/tests/lib/rules/no-misleading-character-class.js b/tests/lib/rules/no-misleading-character-class.js index a02e5e13e32..b7cf3738af5 100644 --- a/tests/lib/rules/no-misleading-character-class.js +++ b/tests/lib/rules/no-misleading-character-class.js @@ -76,11 +76,17 @@ ruleTester.run("no-misleading-character-class", rule, { // RegExp Literals. { code: "var r = /[πŸ‘]/", - errors: [{ messageId: "surrogatePairWithoutUFlag" }] + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[πŸ‘]/u" }] + }] }, { code: "var r = /[\\uD83D\\uDC4D]/", - errors: [{ messageId: "surrogatePairWithoutUFlag" }] + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[\\uD83D\\uDC4D]/u" }] + }] }, { code: "var r = /[Á]/", @@ -124,7 +130,10 @@ ruleTester.run("no-misleading-character-class", rule, { }, { code: "var r = /[πŸ‘ΆπŸ»]/", - errors: [{ messageId: "surrogatePairWithoutUFlag" }] + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[πŸ‘ΆπŸ»]/u" }] + }] }, { code: "var r = /[πŸ‘ΆπŸ»]/u", @@ -140,7 +149,17 @@ ruleTester.run("no-misleading-character-class", rule, { }, { code: "var r = /[πŸ‡―πŸ‡΅]/", - errors: [{ messageId: "surrogatePairWithoutUFlag" }] + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[πŸ‡―πŸ‡΅]/u" }] + }] + }, + { + code: "var r = /[πŸ‡―πŸ‡΅]/i", + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[πŸ‡―πŸ‡΅]/iu" }] + }] }, { code: "var r = /[πŸ‡―πŸ‡΅]/u", @@ -157,7 +176,10 @@ ruleTester.run("no-misleading-character-class", rule, { { code: "var r = /[πŸ‘¨β€πŸ‘©β€πŸ‘¦]/", errors: [ - { messageId: "surrogatePairWithoutUFlag" }, + { + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[πŸ‘¨β€πŸ‘©β€πŸ‘¦]/u" }] + }, { messageId: "zwj" } ] }, @@ -177,11 +199,17 @@ ruleTester.run("no-misleading-character-class", rule, { // RegExp constructors. { code: String.raw`var r = new RegExp("[πŸ‘]", "")`, - errors: [{ messageId: "surrogatePairWithoutUFlag" }] + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[πŸ‘]", "u")` }] + }] }, { code: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "")`, - errors: [{ messageId: "surrogatePairWithoutUFlag" }] + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "u")` }] + }] }, { code: String.raw`var r = new RegExp("[Á]", "")`, @@ -225,7 +253,10 @@ ruleTester.run("no-misleading-character-class", rule, { }, { code: String.raw`var r = new RegExp("[πŸ‘ΆπŸ»]", "")`, - errors: [{ messageId: "surrogatePairWithoutUFlag" }] + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[πŸ‘ΆπŸ»]", "u")` }] + }] }, { code: String.raw`var r = new RegExp("[πŸ‘ΆπŸ»]", "u")`, @@ -241,7 +272,24 @@ ruleTester.run("no-misleading-character-class", rule, { }, { code: String.raw`var r = new RegExp("[πŸ‡―πŸ‡΅]", "")`, - errors: [{ messageId: "surrogatePairWithoutUFlag" }] + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[πŸ‡―πŸ‡΅]", "u")` }] + }] + }, + { + code: String.raw`var r = new RegExp("[πŸ‡―πŸ‡΅]", "i")`, + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[πŸ‡―πŸ‡΅]", "iu")` }] + }] + }, + { + code: String.raw`var r = new RegExp("[πŸ‡―πŸ‡΅]")`, + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[πŸ‡―πŸ‡΅]", "u")` }] + }] }, { code: String.raw`var r = new RegExp("[πŸ‡―πŸ‡΅]", "u")`, @@ -258,7 +306,10 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new RegExp("[πŸ‘¨β€πŸ‘©β€πŸ‘¦]", "")`, errors: [ - { messageId: "surrogatePairWithoutUFlag" }, + { + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[πŸ‘¨β€πŸ‘©β€πŸ‘¦]", "u")` }] + }, { messageId: "zwj" } ] }, @@ -287,7 +338,10 @@ ruleTester.run("no-misleading-character-class", rule, { { code: String.raw`var r = new globalThis.RegExp("[πŸ‡―πŸ‡΅]", "")`, env: { es2020: true }, - errors: [{ messageId: "surrogatePairWithoutUFlag" }] + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new globalThis.RegExp("[πŸ‡―πŸ‡΅]", "u")` }] + }] }, { code: String.raw`var r = new globalThis.RegExp("[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]", "u")`, From 6a64d01cd2907ce1a341c373c8fa38923ac34124 Mon Sep 17 00:00:00 2001 From: Mathias Rasmussen Date: Fri, 4 Feb 2022 16:54:47 +0100 Subject: [PATCH 7/7] Update lib/rules/no-misleading-character-class.js Co-authored-by: Milos Djermanovic --- lib/rules/no-misleading-character-class.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index 1e74a4ab1d1..ba936013249 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -206,7 +206,7 @@ module.exports = { return fixer.insertTextAfterRange(patternNode.range, ', "u"'); } - if (flagsNode.type === "Literal" || flagsNode.type === "TemplateLiteral") { + if ((flagsNode.type === "Literal" && typeof flagsNode.value === "string") || flagsNode.type === "TemplateLiteral") { const range = [flagsNode.range[0], flagsNode.range[1] - 1]; return fixer.insertTextAfterRange(range, "u");