Skip to content

Commit 2ff8ad9

Browse files
committed
[clangd][WIP] Add doxygen parsing for Hover
1 parent f1627e1 commit 2ff8ad9

23 files changed

+916
-143
lines changed

clang-tools-extra/clangd/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ add_clang_library(clangDaemon STATIC
108108
SemanticHighlighting.cpp
109109
SemanticSelection.cpp
110110
SourceCode.cpp
111+
SymbolDocumentation.cpp
111112
SystemIncludeExtractor.cpp
112113
TidyProvider.cpp
113114
TUScheduler.cpp

clang-tools-extra/clangd/CodeComplete.cpp

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -504,11 +504,11 @@ struct CodeCompletionBuilder {
504504
}
505505
};
506506
if (C.IndexResult) {
507-
SetDoc(C.IndexResult->Documentation);
507+
SetDoc(C.IndexResult->Documentation.CommentText);
508508
} else if (C.SemaResult) {
509-
const auto DocComment = getDocComment(*ASTCtx, *C.SemaResult,
510-
/*CommentsFromHeaders=*/false);
511-
SetDoc(formatDocumentation(*SemaCCS, DocComment));
509+
const auto DocComment = getDocumentation(*ASTCtx, *C.SemaResult,
510+
/*CommentsFromHeaders=*/false);
511+
SetDoc(formatDocumentation(*SemaCCS, DocComment.CommentText));
512512
}
513513
}
514514
if (Completion.Deprecated) {
@@ -1106,8 +1106,9 @@ class SignatureHelpCollector final : public CodeCompleteConsumer {
11061106
ScoredSignatures.push_back(processOverloadCandidate(
11071107
Candidate, *CCS,
11081108
Candidate.getFunction()
1109-
? getDeclComment(S.getASTContext(), *Candidate.getFunction())
1110-
: ""));
1109+
? getDeclDocumentation(S.getASTContext(),
1110+
*Candidate.getFunction())
1111+
: SymbolDocumentationOwned{}));
11111112
}
11121113

11131114
// Sema does not load the docs from the preamble, so we need to fetch extra
@@ -1122,7 +1123,7 @@ class SignatureHelpCollector final : public CodeCompleteConsumer {
11221123
}
11231124
Index->lookup(IndexRequest, [&](const Symbol &S) {
11241125
if (!S.Documentation.empty())
1125-
FetchedDocs[S.ID] = std::string(S.Documentation);
1126+
FetchedDocs[S.ID] = std::string(S.Documentation.CommentText);
11261127
});
11271128
vlog("SigHelp: requested docs for {0} symbols from the index, got {1} "
11281129
"symbols with non-empty docs in the response",
@@ -1231,15 +1232,17 @@ class SignatureHelpCollector final : public CodeCompleteConsumer {
12311232

12321233
// FIXME(ioeric): consider moving CodeCompletionString logic here to
12331234
// CompletionString.h.
1234-
ScoredSignature processOverloadCandidate(const OverloadCandidate &Candidate,
1235-
const CodeCompletionString &CCS,
1236-
llvm::StringRef DocComment) const {
1235+
ScoredSignature
1236+
processOverloadCandidate(const OverloadCandidate &Candidate,
1237+
const CodeCompletionString &CCS,
1238+
const SymbolDocumentationOwned &DocComment) const {
12371239
SignatureInformation Signature;
12381240
SignatureQualitySignals Signal;
12391241
const char *ReturnType = nullptr;
12401242

12411243
markup::Document OverloadComment;
1242-
parseDocumentation(formatDocumentation(CCS, DocComment), OverloadComment);
1244+
parseDocumentation(formatDocumentation(CCS, DocComment.CommentText),
1245+
OverloadComment);
12431246
Signature.documentation = renderDoc(OverloadComment, DocumentationFormat);
12441247
Signal.Kind = Candidate.getKind();
12451248

@@ -1898,7 +1901,7 @@ class CodeCompleteFlow {
18981901
return;
18991902
auto &C = Output.Completions[SymbolToCompletion.at(S.ID)];
19001903
C.Documentation.emplace();
1901-
parseDocumentation(S.Documentation, *C.Documentation);
1904+
parseDocumentation(S.Documentation.CommentText, *C.Documentation);
19021905
});
19031906
}
19041907

clang-tools-extra/clangd/CodeCompletionStrings.cpp

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -80,39 +80,42 @@ bool shouldPatchPlaceholder0(CodeCompletionResult::ResultKind ResultKind,
8080

8181
} // namespace
8282

83-
std::string getDocComment(const ASTContext &Ctx,
84-
const CodeCompletionResult &Result,
85-
bool CommentsFromHeaders) {
83+
SymbolDocumentationOwned getDocumentation(const ASTContext &Ctx,
84+
const CodeCompletionResult &Result,
85+
bool CommentsFromHeaders) {
86+
// FIXME: CommentsFromHeaders seems to be unused? Is this a bug?
87+
8688
// FIXME: clang's completion also returns documentation for RK_Pattern if they
8789
// contain a pattern for ObjC properties. Unfortunately, there is no API to
8890
// get this declaration, so we don't show documentation in that case.
8991
if (Result.Kind != CodeCompletionResult::RK_Declaration)
90-
return "";
91-
return Result.getDeclaration() ? getDeclComment(Ctx, *Result.getDeclaration())
92-
: "";
92+
return {};
93+
return Result.getDeclaration()
94+
? getDeclDocumentation(Ctx, *Result.getDeclaration())
95+
: SymbolDocumentationOwned{};
9396
}
9497

95-
std::string getDeclComment(const ASTContext &Ctx, const NamedDecl &Decl) {
98+
SymbolDocumentationOwned getDeclDocumentation(const ASTContext &Ctx,
99+
const NamedDecl &Decl) {
96100
if (isa<NamespaceDecl>(Decl)) {
97101
// Namespaces often have too many redecls for any particular redecl comment
98102
// to be useful. Moreover, we often confuse file headers or generated
99103
// comments with namespace comments. Therefore we choose to just ignore
100104
// the comments for namespaces.
101-
return "";
105+
return {};
102106
}
103107
const RawComment *RC = getCompletionComment(Ctx, &Decl);
104108
if (!RC)
105-
return "";
109+
return {};
106110
// Sanity check that the comment does not come from the PCH. We choose to not
107111
// write them into PCH, because they are racy and slow to load.
108112
assert(!Ctx.getSourceManager().isLoadedSourceLocation(RC->getBeginLoc()));
109-
std::string Doc =
110-
RC->getFormattedText(Ctx.getSourceManager(), Ctx.getDiagnostics());
111-
if (!looksLikeDocComment(Doc))
112-
return "";
113-
// Clang requires source to be UTF-8, but doesn't enforce this in comments.
114-
if (!llvm::json::isUTF8(Doc))
115-
Doc = llvm::json::fixUTF8(Doc);
113+
114+
SymbolDocumentationOwned Doc = parseDoxygenComment(*RC, Ctx, &Decl);
115+
116+
if (!looksLikeDocComment(Doc.CommentText))
117+
return {};
118+
116119
return Doc;
117120
}
118121

clang-tools-extra/clangd/CodeCompletionStrings.h

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,25 @@
1616

1717
#include "clang/Sema/CodeCompleteConsumer.h"
1818

19+
#include "SymbolDocumentation.h"
20+
1921
namespace clang {
2022
class ASTContext;
2123

2224
namespace clangd {
2325

24-
/// Gets a minimally formatted documentation comment of \p Result, with comment
25-
/// markers stripped. See clang::RawComment::getFormattedText() for the detailed
26-
/// explanation of how the comment text is transformed.
27-
/// Returns empty string when no comment is available.
26+
/// Gets the parsed doxygen documentation of \p Result.
27+
/// Returns an empty SymbolDocumentationOwned when no comment is available.
2828
/// If \p CommentsFromHeaders parameter is set, only comments from the main
2929
/// file will be returned. It is used to workaround crashes when parsing
3030
/// comments in the stale headers, coming from completion preamble.
31-
std::string getDocComment(const ASTContext &Ctx,
32-
const CodeCompletionResult &Result,
33-
bool CommentsFromHeaders);
31+
SymbolDocumentationOwned getDocumentation(const ASTContext &Ctx,
32+
const CodeCompletionResult &Result,
33+
bool CommentsFromHeaders);
3434

35-
/// Similar to getDocComment, but returns the comment for a NamedDecl.
36-
std::string getDeclComment(const ASTContext &Ctx, const NamedDecl &D);
35+
/// Similar to getDocumentation, but returns the comment for a NamedDecl.
36+
SymbolDocumentationOwned getDeclDocumentation(const ASTContext &Ctx,
37+
const NamedDecl &D);
3738

3839
/// Formats the signature for an item, as a display string and snippet.
3940
/// e.g. for const_reference std::vector<T>::at(size_type) const, this returns:

clang-tools-extra/clangd/Hover.cpp

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ void enhanceFromIndex(HoverInfo &Hover, const NamedDecl &ND,
347347
LookupRequest Req;
348348
Req.IDs.insert(ID);
349349
Index->lookup(Req, [&](const Symbol &S) {
350-
Hover.Documentation = std::string(S.Documentation);
350+
Hover.Documentation = S.Documentation.toOwned();
351351
});
352352
}
353353

@@ -625,10 +625,11 @@ HoverInfo getHoverContents(const NamedDecl *D, const PrintingPolicy &PP,
625625

626626
HI.Name = printName(Ctx, *D);
627627
const auto *CommentD = getDeclForComment(D);
628-
HI.Documentation = getDeclComment(Ctx, *CommentD);
628+
HI.Documentation = getDeclDocumentation(Ctx, *CommentD);
629629
enhanceFromIndex(HI, *CommentD, Index);
630630
if (HI.Documentation.empty())
631-
HI.Documentation = synthesizeDocumentation(D);
631+
HI.Documentation =
632+
SymbolDocumentationOwned::descriptionOnly(synthesizeDocumentation(D));
632633

633634
HI.Kind = index::getSymbolInfo(D).Kind;
634635

@@ -682,7 +683,8 @@ getPredefinedExprHoverContents(const PredefinedExpr &PE, ASTContext &Ctx,
682683
HoverInfo HI;
683684
HI.Name = PE.getIdentKindName();
684685
HI.Kind = index::SymbolKind::Variable;
685-
HI.Documentation = "Name of the current function (predefined variable)";
686+
HI.Documentation = SymbolDocumentationOwned::descriptionOnly(
687+
"Name of the current function (predefined variable)");
686688
if (const StringLiteral *Name = PE.getFunctionName()) {
687689
HI.Value.emplace();
688690
llvm::raw_string_ostream OS(*HI.Value);
@@ -856,7 +858,7 @@ HoverInfo getDeducedTypeHoverContents(QualType QT, const syntax::Token &Tok,
856858

857859
if (const auto *D = QT->getAsTagDecl()) {
858860
const auto *CommentD = getDeclForComment(D);
859-
HI.Documentation = getDeclComment(ASTCtx, *CommentD);
861+
HI.Documentation = getDeclDocumentation(ASTCtx, *CommentD);
860862
enhanceFromIndex(HI, *CommentD, Index);
861863
}
862864
}
@@ -956,7 +958,8 @@ std::optional<HoverInfo> getHoverContents(const Attr *A, ParsedAST &AST) {
956958
llvm::raw_string_ostream OS(HI.Definition);
957959
A->printPretty(OS, AST.getASTContext().getPrintingPolicy());
958960
}
959-
HI.Documentation = Attr::getDocumentation(A->getKind()).str();
961+
HI.Documentation = SymbolDocumentationOwned::descriptionOnly(
962+
Attr::getDocumentation(A->getKind()).str());
960963
return HI;
961964
}
962965

@@ -1455,6 +1458,10 @@ markup::Document HoverInfo::present() const {
14551458

14561459
// Put a linebreak after header to increase readability.
14571460
Output.addRuler();
1461+
1462+
if (!Documentation.Brief.empty())
1463+
parseDocumentation(Documentation.Brief, Output);
1464+
14581465
// Print Types on their own lines to reduce chances of getting line-wrapped by
14591466
// editor, as they might be long.
14601467
if (ReturnType) {
@@ -1463,15 +1470,44 @@ markup::Document HoverInfo::present() const {
14631470
// Parameters:
14641471
// - `bool param1`
14651472
// - `int param2 = 5`
1466-
Output.addParagraph().appendText("").appendCode(
1473+
auto &P = Output.addParagraph().appendText("").appendCode(
14671474
llvm::to_string(*ReturnType));
1468-
}
14691475

1476+
if (!Documentation.Returns.empty())
1477+
P.appendText(": ").appendText(Documentation.Returns);
1478+
}
14701479
if (Parameters && !Parameters->empty()) {
14711480
Output.addParagraph().appendText("Parameters: ");
14721481
markup::BulletList &L = Output.addBulletList();
1473-
for (const auto &Param : *Parameters)
1474-
L.addItem().addParagraph().appendCode(llvm::to_string(Param));
1482+
1483+
llvm::SmallVector<ParameterDocumentationOwned> ParamDocs =
1484+
Documentation.Parameters;
1485+
1486+
for (const auto &Param : *Parameters) {
1487+
auto &Paragraph = L.addItem().addParagraph();
1488+
Paragraph.appendCode(llvm::to_string(Param));
1489+
1490+
if (Param.Name.has_value()) {
1491+
auto ParamDoc = std::find_if(ParamDocs.begin(), ParamDocs.end(),
1492+
[Param](const auto &ParamDoc) {
1493+
return Param.Name == ParamDoc.Name;
1494+
});
1495+
if (ParamDoc != ParamDocs.end()) {
1496+
Paragraph.appendText(": ").appendText(ParamDoc->Description);
1497+
ParamDocs.erase(ParamDoc);
1498+
}
1499+
}
1500+
}
1501+
1502+
// We erased all parameters that matched, but some may still be left,
1503+
// usually typos. Let's also print them here.
1504+
for (const auto &ParamDoc : ParamDocs) {
1505+
L.addItem()
1506+
.addParagraph()
1507+
.appendCode(ParamDoc.Name)
1508+
.appendText(": ")
1509+
.appendText(ParamDoc.Description);
1510+
}
14751511
}
14761512

14771513
// Don't print Type after Parameters or ReturnType as this will just duplicate
@@ -1518,8 +1554,30 @@ markup::Document HoverInfo::present() const {
15181554
Output.addParagraph().appendText(OS.str());
15191555
}
15201556

1521-
if (!Documentation.empty())
1522-
parseDocumentation(Documentation, Output);
1557+
if (!Documentation.Description.empty())
1558+
parseDocumentation(Documentation.Description, Output);
1559+
1560+
if (!Documentation.Warnings.empty()) {
1561+
Output.addRuler();
1562+
Output.addParagraph()
1563+
.appendText("Warning")
1564+
.appendText(Documentation.Warnings.size() > 1 ? "s" : "")
1565+
.appendText(": ");
1566+
markup::BulletList &L = Output.addBulletList();
1567+
for (const auto &Warning : Documentation.Warnings)
1568+
parseDocumentation(Warning, L.addItem());
1569+
}
1570+
1571+
if (!Documentation.Notes.empty()) {
1572+
Output.addRuler();
1573+
Output.addParagraph()
1574+
.appendText("Note")
1575+
.appendText(Documentation.Notes.size() > 1 ? "s" : "")
1576+
.appendText(": ");
1577+
markup::BulletList &L = Output.addBulletList();
1578+
for (const auto &Note : Documentation.Notes)
1579+
parseDocumentation(Note, L.addItem());
1580+
}
15231581

15241582
if (!Definition.empty()) {
15251583
Output.addRuler();

clang-tools-extra/clangd/Hover.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include "ParsedAST.h"
1313
#include "Protocol.h"
14+
#include "SymbolDocumentation.h"
1415
#include "support/Markup.h"
1516
#include "clang/Index/IndexSymbol.h"
1617
#include <optional>
@@ -73,7 +74,7 @@ struct HoverInfo {
7374
std::string Provider;
7475
std::optional<Range> SymRange;
7576
index::SymbolKind Kind = index::SymbolKind::Unknown;
76-
std::string Documentation;
77+
SymbolDocumentationOwned Documentation;
7778
/// Source code containing the definition of the symbol.
7879
std::string Definition;
7980
const char *DefinitionLanguage = "cpp";

0 commit comments

Comments
 (0)