Fix auto import crash on weird JS aliasing (#46490)

* Fix auto import crash on weird JS aliasing

* Comment up the weird test

* Fix setting members on union/intersection type, happens later
This commit is contained in:
Andrew Branch
2021-10-22 16:09:32 -07:00
committed by GitHub
parent 334b8eaa57
commit 907fc72db4
5 changed files with 93 additions and 17 deletions

View File

@@ -3656,8 +3656,8 @@ namespace ts {
if (exportEquals !== moduleSymbol) {
const type = getTypeOfSymbol(exportEquals);
if (shouldTreatPropertiesOfExternalModuleAsExports(type)) {
getPropertiesOfType(type).forEach(symbol => {
cb(symbol, symbol.escapedName);
forEachPropertyOfType(type, (symbol, escapedName) => {
cb(symbol, escapedName);
});
}
}
@@ -4033,13 +4033,17 @@ namespace ts {
function getNamedMembers(members: SymbolTable): Symbol[] {
let result: Symbol[] | undefined;
members.forEach((symbol, id) => {
if (!isReservedMemberName(id) && symbolIsValue(symbol)) {
if (isNamedMember(symbol, id)) {
(result || (result = [])).push(symbol);
}
});
return result || emptyArray;
}
function isNamedMember(member: Symbol, escapedName: __String) {
return !isReservedMemberName(escapedName) && symbolIsValue(member);
}
function getNamedOrIndexSignatureMembers(members: SymbolTable): Symbol[] {
const result = getNamedMembers(members);
const index = getIndexSymbolFromSymbolTable(members);
@@ -11571,6 +11575,17 @@ namespace ts {
getPropertiesOfObjectType(type);
}
function forEachPropertyOfType(type: Type, action: (symbol: Symbol, escapedName: __String) => void): void {
type = getReducedApparentType(type);
if (type.flags & TypeFlags.StructuredType) {
resolveStructuredTypeMembers(type as StructuredType).members.forEach((symbol, escapedName) => {
if (isNamedMember(symbol, escapedName)) {
action(symbol, escapedName);
}
});
}
}
function isTypeInvalidDueToUnionDiscriminant(contextualType: Type, obj: ObjectLiteralExpression | JsxAttributes): boolean {
const list = obj.properties as NodeArray<ObjectLiteralElementLike | JsxAttributeLike>;
return list.some(property => {

View File

@@ -268,7 +268,7 @@ namespace ts.codefix {
}
export function getImportCompletionAction(
exportedSymbol: Symbol,
targetSymbol: Symbol,
moduleSymbol: Symbol,
sourceFile: SourceFile,
symbolName: string,
@@ -280,8 +280,8 @@ namespace ts.codefix {
): { readonly moduleSpecifier: string, readonly codeAction: CodeAction } {
const compilerOptions = program.getCompilerOptions();
const exportInfos = pathIsBareSpecifier(stripQuotes(moduleSymbol.name))
? [getSymbolExportInfoForSymbol(exportedSymbol, moduleSymbol, program, host)]
: getAllReExportingModules(sourceFile, exportedSymbol, moduleSymbol, symbolName, host, program, preferences, /*useAutoImportProvider*/ true);
? [getSymbolExportInfoForSymbol(targetSymbol, moduleSymbol, program, host)]
: getAllReExportingModules(sourceFile, targetSymbol, moduleSymbol, symbolName, host, program, preferences, /*useAutoImportProvider*/ true);
const useRequire = shouldUseRequire(sourceFile, program);
const isValidTypeOnlyUseSite = isValidTypeOnlyAliasUseSite(getTokenAtPosition(sourceFile, position));
const fix = Debug.checkDefined(getImportFixForSymbol(sourceFile, exportInfos, moduleSymbol, symbolName, program, position, isValidTypeOnlyUseSite, useRequire, host, preferences));
@@ -326,7 +326,7 @@ namespace ts.codefix {
}
}
function getAllReExportingModules(importingFile: SourceFile, exportedSymbol: Symbol, exportingModuleSymbol: Symbol, symbolName: string, host: LanguageServiceHost, program: Program, preferences: UserPreferences, useAutoImportProvider: boolean): readonly SymbolExportInfo[] {
function getAllReExportingModules(importingFile: SourceFile, targetSymbol: Symbol, exportingModuleSymbol: Symbol, symbolName: string, host: LanguageServiceHost, program: Program, preferences: UserPreferences, useAutoImportProvider: boolean): readonly SymbolExportInfo[] {
const result: SymbolExportInfo[] = [];
const compilerOptions = program.getCompilerOptions();
const getModuleSpecifierResolutionHost = memoizeOne((isFromPackageJson: boolean) => {
@@ -341,12 +341,12 @@ namespace ts.codefix {
}
const defaultInfo = getDefaultLikeExportInfo(moduleSymbol, checker, compilerOptions);
if (defaultInfo && (defaultInfo.name === symbolName || moduleSymbolToValidIdentifier(moduleSymbol, getEmitScriptTarget(compilerOptions)) === symbolName) && skipAlias(defaultInfo.symbol, checker) === exportedSymbol && isImportable(program, moduleFile, isFromPackageJson)) {
if (defaultInfo && (defaultInfo.name === symbolName || moduleSymbolToValidIdentifier(moduleSymbol, getEmitScriptTarget(compilerOptions)) === symbolName) && skipAlias(defaultInfo.symbol, checker) === targetSymbol && isImportable(program, moduleFile, isFromPackageJson)) {
result.push({ symbol: defaultInfo.symbol, moduleSymbol, moduleFileName: moduleFile?.fileName, exportKind: defaultInfo.exportKind, targetFlags: skipAlias(defaultInfo.symbol, checker).flags, isFromPackageJson });
}
for (const exported of checker.getExportsAndPropertiesOfModule(moduleSymbol)) {
if (exported.name === symbolName && skipAlias(exported, checker) === exportedSymbol && isImportable(program, moduleFile, isFromPackageJson)) {
if (exported.name === symbolName && checker.getMergedSymbol(skipAlias(exported, checker)) === targetSymbol && isImportable(program, moduleFile, isFromPackageJson)) {
result.push({ symbol: exported, moduleSymbol, moduleFileName: moduleFile?.fileName, exportKind: ExportKind.Named, targetFlags: skipAlias(exported, checker).flags, isFromPackageJson });
}
}

View File

@@ -1183,9 +1183,9 @@ namespace ts.Completions {
const checker = origin.isFromPackageJson ? host.getPackageJsonAutoImportProvider!()!.getTypeChecker() : program.getTypeChecker();
const { moduleSymbol } = origin;
const exportedSymbol = checker.getMergedSymbol(skipAlias(symbol.exportSymbol || symbol, checker));
const targetSymbol = checker.getMergedSymbol(skipAlias(symbol.exportSymbol || symbol, checker));
const { moduleSpecifier, codeAction } = codefix.getImportCompletionAction(
exportedSymbol,
targetSymbol,
moduleSymbol,
sourceFile,
getNameForExportedSymbol(symbol, getEmitScriptTarget(compilerOptions)),

View File

@@ -79,10 +79,14 @@ namespace ts {
}
const isDefault = exportKind === ExportKind.Default;
const namedSymbol = isDefault && getLocalSymbolForExportDefault(symbol) || symbol;
// A re-export merged with an export from a module augmentation can result in `symbol`
// being an external module symbol; the name it is re-exported by will be `symbolTableKey`
// (which comes from the keys of `moduleSymbol.exports`.)
const importedName = isExternalModuleSymbol(namedSymbol)
// 1. A named export must be imported by its key in `moduleSymbol.exports` or `moduleSymbol.members`.
// 2. A re-export merged with an export from a module augmentation can result in `symbol`
// being an external module symbol; the name it is re-exported by will be `symbolTableKey`
// (which comes from the keys of `moduleSymbol.exports`.)
// 3. Otherwise, we have a default/namespace import that can be imported by any name, and
// `symbolTableKey` will be something undesirable like `export=` or `default`, so we try to
// get a better name.
const importedName = exportKind === ExportKind.Named || isExternalModuleSymbol(namedSymbol)
? unescapeLeadingUnderscores(symbolTableKey)
: getNameForExportedSymbol(namedSymbol, scriptTarget);
const moduleName = stripQuotes(moduleSymbol.name);
@@ -321,7 +325,7 @@ namespace ts {
let moduleCount = 0;
forEachExternalModuleToImportFrom(program, host, /*useAutoImportProvider*/ true, (moduleSymbol, moduleFile, program, isFromPackageJson) => {
if (++moduleCount % 100 === 0) cancellationToken?.throwIfCancellationRequested();
const seenExports = new Map<Symbol, true>();
const seenExports = new Map<__String, true>();
const checker = program.getTypeChecker();
const defaultInfo = getDefaultLikeExportInfo(moduleSymbol, checker, compilerOptions);
// Note: I think we shouldn't actually see resolved module symbols here, but weird merges
@@ -339,7 +343,7 @@ namespace ts {
checker);
}
checker.forEachExportAndPropertyOfModule(moduleSymbol, (exported, key) => {
if (exported !== defaultInfo?.symbol && isImportableSymbol(exported, checker) && addToSeen(seenExports, exported)) {
if (exported !== defaultInfo?.symbol && isImportableSymbol(exported, checker) && addToSeen(seenExports, key)) {
cache.add(
importingFile.path,
exported,