Find method references through base abstract classes implementing an interface (#61234)

This commit is contained in:
Mateusz Burzyński 2025-02-21 20:08:34 +01:00 committed by GitHub
parent b95187d1ce
commit edc497bb2b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 529 additions and 2 deletions

View File

@ -2679,9 +2679,10 @@ export namespace Core {
return firstDefined(symbol.declarations, declaration =>
firstDefined(getAllSuperTypeNodes(declaration), typeReference => {
const type = checker.getTypeAtLocation(typeReference);
const propertySymbol = type && type.symbol && checker.getPropertyOfType(type, propertyName);
const propertySymbol = type.symbol && checker.getPropertyOfType(type, propertyName);
// Visit the typeReference as well to see if it directly or indirectly uses that property
return type && propertySymbol && (firstDefined(checker.getRootSymbols(propertySymbol), cb) || recur(type.symbol));
// When `propertySymbol` is missing continue the recursion through parents as some parent up the chain might be an abstract class that implements interface having the property
return propertySymbol && firstDefined(checker.getRootSymbols(propertySymbol), cb) || type.symbol && recur(type.symbol);
}));
}
}

View File

@ -0,0 +1,501 @@
// === findAllReferences ===
// === /tests/cases/fourslash/referencesForInheritedProperties10.ts ===
// interface IFeedbackHandler {
// /*FIND ALL REFS*/<|[|{| defId: 0, isDefinition: true |}handleAccept|]?(): void;|>
// handleReject?(): void;
// }
//
// abstract class AbstractFeedbackHandler implements IFeedbackHandler {}
//
// class FeedbackHandler extends AbstractFeedbackHandler {
// <|[|{| defId: 1, isWriteAccess: true |}handleAccept|](): void {
// console.log("Feedback accepted");
// }|>
//
// handleReject(): void {
// console.log("Feedback rejected");
// }
// }
//
// function foo(handler: IFeedbackHandler) {
// handler.[|{| defId: 0 |}handleAccept|]?.();
// handler.handleReject?.();
// }
// === Definitions ===
// === /tests/cases/fourslash/referencesForInheritedProperties10.ts ===
// interface IFeedbackHandler {
// /*FIND ALL REFS*/<|[|{| defId: 0 |}handleAccept|]?(): void;|>
// handleReject?(): void;
// }
//
// abstract class AbstractFeedbackHandler implements IFeedbackHandler {}
//
// class FeedbackHandler extends AbstractFeedbackHandler {
// <|[|{| defId: 1 |}handleAccept|](): void {
// console.log("Feedback accepted");
// }|>
//
// handleReject(): void {
// console.log("Feedback rejected");
// --- (line: 15) skipped ---
// === Details ===
[
{
"defId": 0,
"containerKind": "",
"containerName": "",
"kind": "method",
"name": "(method) IFeedbackHandler.handleAccept?(): void",
"displayParts": [
{
"text": "(",
"kind": "punctuation"
},
{
"text": "method",
"kind": "text"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "IFeedbackHandler",
"kind": "interfaceName"
},
{
"text": ".",
"kind": "punctuation"
},
{
"text": "handleAccept",
"kind": "methodName"
},
{
"text": "?",
"kind": "punctuation"
},
{
"text": "(",
"kind": "punctuation"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": ":",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "void",
"kind": "keyword"
}
]
},
{
"defId": 1,
"containerKind": "",
"containerName": "",
"kind": "method",
"name": "(method) FeedbackHandler.handleAccept(): void",
"displayParts": [
{
"text": "(",
"kind": "punctuation"
},
{
"text": "method",
"kind": "text"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "FeedbackHandler",
"kind": "className"
},
{
"text": ".",
"kind": "punctuation"
},
{
"text": "handleAccept",
"kind": "methodName"
},
{
"text": "(",
"kind": "punctuation"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": ":",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "void",
"kind": "keyword"
}
]
}
]
// === findAllReferences ===
// === /tests/cases/fourslash/referencesForInheritedProperties10.ts ===
// interface IFeedbackHandler {
// <|[|{| defId: 0 |}handleAccept|]?(): void;|>
// handleReject?(): void;
// }
//
// abstract class AbstractFeedbackHandler implements IFeedbackHandler {}
//
// class FeedbackHandler extends AbstractFeedbackHandler {
// /*FIND ALL REFS*/<|[|{| defId: 1, isWriteAccess: true, isDefinition: true |}handleAccept|](): void {
// console.log("Feedback accepted");
// }|>
//
// handleReject(): void {
// console.log("Feedback rejected");
// }
// }
//
// function foo(handler: IFeedbackHandler) {
// handler.[|{| defId: 0 |}handleAccept|]?.();
// handler.handleReject?.();
// }
// === Definitions ===
// === /tests/cases/fourslash/referencesForInheritedProperties10.ts ===
// interface IFeedbackHandler {
// <|[|{| defId: 0 |}handleAccept|]?(): void;|>
// handleReject?(): void;
// }
//
// abstract class AbstractFeedbackHandler implements IFeedbackHandler {}
//
// class FeedbackHandler extends AbstractFeedbackHandler {
// /*FIND ALL REFS*/<|[|{| defId: 1 |}handleAccept|](): void {
// console.log("Feedback accepted");
// }|>
//
// handleReject(): void {
// console.log("Feedback rejected");
// --- (line: 15) skipped ---
// === Details ===
[
{
"defId": 0,
"containerKind": "",
"containerName": "",
"kind": "method",
"name": "(method) IFeedbackHandler.handleAccept?(): void",
"displayParts": [
{
"text": "(",
"kind": "punctuation"
},
{
"text": "method",
"kind": "text"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "IFeedbackHandler",
"kind": "interfaceName"
},
{
"text": ".",
"kind": "punctuation"
},
{
"text": "handleAccept",
"kind": "methodName"
},
{
"text": "?",
"kind": "punctuation"
},
{
"text": "(",
"kind": "punctuation"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": ":",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "void",
"kind": "keyword"
}
]
},
{
"defId": 1,
"containerKind": "",
"containerName": "",
"kind": "method",
"name": "(method) FeedbackHandler.handleAccept(): void",
"displayParts": [
{
"text": "(",
"kind": "punctuation"
},
{
"text": "method",
"kind": "text"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "FeedbackHandler",
"kind": "className"
},
{
"text": ".",
"kind": "punctuation"
},
{
"text": "handleAccept",
"kind": "methodName"
},
{
"text": "(",
"kind": "punctuation"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": ":",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "void",
"kind": "keyword"
}
]
}
]
// === findAllReferences ===
// === /tests/cases/fourslash/referencesForInheritedProperties10.ts ===
// interface IFeedbackHandler {
// <|[|{| defId: 0 |}handleAccept|]?(): void;|>
// handleReject?(): void;
// }
//
// abstract class AbstractFeedbackHandler implements IFeedbackHandler {}
//
// class FeedbackHandler extends AbstractFeedbackHandler {
// <|[|{| defId: 1, isWriteAccess: true |}handleAccept|](): void {
// console.log("Feedback accepted");
// }|>
//
// handleReject(): void {
// console.log("Feedback rejected");
// }
// }
//
// function foo(handler: IFeedbackHandler) {
// handler./*FIND ALL REFS*/[|{| defId: 0 |}handleAccept|]?.();
// handler.handleReject?.();
// }
// === Definitions ===
// === /tests/cases/fourslash/referencesForInheritedProperties10.ts ===
// interface IFeedbackHandler {
// <|[|{| defId: 0 |}handleAccept|]?(): void;|>
// handleReject?(): void;
// }
//
// abstract class AbstractFeedbackHandler implements IFeedbackHandler {}
//
// class FeedbackHandler extends AbstractFeedbackHandler {
// <|[|{| defId: 1 |}handleAccept|](): void {
// console.log("Feedback accepted");
// }|>
//
// handleReject(): void {
// console.log("Feedback rejected");
// }
// }
//
// function foo(handler: IFeedbackHandler) {
// handler./*FIND ALL REFS*/handleAccept?.();
// handler.handleReject?.();
// }
// === Details ===
[
{
"defId": 0,
"containerKind": "",
"containerName": "",
"kind": "method",
"name": "(method) IFeedbackHandler.handleAccept?(): void",
"displayParts": [
{
"text": "(",
"kind": "punctuation"
},
{
"text": "method",
"kind": "text"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "IFeedbackHandler",
"kind": "interfaceName"
},
{
"text": ".",
"kind": "punctuation"
},
{
"text": "handleAccept",
"kind": "methodName"
},
{
"text": "?",
"kind": "punctuation"
},
{
"text": "(",
"kind": "punctuation"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": ":",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "void",
"kind": "keyword"
}
]
},
{
"defId": 1,
"containerKind": "",
"containerName": "",
"kind": "method",
"name": "(method) FeedbackHandler.handleAccept(): void",
"displayParts": [
{
"text": "(",
"kind": "punctuation"
},
{
"text": "method",
"kind": "text"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "FeedbackHandler",
"kind": "className"
},
{
"text": ".",
"kind": "punctuation"
},
{
"text": "handleAccept",
"kind": "methodName"
},
{
"text": "(",
"kind": "punctuation"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": ":",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "void",
"kind": "keyword"
}
]
}
]

View File

@ -0,0 +1,25 @@
/// <reference path='fourslash.ts'/>
//// interface IFeedbackHandler {
//// /*1*/handleAccept?(): void;
//// handleReject?(): void;
//// }
////
//// abstract class AbstractFeedbackHandler implements IFeedbackHandler {}
////
//// class FeedbackHandler extends AbstractFeedbackHandler {
//// /*2*/handleAccept(): void {
//// console.log("Feedback accepted");
//// }
////
//// handleReject(): void {
//// console.log("Feedback rejected");
//// }
//// }
////
//// function foo(handler: IFeedbackHandler) {
//// handler./*3*/handleAccept?.();
//// handler.handleReject?.();
//// }
verify.baselineFindAllReferences('1', '2', '3');