Skip to content

Commit

Permalink
findAllReferences: Be consistent how we handle unions in root symobls (
Browse files Browse the repository at this point in the history
  • Loading branch information
Andy authored Apr 5, 2018
1 parent 4f80fbd commit f61f126
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 25 deletions.
33 changes: 13 additions & 20 deletions src/services/findAllReferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1535,32 +1535,25 @@ namespace ts.FindAllReferences.Core {
function findRootSymbol(sym: Symbol): Symbol | undefined {
// Unwrap symbols to get to the root (e.g. transient symbols as a result of widening)
// Or a union property, use its underlying unioned symbols
return firstDefined(checker.getRootSymbols(sym), rootSymbol => {
// if it is in the list, then we are done
if (search.includes(rootSymbol)) {
return firstDefined(checker.getRootSymbols(sym), rootSymbol =>
isMatchingRootSymbol(search, rootSymbol, state.inheritsFromCache, checker)
// For a root symbol that is a component of a union or intersection, use the original (union/intersection) symbol.
// That we when a symbol references the whole union we avoid claiming it references some particular member of the union.
// For a transient symbol we want to use the root symbol instead.
return getCheckFlags(sym) & CheckFlags.Synthetic ? sym : rootSymbol;
}

// Finally, try all properties with the same name in any type the containing type extended or implemented, and
// see if any is in the list. If we were passed a parent symbol, only include types that are subtypes of the
// parent symbol
if (rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
// Parents will only be defined if implementations is true
if (search.parents && !some(search.parents, parent => explicitlyInheritsFrom(rootSymbol.parent, parent, state.inheritsFromCache, checker))) {
return undefined;
}

return getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.name, checker).some(search.includes) ? rootSymbol : undefined;
}

return undefined;
});
? getCheckFlags(sym) & CheckFlags.Synthetic ? sym : rootSymbol
: undefined);
}
}

function isMatchingRootSymbol(search: Search, rootSymbol: Symbol, inheritsFromCache: Map<boolean>, checker: TypeChecker): boolean {
return search.includes(rootSymbol)
|| rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)
// If we were passed a parent symbol (if 'implementations' set), only include types that are subtypes of the parent symbol.
&& !(search.parents && !search.parents.some(parent => explicitlyInheritsFrom(rootSymbol.parent, parent, inheritsFromCache, checker)))
// Try all properties with the same name in any type the containing type extended or implemented.
&& getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.name, checker).some(search.includes);
}

/** Gets all symbols for one property. Does not get symbols for every property. */
function getPropertySymbolsFromContextualType(node: ObjectLiteralElement, checker: TypeChecker): ReadonlyArray<Symbol> {
const contextualType = checker.getContextualType(<ObjectLiteralExpression>node.parent);
Expand Down
6 changes: 1 addition & 5 deletions tests/cases/fourslash/referencesForUnionProperties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,7 @@ verify.referenceGroups(one, [
{ definition: "(property) a: number", ranges: [one] },
{ definition: "(property) a: string | number", ranges: [x] },
]);
verify.referenceGroups(base, [
{ definition: "(property) Base.a: string", ranges: [base] },
{ definition: "(property) HasAOrB.a: string", ranges: [hasAOrB, x] },
]);
verify.referenceGroups(hasAOrB, [
verify.referenceGroups([base, hasAOrB], [
{ definition: "(property) Base.a: string", ranges: [base] },
{ definition: "(property) HasAOrB.a: string", ranges: [hasAOrB] },
{ definition: "(property) a: string | number", ranges: [x] },
Expand Down

0 comments on commit f61f126

Please sign in to comment.