Skip to content

Commit 258be21

Browse files
authored
Fix completions crash on transient exported property named 'default' (#42583)
* Fix completions crash on transient exported property named default * Revert simplification, both conditions were needed
1 parent 05e2f74 commit 258be21

File tree

5 files changed

+30
-12
lines changed

5 files changed

+30
-12
lines changed

src/harness/fourslashImpl.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2802,7 +2802,7 @@ namespace FourSlash {
28022802
const matchingName = completions?.filter(e => e.name === options.name);
28032803
const detailMessage = matchingName?.length
28042804
? `\n Found ${matchingName.length} with name '${options.name}' from source(s) ${matchingName.map(e => `'${e.source}'`).join(", ")}.`
2805-
: "";
2805+
: ` (In fact, there were no completions with name '${options.name}' at all.)`;
28062806
return this.raiseError(`No completions were found for the given name, source, and preferences.` + detailMessage);
28072807
}
28082808
const codeActions = details.codeActions;

src/services/codefixes/importFixes.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,7 @@ namespace ts.codefix {
609609
const exported = getDefaultLikeExportWorker(importingFile, moduleSymbol, checker, compilerOptions);
610610
if (!exported) return undefined;
611611
const { symbol, kind } = exported;
612-
const info = getDefaultExportInfoWorker(symbol, moduleSymbol, checker, compilerOptions);
612+
const info = getDefaultExportInfoWorker(symbol, checker, compilerOptions);
613613
return info && { symbol, kind, ...info };
614614
}
615615

@@ -645,7 +645,7 @@ namespace ts.codefix {
645645
return allowSyntheticDefaults ? ImportKind.Default : ImportKind.CommonJS;
646646
}
647647

648-
function getDefaultExportInfoWorker(defaultExport: Symbol, moduleSymbol: Symbol, checker: TypeChecker, compilerOptions: CompilerOptions): { readonly symbolForMeaning: Symbol, readonly name: string } | undefined {
648+
function getDefaultExportInfoWorker(defaultExport: Symbol, checker: TypeChecker, compilerOptions: CompilerOptions): { readonly symbolForMeaning: Symbol, readonly name: string } | undefined {
649649
const localSymbol = getLocalSymbolForExportDefault(defaultExport);
650650
if (localSymbol) return { symbolForMeaning: localSymbol, name: localSymbol.name };
651651

@@ -659,23 +659,21 @@ namespace ts.codefix {
659659
// but we can still offer completions for it.
660660
// - `aliased.parent` will be undefined if the module is exporting `globalThis.something`,
661661
// or another expression that resolves to a global.
662-
return getDefaultExportInfoWorker(aliased, aliased.parent, checker, compilerOptions);
662+
return getDefaultExportInfoWorker(aliased, checker, compilerOptions);
663663
}
664664
}
665665

666666
if (defaultExport.escapedName !== InternalSymbolName.Default &&
667667
defaultExport.escapedName !== InternalSymbolName.ExportEquals) {
668668
return { symbolForMeaning: defaultExport, name: defaultExport.getName() };
669669
}
670-
return { symbolForMeaning: defaultExport, name: moduleSymbolToValidIdentifier(moduleSymbol, compilerOptions.target) };
670+
return { symbolForMeaning: defaultExport, name: getNameForExportedSymbol(defaultExport, compilerOptions.target) };
671671
}
672672

673673
function getNameForExportDefault(symbol: Symbol): string | undefined {
674674
return symbol.declarations && firstDefined(symbol.declarations, declaration => {
675675
if (isExportAssignment(declaration)) {
676-
if (isIdentifier(declaration.expression)) {
677-
return declaration.expression.text;
678-
}
676+
return tryCast(skipOuterExpressions(declaration.expression), isIdentifier)?.text;
679677
}
680678
else if (isExportSpecifier(declaration)) {
681679
Debug.assert(declaration.name.text === InternalSymbolName.Default, "Expected the specifier to be a default export");

src/services/completions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -732,7 +732,7 @@ namespace ts.Completions {
732732
exportedSymbol,
733733
moduleSymbol,
734734
sourceFile,
735-
getNameForExportedSymbol(symbol, compilerOptions.target!),
735+
getNameForExportedSymbol(symbol, compilerOptions.target),
736736
host,
737737
program,
738738
formatContext,

src/services/utilities.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2880,10 +2880,10 @@ namespace ts {
28802880
return isArray(valueOrArray) ? first(valueOrArray) : valueOrArray;
28812881
}
28822882

2883-
export function getNameForExportedSymbol(symbol: Symbol, scriptTarget: ScriptTarget) {
2884-
if (symbol.escapedName === InternalSymbolName.ExportEquals || symbol.escapedName === InternalSymbolName.Default) {
2883+
export function getNameForExportedSymbol(symbol: Symbol, scriptTarget: ScriptTarget | undefined) {
2884+
if (!(symbol.flags & SymbolFlags.Transient) && (symbol.escapedName === InternalSymbolName.ExportEquals || symbol.escapedName === InternalSymbolName.Default)) {
28852885
// Name of "export default foo;" is "foo". Name of "export default 0" is the filename converted to camelCase.
2886-
return firstDefined(symbol.declarations, d => isExportAssignment(d) && isIdentifier(d.expression) ? d.expression.text : undefined)
2886+
return firstDefined(symbol.declarations, d => isExportAssignment(d) ? tryCast(skipOuterExpressions(d.expression), isIdentifier)?.text : undefined)
28872887
|| codefix.moduleSymbolToValidIdentifier(getSymbolParentOrFail(symbol), scriptTarget);
28882888
}
28892889
return symbol.name;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @Filename: /collection.ts
4+
//// class Collection {
5+
//// public static readonly default: typeof Collection = Collection;
6+
//// }
7+
//// export = Collection as typeof Collection & { default: typeof Collection };
8+
9+
// @Filename: /index.ts
10+
//// Colle/**/
11+
12+
verify.applyCodeActionFromCompletion("", {
13+
name: "Collection",
14+
source: "/collection",
15+
description: `Import 'Collection' from module "./collection"`,
16+
preferences: {
17+
includeCompletionsForModuleExports: true
18+
},
19+
newFileContent: `import Collection = require("./collection");\n\nColle`
20+
});

0 commit comments

Comments
 (0)