From edc497bb2bf9ceccfdbfea59b42f9ae565ffa1cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Fri, 21 Feb 2025 20:08:34 +0100 Subject: [PATCH] Find method references through base abstract classes implementing an interface (#61234) --- src/services/findAllReferences.ts | 5 +- ...cesForInheritedProperties10.baseline.jsonc | 501 ++++++++++++++++++ .../referencesForInheritedProperties10.ts | 25 + 3 files changed, 529 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/referencesForInheritedProperties10.baseline.jsonc create mode 100644 tests/cases/fourslash/referencesForInheritedProperties10.ts diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index ea1f164f84f..522577f9d8f 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -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); })); } } diff --git a/tests/baselines/reference/referencesForInheritedProperties10.baseline.jsonc b/tests/baselines/reference/referencesForInheritedProperties10.baseline.jsonc new file mode 100644 index 00000000000..7a6b9b3fbfa --- /dev/null +++ b/tests/baselines/reference/referencesForInheritedProperties10.baseline.jsonc @@ -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" + } + ] + } + ] \ No newline at end of file diff --git a/tests/cases/fourslash/referencesForInheritedProperties10.ts b/tests/cases/fourslash/referencesForInheritedProperties10.ts new file mode 100644 index 00000000000..af6daec7f3a --- /dev/null +++ b/tests/cases/fourslash/referencesForInheritedProperties10.ts @@ -0,0 +1,25 @@ +/// + +//// 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');