diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -1907,7 +1907,9 @@ return Result; } for (const NamedDecl *Decl : getDeclAtPosition(AST, *Loc, {})) { - if (!Decl->isFunctionOrFunctionTemplate()) + if (!(isa(Decl) && + cast(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 --- a/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp +++ b/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp @@ -51,6 +51,13 @@ using ::testing::Matcher; using ::testing::UnorderedElementsAre; +void verifyIncomingMultiFile(std::string SourceExt, std::string HeaderExt, + Annotations &CalleeH, Annotations &Caller1H, + Annotations &Caller2H, Annotations &CalleeC, + Annotations &Caller1C, Annotations &Caller2C, + Annotations &Caller3C); +void verifyIncomingOneFile(Annotations &Source, std::string Filename); + // Helpers for matching call hierarchy data structures. MATCHER_P(WithName, N, "") { return arg.name == N; } MATCHER_P(WithSelectionRange, R, "") { return arg.selectionRange == R; } @@ -65,25 +72,11 @@ UnorderedElementsAre(M...)); } -TEST(CallHierarchy, IncomingOneFile) { - Annotations Source(R"cpp( - void call^ee(int); - void caller1() { - $Callee[[callee]](42); - } - void caller2() { - $Caller1A[[caller1]](); - $Caller1B[[caller1]](); - } - void caller3() { - $Caller1C[[caller1]](); - $Caller2[[caller2]](); - } - )cpp"); +void verifyIncomingOneFile(Annotations &Source, std::string Filename) { TestTU TU = TestTU::withCode(Source.code()); + TU.Filename = Filename; auto AST = TU.build(); auto Index = TU.index(); - std::vector Items = prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename)); ASSERT_THAT(Items, ElementsAre(WithName("callee"))); @@ -91,7 +84,6 @@ 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")), @@ -109,6 +101,45 @@ EXPECT_THAT(IncomingLevel4, IsEmpty()); } +TEST(CallHierarchy, IncomingOneFileCpp) { + Annotations Source(R"cpp( + void call^ee(int); + void caller1() { + $Callee[[callee]](42); + } + void caller2() { + $Caller1A[[caller1]](); + $Caller1B[[caller1]](); + } + void caller3() { + $Caller1C[[caller1]](); + $Caller2[[caller2]](); + } + )cpp"); + verifyIncomingOneFile(Source, "TestTU.cpp"); +} + +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"); + verifyIncomingOneFile(Source, "TestTU.m"); +} + TEST(CallHierarchy, MainFileOnlyRef) { // In addition to testing that we store refs to main-file only symbols, // this tests that anonymous namespaces do not interfere with the @@ -172,7 +203,7 @@ 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 @@ -215,15 +246,84 @@ } )cpp"); - TestWorkspace Workspace; - Workspace.addSource("callee.hh", CalleeH.code()); - Workspace.addSource("caller1.hh", Caller1H.code()); - Workspace.addSource("caller2.hh", Caller2H.code()); - Workspace.addMainFile("callee.cc", CalleeC.code()); - Workspace.addMainFile("caller1.cc", Caller1C.code()); - Workspace.addMainFile("caller2.cc", Caller2C.code()); - Workspace.addMainFile("caller3.cc", Caller3C.code()); + verifyIncomingMultiFile(".cc", ".hh", CalleeH, Caller1H, Caller2H, CalleeC, + Caller1C, Caller2C, Caller3C); +} + +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 different 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"); + verifyIncomingMultiFile(".m", ".mi", CalleeH, Caller1H, Caller2H, CalleeC, + Caller1C, Caller2C, Caller3C); +} + +void verifyIncomingMultiFile(std::string SourceExt, std::string HeaderExt, + Annotations &CalleeH, Annotations &Caller1H, + Annotations &Caller2H, Annotations &CalleeC, + Annotations &Caller1C, Annotations &Caller2C, + Annotations &Caller3C) { + TestWorkspace Workspace; + Workspace.addSource("callee" + HeaderExt, CalleeH.code()); + Workspace.addSource("caller1" + HeaderExt, Caller1H.code()); + Workspace.addSource("caller2" + HeaderExt, Caller2H.code()); + Workspace.addMainFile("callee" + SourceExt, CalleeC.code()); + Workspace.addMainFile("caller1" + SourceExt, Caller1C.code()); + Workspace.addMainFile("caller2" + SourceExt, Caller2C.code()); + Workspace.addMainFile("caller3" + SourceExt, Caller3C.code()); auto Index = Workspace.index(); auto CheckCallHierarchy = [&](ParsedAST &AST, Position Pos, PathRef TUPath) { @@ -253,19 +353,19 @@ }; // Check that invoking from a call site works. - auto AST = Workspace.openFile("caller1.cc"); + auto AST = Workspace.openFile("caller1" + SourceExt); ASSERT_TRUE(bool(AST)); - CheckCallHierarchy(*AST, Caller1C.point(), testPath("caller1.cc")); + CheckCallHierarchy(*AST, Caller1C.point(), testPath("caller1" + SourceExt)); // Check that invoking from the declaration site works. - AST = Workspace.openFile("callee.hh"); + AST = Workspace.openFile("callee" + HeaderExt); ASSERT_TRUE(bool(AST)); - CheckCallHierarchy(*AST, CalleeH.point(), testPath("callee.hh")); + CheckCallHierarchy(*AST, CalleeH.point(), testPath("callee" + HeaderExt)); // Check that invoking from the definition site works. - AST = Workspace.openFile("callee.cc"); + AST = Workspace.openFile("callee" + SourceExt); ASSERT_TRUE(bool(AST)); - CheckCallHierarchy(*AST, CalleeC.point(), testPath("callee.cc")); + CheckCallHierarchy(*AST, CalleeC.point(), testPath("callee" + SourceExt)); } TEST(CallHierarchy, CallInLocalVarDecl) {