Skip to content

Commit 162b053

Browse files
[clangd] Strip invalid fromRanges for outgoing calls
`CallHierarchyOutgoingCall::fromRanges` are interpreted as ranges in the same file as the item for which 'outgoingCalls' was called. It's possible for outgoing calls to be in a different file than that item if the item is just a declaration (e.g. in a header file). Now, such calls are dropped instead of being returned to the client.
1 parent 2f6bc47 commit 162b053

File tree

2 files changed

+26
-10
lines changed

2 files changed

+26
-10
lines changed

clang-tools-extra/clangd/XRefs.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2380,7 +2380,7 @@ outgoingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
23802380
// Initially store the ranges in a map keyed by SymbolID of the callee.
23812381
// This allows us to group different calls to the same function
23822382
// into the same CallHierarchyOutgoingCall.
2383-
llvm::DenseMap<SymbolID, std::vector<Range>> CallsOut;
2383+
llvm::DenseMap<SymbolID, std::vector<Location>> CallsOut;
23842384
// We can populate the ranges based on a refs request only. As we do so, we
23852385
// also accumulate the callee IDs into a lookup request.
23862386
LookupRequest CallsOutLookup;
@@ -2390,8 +2390,8 @@ outgoingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
23902390
elog("outgoingCalls failed to convert location: {0}", Loc.takeError());
23912391
return;
23922392
}
2393-
auto It = CallsOut.try_emplace(R.Symbol, std::vector<Range>{}).first;
2394-
It->second.push_back(Loc->range);
2393+
auto It = CallsOut.try_emplace(R.Symbol, std::vector<Location>{}).first;
2394+
It->second.push_back(*Loc);
23952395

23962396
CallsOutLookup.IDs.insert(R.Symbol);
23972397
});
@@ -2411,9 +2411,22 @@ outgoingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
24112411

24122412
auto It = CallsOut.find(Callee.ID);
24132413
assert(It != CallsOut.end());
2414-
if (auto CHI = symbolToCallHierarchyItem(Callee, Item.uri.file()))
2414+
if (auto CHI = symbolToCallHierarchyItem(Callee, Item.uri.file())) {
2415+
std::vector<Range> FromRanges;
2416+
for (const Location &L : It->second) {
2417+
if (L.uri != Item.uri) {
2418+
// Call location not in same file as the item that outgoingCalls was
2419+
// requested for. This can happen when Item is a declaration separate
2420+
// from the implementation. There's not much we can do, since the
2421+
// protocol only allows returning ranges interpreted as being in
2422+
// Item's file.
2423+
continue;
2424+
}
2425+
FromRanges.push_back(L.range);
2426+
}
24152427
Results.push_back(
2416-
CallHierarchyOutgoingCall{std::move(*CHI), std::move(It->second)});
2428+
CallHierarchyOutgoingCall{std::move(*CHI), std::move(FromRanges)});
2429+
}
24172430
});
24182431
// Sort results by name of the callee.
24192432
llvm::sort(Results, [](const CallHierarchyOutgoingCall &A,

clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,8 @@ TEST(CallHierarchy, MultiFileCpp) {
383383
EXPECT_THAT(IncomingLevel4, IsEmpty());
384384
};
385385

386-
auto CheckOutgoingCalls = [&](ParsedAST &AST, Position Pos, PathRef TUPath) {
386+
auto CheckOutgoingCalls = [&](ParsedAST &AST, Position Pos, PathRef TUPath,
387+
bool IsDeclaration) {
387388
std::vector<CallHierarchyItem> Items =
388389
prepareCallHierarchy(AST, Pos, TUPath);
389390
ASSERT_THAT(Items, ElementsAre(withName("caller3")));
@@ -392,9 +393,11 @@ TEST(CallHierarchy, MultiFileCpp) {
392393
OutgoingLevel1,
393394
ElementsAre(
394395
AllOf(to(AllOf(withName("caller1"), withDetail("nsa::caller1"))),
395-
oFromRanges(Caller3C.range("Caller1"))),
396+
IsDeclaration ? oFromRanges()
397+
: oFromRanges(Caller3C.range("Caller1"))),
396398
AllOf(to(AllOf(withName("caller2"), withDetail("nsb::caller2"))),
397-
oFromRanges(Caller3C.range("Caller2")))));
399+
IsDeclaration ? oFromRanges()
400+
: oFromRanges(Caller3C.range("Caller2")))));
398401

399402
auto OutgoingLevel2 = outgoingCalls(OutgoingLevel1[1].to, Index.get());
400403
ASSERT_THAT(OutgoingLevel2,
@@ -423,15 +426,15 @@ TEST(CallHierarchy, MultiFileCpp) {
423426
CheckIncomingCalls(*AST, CalleeH.point(), testPath("callee.hh"));
424427
AST = Workspace.openFile("caller3.hh");
425428
ASSERT_TRUE(bool(AST));
426-
CheckOutgoingCalls(*AST, Caller3H.point(), testPath("caller3.hh"));
429+
CheckOutgoingCalls(*AST, Caller3H.point(), testPath("caller3.hh"), true);
427430

428431
// Check that invoking from the definition site works.
429432
AST = Workspace.openFile("callee.cc");
430433
ASSERT_TRUE(bool(AST));
431434
CheckIncomingCalls(*AST, CalleeC.point(), testPath("callee.cc"));
432435
AST = Workspace.openFile("caller3.cc");
433436
ASSERT_TRUE(bool(AST));
434-
CheckOutgoingCalls(*AST, Caller3C.point(), testPath("caller3.cc"));
437+
CheckOutgoingCalls(*AST, Caller3C.point(), testPath("caller3.cc"), false);
435438
}
436439

437440
TEST(CallHierarchy, IncomingMultiFileObjC) {

0 commit comments

Comments
 (0)