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

Auto-imports: fix some exports being incorrectly stored as re-exports of others due to key conflict #45792

Merged
merged 8 commits into from
Oct 8, 2021

Conversation

andrewbranch
Copy link
Member

@andrewbranch andrewbranch commented Sep 9, 2021

There’s some explanation in review comments

Fixes #45763
Fixes #45784

@typescript-bot typescript-bot added Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug labels Sep 9, 2021
@typescript-bot typescript-bot added For Milestone Bug PRs that fix a bug with a specific milestone and removed For Uncommitted Bug PR for untriaged, rejected, closed or missing bug labels Sep 9, 2021
@andrewbranch
Copy link
Member Author

@typescript-bot pack this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Sep 9, 2021

Heya @andrewbranch, I've started to run the tarball bundle task on this PR at 57220f2. You can monitor the build here.

@microsoft microsoft deleted a comment from typescript-bot Sep 9, 2021
@microsoft microsoft deleted a comment from typescript-bot Sep 9, 2021
@andrewbranch andrewbranch marked this pull request as ready for review September 9, 2021 17:29
@typescript-bot
Copy link
Collaborator

typescript-bot commented Sep 9, 2021

Hey @andrewbranch, I've packed this into an installable tgz. You can install it for testing by referencing it in your package.json like so:

{
    "devDependencies": {
        "typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/110109/artifacts?artifactName=tgz&fileId=244C0951A81B1898389C45E97CA686C5006F6BDB37BAF2ED2F2D91492665B67E02&fileName=/typescript-4.5.0-insiders.20210909.tgz"
    }
}

and then running npm install.


There is also a playground for this build and an npm module you can use via "typescript": "npm:@typescript-deploys/pr-build@4.5.0-pr-45792-2".;

Copy link
Member

@sandersn sandersn left a comment

Choose a reason for hiding this comment

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

A couple of questions to check my understanding.

src/services/exportInfoMap.ts Outdated Show resolved Hide resolved
@andrewbranch andrewbranch added the Experiment A fork with an experimental idea which might not make it into master label Sep 30, 2021
@andrewbranch
Copy link
Member Author

The reason I haven’t explained what the heck is happening in this PR is I’m hoping to have some time to do a more thorough fix by saving a reference to the originally-exporting module and the name of the export in the module’s symbol table at the end of alias resolution in the checker. This is precisely the information we need to be able to identify every unique export, and the checker already has that information during alias resolution, but it discards it.

@johnsoncodehk
Copy link

Can this PR fix #46115?

@andrewbranch
Copy link
Member Author

Probably, feel free to try it

@callmeberzerker
Copy link

I just tested this and it works fine for my react project. useEffect, useState etc. are not wrongly imported from react-transition-group anymore.

Comment on lines -1769 to +1831
const origin: SymbolOriginInfoExport = {
kind: getNullableSymbolOriginInfoKind(SymbolOriginInfoKind.SymbolMemberExport),
const fileName = isExternalModuleNameRelative(stripQuotes(moduleSymbol.name)) ? getSourceFileOfModule(moduleSymbol)?.fileName : undefined;
const { moduleSpecifier } = codefix.getModuleSpecifierForBestExportInfo([{
exportKind: ExportKind.Named,
moduleFileName: fileName,
isFromPackageJson: false,
moduleSymbol,
isDefaultExport: false,
symbolName: firstAccessibleSymbol.name,
exportName: firstAccessibleSymbol.name,
fileName: isExternalModuleNameRelative(stripQuotes(moduleSymbol.name)) ? cast(moduleSymbol.valueDeclaration, isSourceFile).fileName : undefined,
};
symbolToOriginInfoMap[index] = origin;
symbol: firstAccessibleSymbol,
targetFlags: skipAlias(firstAccessibleSymbol, typeChecker).flags,
}], sourceFile, program, host, preferences) || {};

if (moduleSpecifier) {
const origin: SymbolOriginInfoResolvedExport = {
kind: getNullableSymbolOriginInfoKind(SymbolOriginInfoKind.SymbolMemberExport),
moduleSymbol,
isDefaultExport: false,
symbolName: firstAccessibleSymbol.name,
exportName: firstAccessibleSymbol.name,
fileName,
moduleSpecifier,
};
symbolToOriginInfoMap[index] = origin;
}
Copy link
Member Author

Choose a reason for hiding this comment

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

This is one of the larger set of changes in the PR, but it’s kind of a drive-by fix for a probable ancient bug that the real change, the addition of exportMapKey to SymbolOriginInfoExport, forced me to consider. This has always been a weird ad-hoc auto-import for when you need to import a symbol in order to do a property completion, as shown in this test.

I think there have likely been cases where fetching the actual auto-import (in completion details) for this completion would have failed due to some oversimplified assumptions here. I’m now guarding against that a bit more by resolving its module specifier up front and ensuring that the symbol can definitely be re-retrieved by looking for its name in the exports of the module. I could have sort of ignored this problem, but it felt better to deal with it.

@@ -46,8 +46,8 @@ namespace ts {
isUsableByFile(importingFile: Path): boolean;
clear(): void;
add(importingFile: Path, symbol: Symbol, key: __String, moduleSymbol: Symbol, moduleFile: SourceFile | undefined, exportKind: ExportKind, isFromPackageJson: boolean, scriptTarget: ScriptTarget, checker: TypeChecker): void;
get(importingFile: Path, importedName: string, symbol: Symbol, moduleName: string, checker: TypeChecker): readonly SymbolExportInfo[] | undefined;
forEach(importingFile: Path, action: (info: readonly SymbolExportInfo[], name: string, isFromAmbientModule: boolean) => void): void;
get(importingFile: Path, key: string): readonly SymbolExportInfo[] | undefined;
Copy link
Member Author

Choose a reason for hiding this comment

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

The central insight of this PR is that the cache key does not actually need to be derivable from an updated program later on, because the only thing that ever calls get is continuePreviousIncompleteResponse in completions.ts, and rather than recomputing the key from symbols it currently has access to, we can just save the key we originally came up with on CompletionEntryData and use that to access the cache in continuePreviousIncompleteResponse. This means the only criteria that the key needs to satisfy is that it produces proper grouping while building the map. This opens the door to using getSymbolId(skipAlias(someExport)) in the key, which doesn't have the same very weird pitfalls as trying to find a way to uniquely identify the target symbol that remains stable over program updates.

@andrewbranch andrewbranch removed the Experiment A fork with an experimental idea which might not make it into master label Oct 8, 2021
Copy link
Member

@amcasey amcasey left a comment

Choose a reason for hiding this comment

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

I'm happy with the concept and the protocol changes and I'm satisfied that we have an escape hatch if it breaks. I agree with @DanielRosenwasser that a little more baking time wouldn't hurt.

@andrewbranch andrewbranch merged commit 64b8172 into microsoft:main Oct 8, 2021
@andrewbranch andrewbranch deleted the bug/45763 branch October 8, 2021 22:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Author: Team For Milestone Bug PRs that fix a bug with a specific milestone
Projects
Archived in project
6 participants