mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Include static 'this' accesses in references of class (#20483)
This commit is contained in:
@@ -5233,6 +5233,18 @@ namespace ts {
|
||||
return node && (node.kind === SyntaxKind.GetAccessor || node.kind === SyntaxKind.SetAccessor);
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function isMethodOrAccessor(node: Node): node is MethodDeclaration | AccessorDeclaration {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Type members
|
||||
|
||||
export function isTypeElement(node: Node): node is TypeElement {
|
||||
|
||||
@@ -376,7 +376,7 @@ namespace ts.FindAllReferences.Core {
|
||||
const searchMeaning = getIntersectingMeaningFromDeclarations(getMeaningFromLocation(node), symbol.declarations);
|
||||
|
||||
const result: SymbolAndEntries[] = [];
|
||||
const state = new State(sourceFiles, /*isForConstructor*/ node.kind === SyntaxKind.ConstructorKeyword, checker, cancellationToken, searchMeaning, options, result);
|
||||
const state = new State(sourceFiles, getSpecialSearchKind(node), checker, cancellationToken, searchMeaning, options, result);
|
||||
|
||||
if (node.kind === SyntaxKind.DefaultKeyword) {
|
||||
addReference(node, symbol, node, state);
|
||||
@@ -403,6 +403,21 @@ namespace ts.FindAllReferences.Core {
|
||||
return result;
|
||||
}
|
||||
|
||||
function getSpecialSearchKind(node: Node): SpecialSearchKind {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ConstructorKeyword:
|
||||
return SpecialSearchKind.Constructor;
|
||||
case SyntaxKind.Identifier:
|
||||
if (isClassLike(node.parent)) {
|
||||
Debug.assert(node.parent.name === node);
|
||||
return SpecialSearchKind.Class;
|
||||
}
|
||||
// falls through
|
||||
default:
|
||||
return SpecialSearchKind.None;
|
||||
}
|
||||
}
|
||||
|
||||
/** Handle a few special cases relating to export/import specifiers. */
|
||||
function skipPastExportOrImportSpecifier(symbol: Symbol, node: Node, checker: TypeChecker): Symbol {
|
||||
const { parent } = node;
|
||||
@@ -439,6 +454,12 @@ namespace ts.FindAllReferences.Core {
|
||||
includes(symbol: Symbol): boolean;
|
||||
}
|
||||
|
||||
const enum SpecialSearchKind {
|
||||
None,
|
||||
Constructor,
|
||||
Class,
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds all state needed for the finding references.
|
||||
* Unlike `Search`, there is only one `State`.
|
||||
@@ -472,7 +493,7 @@ namespace ts.FindAllReferences.Core {
|
||||
constructor(
|
||||
readonly sourceFiles: ReadonlyArray<SourceFile>,
|
||||
/** True if we're searching for constructor references. */
|
||||
readonly isForConstructor: boolean,
|
||||
readonly specialSearchKind: SpecialSearchKind,
|
||||
readonly checker: TypeChecker,
|
||||
readonly cancellationToken: CancellationToken,
|
||||
readonly searchMeaning: SemanticMeaning,
|
||||
@@ -845,11 +866,16 @@ namespace ts.FindAllReferences.Core {
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.isForConstructor) {
|
||||
findConstructorReferences(referenceLocation, sourceFile, search, state);
|
||||
}
|
||||
else {
|
||||
addReference(referenceLocation, relatedSymbol, search.location, state);
|
||||
switch (state.specialSearchKind) {
|
||||
case SpecialSearchKind.None:
|
||||
addReference(referenceLocation, relatedSymbol, search.location, state);
|
||||
break;
|
||||
case SpecialSearchKind.Constructor:
|
||||
addConstructorReferences(referenceLocation, sourceFile, search, state);
|
||||
break;
|
||||
case SpecialSearchKind.Class:
|
||||
addClassStaticThisReferences(referenceLocation, search, state);
|
||||
break;
|
||||
}
|
||||
|
||||
getImportOrExportReferences(referenceLocation, referenceSymbol, search, state);
|
||||
@@ -961,27 +987,52 @@ namespace ts.FindAllReferences.Core {
|
||||
}
|
||||
|
||||
/** Adds references when a constructor is used with `new this()` in its own class and `super()` calls in subclasses. */
|
||||
function findConstructorReferences(referenceLocation: Node, sourceFile: SourceFile, search: Search, state: State): void {
|
||||
function addConstructorReferences(referenceLocation: Node, sourceFile: SourceFile, search: Search, state: State): void {
|
||||
if (isNewExpressionTarget(referenceLocation)) {
|
||||
addReference(referenceLocation, search.symbol, search.location, state);
|
||||
}
|
||||
|
||||
const pusher = state.referenceAdder(search.symbol, search.location);
|
||||
const pusher = () => state.referenceAdder(search.symbol, search.location);
|
||||
|
||||
if (isClassLike(referenceLocation.parent)) {
|
||||
Debug.assert(referenceLocation.parent.name === referenceLocation);
|
||||
// This is the class declaration containing the constructor.
|
||||
findOwnConstructorReferences(search.symbol, sourceFile, pusher);
|
||||
findOwnConstructorReferences(search.symbol, sourceFile, pusher());
|
||||
}
|
||||
else {
|
||||
// If this class appears in `extends C`, then the extending class' "super" calls are references.
|
||||
const classExtending = tryGetClassByExtendingIdentifier(referenceLocation);
|
||||
if (classExtending && isClassLike(classExtending)) {
|
||||
findSuperConstructorAccesses(classExtending, pusher);
|
||||
if (classExtending) {
|
||||
findSuperConstructorAccesses(classExtending, pusher());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addClassStaticThisReferences(referenceLocation: Node, search: Search, state: State): void {
|
||||
addReference(referenceLocation, search.symbol, search.location, 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));
|
||||
}
|
||||
}
|
||||
|
||||
function addStaticThisReferences(classLike: ClassLikeDeclaration, pusher: (node: Node) => void): void {
|
||||
for (const member of classLike.members) {
|
||||
if (!(isMethodOrAccessor(member) && hasModifier(member, ModifierFlags.Static))) {
|
||||
continue;
|
||||
}
|
||||
member.body.forEachChild(function cb(node) {
|
||||
if (node.kind === SyntaxKind.ThisKeyword) {
|
||||
pusher(node);
|
||||
}
|
||||
else if (!isFunctionLike(node)) {
|
||||
node.forEachChild(cb);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getPropertyAccessExpressionFromRightHandSide(node: Node): PropertyAccessExpression {
|
||||
return isRightSideOfPropertyAccess(node) && <PropertyAccessExpression>node.parent;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////class [|{| "isWriteAccess": true, "isDefinition": true |}C|] {
|
||||
//// static s() {
|
||||
//// [|this|];
|
||||
//// }
|
||||
//// static get f() { return [|this|]; }
|
||||
////}
|
||||
|
||||
const [r0, r1, r2] = test.ranges();
|
||||
verify.referenceGroups(r0, [{ definition: "class C", ranges: [r0, r1, r2] }]);
|
||||
verify.referenceGroups([r1, r2], [{ definition: "this: typeof C", ranges: [r1, r2] }]);
|
||||
Reference in New Issue
Block a user