Skip to content
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

moveToFile: fix check for global name resolving #60173

Closed
wants to merge 8 commits into from

Conversation

iisaduan
Copy link
Member

@iisaduan iisaduan commented Oct 8, 2024

Fixes #59799.

Fixes isGlobalType, which was added in #58811.

@typescript-bot typescript-bot added Author: Team For Milestone Bug PRs that fix a bug with a specific milestone labels Oct 8, 2024
@iisaduan
Copy link
Member Author

iisaduan commented Oct 8, 2024

close/reopening to fix ci

@iisaduan iisaduan closed this Oct 8, 2024
@iisaduan iisaduan reopened this Oct 8, 2024
@@ -946,7 +946,8 @@ export function getUsageInfo(oldFile: SourceFile, toMove: readonly Statement[],
}
}

function isGlobalType(checker: TypeChecker, symbol: Symbol) {
function isGlobalType(checker: TypeChecker, location: Node, symbol: Symbol) {
if (checker.resolveName(symbol.name, location, SymbolFlags.All, /*excludeGlobals*/ true)) return false;
return !!checker.resolveName(symbol.name, /*location*/ undefined, SymbolFlags.Type, /*excludeGlobals*/ false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm having trouble wrapping my head around this function, I think because it takes a symbol but only uses it for its name. Do the function name and parameters need to be tweaked a bit to better reflect what this is trying to accomplish?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there another way to check if a symbol refers to a global, and can be checked reliably after symbol merging/through aliasing? I guess this function doesn't need a Symbol to be passed in as it currently is, but I couldn't think of a better way to do this check for globals.

The old code was checking if there was any global symbol with that name that was a type without seeing first if there was a local symbol with that name. The function name seems alright to me since it's checking if, at this node, the symbol refers to a global type.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function name seems alright to me since it's checking if, at this node, the symbol refers to a global type

It’s checking if a given name refers to a global type; it’s not really checking anything about the symbol that’s being passed in. If you wanted to check whether the symbol itself is a type that is accessible as a global at a particular location, you might check for equality between the symbol and the resolveName result. But I’m not sure that’s relevant here.

But the line added here seems to allow for a false negative of the simple question “does this name refer to a global type.” Just because a name resolves to a local value doesn’t mean it doesn’t also resolve to a global type at the same location:

const Event = {};
let e: Event; // <--

If you pass in the node for the Event type, isGlobalType will return false. That feels like the wrong answer just reading the function name, even if it has the desired behavior for Move To File (does it?).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the line added here seems to allow for a false negative of the simple question “does this name refer to a global type.” Just because a name resolves to a local value doesn’t mean it doesn’t also resolve to a global type at the same location:

The correct check to have here is if the symbol refers to a global type or not, and the previous check did not do that (and seems like the previous check wanted to check the symbol, given that they took a symbol instead of just the name).

even if it has the desired behavior for Move To File (does it?).

It makes sense to me that if we try to move a symbol that references a local definition, but also happens to have an unrelated global definition, that we keep the reference in the new location as the same symbol that was in the original file. Otherwise, the code in the new file can be broken, like in the bug report above. I spoke to @navya9singh about this, and we concluded that we should generate the import to keep the original reference. Also, other codefixes keep local references across files, even if the symbol resolves to a global (added test to show this).

Copy link
Member

@DanielRosenwasser DanielRosenwasser Oct 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@andrewbranch am I correct that you're mostly not a fan of the function doing the following?

  1. taking a Symbol instead of a string (since all it needs is the name of the symbol)
  2. being named isGlobalType, since a type might not actually be declared in the global scope (so e.g. cannotResolveTypeAt would be a "better" name)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes - I didn't recontextualize myself with the broader problem enough to tell whether the implementation/behavior is wrong; I can just tell from reading the function in isolation that something is a little fishy (specifically, your points above).

Copy link
Member Author

@iisaduan iisaduan Oct 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DanielRosenwasser @andrewbranch How do you feel about the new behavior? I can find a better way to do this check and/or rename the function if the new behavior makes sense.

I can change the first part of this function to skipAlias (which in checker is resolveSymbol), but I think I'm confused because I've looked through the existing code, and I don't know how else to perform the second part of this check to find if there is a global without using the name resolver.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Author: Team Fix Available A PR has been opened for this issue For Milestone Bug PRs that fix a bug with a specific milestone
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

Import named Disposable is not moved when moving code to another file
4 participants