diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index a151d776403..b903232f467 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -1438,7 +1438,7 @@ namespace ts.FindAllReferences.Core { // Add symbol of properties/methods of the same name in base classes and implemented interfaces definitions if (!implementations && rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { - getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.name, result, /*previousIterationSymbolsCache*/ createSymbolTable(), checker); + getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.name, checker, result); } } } @@ -1458,27 +1458,22 @@ namespace ts.FindAllReferences.Core { * @param previousIterationSymbolsCache a cache of symbol from previous iterations of calling this function to prevent infinite revisiting of the same symbol. * The value of previousIterationSymbol is undefined when the function is first called. */ - function getPropertySymbolsFromBaseTypes(symbol: Symbol, propertyName: string, result: Push, previousIterationSymbolsCache: SymbolTable, checker: TypeChecker): void { - // If the current symbol is the same as the previous-iteration symbol, we can just return the symbol that has already been visited - // This is particularly important for the following cases, so that we do not infinitely visit the same symbol. - // For example: - // interface C extends C { - // /*findRef*/propName: string; - // } - // The first time getPropertySymbolsFromBaseTypes is called when finding-all-references at propName, - // the symbol argument will be the symbol of an interface "C" and previousIterationSymbol is undefined, - // the function will add any found symbol of the property-name, then its sub-routine will call - // getPropertySymbolsFromBaseTypes again to walk up any base types to prevent revisiting already - // visited symbol, interface "C", the sub-routine will pass the current symbol as previousIterationSymbol. - if (!symbol || previousIterationSymbolsCache.has(symbol.escapedName)) { - return; - } + function getPropertySymbolsFromBaseTypes(symbol: Symbol, propertyName: string, checker: TypeChecker, result: Symbol[] = []): Symbol[] { + const seen = createMap(); + recur(symbol); + return result; + + function recur(symbol: Symbol): void { + // Use `addToSeen` to ensure we don't infinitely recurse in this situation: + // interface C extends C { + // /*findRef*/propName: string; + // } + if (!(symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) || !addToSeen(seen, getSymbolId(symbol))) return; - if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { for (const declaration of symbol.declarations) { for (const typeReference of getAllSuperTypeNodes(declaration)) { const type = checker.getTypeAtLocation(typeReference); - if (!type) continue; + if (!(type && type.symbol)) continue; const propertySymbol = checker.getPropertyOfType(type, propertyName); if (propertySymbol) { @@ -1486,8 +1481,7 @@ namespace ts.FindAllReferences.Core { } // Visit the typeReference as well to see if it directly or indirectly use that property - previousIterationSymbolsCache.set(symbol.escapedName, symbol); - getPropertySymbolsFromBaseTypes(type.symbol, propertyName, result, previousIterationSymbolsCache, checker); + recur(type.symbol); } } } @@ -1559,9 +1553,7 @@ namespace ts.FindAllReferences.Core { return undefined; } - const result: Symbol[] = []; - getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.name, result, /*previousIterationSymbolsCache*/ createSymbolTable(), checker); - return result.some(search.includes) ? rootSymbol : undefined; + return getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.name, checker).some(search.includes) ? rootSymbol : undefined; } return undefined; @@ -1667,20 +1659,11 @@ namespace ts.FindAllReferences.Core { */ function getParentSymbolsOfPropertyAccess(location: Node, symbol: Symbol, checker: TypeChecker): Symbol[] | undefined { const propertyAccessExpression = getPropertyAccessExpressionFromRightHandSide(location); - if (!propertyAccessExpression) { - return undefined; - } - - const localParentType = checker.getTypeAtLocation(propertyAccessExpression.expression); - if (!localParentType) { - return undefined; - } - - if (localParentType.symbol && localParentType.symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface) && localParentType.symbol !== symbol.parent) { - return [localParentType.symbol]; - } - else if (localParentType.flags & TypeFlags.UnionOrIntersection) { - return getSymbolsForClassAndInterfaceComponents(localParentType); - } + const localParentType = propertyAccessExpression && checker.getTypeAtLocation(propertyAccessExpression.expression); + return localParentType && localParentType.symbol && localParentType.symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface) && localParentType.symbol !== symbol.parent + ? [localParentType.symbol] + : localParentType && localParentType.flags & TypeFlags.UnionOrIntersection + ? getSymbolsForClassAndInterfaceComponents(localParentType) + : undefined; } }