[clang-tools-extra] d408c34 - [clangd] Add extension for adding context (enclosing function or class) in references results
Tom Praschan via cfe-commits
cfe-commits at lists.llvm.org
Sun Jan 1 05:30:38 PST 2023
Author: Tom Praschan
Date: 2023-01-01T15:25:18+01:00
New Revision: d408c34d1f588e518b0ee15c00b55db131944cfb
URL: https://github.com/llvm/llvm-project/commit/d408c34d1f588e518b0ee15c00b55db131944cfb
DIFF: https://github.com/llvm/llvm-project/commit/d408c34d1f588e518b0ee15c00b55db131944cfb.diff
LOG: [clangd] Add extension for adding context (enclosing function or class) in references results
Relevant issue: https://github.com/clangd/clangd/issues/177
Reviewed By: nridge
Differential Revision: https://reviews.llvm.org/D137894
Added:
clang-tools-extra/clangd/test/references-container.test
Modified:
clang-tools-extra/clangd/ClangdLSPServer.cpp
clang-tools-extra/clangd/ClangdLSPServer.h
clang-tools-extra/clangd/ClangdServer.cpp
clang-tools-extra/clangd/ClangdServer.h
clang-tools-extra/clangd/Protocol.cpp
clang-tools-extra/clangd/Protocol.h
clang-tools-extra/clangd/XRefs.cpp
clang-tools-extra/clangd/XRefs.h
clang-tools-extra/clangd/index/SymbolCollector.cpp
clang-tools-extra/clangd/index/SymbolCollector.h
clang-tools-extra/clangd/unittests/XRefsTests.cpp
Removed:
################################################################################
diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp
index 6259057e9cf47..8f5c8fa702429 100644
--- a/clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -485,6 +485,7 @@ void ClangdLSPServer::onInitialize(const InitializeParams &Params,
SupportsCodeAction = Params.capabilities.CodeActionStructure;
SupportsHierarchicalDocumentSymbol =
Params.capabilities.HierarchicalDocumentSymbol;
+ SupportsReferenceContainer = Params.capabilities.ReferenceContainer;
SupportFileStatus = Params.initializationOptions.FileStatus;
HoverContentFormat = Params.capabilities.HoverContentFormat;
Opts.LineFoldingOnly = Params.capabilities.LineFoldingOnly;
@@ -1375,25 +1376,27 @@ void ClangdLSPServer::onChangeConfiguration(
applyConfiguration(Params.settings);
}
-void ClangdLSPServer::onReference(const ReferenceParams &Params,
- Callback<std::vector<Location>> Reply) {
- Server->findReferences(
- Params.textDocument.uri.file(), Params.position, Opts.ReferencesLimit,
- [Reply = std::move(Reply),
- IncludeDecl(Params.context.includeDeclaration)](
- llvm::Expected<ReferencesResult> Refs) mutable {
- if (!Refs)
- return Reply(Refs.takeError());
- // Filter out declarations if the client asked.
- std::vector<Location> Result;
- Result.reserve(Refs->References.size());
- for (auto &Ref : Refs->References) {
- bool IsDecl = Ref.Attributes & ReferencesResult::Declaration;
- if (IncludeDecl || !IsDecl)
- Result.push_back(std::move(Ref.Loc));
- }
- return Reply(std::move(Result));
- });
+void ClangdLSPServer::onReference(
+ const ReferenceParams &Params,
+ Callback<std::vector<ReferenceLocation>> Reply) {
+ Server->findReferences(Params.textDocument.uri.file(), Params.position,
+ Opts.ReferencesLimit, SupportsReferenceContainer,
+ [Reply = std::move(Reply),
+ IncludeDecl(Params.context.includeDeclaration)](
+ llvm::Expected<ReferencesResult> Refs) mutable {
+ if (!Refs)
+ return Reply(Refs.takeError());
+ // Filter out declarations if the client asked.
+ std::vector<ReferenceLocation> Result;
+ Result.reserve(Refs->References.size());
+ for (auto &Ref : Refs->References) {
+ bool IsDecl =
+ Ref.Attributes & ReferencesResult::Declaration;
+ if (IncludeDecl || !IsDecl)
+ Result.push_back(std::move(Ref.Loc));
+ }
+ return Reply(std::move(Result));
+ });
}
void ClangdLSPServer::onGoToType(const TextDocumentPositionParams &Params,
diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h b/clang-tools-extra/clangd/ClangdLSPServer.h
index bbf72d8a54c76..43b5e748075d7 100644
--- a/clang-tools-extra/clangd/ClangdLSPServer.h
+++ b/clang-tools-extra/clangd/ClangdLSPServer.h
@@ -120,7 +120,7 @@ class ClangdLSPServer : private ClangdServer::Callbacks,
Callback<std::vector<Location>>);
void onGoToImplementation(const TextDocumentPositionParams &,
Callback<std::vector<Location>>);
- void onReference(const ReferenceParams &, Callback<std::vector<Location>>);
+ void onReference(const ReferenceParams &, Callback<std::vector<ReferenceLocation>>);
void onSwitchSourceHeader(const TextDocumentIdentifier &,
Callback<std::optional<URIForFile>>);
void onDocumentHighlight(const TextDocumentPositionParams &,
@@ -262,6 +262,8 @@ class ClangdLSPServer : private ClangdServer::Callbacks,
bool SupportsHierarchicalDocumentSymbol = false;
/// Whether the client supports showing file status.
bool SupportFileStatus = false;
+ /// Whether the client supports attaching a container string to references.
+ bool SupportsReferenceContainer = false;
/// Which kind of markup should we use in textDocument/hover responses.
MarkupKind HoverContentFormat = MarkupKind::PlainText;
/// Whether the client supports offsets for parameter info labels.
diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp
index 349a53d4dc14e..2bde2a9e463bf 100644
--- a/clang-tools-extra/clangd/ClangdServer.cpp
+++ b/clang-tools-extra/clangd/ClangdServer.cpp
@@ -897,12 +897,13 @@ void ClangdServer::findImplementations(
}
void ClangdServer::findReferences(PathRef File, Position Pos, uint32_t Limit,
+ bool AddContainer,
Callback<ReferencesResult> CB) {
- auto Action = [Pos, Limit, CB = std::move(CB),
+ auto Action = [Pos, Limit, AddContainer, CB = std::move(CB),
this](llvm::Expected<InputsAndAST> InpAST) mutable {
if (!InpAST)
return CB(InpAST.takeError());
- CB(clangd::findReferences(InpAST->AST, Pos, Limit, Index));
+ CB(clangd::findReferences(InpAST->AST, Pos, Limit, Index, AddContainer));
};
WorkScheduler->runWithAST("References", File, std::move(Action));
diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h
index 8ad6300ec294d..98c67b8ef23cd 100644
--- a/clang-tools-extra/clangd/ClangdServer.h
+++ b/clang-tools-extra/clangd/ClangdServer.h
@@ -299,7 +299,7 @@ class ClangdServer {
/// Retrieve locations for symbol references.
void findReferences(PathRef File, Position Pos, uint32_t Limit,
- Callback<ReferencesResult> CB);
+ bool AddContainer, Callback<ReferencesResult> CB);
/// Run formatting for the \p File with content \p Code.
/// If \p Rng is non-null, formats only that region.
diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp
index 8b833c0f3f8b2..d3e92a4dbd7d6 100644
--- a/clang-tools-extra/clangd/Protocol.cpp
+++ b/clang-tools-extra/clangd/Protocol.cpp
@@ -161,6 +161,22 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Location &L) {
return OS << L.range << '@' << L.uri;
}
+llvm::json::Value toJSON(const ReferenceLocation &P) {
+ llvm::json::Object Result{
+ {"uri", P.uri},
+ {"range", P.range},
+ };
+ if (P.containerName)
+ Result.insert({"containerName", P.containerName});
+ return Result;
+}
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+ const ReferenceLocation &L) {
+ return OS << L.range << '@' << L.uri << " (container: " << L.containerName
+ << ")";
+}
+
bool fromJSON(const llvm::json::Value &Params, TextDocumentItem &R,
llvm::json::Path P) {
llvm::json::ObjectMapper O(Params, P);
@@ -332,6 +348,9 @@ bool fromJSON(const llvm::json::Value &Params, ClientCapabilities &R,
if (auto RelatedInfo = Diagnostics->getBoolean("relatedInformation"))
R.DiagnosticRelatedInformation = *RelatedInfo;
}
+ if (auto *References = TextDocument->getObject("references"))
+ if (auto ContainerSupport = References->getBoolean("container"))
+ R.ReferenceContainer = *ContainerSupport;
if (auto *Completion = TextDocument->getObject("completion")) {
if (auto *Item = Completion->getObject("completionItem")) {
if (auto SnippetSupport = Item->getBoolean("snippetSupport"))
diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h
index 96815b7011cb1..d68efad691757 100644
--- a/clang-tools-extra/clangd/Protocol.h
+++ b/clang-tools-extra/clangd/Protocol.h
@@ -228,6 +228,16 @@ struct Location {
llvm::json::Value toJSON(const Location &);
llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Location &);
+/// Extends Locations returned by textDocument/references with extra info.
+/// This is a clangd extension: LSP uses `Location`.
+struct ReferenceLocation : Location {
+ /// clangd extension: contains the name of the function or class in which the
+ /// reference occurs
+ std::optional<std::string> containerName;
+};
+llvm::json::Value toJSON(const ReferenceLocation &);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &, const ReferenceLocation &);
+
struct TextEdit {
/// The range of the text document to be manipulated. To insert
/// text into a document create a range where start === end.
@@ -429,6 +439,10 @@ struct ClientCapabilities {
/// textDocument.completion.editsNearCursor
bool CompletionFixes = false;
+ /// Client supports displaying a container string for results of
+ /// textDocument/reference (clangd extension)
+ bool ReferenceContainer = false;
+
/// Client supports hierarchical document symbols.
/// textDocument.documentSymbol.hierarchicalDocumentSymbolSupport
bool HierarchicalDocumentSymbol = false;
diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp
index d86b91c430156..953f911b1398e 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -19,6 +19,7 @@
#include "index/Index.h"
#include "index/Merge.h"
#include "index/Relation.h"
+#include "index/SymbolCollector.h"
#include "index/SymbolID.h"
#include "index/SymbolLocation.h"
#include "support/Logger.h"
@@ -857,6 +858,7 @@ class ReferenceFinder : public index::IndexDataConsumer {
struct Reference {
syntax::Token SpelledTok;
index::SymbolRoleSet Role;
+ const Decl *Container;
Range range(const SourceManager &SM) const {
return halfOpenToRange(SM, SpelledTok.range(SM).toCharRange(SM));
@@ -919,10 +921,14 @@ class ReferenceFinder : public index::IndexDataConsumer {
if (Locs.empty())
Locs.push_back(Loc);
+ SymbolCollector::Options CollectorOpts;
+ CollectorOpts.CollectMainFileSymbols = true;
for (SourceLocation L : Locs) {
L = SM.getFileLoc(L);
if (const auto *Tok = TB.spelledTokenAt(L))
- References.push_back({*Tok, Roles});
+ References.push_back(
+ {*Tok, Roles,
+ SymbolCollector::getRefContainer(ASTNode.Parent, CollectorOpts)});
}
return true;
}
@@ -1295,10 +1301,19 @@ void getOverriddenMethods(const CXXMethodDecl *CMD,
getOverriddenMethods(Base, OverriddenMethods);
}
}
+
+llvm::Optional<std::string>
+stringifyContainerForMainFileRef(const Decl *Container) {
+ // FIXME We might also want to display the signature here
+ // When doing so, remember to also add the Signature to index results!
+ if (auto *ND = llvm::dyn_cast_if_present<NamedDecl>(Container))
+ return printQualifiedName(*ND);
+ return {};
+}
} // namespace
ReferencesResult findReferences(ParsedAST &AST, Position Pos, uint32_t Limit,
- const SymbolIndex *Index) {
+ const SymbolIndex *Index, bool AddContext) {
ReferencesResult Results;
const SourceManager &SM = AST.getSourceManager();
auto MainFilePath = AST.tuPath();
@@ -1389,6 +1404,9 @@ ReferencesResult findReferences(ParsedAST &AST, Position Pos, uint32_t Limit,
ReferencesResult::Reference Result;
Result.Loc.range = Ref.range(SM);
Result.Loc.uri = URIMainFile;
+ if (AddContext)
+ Result.Loc.containerName =
+ stringifyContainerForMainFileRef(Ref.Container);
if (Ref.Role & static_cast<unsigned>(index::SymbolRole::Declaration))
Result.Attributes |= ReferencesResult::Declaration;
// clang-index doesn't report definitions as declarations, but they are.
@@ -1399,6 +1417,11 @@ ReferencesResult findReferences(ParsedAST &AST, Position Pos, uint32_t Limit,
}
// Add decl/def of overridding methods.
if (Index && !OverriddenBy.Subjects.empty()) {
+ LookupRequest ContainerLookup;
+ // Different overrides will always be contained in
diff erent classes, so
+ // we have a one-to-one mapping between SymbolID and index here, thus we
+ // don't need to use std::vector as the map's value type.
+ llvm::DenseMap<SymbolID, size_t> RefIndexForContainer;
Index->relations(OverriddenBy, [&](const SymbolID &Subject,
const Symbol &Object) {
if (Limit && Results.References.size() >= Limit) {
@@ -1410,20 +1433,32 @@ ReferencesResult findReferences(ParsedAST &AST, Position Pos, uint32_t Limit,
const auto LSPLocDef = toLSPLocation(Object.Definition, MainFilePath);
if (LSPLocDecl && LSPLocDecl != LSPLocDef) {
ReferencesResult::Reference Result;
- Result.Loc = std::move(*LSPLocDecl);
+ Result.Loc = {std::move(*LSPLocDecl), std::nullopt};
Result.Attributes =
ReferencesResult::Declaration | ReferencesResult::Override;
+ RefIndexForContainer.insert({Object.ID, Results.References.size()});
+ ContainerLookup.IDs.insert(Object.ID);
Results.References.push_back(std::move(Result));
}
if (LSPLocDef) {
ReferencesResult::Reference Result;
- Result.Loc = std::move(*LSPLocDef);
+ Result.Loc = {std::move(*LSPLocDef), std::nullopt};
Result.Attributes = ReferencesResult::Declaration |
ReferencesResult::Definition |
ReferencesResult::Override;
+ RefIndexForContainer.insert({Object.ID, Results.References.size()});
+ ContainerLookup.IDs.insert(Object.ID);
Results.References.push_back(std::move(Result));
}
});
+
+ if (!ContainerLookup.IDs.empty() && AddContext)
+ Index->lookup(ContainerLookup, [&](const Symbol &Container) {
+ auto Ref = RefIndexForContainer.find(Container.ID);
+ assert(Ref != RefIndexForContainer.end());
+ Results.References[Ref->getSecond()].Loc.containerName =
+ Container.Scope.str() + Container.Name.str();
+ });
}
}
// Now query the index for references from other files.
@@ -1443,6 +1478,8 @@ ReferencesResult findReferences(ParsedAST &AST, Position Pos, uint32_t Limit,
Req.Limit = Limit - Results.References.size();
}
}
+ LookupRequest ContainerLookup;
+ llvm::DenseMap<SymbolID, std::vector<size_t>> RefIndicesForContainer;
Results.HasMore |= Index->refs(Req, [&](const Ref &R) {
auto LSPLoc = toLSPLocation(R.Location, MainFilePath);
// Avoid indexed results for the main file - the AST is authoritative.
@@ -1450,7 +1487,7 @@ ReferencesResult findReferences(ParsedAST &AST, Position Pos, uint32_t Limit,
(!AllowMainFileSymbols && LSPLoc->uri.file() == MainFilePath))
return;
ReferencesResult::Reference Result;
- Result.Loc = std::move(*LSPLoc);
+ Result.Loc = {std::move(*LSPLoc), std::nullopt};
if (AllowAttributes) {
if ((R.Kind & RefKind::Declaration) == RefKind::Declaration)
Result.Attributes |= ReferencesResult::Declaration;
@@ -1459,8 +1496,23 @@ ReferencesResult findReferences(ParsedAST &AST, Position Pos, uint32_t Limit,
Result.Attributes |=
ReferencesResult::Declaration | ReferencesResult::Definition;
}
+ if (AddContext) {
+ SymbolID Container = R.Container;
+ ContainerLookup.IDs.insert(Container);
+ RefIndicesForContainer[Container].push_back(Results.References.size());
+ }
Results.References.push_back(std::move(Result));
});
+
+ if (!ContainerLookup.IDs.empty() && AddContext)
+ Index->lookup(ContainerLookup, [&](const Symbol &Container) {
+ auto Ref = RefIndicesForContainer.find(Container.ID);
+ assert(Ref != RefIndicesForContainer.end());
+ auto ContainerName = Container.Scope.str() + Container.Name.str();
+ for (auto I : Ref->getSecond()) {
+ Results.References[I].Loc.containerName = ContainerName;
+ }
+ });
};
QueryIndex(std::move(IDsToQuery), /*AllowAttributes=*/true,
/*AllowMainFileSymbols=*/false);
diff --git a/clang-tools-extra/clangd/XRefs.h b/clang-tools-extra/clangd/XRefs.h
index c854b85a233ad..60e8e881958db 100644
--- a/clang-tools-extra/clangd/XRefs.h
+++ b/clang-tools-extra/clangd/XRefs.h
@@ -88,7 +88,7 @@ struct ReferencesResult {
Override = 1 << 2,
};
struct Reference {
- Location Loc;
+ ReferenceLocation Loc;
unsigned Attributes = 0;
};
std::vector<Reference> References;
@@ -112,7 +112,8 @@ std::vector<LocatedSymbol> findType(ParsedAST &AST, Position Pos);
/// Returns references of the symbol at a specified \p Pos.
/// \p Limit limits the number of results returned (0 means no limit).
ReferencesResult findReferences(ParsedAST &AST, Position Pos, uint32_t Limit,
- const SymbolIndex *Index = nullptr);
+ const SymbolIndex *Index = nullptr,
+ bool AddContext = false);
/// Get info about symbols at \p Pos.
std::vector<SymbolDetails> getSymbolInfo(ParsedAST &AST, Position Pos);
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 362959d4ce90b..4c865a4317ba5 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -153,31 +153,6 @@ llvm::Optional<RelationKind> indexableRelation(const index::SymbolRelation &R) {
return std::nullopt;
}
-// Given a ref contained in enclosing decl `Enclosing`, return
-// the decl that should be used as that ref's Ref::Container. This is
-// usually `Enclosing` itself, but in cases where `Enclosing` is not
-// indexed, we walk further up because Ref::Container should always be
-// an indexed symbol.
-// Note: we don't use DeclContext as the container as in some cases
-// it's useful to use a Decl which is not a DeclContext. For example,
-// for a ref occurring in the initializer of a namespace-scope variable,
-// it's useful to use that variable as the container, as otherwise the
-// next enclosing DeclContext would be a NamespaceDecl or TranslationUnitDecl,
-// which are both not indexed and less granular than we'd like for use cases
-// like call hierarchy.
-const Decl *getRefContainer(const Decl *Enclosing,
- const SymbolCollector::Options &Opts) {
- while (Enclosing) {
- const auto *ND = dyn_cast<NamedDecl>(Enclosing);
- if (ND && SymbolCollector::shouldCollectSymbol(*ND, ND->getASTContext(),
- Opts, true)) {
- break;
- }
- Enclosing = dyn_cast_or_null<Decl>(Enclosing->getDeclContext());
- }
- return Enclosing;
-}
-
// Check if there is an exact spelling of \p ND at \p Loc.
bool isSpelled(SourceLocation Loc, const NamedDecl &ND) {
auto Name = ND.getDeclName();
@@ -520,6 +495,19 @@ bool SymbolCollector::shouldCollectSymbol(const NamedDecl &ND,
return true;
}
+const Decl *
+SymbolCollector::getRefContainer(const Decl *Enclosing,
+ const SymbolCollector::Options &Opts) {
+ while (Enclosing) {
+ const auto *ND = dyn_cast<NamedDecl>(Enclosing);
+ if (ND && shouldCollectSymbol(*ND, ND->getASTContext(), Opts, true)) {
+ break;
+ }
+ Enclosing = dyn_cast_or_null<Decl>(Enclosing->getDeclContext());
+ }
+ return Enclosing;
+}
+
// Always return true to continue indexing.
bool SymbolCollector::handleDeclOccurrence(
const Decl *D, index::SymbolRoleSet Roles,
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.h b/clang-tools-extra/clangd/index/SymbolCollector.h
index 6e2998f2d035d..f3d8d80aa8dee 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.h
+++ b/clang-tools-extra/clangd/index/SymbolCollector.h
@@ -103,6 +103,21 @@ class SymbolCollector : public index::IndexDataConsumer {
static bool shouldCollectSymbol(const NamedDecl &ND, const ASTContext &ASTCtx,
const Options &Opts, bool IsMainFileSymbol);
+ // Given a ref contained in enclosing decl `Enclosing`, return
+ // the decl that should be used as that ref's Ref::Container. This is
+ // usually `Enclosing` itself, but in cases where `Enclosing` is not
+ // indexed, we walk further up because Ref::Container should always be
+ // an indexed symbol.
+ // Note: we don't use DeclContext as the container as in some cases
+ // it's useful to use a Decl which is not a DeclContext. For example,
+ // for a ref occurring in the initializer of a namespace-scope variable,
+ // it's useful to use that variable as the container, as otherwise the
+ // next enclosing DeclContext would be a NamespaceDecl or TranslationUnitDecl,
+ // which are both not indexed and less granular than we'd like for use cases
+ // like call hierarchy.
+ static const Decl *getRefContainer(const Decl *Enclosing,
+ const SymbolCollector::Options &Opts);
+
void initialize(ASTContext &Ctx) override;
void setPreprocessor(std::shared_ptr<Preprocessor> PP) override {
diff --git a/clang-tools-extra/clangd/test/references-container.test b/clang-tools-extra/clangd/test/references-container.test
new file mode 100644
index 0000000000000..c27ec886e731c
--- /dev/null
+++ b/clang-tools-extra/clangd/test/references-container.test
@@ -0,0 +1,37 @@
+# RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{"textDocument":{"references":{"container": true}}},"trace":"off"}}
+---
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{
+ "uri":"test:///main.cpp",
+ "languageId":"cpp",
+ "version":1,
+ "text":"void foo();void bar() { foo(); }"
+}}}
+---
+{"jsonrpc":"2.0","id":1,"method":"textDocument/references","params":{
+ "textDocument":{"uri":"test:///main.cpp"},
+ "position":{"line":0,"character":6},
+ "context":{"includeDeclaration": false}
+}}
+# CHECK: "id": 1
+# CHECK-NEXT: "jsonrpc": "2.0",
+# CHECK-NEXT: "result": [
+# CHECK-NEXT: {
+# CHECK-NEXT: "containerName": "bar",
+# CHECK-NEXT: "range": {
+# CHECK-NEXT: "end": {
+# CHECK-NEXT: "character": 27,
+# CHECK-NEXT: "line": 0
+# CHECK-NEXT: },
+# CHECK-NEXT: "start": {
+# CHECK-NEXT: "character": 24,
+# CHECK-NEXT: "line": 0
+# CHECK-NEXT: }
+# CHECK-NEXT: },
+# CHECK-NEXT: "uri": "{{.*}}/main.cpp"
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+---
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}
+---
+{"jsonrpc":"2.0","method":"exit"}
diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
index 0f06a22ee6c97..1a25b5d69d7ac 100644
--- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp
@@ -301,6 +301,9 @@ MATCHER_P3(sym, Name, Decl, DefOrNone, "") {
MATCHER_P(sym, Name, "") { return arg.Name == Name; }
MATCHER_P(rangeIs, R, "") { return arg.Loc.range == R; }
+MATCHER_P(containerIs, C, "") {
+ return arg.Loc.containerName.value_or("") == C;
+}
MATCHER_P(attrsAre, A, "") { return arg.Attributes == A; }
MATCHER_P(hasID, ID, "") { return arg.ID == ID; }
@@ -1900,28 +1903,30 @@ void checkFindRefs(llvm::StringRef Test, bool UseIndex = false) {
auto AST = TU.build();
std::vector<Matcher<ReferencesResult::Reference>> ExpectedLocations;
- for (const auto &R : T.ranges())
- ExpectedLocations.push_back(AllOf(rangeIs(R), attrsAre(0u)));
+ for (const auto &[R, Context] : T.rangesWithPayload())
+ ExpectedLocations.push_back(
+ AllOf(rangeIs(R), containerIs(Context), attrsAre(0u)));
// $def is actually shorthand for both definition and declaration.
// If we have cases that are definition-only, we should change this.
- for (const auto &R : T.ranges("def"))
- ExpectedLocations.push_back(
- AllOf(rangeIs(R), attrsAre(ReferencesResult::Definition |
- ReferencesResult::Declaration)));
- for (const auto &R : T.ranges("decl"))
- ExpectedLocations.push_back(
- AllOf(rangeIs(R), attrsAre(ReferencesResult::Declaration)));
- for (const auto &R : T.ranges("overridedecl"))
+ for (const auto &[R, Context] : T.rangesWithPayload("def"))
+ ExpectedLocations.push_back(AllOf(rangeIs(R), containerIs(Context),
+ attrsAre(ReferencesResult::Definition |
+ ReferencesResult::Declaration)));
+ for (const auto &[R, Context] : T.rangesWithPayload("decl"))
+ ExpectedLocations.push_back(AllOf(rangeIs(R), containerIs(Context),
+ attrsAre(ReferencesResult::Declaration)));
+ for (const auto &[R, Context] : T.rangesWithPayload("overridedecl"))
ExpectedLocations.push_back(AllOf(
- rangeIs(R),
+ rangeIs(R), containerIs(Context),
attrsAre(ReferencesResult::Declaration | ReferencesResult::Override)));
- for (const auto &R : T.ranges("overridedef"))
- ExpectedLocations.push_back(
- AllOf(rangeIs(R), attrsAre(ReferencesResult::Declaration |
- ReferencesResult::Definition |
- ReferencesResult::Override)));
+ for (const auto &[R, Context] : T.rangesWithPayload("overridedef"))
+ ExpectedLocations.push_back(AllOf(rangeIs(R), containerIs(Context),
+ attrsAre(ReferencesResult::Declaration |
+ ReferencesResult::Definition |
+ ReferencesResult::Override)));
for (const auto &P : T.points()) {
- EXPECT_THAT(findReferences(AST, P, 0, UseIndex ? TU.index().get() : nullptr)
+ EXPECT_THAT(findReferences(AST, P, 0, UseIndex ? TU.index().get() : nullptr,
+ /*AddContext*/ true)
.References,
UnorderedElementsAreArray(ExpectedLocations))
<< "Failed for Refs at " << P << "\n"
@@ -1933,18 +1938,18 @@ TEST(FindReferences, WithinAST) {
const char *Tests[] = {
R"cpp(// Local variable
int main() {
- int $def[[foo]];
- [[^foo]] = 2;
- int test1 = [[foo]];
+ int $def(main)[[foo]];
+ $(main)[[^foo]] = 2;
+ int test1 = $(main)[[foo]];
}
)cpp",
R"cpp(// Struct
namespace ns1 {
- struct $def[[Foo]] {};
+ struct $def(ns1)[[Foo]] {};
} // namespace ns1
int main() {
- ns1::[[Fo^o]]* Params;
+ ns1::$(main)[[Fo^o]]* Params;
}
)cpp",
@@ -1952,51 +1957,51 @@ TEST(FindReferences, WithinAST) {
class $decl[[Foo]];
class $def[[Foo]] {};
int main() {
- [[Fo^o]] foo;
+ $(main)[[Fo^o]] foo;
}
)cpp",
R"cpp(// Function
int $def[[foo]](int) {}
int main() {
- auto *X = &[[^foo]];
- [[foo]](42);
+ auto *X = &$(main)[[^foo]];
+ $(main)[[foo]](42);
}
)cpp",
R"cpp(// Field
struct Foo {
- int $def[[foo]];
- Foo() : [[foo]](0) {}
+ int $def(Foo)[[foo]];
+ Foo() : $(Foo::Foo)[[foo]](0) {}
};
int main() {
Foo f;
- f.[[f^oo]] = 1;
+ f.$(main)[[f^oo]] = 1;
}
)cpp",
R"cpp(// Method call
- struct Foo { int $decl[[foo]](); };
- int Foo::$def[[foo]]() {}
+ struct Foo { int $decl(Foo)[[foo]](); };
+ int Foo::$def(Foo)[[foo]]() {}
int main() {
Foo f;
- f.[[^foo]]();
+ f.$(main)[[^foo]]();
}
)cpp",
R"cpp(// Constructor
struct Foo {
- $decl[[F^oo]](int);
+ $decl(Foo)[[F^oo]](int);
};
void foo() {
- Foo f = [[Foo]](42);
+ Foo f = $(foo)[[Foo]](42);
}
)cpp",
R"cpp(// Typedef
typedef int $def[[Foo]];
int main() {
- [[^Foo]] bar;
+ $(main)[[^Foo]] bar;
}
)cpp",
@@ -2004,7 +2009,7 @@ TEST(FindReferences, WithinAST) {
namespace $decl[[ns]] { // FIXME: def?
struct Foo {};
} // namespace ns
- int main() { [[^ns]]::Foo foo; }
+ int main() { $(main)[[^ns]]::Foo foo; }
)cpp",
R"cpp(// Macros
@@ -2013,17 +2018,17 @@ TEST(FindReferences, WithinAST) {
#define CAT(X, Y) X##Y
class $def[[Fo^o]] {};
void test() {
- TYPE([[Foo]]) foo;
- [[FOO]] foo2;
- TYPE(TYPE([[Foo]])) foo3;
- [[CAT]](Fo, o) foo4;
+ TYPE($(test)[[Foo]]) foo;
+ $(test)[[FOO]] foo2;
+ TYPE(TYPE($(test)[[Foo]])) foo3;
+ $(test)[[CAT]](Fo, o) foo4;
}
)cpp",
R"cpp(// Macros
#define $def[[MA^CRO]](X) (X+1)
void test() {
- int x = [[MACRO]]([[MACRO]](1));
+ int x = $[[MACRO]]($[[MACRO]](1));
}
)cpp",
@@ -2031,57 +2036,57 @@ TEST(FindReferences, WithinAST) {
int breakPreamble;
#define $def[[MA^CRO]](X) (X+1)
void test() {
- int x = [[MACRO]]([[MACRO]](1));
+ int x = $[[MACRO]]($[[MACRO]](1));
}
)cpp",
R"cpp(
int $def[[v^ar]] = 0;
- void foo(int s = [[var]]);
+ void foo(int s = $(foo)[[var]]);
)cpp",
R"cpp(
template <typename T>
class $def[[Fo^o]] {};
- void func([[Foo]]<int>);
+ void func($(func)[[Foo]]<int>);
)cpp",
R"cpp(
template <typename T>
class $def[[Foo]] {};
- void func([[Fo^o]]<int>);
+ void func($(func)[[Fo^o]]<int>);
)cpp",
R"cpp(// Not touching any identifiers.
struct Foo {
- $def[[~]]Foo() {};
+ $def(Foo)[[~]]Foo() {};
};
void foo() {
Foo f;
- f.[[^~]]Foo();
+ f.$(foo)[[^~]]Foo();
}
)cpp",
R"cpp(// Lambda capture initializer
void foo() {
- int $def[[w^aldo]] = 42;
- auto lambda = [x = [[waldo]]](){};
+ int $def(foo)[[w^aldo]] = 42;
+ auto lambda = [x = $(foo)[[waldo]]](){};
}
)cpp",
R"cpp(// Renaming alias
template <typename> class Vector {};
using $def[[^X]] = Vector<int>;
- [[X]] x1;
+ $(x1)[[X]] x1;
Vector<int> x2;
Vector<double> y;
)cpp",
R"cpp(// Dependent code
template <typename T> void $decl[[foo]](T t);
- template <typename T> void bar(T t) { [[foo]](t); } // foo in bar is uninstantiated.
- void baz(int x) { [[f^oo]](x); }
+ template <typename T> void bar(T t) { $(bar)[[foo]](t); } // foo in bar is uninstantiated.
+ void baz(int x) { $(baz)[[f^oo]](x); }
)cpp",
R"cpp(
namespace ns {
struct S{};
- void $decl[[foo]](S s);
+ void $decl(ns)[[foo]](S s);
} // namespace ns
template <typename T> void foo(T t);
// FIXME: Maybe report this foo as a ref to ns::foo (because of ADL)
@@ -2090,30 +2095,30 @@ TEST(FindReferences, WithinAST) {
void baz(int x) {
ns::S s;
bar<ns::S>(s);
- [[f^oo]](s);
+ $(baz)[[f^oo]](s);
}
)cpp",
R"cpp(// unresolved member expression
struct Foo {
- template <typename T> void $decl[[b^ar]](T t);
+ template <typename T> void $decl(Foo)[[b^ar]](T t);
};
template <typename T> void test(Foo F, T t) {
- F.[[bar]](t);
+ F.$(test)[[bar]](t);
}
)cpp",
// Enum base
R"cpp(
typedef int $def[[MyTypeD^ef]];
- enum MyEnum : [[MyTy^peDef]] { };
+ enum MyEnum : $(MyEnum)[[MyTy^peDef]] { };
)cpp",
R"cpp(
typedef int $def[[MyType^Def]];
- enum MyEnum : [[MyTypeD^ef]];
+ enum MyEnum : $(MyEnum)[[MyTypeD^ef]];
)cpp",
R"cpp(
using $def[[MyTypeD^ef]] = int;
- enum MyEnum : [[MyTy^peDef]] { };
+ enum MyEnum : $(MyEnum)[[MyTy^peDef]] { };
)cpp",
};
for (const char *Test : Tests)
@@ -2127,13 +2132,13 @@ TEST(FindReferences, ConceptsWithinAST) {
template <class T>
concept IsSmallPtr = requires(T x) {
- { *x } -> [[IsSmal^l]];
+ { *x } -> $(IsSmallPtr)[[IsSmal^l]];
};
- [[IsSmall]] auto i = 'c';
- template<[[IsSmal^l]] U> void foo();
- template<class U> void bar() requires [[IsSmal^l]]<U>;
- template<class U> requires [[IsSmal^l]]<U> void baz();
+ $(i)[[IsSmall]] auto i = 'c';
+ template<$(foo)[[IsSmal^l]] U> void foo();
+ template<class U> void bar() requires $(bar)[[IsSmal^l]]<U>;
+ template<class U> requires $(baz)[[IsSmal^l]]<U> void baz();
static_assert([[IsSma^ll]]<char>);
)cpp";
checkFindRefs(Code);
@@ -2146,7 +2151,7 @@ TEST(FindReferences, ConceptReq) {
template <class T>
concept IsSmallPtr = requires(T x) {
- { *x } -> [[IsSmal^l]];
+ { *x } -> $(IsSmallPtr)[[IsSmal^l]];
};
)cpp";
checkFindRefs(Code);
@@ -2159,7 +2164,7 @@ TEST(FindReferences, RequiresExprParameters) {
template <class T>
concept IsSmallPtr = requires(T $def[[^x]]) {
- { *[[^x]] } -> IsSmall;
+ { *$(IsSmallPtr)[[^x]] } -> IsSmall;
};
)cpp";
checkFindRefs(Code);
@@ -2170,18 +2175,19 @@ TEST(FindReferences, IncludeOverrides) {
R"cpp(
class Base {
public:
- virtu^al void $decl[[f^unc]]() ^= ^0;
+ virtu^al void $decl(Base)[[f^unc]]() ^= ^0;
};
class Derived : public Base {
public:
- void $overridedecl[[func]]() override;
+ void $overridedecl(Derived::func)[[func]]() override;
};
void Derived::$overridedef[[func]]() {}
class Derived2 : public Base {
- void $overridedef[[func]]() override {}
+ void $overridedef(Derived2::func)[[func]]() override {}
};
- void test(Derived* D) {
+ void test(Derived* D, Base* B) {
D->func(); // No references to the overrides.
+ B->$(test)[[func]]();
})cpp";
checkFindRefs(Test, /*UseIndex=*/true);
}
@@ -2191,21 +2197,21 @@ TEST(FindReferences, RefsToBaseMethod) {
R"cpp(
class BaseBase {
public:
- virtual void [[func]]();
+ virtual void $(BaseBase)[[func]]();
};
class Base : public BaseBase {
public:
- void [[func]]() override;
+ void $(Base)[[func]]() override;
};
class Derived : public Base {
public:
- void $decl[[fu^nc]]() over^ride;
+ void $decl(Derived)[[fu^nc]]() over^ride;
};
void test(BaseBase* BB, Base* B, Derived* D) {
// refs to overridden methods in complete type hierarchy are reported.
- BB->[[func]]();
- B->[[func]]();
- D->[[fu^nc]]();
+ BB->$(test)[[func]]();
+ B->$(test)[[func]]();
+ D->$(test)[[fu^nc]]();
})cpp";
checkFindRefs(Test, /*UseIndex=*/true);
}
@@ -2237,18 +2243,18 @@ TEST(FindReferences, MainFileReferencesOnly) {
TEST(FindReferences, ExplicitSymbols) {
const char *Tests[] = {
R"cpp(
- struct Foo { Foo* $decl[[self]]() const; };
+ struct Foo { Foo* $decl(Foo)[[self]]() const; };
void f() {
Foo foo;
- if (Foo* T = foo.[[^self]]()) {} // Foo member call expr.
+ if (Foo* T = foo.$(f)[[^self]]()) {} // Foo member call expr.
}
)cpp",
R"cpp(
struct Foo { Foo(int); };
Foo f() {
- int $def[[b]];
- return [[^b]]; // Foo constructor expr.
+ int $def(f)[[b]];
+ return $(f)[[^b]]; // Foo constructor expr.
}
)cpp",
@@ -2257,7 +2263,7 @@ TEST(FindReferences, ExplicitSymbols) {
void g(Foo);
Foo $decl[[f]]();
void call() {
- g([[^f]]()); // Foo constructor expr.
+ g($(call)[[^f]]()); // Foo constructor expr.
}
)cpp",
@@ -2266,7 +2272,7 @@ TEST(FindReferences, ExplicitSymbols) {
void $decl[[foo]](double);
namespace ns {
- using ::$decl[[fo^o]];
+ using ::$decl(ns)[[fo^o]];
}
)cpp",
@@ -2276,9 +2282,9 @@ TEST(FindReferences, ExplicitSymbols) {
};
int test() {
- X $def[[a]];
- [[a]].operator bool();
- if ([[a^]]) {} // ignore implicit conversion-operator AST node
+ X $def(test)[[a]];
+ $(test)[[a]].operator bool();
+ if ($(test)[[a^]]) {} // ignore implicit conversion-operator AST node
}
)cpp",
};
@@ -2299,7 +2305,10 @@ TEST(FindReferences, NeedsIndexForSymbols) {
findReferences(AST, Main.point(), 0, /*Index=*/nullptr).References,
ElementsAre(rangeIs(Main.range())));
Annotations IndexedMain(R"cpp(
- int [[foo]]() { return 42; }
+ int $decl[[foo]]() { return 42; }
+ void bar() { $bar(bar)[[foo]](); }
+ struct S { void bar() { $S(S::bar)[[foo]](); } };
+ namespace N { void bar() { $N(N::bar)[[foo]](); } }
)cpp");
// References from indexed files are included.
@@ -2308,11 +2317,17 @@ TEST(FindReferences, NeedsIndexForSymbols) {
IndexedTU.Filename = "Indexed.cpp";
IndexedTU.HeaderCode = Header;
EXPECT_THAT(
- findReferences(AST, Main.point(), 0, IndexedTU.index().get()).References,
- ElementsAre(rangeIs(Main.range()),
- AllOf(rangeIs(IndexedMain.range()),
- attrsAre(ReferencesResult::Declaration |
- ReferencesResult::Definition))));
+ findReferences(AST, Main.point(), 0, IndexedTU.index().get(),
+ /*AddContext*/ true)
+ .References,
+ ElementsAre(
+ rangeIs(Main.range()),
+ AllOf(rangeIs(IndexedMain.range("decl")),
+ attrsAre(ReferencesResult::Declaration |
+ ReferencesResult::Definition)),
+ AllOf(rangeIs(IndexedMain.range("bar")), containerIs("bar")),
+ AllOf(rangeIs(IndexedMain.range("S")), containerIs("S::bar")),
+ AllOf(rangeIs(IndexedMain.range("N")), containerIs("N::bar"))));
auto LimitRefs =
findReferences(AST, Main.point(), /*Limit*/ 1, IndexedTU.index().get());
EXPECT_EQ(1u, LimitRefs.References.size());
More information about the cfe-commits
mailing list