findAllReferences: Make definition info independent of search location (#21748)

This commit is contained in:
Andy
2018-03-01 12:46:00 -08:00
committed by GitHub
parent bb05122a50
commit 0a72568e59
35 changed files with 83 additions and 177 deletions

View File

@@ -8,7 +8,7 @@ namespace ts.FindAllReferences {
}
export type Definition =
| { type: "symbol"; symbol: Symbol; node: Node }
| { type: "symbol"; symbol: Symbol }
| { type: "label"; node: Identifier }
| { type: "keyword"; node: ts.Node }
| { type: "this"; node: ts.Node }
@@ -42,11 +42,12 @@ namespace ts.FindAllReferences {
}
export function findReferencedSymbols(program: Program, cancellationToken: CancellationToken, sourceFiles: ReadonlyArray<SourceFile>, sourceFile: SourceFile, position: number): ReferencedSymbol[] | undefined {
const referencedSymbols = findAllReferencedSymbols(program, cancellationToken, sourceFiles, sourceFile, position);
const node = getTouchingPropertyName(sourceFile, position, /*includeJsDocComment*/ true);
const referencedSymbols = Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, /*options*/ {});
const checker = program.getTypeChecker();
return !referencedSymbols || !referencedSymbols.length ? undefined : mapDefined<SymbolAndEntries, ReferencedSymbol>(referencedSymbols, ({ definition, references }) =>
// Only include referenced symbols that have a valid definition.
definition && { definition: definitionToReferencedSymbolDefinitionInfo(definition, checker), references: references.map(toReferenceEntry) });
definition && { definition: definitionToReferencedSymbolDefinitionInfo(definition, checker, node), references: references.map(toReferenceEntry) });
}
export function getImplementationsAtPosition(program: Program, cancellationToken: CancellationToken, sourceFiles: ReadonlyArray<SourceFile>, sourceFile: SourceFile, position: number): ImplementationLocation[] {
@@ -83,31 +84,26 @@ namespace ts.FindAllReferences {
}
export function findReferencedEntries(program: Program, cancellationToken: CancellationToken, sourceFiles: ReadonlyArray<SourceFile>, sourceFile: SourceFile, position: number, options?: Options): ReferenceEntry[] | undefined {
const x = flattenEntries(findAllReferencedSymbols(program, cancellationToken, sourceFiles, sourceFile, position, options));
return map(x, toReferenceEntry);
const node = getTouchingPropertyName(sourceFile, position, /*includeJsDocComment*/ true);
return map(flattenEntries(Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, options)), toReferenceEntry);
}
export function getReferenceEntriesForNode(position: number, node: Node, program: Program, sourceFiles: ReadonlyArray<SourceFile>, cancellationToken: CancellationToken, options: Options = {}): Entry[] | undefined {
return flattenEntries(Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, options));
}
function findAllReferencedSymbols(program: Program, cancellationToken: CancellationToken, sourceFiles: ReadonlyArray<SourceFile>, sourceFile: SourceFile, position: number, options?: Options): SymbolAndEntries[] | undefined {
const node = getTouchingPropertyName(sourceFile, position, /*includeJsDocComment*/ true);
return Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, options);
}
function flattenEntries(referenceSymbols: SymbolAndEntries[]): Entry[] {
return referenceSymbols && flatMap(referenceSymbols, r => r.references);
}
function definitionToReferencedSymbolDefinitionInfo(def: Definition, checker: TypeChecker): ReferencedSymbolDefinitionInfo | undefined {
function definitionToReferencedSymbolDefinitionInfo(def: Definition, checker: TypeChecker, originalNode: Node): ReferencedSymbolDefinitionInfo | undefined {
const info = (() => {
switch (def.type) {
case "symbol": {
const { symbol, node } = def;
const { displayParts, kind } = getDefinitionKindAndDisplayParts(symbol, node, checker);
const { symbol } = def;
const { displayParts, kind } = getDefinitionKindAndDisplayParts(symbol, checker, originalNode);
const name = displayParts.map(p => p.text).join("");
return { node, name, kind, displayParts };
return { node: symbol.declarations ? getNameOfDeclaration(first(symbol.declarations)) || first(symbol.declarations) : originalNode, name, kind, displayParts };
}
case "label": {
const { node } = def;
@@ -129,13 +125,11 @@ namespace ts.FindAllReferences {
const { node } = def;
return { node, name: node.text, kind: ScriptElementKind.variableElement, displayParts: [displayPart(getTextOfNode(node), SymbolDisplayPartKind.stringLiteral)] };
}
default:
return Debug.assertNever(def);
}
})();
if (!info) {
return undefined;
}
const { node, name, kind, displayParts } = info;
const sourceFile = node.getSourceFile();
return {
@@ -149,9 +143,11 @@ namespace ts.FindAllReferences {
};
}
function getDefinitionKindAndDisplayParts(symbol: Symbol, node: Node, checker: TypeChecker): { displayParts: SymbolDisplayPart[], kind: ScriptElementKind } {
function getDefinitionKindAndDisplayParts(symbol: Symbol, checker: TypeChecker, node: Node): { displayParts: SymbolDisplayPart[], kind: ScriptElementKind } {
const meaning = Core.getIntersectingMeaningFromDeclarations(node, symbol);
const enclosingDeclaration = firstOrUndefined(symbol.declarations) || node;
const { displayParts, symbolKind } =
SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(checker, symbol, node.getSourceFile(), getContainerNode(node), node);
SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(checker, symbol, enclosingDeclaration.getSourceFile(), enclosingDeclaration, enclosingDeclaration, meaning);
return { displayParts, kind: symbolKind };
}
@@ -186,7 +182,7 @@ namespace ts.FindAllReferences {
function implementationKindDisplayParts(node: ts.Node, checker: ts.TypeChecker): { kind: ScriptElementKind, displayParts: SymbolDisplayPart[] } {
const symbol = checker.getSymbolAtLocation(isDeclaration(node) && node.name ? node.name : node);
if (symbol) {
return getDefinitionKindAndDisplayParts(symbol, node, checker);
return getDefinitionKindAndDisplayParts(symbol, checker, node);
}
else if (node.kind === SyntaxKind.ObjectLiteralExpression) {
return {
@@ -317,10 +313,7 @@ namespace ts.FindAllReferences.Core {
}
}
return [{
definition: { type: "symbol", symbol, node: symbol.valueDeclaration },
references
}];
return [{ definition: { type: "symbol", symbol }, references }];
}
/** getReferencedSymbols for special node kinds. */
@@ -357,13 +350,13 @@ namespace ts.FindAllReferences.Core {
symbol = skipPastExportOrImportSpecifierOrUnion(symbol, node, checker) || symbol;
// Compute the meaning from the location and the symbol it references
const searchMeaning = getIntersectingMeaningFromDeclarations(getMeaningFromLocation(node), symbol.declarations);
const searchMeaning = getIntersectingMeaningFromDeclarations(node, symbol);
const result: SymbolAndEntries[] = [];
const state = new State(sourceFiles, getSpecialSearchKind(node), checker, cancellationToken, searchMeaning, options, result);
if (node.kind === SyntaxKind.DefaultKeyword) {
addReference(node, symbol, node, state);
addReference(node, symbol, state);
searchForImportsOfExport(node, symbol, { exportingModuleSymbol: Debug.assertDefined(symbol.parent, "Expected export symbol to have a parent"), exportKind: ExportKind.Default }, state);
}
else {
@@ -434,7 +427,6 @@ namespace ts.FindAllReferences.Core {
/** If coming from an export, we will not recursively search for the imported symbol (since that's where we came from). */
readonly comingFrom?: ImportExport;
readonly location: Node;
readonly symbol: Symbol;
readonly text: string;
readonly escapedText: __String;
@@ -514,7 +506,7 @@ namespace ts.FindAllReferences.Core {
const escapedText = escapeLeadingUnderscores(text);
const parents = this.options.implementations && getParentSymbolsOfPropertyAccess(location, symbol, this.checker);
return {
location, symbol, comingFrom, text, escapedText, parents,
symbol, comingFrom, text, escapedText, parents,
includes: referenceSymbol => allSearchSymbols ? contains(allSearchSymbols, referenceSymbol) : referenceSymbol === symbol,
};
}
@@ -524,12 +516,12 @@ namespace ts.FindAllReferences.Core {
* Callback to add references for a particular searched symbol.
* This initializes a reference group, so only call this if you will add at least one reference.
*/
referenceAdder(searchSymbol: Symbol, searchLocation: Node): (node: Node) => void {
referenceAdder(searchSymbol: Symbol): (node: Node) => void {
const symbolId = getSymbolId(searchSymbol);
let references = this.symbolIdToReferences[symbolId];
if (!references) {
references = this.symbolIdToReferences[symbolId] = [];
this.result.push({ definition: { type: "symbol", symbol: searchSymbol, node: searchLocation }, references });
this.result.push({ definition: { type: "symbol", symbol: searchSymbol }, references });
}
return node => references.push(nodeEntry(node));
}
@@ -559,7 +551,7 @@ namespace ts.FindAllReferences.Core {
// For `import { foo as bar }` just add the reference to `foo`, and don't otherwise search in the file.
if (singleReferences.length) {
const addRef = state.referenceAdder(exportSymbol, exportLocation);
const addRef = state.referenceAdder(exportSymbol);
for (const singleRef of singleReferences) {
addRef(singleRef);
}
@@ -862,7 +854,7 @@ namespace ts.FindAllReferences.Core {
switch (state.specialSearchKind) {
case SpecialSearchKind.None:
addReference(referenceLocation, relatedSymbol, search.location, state);
addReference(referenceLocation, relatedSymbol, state);
break;
case SpecialSearchKind.Constructor:
addConstructorReferences(referenceLocation, sourceFile, search, state);
@@ -896,7 +888,7 @@ namespace ts.FindAllReferences.Core {
}
if (!state.options.isForRename && state.markSeenReExportRHS(name)) {
addReference(name, referenceSymbol, name, state);
addReference(name, referenceSymbol, state);
}
}
else {
@@ -920,7 +912,7 @@ namespace ts.FindAllReferences.Core {
}
function addRef() {
addReference(referenceLocation, localSymbol, search.location, state);
addReference(referenceLocation, localSymbol, state);
}
}
@@ -969,12 +961,12 @@ namespace ts.FindAllReferences.Core {
* position of property accessing, the referenceEntry of such position will be handled in the first case.
*/
if (!(flags & SymbolFlags.Transient) && search.includes(shorthandValueSymbol)) {
addReference(getNameOfDeclaration(valueDeclaration), shorthandValueSymbol, search.location, state);
addReference(getNameOfDeclaration(valueDeclaration), shorthandValueSymbol, state);
}
}
function addReference(referenceLocation: Node, relatedSymbol: Symbol, searchLocation: Node, state: State): void {
const addRef = state.referenceAdder(relatedSymbol, searchLocation);
function addReference(referenceLocation: Node, relatedSymbol: Symbol, state: State): void {
const addRef = state.referenceAdder(relatedSymbol);
if (state.options.implementations) {
addImplementationReferences(referenceLocation, addRef, state);
}
@@ -986,10 +978,10 @@ namespace ts.FindAllReferences.Core {
/** Adds references when a constructor is used with `new this()` in its own class and `super()` calls in subclasses. */
function addConstructorReferences(referenceLocation: Node, sourceFile: SourceFile, search: Search, state: State): void {
if (isNewExpressionTarget(referenceLocation)) {
addReference(referenceLocation, search.symbol, search.location, state);
addReference(referenceLocation, search.symbol, state);
}
const pusher = () => state.referenceAdder(search.symbol, search.location);
const pusher = () => state.referenceAdder(search.symbol);
if (isClassLike(referenceLocation.parent)) {
Debug.assert(referenceLocation.kind === SyntaxKind.DefaultKeyword || referenceLocation.parent.name === referenceLocation);
@@ -1006,11 +998,11 @@ namespace ts.FindAllReferences.Core {
}
function addClassStaticThisReferences(referenceLocation: Node, search: Search, state: State): void {
addReference(referenceLocation, search.symbol, search.location, state);
addReference(referenceLocation, search.symbol, state);
if (isClassLike(referenceLocation.parent)) {
Debug.assert(referenceLocation.parent.name === referenceLocation);
// This is the class declaration.
addStaticThisReferences(referenceLocation.parent, state.referenceAdder(search.symbol, search.location));
addStaticThisReferences(referenceLocation.parent, state.referenceAdder(search.symbol));
}
}
@@ -1300,7 +1292,7 @@ namespace ts.FindAllReferences.Core {
return container && (ModifierFlags.Static & getModifierFlags(container)) === staticFlag && container.parent.symbol === searchSpaceNode.symbol ? nodeEntry(node) : undefined;
});
return [{ definition: { type: "symbol", symbol: searchSpaceNode.symbol, node: superKeyword }, references }];
return [{ definition: { type: "symbol", symbol: searchSpaceNode.symbol }, references }];
}
function getReferencesForThisKeyword(thisOrSuperKeyword: Node, sourceFiles: ReadonlyArray<SourceFile>, cancellationToken: CancellationToken): SymbolAndEntries[] {
@@ -1645,7 +1637,9 @@ namespace ts.FindAllReferences.Core {
* module, we want to keep the search limited to only types, as the two declarations (interface and uninstantiated module)
* do not intersect in any of the three spaces.
*/
function getIntersectingMeaningFromDeclarations(meaning: SemanticMeaning, declarations: Declaration[]): SemanticMeaning {
export function getIntersectingMeaningFromDeclarations(node: Node, symbol: Symbol): SemanticMeaning {
let meaning = getMeaningFromLocation(node);
const { declarations } = symbol;
if (declarations) {
let lastIterationMeaning: SemanticMeaning;
do {