Skip to content

Commit 487be36

Browse files
authored
Fix auto import completion inserting wrong module specifier (microsoft#41955)
* Don’t look for reëxports when import source was an ambient module * Add additional assertion for clarity
1 parent e84a95f commit 487be36

File tree

3 files changed

+87
-2
lines changed

3 files changed

+87
-2
lines changed

src/harness/fourslashImpl.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2798,7 +2798,12 @@ namespace FourSlash {
27982798

27992799
const details = this.getCompletionEntryDetails(options.name, options.source, options.preferences);
28002800
if (!details) {
2801-
return this.raiseError(`No completions were found for the given name, source, and preferences.`);
2801+
const completions = this.getCompletionListAtCaret(options.preferences)?.entries;
2802+
const matchingName = completions?.filter(e => e.name === options.name);
2803+
const detailMessage = matchingName?.length
2804+
? `\n Found ${matchingName.length} with name '${options.name}' from source(s) ${matchingName.map(e => `'${e.source}'`).join(", ")}.`
2805+
: "";
2806+
return this.raiseError(`No completions were found for the given name, source, and preferences.` + detailMessage);
28022807
}
28032808
const codeActions = details.codeActions;
28042809
if (codeActions?.length !== 1) {

src/services/codefixes/importFixes.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,9 @@ namespace ts.codefix {
210210
preferences: UserPreferences,
211211
): { readonly moduleSpecifier: string, readonly codeAction: CodeAction } {
212212
const compilerOptions = program.getCompilerOptions();
213-
const exportInfos = getAllReExportingModules(sourceFile, exportedSymbol, moduleSymbol, symbolName, host, program, /*useAutoImportProvider*/ true);
213+
const exportInfos = pathIsBareSpecifier(stripQuotes(moduleSymbol.name))
214+
? [getSymbolExportInfoForSymbol(exportedSymbol, moduleSymbol, sourceFile, program, host)]
215+
: getAllReExportingModules(sourceFile, exportedSymbol, moduleSymbol, symbolName, host, program, /*useAutoImportProvider*/ true);
214216
const useRequire = shouldUseRequire(sourceFile, program);
215217
const preferTypeOnlyImport = compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error && !isSourceFileJS(sourceFile) && isValidTypeOnlyAliasUseSite(getTokenAtPosition(sourceFile, position));
216218
const moduleSpecifier = first(getNewImportInfos(program, sourceFile, position, preferTypeOnlyImport, useRequire, exportInfos, host, preferences)).moduleSpecifier;
@@ -228,6 +230,27 @@ namespace ts.codefix {
228230
return { description, changes, commands };
229231
}
230232

233+
function getSymbolExportInfoForSymbol(symbol: Symbol, moduleSymbol: Symbol, importingFile: SourceFile, program: Program, host: LanguageServiceHost): SymbolExportInfo {
234+
const compilerOptions = program.getCompilerOptions();
235+
const mainProgramInfo = getInfoWithChecker(program.getTypeChecker());
236+
if (mainProgramInfo) {
237+
return mainProgramInfo;
238+
}
239+
const autoImportProvider = host.getPackageJsonAutoImportProvider?.()?.getTypeChecker();
240+
return Debug.checkDefined(autoImportProvider && getInfoWithChecker(autoImportProvider), `Could not find symbol in specified module for code actions`);
241+
242+
function getInfoWithChecker(checker: TypeChecker): SymbolExportInfo | undefined {
243+
const defaultInfo = getDefaultLikeExportInfo(importingFile, moduleSymbol, checker, compilerOptions);
244+
if (defaultInfo && skipAlias(defaultInfo.symbol, checker) === symbol) {
245+
return { moduleSymbol, importKind: defaultInfo.kind, exportedSymbolIsTypeOnly: isTypeOnlySymbol(symbol, checker) };
246+
}
247+
const named = checker.tryGetMemberInModuleExportsAndProperties(symbol.name, moduleSymbol);
248+
if (named && skipAlias(named, checker) === symbol) {
249+
return { moduleSymbol, importKind: ImportKind.Named, exportedSymbolIsTypeOnly: isTypeOnlySymbol(symbol, checker) };
250+
}
251+
}
252+
}
253+
231254
function getAllReExportingModules(importingFile: SourceFile, exportedSymbol: Symbol, exportingModuleSymbol: Symbol, symbolName: string, host: LanguageServiceHost, program: Program, useAutoImportProvider: boolean): readonly SymbolExportInfo[] {
232255
const result: SymbolExportInfo[] = [];
233256
const compilerOptions = program.getCompilerOptions();
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @module: commonjs
4+
5+
// @Filename: a.d.ts
6+
//// declare namespace foo { class Bar {} }
7+
//// declare module 'path1' {
8+
//// import Bar = foo.Bar;
9+
//// export default Bar;
10+
//// }
11+
//// declare module 'path2longer' {
12+
//// import Bar = foo.Bar;
13+
//// export {Bar};
14+
//// }
15+
////
16+
17+
// @Filename: b.ts
18+
//// Ba/**/
19+
20+
verify.completions({
21+
marker: "",
22+
exact: [
23+
completion.globalThisEntry,
24+
...completion.globalsVars,
25+
{
26+
name: "foo",
27+
sortText: completion.SortText.GlobalsOrKeywords
28+
},
29+
completion.undefinedVarEntry,
30+
{
31+
name: "Bar",
32+
source: "path1",
33+
hasAction: true,
34+
sortText: completion.SortText.AutoImportSuggestions
35+
},
36+
{
37+
name: "Bar",
38+
source: "path2longer",
39+
hasAction: true,
40+
sortText: completion.SortText.AutoImportSuggestions
41+
},
42+
...completion.statementKeywordsWithTypes
43+
],
44+
preferences: {
45+
includeCompletionsForModuleExports: true
46+
}
47+
});
48+
49+
verify.applyCodeActionFromCompletion("", {
50+
name: "Bar",
51+
source: "path2longer",
52+
description: `Import 'Bar' from module "path2longer"`,
53+
newFileContent: `import { Bar } from "path2longer";\n\nBa`,
54+
preferences: {
55+
includeCompletionsForModuleExports: true
56+
}
57+
});

0 commit comments

Comments
 (0)