Skip to content

Commit 6ec13bb

Browse files
author
Andy
authored
Use getAllSuperTypeNodes in more places (microsoft#22718)
1 parent cb9f436 commit 6ec13bb

File tree

5 files changed

+61
-147
lines changed

5 files changed

+61
-147
lines changed

src/compiler/utilities.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2111,6 +2111,13 @@ namespace ts {
21112111
return heritageClause ? heritageClause.types : undefined;
21122112
}
21132113

2114+
/** Returns the node in an `extends` or `implements` clause of a class or interface. */
2115+
export function getAllSuperTypeNodes(node: Node): ReadonlyArray<TypeNode> {
2116+
return isInterfaceDeclaration(node) ? getInterfaceBaseTypeNodes(node) || emptyArray
2117+
: isClassLike(node) ? concatenate(singleElementArray(getClassExtendsHeritageClauseElement(node)), getClassImplementsHeritageClauseElements(node)) || emptyArray
2118+
: emptyArray;
2119+
}
2120+
21142121
export function getInterfaceBaseTypeNodes(node: InterfaceDeclaration) {
21152122
const heritageClause = getHeritageClause(node.heritageClauses, SyntaxKind.ExtendsKeyword);
21162123
return heritageClause ? heritageClause.types : undefined;

src/services/completions.ts

Lines changed: 26 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1563,43 +1563,34 @@ namespace ts.Completions {
15631563
completionKind = CompletionKind.MemberLike;
15641564
// Declaring new property/method/accessor
15651565
isNewIdentifierLocation = true;
1566-
// Has keywords for class elements
15671566
keywordFilters = isClassLike(decl) ? KeywordCompletionFilters.ClassElementKeywords : KeywordCompletionFilters.InterfaceElementKeywords;
15681567

15691568
// If you're in an interface you don't want to repeat things from super-interface. So just stop here.
15701569
if (!isClassLike(decl)) return GlobalsSearch.Success;
15711570

1572-
const baseTypeNode = getClassExtendsHeritageClauseElement(decl);
1573-
const implementsTypeNodes = getClassImplementsHeritageClauseElements(decl);
1574-
if (!baseTypeNode && !implementsTypeNodes) return GlobalsSearch.Success;
1575-
15761571
const classElement = contextToken.parent;
1577-
const classElementModifierFlags = (isClassElement(classElement) ? getModifierFlags(classElement) : ModifierFlags.None)
1578-
// If this context token is not something we are editing now, consider if this would lead to be modifier
1579-
| (!isCurrentlyEditingNode(contextToken) ? modifierToFlag(keywordForNode(contextToken)) : ModifierFlags.None);
1580-
1581-
// No member list for private methods
1582-
if (classElementModifierFlags & ModifierFlags.Private) return GlobalsSearch.Success;
1583-
1584-
let baseClassTypeToGetPropertiesFrom: Type | undefined;
1585-
if (baseTypeNode) {
1586-
baseClassTypeToGetPropertiesFrom = typeChecker.getTypeAtLocation(baseTypeNode);
1587-
if (classElementModifierFlags & ModifierFlags.Static) {
1588-
// Use static class to get property symbols from
1589-
baseClassTypeToGetPropertiesFrom = typeChecker.getTypeOfSymbolAtLocation(baseClassTypeToGetPropertiesFrom.symbol, decl);
1572+
let classElementModifierFlags = isClassElement(classElement) && getModifierFlags(classElement);
1573+
// If this is context token is not something we are editing now, consider if this would lead to be modifier
1574+
if (contextToken.kind === SyntaxKind.Identifier && !isCurrentlyEditingNode(contextToken)) {
1575+
switch (contextToken.getText()) {
1576+
case "private":
1577+
classElementModifierFlags = classElementModifierFlags | ModifierFlags.Private;
1578+
break;
1579+
case "static":
1580+
classElementModifierFlags = classElementModifierFlags | ModifierFlags.Static;
1581+
break;
15901582
}
15911583
}
15921584

1593-
const implementedInterfaceTypePropertySymbols = !implementsTypeNodes || (classElementModifierFlags & ModifierFlags.Static)
1594-
? emptyArray
1595-
: flatMap(implementsTypeNodes, typeNode => typeChecker.getPropertiesOfType(typeChecker.getTypeAtLocation(typeNode)));
1596-
1597-
// List of property symbols of base type that are not private and already implemented
1598-
symbols = filterClassMembersList(
1599-
baseClassTypeToGetPropertiesFrom ? typeChecker.getPropertiesOfType(baseClassTypeToGetPropertiesFrom) : emptyArray,
1600-
implementedInterfaceTypePropertySymbols,
1601-
decl.members,
1602-
classElementModifierFlags);
1585+
// No member list for private methods
1586+
if (!(classElementModifierFlags & ModifierFlags.Private)) {
1587+
// List of property symbols of base type that are not private and already implemented
1588+
const baseSymbols = flatMap(getAllSuperTypeNodes(decl), baseTypeNode => {
1589+
const type = typeChecker.getTypeAtLocation(baseTypeNode);
1590+
return typeChecker.getPropertiesOfType(classElementModifierFlags & ModifierFlags.Static ? typeChecker.getTypeOfSymbolAtLocation(type.symbol, decl) : type);
1591+
});
1592+
symbols = filterClassMembersList(baseSymbols, decl.members, classElementModifierFlags);
1593+
}
16031594

16041595
return GlobalsSearch.Success;
16051596
}
@@ -1967,12 +1958,8 @@ namespace ts.Completions {
19671958
*
19681959
* @returns Symbols to be suggested in an class element depending on existing memebers and symbol flags
19691960
*/
1970-
function filterClassMembersList(
1971-
baseSymbols: ReadonlyArray<Symbol>,
1972-
implementingTypeSymbols: ReadonlyArray<Symbol>,
1973-
existingMembers: ReadonlyArray<ClassElement>,
1974-
currentClassElementModifierFlags: ModifierFlags): Symbol[] {
1975-
const existingMemberNames = createUnderscoreEscapedMap<boolean>();
1961+
function filterClassMembersList(baseSymbols: ReadonlyArray<Symbol>, existingMembers: ReadonlyArray<ClassElement>, currentClassElementModifierFlags: ModifierFlags): Symbol[] {
1962+
const existingMemberNames = createUnderscoreEscapedMap<true>();
19761963
for (const m of existingMembers) {
19771964
// Ignore omitted expressions for missing members
19781965
if (m.kind !== SyntaxKind.PropertyDeclaration &&
@@ -1993,10 +1980,7 @@ namespace ts.Completions {
19931980
}
19941981

19951982
// do not filter it out if the static presence doesnt match
1996-
const mIsStatic = hasModifier(m, ModifierFlags.Static);
1997-
const currentElementIsStatic = !!(currentClassElementModifierFlags & ModifierFlags.Static);
1998-
if ((mIsStatic && !currentElementIsStatic) ||
1999-
(!mIsStatic && currentElementIsStatic)) {
1983+
if (hasModifier(m, ModifierFlags.Static) !== !!(currentClassElementModifierFlags & ModifierFlags.Static)) {
20001984
continue;
20011985
}
20021986

@@ -2006,24 +1990,10 @@ namespace ts.Completions {
20061990
}
20071991
}
20081992

2009-
const result: Symbol[] = [];
2010-
addPropertySymbols(baseSymbols, ModifierFlags.Private);
2011-
addPropertySymbols(implementingTypeSymbols, ModifierFlags.NonPublicAccessibilityModifier);
2012-
return result;
2013-
2014-
function addPropertySymbols(properties: ReadonlyArray<Symbol>, inValidModifierFlags: ModifierFlags) {
2015-
for (const property of properties) {
2016-
if (isValidProperty(property, inValidModifierFlags)) {
2017-
result.push(property);
2018-
}
2019-
}
2020-
}
2021-
2022-
function isValidProperty(propertySymbol: Symbol, inValidModifierFlags: ModifierFlags) {
2023-
return !existingMemberNames.get(propertySymbol.escapedName) &&
2024-
propertySymbol.getDeclarations() &&
2025-
!(getDeclarationModifierFlagsFromSymbol(propertySymbol) & inValidModifierFlags);
2026-
}
1993+
return baseSymbols.filter(propertySymbol =>
1994+
!existingMemberNames.has(propertySymbol.escapedName) &&
1995+
!!propertySymbol.declarations &&
1996+
!(getDeclarationModifierFlagsFromSymbol(propertySymbol) & ModifierFlags.Private));
20271997
}
20281998

20291999
/**

src/services/findAllReferences.ts

Lines changed: 23 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,63 +1200,31 @@ namespace ts.FindAllReferences.Core {
12001200
* distinction between structurally compatible implementations and explicit implementations, so we
12011201
* must use the AST.
12021202
*
1203-
* @param child A class or interface Symbol
1203+
* @param symbol A class or interface Symbol
12041204
* @param parent Another class or interface Symbol
12051205
* @param cachedResults A map of symbol id pairs (i.e. "child,parent") to booleans indicating previous results
12061206
*/
1207-
function explicitlyInheritsFrom(child: Symbol, parent: Symbol, cachedResults: Map<boolean>, checker: TypeChecker): boolean {
1208-
const parentIsInterface = parent.getFlags() & SymbolFlags.Interface;
1209-
return searchHierarchy(child);
1210-
1211-
function searchHierarchy(symbol: Symbol): boolean {
1212-
if (symbol === parent) {
1213-
return true;
1214-
}
1215-
1216-
const key = getSymbolId(symbol) + "," + getSymbolId(parent);
1217-
const cached = cachedResults.get(key);
1218-
if (cached !== undefined) {
1219-
return cached;
1220-
}
1221-
1222-
// Set the key so that we don't infinitely recurse
1223-
cachedResults.set(key, false);
1224-
1225-
const inherits = forEach(symbol.getDeclarations(), declaration => {
1226-
if (isClassLike(declaration)) {
1227-
if (parentIsInterface) {
1228-
const interfaceReferences = getClassImplementsHeritageClauseElements(declaration);
1229-
if (interfaceReferences) {
1230-
for (const typeReference of interfaceReferences) {
1231-
if (searchTypeReference(typeReference)) {
1232-
return true;
1233-
}
1234-
}
1235-
}
1236-
}
1237-
return searchTypeReference(getClassExtendsHeritageClauseElement(declaration));
1238-
}
1239-
else if (declaration.kind === SyntaxKind.InterfaceDeclaration) {
1240-
if (parentIsInterface) {
1241-
return forEach(getInterfaceBaseTypeNodes(<InterfaceDeclaration>declaration), searchTypeReference);
1242-
}
1243-
}
1244-
return false;
1245-
});
1207+
function explicitlyInheritsFrom(symbol: Symbol, parent: Symbol, cachedResults: Map<boolean>, checker: TypeChecker): boolean {
1208+
if (symbol === parent) {
1209+
return true;
1210+
}
12461211

1247-
cachedResults.set(key, inherits);
1248-
return inherits;
1212+
const key = getSymbolId(symbol) + "," + getSymbolId(parent);
1213+
const cached = cachedResults.get(key);
1214+
if (cached !== undefined) {
1215+
return cached;
12491216
}
12501217

1251-
function searchTypeReference(typeReference: ExpressionWithTypeArguments): boolean {
1252-
if (typeReference) {
1218+
// Set the key so that we don't infinitely recurse
1219+
cachedResults.set(key, false);
1220+
1221+
const inherits = symbol.declarations.some(declaration =>
1222+
getAllSuperTypeNodes(declaration).some(typeReference => {
12531223
const type = checker.getTypeAtLocation(typeReference);
1254-
if (type && type.symbol) {
1255-
return searchHierarchy(type.symbol);
1256-
}
1257-
}
1258-
return false;
1259-
}
1224+
return !!type && !!type.symbol && explicitlyInheritsFrom(type.symbol, parent, cachedResults, checker);
1225+
}));
1226+
cachedResults.set(key, inherits);
1227+
return inherits;
12601228
}
12611229

12621230
function getReferencesForSuperKeyword(superKeyword: Node): SymbolAndEntries[] {
@@ -1491,10 +1459,6 @@ namespace ts.FindAllReferences.Core {
14911459
* The value of previousIterationSymbol is undefined when the function is first called.
14921460
*/
14931461
function getPropertySymbolsFromBaseTypes(symbol: Symbol, propertyName: string, result: Push<Symbol>, previousIterationSymbolsCache: SymbolTable, checker: TypeChecker): void {
1494-
if (!symbol) {
1495-
return;
1496-
}
1497-
14981462
// If the current symbol is the same as the previous-iteration symbol, we can just return the symbol that has already been visited
14991463
// This is particularly important for the following cases, so that we do not infinitely visit the same symbol.
15001464
// For example:
@@ -1506,27 +1470,16 @@ namespace ts.FindAllReferences.Core {
15061470
// the function will add any found symbol of the property-name, then its sub-routine will call
15071471
// getPropertySymbolsFromBaseTypes again to walk up any base types to prevent revisiting already
15081472
// visited symbol, interface "C", the sub-routine will pass the current symbol as previousIterationSymbol.
1509-
if (previousIterationSymbolsCache.has(symbol.escapedName)) {
1473+
if (!symbol || previousIterationSymbolsCache.has(symbol.escapedName)) {
15101474
return;
15111475
}
15121476

15131477
if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
1514-
forEach(symbol.getDeclarations(), declaration => {
1515-
if (isClassLike(declaration)) {
1516-
getPropertySymbolFromTypeReference(getClassExtendsHeritageClauseElement(<ClassDeclaration>declaration));
1517-
forEach(getClassImplementsHeritageClauseElements(<ClassDeclaration>declaration), getPropertySymbolFromTypeReference);
1518-
}
1519-
else if (declaration.kind === SyntaxKind.InterfaceDeclaration) {
1520-
forEach(getInterfaceBaseTypeNodes(<InterfaceDeclaration>declaration), getPropertySymbolFromTypeReference);
1521-
}
1522-
});
1523-
}
1524-
return;
1478+
for (const declaration of symbol.declarations) {
1479+
for (const typeReference of getAllSuperTypeNodes(declaration)) {
1480+
const type = checker.getTypeAtLocation(typeReference);
1481+
if (!type) continue;
15251482

1526-
function getPropertySymbolFromTypeReference(typeReference: ExpressionWithTypeArguments): void {
1527-
if (typeReference) {
1528-
const type = checker.getTypeAtLocation(typeReference);
1529-
if (type) {
15301483
const propertySymbol = checker.getPropertyOfType(type, propertyName);
15311484
if (propertySymbol) {
15321485
result.push(...checker.getRootSymbols(propertySymbol));

src/services/services.ts

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,7 @@ namespace ts {
576576
*/
577577
function findInheritedJSDocComments(declaration: Declaration, propertyName: string, typeChecker: TypeChecker): SymbolDisplayPart[] {
578578
let foundDocs = false;
579-
return flatMap(getAllSuperTypeNodes(declaration), superTypeNode => {
579+
return flatMap(declaration.parent ? getAllSuperTypeNodes(declaration.parent) : emptyArray, superTypeNode => {
580580
if (foundDocs) {
581581
return emptyArray;
582582
}
@@ -594,21 +594,6 @@ namespace ts {
594594
});
595595
}
596596

597-
/**
598-
* Finds and returns the `TypeNode` for all super classes and implemented interfaces given a declaration.
599-
* @param declaration The possibly-inherited declaration.
600-
* @returns A filled array of `TypeNode`s containing all super classes and implemented interfaces if any exist, otherwise an empty array.
601-
*/
602-
function getAllSuperTypeNodes(declaration: Declaration): ReadonlyArray<TypeNode> {
603-
const container = declaration.parent;
604-
if (!container || (!isClassDeclaration(container) && !isInterfaceDeclaration(container))) {
605-
return emptyArray;
606-
}
607-
const extended = getClassExtendsHeritageClauseElement(container);
608-
const types = extended ? [extended] : emptyArray;
609-
return isClassLike(container) ? concatenate(types, getClassImplementsHeritageClauseElements(container)) : types;
610-
}
611-
612597
class SourceFileObject extends NodeObject implements SourceFile {
613598
public kind: SyntaxKind.SourceFile;
614599
public _declarationBrand: any;

tests/cases/fourslash/completionEntryForClassMembers2.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ verifyClassElementLocations(
350350
value => value[0] !== "getValue"),
351351
["extendsBAndImplementsI3WithSameNameMembersAndHasImplementedTheMember"]);
352352

353-
const invalidMembersOfB0AtInstanceSide = privateMembersOfBaseClassB0.concat(validStaticMembersOfBaseClassB0);
353+
const invalidMembersOfB0AtInstanceSide = privateMembersOfBaseClassB0.concat(validStaticMembersOfBaseClassB0);
354354
const invalidMembersOfB0AtStaticSide = privateMembersOfBaseClassB0.concat(validInstanceMembersOfBaseClassB0);
355355
// members of B0 and members of I4
356356
verifyClassElementLocations({
@@ -403,19 +403,18 @@ verifyClassElementLocations({ validMembers: membersOfI7, invalidMembers: noMembe
403403
"implementsIAndAlsoImplementsI7whichExtendsI"
404404
]);
405405

406-
const invalidMembersOfB0AtInstanceSideFromInterfaceExtendingB0 = invalidMembersOfB0AtInstanceSide
407-
.concat(protectedPropertiesOfBaseClassB0);
406+
const invalidMembersOfB0AtInstanceSideFromInterfaceExtendingB0 = invalidMembersOfB0AtInstanceSide;
408407
// members of I5 extends B0
409408
verifyClassElementLocations({
410-
validMembers: membersOfI5,
409+
validMembers: membersOfI5.concat(protectedPropertiesOfBaseClassB0),
411410
invalidMembers: invalidMembersOfB0AtInstanceSideFromInterfaceExtendingB0
412411
}, [
413412
"implementsI5ThatExtendsB0",
414413
]);
415414

416415
// members of I6 extends B0
417416
verifyClassElementLocations({
418-
validMembers: membersOfI6,
417+
validMembers: membersOfI6.concat(protectedPropertiesOfBaseClassB0),
419418
invalidMembers: invalidMembersOfB0AtInstanceSideFromInterfaceExtendingB0
420419
}, [
421420
"implementsI6ThatExtendsB0AndHasStaticMethodOfB0",

0 commit comments

Comments
 (0)