[clang-tools-extra] e2cad4d - [clangd] Add ObjC method support to prepareCallHierarchy
Kadir Cetinkaya via cfe-commits
cfe-commits at lists.llvm.org
Thu Nov 25 02:25:01 PST 2021
Author: Sheldon Neuberger
Date: 2021-11-25T11:23:24+01:00
New Revision: e2cad4df22a6a411e7f7fcbc9bff0bd789545136
URL: https://github.com/llvm/llvm-project/commit/e2cad4df22a6a411e7f7fcbc9bff0bd789545136
DIFF: https://github.com/llvm/llvm-project/commit/e2cad4df22a6a411e7f7fcbc9bff0bd789545136.diff
LOG: [clangd] Add ObjC method support to prepareCallHierarchy
This fixes "textDocument/prepareCallHierarchy" in clangd for ObjC methods. Details at https://github.com/clangd/vscode-clangd/issues/247.
clangd uses Decl::isFunctionOrFunctionTemplate to check if the decl given in a prepareCallHierarchy request is eligible for prepareCallHierarchy. We change to use isFunctionOrMethod which includes functions and ObjC methods.
Reviewed By: kadircet
Differential Revision: https://reviews.llvm.org/D114058
Added:
Modified:
clang-tools-extra/clangd/XRefs.cpp
clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp
Removed:
################################################################################
diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp
index 452067a90a9c..b46d2c0043dc 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -1906,7 +1906,9 @@ prepareCallHierarchy(ParsedAST &AST, Position Pos, PathRef TUPath) {
return Result;
}
for (const NamedDecl *Decl : getDeclAtPosition(AST, *Loc, {})) {
- if (!Decl->isFunctionOrFunctionTemplate())
+ if (!(isa<DeclContext>(Decl) &&
+ cast<DeclContext>(Decl)->isFunctionOrMethod()) &&
+ Decl->getKind() != Decl::Kind::FunctionTemplate)
continue;
if (auto CHI = declToCallHierarchyItem(*Decl))
Result.emplace_back(std::move(*CHI));
diff --git a/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp b/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp
index 6f63ea2bc18b..a9d0385654dd 100644
--- a/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp
@@ -65,7 +65,7 @@ ::testing::Matcher<CallHierarchyIncomingCall> FromRanges(RangeMatchers... M) {
UnorderedElementsAre(M...));
}
-TEST(CallHierarchy, IncomingOneFile) {
+TEST(CallHierarchy, IncomingOneFileCpp) {
Annotations Source(R"cpp(
void call^ee(int);
void caller1() {
@@ -91,7 +91,51 @@ TEST(CallHierarchy, IncomingOneFile) {
ASSERT_THAT(IncomingLevel1,
ElementsAre(AllOf(From(WithName("caller1")),
FromRanges(Source.range("Callee")))));
+ auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get());
+ ASSERT_THAT(IncomingLevel2,
+ ElementsAre(AllOf(From(WithName("caller2")),
+ FromRanges(Source.range("Caller1A"),
+ Source.range("Caller1B"))),
+ AllOf(From(WithName("caller3")),
+ FromRanges(Source.range("Caller1C")))));
+ auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get());
+ ASSERT_THAT(IncomingLevel3,
+ ElementsAre(AllOf(From(WithName("caller3")),
+ FromRanges(Source.range("Caller2")))));
+
+ auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get());
+ EXPECT_THAT(IncomingLevel4, IsEmpty());
+}
+
+TEST(CallHierarchy, IncomingOneFileObjC) {
+ Annotations Source(R"objc(
+ @implementation MyClass {}
+ +(void)call^ee {}
+ +(void) caller1 {
+ [MyClass $Callee[[callee]]];
+ }
+ +(void) caller2 {
+ [MyClass $Caller1A[[caller1]]];
+ [MyClass $Caller1B[[caller1]]];
+ }
+ +(void) caller3 {
+ [MyClass $Caller1C[[caller1]]];
+ [MyClass $Caller2[[caller2]]];
+ }
+ @end
+ )objc");
+ TestTU TU = TestTU::withCode(Source.code());
+ TU.Filename = "TestTU.m";
+ auto AST = TU.build();
+ auto Index = TU.index();
+ std::vector<CallHierarchyItem> Items =
+ prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
+ ASSERT_THAT(Items, ElementsAre(WithName("callee")));
+ auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
+ ASSERT_THAT(IncomingLevel1,
+ ElementsAre(AllOf(From(WithName("caller1")),
+ FromRanges(Source.range("Callee")))));
auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get());
ASSERT_THAT(IncomingLevel2,
ElementsAre(AllOf(From(WithName("caller2")),
@@ -172,7 +216,7 @@ TEST(CallHierarchy, IncomingQualified) {
FromRanges(Source.range("Caller2")))));
}
-TEST(CallHierarchy, IncomingMultiFile) {
+TEST(CallHierarchy, IncomingMultiFileCpp) {
// The test uses a .hh suffix for header files to get clang
// to parse them in C++ mode. .h files are parsed in C mode
// by default, which causes problems because e.g. symbol
@@ -268,6 +312,115 @@ TEST(CallHierarchy, IncomingMultiFile) {
CheckCallHierarchy(*AST, CalleeC.point(), testPath("callee.cc"));
}
+TEST(CallHierarchy, IncomingMultiFileObjC) {
+ // The test uses a .mi suffix for header files to get clang
+ // to parse them in ObjC mode. .h files are parsed in C mode
+ // by default, which causes problems because e.g. symbol
+ // USRs are
diff erent in C mode (do not include function signatures).
+
+ Annotations CalleeH(R"objc(
+ @interface CalleeClass
+ +(void)call^ee;
+ @end
+ )objc");
+ Annotations CalleeC(R"objc(
+ #import "callee.mi"
+ @implementation CalleeClass {}
+ +(void)call^ee {}
+ @end
+ )objc");
+ Annotations Caller1H(R"objc(
+ @interface Caller1Class
+ +(void)caller1;
+ @end
+ )objc");
+ Annotations Caller1C(R"objc(
+ #import "callee.mi"
+ #import "caller1.mi"
+ @implementation Caller1Class {}
+ +(void)caller1 {
+ [CalleeClass [[calle^e]]];
+ }
+ @end
+ )objc");
+ Annotations Caller2H(R"objc(
+ @interface Caller2Class
+ +(void)caller2;
+ @end
+ )objc");
+ Annotations Caller2C(R"objc(
+ #import "caller1.mi"
+ #import "caller2.mi"
+ @implementation Caller2Class {}
+ +(void)caller2 {
+ [Caller1Class $A[[caller1]]];
+ [Caller1Class $B[[caller1]]];
+ }
+ @end
+ )objc");
+ Annotations Caller3C(R"objc(
+ #import "caller1.mi"
+ #import "caller2.mi"
+ @implementation Caller3Class {}
+ +(void)caller3 {
+ [Caller1Class $Caller1[[caller1]]];
+ [Caller2Class $Caller2[[caller2]]];
+ }
+ @end
+ )objc");
+
+ TestWorkspace Workspace;
+ Workspace.addSource("callee.mi", CalleeH.code());
+ Workspace.addSource("caller1.mi", Caller1H.code());
+ Workspace.addSource("caller2.mi", Caller2H.code());
+ Workspace.addMainFile("callee.m", CalleeC.code());
+ Workspace.addMainFile("caller1.m", Caller1C.code());
+ Workspace.addMainFile("caller2.m", Caller2C.code());
+ Workspace.addMainFile("caller3.m", Caller3C.code());
+ auto Index = Workspace.index();
+
+ auto CheckCallHierarchy = [&](ParsedAST &AST, Position Pos, PathRef TUPath) {
+ std::vector<CallHierarchyItem> Items =
+ prepareCallHierarchy(AST, Pos, TUPath);
+ ASSERT_THAT(Items, ElementsAre(WithName("callee")));
+ auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
+ ASSERT_THAT(IncomingLevel1,
+ ElementsAre(AllOf(From(WithName("caller1")),
+ FromRanges(Caller1C.range()))));
+
+ auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get());
+ ASSERT_THAT(
+ IncomingLevel2,
+ ElementsAre(AllOf(From(WithName("caller2")),
+ FromRanges(Caller2C.range("A"), Caller2C.range("B"))),
+ AllOf(From(WithName("caller3")),
+ FromRanges(Caller3C.range("Caller1")))));
+
+ auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get());
+ ASSERT_THAT(IncomingLevel3,
+ ElementsAre(AllOf(From(WithName("caller3")),
+ FromRanges(Caller3C.range("Caller2")))));
+
+ auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get());
+ EXPECT_THAT(IncomingLevel4, IsEmpty());
+ };
+
+ // Check that invoking from a call site works.
+ auto AST = Workspace.openFile("caller1.m");
+ ASSERT_TRUE(bool(AST));
+ CheckCallHierarchy(*AST, Caller1C.point(), testPath("caller1.m"));
+
+ // Check that invoking from the declaration site works.
+ AST = Workspace.openFile("callee.mi");
+ ASSERT_TRUE(bool(AST));
+ CheckCallHierarchy(*AST, CalleeH.point(), testPath("callee.mi"));
+
+ // Check that invoking from the definition site works.
+ AST = Workspace.openFile("callee.m");
+ ASSERT_TRUE(bool(AST));
+ CheckCallHierarchy(*AST, CalleeC.point(), testPath("callee.m"));
+}
+
TEST(CallHierarchy, CallInLocalVarDecl) {
// Tests that local variable declarations are not treated as callers
// (they're not indexed, so they can't be represented as call hierarchy
More information about the cfe-commits
mailing list