Skip to content

Commit bbb98cf

Browse files
authored
fix(52664): Assertion failure on completions for derived class with computed base property name (#52673)
1 parent 0463766 commit bbb98cf

File tree

6 files changed

+314
-27
lines changed

6 files changed

+314
-27
lines changed

src/compiler/checker.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42817,17 +42817,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4281742817
* Checks a member declaration node to see if has a missing or invalid `override` modifier.
4281842818
* @param node Class-like node where the member is declared.
4281942819
* @param member Member declaration node.
42820+
* @param memberSymbol Member symbol.
4282042821
* Note: `member` can be a synthetic node without a parent.
4282142822
*/
42822-
function getMemberOverrideModifierStatus(node: ClassLikeDeclaration, member: ClassElement): MemberOverrideStatus {
42823+
function getMemberOverrideModifierStatus(node: ClassLikeDeclaration, member: ClassElement, memberSymbol: Symbol): MemberOverrideStatus {
4282342824
if (!member.name) {
4282442825
return MemberOverrideStatus.Ok;
4282542826
}
4282642827

42827-
const symbol = getSymbolOfDeclaration(node);
42828-
const type = getDeclaredTypeOfSymbol(symbol) as InterfaceType;
42828+
const classSymbol = getSymbolOfDeclaration(node);
42829+
const type = getDeclaredTypeOfSymbol(classSymbol) as InterfaceType;
4282942830
const typeWithThis = getTypeWithThisArgument(type);
42830-
const staticType = getTypeOfSymbol(symbol) as ObjectType;
42831+
const staticType = getTypeOfSymbol(classSymbol) as ObjectType;
4283142832

4283242833
const baseTypeNode = getEffectiveBaseTypeNode(node);
4283342834
const baseTypes = baseTypeNode && getBaseTypes(type);
@@ -42838,8 +42839,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4283842839
? hasOverrideModifier(member)
4283942840
: hasSyntacticModifier(member, ModifierFlags.Override);
4284042841

42841-
const memberName = unescapeLeadingUnderscores(getTextOfPropertyName(member.name));
42842-
4284342842
return checkMemberForOverrideModifier(
4284442843
node,
4284542844
staticType,
@@ -42851,7 +42850,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4285142850
hasAbstractModifier(member),
4285242851
isStatic(member),
4285342852
/* memberIsParameterProperty */ false,
42854-
memberName,
42853+
symbolName(memberSymbol),
4285542854
);
4285642855
}
4285742856

src/compiler/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5238,7 +5238,7 @@ export interface TypeChecker {
52385238
/** @internal */ isDeclarationVisible(node: Declaration | AnyImportSyntax): boolean;
52395239
/** @internal */ isPropertyAccessible(node: Node, isSuper: boolean, isWrite: boolean, containingType: Type, property: Symbol): boolean;
52405240
/** @internal */ getTypeOnlyAliasDeclaration(symbol: Symbol): TypeOnlyAliasDeclaration | undefined;
5241-
/** @internal */ getMemberOverrideModifierStatus(node: ClassLikeDeclaration, member: ClassElement): MemberOverrideStatus;
5241+
/** @internal */ getMemberOverrideModifierStatus(node: ClassLikeDeclaration, member: ClassElement, memberSymbol: Symbol): MemberOverrideStatus;
52425242
/** @internal */ isTypeParameterPossiblyReferenced(tp: TypeParameter, node: Node): boolean;
52435243
}
52445244

src/services/completions.ts

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ import {
6262
first,
6363
firstDefined,
6464
flatMap,
65+
forEach,
6566
formatting,
6667
FunctionLikeDeclaration,
6768
getAllSuperTypeNodes,
@@ -427,15 +428,16 @@ export enum CompletionSource {
427428

428429
/** @internal */
429430
export const enum SymbolOriginInfoKind {
430-
ThisType = 1 << 0,
431-
SymbolMember = 1 << 1,
432-
Export = 1 << 2,
433-
Promise = 1 << 3,
434-
Nullable = 1 << 4,
435-
ResolvedExport = 1 << 5,
436-
TypeOnlyAlias = 1 << 6,
437-
ObjectLiteralMethod = 1 << 7,
438-
Ignore = 1 << 8,
431+
ThisType = 1 << 0,
432+
SymbolMember = 1 << 1,
433+
Export = 1 << 2,
434+
Promise = 1 << 3,
435+
Nullable = 1 << 4,
436+
ResolvedExport = 1 << 5,
437+
TypeOnlyAlias = 1 << 6,
438+
ObjectLiteralMethod = 1 << 7,
439+
Ignore = 1 << 8,
440+
ComputedPropertyName = 1 << 9,
439441

440442
SymbolMemberNoExport = SymbolMember,
441443
SymbolMemberExport = SymbolMember | Export,
@@ -475,6 +477,10 @@ interface SymbolOriginInfoObjectLiteralMethod extends SymbolOriginInfo {
475477
isSnippet?: true,
476478
}
477479

480+
interface SymbolOriginInfoComputedPropertyName extends SymbolOriginInfo {
481+
symbolName: string;
482+
}
483+
478484
function originIsThisType(origin: SymbolOriginInfo): boolean {
479485
return !!(origin.kind & SymbolOriginInfoKind.ThisType);
480486
}
@@ -491,8 +497,8 @@ function originIsResolvedExport(origin: SymbolOriginInfo | undefined): origin is
491497
return !!(origin && origin.kind === SymbolOriginInfoKind.ResolvedExport);
492498
}
493499

494-
function originIncludesSymbolName(origin: SymbolOriginInfo | undefined): origin is SymbolOriginInfoExport | SymbolOriginInfoResolvedExport {
495-
return originIsExport(origin) || originIsResolvedExport(origin);
500+
function originIncludesSymbolName(origin: SymbolOriginInfo | undefined): origin is SymbolOriginInfoExport | SymbolOriginInfoResolvedExport | SymbolOriginInfoComputedPropertyName {
501+
return originIsExport(origin) || originIsResolvedExport(origin) || originIsComputedPropertyName(origin);
496502
}
497503

498504
function originIsPackageJsonImport(origin: SymbolOriginInfo | undefined): origin is SymbolOriginInfoExport {
@@ -519,6 +525,10 @@ function originIsIgnore(origin: SymbolOriginInfo | undefined): boolean {
519525
return !!(origin && origin.kind & SymbolOriginInfoKind.Ignore);
520526
}
521527

528+
function originIsComputedPropertyName(origin: SymbolOriginInfo | undefined): origin is SymbolOriginInfoComputedPropertyName {
529+
return !!(origin && origin.kind & SymbolOriginInfoKind.ComputedPropertyName);
530+
}
531+
522532
/** @internal */
523533
export interface UniqueNameSet {
524534
add(name: string): void;
@@ -1552,10 +1562,9 @@ function getEntryForMemberCompletion(
15521562
requiredModifiers |= ModifierFlags.Abstract;
15531563
}
15541564
if (isClassElement(node)
1555-
&& checker.getMemberOverrideModifierStatus(classLikeDeclaration, node) === MemberOverrideStatus.NeedsOverride) {
1565+
&& checker.getMemberOverrideModifierStatus(classLikeDeclaration, node, symbol) === MemberOverrideStatus.NeedsOverride) {
15561566
requiredModifiers |= ModifierFlags.Override;
15571567
}
1558-
15591568
if (!completionNodes.length) {
15601569
// Keep track of added missing required modifiers and modifiers already present.
15611570
// This is needed when we have overloaded signatures,
@@ -2322,7 +2331,8 @@ export function getCompletionEntryDetails(
23222331
case "symbol": {
23232332
const { symbol, location, contextToken, origin, previousToken } = symbolCompletion;
23242333
const { codeActions, sourceDisplay } = getCompletionEntryCodeActionsAndSourceDisplay(name, location, contextToken, origin, symbol, program, host, compilerOptions, sourceFile, position, previousToken, formatContext, preferences, data, source, cancellationToken);
2325-
return createCompletionDetailsForSymbol(symbol, typeChecker, sourceFile, location, cancellationToken, codeActions, sourceDisplay); // TODO: GH#18217
2334+
const symbolName = originIsComputedPropertyName(origin) ? origin.symbolName : symbol.name;
2335+
return createCompletionDetailsForSymbol(symbol, symbolName, typeChecker, sourceFile, location, cancellationToken, codeActions, sourceDisplay); // TODO: GH#18217
23262336
}
23272337
case "literal": {
23282338
const { literal } = symbolCompletion;
@@ -2374,12 +2384,12 @@ function createSimpleDetails(name: string, kind: ScriptElementKind, kind2: Symbo
23742384
}
23752385

23762386
/** @internal */
2377-
export function createCompletionDetailsForSymbol(symbol: Symbol, checker: TypeChecker, sourceFile: SourceFile, location: Node, cancellationToken: CancellationToken, codeActions?: CodeAction[], sourceDisplay?: SymbolDisplayPart[]): CompletionEntryDetails {
2387+
export function createCompletionDetailsForSymbol(symbol: Symbol, name: string, checker: TypeChecker, sourceFile: SourceFile, location: Node, cancellationToken: CancellationToken, codeActions?: CodeAction[], sourceDisplay?: SymbolDisplayPart[]): CompletionEntryDetails {
23782388
const { displayParts, documentation, symbolKind, tags } =
23792389
checker.runWithCancellationToken(cancellationToken, checker =>
23802390
SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(checker, symbol, sourceFile, location, location, SemanticMeaning.All)
23812391
);
2382-
return createCompletionDetails(symbol.name, SymbolDisplay.getSymbolModifiers(checker, symbol), symbolKind, displayParts, documentation, tags, codeActions, sourceDisplay);
2392+
return createCompletionDetails(name, SymbolDisplay.getSymbolModifiers(checker, symbol), symbolKind, displayParts, documentation, tags, codeActions, sourceDisplay);
23832393
}
23842394

23852395
/** @internal */
@@ -3963,8 +3973,17 @@ function getCompletionData(
39633973
type && typeChecker.getPropertiesOfType(type);
39643974
});
39653975
symbols = concatenate(symbols, filterClassMembersList(baseSymbols, decl.members, classElementModifierFlags));
3976+
forEach(symbols, (symbol, index) => {
3977+
const declaration = symbol?.valueDeclaration;
3978+
if (declaration && isClassElement(declaration) && declaration.name && isComputedPropertyName(declaration.name)) {
3979+
const origin: SymbolOriginInfoComputedPropertyName = {
3980+
kind: SymbolOriginInfoKind.ComputedPropertyName,
3981+
symbolName: typeChecker.symbolToString(symbol),
3982+
};
3983+
symbolToOriginInfoMap[index] = origin;
3984+
}
3985+
});
39663986
}
3967-
39683987
return GlobalsSearch.Success;
39693988
}
39703989

@@ -4545,7 +4564,7 @@ function getCompletionEntryDisplayNameForSymbol(
45454564
}
45464565
switch (kind) {
45474566
case CompletionKind.MemberLike:
4548-
return undefined;
4567+
return originIsComputedPropertyName(origin) ? { name: origin.symbolName, needsConvertPropertyAccess: false } : undefined;
45494568
case CompletionKind.ObjectPropertyDeclaration:
45504569
// TODO: GH#18169
45514570
return { name: JSON.stringify(name), needsConvertPropertyAccess: false };

src/services/stringCompletions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ function stringLiteralCompletionDetails(name: string, location: Node, completion
287287
}
288288
case StringLiteralCompletionKind.Properties: {
289289
const match = find(completion.symbols, s => s.name === name);
290-
return match && createCompletionDetailsForSymbol(match, checker, sourceFile, location, cancellationToken);
290+
return match && createCompletionDetailsForSymbol(match, match.name, checker, sourceFile, location, cancellationToken);
291291
}
292292
case StringLiteralCompletionKind.Types:
293293
return find(completion.types, t => t.value === name) ? createCompletionDetails(name, ScriptElementKindModifier.none, ScriptElementKind.string, [textPart(name)]) : undefined;

0 commit comments

Comments
 (0)