tom-anders created this revision. Herald added subscribers: kadircet, arphaman. Herald added a project: All. tom-anders updated this revision to Diff 476438. tom-anders added a comment. tom-anders retitled this revision from "[clangd] Prototype for adding enclosing function in references results" to "[clangd] Add extension for adding context (enclosing function or class) in references results". tom-anders published this revision for review. tom-anders marked 2 inline comments as done. Herald added subscribers: cfe-commits, MaskRay, ilya-biryukov. Herald added a project: clang-tools-extra.
Add tests, add client capability, add new ReferenceLocation type ================ Comment at: clang-tools-extra/clangd/Protocol.h:218 + /// reference occurs + std::string container; + ---------------- Location is a common vocabulary type. If we're going to add an extension that relates to a particular method, we should define a new type that's wire-compatible if that's at all feasible. Could be a subclass of Location or just a clone. (If this is just a shortcut taken while drafting this patch, please ignore me!) ================ Comment at: clang-tools-extra/clangd/XRefs.cpp:1479 + + Index->lookup(ContainerLookup, [&](const Symbol &Container) { + auto Ref = RefIndexForContainer.find(Container.ID); ---------------- Being able to get all the containers in one ~constant-time index query is great! May still not want to have this on-by-default when the client doesn't advertise support, as it's a fixed nontrivial latency hit for remote index. Also we should skip this query when the set of IDs is empty. That can go here or (maybe better) in the remote index client. ================ Comment at: clang-tools-extra/clangd/XRefs.cpp:1479 + + Index->lookup(ContainerLookup, [&](const Symbol &Container) { + auto Ref = RefIndexForContainer.find(Container.ID); ---------------- sammccall wrote: > Being able to get all the containers in one ~constant-time index query is > great! May still not want to have this on-by-default when the client doesn't > advertise support, as it's a fixed nontrivial latency hit for remote index. > > Also we should skip this query when the set of IDs is empty. That can go here > or (maybe better) in the remote index client. > Being able to get all the containers in one ~constant-time index query is > great! May still not want to have this on-by-default when the client doesn't > advertise support, as it's a fixed nontrivial latency hit for remote index. Makes sense, we could either add a ClientCapability for this (Looks like we already have some capabilities related to extensions, so this wouldn't be a novelty), or it could just be a command line flag. Any preferences? > Also we should skip this query when the set of IDs is empty. That can go here > or (maybe better) in the remote index client. Agreed, probably more foolproof to let the index do this (otherwise the next person will forget about that as well probably) Relevant issue: https://github.com/clangd/clangd/issues/177 Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D137894 Files: 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/unittests/XRefsTests.cpp
Index: clang-tools-extra/clangd/unittests/XRefsTests.cpp =================================================================== --- clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -302,6 +302,7 @@ MATCHER_P(sym, Name, "") { return arg.Name == Name; } MATCHER_P(rangeIs, R, "") { return arg.Loc.range == R; } +MATCHER_P(contextIs, C, "") { return arg.Loc.context.value_or("") == C; } MATCHER_P(attrsAre, A, "") { return arg.Attributes == A; } MATCHER_P(hasID, ID, "") { return arg.ID == ID; } @@ -1900,28 +1901,30 @@ 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), contextIs(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), contextIs(Context), + attrsAre(ReferencesResult::Definition | + ReferencesResult::Declaration))); + for (const auto &[R, Context] : T.rangesWithPayload("decl")) + ExpectedLocations.push_back(AllOf(rangeIs(R), contextIs(Context), + attrsAre(ReferencesResult::Declaration))); + for (const auto &[R, Context] : T.rangesWithPayload("overridedecl")) ExpectedLocations.push_back(AllOf( - rangeIs(R), + rangeIs(R), contextIs(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), contextIs(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,9 +1936,9 @@ 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", @@ -1944,7 +1947,7 @@ struct $def[[Foo]] {}; } // namespace ns1 int main() { - ns1::[[Fo^o]]* Params; + ns1::$(main)[[Fo^o]]* Params; } )cpp", @@ -1952,51 +1955,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 +2007,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 +2016,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,19 +2034,19 @@ 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 = $[[var]]); )cpp", R"cpp( template <typename T> class $def[[Fo^o]] {}; - void func([[Foo]]<int>); + void func($[[Foo]]<int>); )cpp", R"cpp( @@ -2053,17 +2056,17 @@ )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 @@ -2075,8 +2078,8 @@ )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 { @@ -2090,15 +2093,15 @@ 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", @@ -2131,7 +2134,7 @@ }; [[IsSmall]] auto i = 'c'; - template<[[IsSmal^l]] U> void foo(); + template<$(foo)[[IsSmal^l]] U> void foo(); template<class U> void bar() requires [[IsSmal^l]]<U>; template<class U> requires [[IsSmal^l]]<U> void baz(); static_assert([[IsSma^ll]]<char>); @@ -2146,6 +2149,7 @@ template <class T> concept IsSmallPtr = requires(T x) { + // FIXME Should probably report IsSmallPtr as context here { *x } -> [[IsSmal^l]]; }; )cpp"; @@ -2170,17 +2174,17 @@ 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]]() override; }; void Derived::$overridedef[[func]]() {} class Derived2 : public Base { - void $overridedef[[func]]() override {} + void $overridedef(Derived2)[[func]]() override {} }; - void test(Derived* D) { + void test(Derived* D, Base* B) { D->func(); // No references to the overrides. })cpp"; checkFindRefs(Test, /*UseIndex=*/true); @@ -2191,21 +2195,23 @@ 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]](); + // FIXME BB->func() gets no context here, this seems to be a bug (or feature?) + // in the index for multiple levels of inheritance + BB->$[[func]](); + B->$(test)[[func]](); + D->$(test)[[fu^nc]](); })cpp"; checkFindRefs(Test, /*UseIndex=*/true); } @@ -2214,7 +2220,7 @@ llvm::StringRef Test = R"cpp( void test() { - int [[fo^o]] = 1; + int $(test)[[fo^o]] = 1; // refs not from main file should not be included. #include "foo.inc" })cpp"; @@ -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", @@ -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,15 @@ IndexedTU.Filename = "Indexed.cpp"; IndexedTU.HeaderCode = Header; EXPECT_THAT( - findReferences(AST, Main.point(), 0, IndexedTU.index().get()).References, + findReferences(AST, Main.point(), 0, IndexedTU.index().get(), /*AddContext*/true).References, ElementsAre(rangeIs(Main.range()), - AllOf(rangeIs(IndexedMain.range()), + AllOf(rangeIs(IndexedMain.range("decl")), attrsAre(ReferencesResult::Declaration | - ReferencesResult::Definition)))); + ReferencesResult::Definition)), + AllOf(rangeIs(IndexedMain.range("bar")), contextIs("bar")), + AllOf(rangeIs(IndexedMain.range("S")), contextIs("S::bar")), + AllOf(rangeIs(IndexedMain.range("N")), contextIs("N::bar")) + )); auto LimitRefs = findReferences(AST, Main.point(), /*Limit*/ 1, IndexedTU.index().get()); EXPECT_EQ(1u, LimitRefs.References.size()); @@ -2337,9 +2350,10 @@ auto AST = TU.build(); // References in main file are returned without index. - EXPECT_THAT( - findReferences(AST, Main.point(), 0, /*Index=*/nullptr).References, - ElementsAre(rangeIs(Main.range()))); + EXPECT_THAT(findReferences(AST, Main.point(), 0, /*Index=*/nullptr, + /*AddContext*/ true) + .References, + ElementsAre(rangeIs(Main.range()))); Annotations IndexedMain(R"cpp( int indexed_main() { @@ -2355,8 +2369,8 @@ EXPECT_THAT( findReferences(AST, Main.point(), 0, IndexedTU.index().get()).References, ElementsAre(rangeIs(Main.range()), rangeIs(IndexedMain.range()))); - auto LimitRefs = - findReferences(AST, Main.point(), /*Limit*/ 1, IndexedTU.index().get()); + auto LimitRefs = findReferences(AST, Main.point(), /*Limit*/ 1, + IndexedTU.index().get(), /*AddContext*/ true); EXPECT_EQ(1u, LimitRefs.References.size()); EXPECT_TRUE(LimitRefs.HasMore); } Index: clang-tools-extra/clangd/XRefs.h =================================================================== --- clang-tools-extra/clangd/XRefs.h +++ clang-tools-extra/clangd/XRefs.h @@ -88,7 +88,7 @@ Override = 1 << 2, }; struct Reference { - Location Loc; + ReferenceLocation Loc; unsigned Attributes = 0; }; std::vector<Reference> 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<SymbolDetails> getSymbolInfo(ParsedAST &AST, Position Pos); Index: clang-tools-extra/clangd/XRefs.cpp =================================================================== --- clang-tools-extra/clangd/XRefs.cpp +++ clang-tools-extra/clangd/XRefs.cpp @@ -859,6 +859,7 @@ syntax::Token SpelledTok; index::SymbolRoleSet Role; SymbolID Target; + const DeclContext *Context; Range range(const SourceManager &SM) const { return halfOpenToRange(SM, SpelledTok.range(SM).toCharRange(SM)); @@ -927,7 +928,8 @@ for (SourceLocation L : Locs) { L = SM.getFileLoc(L); if (const auto *Tok = TB.spelledTokenAt(L)) - References.push_back({*Tok, Roles, DeclID->getSecond()}); + References.push_back( + {*Tok, Roles, DeclID->getSecond(), ASTNode.ContainerDC}); } return true; } @@ -1300,10 +1302,21 @@ getOverriddenMethods(Base, OverriddenMethods); } } + +llvm::Optional<std::string> +getContextStringForMainFileRef(const DeclContext *DeclCtx) { + for (auto *Ctx = DeclCtx; Ctx; Ctx = Ctx->getParent()) { + if (const auto *FD = llvm::dyn_cast<FunctionDecl>(Ctx)) + return FD->getQualifiedNameAsString(); + if (const auto *RD = llvm::dyn_cast<CXXRecordDecl>(Ctx)) + return RD->getQualifiedNameAsString(); + } + 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(); @@ -1394,6 +1407,8 @@ ReferencesResult::Reference Result; Result.Loc.range = Ref.range(SM); Result.Loc.uri = URIMainFile; + if (AddContext) + Result.Loc.context = getContextStringForMainFileRef(Ref.Context); if (Ref.Role & static_cast<unsigned>(index::SymbolRole::Declaration)) Result.Attributes |= ReferencesResult::Declaration; // clang-index doesn't report definitions as declarations, but they are. @@ -1404,6 +1419,8 @@ } // Add decl/def of overridding methods. if (Index && !OverriddenBy.Subjects.empty()) { + LookupRequest ContainerLookup; + llvm::DenseMap<SymbolID, size_t> RefIndexForContainer; Index->relations(OverriddenBy, [&](const SymbolID &Subject, const Symbol &Object) { if (Limit && Results.References.size() >= Limit) { @@ -1415,20 +1432,35 @@ const auto LSPLocDef = toLSPLocation(Object.Definition, MainFilePath); if (LSPLocDecl && LSPLocDecl != LSPLocDef) { ReferencesResult::Reference Result; - Result.Loc = std::move(*LSPLocDecl); + Result.Loc = {std::move(*LSPLocDecl), llvm::None}; 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), llvm::None}; 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()); + // Name is not useful here, because it's just gonna be the name of the + // function the cursor is on. Scope is interesting though, since this + // is the name of the class the overridden method is declared in. + Results.References[Ref->getSecond()].Loc.context = + Container.Scope.drop_back(2).str(); // Drop trailing "::" + }); } } // Now query the index for references from other files. @@ -1448,6 +1480,8 @@ Req.Limit = Limit - Results.References.size(); } } + LookupRequest ContainerLookup; + llvm::DenseMap<SymbolID, size_t> RefIndexForContainer; 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. @@ -1455,7 +1489,7 @@ (!AllowMainFileSymbols && LSPLoc->uri.file() == MainFilePath)) return; ReferencesResult::Reference Result; - Result.Loc = std::move(*LSPLoc); + Result.Loc = {std::move(*LSPLoc), llvm::None}; if (AllowAttributes) { if ((R.Kind & RefKind::Declaration) == RefKind::Declaration) Result.Attributes |= ReferencesResult::Declaration; @@ -1464,8 +1498,20 @@ Result.Attributes |= ReferencesResult::Declaration | ReferencesResult::Definition; } + SymbolID Container = R.Container; + ContainerLookup.IDs.insert(Container); Results.References.push_back(std::move(Result)); + RefIndexForContainer[Container] = Results.References.size() - 1; }); + + 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.context = + Container.Scope.str() + Container.Name.str() + + Container.Signature.str(); + }); }; QueryIndex(std::move(IDsToQuery), /*AllowAttributes=*/true, /*AllowMainFileSymbols=*/false); Index: clang-tools-extra/clangd/Protocol.h =================================================================== --- clang-tools-extra/clangd/Protocol.h +++ clang-tools-extra/clangd/Protocol.h @@ -228,6 +228,33 @@ llvm::json::Value toJSON(const Location &); llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Location &); +/// Special Location-like type to be used for textDocument/references +/// Contains an additional field for the context in which the reference occurs +struct ReferenceLocation : Location { + /// clangd extension: contains the name of the function or class in which the + /// reference occurs + llvm::Optional<std::string> context; + + friend bool operator==(const ReferenceLocation &LHS, + const ReferenceLocation &RHS) { + return LHS.uri == RHS.uri && LHS.range == RHS.range && + LHS.context == RHS.context; + } + + friend bool operator!=(const ReferenceLocation &LHS, + const ReferenceLocation &RHS) { + return !(LHS == RHS); + } + + friend bool operator<(const ReferenceLocation &LHS, + const ReferenceLocation &RHS) { + return std::tie(LHS.uri, LHS.range, LHS.context) < + std::tie(RHS.uri, RHS.range, RHS.context); + } +}; +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 +456,10 @@ /// textDocument.completion.editsNearCursor bool CompletionFixes = false; + /// Client supports displaying a context string for results of + /// textDocument/reference (clangd extension) + bool ReferenceContext = false; + /// Client supports hierarchical document symbols. /// textDocument.documentSymbol.hierarchicalDocumentSymbolSupport bool HierarchicalDocumentSymbol = false; Index: clang-tools-extra/clangd/Protocol.cpp =================================================================== --- clang-tools-extra/clangd/Protocol.cpp +++ clang-tools-extra/clangd/Protocol.cpp @@ -161,6 +161,21 @@ 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.context) + Result.insert({"context", P.context}); + return Result; +} + +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const ReferenceLocation &L) { + return OS << L.range << '@' << L.uri << " (context: " << L.context << ")"; +} + bool fromJSON(const llvm::json::Value &Params, TextDocumentItem &R, llvm::json::Path P) { llvm::json::ObjectMapper O(Params, P); @@ -332,6 +347,9 @@ if (auto RelatedInfo = Diagnostics->getBoolean("relatedInformation")) R.DiagnosticRelatedInformation = *RelatedInfo; } + if (auto *References = TextDocument->getObject("references")) + if (auto ContextSupport = References->getBoolean("context")) + R.ReferenceContext = *ContextSupport; if (auto *Completion = TextDocument->getObject("completion")) { if (auto *Item = Completion->getObject("completionItem")) { if (auto SnippetSupport = Item->getBoolean("snippetSupport")) Index: clang-tools-extra/clangd/ClangdServer.h =================================================================== --- clang-tools-extra/clangd/ClangdServer.h +++ 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<ReferencesResult> CB); + bool AddContext, Callback<ReferencesResult> CB); /// Run formatting for the \p File with content \p Code. /// If \p Rng is non-null, formats only that region. Index: clang-tools-extra/clangd/ClangdServer.cpp =================================================================== --- clang-tools-extra/clangd/ClangdServer.cpp +++ clang-tools-extra/clangd/ClangdServer.cpp @@ -889,12 +889,13 @@ } void ClangdServer::findReferences(PathRef File, Position Pos, uint32_t Limit, + bool AddContext, Callback<ReferencesResult> CB) { - auto Action = [Pos, Limit, CB = std::move(CB), + auto Action = [Pos, Limit, AddContext, 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, AddContext)); }; WorkScheduler->runWithAST("References", File, std::move(Action)); Index: clang-tools-extra/clangd/ClangdLSPServer.h =================================================================== --- clang-tools-extra/clangd/ClangdLSPServer.h +++ clang-tools-extra/clangd/ClangdLSPServer.h @@ -120,7 +120,7 @@ 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<llvm::Optional<URIForFile>>); 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 context string to references. + bool SupportsReferenceContext = 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. Index: clang-tools-extra/clangd/ClangdLSPServer.cpp =================================================================== --- clang-tools-extra/clangd/ClangdLSPServer.cpp +++ clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -1376,25 +1376,27 @@ 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, SupportsReferenceContext, + [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,
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits