[clang-tools-extra] 378e4ed - [clangd] Show callers of base functions in incomingCalls (#163024)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Oct 20 19:27:22 PDT 2025
Author: timon-ul
Date: 2025-10-21T02:27:18Z
New Revision: 378e4ed1acf94ee911e2ccfda0045ac3fdc9e291
URL: https://github.com/llvm/llvm-project/commit/378e4ed1acf94ee911e2ccfda0045ac3fdc9e291
DIFF: https://github.com/llvm/llvm-project/commit/378e4ed1acf94ee911e2ccfda0045ac3fdc9e291.diff
LOG: [clangd] Show callers of base functions in incomingCalls (#163024)
If call hierarchy incoming calls is invoked on a virtual function, clangd
now returns the callers of base functions as well.
The patch also introduces a protocol extension to annotate such calls
differently (as they may or may not actually call the target function),
so that clients can visualize these callers differently if they wish.
Added:
Modified:
clang-tools-extra/clangd/Protocol.h
clang-tools-extra/clangd/XRefs.cpp
clang-tools-extra/clangd/index/Index.cpp
clang-tools-extra/clangd/index/Index.h
clang-tools-extra/clangd/index/MemIndex.cpp
clang-tools-extra/clangd/index/MemIndex.h
clang-tools-extra/clangd/index/Merge.cpp
clang-tools-extra/clangd/index/Merge.h
clang-tools-extra/clangd/index/ProjectAware.cpp
clang-tools-extra/clangd/index/dex/Dex.cpp
clang-tools-extra/clangd/index/dex/Dex.h
clang-tools-extra/clangd/index/remote/Client.cpp
clang-tools-extra/clangd/index/remote/Service.proto
clang-tools-extra/clangd/index/remote/server/Server.cpp
clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp
clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
clang-tools-extra/clangd/unittests/RenameTests.cpp
Removed:
################################################################################
diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h
index 3a6bf155ee153..2248572060431 100644
--- a/clang-tools-extra/clangd/Protocol.h
+++ b/clang-tools-extra/clangd/Protocol.h
@@ -1627,6 +1627,12 @@ struct CallHierarchyIncomingCall {
/// The range at which the calls appear.
/// This is relative to the caller denoted by `From`.
std::vector<Range> fromRanges;
+
+ /// For the case of being a virtual function we also return calls
+ /// to the base function. This caller might be a false positive.
+ /// We currently have no way of discerning this.
+ /// This is a clangd extension.
+ bool mightNeverCall = false;
};
llvm::json::Value toJSON(const CallHierarchyIncomingCall &);
diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp
index 05e04ac161e54..ef45acf501612 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -21,6 +21,7 @@
#include "clang-include-cleaner/Types.h"
#include "index/Index.h"
#include "index/Merge.h"
+#include "index/Ref.h"
#include "index/Relation.h"
#include "index/SymbolCollector.h"
#include "index/SymbolID.h"
@@ -56,6 +57,7 @@
#include "clang/Tooling/Syntax/Tokens.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallSet.h"
@@ -66,6 +68,7 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
#include <optional>
#include <string>
#include <vector>
@@ -2350,51 +2353,64 @@ incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
// to an AST node isn't cheap, particularly when the declaration isn't
// in the main file.
// FIXME: Consider also using AST information when feasible.
- RefsRequest Request;
- Request.IDs.insert(*ID);
- Request.WantContainer = true;
- // We could restrict more specifically to calls by introducing a new RefKind,
- // but non-call references (such as address-of-function) can still be
- // interesting as they can indicate indirect calls.
- Request.Filter = RefKind::Reference;
- // Initially store the ranges in a map keyed by SymbolID of the caller.
- // This allows us to group
diff erent calls with the same caller
- // into the same CallHierarchyIncomingCall.
- llvm::DenseMap<SymbolID, std::vector<Location>> CallsIn;
- // We can populate the ranges based on a refs request only. As we do so, we
- // also accumulate the container IDs into a lookup request.
- LookupRequest ContainerLookup;
- Index->refs(Request, [&](const Ref &R) {
- auto Loc = indexToLSPLocation(R.Location, Item.uri.file());
- if (!Loc) {
- elog("incomingCalls failed to convert location: {0}", Loc.takeError());
- return;
- }
- CallsIn[R.Container].push_back(*Loc);
+ auto QueryIndex = [&](llvm::DenseSet<SymbolID> IDs, bool MightNeverCall) {
+ RefsRequest Request;
+ Request.IDs = std::move(IDs);
+ Request.WantContainer = true;
+ // We could restrict more specifically to calls by introducing a new
+ // RefKind, but non-call references (such as address-of-function) can still
+ // be interesting as they can indicate indirect calls.
+ Request.Filter = RefKind::Reference;
+ // Initially store the ranges in a map keyed by SymbolID of the caller.
+ // This allows us to group
diff erent calls with the same caller
+ // into the same CallHierarchyIncomingCall.
+ llvm::DenseMap<SymbolID, std::vector<Location>> CallsIn;
+ // We can populate the ranges based on a refs request only. As we do so, we
+ // also accumulate the container IDs into a lookup request.
+ LookupRequest ContainerLookup;
+ Index->refs(Request, [&](const Ref &R) {
+ auto Loc = indexToLSPLocation(R.Location, Item.uri.file());
+ if (!Loc) {
+ elog("incomingCalls failed to convert location: {0}", Loc.takeError());
+ return;
+ }
+ CallsIn[R.Container].push_back(*Loc);
- ContainerLookup.IDs.insert(R.Container);
- });
- // Perform the lookup request and combine its results with CallsIn to
- // get complete CallHierarchyIncomingCall objects.
- Index->lookup(ContainerLookup, [&](const Symbol &Caller) {
- auto It = CallsIn.find(Caller.ID);
- assert(It != CallsIn.end());
- if (auto CHI = symbolToCallHierarchyItem(Caller, Item.uri.file())) {
- std::vector<Range> FromRanges;
- for (const Location &L : It->second) {
- if (L.uri != CHI->uri) {
- // Call location not in same file as caller.
- // This can happen in some edge cases. There's not much we can do,
- // since the protocol only allows returning ranges interpreted as
- // being in the caller's file.
- continue;
+ ContainerLookup.IDs.insert(R.Container);
+ });
+ // Perform the lookup request and combine its results with CallsIn to
+ // get complete CallHierarchyIncomingCall objects.
+ Index->lookup(ContainerLookup, [&](const Symbol &Caller) {
+ auto It = CallsIn.find(Caller.ID);
+ assert(It != CallsIn.end());
+ if (auto CHI = symbolToCallHierarchyItem(Caller, Item.uri.file())) {
+ std::vector<Range> FromRanges;
+ for (const Location &L : It->second) {
+ if (L.uri != CHI->uri) {
+ // Call location not in same file as caller.
+ // This can happen in some edge cases. There's not much we can do,
+ // since the protocol only allows returning ranges interpreted as
+ // being in the caller's file.
+ continue;
+ }
+ FromRanges.push_back(L.range);
}
- FromRanges.push_back(L.range);
+ Results.push_back(CallHierarchyIncomingCall{
+ std::move(*CHI), std::move(FromRanges), MightNeverCall});
}
- Results.push_back(
- CallHierarchyIncomingCall{std::move(*CHI), std::move(FromRanges)});
- }
- });
+ });
+ };
+ QueryIndex({ID.get()}, false);
+ // In the case of being a virtual function we also want to return
+ // potential calls through the base function.
+ if (Item.kind == SymbolKind::Method) {
+ llvm::DenseSet<SymbolID> IDs;
+ RelationsRequest Req{{ID.get()}, RelationKind::OverriddenBy, std::nullopt};
+ Index->reverseRelations(Req, [&](const SymbolID &, const Symbol &Caller) {
+ IDs.insert(Caller.ID);
+ });
+ QueryIndex(std::move(IDs), true);
+ }
// Sort results by name of container.
llvm::sort(Results, [](const CallHierarchyIncomingCall &A,
const CallHierarchyIncomingCall &B) {
diff --git a/clang-tools-extra/clangd/index/Index.cpp b/clang-tools-extra/clangd/index/Index.cpp
index 86dc6ed763344..a2ec910606d92 100644
--- a/clang-tools-extra/clangd/index/Index.cpp
+++ b/clang-tools-extra/clangd/index/Index.cpp
@@ -77,6 +77,12 @@ void SwapIndex::relations(
return snapshot()->relations(R, CB);
}
+void SwapIndex::reverseRelations(
+ const RelationsRequest &R,
+ llvm::function_ref<void(const SymbolID &, const Symbol &)> CB) const {
+ return snapshot()->reverseRelations(R, CB);
+}
+
llvm::unique_function<IndexContents(llvm::StringRef) const>
SwapIndex::indexedFiles() const {
// The index snapshot should outlive this method return value.
diff --git a/clang-tools-extra/clangd/index/Index.h b/clang-tools-extra/clangd/index/Index.h
index a193b1a191216..b62b15d103112 100644
--- a/clang-tools-extra/clangd/index/Index.h
+++ b/clang-tools-extra/clangd/index/Index.h
@@ -181,6 +181,14 @@ class SymbolIndex {
llvm::function_ref<void(const SymbolID &Subject, const Symbol &Object)>
Callback) const = 0;
+ /// Finds all relations (O, P, S) stored in the index such that S is among
+ /// Req.Subjects and P is Req.Predicate, and invokes \p Callback for (S, O) in
+ /// each. Currently only allows the OverriddenBy relation.
+ virtual void reverseRelations(
+ const RelationsRequest &Req,
+ llvm::function_ref<void(const SymbolID &Subject, const Symbol &Object)>
+ Callback) const = 0;
+
/// Returns function which checks if the specified file was used to build this
/// index or not. The function must only be called while the index is alive.
using IndexedFiles =
@@ -214,6 +222,11 @@ class SwapIndex : public SymbolIndex {
llvm::function_ref<void(const SymbolID &, const Symbol &)>)
const override;
+ void
+ reverseRelations(const RelationsRequest &,
+ llvm::function_ref<void(const SymbolID &, const Symbol &)>)
+ const override;
+
llvm::unique_function<IndexContents(llvm::StringRef) const>
indexedFiles() const override;
diff --git a/clang-tools-extra/clangd/index/MemIndex.cpp b/clang-tools-extra/clangd/index/MemIndex.cpp
index 9c9d3942bdee6..feac1cf4fb7a7 100644
--- a/clang-tools-extra/clangd/index/MemIndex.cpp
+++ b/clang-tools-extra/clangd/index/MemIndex.cpp
@@ -125,6 +125,27 @@ void MemIndex::relations(
}
}
+void MemIndex::reverseRelations(
+ const RelationsRequest &Req,
+ llvm::function_ref<void(const SymbolID &, const Symbol &)> Callback) const {
+ assert(Req.Predicate == RelationKind::OverriddenBy);
+ uint32_t Remaining = Req.Limit.value_or(std::numeric_limits<uint32_t>::max());
+ for (const SymbolID &Subject : Req.Subjects) {
+ LookupRequest LookupReq;
+ auto It = ReverseRelations.find(
+ std::make_pair(Subject, static_cast<uint8_t>(Req.Predicate)));
+ if (It != ReverseRelations.end()) {
+ for (const auto &Obj : It->second) {
+ if (Remaining > 0) {
+ --Remaining;
+ LookupReq.IDs.insert(Obj);
+ }
+ }
+ }
+ lookup(LookupReq, [&](const Symbol &Object) { Callback(Subject, Object); });
+ }
+}
+
llvm::unique_function<IndexContents(llvm::StringRef) const>
MemIndex::indexedFiles() const {
return [this](llvm::StringRef FileURI) {
@@ -134,7 +155,8 @@ MemIndex::indexedFiles() const {
size_t MemIndex::estimateMemoryUsage() const {
return Index.getMemorySize() + Refs.getMemorySize() +
- Relations.getMemorySize() + BackingDataSize;
+ Relations.getMemorySize() + ReverseRelations.getMemorySize() +
+ BackingDataSize;
}
} // namespace clangd
diff --git a/clang-tools-extra/clangd/index/MemIndex.h b/clang-tools-extra/clangd/index/MemIndex.h
index fb1052b0c7ca8..8ece9994872a9 100644
--- a/clang-tools-extra/clangd/index/MemIndex.h
+++ b/clang-tools-extra/clangd/index/MemIndex.h
@@ -10,6 +10,7 @@
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_MEMINDEX_H
#include "index/Index.h"
+#include "index/Relation.h"
#include "llvm/ADT/StringSet.h"
#include <mutex>
@@ -27,10 +28,16 @@ class MemIndex : public SymbolIndex {
Index[S.ID] = &S;
for (const std::pair<SymbolID, llvm::ArrayRef<Ref>> &R : Refs)
this->Refs.try_emplace(R.first, R.second.begin(), R.second.end());
- for (const Relation &R : Relations)
+ for (const Relation &R : Relations) {
this->Relations[std::make_pair(R.Subject,
static_cast<uint8_t>(R.Predicate))]
.push_back(R.Object);
+ if (R.Predicate == RelationKind::OverriddenBy) {
+ this->ReverseRelations[std::make_pair(
+ R.Object, static_cast<uint8_t>(R.Predicate))]
+ .push_back(R.Subject);
+ }
+ }
}
// Symbols are owned by BackingData, Index takes ownership.
template <typename SymbolRange, typename RefRange, typename RelationRange,
@@ -80,6 +87,11 @@ class MemIndex : public SymbolIndex {
llvm::function_ref<void(const SymbolID &, const Symbol &)>
Callback) const override;
+ void
+ reverseRelations(const RelationsRequest &Req,
+ llvm::function_ref<void(const SymbolID &, const Symbol &)>
+ Callback) const override;
+
llvm::unique_function<IndexContents(llvm::StringRef) const>
indexedFiles() const override;
@@ -94,6 +106,9 @@ class MemIndex : public SymbolIndex {
static_assert(sizeof(RelationKind) == sizeof(uint8_t),
"RelationKind should be of same size as a uint8_t");
llvm::DenseMap<std::pair<SymbolID, uint8_t>, std::vector<SymbolID>> Relations;
+ // Reverse relations, currently only for OverriddenBy
+ llvm::DenseMap<std::pair<SymbolID, uint8_t>, std::vector<SymbolID>>
+ ReverseRelations;
// Set of files which were used during this index build.
llvm::StringSet<> Files;
// Contents of the index (symbols, references, etc.)
diff --git a/clang-tools-extra/clangd/index/Merge.cpp b/clang-tools-extra/clangd/index/Merge.cpp
index aecca38a885b6..625b1c6926a28 100644
--- a/clang-tools-extra/clangd/index/Merge.cpp
+++ b/clang-tools-extra/clangd/index/Merge.cpp
@@ -221,6 +221,32 @@ void MergedIndex::relations(
});
}
+void MergedIndex::reverseRelations(
+ const RelationsRequest &Req,
+ llvm::function_ref<void(const SymbolID &, const Symbol &)> Callback) const {
+ uint32_t Remaining = Req.Limit.value_or(std::numeric_limits<uint32_t>::max());
+ // Return results from both indexes but avoid duplicates.
+ // We might return stale relations from the static index;
+ // we don't currently have a good way of identifying them.
+ llvm::DenseSet<std::pair<SymbolID, SymbolID>> SeenRelations;
+ Dynamic->reverseRelations(
+ Req, [&](const SymbolID &Subject, const Symbol &Object) {
+ Callback(Subject, Object);
+ SeenRelations.insert(std::make_pair(Subject, Object.ID));
+ --Remaining;
+ });
+ if (Remaining == 0)
+ return;
+ Static->reverseRelations(
+ Req, [&](const SymbolID &Subject, const Symbol &Object) {
+ if (Remaining > 0 &&
+ !SeenRelations.count(std::make_pair(Subject, Object.ID))) {
+ --Remaining;
+ Callback(Subject, Object);
+ }
+ });
+}
+
// Returns true if \p L is (strictly) preferred to \p R (e.g. by file paths). If
// neither is preferred, this returns false.
static bool prefer(const SymbolLocation &L, const SymbolLocation &R) {
diff --git a/clang-tools-extra/clangd/index/Merge.h b/clang-tools-extra/clangd/index/Merge.h
index 7441be6e57e85..5910c27cab58a 100644
--- a/clang-tools-extra/clangd/index/Merge.h
+++ b/clang-tools-extra/clangd/index/Merge.h
@@ -44,6 +44,10 @@ class MergedIndex : public SymbolIndex {
void relations(const RelationsRequest &,
llvm::function_ref<void(const SymbolID &, const Symbol &)>)
const override;
+ void
+ reverseRelations(const RelationsRequest &,
+ llvm::function_ref<void(const SymbolID &, const Symbol &)>)
+ const override;
llvm::unique_function<IndexContents(llvm::StringRef) const>
indexedFiles() const override;
size_t estimateMemoryUsage() const override {
diff --git a/clang-tools-extra/clangd/index/ProjectAware.cpp b/clang-tools-extra/clangd/index/ProjectAware.cpp
index 9836f0130362a..34d037b854e3d 100644
--- a/clang-tools-extra/clangd/index/ProjectAware.cpp
+++ b/clang-tools-extra/clangd/index/ProjectAware.cpp
@@ -51,6 +51,11 @@ class ProjectAwareIndex : public SymbolIndex {
llvm::function_ref<void(const SymbolID &, const Symbol &)>
Callback) const override;
+ void
+ reverseRelations(const RelationsRequest &,
+ llvm::function_ref<void(const SymbolID &, const Symbol &)>)
+ const override;
+
llvm::unique_function<IndexContents(llvm::StringRef) const>
indexedFiles() const override;
@@ -124,6 +129,14 @@ void ProjectAwareIndex::relations(
return Idx->relations(Req, Callback);
}
+void ProjectAwareIndex::reverseRelations(
+ const RelationsRequest &Req,
+ llvm::function_ref<void(const SymbolID &, const Symbol &)> Callback) const {
+ trace::Span Tracer("ProjectAwareIndex::relations");
+ if (auto *Idx = getIndex())
+ return Idx->reverseRelations(Req, Callback);
+}
+
llvm::unique_function<IndexContents(llvm::StringRef) const>
ProjectAwareIndex::indexedFiles() const {
trace::Span Tracer("ProjectAwareIndex::indexedFiles");
diff --git a/clang-tools-extra/clangd/index/dex/Dex.cpp b/clang-tools-extra/clangd/index/dex/Dex.cpp
index 575a96a112979..179d8c4da0b3e 100644
--- a/clang-tools-extra/clangd/index/dex/Dex.cpp
+++ b/clang-tools-extra/clangd/index/dex/Dex.cpp
@@ -379,6 +379,28 @@ void Dex::relations(
}
}
+void Dex::reverseRelations(
+ const RelationsRequest &Req,
+ llvm::function_ref<void(const SymbolID &, const Symbol &)> Callback) const {
+ trace::Span Tracer("Dex reverseRelations");
+ assert(Req.Predicate == RelationKind::OverriddenBy);
+ uint32_t Remaining = Req.Limit.value_or(std::numeric_limits<uint32_t>::max());
+ for (const SymbolID &Subject : Req.Subjects) {
+ LookupRequest LookupReq;
+ auto It = ReverseRelations.find(
+ std::make_pair(Subject, static_cast<uint8_t>(Req.Predicate)));
+ if (It != ReverseRelations.end()) {
+ for (const auto &Obj : It->second) {
+ if (Remaining > 0) {
+ --Remaining;
+ LookupReq.IDs.insert(Obj);
+ }
+ }
+ }
+ lookup(LookupReq, [&](const Symbol &Object) { Callback(Subject, Object); });
+ }
+}
+
llvm::unique_function<IndexContents(llvm::StringRef) const>
Dex::indexedFiles() const {
return [this](llvm::StringRef FileURI) {
@@ -396,6 +418,7 @@ size_t Dex::estimateMemoryUsage() const {
Bytes += Refs.getMemorySize();
Bytes += RevRefs.size() * sizeof(RevRef);
Bytes += Relations.getMemorySize();
+ Bytes += ReverseRelations.getMemorySize();
return Bytes + BackingDataSize;
}
diff --git a/clang-tools-extra/clangd/index/dex/Dex.h b/clang-tools-extra/clangd/index/dex/Dex.h
index 502f597d81ef0..1ea7d8c06c67c 100644
--- a/clang-tools-extra/clangd/index/dex/Dex.h
+++ b/clang-tools-extra/clangd/index/dex/Dex.h
@@ -43,10 +43,16 @@ class Dex : public SymbolIndex {
this->Symbols.push_back(&Sym);
for (auto &&Ref : Refs)
this->Refs.try_emplace(Ref.first, Ref.second);
- for (auto &&Rel : Relations)
+ for (auto &&Rel : Relations) {
this->Relations[std::make_pair(Rel.Subject,
static_cast<uint8_t>(Rel.Predicate))]
.push_back(Rel.Object);
+ if (Rel.Predicate == RelationKind::OverriddenBy) {
+ this->ReverseRelations[std::make_pair(Rel.Object, static_cast<uint8_t>(
+ Rel.Predicate))]
+ .push_back(Rel.Subject);
+ }
+ }
buildIndex(SupportContainedRefs);
}
// Symbols and Refs are owned by BackingData, Index takes ownership.
@@ -96,6 +102,11 @@ class Dex : public SymbolIndex {
llvm::function_ref<void(const SymbolID &, const Symbol &)>
Callback) const override;
+ void
+ reverseRelations(const RelationsRequest &,
+ llvm::function_ref<void(const SymbolID &, const Symbol &)>)
+ const override;
+
llvm::unique_function<IndexContents(llvm::StringRef) const>
indexedFiles() const override;
@@ -142,6 +153,9 @@ class Dex : public SymbolIndex {
static_assert(sizeof(RelationKind) == sizeof(uint8_t),
"RelationKind should be of same size as a uint8_t");
llvm::DenseMap<std::pair<SymbolID, uint8_t>, std::vector<SymbolID>> Relations;
+ // Reverse relations, currently only for OverriddenBy
+ llvm::DenseMap<std::pair<SymbolID, uint8_t>, std::vector<SymbolID>>
+ ReverseRelations;
std::shared_ptr<void> KeepAlive; // poor man's move-only std::any
// Set of files which were used during this index build.
llvm::StringSet<> Files;
diff --git a/clang-tools-extra/clangd/index/remote/Client.cpp b/clang-tools-extra/clangd/index/remote/Client.cpp
index 79b827126b4ef..3b31a9fb67272 100644
--- a/clang-tools-extra/clangd/index/remote/Client.cpp
+++ b/clang-tools-extra/clangd/index/remote/Client.cpp
@@ -164,6 +164,17 @@ class IndexClient : public clangd::SymbolIndex {
});
}
+ void reverseRelations(
+ const clangd::RelationsRequest &Request,
+ llvm::function_ref<void(const SymbolID &, const clangd::Symbol &)>
+ Callback) const override {
+ streamRPC(Request, &remote::v1::SymbolIndex::Stub::ReverseRelations,
+ // Unpack protobuf Relation.
+ [&](std::pair<SymbolID, clangd::Symbol> SubjectAndObject) {
+ Callback(SubjectAndObject.first, SubjectAndObject.second);
+ });
+ }
+
llvm::unique_function<IndexContents(llvm::StringRef) const>
indexedFiles() const override {
// FIXME: For now we always return IndexContents::None regardless of whether
diff --git a/clang-tools-extra/clangd/index/remote/Service.proto b/clang-tools-extra/clangd/index/remote/Service.proto
index 43023321cb9e1..3223298f608fc 100644
--- a/clang-tools-extra/clangd/index/remote/Service.proto
+++ b/clang-tools-extra/clangd/index/remote/Service.proto
@@ -24,4 +24,6 @@ service SymbolIndex {
rpc ContainedRefs(ContainedRefsRequest) returns (stream ContainedRefsReply) {}
rpc Relations(RelationsRequest) returns (stream RelationsReply) {}
+
+ rpc ReverseRelations(RelationsRequest) returns (stream RelationsReply) {}
}
diff --git a/clang-tools-extra/clangd/index/remote/server/Server.cpp b/clang-tools-extra/clangd/index/remote/server/Server.cpp
index 890b6c27ed928..af9e9c3c8ff71 100644
--- a/clang-tools-extra/clangd/index/remote/server/Server.cpp
+++ b/clang-tools-extra/clangd/index/remote/server/Server.cpp
@@ -351,6 +351,54 @@ class RemoteIndexServer final : public v1::SymbolIndex::Service {
return grpc::Status::OK;
}
+ grpc::Status
+ ReverseRelations(grpc::ServerContext *Context,
+ const RelationsRequest *Request,
+ grpc::ServerWriter<RelationsReply> *Reply) override {
+ auto StartTime = stopwatch::now();
+ WithContextValue WithRequestContext(CurrentRequest, Context);
+ logRequest(*Request);
+ trace::Span Tracer("ReverseRelationsRequest");
+ auto Req = ProtobufMarshaller->fromProtobuf(Request);
+ if (!Req) {
+ elog("Can not parse ReverseRelationsRequest from protobuf: {0}",
+ Req.takeError());
+ return grpc::Status::CANCELLED;
+ }
+ if (!Req->Limit || *Req->Limit > LimitResults) {
+ log("[public] Limiting result size for ReverseRelations request from {0} "
+ "to "
+ "{1}.",
+ Req->Limit, LimitResults);
+ Req->Limit = LimitResults;
+ }
+ unsigned Sent = 0;
+ unsigned FailedToSend = 0;
+ Index.reverseRelations(
+ *Req, [&](const SymbolID &Subject, const clangd::Symbol &Object) {
+ auto SerializedItem = ProtobufMarshaller->toProtobuf(Subject, Object);
+ if (!SerializedItem) {
+ elog("Unable to convert Relation to protobuf: {0}",
+ SerializedItem.takeError());
+ ++FailedToSend;
+ return;
+ }
+ RelationsReply NextMessage;
+ *NextMessage.mutable_stream_result() = *SerializedItem;
+ logResponse(NextMessage);
+ Reply->Write(NextMessage);
+ ++Sent;
+ });
+ RelationsReply LastMessage;
+ LastMessage.mutable_final_result()->set_has_more(true);
+ logResponse(LastMessage);
+ Reply->Write(LastMessage);
+ SPAN_ATTACH(Tracer, "Sent", Sent);
+ SPAN_ATTACH(Tracer, "Failed to send", FailedToSend);
+ logRequestSummary("v1/ReverseRelations", Sent, StartTime);
+ return grpc::Status::OK;
+ }
+
// Proxy object to allow proto messages to be lazily serialized as text.
struct TextProto {
const google::protobuf::Message &M;
diff --git a/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp b/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp
index 08cc80ff8981e..9859577c7cf7e 100644
--- a/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CallHierarchyTests.cpp
@@ -162,6 +162,43 @@ TEST(CallHierarchy, IncomingOneFileObjC) {
EXPECT_THAT(IncomingLevel4, IsEmpty());
}
+TEST(CallHierarchy, IncomingIncludeOverrides) {
+ Annotations Source(R"cpp(
+ void call^ee() {}
+ struct Interface {
+ virtual void Func() = 0;
+ };
+ struct Implementation : public Interface {
+ void Func() override {
+ $Callee[[callee]]();
+ }
+ };
+ void Test(Interface& cls){
+ cls.$FuncCall[[Func]]();
+ }
+ )cpp");
+ TestTU TU = TestTU::withCode(Source.code());
+ 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(AllOf(withName("Func"),
+ withDetail("Implementation::Func"))),
+ iFromRanges(Source.range("Callee")))));
+ auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get());
+ ASSERT_THAT(
+ IncomingLevel2,
+ ElementsAre(AllOf(from(AllOf(withName("Test"), withDetail("Test"))),
+ iFromRanges(Source.range("FuncCall")))));
+
+ auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get());
+ EXPECT_THAT(IncomingLevel3, IsEmpty());
+}
+
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
diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
index 768f88f177e56..e2bdb0fe46e37 100644
--- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -1852,6 +1852,11 @@ class IndexRequestCollector : public SymbolIndex {
llvm::function_ref<void(const SymbolID &, const Symbol &)>)
const override {}
+ void
+ reverseRelations(const RelationsRequest &,
+ llvm::function_ref<void(const SymbolID &, const Symbol &)>)
+ const override {}
+
llvm::unique_function<IndexContents(llvm::StringRef) const>
indexedFiles() const override {
return [](llvm::StringRef) { return IndexContents::None; };
diff --git a/clang-tools-extra/clangd/unittests/RenameTests.cpp b/clang-tools-extra/clangd/unittests/RenameTests.cpp
index 5d2a77b62a219..42279b51230e7 100644
--- a/clang-tools-extra/clangd/unittests/RenameTests.cpp
+++ b/clang-tools-extra/clangd/unittests/RenameTests.cpp
@@ -1674,6 +1674,11 @@ TEST(CrossFileRenameTests, DirtyBuffer) {
llvm::function_ref<void(const SymbolID &, const Symbol &)>
Callback) const override {}
+ void
+ reverseRelations(const RelationsRequest &Req,
+ llvm::function_ref<void(const SymbolID &, const Symbol &)>
+ Callback) const override {}
+
llvm::unique_function<IndexContents(llvm::StringRef) const>
indexedFiles() const override {
return [](llvm::StringRef) { return IndexContents::None; };
@@ -1729,6 +1734,11 @@ TEST(CrossFileRenameTests, DeduplicateRefsFromIndex) {
llvm::function_ref<void(const SymbolID &, const Symbol &)>)
const override {}
+ void
+ reverseRelations(const RelationsRequest &,
+ llvm::function_ref<void(const SymbolID &, const Symbol &)>)
+ const override {}
+
llvm::unique_function<IndexContents(llvm::StringRef) const>
indexedFiles() const override {
return [](llvm::StringRef) { return IndexContents::None; };
More information about the cfe-commits
mailing list