clang-tools 22.0.0git
SymbolDocumentation.cpp
Go to the documentation of this file.
1//===--- SymbolDocumentation.cpp ==-------------------------------*- C++-*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10
11#include "support/Markup.h"
12#include "clang/AST/Comment.h"
13#include "clang/AST/CommentCommandTraits.h"
14#include "clang/AST/CommentVisitor.h"
15#include "llvm/ADT/DenseMap.h"
16#include "llvm/ADT/StringRef.h"
17
18namespace clang {
19namespace clangd {
20namespace {
21
22std::string commandMarkerAsString(comments::CommandMarkerKind CommandMarker) {
23 switch (CommandMarker) {
24 case comments::CommandMarkerKind::CMK_At:
25 return "@";
26 case comments::CommandMarkerKind::CMK_Backslash:
27 return "\\";
28 }
29 llvm_unreachable("Unknown command marker kind");
30}
31
32void commandToMarkup(markup::Paragraph &Out, StringRef Command,
33 comments::CommandMarkerKind CommandMarker,
34 StringRef Args) {
35 Out.appendBoldText(commandMarkerAsString(CommandMarker) + Command.str());
36 Out.appendSpace();
37 if (!Args.empty()) {
38 Out.appendEmphasizedText(Args.str());
39 }
40}
41} // namespace
42
44 : public comments::ConstCommentVisitor<ParagraphToMarkupDocument> {
45public:
47 const comments::CommandTraits &Traits)
48 : Out(Out), Traits(Traits) {}
49
50 void visitParagraphComment(const comments::ParagraphComment *C) {
51 if (!C)
52 return;
53
54 for (const auto *Child = C->child_begin(); Child != C->child_end();
55 ++Child) {
56 visit(*Child);
57 }
58 }
59
60 void visitTextComment(const comments::TextComment *C) {
61 // Always trim leading space after a newline.
62 StringRef Text = C->getText();
63 if (LastChunkEndsWithNewline && C->getText().starts_with(' '))
64 Text = Text.drop_front();
65
66 LastChunkEndsWithNewline = C->hasTrailingNewline();
67 Out.appendText(Text.str() + (LastChunkEndsWithNewline ? "\n" : ""));
68 }
69
70 void visitInlineCommandComment(const comments::InlineCommandComment *C) {
71
72 if (C->getNumArgs() > 0) {
73 std::string ArgText;
74 for (unsigned I = 0; I < C->getNumArgs(); ++I) {
75 if (!ArgText.empty())
76 ArgText += " ";
77 ArgText += C->getArgText(I);
78 }
79
80 switch (C->getRenderKind()) {
81 case comments::InlineCommandRenderKind::Monospaced:
82 Out.appendCode(ArgText);
83 break;
84 case comments::InlineCommandRenderKind::Bold:
85 Out.appendBoldText(ArgText);
86 break;
87 case comments::InlineCommandRenderKind::Emphasized:
88 Out.appendEmphasizedText(ArgText);
89 break;
90 default:
91 commandToMarkup(Out, C->getCommandName(Traits), C->getCommandMarker(),
92 ArgText);
93 break;
94 }
95 } else {
96 if (C->getCommandName(Traits) == "n") {
97 // \n is a special case, it is used to create a new line.
98 Out.appendText(" \n");
99 LastChunkEndsWithNewline = true;
100 return;
101 }
102
103 commandToMarkup(Out, C->getCommandName(Traits), C->getCommandMarker(),
104 "");
105 }
106 }
107
108 void visitHTMLStartTagComment(const comments::HTMLStartTagComment *STC) {
109 std::string TagText = "<" + STC->getTagName().str();
110
111 for (unsigned I = 0; I < STC->getNumAttrs(); ++I) {
112 const comments::HTMLStartTagComment::Attribute &Attr = STC->getAttr(I);
113 TagText += " " + Attr.Name.str() + "=\"" + Attr.Value.str() + "\"";
114 }
115
116 if (STC->isSelfClosing())
117 TagText += " /";
118 TagText += ">";
119
120 LastChunkEndsWithNewline = STC->hasTrailingNewline();
121 Out.appendText(TagText + (LastChunkEndsWithNewline ? "\n" : ""));
122 }
123
124 void visitHTMLEndTagComment(const comments::HTMLEndTagComment *ETC) {
125 LastChunkEndsWithNewline = ETC->hasTrailingNewline();
126 Out.appendText("</" + ETC->getTagName().str() + ">" +
127 (LastChunkEndsWithNewline ? "\n" : ""));
128 }
129
130private:
132 const comments::CommandTraits &Traits;
133
134 /// If true, the next leading space after a new line is trimmed.
135 /// Initially set it to true, to always trim the first text line.
136 bool LastChunkEndsWithNewline = true;
137};
138
140 : public comments::ConstCommentVisitor<ParagraphToString> {
141public:
142 ParagraphToString(llvm::raw_string_ostream &Out,
143 const comments::CommandTraits &Traits)
144 : Out(Out), Traits(Traits) {}
145
146 void visitParagraphComment(const comments::ParagraphComment *C) {
147 if (!C)
148 return;
149
150 for (const auto *Child = C->child_begin(); Child != C->child_end();
151 ++Child) {
152 visit(*Child);
153 }
154 }
155
156 void visitTextComment(const comments::TextComment *C) { Out << C->getText(); }
157
158 void visitInlineCommandComment(const comments::InlineCommandComment *C) {
159 Out << commandMarkerAsString(C->getCommandMarker());
160 Out << C->getCommandName(Traits);
161 if (C->getNumArgs() > 0) {
162 for (unsigned I = 0; I < C->getNumArgs(); ++I)
163 Out << " " << C->getArgText(I);
164 }
165 Out << " ";
166 }
167
168 void visitHTMLStartTagComment(const comments::HTMLStartTagComment *STC) {
169 Out << "<" << STC->getTagName().str();
170
171 for (unsigned I = 0; I < STC->getNumAttrs(); ++I) {
172 const comments::HTMLStartTagComment::Attribute &Attr = STC->getAttr(I);
173 Out << " " << Attr.Name.str();
174 if (!Attr.Value.str().empty())
175 Out << "=\"" << Attr.Value.str() << "\"";
176 }
177
178 if (STC->isSelfClosing())
179 Out << " /";
180 Out << ">";
181
182 Out << (STC->hasTrailingNewline() ? "\n" : "");
183 }
184
185 void visitHTMLEndTagComment(const comments::HTMLEndTagComment *ETC) {
186 Out << "</" << ETC->getTagName().str() << ">"
187 << (ETC->hasTrailingNewline() ? "\n" : "");
188 }
189
190private:
191 llvm::raw_string_ostream &Out;
192 const comments::CommandTraits &Traits;
193};
194
196 : public comments::ConstCommentVisitor<BlockCommentToMarkupDocument> {
197public:
198 BlockCommentToMarkupDocument(markup::Document &Out,
199 const comments::CommandTraits &Traits)
200 : Out(Out), Traits(Traits) {}
201
202 void visitBlockCommandComment(const comments::BlockCommandComment *B) {
203
204 switch (B->getCommandID()) {
205 case comments::CommandTraits::KCI_arg:
206 case comments::CommandTraits::KCI_li:
207 // \li and \arg are special cases, they are used to create a list item.
208 // In markdown it is a bullet list.
209 ParagraphToMarkupDocument(Out.addBulletList().addItem().addParagraph(),
210 Traits)
211 .visit(B->getParagraph());
212 break;
213 default: {
214 // Some commands have arguments, like \throws.
215 // The arguments are not part of the paragraph.
216 // We need reconstruct them here.
217 std::string ArgText;
218 for (unsigned I = 0; I < B->getNumArgs(); ++I) {
219 if (!ArgText.empty())
220 ArgText += " ";
221 ArgText += B->getArgText(I);
222 }
223 auto &P = Out.addParagraph();
224 commandToMarkup(P, B->getCommandName(Traits), B->getCommandMarker(),
225 ArgText);
226 if (B->getParagraph() && !B->getParagraph()->isWhitespace()) {
227 // For commands with arguments, the paragraph starts after the first
228 // space. Therefore we need to append a space manually in this case.
229 if (!ArgText.empty())
230 P.appendSpace();
231 ParagraphToMarkupDocument(P, Traits).visit(B->getParagraph());
232 }
233 }
234 }
235 }
236
237 void visitVerbatimBlockComment(const comments::VerbatimBlockComment *VB) {
238 commandToMarkup(Out.addParagraph(), VB->getCommandName(Traits),
239 VB->getCommandMarker(), "");
240
241 std::string VerbatimText;
242
243 for (const auto *LI = VB->child_begin(); LI != VB->child_end(); ++LI) {
244 if (const auto *Line = cast<comments::VerbatimBlockLineComment>(*LI)) {
245 VerbatimText += Line->getText().str() + "\n";
246 }
247 }
248
249 Out.addCodeBlock(VerbatimText, "");
250
251 commandToMarkup(Out.addParagraph(), VB->getCloseName(),
252 VB->getCommandMarker(), "");
253 }
254
255 void visitVerbatimLineComment(const comments::VerbatimLineComment *VL) {
256 auto &P = Out.addParagraph();
257 commandToMarkup(P, VL->getCommandName(Traits), VL->getCommandMarker(), "");
258 P.appendSpace().appendCode(VL->getText().str(), true).appendSpace();
259 }
260
261private:
262 markup::Document &Out;
263 const comments::CommandTraits &Traits;
264 StringRef CommentEscapeMarker;
265};
266
268 const comments::BlockCommandComment *B) {
269 switch (B->getCommandID()) {
270 case comments::CommandTraits::KCI_brief: {
271 if (!BriefParagraph) {
272 BriefParagraph = B->getParagraph();
273 return;
274 }
275 break;
276 }
277 case comments::CommandTraits::KCI_return:
278 case comments::CommandTraits::KCI_returns:
279 if (!ReturnParagraph) {
280 ReturnParagraph = B->getParagraph();
281 return;
282 }
283 break;
284 case comments::CommandTraits::KCI_retval:
285 RetvalParagraphs.push_back(B->getParagraph());
286 return;
287 case comments::CommandTraits::KCI_warning:
288 WarningParagraphs.push_back(B->getParagraph());
289 return;
290 case comments::CommandTraits::KCI_note:
291 NoteParagraphs.push_back(B->getParagraph());
292 return;
293 default:
294 break;
295 }
296
297 // For all other commands, we store them in the UnhandledCommands map.
298 // This allows us to keep the order of the comments.
299 UnhandledCommands[CommentPartIndex] = B;
300 CommentPartIndex++;
301}
302
303void SymbolDocCommentVisitor::paragraphsToMarkup(
304 markup::Document &Out,
306 const {
307 if (Paragraphs.empty())
308 return;
309
310 for (const auto *P : Paragraphs) {
311 ParagraphToMarkupDocument(Out.addParagraph(), Traits).visit(P);
312 }
313}
314
316 if (!BriefParagraph)
317 return;
318 ParagraphToMarkupDocument(Out, Traits).visit(BriefParagraph);
319}
320
322 if (!ReturnParagraph)
323 return;
324 ParagraphToMarkupDocument(Out, Traits).visit(ReturnParagraph);
325}
326
327void SymbolDocCommentVisitor::notesToMarkup(markup::Document &Out) const {
328 paragraphsToMarkup(Out, NoteParagraphs);
329}
330
331void SymbolDocCommentVisitor::warningsToMarkup(markup::Document &Out) const {
332 paragraphsToMarkup(Out, WarningParagraphs);
333}
334
336 StringRef ParamName, markup::Paragraph &Out) const {
337 if (ParamName.empty())
338 return;
339
340 if (const auto *P = Parameters.lookup(ParamName)) {
341 ParagraphToMarkupDocument(Out, Traits).visit(P->getParagraph());
342 }
343}
344
346 StringRef ParamName, llvm::raw_string_ostream &Out) const {
347 if (ParamName.empty())
348 return;
349
350 if (const auto *P = Parameters.lookup(ParamName)) {
351 ParagraphToString(Out, Traits).visit(P->getParagraph());
352 }
353}
354
355void SymbolDocCommentVisitor::docToMarkup(markup::Document &Out) const {
356 for (unsigned I = 0; I < CommentPartIndex; ++I) {
357 if (const auto *BC = UnhandledCommands.lookup(I)) {
358 BlockCommentToMarkupDocument(Out, Traits).visit(BC);
359 } else if (const auto *P = FreeParagraphs.lookup(I)) {
360 ParagraphToMarkupDocument(Out.addParagraph(), Traits).visit(P);
361 }
362 }
363}
364
366 StringRef TemplateParamName, markup::Paragraph &Out) const {
367 if (TemplateParamName.empty())
368 return;
369
370 if (const auto *TP = TemplateParameters.lookup(TemplateParamName)) {
371 ParagraphToMarkupDocument(Out, Traits).visit(TP->getParagraph());
372 }
373}
374
376 StringRef TemplateParamName, llvm::raw_string_ostream &Out) const {
377 if (TemplateParamName.empty())
378 return;
379
380 if (const auto *P = TemplateParameters.lookup(TemplateParamName)) {
381 ParagraphToString(Out, Traits).visit(P->getParagraph());
382 }
383}
384
385} // namespace clangd
386} // namespace clang
void visitVerbatimLineComment(const comments::VerbatimLineComment *VL)
void visitVerbatimBlockComment(const comments::VerbatimBlockComment *VB)
BlockCommentToMarkupDocument(markup::Document &Out, const comments::CommandTraits &Traits)
void visitBlockCommandComment(const comments::BlockCommandComment *B)
void visitHTMLEndTagComment(const comments::HTMLEndTagComment *ETC)
void visitTextComment(const comments::TextComment *C)
void visitHTMLStartTagComment(const comments::HTMLStartTagComment *STC)
void visitParagraphComment(const comments::ParagraphComment *C)
void visitInlineCommandComment(const comments::InlineCommandComment *C)
ParagraphToMarkupDocument(markup::Paragraph &Out, const comments::CommandTraits &Traits)
void visitInlineCommandComment(const comments::InlineCommandComment *C)
void visitHTMLEndTagComment(const comments::HTMLEndTagComment *ETC)
void visitParagraphComment(const comments::ParagraphComment *C)
ParagraphToString(llvm::raw_string_ostream &Out, const comments::CommandTraits &Traits)
void visitTextComment(const comments::TextComment *C)
void visitHTMLStartTagComment(const comments::HTMLStartTagComment *STC)
void notesToMarkup(markup::Document &Out) const
Converts the "note" command(s) to a markup document.
void docToMarkup(markup::Document &Out) const
Converts all unhandled comment commands to a markup document.
void templateTypeParmDocToMarkup(StringRef TemplateParamName, markup::Paragraph &Out) const
void returnToMarkup(markup::Paragraph &Out) const
Converts the "return" command(s) to a markup document.
void parameterDocToMarkup(StringRef ParamName, markup::Paragraph &Out) const
void templateTypeParmDocToString(StringRef TemplateParamName, llvm::raw_string_ostream &Out) const
void parameterDocToString(StringRef ParamName, llvm::raw_string_ostream &Out) const
void visitBlockCommandComment(const comments::BlockCommandComment *B)
void briefToMarkup(markup::Paragraph &Out) const
Converts the "brief" command(s) to a markup document.
void warningsToMarkup(markup::Document &Out) const
Converts the "warning" command(s) to a markup document.
Represents parts of the markup that can contain strings, like inline code, code block or plain text.
Definition Markup.h:45
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:44
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//