Skip to content

Commit

Permalink
Make findAllReferences work on triple-slash reference paths that reso…
Browse files Browse the repository at this point in the history
…lve to scripts (#41936)

* Support find-all-references on triple-slash references that resolve to scripts

* Rename terrible name

* Add test for <reference types="..." />

* Actually accept baselines
  • Loading branch information
andrewbranch authored Dec 11, 2020
1 parent 9dfbf07 commit 78ded65
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 23 deletions.
65 changes: 49 additions & 16 deletions src/services/findAllReferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ namespace ts.FindAllReferences {
readonly references: readonly Entry[];
}

export const enum DefinitionKind { Symbol, Label, Keyword, This, String }
export const enum DefinitionKind { Symbol, Label, Keyword, This, String, TripleSlashReference }
export type Definition =
| { readonly type: DefinitionKind.Symbol; readonly symbol: Symbol }
| { readonly type: DefinitionKind.Label; readonly node: Identifier }
| { readonly type: DefinitionKind.Keyword; readonly node: Node }
| { readonly type: DefinitionKind.This; readonly node: Node }
| { readonly type: DefinitionKind.String; readonly node: StringLiteralLike };
| { readonly type: DefinitionKind.String; readonly node: StringLiteralLike }
| { readonly type: DefinitionKind.TripleSlashReference; readonly reference: FileReference, readonly file: SourceFile };

export const enum EntryKind { Span, Node, StringLiteral, SearchedLocalFoundProperty, SearchedPropertyFoundLocal }
export type NodeEntryKind = EntryKind.Node | EntryKind.StringLiteral | EntryKind.SearchedLocalFoundProperty | EntryKind.SearchedPropertyFoundLocal;
Expand Down Expand Up @@ -298,17 +299,16 @@ namespace ts.FindAllReferences {
}

function definitionToReferencedSymbolDefinitionInfo(def: Definition, checker: TypeChecker, originalNode: Node): ReferencedSymbolDefinitionInfo {
const info = (() => {
const info = ((): { sourceFile: SourceFile, textSpan: TextSpan, name: string, kind: ScriptElementKind, displayParts: SymbolDisplayPart[], context?: Node | ContextWithStartAndEndNode } => {
switch (def.type) {
case DefinitionKind.Symbol: {
const { symbol } = def;
const { displayParts, kind } = getDefinitionKindAndDisplayParts(symbol, checker, originalNode);
const name = displayParts.map(p => p.text).join("");
const declaration = symbol.declarations && firstOrUndefined(symbol.declarations);
const node = declaration ? (getNameOfDeclaration(declaration) || declaration) : originalNode;
return {
node: declaration ?
getNameOfDeclaration(declaration) || declaration :
originalNode,
...getFileAndTextSpanFromNode(node),
name,
kind,
displayParts,
Expand All @@ -317,32 +317,44 @@ namespace ts.FindAllReferences {
}
case DefinitionKind.Label: {
const { node } = def;
return { node, name: node.text, kind: ScriptElementKind.label, displayParts: [displayPart(node.text, SymbolDisplayPartKind.text)] };
return { ...getFileAndTextSpanFromNode(node), name: node.text, kind: ScriptElementKind.label, displayParts: [displayPart(node.text, SymbolDisplayPartKind.text)] };
}
case DefinitionKind.Keyword: {
const { node } = def;
const name = tokenToString(node.kind)!;
return { node, name, kind: ScriptElementKind.keyword, displayParts: [{ text: name, kind: ScriptElementKind.keyword }] };
return { ...getFileAndTextSpanFromNode(node), name, kind: ScriptElementKind.keyword, displayParts: [{ text: name, kind: ScriptElementKind.keyword }] };
}
case DefinitionKind.This: {
const { node } = def;
const symbol = checker.getSymbolAtLocation(node);
const displayParts = symbol && SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(
checker, symbol, node.getSourceFile(), getContainerNode(node), node).displayParts || [textPart("this")];
return { node, name: "this", kind: ScriptElementKind.variableElement, displayParts };
return { ...getFileAndTextSpanFromNode(node), name: "this", kind: ScriptElementKind.variableElement, displayParts };
}
case DefinitionKind.String: {
const { node } = def;
return { node, name: node.text, kind: ScriptElementKind.variableElement, displayParts: [displayPart(getTextOfNode(node), SymbolDisplayPartKind.stringLiteral)] };
return {
...getFileAndTextSpanFromNode(node),
name: node.text,
kind: ScriptElementKind.variableElement,
displayParts: [displayPart(getTextOfNode(node), SymbolDisplayPartKind.stringLiteral)]
};
}
case DefinitionKind.TripleSlashReference: {
return {
textSpan: createTextSpanFromRange(def.reference),
sourceFile: def.file,
name: def.reference.fileName,
kind: ScriptElementKind.string,
displayParts: [displayPart(`"${def.reference.fileName}"`, SymbolDisplayPartKind.stringLiteral)]
};
}
default:
return Debug.assertNever(def);
}
})();

const { node, name, kind, displayParts, context } = info;
const sourceFile = node.getSourceFile();
const textSpan = getTextSpan(isComputedPropertyName(node) ? node.expression : node, sourceFile);
const { sourceFile, textSpan, name, kind, displayParts, context } = info;
return {
containerKind: ScriptElementKind.unknown,
containerName: "",
Expand All @@ -355,6 +367,14 @@ namespace ts.FindAllReferences {
};
}

function getFileAndTextSpanFromNode(node: Node) {
const sourceFile = node.getSourceFile();
return {
sourceFile,
textSpan: getTextSpan(isComputedPropertyName(node) ? node.expression : node, sourceFile)
};
}

function getDefinitionKindAndDisplayParts(symbol: Symbol, checker: TypeChecker, node: Node): { displayParts: SymbolDisplayPart[], kind: ScriptElementKind } {
const meaning = Core.getIntersectingMeaningFromDeclarations(node, symbol);
const enclosingDeclaration = symbol.declarations && firstOrUndefined(symbol.declarations) || node;
Expand Down Expand Up @@ -603,9 +623,22 @@ namespace ts.FindAllReferences {
node = getAdjustedRenameLocation(node);
}
if (isSourceFile(node)) {
const reference = GoToDefinition.getReferenceAtPosition(node, position, program);
const moduleSymbol = reference && program.getTypeChecker().getMergedSymbol(reference.file.symbol);
return moduleSymbol && getReferencedSymbolsForModule(program, moduleSymbol, /*excludeImportTypeOfExportEquals*/ false, sourceFiles, sourceFilesSet);
const resolvedRef = GoToDefinition.getReferenceAtPosition(node, position, program);
if (!resolvedRef) {
return undefined;
}
const moduleSymbol = program.getTypeChecker().getMergedSymbol(resolvedRef.file.symbol);
if (moduleSymbol) {
return getReferencedSymbolsForModule(program, moduleSymbol, /*excludeImportTypeOfExportEquals*/ false, sourceFiles, sourceFilesSet);
}
const fileIncludeReasons = program.getFileIncludeReasons();
if (!fileIncludeReasons) {
return undefined;
}
return [{
definition: { type: DefinitionKind.TripleSlashReference, reference: resolvedRef.reference, file: node },
references: getReferencesForNonModule(resolvedRef.file, fileIncludeReasons, program) || emptyArray
}];
}

if (!options.implementations) {
Expand Down
14 changes: 7 additions & 7 deletions src/services/goToDefinition.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/* @internal */
namespace ts.GoToDefinition {
export function getDefinitionAtPosition(program: Program, sourceFile: SourceFile, position: number): readonly DefinitionInfo[] | undefined {
const reference = getReferenceAtPosition(sourceFile, position, program);
if (reference) {
return [getDefinitionInfoForFileReference(reference.fileName, reference.file.fileName)];
const resolvedRef = getReferenceAtPosition(sourceFile, position, program);
if (resolvedRef) {
return [getDefinitionInfoForFileReference(resolvedRef.reference.fileName, resolvedRef.file.fileName)];
}

const node = getTouchingPropertyName(sourceFile, position);
Expand Down Expand Up @@ -108,24 +108,24 @@ namespace ts.GoToDefinition {
|| (!isCallLikeExpression(calledDeclaration.parent) && s === calledDeclaration.parent.symbol);
}

export function getReferenceAtPosition(sourceFile: SourceFile, position: number, program: Program): { fileName: string, file: SourceFile } | undefined {
export function getReferenceAtPosition(sourceFile: SourceFile, position: number, program: Program): { reference: FileReference, file: SourceFile } | undefined {
const referencePath = findReferenceInPosition(sourceFile.referencedFiles, position);
if (referencePath) {
const file = program.getSourceFileFromReference(sourceFile, referencePath);
return file && { fileName: referencePath.fileName, file };
return file && { reference: referencePath, file };
}

const typeReferenceDirective = findReferenceInPosition(sourceFile.typeReferenceDirectives, position);
if (typeReferenceDirective) {
const reference = program.getResolvedTypeReferenceDirectives().get(typeReferenceDirective.fileName);
const file = reference && program.getSourceFile(reference.resolvedFileName!); // TODO:GH#18217
return file && { fileName: typeReferenceDirective.fileName, file };
return file && { reference: typeReferenceDirective, file };
}

const libReferenceDirective = findReferenceInPosition(sourceFile.libReferenceDirectives, position);
if (libReferenceDirective) {
const file = program.getLibFileFromReference(libReferenceDirective);
return file && { fileName: libReferenceDirective.fileName, file };
return file && { reference: libReferenceDirective, file };
}

return undefined;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// === /c.js ===
// require([|"./b"|]);
// require("globals");

// === /a.ts ===
// /// <reference path="[|b.ts|]/*FIND ALL REFS*/" />
// /// <reference types="globals" />

[
{
"definition": {
"containerKind": "",
"containerName": "",
"fileName": "/a.ts",
"kind": "string",
"name": "b.ts",
"textSpan": {
"start": 21,
"length": 4
},
"displayParts": [
{
"text": "\"b.ts\"",
"kind": "stringLiteral"
}
]
},
"references": [
{
"textSpan": {
"start": 21,
"length": 4
},
"fileName": "/a.ts",
"isWriteAccess": false,
"isDefinition": false
},
{
"textSpan": {
"start": 8,
"length": 5
},
"fileName": "/c.js",
"isWriteAccess": false,
"isDefinition": false
}
]
}
]

// === /c.js ===
// require("./b");
// require([|"globals"|]);

// === /a.ts ===
// /// <reference path="b.ts" />
// /// <reference types="[|globals|]/*FIND ALL REFS*/" />

[
{
"definition": {
"containerKind": "",
"containerName": "",
"fileName": "/a.ts",
"kind": "string",
"name": "globals",
"textSpan": {
"start": 52,
"length": 7
},
"displayParts": [
{
"text": "\"globals\"",
"kind": "stringLiteral"
}
]
},
"references": [
{
"textSpan": {
"start": 52,
"length": 7
},
"fileName": "/a.ts",
"isWriteAccess": false,
"isDefinition": false
},
{
"textSpan": {
"start": 24,
"length": 9
},
"fileName": "/c.js",
"isWriteAccess": false,
"isDefinition": false
}
]
}
]
19 changes: 19 additions & 0 deletions tests/cases/fourslash/findAllReferencesTripleSlash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/// <reference path="fourslash.ts" />

// @checkJs: true

// @Filename: /node_modules/@types/globals/index.d.ts
//// declare const someAmbientGlobal: unknown;

// @Filename: /a.ts
//// /// <reference path="b.ts/*1*/" />
//// /// <reference types="globals/*2*/" />

// @Filename: /b.ts
//// console.log("b.ts");

// @Filename: /c.js
//// require("./b");
//// require("globals");

verify.baselineFindAllReferences("1", "2");

0 comments on commit 78ded65

Please sign in to comment.