Repository: eslint/eslint
File: lib/rules/require-unicode-regexp.js (JavaScript)

Code before:
                    let missingFlag = !flagsNode;

                    if (typeof flags === "string") {
                        missingFlag = checkFlags(requireFlag, flags);
                    }

                    if (missingFlag) {
                        context.report({
                            messageId: requireFlag === "v" ? "requireVFlag" : "requireUFlag",
                            node: refNode,
                            suggest: typeof pattern === "string" && isValidWithUnicodeFlag(context.languageOptions.ecmaVersion, pattern, requireFlag)
                                ? [
                                    {
                                        fix(fixer) {
                                            const replaceFlag = requireFlag ?? "u";

                                            if (flagsNode) {
                                                if ((flagsNode.type === "Literal" && typeof flagsNode.value === "string") || flagsNode.type === "TemplateLiteral") {
                                                    const flagsNodeText = sourceCode.getText(flagsNode);
                                                    const flag = requireFlag === "u" ? "v" : "u";

                                                    return fixer.replaceText(flagsNode, [
                                                        flagsNodeText.slice(0, flagsNodeText.length - 1).replace(
                                                            flag,
                                                            ""
                                                        ),
                                                        flagsNodeText.slice(flagsNodeText.length - 1)
                                                    ].join(replaceFlag));
                                                }

                                                // We intentionally don't suggest concatenating + "u" to non-literals
                                                return null;
                                            }

                                            const penultimateToken = sourceCode.getLastToken(refNode, { skip: 1 }); // skip closing parenthesis

                                            return fixer.insertTextAfter(
                                                penultimateToken,
                                                astUtils.isCommaToken(penultimateToken)
                                                    ? ` "'${replaceFlag}'",`
                                                    : `, "${replaceFlag}"`
                                            );
                                        },
                                        messageId: requireFlag === "v" ? "addVFlag" : "addUFlag"
                                    }
                                ]
                                : null
                        });
                    }
                }
            }

Review comment (security):
In these cases, the fix wouldn't be correct:

```js
/*eslint require-unicode-regexp: ["error", { "requireFlag": "v" }] */

const foo = new RegExp("foo", "\u0067"); // fixed to "\0067v"

const regularFlags = "sm";

const bar = new RegExp("bar", `${regularFlags}g`); // fixed to `${reglarFlags}gv`

```

I think, in case when the other flag is already present, it's fine to provide the suggestion only if the flags node is a string literal without escape sequences (i.e. without a `\` character) or a template literal with no expressions and no escape sequences.

Code after:
                    let missingFlag = !flagsNode;

                    if (typeof flags === "string") {
                        missingFlag = checkFlags(requireFlag, flags);
                    }

                    if (missingFlag) {
                        context.report({
                            messageId: requireFlag === "v" ? "requireVFlag" : "requireUFlag",
                            node: refNode,
                            suggest: typeof pattern === "string" && isValidWithUnicodeFlag(context.languageOptions.ecmaVersion, pattern, requireFlag)
                                ? [
                                    {
                                        fix(fixer) {
                                            const replaceFlag = requireFlag ?? "u";

                                            if (flagsNode) {
                                                if ((flagsNode.type === "Literal" && typeof flagsNode.value === "string") || flagsNode.type === "TemplateLiteral") {
                                                    const flagsNodeText = sourceCode.getText(flagsNode);
                                                    const flag = requireFlag === "u" ? "v" : "u";

                                                    if (flags.includes(flag)) {

                                                        // Avoid replacing "u" in escapes like `\uXXXX`
                                                        if (flagsNode.type === "Literal" && flagsNode.raw.includes("\\")) {
                                                            return null;
                                                        }

                                                        // Avoid replacing "u" in expressions like "`${regularFlags}g`"
                                                        if (flagsNode.type === "TemplateLiteral" && (
                                                            flagsNode.expressions.length ||
                                                            flagsNode.quasis.some(({ value: { raw } }) => raw.includes("\\"))
                                                        )) {
                                                            return null;
                                                        }

                                                        return fixer.replaceText(flagsNode, flagsNodeText.replace(flag, replaceFlag));
                                                    }

                                                    return fixer.replaceText(flagsNode, [
                                                        flagsNodeText.slice(0, flagsNodeText.length - 1),
                                                        flagsNodeText.slice(flagsNodeText.length - 1)
                                                    ].join(replaceFlag));
                                                }

                                                // We intentionally don't suggest concatenating + "u" to non-literals
                                                return null;
                                            }

                                            const penultimateToken = sourceCode.getLastToken(refNode, { skip: 1 }); // skip closing parenthesis