mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-09 07:55:10 -05:00
Merge pull request #13566 from Microsoft/find_all_refs_primitive
Support find-all-references for type keywords
This commit is contained in:
@@ -17,25 +17,11 @@ namespace ts.DocumentHighlights {
|
||||
}
|
||||
|
||||
function getSemanticDocumentHighlights(node: Node, typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFilesToSearch: SourceFile[]): DocumentHighlights[] {
|
||||
if (node.kind === SyntaxKind.Identifier ||
|
||||
node.kind === SyntaxKind.ThisKeyword ||
|
||||
node.kind === SyntaxKind.ThisType ||
|
||||
node.kind === SyntaxKind.SuperKeyword ||
|
||||
node.kind === SyntaxKind.StringLiteral ||
|
||||
isLiteralNameOfPropertyDeclarationOrIndexAccess(node)) {
|
||||
|
||||
const referencedSymbols = FindAllReferences.getReferencedSymbolsForNode(typeChecker, cancellationToken, node, sourceFilesToSearch, /*findInStrings*/ false, /*findInComments*/ false, /*implementations*/false);
|
||||
return convertReferencedSymbols(referencedSymbols);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
const referencedSymbols = FindAllReferences.getReferencedSymbolsForNode(typeChecker, cancellationToken, node, sourceFilesToSearch, /*findInStrings*/false, /*findInComments*/false, /*implementations*/false);
|
||||
return referencedSymbols && convertReferencedSymbols(referencedSymbols);
|
||||
}
|
||||
|
||||
function convertReferencedSymbols(referencedSymbols: ReferencedSymbol[]): DocumentHighlights[] {
|
||||
if (!referencedSymbols) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const fileNameToDocumentHighlights = createMap<DocumentHighlights>();
|
||||
const result: DocumentHighlights[] = [];
|
||||
for (const referencedSymbol of referencedSymbols) {
|
||||
|
||||
@@ -1,29 +1,16 @@
|
||||
/* @internal */
|
||||
namespace ts.FindAllReferences {
|
||||
export function findReferencedSymbols(typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFiles: SourceFile[], sourceFile: SourceFile, position: number, findInStrings: boolean, findInComments: boolean): ReferencedSymbol[] {
|
||||
export function findReferencedSymbols(typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFiles: SourceFile[], sourceFile: SourceFile, position: number, findInStrings: boolean, findInComments: boolean): ReferencedSymbol[] | undefined {
|
||||
const node = getTouchingPropertyName(sourceFile, position, /*includeJsDocComment*/ true);
|
||||
if (node === sourceFile) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.NumericLiteral:
|
||||
if (!isLiteralNameOfPropertyDeclarationOrIndexAccess(node)) {
|
||||
break;
|
||||
}
|
||||
// Fallthrough
|
||||
case SyntaxKind.Identifier:
|
||||
case SyntaxKind.ThisKeyword:
|
||||
// case SyntaxKind.SuperKeyword: TODO:GH#9268
|
||||
case SyntaxKind.ConstructorKeyword:
|
||||
case SyntaxKind.StringLiteral:
|
||||
return getReferencedSymbolsForNode(typeChecker, cancellationToken, node, sourceFiles, findInStrings, findInComments, /*implementations*/false);
|
||||
}
|
||||
return undefined;
|
||||
return getReferencedSymbolsForNode(typeChecker, cancellationToken, node, sourceFiles, findInStrings, findInComments, /*implementations*/false);
|
||||
}
|
||||
|
||||
export function getReferencedSymbolsForNode(typeChecker: TypeChecker, cancellationToken: CancellationToken, node: Node, sourceFiles: SourceFile[], findInStrings: boolean, findInComments: boolean, implementations: boolean): ReferencedSymbol[] {
|
||||
export function getReferencedSymbolsForNode(typeChecker: TypeChecker, cancellationToken: CancellationToken, node: Node, sourceFiles: SourceFile[], findInStrings: boolean, findInComments: boolean, implementations: boolean): ReferencedSymbol[] | undefined {
|
||||
if (!implementations) {
|
||||
if (isTypeKeyword(node.kind)) {
|
||||
return getAllReferencesForKeyword(sourceFiles, node.kind, cancellationToken);
|
||||
}
|
||||
|
||||
// Labels
|
||||
if (isLabelName(node)) {
|
||||
if (isJumpStatementTarget(node)) {
|
||||
@@ -68,8 +55,6 @@ namespace ts.FindAllReferences {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let result: ReferencedSymbol[];
|
||||
|
||||
// Compute the meaning from the location and the symbol it references
|
||||
const searchMeaning = getIntersectingMeaningFromDeclarations(getMeaningFromLocation(node), declarations);
|
||||
|
||||
@@ -84,6 +69,7 @@ namespace ts.FindAllReferences {
|
||||
// Maps from a symbol ID to the ReferencedSymbol entry in 'result'.
|
||||
const symbolToIndex: number[] = [];
|
||||
|
||||
let result: ReferencedSymbol[];
|
||||
if (scope) {
|
||||
result = [];
|
||||
getReferencesInNode(scope, symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result, symbolToIndex, implementations, typeChecker, cancellationToken);
|
||||
@@ -92,10 +78,7 @@ namespace ts.FindAllReferences {
|
||||
const internedName = getInternedName(symbol, node);
|
||||
for (const sourceFile of sourceFiles) {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
|
||||
const nameTable = getNameTable(sourceFile);
|
||||
|
||||
if (nameTable.get(internedName) !== undefined) {
|
||||
if (sourceFileHasName(sourceFile, internedName)) {
|
||||
result = result || [];
|
||||
getReferencesInNode(sourceFile, symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result, symbolToIndex, implementations, typeChecker, cancellationToken);
|
||||
}
|
||||
@@ -105,9 +88,13 @@ namespace ts.FindAllReferences {
|
||||
return result;
|
||||
}
|
||||
|
||||
function sourceFileHasName(sourceFile: SourceFile, name: string): boolean {
|
||||
return getNameTable(sourceFile).get(name) !== undefined;
|
||||
}
|
||||
|
||||
function getDefinition(symbol: Symbol, node: Node, typeChecker: TypeChecker): ReferencedSymbolDefinitionInfo {
|
||||
const info = SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, node.getSourceFile(), getContainerNode(node), node);
|
||||
const name = map(info.displayParts, p => p.text).join("");
|
||||
const { displayParts, symbolKind } = SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, node.getSourceFile(), getContainerNode(node), node);
|
||||
const name = displayParts.map(p => p.text).join("");
|
||||
const declarations = symbol.declarations;
|
||||
if (!declarations || declarations.length === 0) {
|
||||
return undefined;
|
||||
@@ -117,10 +104,10 @@ namespace ts.FindAllReferences {
|
||||
containerKind: "",
|
||||
containerName: "",
|
||||
name,
|
||||
kind: info.symbolKind,
|
||||
kind: symbolKind,
|
||||
fileName: declarations[0].getSourceFile().fileName,
|
||||
textSpan: createTextSpan(declarations[0].getStart(), 0),
|
||||
displayParts: info.displayParts
|
||||
displayParts
|
||||
};
|
||||
}
|
||||
|
||||
@@ -351,6 +338,43 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
}
|
||||
|
||||
function getAllReferencesForKeyword(sourceFiles: SourceFile[], keywordKind: ts.SyntaxKind, cancellationToken: CancellationToken): ReferencedSymbol[] {
|
||||
const name = tokenToString(keywordKind);
|
||||
const definition: ReferencedSymbolDefinitionInfo = {
|
||||
containerKind: "",
|
||||
containerName: "",
|
||||
fileName: "",
|
||||
kind: ScriptElementKind.keyword,
|
||||
name,
|
||||
textSpan: createTextSpan(0, 1),
|
||||
displayParts: [{ text: name, kind: ScriptElementKind.keyword }]
|
||||
}
|
||||
|
||||
const references: ReferenceEntry[] = [];
|
||||
for (const sourceFile of sourceFiles) {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
addReferencesForKeywordInFile(sourceFile, keywordKind, name, cancellationToken, references);
|
||||
}
|
||||
|
||||
return [{ definition, references }];
|
||||
}
|
||||
|
||||
function addReferencesForKeywordInFile(sourceFile: SourceFile, kind: SyntaxKind, searchText: string, cancellationToken: CancellationToken, references: Push<ReferenceEntry>): void {
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, searchText, sourceFile.getStart(), sourceFile.getEnd(), cancellationToken);
|
||||
for (const position of possiblePositions) {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
const referenceLocation = getTouchingPropertyName(sourceFile, position);
|
||||
if (referenceLocation.kind === kind) {
|
||||
references.push({
|
||||
textSpan: createTextSpanFromNode(referenceLocation),
|
||||
fileName: sourceFile.fileName,
|
||||
isWriteAccess: false,
|
||||
isDefinition: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Search within node "container" for references for a search value, where the search value is defined as a
|
||||
* tuple of(searchSymbol, searchText, searchLocation, and searchMeaning).
|
||||
* searchLocation: a node where the search value
|
||||
@@ -375,67 +399,64 @@ namespace ts.FindAllReferences {
|
||||
|
||||
const parents = getParentSymbolsOfPropertyAccess();
|
||||
const inheritsFromCache: Map<boolean> = createMap<boolean>();
|
||||
// Build the set of symbols to search for, initially it has only the current symbol
|
||||
const searchSymbols = populateSearchSymbolSet(searchSymbol, searchLocation, typeChecker, implementations);
|
||||
|
||||
if (possiblePositions.length) {
|
||||
// Build the set of symbols to search for, initially it has only the current symbol
|
||||
const searchSymbols = populateSearchSymbolSet(searchSymbol, searchLocation, typeChecker, implementations);
|
||||
for (const position of possiblePositions) {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
|
||||
forEach(possiblePositions, position => {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
const referenceLocation = getTouchingPropertyName(sourceFile, position);
|
||||
if (!isValidReferencePosition(referenceLocation, searchText)) {
|
||||
// This wasn't the start of a token. Check to see if it might be a
|
||||
// match in a comment or string if that's what the caller is asking
|
||||
// for.
|
||||
if (!implementations && ((findInStrings && isInString(sourceFile, position)) ||
|
||||
(findInComments && isInNonReferenceComment(sourceFile, position)))) {
|
||||
|
||||
const referenceLocation = getTouchingPropertyName(sourceFile, position);
|
||||
if (!isValidReferencePosition(referenceLocation, searchText)) {
|
||||
// This wasn't the start of a token. Check to see if it might be a
|
||||
// match in a comment or string if that's what the caller is asking
|
||||
// for.
|
||||
if (!implementations && ((findInStrings && isInString(sourceFile, position)) ||
|
||||
(findInComments && isInNonReferenceComment(sourceFile, position)))) {
|
||||
|
||||
// In the case where we're looking inside comments/strings, we don't have
|
||||
// an actual definition. So just use 'undefined' here. Features like
|
||||
// 'Rename' won't care (as they ignore the definitions), and features like
|
||||
// 'FindReferences' will just filter out these results.
|
||||
result.push({
|
||||
definition: undefined,
|
||||
references: [{
|
||||
fileName: sourceFile.fileName,
|
||||
textSpan: createTextSpan(position, searchText.length),
|
||||
isWriteAccess: false,
|
||||
isDefinition: false
|
||||
}]
|
||||
});
|
||||
}
|
||||
return;
|
||||
// In the case where we're looking inside comments/strings, we don't have
|
||||
// an actual definition. So just use 'undefined' here. Features like
|
||||
// 'Rename' won't care (as they ignore the definitions), and features like
|
||||
// 'FindReferences' will just filter out these results.
|
||||
result.push({
|
||||
definition: undefined,
|
||||
references: [{
|
||||
fileName: sourceFile.fileName,
|
||||
textSpan: createTextSpan(position, searchText.length),
|
||||
isWriteAccess: false,
|
||||
isDefinition: false
|
||||
}]
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(getMeaningFromLocation(referenceLocation) & searchMeaning)) {
|
||||
return;
|
||||
if (!(getMeaningFromLocation(referenceLocation) & searchMeaning)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const referenceSymbol = typeChecker.getSymbolAtLocation(referenceLocation);
|
||||
if (referenceSymbol) {
|
||||
const referenceSymbolDeclaration = referenceSymbol.valueDeclaration;
|
||||
const shorthandValueSymbol = typeChecker.getShorthandAssignmentValueSymbol(referenceSymbolDeclaration);
|
||||
const relatedSymbol = getRelatedSymbol(searchSymbols, referenceSymbol, referenceLocation,
|
||||
/*searchLocationIsConstructor*/ searchLocation.kind === SyntaxKind.ConstructorKeyword, parents, inheritsFromCache, typeChecker);
|
||||
|
||||
if (relatedSymbol) {
|
||||
addReferenceToRelatedSymbol(referenceLocation, relatedSymbol);
|
||||
}
|
||||
|
||||
const referenceSymbol = typeChecker.getSymbolAtLocation(referenceLocation);
|
||||
if (referenceSymbol) {
|
||||
const referenceSymbolDeclaration = referenceSymbol.valueDeclaration;
|
||||
const shorthandValueSymbol = typeChecker.getShorthandAssignmentValueSymbol(referenceSymbolDeclaration);
|
||||
const relatedSymbol = getRelatedSymbol(searchSymbols, referenceSymbol, referenceLocation,
|
||||
/*searchLocationIsConstructor*/ searchLocation.kind === SyntaxKind.ConstructorKeyword, parents, inheritsFromCache, typeChecker);
|
||||
|
||||
if (relatedSymbol) {
|
||||
addReferenceToRelatedSymbol(referenceLocation, relatedSymbol);
|
||||
}
|
||||
/* Because in short-hand property assignment, an identifier which stored as name of the short-hand property assignment
|
||||
* has two meaning : property name and property value. Therefore when we do findAllReference at the position where
|
||||
* an identifier is declared, the language service should return the position of the variable declaration as well as
|
||||
* the position in short-hand property assignment excluding property accessing. However, if we do findAllReference at the
|
||||
* position of property accessing, the referenceEntry of such position will be handled in the first case.
|
||||
*/
|
||||
else if (!(referenceSymbol.flags & SymbolFlags.Transient) && searchSymbols.indexOf(shorthandValueSymbol) >= 0) {
|
||||
addReferenceToRelatedSymbol(referenceSymbolDeclaration.name, shorthandValueSymbol);
|
||||
}
|
||||
else if (searchLocation.kind === SyntaxKind.ConstructorKeyword) {
|
||||
findAdditionalConstructorReferences(referenceSymbol, referenceLocation);
|
||||
}
|
||||
/* Because in short-hand property assignment, an identifier which stored as name of the short-hand property assignment
|
||||
* has two meaning : property name and property value. Therefore when we do findAllReference at the position where
|
||||
* an identifier is declared, the language service should return the position of the variable declaration as well as
|
||||
* the position in short-hand property assignment excluding property accessing. However, if we do findAllReference at the
|
||||
* position of property accessing, the referenceEntry of such position will be handled in the first case.
|
||||
*/
|
||||
else if (!(referenceSymbol.flags & SymbolFlags.Transient) && searchSymbols.indexOf(shorthandValueSymbol) >= 0) {
|
||||
addReferenceToRelatedSymbol(referenceSymbolDeclaration.name, shorthandValueSymbol);
|
||||
}
|
||||
});
|
||||
else if (searchLocation.kind === SyntaxKind.ConstructorKeyword) {
|
||||
findAdditionalConstructorReferences(referenceSymbol, referenceLocation);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
|
||||
@@ -1959,11 +1959,9 @@ namespace ts {
|
||||
|
||||
function walk(node: Node) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.Identifier: {
|
||||
const text = (<Identifier>node).text;
|
||||
nameTable.set(text, nameTable.get(text) === undefined ? node.pos : -1);
|
||||
case SyntaxKind.Identifier:
|
||||
setNameTable((<Identifier>node).text, node);
|
||||
break;
|
||||
}
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.NumericLiteral:
|
||||
// We want to store any numbers/strings if they were a name that could be
|
||||
@@ -1974,9 +1972,7 @@ namespace ts {
|
||||
node.parent.kind === SyntaxKind.ExternalModuleReference ||
|
||||
isArgumentOfElementAccessExpression(node) ||
|
||||
isLiteralComputedPropertyDeclarationName(node)) {
|
||||
|
||||
const text = (<LiteralExpression>node).text;
|
||||
nameTable.set(text, nameTable.get(text) === undefined ? node.pos : -1);
|
||||
setNameTable((<LiteralExpression>node).text, node);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -1988,6 +1984,10 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setNameTable(text: string, node: ts.Node): void {
|
||||
nameTable.set(text, nameTable.get(text) === undefined ? node.pos : -1);
|
||||
}
|
||||
}
|
||||
|
||||
function isArgumentOfElementAccessExpression(node: Node) {
|
||||
|
||||
@@ -495,7 +495,7 @@ namespace ts {
|
||||
|
||||
export interface SymbolDisplayPart {
|
||||
text: string;
|
||||
kind: string;
|
||||
kind: string; // A ScriptElementKind
|
||||
}
|
||||
|
||||
export interface QuickInfo {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// These utilities are common to multiple language service features.
|
||||
// These utilities are common to multiple language service features.
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
export const scanner: Scanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ true);
|
||||
@@ -71,7 +71,10 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function getMeaningFromLocation(node: Node): SemanticMeaning {
|
||||
if (node.parent.kind === SyntaxKind.ExportAssignment) {
|
||||
if (node.kind === SyntaxKind.SourceFile) {
|
||||
return SemanticMeaning.Value;
|
||||
}
|
||||
else if (node.parent.kind === SyntaxKind.ExportAssignment) {
|
||||
return SemanticMeaning.Value | SemanticMeaning.Type | SemanticMeaning.Namespace;
|
||||
}
|
||||
else if (isInRightSideOfImport(node)) {
|
||||
@@ -1116,6 +1119,22 @@ namespace ts {
|
||||
export function createTextSpanFromNode(node: Node, sourceFile?: SourceFile): TextSpan {
|
||||
return createTextSpanFromBounds(node.getStart(sourceFile), node.getEnd());
|
||||
}
|
||||
|
||||
export function isTypeKeyword(kind: SyntaxKind): boolean {
|
||||
switch (kind) {
|
||||
case SyntaxKind.AnyKeyword:
|
||||
case SyntaxKind.BooleanKeyword:
|
||||
case SyntaxKind.NeverKeyword:
|
||||
case SyntaxKind.NumberKeyword:
|
||||
case SyntaxKind.ObjectKeyword:
|
||||
case SyntaxKind.StringKeyword:
|
||||
case SyntaxKind.SymbolKeyword:
|
||||
case SyntaxKind.VoidKeyword:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Display-part writer helpers
|
||||
|
||||
31
tests/cases/fourslash/findAllRefsPrimitive.ts
Normal file
31
tests/cases/fourslash/findAllRefsPrimitive.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
// @noLib: true
|
||||
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// @Filename: a.ts
|
||||
////const x: [|any|] = 0;
|
||||
////const any = 2;
|
||||
////const y: [|any|] = any;
|
||||
|
||||
////function f(b: [|boolean|]): [|boolean|];
|
||||
|
||||
////type T = [|never|]; type U = [|never|];
|
||||
|
||||
////function n(x: [|number|]): [|number|];
|
||||
|
||||
////function o(x: [|object|]): [|object|];
|
||||
|
||||
////function s(x: [|string|]): [|string|];
|
||||
|
||||
////function sy(s: [|symbol|]): [|symbol|];
|
||||
|
||||
////function v(v: [|void|]): [|void|];
|
||||
|
||||
// @Filename: b.ts
|
||||
// const z: [|any|] = 0;
|
||||
|
||||
verify.rangesWithSameTextReferenceEachOther();
|
||||
verify.rangesWithSameTextAreDocumentHighlights();
|
||||
|
||||
goTo.rangeStart(test.ranges()[0]);
|
||||
verify.renameInfoFailed();
|
||||
@@ -1,12 +0,0 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////var obj1: {
|
||||
//// (bar: any): any;
|
||||
//// new (bar: any): any;
|
||||
//// [bar: any]: any;
|
||||
//// bar: any;
|
||||
//// foob(bar: any): an/**/y;
|
||||
////};
|
||||
|
||||
goTo.marker();
|
||||
verify.occurrencesAtPositionCount(0);
|
||||
@@ -3,7 +3,7 @@
|
||||
// @Filename: localGetReferences_1.ts
|
||||
////// Comment Refence Test: g/*1*/lobalVar
|
||||
////// References to a variable declared in global.
|
||||
////var [|globalVar|]: n/*2*/umber = 2;
|
||||
////var [|globalVar|]: number = 2;
|
||||
////
|
||||
////class fooCls {
|
||||
//// // References to static variable declared in a class.
|
||||
@@ -189,10 +189,6 @@
|
||||
goTo.marker("1");
|
||||
verify.referencesAre([]);
|
||||
|
||||
// References to type.
|
||||
goTo.marker("2");
|
||||
verify.referencesAre([]);
|
||||
|
||||
// References to unresolved symbol.
|
||||
goTo.marker("3");
|
||||
verify.referencesAre([]);
|
||||
|
||||
Reference in New Issue
Block a user