clang 19.0.0git
CommentToXML.cpp
Go to the documentation of this file.
1//===--- CommentToXML.cpp - Convert comments to XML representation --------===//
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
11#include "clang/AST/Attr.h"
12#include "clang/AST/Comment.h"
16#include "clang/Format/Format.h"
18#include "llvm/ADT/StringExtras.h"
19#include "llvm/ADT/TinyPtrVector.h"
20#include "llvm/Support/raw_ostream.h"
21
22using namespace clang;
23using namespace clang::comments;
24using namespace clang::index;
25
26namespace {
27
28/// This comparison will sort parameters with valid index by index, then vararg
29/// parameters, and invalid (unresolved) parameters last.
30class ParamCommandCommentCompareIndex {
31public:
32 bool operator()(const ParamCommandComment *LHS,
33 const ParamCommandComment *RHS) const {
34 unsigned LHSIndex = UINT_MAX;
35 unsigned RHSIndex = UINT_MAX;
36
37 if (LHS->isParamIndexValid()) {
38 if (LHS->isVarArgParam())
39 LHSIndex = UINT_MAX - 1;
40 else
41 LHSIndex = LHS->getParamIndex();
42 }
43 if (RHS->isParamIndexValid()) {
44 if (RHS->isVarArgParam())
45 RHSIndex = UINT_MAX - 1;
46 else
47 RHSIndex = RHS->getParamIndex();
48 }
49 return LHSIndex < RHSIndex;
50 }
51};
52
53/// This comparison will sort template parameters in the following order:
54/// \li real template parameters (depth = 1) in index order;
55/// \li all other names (depth > 1);
56/// \li unresolved names.
57class TParamCommandCommentComparePosition {
58public:
59 bool operator()(const TParamCommandComment *LHS,
60 const TParamCommandComment *RHS) const {
61 // Sort unresolved names last.
62 if (!LHS->isPositionValid())
63 return false;
64 if (!RHS->isPositionValid())
65 return true;
66
67 if (LHS->getDepth() > 1)
68 return false;
69 if (RHS->getDepth() > 1)
70 return true;
71
72 // Sort template parameters in index order.
73 if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
74 return LHS->getIndex(0) < RHS->getIndex(0);
75
76 // Leave all other names in source order.
77 return true;
78 }
79};
80
81/// Separate parts of a FullComment.
82struct FullCommentParts {
83 /// Take a full comment apart and initialize members accordingly.
84 FullCommentParts(const FullComment *C,
85 const CommandTraits &Traits);
86
87 const BlockContentComment *Brief;
88 const BlockContentComment *Headerfile;
89 const ParagraphComment *FirstParagraph;
93 llvm::TinyPtrVector<const BlockCommandComment *> Exceptions;
95};
96
97FullCommentParts::FullCommentParts(const FullComment *C,
98 const CommandTraits &Traits) :
99 Brief(nullptr), Headerfile(nullptr), FirstParagraph(nullptr) {
100 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
101 I != E; ++I) {
102 const Comment *Child = *I;
103 if (!Child)
104 continue;
105 switch (Child->getCommentKind()) {
106 case CommentKind::None:
107 continue;
108
109 case CommentKind::ParagraphComment: {
110 const ParagraphComment *PC = cast<ParagraphComment>(Child);
111 if (PC->isWhitespace())
112 break;
113 if (!FirstParagraph)
114 FirstParagraph = PC;
115
116 MiscBlocks.push_back(PC);
117 break;
118 }
119
120 case CommentKind::BlockCommandComment: {
121 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
122 const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
123 if (!Brief && Info->IsBriefCommand) {
124 Brief = BCC;
125 break;
126 }
127 if (!Headerfile && Info->IsHeaderfileCommand) {
128 Headerfile = BCC;
129 break;
130 }
131 if (Info->IsReturnsCommand) {
132 Returns.push_back(BCC);
133 break;
134 }
135 if (Info->IsThrowsCommand) {
136 Exceptions.push_back(BCC);
137 break;
138 }
139 MiscBlocks.push_back(BCC);
140 break;
141 }
142
143 case CommentKind::ParamCommandComment: {
144 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
145 if (!PCC->hasParamName())
146 break;
147
148 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
149 break;
150
151 Params.push_back(PCC);
152 break;
153 }
154
155 case CommentKind::TParamCommandComment: {
156 const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
157 if (!TPCC->hasParamName())
158 break;
159
160 if (!TPCC->hasNonWhitespaceParagraph())
161 break;
162
163 TParams.push_back(TPCC);
164 break;
165 }
166
167 case CommentKind::VerbatimBlockComment:
168 MiscBlocks.push_back(cast<BlockCommandComment>(Child));
169 break;
170
171 case CommentKind::VerbatimLineComment: {
172 const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
173 const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());
174 if (!Info->IsDeclarationCommand)
175 MiscBlocks.push_back(VLC);
176 break;
177 }
178
179 case CommentKind::TextComment:
180 case CommentKind::InlineCommandComment:
181 case CommentKind::HTMLStartTagComment:
182 case CommentKind::HTMLEndTagComment:
183 case CommentKind::VerbatimBlockLineComment:
184 case CommentKind::FullComment:
185 llvm_unreachable("AST node of this kind can't be a child of "
186 "a FullComment");
187 }
188 }
189
190 // Sort params in order they are declared in the function prototype.
191 // Unresolved parameters are put at the end of the list in the same order
192 // they were seen in the comment.
193 llvm::stable_sort(Params, ParamCommandCommentCompareIndex());
194 llvm::stable_sort(TParams, TParamCommandCommentComparePosition());
195}
196
197void printHTMLStartTagComment(const HTMLStartTagComment *C,
198 llvm::raw_svector_ostream &Result) {
199 Result << "<" << C->getTagName();
200
201 if (C->getNumAttrs() != 0) {
202 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
203 Result << " ";
204 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
205 Result << Attr.Name;
206 if (!Attr.Value.empty())
207 Result << "=\"" << Attr.Value << "\"";
208 }
209 }
210
211 if (!C->isSelfClosing())
212 Result << ">";
213 else
214 Result << "/>";
215}
216
217class CommentASTToHTMLConverter :
218 public ConstCommentVisitor<CommentASTToHTMLConverter> {
219public:
220 /// \param Str accumulator for HTML.
221 CommentASTToHTMLConverter(const FullComment *FC,
223 const CommandTraits &Traits) :
224 FC(FC), Result(Str), Traits(Traits)
225 { }
226
227 // Inline content.
228 void visitTextComment(const TextComment *C);
229 void visitInlineCommandComment(const InlineCommandComment *C);
230 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
231 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
232
233 // Block content.
234 void visitParagraphComment(const ParagraphComment *C);
235 void visitBlockCommandComment(const BlockCommandComment *C);
236 void visitParamCommandComment(const ParamCommandComment *C);
237 void visitTParamCommandComment(const TParamCommandComment *C);
238 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
239 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
240 void visitVerbatimLineComment(const VerbatimLineComment *C);
241
242 void visitFullComment(const FullComment *C);
243
244 // Helpers.
245
246 /// Convert a paragraph that is not a block by itself (an argument to some
247 /// command).
248 void visitNonStandaloneParagraphComment(const ParagraphComment *C);
249
250 void appendToResultWithHTMLEscaping(StringRef S);
251
252private:
253 const FullComment *FC;
254 /// Output stream for HTML.
255 llvm::raw_svector_ostream Result;
256
257 const CommandTraits &Traits;
258};
259} // end unnamed namespace
260
261void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
262 appendToResultWithHTMLEscaping(C->getText());
263}
264
265void CommentASTToHTMLConverter::visitInlineCommandComment(
266 const InlineCommandComment *C) {
267 // Nothing to render if no arguments supplied.
268 if (C->getNumArgs() == 0)
269 return;
270
271 // Nothing to render if argument is empty.
272 StringRef Arg0 = C->getArgText(0);
273 if (Arg0.empty())
274 return;
275
276 switch (C->getRenderKind()) {
277 case InlineCommandRenderKind::Normal:
278 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
279 appendToResultWithHTMLEscaping(C->getArgText(i));
280 Result << " ";
281 }
282 return;
283
284 case InlineCommandRenderKind::Bold:
285 assert(C->getNumArgs() == 1);
286 Result << "<b>";
287 appendToResultWithHTMLEscaping(Arg0);
288 Result << "</b>";
289 return;
290 case InlineCommandRenderKind::Monospaced:
291 assert(C->getNumArgs() == 1);
292 Result << "<tt>";
293 appendToResultWithHTMLEscaping(Arg0);
294 Result<< "</tt>";
295 return;
296 case InlineCommandRenderKind::Emphasized:
297 assert(C->getNumArgs() == 1);
298 Result << "<em>";
299 appendToResultWithHTMLEscaping(Arg0);
300 Result << "</em>";
301 return;
302 case InlineCommandRenderKind::Anchor:
303 assert(C->getNumArgs() == 1);
304 Result << "<span id=\"" << Arg0 << "\"></span>";
305 return;
306 }
307}
308
309void CommentASTToHTMLConverter::visitHTMLStartTagComment(
310 const HTMLStartTagComment *C) {
311 printHTMLStartTagComment(C, Result);
312}
313
314void CommentASTToHTMLConverter::visitHTMLEndTagComment(
315 const HTMLEndTagComment *C) {
316 Result << "</" << C->getTagName() << ">";
317}
318
319void CommentASTToHTMLConverter::visitParagraphComment(
320 const ParagraphComment *C) {
321 if (C->isWhitespace())
322 return;
323
324 Result << "<p>";
325 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
326 I != E; ++I) {
327 visit(*I);
328 }
329 Result << "</p>";
330}
331
332void CommentASTToHTMLConverter::visitBlockCommandComment(
333 const BlockCommandComment *C) {
334 const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
335 if (Info->IsBriefCommand) {
336 Result << "<p class=\"para-brief\">";
337 visitNonStandaloneParagraphComment(C->getParagraph());
338 Result << "</p>";
339 return;
340 }
341 if (Info->IsReturnsCommand) {
342 Result << "<p class=\"para-returns\">"
343 "<span class=\"word-returns\">Returns</span> ";
344 visitNonStandaloneParagraphComment(C->getParagraph());
345 Result << "</p>";
346 return;
347 }
348 // We don't know anything about this command. Just render the paragraph.
349 visit(C->getParagraph());
350}
351
352void CommentASTToHTMLConverter::visitParamCommandComment(
353 const ParamCommandComment *C) {
354 if (C->isParamIndexValid()) {
355 if (C->isVarArgParam()) {
356 Result << "<dt class=\"param-name-index-vararg\">";
357 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
358 } else {
359 Result << "<dt class=\"param-name-index-"
360 << C->getParamIndex()
361 << "\">";
362 appendToResultWithHTMLEscaping(C->getParamName(FC));
363 }
364 } else {
365 Result << "<dt class=\"param-name-index-invalid\">";
366 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
367 }
368 Result << "</dt>";
369
370 if (C->isParamIndexValid()) {
371 if (C->isVarArgParam())
372 Result << "<dd class=\"param-descr-index-vararg\">";
373 else
374 Result << "<dd class=\"param-descr-index-"
375 << C->getParamIndex()
376 << "\">";
377 } else
378 Result << "<dd class=\"param-descr-index-invalid\">";
379
380 visitNonStandaloneParagraphComment(C->getParagraph());
381 Result << "</dd>";
382}
383
384void CommentASTToHTMLConverter::visitTParamCommandComment(
385 const TParamCommandComment *C) {
386 if (C->isPositionValid()) {
387 if (C->getDepth() == 1)
388 Result << "<dt class=\"tparam-name-index-"
389 << C->getIndex(0)
390 << "\">";
391 else
392 Result << "<dt class=\"tparam-name-index-other\">";
393 appendToResultWithHTMLEscaping(C->getParamName(FC));
394 } else {
395 Result << "<dt class=\"tparam-name-index-invalid\">";
396 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
397 }
398
399 Result << "</dt>";
400
401 if (C->isPositionValid()) {
402 if (C->getDepth() == 1)
403 Result << "<dd class=\"tparam-descr-index-"
404 << C->getIndex(0)
405 << "\">";
406 else
407 Result << "<dd class=\"tparam-descr-index-other\">";
408 } else
409 Result << "<dd class=\"tparam-descr-index-invalid\">";
410
411 visitNonStandaloneParagraphComment(C->getParagraph());
412 Result << "</dd>";
413}
414
415void CommentASTToHTMLConverter::visitVerbatimBlockComment(
416 const VerbatimBlockComment *C) {
417 unsigned NumLines = C->getNumLines();
418 if (NumLines == 0)
419 return;
420
421 Result << "<pre>";
422 for (unsigned i = 0; i != NumLines; ++i) {
423 appendToResultWithHTMLEscaping(C->getText(i));
424 if (i + 1 != NumLines)
425 Result << '\n';
426 }
427 Result << "</pre>";
428}
429
430void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
432 llvm_unreachable("should not see this AST node");
433}
434
435void CommentASTToHTMLConverter::visitVerbatimLineComment(
436 const VerbatimLineComment *C) {
437 Result << "<pre>";
438 appendToResultWithHTMLEscaping(C->getText());
439 Result << "</pre>";
440}
441
442void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
443 FullCommentParts Parts(C, Traits);
444
445 bool FirstParagraphIsBrief = false;
446 if (Parts.Headerfile)
447 visit(Parts.Headerfile);
448 if (Parts.Brief)
449 visit(Parts.Brief);
450 else if (Parts.FirstParagraph) {
451 Result << "<p class=\"para-brief\">";
452 visitNonStandaloneParagraphComment(Parts.FirstParagraph);
453 Result << "</p>";
454 FirstParagraphIsBrief = true;
455 }
456
457 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
458 const Comment *C = Parts.MiscBlocks[i];
459 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
460 continue;
461 visit(C);
462 }
463
464 if (Parts.TParams.size() != 0) {
465 Result << "<dl>";
466 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
467 visit(Parts.TParams[i]);
468 Result << "</dl>";
469 }
470
471 if (Parts.Params.size() != 0) {
472 Result << "<dl>";
473 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
474 visit(Parts.Params[i]);
475 Result << "</dl>";
476 }
477
478 if (Parts.Returns.size() != 0) {
479 Result << "<div class=\"result-discussion\">";
480 for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
481 visit(Parts.Returns[i]);
482 Result << "</div>";
483 }
484
485}
486
487void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
488 const ParagraphComment *C) {
489 if (!C)
490 return;
491
492 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
493 I != E; ++I) {
494 visit(*I);
495 }
496}
497
498void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
499 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
500 const char C = *I;
501 switch (C) {
502 case '&':
503 Result << "&amp;";
504 break;
505 case '<':
506 Result << "&lt;";
507 break;
508 case '>':
509 Result << "&gt;";
510 break;
511 case '"':
512 Result << "&quot;";
513 break;
514 case '\'':
515 Result << "&#39;";
516 break;
517 case '/':
518 Result << "&#47;";
519 break;
520 default:
521 Result << C;
522 break;
523 }
524 }
525}
526
527namespace {
528class CommentASTToXMLConverter :
529 public ConstCommentVisitor<CommentASTToXMLConverter> {
530public:
531 /// \param Str accumulator for XML.
532 CommentASTToXMLConverter(const FullComment *FC,
534 const CommandTraits &Traits,
535 const SourceManager &SM) :
536 FC(FC), Result(Str), Traits(Traits), SM(SM) { }
537
538 // Inline content.
539 void visitTextComment(const TextComment *C);
540 void visitInlineCommandComment(const InlineCommandComment *C);
541 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
542 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
543
544 // Block content.
545 void visitParagraphComment(const ParagraphComment *C);
546
547 void appendParagraphCommentWithKind(const ParagraphComment *C,
548 StringRef Kind);
549
550 void visitBlockCommandComment(const BlockCommandComment *C);
551 void visitParamCommandComment(const ParamCommandComment *C);
552 void visitTParamCommandComment(const TParamCommandComment *C);
553 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
554 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
555 void visitVerbatimLineComment(const VerbatimLineComment *C);
556
557 void visitFullComment(const FullComment *C);
558
559 // Helpers.
560 void appendToResultWithXMLEscaping(StringRef S);
561 void appendToResultWithCDATAEscaping(StringRef S);
562
563 void formatTextOfDeclaration(const DeclInfo *DI,
565
566private:
567 const FullComment *FC;
568
569 /// Output stream for XML.
570 llvm::raw_svector_ostream Result;
571
572 const CommandTraits &Traits;
573 const SourceManager &SM;
574};
575
576void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
578 ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
579 const LangOptions &LangOpts = Context.getLangOpts();
580 llvm::raw_svector_ostream OS(Str);
581 PrintingPolicy PPolicy(LangOpts);
582 PPolicy.PolishForDeclaration = true;
583 PPolicy.TerseOutput = true;
584 PPolicy.ConstantsAsWritten = true;
585 ThisDecl->CurrentDecl->print(OS, PPolicy,
586 /*Indentation*/0, /*PrintInstantiation*/false);
587}
588
589void CommentASTToXMLConverter::formatTextOfDeclaration(
591 // Formatting API expects null terminated input string.
592 StringRef StringDecl(Declaration.c_str(), Declaration.size());
593
594 // Formatter specific code.
595 unsigned Offset = 0;
596 unsigned Length = Declaration.size();
597
599 Style.FixNamespaceComments = false;
600 tooling::Replacements Replaces =
601 reformat(Style, StringDecl, tooling::Range(Offset, Length), "xmldecl.xd");
602 auto FormattedStringDecl = applyAllReplacements(StringDecl, Replaces);
603 if (static_cast<bool>(FormattedStringDecl)) {
604 Declaration = *FormattedStringDecl;
605 }
606}
607
608} // end unnamed namespace
609
610void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
611 appendToResultWithXMLEscaping(C->getText());
612}
613
614void CommentASTToXMLConverter::visitInlineCommandComment(
615 const InlineCommandComment *C) {
616 // Nothing to render if no arguments supplied.
617 if (C->getNumArgs() == 0)
618 return;
619
620 // Nothing to render if argument is empty.
621 StringRef Arg0 = C->getArgText(0);
622 if (Arg0.empty())
623 return;
624
625 switch (C->getRenderKind()) {
626 case InlineCommandRenderKind::Normal:
627 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
628 appendToResultWithXMLEscaping(C->getArgText(i));
629 Result << " ";
630 }
631 return;
632 case InlineCommandRenderKind::Bold:
633 assert(C->getNumArgs() == 1);
634 Result << "<bold>";
635 appendToResultWithXMLEscaping(Arg0);
636 Result << "</bold>";
637 return;
638 case InlineCommandRenderKind::Monospaced:
639 assert(C->getNumArgs() == 1);
640 Result << "<monospaced>";
641 appendToResultWithXMLEscaping(Arg0);
642 Result << "</monospaced>";
643 return;
644 case InlineCommandRenderKind::Emphasized:
645 assert(C->getNumArgs() == 1);
646 Result << "<emphasized>";
647 appendToResultWithXMLEscaping(Arg0);
648 Result << "</emphasized>";
649 return;
650 case InlineCommandRenderKind::Anchor:
651 assert(C->getNumArgs() == 1);
652 Result << "<anchor id=\"" << Arg0 << "\"></anchor>";
653 return;
654 }
655}
656
657void CommentASTToXMLConverter::visitHTMLStartTagComment(
658 const HTMLStartTagComment *C) {
659 Result << "<rawHTML";
660 if (C->isMalformed())
661 Result << " isMalformed=\"1\"";
662 Result << ">";
663 {
664 SmallString<32> Tag;
665 {
666 llvm::raw_svector_ostream TagOS(Tag);
667 printHTMLStartTagComment(C, TagOS);
668 }
669 appendToResultWithCDATAEscaping(Tag);
670 }
671 Result << "</rawHTML>";
672}
673
674void
675CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
676 Result << "<rawHTML";
677 if (C->isMalformed())
678 Result << " isMalformed=\"1\"";
679 Result << ">&lt;/" << C->getTagName() << "&gt;</rawHTML>";
680}
681
682void
683CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
684 appendParagraphCommentWithKind(C, StringRef());
685}
686
687void CommentASTToXMLConverter::appendParagraphCommentWithKind(
688 const ParagraphComment *C,
689 StringRef ParagraphKind) {
690 if (C->isWhitespace())
691 return;
692
693 if (ParagraphKind.empty())
694 Result << "<Para>";
695 else
696 Result << "<Para kind=\"" << ParagraphKind << "\">";
697
698 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
699 I != E; ++I) {
700 visit(*I);
701 }
702 Result << "</Para>";
703}
704
705void CommentASTToXMLConverter::visitBlockCommandComment(
706 const BlockCommandComment *C) {
707 StringRef ParagraphKind;
708
709 switch (C->getCommandID()) {
710 case CommandTraits::KCI_attention:
711 case CommandTraits::KCI_author:
712 case CommandTraits::KCI_authors:
713 case CommandTraits::KCI_bug:
714 case CommandTraits::KCI_copyright:
715 case CommandTraits::KCI_date:
716 case CommandTraits::KCI_invariant:
717 case CommandTraits::KCI_note:
718 case CommandTraits::KCI_post:
719 case CommandTraits::KCI_pre:
720 case CommandTraits::KCI_remark:
721 case CommandTraits::KCI_remarks:
722 case CommandTraits::KCI_sa:
723 case CommandTraits::KCI_see:
724 case CommandTraits::KCI_since:
725 case CommandTraits::KCI_todo:
726 case CommandTraits::KCI_version:
727 case CommandTraits::KCI_warning:
728 ParagraphKind = C->getCommandName(Traits);
729 break;
730 default:
731 break;
732 }
733
734 appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
735}
736
737void CommentASTToXMLConverter::visitParamCommandComment(
738 const ParamCommandComment *C) {
739 Result << "<Parameter><Name>";
740 appendToResultWithXMLEscaping(C->isParamIndexValid()
741 ? C->getParamName(FC)
742 : C->getParamNameAsWritten());
743 Result << "</Name>";
744
745 if (C->isParamIndexValid()) {
746 if (C->isVarArgParam())
747 Result << "<IsVarArg />";
748 else
749 Result << "<Index>" << C->getParamIndex() << "</Index>";
750 }
751
752 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
753 switch (C->getDirection()) {
754 case ParamCommandPassDirection::In:
755 Result << "in";
756 break;
757 case ParamCommandPassDirection::Out:
758 Result << "out";
759 break;
760 case ParamCommandPassDirection::InOut:
761 Result << "in,out";
762 break;
763 }
764 Result << "</Direction><Discussion>";
765 visit(C->getParagraph());
766 Result << "</Discussion></Parameter>";
767}
768
769void CommentASTToXMLConverter::visitTParamCommandComment(
770 const TParamCommandComment *C) {
771 Result << "<Parameter><Name>";
772 appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
773 : C->getParamNameAsWritten());
774 Result << "</Name>";
775
776 if (C->isPositionValid() && C->getDepth() == 1) {
777 Result << "<Index>" << C->getIndex(0) << "</Index>";
778 }
779
780 Result << "<Discussion>";
781 visit(C->getParagraph());
782 Result << "</Discussion></Parameter>";
783}
784
785void CommentASTToXMLConverter::visitVerbatimBlockComment(
786 const VerbatimBlockComment *C) {
787 unsigned NumLines = C->getNumLines();
788 if (NumLines == 0)
789 return;
790
791 switch (C->getCommandID()) {
792 case CommandTraits::KCI_code:
793 Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
794 break;
795 default:
796 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
797 break;
798 }
799 for (unsigned i = 0; i != NumLines; ++i) {
800 appendToResultWithXMLEscaping(C->getText(i));
801 if (i + 1 != NumLines)
802 Result << '\n';
803 }
804 Result << "</Verbatim>";
805}
806
807void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
809 llvm_unreachable("should not see this AST node");
810}
811
812void CommentASTToXMLConverter::visitVerbatimLineComment(
813 const VerbatimLineComment *C) {
814 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
815 appendToResultWithXMLEscaping(C->getText());
816 Result << "</Verbatim>";
817}
818
819void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
820 FullCommentParts Parts(C, Traits);
821
822 const DeclInfo *DI = C->getDeclInfo();
823 StringRef RootEndTag;
824 if (DI) {
825 switch (DI->getKind()) {
827 RootEndTag = "</Other>";
828 Result << "<Other";
829 break;
831 RootEndTag = "</Function>";
832 Result << "<Function";
833 switch (DI->TemplateKind) {
835 break;
837 Result << " templateKind=\"template\"";
838 break;
840 Result << " templateKind=\"specialization\"";
841 break;
843 llvm_unreachable("partial specializations of functions "
844 "are not allowed in C++");
845 }
846 if (DI->IsInstanceMethod)
847 Result << " isInstanceMethod=\"1\"";
848 if (DI->IsClassMethod)
849 Result << " isClassMethod=\"1\"";
850 break;
852 RootEndTag = "</Class>";
853 Result << "<Class";
854 switch (DI->TemplateKind) {
856 break;
858 Result << " templateKind=\"template\"";
859 break;
861 Result << " templateKind=\"specialization\"";
862 break;
864 Result << " templateKind=\"partialSpecialization\"";
865 break;
866 }
867 break;
869 RootEndTag = "</Variable>";
870 Result << "<Variable";
871 break;
873 RootEndTag = "</Namespace>";
874 Result << "<Namespace";
875 break;
877 RootEndTag = "</Typedef>";
878 Result << "<Typedef";
879 break;
881 RootEndTag = "</Enum>";
882 Result << "<Enum";
883 break;
884 }
885
886 {
887 // Print line and column number.
889 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
890 FileID FID = LocInfo.first;
891 unsigned FileOffset = LocInfo.second;
892
893 if (FID.isValid()) {
894 if (OptionalFileEntryRef FE = SM.getFileEntryRefForID(FID)) {
895 Result << " file=\"";
896 appendToResultWithXMLEscaping(FE->getName());
897 Result << "\"";
898 }
899 Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
900 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
901 << "\"";
902 }
903 }
904
905 // Finish the root tag.
906 Result << ">";
907
908 bool FoundName = false;
909 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
910 if (DeclarationName DeclName = ND->getDeclName()) {
911 Result << "<Name>";
912 std::string Name = DeclName.getAsString();
913 appendToResultWithXMLEscaping(Name);
914 FoundName = true;
915 Result << "</Name>";
916 }
917 }
918 if (!FoundName)
919 Result << "<Name>&lt;anonymous&gt;</Name>";
920
921 {
922 // Print USR.
925 if (!USR.empty()) {
926 Result << "<USR>";
927 appendToResultWithXMLEscaping(USR);
928 Result << "</USR>";
929 }
930 }
931 } else {
932 // No DeclInfo -- just emit some root tag and name tag.
933 RootEndTag = "</Other>";
934 Result << "<Other><Name>unknown</Name>";
935 }
936
937 if (Parts.Headerfile) {
938 Result << "<Headerfile>";
939 visit(Parts.Headerfile);
940 Result << "</Headerfile>";
941 }
942
943 {
944 // Pretty-print the declaration.
945 Result << "<Declaration>";
947 getSourceTextOfDeclaration(DI, Declaration);
948 formatTextOfDeclaration(DI, Declaration);
949 appendToResultWithXMLEscaping(Declaration);
950 Result << "</Declaration>";
951 }
952
953 bool FirstParagraphIsBrief = false;
954 if (Parts.Brief) {
955 Result << "<Abstract>";
956 visit(Parts.Brief);
957 Result << "</Abstract>";
958 } else if (Parts.FirstParagraph) {
959 Result << "<Abstract>";
960 visit(Parts.FirstParagraph);
961 Result << "</Abstract>";
962 FirstParagraphIsBrief = true;
963 }
964
965 if (Parts.TParams.size() != 0) {
966 Result << "<TemplateParameters>";
967 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
968 visit(Parts.TParams[i]);
969 Result << "</TemplateParameters>";
970 }
971
972 if (Parts.Params.size() != 0) {
973 Result << "<Parameters>";
974 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
975 visit(Parts.Params[i]);
976 Result << "</Parameters>";
977 }
978
979 if (Parts.Exceptions.size() != 0) {
980 Result << "<Exceptions>";
981 for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
982 visit(Parts.Exceptions[i]);
983 Result << "</Exceptions>";
984 }
985
986 if (Parts.Returns.size() != 0) {
987 Result << "<ResultDiscussion>";
988 for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
989 visit(Parts.Returns[i]);
990 Result << "</ResultDiscussion>";
991 }
992
993 if (DI->CommentDecl->hasAttrs()) {
994 const AttrVec &Attrs = DI->CommentDecl->getAttrs();
995 for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
996 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
997 if (!AA) {
998 if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
999 if (DA->getMessage().empty())
1000 Result << "<Deprecated/>";
1001 else {
1002 Result << "<Deprecated>";
1003 appendToResultWithXMLEscaping(DA->getMessage());
1004 Result << "</Deprecated>";
1005 }
1006 }
1007 else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1008 if (UA->getMessage().empty())
1009 Result << "<Unavailable/>";
1010 else {
1011 Result << "<Unavailable>";
1012 appendToResultWithXMLEscaping(UA->getMessage());
1013 Result << "</Unavailable>";
1014 }
1015 }
1016 continue;
1017 }
1018
1019 // 'availability' attribute.
1020 Result << "<Availability";
1021 StringRef Distribution;
1022 if (AA->getPlatform()) {
1023 Distribution = AvailabilityAttr::getPrettyPlatformName(
1024 AA->getPlatform()->getName());
1025 if (Distribution.empty())
1026 Distribution = AA->getPlatform()->getName();
1027 }
1028 Result << " distribution=\"" << Distribution << "\">";
1029 VersionTuple IntroducedInVersion = AA->getIntroduced();
1030 if (!IntroducedInVersion.empty()) {
1031 Result << "<IntroducedInVersion>"
1032 << IntroducedInVersion.getAsString()
1033 << "</IntroducedInVersion>";
1034 }
1035 VersionTuple DeprecatedInVersion = AA->getDeprecated();
1036 if (!DeprecatedInVersion.empty()) {
1037 Result << "<DeprecatedInVersion>"
1038 << DeprecatedInVersion.getAsString()
1039 << "</DeprecatedInVersion>";
1040 }
1041 VersionTuple RemovedAfterVersion = AA->getObsoleted();
1042 if (!RemovedAfterVersion.empty()) {
1043 Result << "<RemovedAfterVersion>"
1044 << RemovedAfterVersion.getAsString()
1045 << "</RemovedAfterVersion>";
1046 }
1047 StringRef DeprecationSummary = AA->getMessage();
1048 if (!DeprecationSummary.empty()) {
1049 Result << "<DeprecationSummary>";
1050 appendToResultWithXMLEscaping(DeprecationSummary);
1051 Result << "</DeprecationSummary>";
1052 }
1053 if (AA->getUnavailable())
1054 Result << "<Unavailable/>";
1055 Result << "</Availability>";
1056 }
1057 }
1058
1059 {
1060 bool StartTagEmitted = false;
1061 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1062 const Comment *C = Parts.MiscBlocks[i];
1063 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1064 continue;
1065 if (!StartTagEmitted) {
1066 Result << "<Discussion>";
1067 StartTagEmitted = true;
1068 }
1069 visit(C);
1070 }
1071 if (StartTagEmitted)
1072 Result << "</Discussion>";
1073 }
1074
1075 Result << RootEndTag;
1076}
1077
1078void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1079 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1080 const char C = *I;
1081 switch (C) {
1082 case '&':
1083 Result << "&amp;";
1084 break;
1085 case '<':
1086 Result << "&lt;";
1087 break;
1088 case '>':
1089 Result << "&gt;";
1090 break;
1091 case '"':
1092 Result << "&quot;";
1093 break;
1094 case '\'':
1095 Result << "&apos;";
1096 break;
1097 default:
1098 Result << C;
1099 break;
1100 }
1101 }
1102}
1103
1104void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) {
1105 if (S.empty())
1106 return;
1107
1108 Result << "<![CDATA[";
1109 while (!S.empty()) {
1110 size_t Pos = S.find("]]>");
1111 if (Pos == 0) {
1112 Result << "]]]]><![CDATA[>";
1113 S = S.drop_front(3);
1114 continue;
1115 }
1116 if (Pos == StringRef::npos)
1117 Pos = S.size();
1118
1119 Result << S.substr(0, Pos);
1120
1121 S = S.drop_front(Pos);
1122 }
1123 Result << "]]>";
1124}
1125
1128
1131 const ASTContext &Context) {
1132 CommentASTToHTMLConverter Converter(FC, HTML,
1133 Context.getCommentCommandTraits());
1134 Converter.visit(FC);
1135}
1136
1139 const ASTContext &Context) {
1140 CommentASTToHTMLConverter Converter(nullptr, Text,
1141 Context.getCommentCommandTraits());
1142 Converter.visit(HTC);
1143}
1144
1147 const ASTContext &Context) {
1148 CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
1149 Context.getSourceManager());
1150 Converter.visit(FC);
1151}
Defines the clang::ASTContext interface.
#define SM(sm)
Definition: Cuda.cpp:82
Defines the clang::FileManager interface and associated types.
StringRef Text
Definition: Format.cpp:2970
Various functions to configurably format source code.
Defines the SourceManager interface.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:182
SourceManager & getSourceManager()
Definition: ASTContext.h:702
comments::CommandTraits & getCommentCommandTraits() const
Definition: ASTContext.h:933
const LangOptions & getLangOpts() const
Definition: ASTContext.h:772
Attr - This represents one attribute.
Definition: Attr.h:42
bool hasAttrs() const
Definition: DeclBase.h:523
ASTContext & getASTContext() const LLVM_READONLY
Definition: DeclBase.cpp:501
SourceLocation getLocation() const
Definition: DeclBase.h:444
void print(raw_ostream &Out, unsigned Indentation=0, bool PrintInstantiation=false) const
AttrVec & getAttrs()
Definition: DeclBase.h:529
The name of a declaration.
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
bool isValid() const
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:449
This represents a decl that may have a name.
Definition: Decl.h:249
Encodes a location in the source.
This class handles loading and caching of source files into memory.
A command that has zero or more word-like arguments (number of word-like arguments depends on command...
Definition: Comment.h:604
Block content (contains inline content).
Definition: Comment.h:538
This class provides information about commands that can be used in comments.
const CommandInfo * getCommandInfo(StringRef Name) const
Any part of the comment.
Definition: Comment.h:65
Comment *const * child_iterator
Definition: Comment.h:251
A full comment attached to a declaration, contains block content.
Definition: Comment.h:1083
An opening HTML tag with attributes.
Definition: Comment.h:433
Abstract class for opening and closing HTML tags.
Definition: Comment.h:391
A command with word-like arguments that is considered inline content.
Definition: Comment.h:335
A single paragraph that contains inline content.
Definition: Comment.h:555
Doxygen \param command.
Definition: Comment.h:711
bool isDirectionExplicit() const LLVM_READONLY
Definition: Comment.h:743
unsigned getParamIndex() const LLVM_READONLY
Definition: Comment.h:779
bool isParamIndexValid() const LLVM_READONLY
Definition: Comment.h:766
bool isVarArgParam() const LLVM_READONLY
Definition: Comment.h:770
Doxygen \tparam command, describes a template parameter.
Definition: Comment.h:793
bool isPositionValid() const LLVM_READONLY
Definition: Comment.h:833
unsigned getIndex(unsigned Depth) const
Definition: Comment.h:842
A verbatim block command (e.
Definition: Comment.h:879
A line of text contained in a verbatim block.
Definition: Comment.h:854
A verbatim line command.
Definition: Comment.h:930
void convertCommentToXML(const comments::FullComment *FC, SmallVectorImpl< char > &XML, const ASTContext &Context)
void convertHTMLTagNodeToText(const comments::HTMLTagComment *HTC, SmallVectorImpl< char > &Text, const ASTContext &Context)
void convertCommentToHTML(const comments::FullComment *FC, SmallVectorImpl< char > &HTML, const ASTContext &Context)
A source range independent of the SourceManager.
Definition: Replacement.h:44
Maintains a set of replacements that are conflict-free.
Definition: Replacement.h:212
#define UINT_MAX
Definition: limits.h:60
FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language=FormatStyle::LanguageKind::LK_Cpp)
Returns a format style complying with the LLVM coding standards: http://llvm.org/docs/CodingStandards...
Definition: Format.cpp:1397
tooling::Replacements reformat(const FormatStyle &Style, StringRef Code, ArrayRef< tooling::Range > Ranges, StringRef FileName="<stdin>", FormattingAttemptStatus *Status=nullptr)
Reformats the given Ranges in Code.
Definition: Format.cpp:3779
bool generateUSRForDecl(const Decl *D, SmallVectorImpl< char > &Buf)
Generate a USR for a Decl, including the USR prefix.
bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite)
Apply all replacements in Replaces to the Rewriter Rewrite.
The JSON file list parser is used to communicate input to InstallAPI.
@ Result
The result type of a method or function.
Describes how types, statements, expressions, and declarations should be printed.
Definition: PrettyPrinter.h:57
Information about a single command.
unsigned IsDeclarationCommand
True if this command contains a declaration for the entity being documented.
unsigned IsThrowsCommand
True if this command is \throws or an alias.
unsigned IsReturnsCommand
True if this command is \returns or an alias.
unsigned IsBriefCommand
True if this command is introducing a brief documentation paragraph (\or an alias).
unsigned IsHeaderfileCommand
True if this is a \headerfile-like command.
Information about the declaration, useful to clients of FullComment.
Definition: Comment.h:962
unsigned TemplateKind
Is CommentDecl a template declaration.
Definition: Comment.h:1047
unsigned IsClassMethod
Is CommentDecl a static member function of C++ class or class method of ObjC class.
Definition: Comment.h:1063
@ FunctionKind
Something that we consider a "function":
Definition: Comment.h:1004
@ EnumKind
An enumeration or scoped enumeration.
Definition: Comment.h:1026
@ OtherKind
Everything else not explicitly mentioned below.
Definition: Comment.h:994
@ NamespaceKind
A C++ namespace.
Definition: Comment.h:1019
@ VariableKind
Something that we consider a "variable":
Definition: Comment.h:1016
@ ClassKind
Something that we consider a "class":
Definition: Comment.h:1010
@ TypedefKind
A C++ typedef-name (a 'typedef' decl specifier or alias-declaration), see TypedefNameDecl.
Definition: Comment.h:1023
DeclKind getKind() const LLVM_READONLY
Definition: Comment.h:1071
unsigned IsInstanceMethod
Is CommentDecl a non-static member function of C++ class or instance method of ObjC class.
Definition: Comment.h:1057
const Decl * CommentDecl
Declaration the comment is actually attached to (in the source).
Definition: Comment.h:965
const Decl * CurrentDecl
CurrentDecl is the declaration with which the FullComment is associated.
Definition: Comment.h:975
The FormatStyle is used to configure the formatting to follow specific guidelines.
Definition: Format.h:55