diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp index fb39b7b29224..a8182ce98ebe 100644 --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -1867,14 +1867,41 @@ private: CodeCompleteResult Output; // Convert the results to final form, assembling the expensive strings. + // If necessary, search the index for documentation comments. + LookupRequest Req; + llvm::DenseMap SymbolToCompletion; for (auto &C : Scored) { Output.Completions.push_back(toCodeCompletion(C.first)); Output.Completions.back().Score = C.second; Output.Completions.back().CompletionTokenRange = ReplacedRange; + if (Opts.Index && !Output.Completions.back().Documentation) { + for (auto &Cand : C.first) { + if (Cand.SemaResult && + Cand.SemaResult->Kind == CodeCompletionResult::RK_Declaration) { + auto ID = clangd::getSymbolID(Cand.SemaResult->getDeclaration()); + if (!ID) + continue; + Req.IDs.insert(ID); + SymbolToCompletion[ID] = Output.Completions.size() - 1; + } + } + } } Output.HasMore = Incomplete; Output.Context = CCContextKind; Output.CompletionRange = ReplacedRange; + + // Look up documentation from the index. + if (Opts.Index) { + Opts.Index->lookup(Req, [&](const Symbol &S) { + if (S.Documentation.empty()) + return; + auto &C = Output.Completions[SymbolToCompletion.at(S.ID)]; + C.Documentation.emplace(); + parseDocumentation(S.Documentation, *C.Documentation); + }); + } + return Output; } diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp index 9d48a6e09fc7..b12f8275b8a2 100644 --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -1136,6 +1136,87 @@ int x = foo^ Contains(AllOf(named("foo"), doc("This comment should be retained!")))); } +TEST(CompletionTest, CommentsOnMembersFromHeader) { + MockFS FS; + MockCompilationDatabase CDB; + + auto Opts = ClangdServer::optsForTest(); + Opts.BuildDynamicSymbolIndex = true; + + ClangdServer Server(CDB, FS, Opts); + + FS.Files[testPath("foo.h")] = R"cpp( + struct alpha { + /// This is a member field. + int gamma; + + /// This is a member function. + int delta(); + }; + )cpp"; + + auto File = testPath("foo.cpp"); + Annotations Test(R"cpp( +#include "foo.h" +alpha a; +int x = a.^ + )cpp"); + runAddDocument(Server, File, Test.code()); + auto CompletionList = + llvm::cantFail(runCodeComplete(Server, File, Test.point(), {})); + + EXPECT_THAT(CompletionList.Completions, + Contains(AllOf(named("gamma"), doc("This is a member field.")))); + EXPECT_THAT( + CompletionList.Completions, + Contains(AllOf(named("delta"), doc("This is a member function.")))); +} + +TEST(CompletionTest, CommentsOnMembersFromHeaderOverloadBundling) { + using testing::AnyOf; + MockFS FS; + MockCompilationDatabase CDB; + + auto Opts = ClangdServer::optsForTest(); + Opts.BuildDynamicSymbolIndex = true; + + ClangdServer Server(CDB, FS, Opts); + + FS.Files[testPath("foo.h")] = R"cpp( + struct alpha { + /// bool overload. + int delta(bool b); + + /// int overload. + int delta(int i); + + void epsilon(long l); + + /// This one has a comment. + void epsilon(int i); + }; + )cpp"; + + auto File = testPath("foo.cpp"); + Annotations Test(R"cpp( +#include "foo.h" +alpha a; +int x = a.^ + )cpp"); + runAddDocument(Server, File, Test.code()); + clangd::CodeCompleteOptions CCOpts; + CCOpts.BundleOverloads = true; + auto CompletionList = + llvm::cantFail(runCodeComplete(Server, File, Test.point(), CCOpts)); + + EXPECT_THAT( + CompletionList.Completions, + Contains(AllOf(named("epsilon"), doc("This one has a comment.")))); + EXPECT_THAT(CompletionList.Completions, + Contains(AllOf(named("delta"), AnyOf(doc("bool overload."), + doc("int overload."))))); +} + TEST(CompletionTest, GlobalCompletionFiltering) { Symbol Class = cls("XYZ");