Skip to content

Commit 2c49160

Browse files
committed
Fix completions crash on transient exported property named default
1 parent f6d3b18 commit 2c49160

File tree

4 files changed

+29
-11
lines changed

4 files changed

+29
-11
lines changed

src/harness/fourslashImpl.ts

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

src/services/codefixes/importFixes.ts

+5-7
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ namespace ts.codefix {
262262
}
263263

264264
const defaultInfo = getDefaultLikeExportInfo(importingFile, moduleSymbol, checker, compilerOptions);
265-
if (defaultInfo && (defaultInfo.name === symbolName || moduleSymbolToValidIdentifier(moduleSymbol, compilerOptions.target) === symbolName) && skipAlias(defaultInfo.symbol, checker) === exportedSymbol) {
265+
if (defaultInfo && getNameForExportedSymbol(defaultInfo.symbol, compilerOptions.target) === symbolName && skipAlias(defaultInfo.symbol, checker) === exportedSymbol) {
266266
result.push({ moduleSymbol, importKind: defaultInfo.kind, exportedSymbolIsTypeOnly: isTypeOnlySymbol(defaultInfo.symbol, checker) });
267267
}
268268

@@ -581,7 +581,7 @@ namespace ts.codefix {
581581

582582
const compilerOptions = program.getCompilerOptions();
583583
const defaultInfo = getDefaultLikeExportInfo(sourceFile, moduleSymbol, checker, compilerOptions);
584-
if (defaultInfo && (defaultInfo.name === symbolName || moduleSymbolToValidIdentifier(moduleSymbol, compilerOptions.target) === symbolName) && symbolHasMeaning(defaultInfo.symbolForMeaning, currentTokenMeaning)) {
584+
if (defaultInfo && getNameForExportedSymbol(defaultInfo.symbol, compilerOptions.target) === symbolName && symbolHasMeaning(defaultInfo.symbolForMeaning, currentTokenMeaning)) {
585585
addSymbol(moduleSymbol, defaultInfo.symbol, defaultInfo.kind, checker);
586586
}
587587

@@ -636,7 +636,7 @@ namespace ts.codefix {
636636
return allowSyntheticDefaults ? ImportKind.Default : ImportKind.CommonJS;
637637
}
638638

639-
function getDefaultExportInfoWorker(defaultExport: Symbol, moduleSymbol: Symbol, checker: TypeChecker, compilerOptions: CompilerOptions): { readonly symbolForMeaning: Symbol, readonly name: string } | undefined {
639+
function getDefaultExportInfoWorker(defaultExport: Symbol, _moduleSymbol: Symbol, checker: TypeChecker, compilerOptions: CompilerOptions): { readonly symbolForMeaning: Symbol, readonly name: string } | undefined {
640640
const localSymbol = getLocalSymbolForExportDefault(defaultExport);
641641
if (localSymbol) return { symbolForMeaning: localSymbol, name: localSymbol.name };
642642

@@ -658,15 +658,13 @@ namespace ts.codefix {
658658
defaultExport.escapedName !== InternalSymbolName.ExportEquals) {
659659
return { symbolForMeaning: defaultExport, name: defaultExport.getName() };
660660
}
661-
return { symbolForMeaning: defaultExport, name: moduleSymbolToValidIdentifier(moduleSymbol, compilerOptions.target) };
661+
return { symbolForMeaning: defaultExport, name: getNameForExportedSymbol(defaultExport, compilerOptions.target) };
662662
}
663663

664664
function getNameForExportDefault(symbol: Symbol): string | undefined {
665665
return symbol.declarations && firstDefined(symbol.declarations, declaration => {
666666
if (isExportAssignment(declaration)) {
667-
if (isIdentifier(declaration.expression)) {
668-
return declaration.expression.text;
669-
}
667+
return tryCast(skipOuterExpressions(declaration.expression), isIdentifier)?.text;
670668
}
671669
else if (isExportSpecifier(declaration)) {
672670
Debug.assert(declaration.name.text === InternalSymbolName.Default, "Expected the specifier to be a default export");

src/services/utilities.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -2888,10 +2888,10 @@ namespace ts {
28882888
return isArray(valueOrArray) ? first(valueOrArray) : valueOrArray;
28892889
}
28902890

2891-
export function getNameForExportedSymbol(symbol: Symbol, scriptTarget: ScriptTarget) {
2892-
if (symbol.escapedName === InternalSymbolName.ExportEquals || symbol.escapedName === InternalSymbolName.Default) {
2891+
export function getNameForExportedSymbol(symbol: Symbol, scriptTarget: ScriptTarget | undefined) {
2892+
if (!(symbol.flags & SymbolFlags.Transient) && (symbol.escapedName === InternalSymbolName.ExportEquals || symbol.escapedName === InternalSymbolName.Default)) {
28932893
// Name of "export default foo;" is "foo". Name of "export default 0" is the filename converted to camelCase.
2894-
return firstDefined(symbol.declarations, d => isExportAssignment(d) && isIdentifier(d.expression) ? d.expression.text : undefined)
2894+
return firstDefined(symbol.declarations, d => isExportAssignment(d) ? tryCast(skipOuterExpressions(d.expression), isIdentifier)?.text : undefined)
28952895
|| codefix.moduleSymbolToValidIdentifier(getSymbolParentOrFail(symbol), scriptTarget);
28962896
}
28972897
return symbol.name;
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)