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
This commit is contained in:
Andrew Branch 2021-02-04 10:22:15 -08:00 committed by GitHub
parent 05e2f74fbe
commit 258be217a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 30 additions and 12 deletions

View File

@ -2802,7 +2802,7 @@ namespace FourSlash {
const matchingName = completions?.filter(e => e.name === options.name);
const detailMessage = matchingName?.length
? `\n Found ${matchingName.length} with name '${options.name}' from source(s) ${matchingName.map(e => `'${e.source}'`).join(", ")}.`
: "";
: ` (In fact, there were no completions with name '${options.name}' at all.)`;
return this.raiseError(`No completions were found for the given name, source, and preferences.` + detailMessage);
}
const codeActions = details.codeActions;

View File

@ -609,7 +609,7 @@ namespace ts.codefix {
const exported = getDefaultLikeExportWorker(importingFile, moduleSymbol, checker, compilerOptions);
if (!exported) return undefined;
const { symbol, kind } = exported;
const info = getDefaultExportInfoWorker(symbol, moduleSymbol, checker, compilerOptions);
const info = getDefaultExportInfoWorker(symbol, checker, compilerOptions);
return info && { symbol, kind, ...info };
}
@ -645,7 +645,7 @@ namespace ts.codefix {
return allowSyntheticDefaults ? ImportKind.Default : ImportKind.CommonJS;
}
function getDefaultExportInfoWorker(defaultExport: Symbol, moduleSymbol: Symbol, checker: TypeChecker, compilerOptions: CompilerOptions): { readonly symbolForMeaning: Symbol, readonly name: string } | undefined {
function getDefaultExportInfoWorker(defaultExport: Symbol, checker: TypeChecker, compilerOptions: CompilerOptions): { readonly symbolForMeaning: Symbol, readonly name: string } | undefined {
const localSymbol = getLocalSymbolForExportDefault(defaultExport);
if (localSymbol) return { symbolForMeaning: localSymbol, name: localSymbol.name };
@ -659,7 +659,7 @@ namespace ts.codefix {
// but we can still offer completions for it.
// - `aliased.parent` will be undefined if the module is exporting `globalThis.something`,
// or another expression that resolves to a global.
return getDefaultExportInfoWorker(aliased, aliased.parent, checker, compilerOptions);
return getDefaultExportInfoWorker(aliased, checker, compilerOptions);
}
}
@ -667,15 +667,13 @@ namespace ts.codefix {
defaultExport.escapedName !== InternalSymbolName.ExportEquals) {
return { symbolForMeaning: defaultExport, name: defaultExport.getName() };
}
return { symbolForMeaning: defaultExport, name: moduleSymbolToValidIdentifier(moduleSymbol, compilerOptions.target) };
return { symbolForMeaning: defaultExport, name: getNameForExportedSymbol(defaultExport, compilerOptions.target) };
}
function getNameForExportDefault(symbol: Symbol): string | undefined {
return symbol.declarations && firstDefined(symbol.declarations, declaration => {
if (isExportAssignment(declaration)) {
if (isIdentifier(declaration.expression)) {
return declaration.expression.text;
}
return tryCast(skipOuterExpressions(declaration.expression), isIdentifier)?.text;
}
else if (isExportSpecifier(declaration)) {
Debug.assert(declaration.name.text === InternalSymbolName.Default, "Expected the specifier to be a default export");

View File

@ -732,7 +732,7 @@ namespace ts.Completions {
exportedSymbol,
moduleSymbol,
sourceFile,
getNameForExportedSymbol(symbol, compilerOptions.target!),
getNameForExportedSymbol(symbol, compilerOptions.target),
host,
program,
formatContext,

View File

@ -2880,10 +2880,10 @@ namespace ts {
return isArray(valueOrArray) ? first(valueOrArray) : valueOrArray;
}
export function getNameForExportedSymbol(symbol: Symbol, scriptTarget: ScriptTarget) {
if (symbol.escapedName === InternalSymbolName.ExportEquals || symbol.escapedName === InternalSymbolName.Default) {
export function getNameForExportedSymbol(symbol: Symbol, scriptTarget: ScriptTarget | undefined) {
if (!(symbol.flags & SymbolFlags.Transient) && (symbol.escapedName === InternalSymbolName.ExportEquals || symbol.escapedName === InternalSymbolName.Default)) {
// Name of "export default foo;" is "foo". Name of "export default 0" is the filename converted to camelCase.
return firstDefined(symbol.declarations, d => isExportAssignment(d) && isIdentifier(d.expression) ? d.expression.text : undefined)
return firstDefined(symbol.declarations, d => isExportAssignment(d) ? tryCast(skipOuterExpressions(d.expression), isIdentifier)?.text : undefined)
|| codefix.moduleSymbolToValidIdentifier(getSymbolParentOrFail(symbol), scriptTarget);
}
return symbol.name;

View File

@ -0,0 +1,20 @@
/// <reference path="fourslash.ts" />
// @Filename: /collection.ts
//// class Collection {
//// public static readonly default: typeof Collection = Collection;
//// }
//// export = Collection as typeof Collection & { default: typeof Collection };
// @Filename: /index.ts
//// Colle/**/
verify.applyCodeActionFromCompletion("", {
name: "Collection",
source: "/collection",
description: `Import 'Collection' from module "./collection"`,
preferences: {
includeCompletionsForModuleExports: true
},
newFileContent: `import Collection = require("./collection");\n\nColle`
});