diff --git a/docs/src/rules/no-misleading-character-class.md b/docs/src/rules/no-misleading-character-class.md index 005127abf48..9d190820146 100644 --- a/docs/src/rules/no-misleading-character-class.md +++ b/docs/src/rules/no-misleading-character-class.md @@ -7,6 +7,8 @@ rule_type: problem + + Disallows characters which are made with multiple code points in character class syntax. Unicode includes the characters which are made with multiple code points. diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js index 94b28784a10..d3d9d382ac5 100644 --- a/lib/rules/no-misleading-character-class.js +++ b/lib/rules/no-misleading-character-class.js @@ -4,13 +4,16 @@ "use strict"; const { CALL, CONSTRUCT, ReferenceTracker, getStringIfConstant } = require("eslint-utils"); -const { RegExpParser, visitRegExpAST } = require("regexpp"); +const { RegExpValidator, RegExpParser, visitRegExpAST } = require("regexpp"); const { isCombiningCharacter, isEmojiModifier, isRegionalIndicatorSymbol, isSurrogatePair } = require("./utils/unicode"); +const astUtils = require("./utils/ast-utils.js"); //------------------------------------------------------------------------------ // Helpers //------------------------------------------------------------------------------ +const REGEXPP_LATEST_ECMA_VERSION = 2022; + /** * Iterate character sequences of a given nodes. * @@ -109,6 +112,8 @@ module.exports = { url: "https://eslint.org/docs/rules/no-misleading-character-class" }, + hasSuggestions: true, + schema: [], messages: { @@ -116,10 +121,12 @@ 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) { + const sourceCode = context.getSourceCode(); const parser = new RegExpParser(); /** @@ -127,17 +134,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) { - const has = { - surrogatePairWithoutUFlag: false, - combiningClass: false, - variationSelector: false, - emojiModifier: false, - regionalIndicatorSymbol: false, - zwj: false - }; + function verify(node, pattern, flags, unicodeFixer) { let patternNode; try { @@ -153,26 +153,75 @@ 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) { + let suggest; + + if (kind === "surrogatePairWithoutUFlag") { + suggest = [{ + messageId: "suggestUnicodeFlag", + fix: unicodeFixer + }]; } + + context.report({ + node, + messageId: kind, + suggest + }); } } + /** + * Checks if the given regular expression pattern would be valid with the `u` flag. + * @param {string} pattern The regular expression pattern to verify. + * @returns {boolean} `true` if the pattern would be valid with the `u` flag. + * `false` if the pattern would be invalid with the `u` flag or the configured + * ecmaVersion doesn't support the `u` flag. + */ + function isValidWithUnicodeFlag(pattern) { + const { ecmaVersion } = context.parserOptions; + + // ecmaVersion is unknown or it doesn't support the 'u' flag + if (typeof ecmaVersion !== "number" || ecmaVersion <= 5) { + return false; + } + + const validator = new RegExpValidator({ + ecmaVersion: Math.min(ecmaVersion + 2009, REGEXPP_LATEST_ECMA_VERSION) + }); + + try { + validator.validatePattern(pattern, void 0, void 0, /* uFlag = */ true); + } catch { + return false; + } + + return true; + } + return { "Literal[regex]"(node) { - verify(node, node.regex.pattern, node.regex.flags); + verify(node, node.regex.pattern, node.regex.flags, fixer => { + if (!isValidWithUnicodeFlag(node.regex.pattern)) { + return null; + } + + return fixer.insertTextAfter(node, "u"); + }); }, "Program"() { const scope = context.getScope(); @@ -191,7 +240,31 @@ module.exports = { const flags = getStringIfConstant(flagsNode, scope); if (typeof pattern === "string") { - verify(node, pattern, flags || ""); + verify(node, pattern, flags || "", fixer => { + + if (!isValidWithUnicodeFlag(pattern)) { + return null; + } + + if (node.arguments.length === 1) { + const penultimateToken = sourceCode.getLastToken(node, { skip: 1 }); // skip closing parenthesis + + return fixer.insertTextAfter( + penultimateToken, + astUtils.isCommaToken(penultimateToken) + ? ' "u",' + : ', "u"' + ); + } + + 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"); + } + + return null; + }); } } } diff --git a/tests/lib/rules/no-misleading-character-class.js b/tests/lib/rules/no-misleading-character-class.js index a02e5e13e32..0aaf34e5942 100644 --- a/tests/lib/rules/no-misleading-character-class.js +++ b/tests/lib/rules/no-misleading-character-class.js @@ -76,223 +76,549 @@ 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 = /[πŸ‘]/", + parserOptions: { ecmaVersion: 3 }, + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: null // ecmaVersion doesn't support the 'u' flag + }] + }, + { + code: "var r = /[πŸ‘]/", + parserOptions: { ecmaVersion: 5 }, + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: null // ecmaVersion doesn't support the 'u' flag + }] + }, + { + code: "var r = /[πŸ‘]\\a/", + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: null // pattern would be invalid with the 'u' flag + }] + }, + { + code: "var r = /(?<=[πŸ‘])/", + parserOptions: { ecmaVersion: 9 }, + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /(?<=[πŸ‘])/u" }] + }] + }, + { + code: "var r = /(?<=[πŸ‘])/", + parserOptions: { ecmaVersion: 2018 }, + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /(?<=[πŸ‘])/u" }] + }] }, { code: "var r = /[Á]/", - errors: [{ messageId: "combiningClass" }] + errors: [{ + messageId: "combiningClass", + suggestions: null + }] }, { code: "var r = /[Á]/u", - errors: [{ messageId: "combiningClass" }] + errors: [{ + messageId: "combiningClass", + suggestions: null + }] }, { code: "var r = /[\\u0041\\u0301]/", - errors: [{ messageId: "combiningClass" }] + errors: [{ + messageId: "combiningClass", + suggestions: null + }] }, { code: "var r = /[\\u0041\\u0301]/u", - errors: [{ messageId: "combiningClass" }] + errors: [{ + messageId: "combiningClass", + suggestions: null + }] }, { code: "var r = /[\\u{41}\\u{301}]/u", - errors: [{ messageId: "combiningClass" }] + errors: [{ + messageId: "combiningClass", + suggestions: null + }] }, { code: "var r = /[❇️]/", - errors: [{ messageId: "combiningClass" }] + errors: [{ + messageId: "combiningClass", + suggestions: null + }] }, { code: "var r = /[❇️]/u", - errors: [{ messageId: "combiningClass" }] + errors: [{ + messageId: "combiningClass", + suggestions: null + }] }, { code: "var r = /[\\u2747\\uFE0F]/", - errors: [{ messageId: "combiningClass" }] + errors: [{ + messageId: "combiningClass", + suggestions: null + }] }, { code: "var r = /[\\u2747\\uFE0F]/u", - errors: [{ messageId: "combiningClass" }] + errors: [{ + messageId: "combiningClass", + suggestions: null + }] }, { code: "var r = /[\\u{2747}\\u{FE0F}]/u", - errors: [{ messageId: "combiningClass" }] + errors: [{ + messageId: "combiningClass", + suggestions: null + }] }, { code: "var r = /[πŸ‘ΆπŸ»]/", - errors: [{ messageId: "surrogatePairWithoutUFlag" }] + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[πŸ‘ΆπŸ»]/u" }] + }] }, { code: "var r = /[πŸ‘ΆπŸ»]/u", - errors: [{ messageId: "emojiModifier" }] + errors: [{ + messageId: "emojiModifier", + suggestions: null + }] }, { code: "var r = /[\\uD83D\\uDC76\\uD83C\\uDFFB]/u", - errors: [{ messageId: "emojiModifier" }] + errors: [{ + messageId: "emojiModifier", + suggestions: null + }] }, { code: "var r = /[\\u{1F476}\\u{1F3FB}]/u", - errors: [{ messageId: "emojiModifier" }] + errors: [{ + messageId: "emojiModifier", + suggestions: null + }] }, { 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", - errors: [{ messageId: "regionalIndicatorSymbol" }] + errors: [{ + messageId: "regionalIndicatorSymbol", + suggestions: null + }] }, { code: "var r = /[\\uD83C\\uDDEF\\uD83C\\uDDF5]/u", - errors: [{ messageId: "regionalIndicatorSymbol" }] + errors: [{ + messageId: "regionalIndicatorSymbol", + suggestions: null + }] }, { code: "var r = /[\\u{1F1EF}\\u{1F1F5}]/u", - errors: [{ messageId: "regionalIndicatorSymbol" }] + errors: [{ + messageId: "regionalIndicatorSymbol", + suggestions: null + }] }, { code: "var r = /[πŸ‘¨β€πŸ‘©β€πŸ‘¦]/", errors: [ - { messageId: "surrogatePairWithoutUFlag" }, - { messageId: "zwj" } + { + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[πŸ‘¨β€πŸ‘©β€πŸ‘¦]/u" }] + }, + { + messageId: "zwj", + suggestions: null + } ] }, { code: "var r = /[πŸ‘¨β€πŸ‘©β€πŸ‘¦]/u", - errors: [{ messageId: "zwj" }] + errors: [{ + messageId: "zwj", + suggestions: null + }] }, { code: "var r = /[\\uD83D\\uDC68\\u200D\\uD83D\\uDC69\\u200D\\uD83D\\uDC66]/u", - errors: [{ messageId: "zwj" }] + errors: [{ + messageId: "zwj", + suggestions: null + }] }, { code: "var r = /[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]/u", - errors: [{ messageId: "zwj" }] + errors: [{ + messageId: "zwj", + suggestions: null + }] }, // 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: "var r = new RegExp('[πŸ‘]', ``)", + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = new RegExp('[πŸ‘]', `u`)" }] + }] + }, + { + code: String.raw`var r = new RegExp("[πŸ‘]", flags)`, + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: null + }] + }, + { + code: String.raw`const flags = ""; var r = new RegExp("[πŸ‘]", flags)`, + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: null + }] }, { 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("[πŸ‘]", "")`, + parserOptions: { ecmaVersion: 3 }, + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: null // ecmaVersion doesn't support the 'u' flag + }] + }, + { + code: String.raw`var r = new RegExp("[πŸ‘]", "")`, + parserOptions: { ecmaVersion: 5 }, + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: null // ecmaVersion doesn't support the 'u' flag + }] + }, + { + code: String.raw`var r = new RegExp("[πŸ‘]\\a", "")`, + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: null // pattern would be invalid with the 'u' flag + }] + }, + { + code: String.raw`var r = new RegExp("/(?<=[πŸ‘])", "")`, + parserOptions: { ecmaVersion: 9 }, + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("/(?<=[πŸ‘])", "u")` }] + }] + }, + { + code: String.raw`var r = new RegExp("/(?<=[πŸ‘])", "")`, + parserOptions: { ecmaVersion: 2018 }, + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("/(?<=[πŸ‘])", "u")` }] + }] }, { code: String.raw`var r = new RegExp("[Á]", "")`, - errors: [{ messageId: "combiningClass" }] + errors: [{ + messageId: "combiningClass", + suggestions: null + }] }, { code: String.raw`var r = new RegExp("[Á]", "u")`, - errors: [{ messageId: "combiningClass" }] + errors: [{ + messageId: "combiningClass", + suggestions: null + }] }, { code: String.raw`var r = new RegExp("[\\u0041\\u0301]", "")`, - errors: [{ messageId: "combiningClass" }] + errors: [{ + messageId: "combiningClass", + suggestions: null + }] }, { code: String.raw`var r = new RegExp("[\\u0041\\u0301]", "u")`, - errors: [{ messageId: "combiningClass" }] + errors: [{ + messageId: "combiningClass", + suggestions: null + }] }, { code: String.raw`var r = new RegExp("[\\u{41}\\u{301}]", "u")`, - errors: [{ messageId: "combiningClass" }] + errors: [{ + messageId: "combiningClass", + suggestions: null + }] }, { code: String.raw`var r = new RegExp("[❇️]", "")`, - errors: [{ messageId: "combiningClass" }] + errors: [{ + messageId: "combiningClass", + suggestions: null + }] }, { code: String.raw`var r = new RegExp("[❇️]", "u")`, - errors: [{ messageId: "combiningClass" }] + errors: [{ + messageId: "combiningClass", + suggestions: null + }] }, { code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "")`, - errors: [{ messageId: "combiningClass" }] + errors: [{ + messageId: "combiningClass", + suggestions: null + }] }, { code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "u")`, - errors: [{ messageId: "combiningClass" }] + errors: [{ + messageId: "combiningClass", + suggestions: null + }] }, { code: String.raw`var r = new RegExp("[\\u{2747}\\u{FE0F}]", "u")`, - errors: [{ messageId: "combiningClass" }] + errors: [{ + messageId: "combiningClass", + suggestions: null + }] }, { 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")`, - errors: [{ messageId: "emojiModifier" }] + errors: [{ + messageId: "emojiModifier", + suggestions: null + }] }, { code: String.raw`var r = new RegExp("[\\uD83D\\uDC76\\uD83C\\uDFFB]", "u")`, - errors: [{ messageId: "emojiModifier" }] + errors: [{ + messageId: "emojiModifier", + suggestions: null + }] }, { code: String.raw`var r = new RegExp("[\\u{1F476}\\u{1F3FB}]", "u")`, - errors: [{ messageId: "emojiModifier" }] + errors: [{ + messageId: "emojiModifier", + suggestions: null + }] }, { 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: "var r = new RegExp('[πŸ‡―πŸ‡΅]', `i`)", + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = new RegExp('[πŸ‡―πŸ‡΅]', `iu`)" }] + }] + }, + { + code: "var r = new RegExp('[πŸ‡―πŸ‡΅]', `${foo}`)", + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = new RegExp('[πŸ‡―πŸ‡΅]', `${foo}u`)" }] + }] + }, + { + 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("[πŸ‡―πŸ‡΅]",)`, + parserOptions: { ecmaVersion: 2017 }, + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[πŸ‡―πŸ‡΅]", "u",)` }] + }] + }, + { + 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((("[πŸ‡―πŸ‡΅]")))`, + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp((("[πŸ‡―πŸ‡΅]")), "u")` }] + }] + }, + { + code: String.raw`var r = new RegExp(("[πŸ‡―πŸ‡΅]"),)`, + parserOptions: { ecmaVersion: 2017 }, + errors: [{ + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp(("[πŸ‡―πŸ‡΅]"), "u",)` }] + }] }, { code: String.raw`var r = new RegExp("[πŸ‡―πŸ‡΅]", "u")`, - errors: [{ messageId: "regionalIndicatorSymbol" }] + errors: [{ + messageId: "regionalIndicatorSymbol", + suggestions: null + }] }, { code: String.raw`var r = new RegExp("[\\uD83C\\uDDEF\\uD83C\\uDDF5]", "u")`, - errors: [{ messageId: "regionalIndicatorSymbol" }] + errors: [{ + messageId: "regionalIndicatorSymbol", + suggestions: null + }] }, { code: String.raw`var r = new RegExp("[\\u{1F1EF}\\u{1F1F5}]", "u")`, - errors: [{ messageId: "regionalIndicatorSymbol" }] + errors: [{ + messageId: "regionalIndicatorSymbol", + suggestions: null + }] }, { code: String.raw`var r = new RegExp("[πŸ‘¨β€πŸ‘©β€πŸ‘¦]", "")`, errors: [ - { messageId: "surrogatePairWithoutUFlag" }, - { messageId: "zwj" } + { + messageId: "surrogatePairWithoutUFlag", + suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[πŸ‘¨β€πŸ‘©β€πŸ‘¦]", "u")` }] + }, + { + messageId: "zwj", + suggestions: null + } ] }, { code: String.raw`var r = new RegExp("[πŸ‘¨β€πŸ‘©β€πŸ‘¦]", "u")`, - errors: [{ messageId: "zwj" }] + errors: [{ + messageId: "zwj", + suggestions: null + }] }, { code: String.raw`var r = new RegExp("[\\uD83D\\uDC68\\u200D\\uD83D\\uDC69\\u200D\\uD83D\\uDC66]", "u")`, - errors: [{ messageId: "zwj" }] + errors: [{ + messageId: "zwj", + suggestions: null + }] }, { code: String.raw`var r = new RegExp("[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]", "u")`, - errors: [{ messageId: "zwj" }] + errors: [{ + messageId: "zwj", + suggestions: null + }] }, { code: String.raw`var r = new globalThis.RegExp("[❇️]", "")`, env: { es2020: true }, - errors: [{ messageId: "combiningClass" }] + errors: [{ + messageId: "combiningClass", + suggestions: null + }] }, { code: String.raw`var r = new globalThis.RegExp("[πŸ‘ΆπŸ»]", "u")`, env: { es2020: true }, - errors: [{ messageId: "emojiModifier" }] + errors: [{ + messageId: "emojiModifier", + suggestions: null + }] }, { 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")`, env: { es2020: true }, - errors: [{ messageId: "zwj" }] + errors: [{ + messageId: "zwj", + suggestions: null + }] } ] });