Skip to content

Commit c72cc8d

Browse files
[clangd] Harden incomingCalls() against possible misinterpretation of a range as pertaining to the wrong file
1 parent 9e65dca commit c72cc8d

File tree

2 files changed

+47
-5
lines changed

2 files changed

+47
-5
lines changed

clang-tools-extra/clangd/XRefs.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
#include "llvm/ADT/StringRef.h"
6464
#include "llvm/Support/Casting.h"
6565
#include "llvm/Support/Error.h"
66+
#include "llvm/Support/ErrorHandling.h"
6667
#include "llvm/Support/Path.h"
6768
#include "llvm/Support/raw_ostream.h"
6869
#include <optional>
@@ -2272,7 +2273,7 @@ incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
22722273
// Initially store the ranges in a map keyed by SymbolID of the caller.
22732274
// This allows us to group different calls with the same caller
22742275
// into the same CallHierarchyIncomingCall.
2275-
llvm::DenseMap<SymbolID, std::vector<Range>> CallsIn;
2276+
llvm::DenseMap<SymbolID, std::vector<Location>> CallsIn;
22762277
// We can populate the ranges based on a refs request only. As we do so, we
22772278
// also accumulate the container IDs into a lookup request.
22782279
LookupRequest ContainerLookup;
@@ -2282,8 +2283,8 @@ incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
22822283
elog("incomingCalls failed to convert location: {0}", Loc.takeError());
22832284
return;
22842285
}
2285-
auto It = CallsIn.try_emplace(R.Container, std::vector<Range>{}).first;
2286-
It->second.push_back(Loc->range);
2286+
auto It = CallsIn.try_emplace(R.Container, std::vector<Location>{}).first;
2287+
It->second.push_back(*Loc);
22872288

22882289
ContainerLookup.IDs.insert(R.Container);
22892290
});
@@ -2292,9 +2293,21 @@ incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
22922293
Index->lookup(ContainerLookup, [&](const Symbol &Caller) {
22932294
auto It = CallsIn.find(Caller.ID);
22942295
assert(It != CallsIn.end());
2295-
if (auto CHI = symbolToCallHierarchyItem(Caller, Item.uri.file()))
2296+
if (auto CHI = symbolToCallHierarchyItem(Caller, Item.uri.file())) {
2297+
std::vector<Range> FromRanges;
2298+
for (const Location &L : It->second) {
2299+
if (L.uri != CHI->uri) {
2300+
// Call location not in same file as caller.
2301+
// This can happen in some edge cases. There's not much we can do,
2302+
// since the protocol only allows returning ranges interpreted as
2303+
// being in the caller's file.
2304+
continue;
2305+
}
2306+
FromRanges.push_back(L.range);
2307+
}
22962308
Results.push_back(
2297-
CallHierarchyIncomingCall{std::move(*CHI), std::move(It->second)});
2309+
CallHierarchyIncomingCall{std::move(*CHI), std::move(FromRanges)});
2310+
}
22982311
});
22992312
// Sort results by name of container.
23002313
llvm::sort(Results, [](const CallHierarchyIncomingCall &A,

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,35 @@ TEST(CallHierarchy, CallInLocalVarDecl) {
446446
AllOf(from(withName("caller3")), fromRanges(Source.range("call3")))));
447447
}
448448

449+
TEST(CallHierarchy, CallInDifferentFileThanCaller) {
450+
Annotations Header(R"cpp(
451+
#define WALDO void caller() {
452+
)cpp");
453+
Annotations Source(R"cpp(
454+
void call^ee();
455+
WALDO
456+
callee();
457+
}
458+
)cpp");
459+
auto TU = TestTU::withCode(Source.code());
460+
TU.HeaderCode = Header.code();
461+
auto AST = TU.build();
462+
auto Index = TU.index();
463+
464+
std::vector<CallHierarchyItem> Items =
465+
prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
466+
ASSERT_THAT(Items, ElementsAre(withName("callee")));
467+
468+
auto Incoming = incomingCalls(Items[0], Index.get());
469+
470+
// The only call site is in the source file, which is a different file from
471+
// the declaration of the function containing the call, which is in the
472+
// header. The protocol does not allow us to represent such calls, so we drop
473+
// them. (The call hierarchy item itself is kept.)
474+
EXPECT_THAT(Incoming,
475+
ElementsAre(AllOf(from(withName("caller")), fromRanges())));
476+
}
477+
449478
} // namespace
450479
} // namespace clangd
451480
} // namespace clang

0 commit comments

Comments
 (0)