diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h b/clang-tools-extra/clangd/ClangdLSPServer.h --- a/clang-tools-extra/clangd/ClangdLSPServer.h +++ b/clang-tools-extra/clangd/ClangdLSPServer.h @@ -120,7 +120,7 @@ Callback>); void onGoToImplementation(const TextDocumentPositionParams &, Callback>); - void onReference(const ReferenceParams &, Callback>); + void onReference(const ReferenceParams &, Callback>); void onSwitchSourceHeader(const TextDocumentIdentifier &, Callback>); void onDocumentHighlight(const TextDocumentPositionParams &, @@ -262,6 +262,8 @@ 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/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -485,6 +485,7 @@ 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 @@ applyConfiguration(Params.settings); } -void ClangdLSPServer::onReference(const ReferenceParams &Params, - Callback> Reply) { - Server->findReferences( - Params.textDocument.uri.file(), Params.position, Opts.ReferencesLimit, - [Reply = std::move(Reply), - IncludeDecl(Params.context.includeDeclaration)]( - llvm::Expected Refs) mutable { - if (!Refs) - return Reply(Refs.takeError()); - // Filter out declarations if the client asked. - std::vector 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> Reply) { + Server->findReferences(Params.textDocument.uri.file(), Params.position, + Opts.ReferencesLimit, SupportsReferenceContainer, + [Reply = std::move(Reply), + IncludeDecl(Params.context.includeDeclaration)]( + llvm::Expected Refs) mutable { + if (!Refs) + return Reply(Refs.takeError()); + // Filter out declarations if the client asked. + std::vector 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/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -299,7 +299,7 @@ /// Retrieve locations for symbol references. void findReferences(PathRef File, Position Pos, uint32_t Limit, - Callback CB); + bool AddContainer, Callback 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/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -897,12 +897,13 @@ } void ClangdServer::findReferences(PathRef File, Position Pos, uint32_t Limit, + bool AddContainer, Callback CB) { - auto Action = [Pos, Limit, CB = std::move(CB), + auto Action = [Pos, Limit, AddContainer, CB = std::move(CB), this](llvm::Expected 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/Protocol.h b/clang-tools-extra/clangd/Protocol.h --- a/clang-tools-extra/clangd/Protocol.h +++ b/clang-tools-extra/clangd/Protocol.h @@ -228,6 +228,16 @@ 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 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 @@ /// 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/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/clang-tools-extra/clangd/Protocol.cpp @@ -161,6 +161,22 @@ 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 @@ 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/XRefs.h b/clang-tools-extra/clangd/XRefs.h --- a/clang-tools-extra/clangd/XRefs.h +++ b/clang-tools-extra/clangd/XRefs.h @@ -88,7 +88,7 @@ Override = 1 << 2, }; struct Reference { - Location Loc; + ReferenceLocation Loc; unsigned Attributes = 0; }; std::vector References; @@ -112,7 +112,8 @@ /// 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 getSymbolInfo(ParsedAST &AST, Position Pos); 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 @@ -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 @@ 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 @@ 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 @@ getOverriddenMethods(Base, OverriddenMethods); } } + +llvm::Optional +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(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::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(index::SymbolRole::Declaration)) Result.Attributes |= ReferencesResult::Declaration; // clang-index doesn't report definitions as declarations, but they are. @@ -1399,6 +1417,11 @@ } // Add decl/def of overridding methods. if (Index && !OverriddenBy.Subjects.empty()) { + LookupRequest ContainerLookup; + // Different overrides will always be contained in different 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 RefIndexForContainer; Index->relations(OverriddenBy, [&](const SymbolID &Subject, const Symbol &Object) { if (Limit && Results.References.size() >= Limit) { @@ -1410,20 +1433,32 @@ 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 @@ Req.Limit = Limit - Results.References.size(); } } + LookupRequest ContainerLookup; + llvm::DenseMap> 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 @@ (!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 @@ 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/index/SymbolCollector.h b/clang-tools-extra/clangd/index/SymbolCollector.h --- a/clang-tools-extra/clangd/index/SymbolCollector.h +++ b/clang-tools-extra/clangd/index/SymbolCollector.h @@ -103,6 +103,21 @@ 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 PP) override { diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp --- a/clang-tools-extra/clangd/index/SymbolCollector.cpp +++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp @@ -153,31 +153,6 @@ 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(Enclosing); - if (ND && SymbolCollector::shouldCollectSymbol(*ND, ND->getASTContext(), - Opts, true)) { - break; - } - Enclosing = dyn_cast_or_null(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 @@ return true; } +const Decl * +SymbolCollector::getRefContainer(const Decl *Enclosing, + const SymbolCollector::Options &Opts) { + while (Enclosing) { + const auto *ND = dyn_cast(Enclosing); + if (ND && shouldCollectSymbol(*ND, ND->getASTContext(), Opts, true)) { + break; + } + Enclosing = dyn_cast_or_null(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/test/references-container.test b/clang-tools-extra/clangd/test/references-container.test new file mode 100644 --- /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 --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -301,6 +301,9 @@ 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 @@ auto AST = TU.build(); std::vector> 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 @@ 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 @@ 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 @@ 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 @@ #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 @@ 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 class $def[[Fo^o]] {}; - void func([[Foo]]); + void func($(func)[[Foo]]); )cpp", R"cpp( template class $def[[Foo]] {}; - void func([[Fo^o]]); + void func($(func)[[Fo^o]]); )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 class Vector {}; using $def[[^X]] = Vector; - [[X]] x1; + $(x1)[[X]] x1; Vector x2; Vector y; )cpp", R"cpp(// Dependent code template void $decl[[foo]](T t); - template void bar(T t) { [[foo]](t); } // foo in bar is uninstantiated. - void baz(int x) { [[f^oo]](x); } + template 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 void foo(T t); // FIXME: Maybe report this foo as a ref to ns::foo (because of ADL) @@ -2090,30 +2095,30 @@ void baz(int x) { ns::S s; bar(s); - [[f^oo]](s); + $(baz)[[f^oo]](s); } )cpp", R"cpp(// unresolved member expression struct Foo { - template void $decl[[b^ar]](T t); + template void $decl(Foo)[[b^ar]](T t); }; template 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 @@ template concept IsSmallPtr = requires(T x) { - { *x } -> [[IsSmal^l]]; + { *x } -> $(IsSmallPtr)[[IsSmal^l]]; }; - [[IsSmall]] auto i = 'c'; - template<[[IsSmal^l]] U> void foo(); - template void bar() requires [[IsSmal^l]]; - template requires [[IsSmal^l]] void baz(); + $(i)[[IsSmall]] auto i = 'c'; + template<$(foo)[[IsSmal^l]] U> void foo(); + template void bar() requires $(bar)[[IsSmal^l]]; + template requires $(baz)[[IsSmal^l]] void baz(); static_assert([[IsSma^ll]]); )cpp"; checkFindRefs(Code); @@ -2146,7 +2151,7 @@ template concept IsSmallPtr = requires(T x) { - { *x } -> [[IsSmal^l]]; + { *x } -> $(IsSmallPtr)[[IsSmal^l]]; }; )cpp"; checkFindRefs(Code); @@ -2159,7 +2164,7 @@ template concept IsSmallPtr = requires(T $def[[^x]]) { - { *[[^x]] } -> IsSmall; + { *$(IsSmallPtr)[[^x]] } -> IsSmall; }; )cpp"; checkFindRefs(Code); @@ -2170,18 +2175,19 @@ 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 @@ 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, 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 @@ void g(Foo); Foo $decl[[f]](); void call() { - g([[^f]]()); // Foo constructor expr. + g($(call)[[^f]]()); // Foo constructor expr. } )cpp", @@ -2266,7 +2272,7 @@ void $decl[[foo]](double); namespace ns { - using ::$decl[[fo^o]]; + using ::$decl(ns)[[fo^o]]; } )cpp", @@ -2276,9 +2282,9 @@ }; 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 @@ 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 @@ 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());