Repository: eslint/eslint
File: lib/rules/prefer-named-capture-group.js (JavaScript)

Code before:
 * @returns {Array<SuggestionResult>} Fixer suggestions for the regex, if statically determinable.
 */
function suggestIfPossible(groupStart, pattern, rawText, regexNode) {
    switch (regexNode.type) {
        case "Literal":
            if (typeof regexNode.value === "string" && rawText.includes("\\")) {
                return null;
            }
            break;
        case "TemplateLiteral":
            if (regexNode.expressions.length || rawText.slice(1, -1) !== pattern) {
                return null;
            }
            break;
        default:
            return null;
    }

    const start = regexNode.range[0] + groupStart + 2;

    return [
        {
            fix(fixer) {
                return fixer.insertTextBeforeRange(
                    [start, start],
                    `?<temp${((pattern.match(/temp\d+/gu) || []).length || 0) + 1}>`
                );
            },
            messageId: "addGroupName"
        },
        {
            fix(fixer) {
                return fixer.insertTextBeforeRange(
                    [start, start],
                    "?:"
                );
            },
            messageId: "addNonCapture"
        }
    ];
}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "suggestion",


Review comment (bug):
In some very edge cases, this could add an existing name and thus produce a parsing error.

Example:

```js
/(?<temp2>(a))/;
```

Suggested fix:

```js
/(?<temp2>(?<temp2>a))/;
```

Any ideas on whether we can avoid this without adding too much complexity to the code?

Code after:
 * @returns {Array<SuggestionResult>} Fixer suggestions for the regex, if statically determinable.
 */
function suggestIfPossible(groupStart, pattern, rawText, regexNode) {
    switch (regexNode.type) {
        case "Literal":
            if (typeof regexNode.value === "string" && rawText.includes("\\")) {
                return null;
            }
            break;
        case "TemplateLiteral":
            if (regexNode.expressions.length || rawText.slice(1, -1) !== pattern) {
                return null;
            }
            break;
        default:
            return null;
    }

    const start = regexNode.range[0] + groupStart + 2;

    return [
        {
            fix(fixer) {
                const existingTemps = pattern.match(/temp\d+/gu) || [];
                const highestTempCount = existingTemps.reduce(
                    (previous, next) =>
                        Math.max(previous, Number(next.slice("temp".length))),
                    0
                );

                return fixer.insertTextBeforeRange(
                    [start, start],
                    `?<temp${highestTempCount + 1}>`
                );
            },
            messageId: "addGroupName"
        },
        {
            fix(fixer) {
                return fixer.insertTextBeforeRange(
                    [start, start],
                    "?:"
                );
            },
            messageId: "addNonCapture"
        }
    ];
}

//------------------------------------------------------------------------------
// Rule Definition