Thanks to visit codestin.com
Credit goes to github.com

Skip to content

"Extract..." refactorings not available on right side of addition/multiplication expression #61630

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
DanielRosenwasser opened this issue Apr 29, 2025 · 3 comments
Labels
Bug A bug in TypeScript Domain: Refactorings e.g. extract to constant or function, rename symbol Effort: Moderate Requires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual". Help Wanted You can do this

Comments

@DanielRosenwasser
Copy link
Member

Today, you can request "Extract..." refactorings on the left side of a chain of multiplications:

function multiply(a: number, b: number, c: number) {
    return [|a * b|] * c;
}

Most people consider * to be associative (even if it's not for IEEE-754 numbers). Unfortunately we strictly allow you to refactor expressions as-parsed, so the following doesn't work:

function multiply(a: number, b: number, c: number) {
    return a * [|b * c|];
}

Expected: Extract constant/function work on b * c

Actual:

{
    "name": "Extract Symbol",
    "description": "Extract function",
    "actions": [
        {
            "name": "Extract Function",
            "description": "Extract function",
            "kind": "refactor.extract.function",
            "notApplicableReason": "Cannot extract range."
        }
    ]
},
{
    "name": "Extract Symbol",
    "description": "Extract constant",
    "actions": [
        {
            "name": "Extract Constant",
            "description": "Extract constant",
            "kind": "refactor.extract.constant",
            "notApplicableReason": "Cannot extract range."
        }
    ]
},
@DanielRosenwasser DanielRosenwasser added Bug A bug in TypeScript Help Wanted You can do this Effort: Moderate Requires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual". Domain: Refactorings e.g. extract to constant or function, rename symbol labels Apr 29, 2025
@RyanCavanaugh
Copy link
Member

At some point we had tried to fix this. This might be a regression

@idango10
Copy link
Contributor

idango10 commented May 2, 2025

It seems the extraction fails at this part of the code:
https://github.com/microsoft/TypeScript/blob/main/src/services/refactors/extractSymbol.ts#L494-L497

// src/services/refactors/extractSymbol.ts

if (start.parent !== end.parent) {
    // start and end nodes belong to different subtrees
    return { errors: [createFileDiagnostic(sourceFile, span.start, length, Messages.cannotExtractRange)] };
}

This behavior aligns with the structure of the AST for the example provided:

// ...
ReturnStatement
    BinaryExpression // a * b * c
        BinaryExpression // a * b
            Identifier // a
            AsteriskToken // *
            Identifier // b
        AsteriskToken // *
        Identifier // c

In this tree, a and b are siblings under the same BinaryExpression node, which allows for successful extraction of a * b. However, b and c are in different subtrees. b is part of the inner BinaryExpression, while c is the right child of the top-level BinaryExpression. Therefore, attempting to extract b * c fails because the start and end nodes have different parents.

@DanielRosenwasser
Copy link
Member Author

Yeah, I think it would be good for us to make an exception in the cases where there is a string of multiplication/addition expressions, though admittedly I haven't thought very hard about how to enable that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Domain: Refactorings e.g. extract to constant or function, rename symbol Effort: Moderate Requires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual". Help Wanted You can do this
Projects
None yet
Development

No branches or pull requests

3 participants