[docs] Fix build-docs.sh
[llvm-project.git] / clang / lib / Index / CommentToXML.cpp
blob1cbd14cd326c4dca4a44a58c299fd1d4774adbec
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 //===----------------------------------------------------------------------===//
9 #include "clang/Index/CommentToXML.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/Attr.h"
12 #include "clang/AST/Comment.h"
13 #include "clang/AST/CommentVisitor.h"
14 #include "clang/Basic/FileManager.h"
15 #include "clang/Basic/SourceManager.h"
16 #include "clang/Format/Format.h"
17 #include "clang/Index/USRGeneration.h"
18 #include "llvm/ADT/StringExtras.h"
19 #include "llvm/ADT/TinyPtrVector.h"
20 #include "llvm/Support/raw_ostream.h"
22 using namespace clang;
23 using namespace clang::comments;
24 using namespace clang::index;
26 namespace {
28 /// This comparison will sort parameters with valid index by index, then vararg
29 /// parameters, and invalid (unresolved) parameters last.
30 class ParamCommandCommentCompareIndex {
31 public:
32 bool operator()(const ParamCommandComment *LHS,
33 const ParamCommandComment *RHS) const {
34 unsigned LHSIndex = UINT_MAX;
35 unsigned RHSIndex = UINT_MAX;
37 if (LHS->isParamIndexValid()) {
38 if (LHS->isVarArgParam())
39 LHSIndex = UINT_MAX - 1;
40 else
41 LHSIndex = LHS->getParamIndex();
43 if (RHS->isParamIndexValid()) {
44 if (RHS->isVarArgParam())
45 RHSIndex = UINT_MAX - 1;
46 else
47 RHSIndex = RHS->getParamIndex();
49 return LHSIndex < RHSIndex;
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.
57 class TParamCommandCommentComparePosition {
58 public:
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;
67 if (LHS->getDepth() > 1)
68 return false;
69 if (RHS->getDepth() > 1)
70 return true;
72 // Sort template parameters in index order.
73 if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
74 return LHS->getIndex(0) < RHS->getIndex(0);
76 // Leave all other names in source order.
77 return true;
81 /// Separate parts of a FullComment.
82 struct FullCommentParts {
83 /// Take a full comment apart and initialize members accordingly.
84 FullCommentParts(const FullComment *C,
85 const CommandTraits &Traits);
87 const BlockContentComment *Brief;
88 const BlockContentComment *Headerfile;
89 const ParagraphComment *FirstParagraph;
90 SmallVector<const BlockCommandComment *, 4> Returns;
91 SmallVector<const ParamCommandComment *, 8> Params;
92 SmallVector<const TParamCommandComment *, 4> TParams;
93 llvm::TinyPtrVector<const BlockCommandComment *> Exceptions;
94 SmallVector<const BlockContentComment *, 8> MiscBlocks;
97 FullCommentParts::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 Comment::NoCommentKind:
107 continue;
109 case Comment::ParagraphCommentKind: {
110 const ParagraphComment *PC = cast<ParagraphComment>(Child);
111 if (PC->isWhitespace())
112 break;
113 if (!FirstParagraph)
114 FirstParagraph = PC;
116 MiscBlocks.push_back(PC);
117 break;
120 case Comment::BlockCommandCommentKind: {
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;
127 if (!Headerfile && Info->IsHeaderfileCommand) {
128 Headerfile = BCC;
129 break;
131 if (Info->IsReturnsCommand) {
132 Returns.push_back(BCC);
133 break;
135 if (Info->IsThrowsCommand) {
136 Exceptions.push_back(BCC);
137 break;
139 MiscBlocks.push_back(BCC);
140 break;
143 case Comment::ParamCommandCommentKind: {
144 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
145 if (!PCC->hasParamName())
146 break;
148 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
149 break;
151 Params.push_back(PCC);
152 break;
155 case Comment::TParamCommandCommentKind: {
156 const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
157 if (!TPCC->hasParamName())
158 break;
160 if (!TPCC->hasNonWhitespaceParagraph())
161 break;
163 TParams.push_back(TPCC);
164 break;
167 case Comment::VerbatimBlockCommentKind:
168 MiscBlocks.push_back(cast<BlockCommandComment>(Child));
169 break;
171 case Comment::VerbatimLineCommentKind: {
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;
179 case Comment::TextCommentKind:
180 case Comment::InlineCommandCommentKind:
181 case Comment::HTMLStartTagCommentKind:
182 case Comment::HTMLEndTagCommentKind:
183 case Comment::VerbatimBlockLineCommentKind:
184 case Comment::FullCommentKind:
185 llvm_unreachable("AST node of this kind can't be a child of "
186 "a FullComment");
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());
197 void printHTMLStartTagComment(const HTMLStartTagComment *C,
198 llvm::raw_svector_ostream &Result) {
199 Result << "<" << C->getTagName();
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 << "\"";
211 if (!C->isSelfClosing())
212 Result << ">";
213 else
214 Result << "/>";
217 class CommentASTToHTMLConverter :
218 public ConstCommentVisitor<CommentASTToHTMLConverter> {
219 public:
220 /// \param Str accumulator for HTML.
221 CommentASTToHTMLConverter(const FullComment *FC,
222 SmallVectorImpl<char> &Str,
223 const CommandTraits &Traits) :
224 FC(FC), Result(Str), Traits(Traits)
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);
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);
242 void visitFullComment(const FullComment *C);
244 // Helpers.
246 /// Convert a paragraph that is not a block by itself (an argument to some
247 /// command).
248 void visitNonStandaloneParagraphComment(const ParagraphComment *C);
250 void appendToResultWithHTMLEscaping(StringRef S);
252 private:
253 const FullComment *FC;
254 /// Output stream for HTML.
255 llvm::raw_svector_ostream Result;
257 const CommandTraits &Traits;
259 } // end unnamed namespace
261 void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
262 appendToResultWithHTMLEscaping(C->getText());
265 void CommentASTToHTMLConverter::visitInlineCommandComment(
266 const InlineCommandComment *C) {
267 // Nothing to render if no arguments supplied.
268 if (C->getNumArgs() == 0)
269 return;
271 // Nothing to render if argument is empty.
272 StringRef Arg0 = C->getArgText(0);
273 if (Arg0.empty())
274 return;
276 switch (C->getRenderKind()) {
277 case InlineCommandComment::RenderNormal:
278 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
279 appendToResultWithHTMLEscaping(C->getArgText(i));
280 Result << " ";
282 return;
284 case InlineCommandComment::RenderBold:
285 assert(C->getNumArgs() == 1);
286 Result << "<b>";
287 appendToResultWithHTMLEscaping(Arg0);
288 Result << "</b>";
289 return;
290 case InlineCommandComment::RenderMonospaced:
291 assert(C->getNumArgs() == 1);
292 Result << "<tt>";
293 appendToResultWithHTMLEscaping(Arg0);
294 Result<< "</tt>";
295 return;
296 case InlineCommandComment::RenderEmphasized:
297 assert(C->getNumArgs() == 1);
298 Result << "<em>";
299 appendToResultWithHTMLEscaping(Arg0);
300 Result << "</em>";
301 return;
302 case InlineCommandComment::RenderAnchor:
303 assert(C->getNumArgs() == 1);
304 Result << "<span id=\"" << Arg0 << "\"></span>";
305 return;
309 void CommentASTToHTMLConverter::visitHTMLStartTagComment(
310 const HTMLStartTagComment *C) {
311 printHTMLStartTagComment(C, Result);
314 void CommentASTToHTMLConverter::visitHTMLEndTagComment(
315 const HTMLEndTagComment *C) {
316 Result << "</" << C->getTagName() << ">";
319 void CommentASTToHTMLConverter::visitParagraphComment(
320 const ParagraphComment *C) {
321 if (C->isWhitespace())
322 return;
324 Result << "<p>";
325 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
326 I != E; ++I) {
327 visit(*I);
329 Result << "</p>";
332 void 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;
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;
348 // We don't know anything about this command. Just render the paragraph.
349 visit(C->getParagraph());
352 void 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));
364 } else {
365 Result << "<dt class=\"param-name-index-invalid\">";
366 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
368 Result << "</dt>";
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\">";
380 visitNonStandaloneParagraphComment(C->getParagraph());
381 Result << "</dd>";
384 void 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());
399 Result << "</dt>";
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\">";
411 visitNonStandaloneParagraphComment(C->getParagraph());
412 Result << "</dd>";
415 void CommentASTToHTMLConverter::visitVerbatimBlockComment(
416 const VerbatimBlockComment *C) {
417 unsigned NumLines = C->getNumLines();
418 if (NumLines == 0)
419 return;
421 Result << "<pre>";
422 for (unsigned i = 0; i != NumLines; ++i) {
423 appendToResultWithHTMLEscaping(C->getText(i));
424 if (i + 1 != NumLines)
425 Result << '\n';
427 Result << "</pre>";
430 void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
431 const VerbatimBlockLineComment *C) {
432 llvm_unreachable("should not see this AST node");
435 void CommentASTToHTMLConverter::visitVerbatimLineComment(
436 const VerbatimLineComment *C) {
437 Result << "<pre>";
438 appendToResultWithHTMLEscaping(C->getText());
439 Result << "</pre>";
442 void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
443 FullCommentParts Parts(C, Traits);
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;
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);
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>";
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>";
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>";
487 void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
488 const ParagraphComment *C) {
489 if (!C)
490 return;
492 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
493 I != E; ++I) {
494 visit(*I);
498 void 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;
527 namespace {
528 class CommentASTToXMLConverter :
529 public ConstCommentVisitor<CommentASTToXMLConverter> {
530 public:
531 /// \param Str accumulator for XML.
532 CommentASTToXMLConverter(const FullComment *FC,
533 SmallVectorImpl<char> &Str,
534 const CommandTraits &Traits,
535 const SourceManager &SM) :
536 FC(FC), Result(Str), Traits(Traits), SM(SM) { }
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);
544 // Block content.
545 void visitParagraphComment(const ParagraphComment *C);
547 void appendParagraphCommentWithKind(const ParagraphComment *C,
548 StringRef Kind);
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);
557 void visitFullComment(const FullComment *C);
559 // Helpers.
560 void appendToResultWithXMLEscaping(StringRef S);
561 void appendToResultWithCDATAEscaping(StringRef S);
563 void formatTextOfDeclaration(const DeclInfo *DI,
564 SmallString<128> &Declaration);
566 private:
567 const FullComment *FC;
569 /// Output stream for XML.
570 llvm::raw_svector_ostream Result;
572 const CommandTraits &Traits;
573 const SourceManager &SM;
576 void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
577 SmallVectorImpl<char> &Str) {
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);
589 void CommentASTToXMLConverter::formatTextOfDeclaration(
590 const DeclInfo *DI, SmallString<128> &Declaration) {
591 // Formatting API expects null terminated input string.
592 StringRef StringDecl(Declaration.c_str(), Declaration.size());
594 // Formatter specific code.
595 unsigned Offset = 0;
596 unsigned Length = Declaration.size();
598 format::FormatStyle Style = format::getLLVMStyle();
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;
608 } // end unnamed namespace
610 void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
611 appendToResultWithXMLEscaping(C->getText());
614 void CommentASTToXMLConverter::visitInlineCommandComment(
615 const InlineCommandComment *C) {
616 // Nothing to render if no arguments supplied.
617 if (C->getNumArgs() == 0)
618 return;
620 // Nothing to render if argument is empty.
621 StringRef Arg0 = C->getArgText(0);
622 if (Arg0.empty())
623 return;
625 switch (C->getRenderKind()) {
626 case InlineCommandComment::RenderNormal:
627 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
628 appendToResultWithXMLEscaping(C->getArgText(i));
629 Result << " ";
631 return;
632 case InlineCommandComment::RenderBold:
633 assert(C->getNumArgs() == 1);
634 Result << "<bold>";
635 appendToResultWithXMLEscaping(Arg0);
636 Result << "</bold>";
637 return;
638 case InlineCommandComment::RenderMonospaced:
639 assert(C->getNumArgs() == 1);
640 Result << "<monospaced>";
641 appendToResultWithXMLEscaping(Arg0);
642 Result << "</monospaced>";
643 return;
644 case InlineCommandComment::RenderEmphasized:
645 assert(C->getNumArgs() == 1);
646 Result << "<emphasized>";
647 appendToResultWithXMLEscaping(Arg0);
648 Result << "</emphasized>";
649 return;
650 case InlineCommandComment::RenderAnchor:
651 assert(C->getNumArgs() == 1);
652 Result << "<anchor id=\"" << Arg0 << "\"></anchor>";
653 return;
657 void CommentASTToXMLConverter::visitHTMLStartTagComment(
658 const HTMLStartTagComment *C) {
659 Result << "<rawHTML";
660 if (C->isMalformed())
661 Result << " isMalformed=\"1\"";
662 Result << ">";
664 SmallString<32> Tag;
666 llvm::raw_svector_ostream TagOS(Tag);
667 printHTMLStartTagComment(C, TagOS);
669 appendToResultWithCDATAEscaping(Tag);
671 Result << "</rawHTML>";
674 void
675 CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
676 Result << "<rawHTML";
677 if (C->isMalformed())
678 Result << " isMalformed=\"1\"";
679 Result << ">&lt;/" << C->getTagName() << "&gt;</rawHTML>";
682 void
683 CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
684 appendParagraphCommentWithKind(C, StringRef());
687 void CommentASTToXMLConverter::appendParagraphCommentWithKind(
688 const ParagraphComment *C,
689 StringRef ParagraphKind) {
690 if (C->isWhitespace())
691 return;
693 if (ParagraphKind.empty())
694 Result << "<Para>";
695 else
696 Result << "<Para kind=\"" << ParagraphKind << "\">";
698 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
699 I != E; ++I) {
700 visit(*I);
702 Result << "</Para>";
705 void CommentASTToXMLConverter::visitBlockCommandComment(
706 const BlockCommandComment *C) {
707 StringRef ParagraphKind;
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;
734 appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
737 void CommentASTToXMLConverter::visitParamCommandComment(
738 const ParamCommandComment *C) {
739 Result << "<Parameter><Name>";
740 appendToResultWithXMLEscaping(C->isParamIndexValid()
741 ? C->getParamName(FC)
742 : C->getParamNameAsWritten());
743 Result << "</Name>";
745 if (C->isParamIndexValid()) {
746 if (C->isVarArgParam())
747 Result << "<IsVarArg />";
748 else
749 Result << "<Index>" << C->getParamIndex() << "</Index>";
752 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
753 switch (C->getDirection()) {
754 case ParamCommandComment::In:
755 Result << "in";
756 break;
757 case ParamCommandComment::Out:
758 Result << "out";
759 break;
760 case ParamCommandComment::InOut:
761 Result << "in,out";
762 break;
764 Result << "</Direction><Discussion>";
765 visit(C->getParagraph());
766 Result << "</Discussion></Parameter>";
769 void CommentASTToXMLConverter::visitTParamCommandComment(
770 const TParamCommandComment *C) {
771 Result << "<Parameter><Name>";
772 appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
773 : C->getParamNameAsWritten());
774 Result << "</Name>";
776 if (C->isPositionValid() && C->getDepth() == 1) {
777 Result << "<Index>" << C->getIndex(0) << "</Index>";
780 Result << "<Discussion>";
781 visit(C->getParagraph());
782 Result << "</Discussion></Parameter>";
785 void CommentASTToXMLConverter::visitVerbatimBlockComment(
786 const VerbatimBlockComment *C) {
787 unsigned NumLines = C->getNumLines();
788 if (NumLines == 0)
789 return;
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;
799 for (unsigned i = 0; i != NumLines; ++i) {
800 appendToResultWithXMLEscaping(C->getText(i));
801 if (i + 1 != NumLines)
802 Result << '\n';
804 Result << "</Verbatim>";
807 void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
808 const VerbatimBlockLineComment *C) {
809 llvm_unreachable("should not see this AST node");
812 void CommentASTToXMLConverter::visitVerbatimLineComment(
813 const VerbatimLineComment *C) {
814 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
815 appendToResultWithXMLEscaping(C->getText());
816 Result << "</Verbatim>";
819 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
820 FullCommentParts Parts(C, Traits);
822 const DeclInfo *DI = C->getDeclInfo();
823 StringRef RootEndTag;
824 if (DI) {
825 switch (DI->getKind()) {
826 case DeclInfo::OtherKind:
827 RootEndTag = "</Other>";
828 Result << "<Other";
829 break;
830 case DeclInfo::FunctionKind:
831 RootEndTag = "</Function>";
832 Result << "<Function";
833 switch (DI->TemplateKind) {
834 case DeclInfo::NotTemplate:
835 break;
836 case DeclInfo::Template:
837 Result << " templateKind=\"template\"";
838 break;
839 case DeclInfo::TemplateSpecialization:
840 Result << " templateKind=\"specialization\"";
841 break;
842 case DeclInfo::TemplatePartialSpecialization:
843 llvm_unreachable("partial specializations of functions "
844 "are not allowed in C++");
846 if (DI->IsInstanceMethod)
847 Result << " isInstanceMethod=\"1\"";
848 if (DI->IsClassMethod)
849 Result << " isClassMethod=\"1\"";
850 break;
851 case DeclInfo::ClassKind:
852 RootEndTag = "</Class>";
853 Result << "<Class";
854 switch (DI->TemplateKind) {
855 case DeclInfo::NotTemplate:
856 break;
857 case DeclInfo::Template:
858 Result << " templateKind=\"template\"";
859 break;
860 case DeclInfo::TemplateSpecialization:
861 Result << " templateKind=\"specialization\"";
862 break;
863 case DeclInfo::TemplatePartialSpecialization:
864 Result << " templateKind=\"partialSpecialization\"";
865 break;
867 break;
868 case DeclInfo::VariableKind:
869 RootEndTag = "</Variable>";
870 Result << "<Variable";
871 break;
872 case DeclInfo::NamespaceKind:
873 RootEndTag = "</Namespace>";
874 Result << "<Namespace";
875 break;
876 case DeclInfo::TypedefKind:
877 RootEndTag = "</Typedef>";
878 Result << "<Typedef";
879 break;
880 case DeclInfo::EnumKind:
881 RootEndTag = "</Enum>";
882 Result << "<Enum";
883 break;
887 // Print line and column number.
888 SourceLocation Loc = DI->CurrentDecl->getLocation();
889 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
890 FileID FID = LocInfo.first;
891 unsigned FileOffset = LocInfo.second;
893 if (FID.isValid()) {
894 if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
895 Result << " file=\"";
896 appendToResultWithXMLEscaping(FE->getName());
897 Result << "\"";
899 Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
900 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
901 << "\"";
905 // Finish the root tag.
906 Result << ">";
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>";
918 if (!FoundName)
919 Result << "<Name>&lt;anonymous&gt;</Name>";
922 // Print USR.
923 SmallString<128> USR;
924 generateUSRForDecl(DI->CommentDecl, USR);
925 if (!USR.empty()) {
926 Result << "<USR>";
927 appendToResultWithXMLEscaping(USR);
928 Result << "</USR>";
931 } else {
932 // No DeclInfo -- just emit some root tag and name tag.
933 RootEndTag = "</Other>";
934 Result << "<Other><Name>unknown</Name>";
937 if (Parts.Headerfile) {
938 Result << "<Headerfile>";
939 visit(Parts.Headerfile);
940 Result << "</Headerfile>";
944 // Pretty-print the declaration.
945 Result << "<Declaration>";
946 SmallString<128> Declaration;
947 getSourceTextOfDeclaration(DI, Declaration);
948 formatTextOfDeclaration(DI, Declaration);
949 appendToResultWithXMLEscaping(Declaration);
950 Result << "</Declaration>";
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;
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>";
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>";
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>";
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>";
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>";
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>";
1016 continue;
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();
1028 Result << " distribution=\"" << Distribution << "\">";
1029 VersionTuple IntroducedInVersion = AA->getIntroduced();
1030 if (!IntroducedInVersion.empty()) {
1031 Result << "<IntroducedInVersion>"
1032 << IntroducedInVersion.getAsString()
1033 << "</IntroducedInVersion>";
1035 VersionTuple DeprecatedInVersion = AA->getDeprecated();
1036 if (!DeprecatedInVersion.empty()) {
1037 Result << "<DeprecatedInVersion>"
1038 << DeprecatedInVersion.getAsString()
1039 << "</DeprecatedInVersion>";
1041 VersionTuple RemovedAfterVersion = AA->getObsoleted();
1042 if (!RemovedAfterVersion.empty()) {
1043 Result << "<RemovedAfterVersion>"
1044 << RemovedAfterVersion.getAsString()
1045 << "</RemovedAfterVersion>";
1047 StringRef DeprecationSummary = AA->getMessage();
1048 if (!DeprecationSummary.empty()) {
1049 Result << "<DeprecationSummary>";
1050 appendToResultWithXMLEscaping(DeprecationSummary);
1051 Result << "</DeprecationSummary>";
1053 if (AA->getUnavailable())
1054 Result << "<Unavailable/>";
1055 Result << "</Availability>";
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;
1069 visit(C);
1071 if (StartTagEmitted)
1072 Result << "</Discussion>";
1075 Result << RootEndTag;
1078 void 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;
1104 void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) {
1105 if (S.empty())
1106 return;
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;
1116 if (Pos == StringRef::npos)
1117 Pos = S.size();
1119 Result << S.substr(0, Pos);
1121 S = S.drop_front(Pos);
1123 Result << "]]>";
1126 CommentToXMLConverter::CommentToXMLConverter() {}
1127 CommentToXMLConverter::~CommentToXMLConverter() {}
1129 void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC,
1130 SmallVectorImpl<char> &HTML,
1131 const ASTContext &Context) {
1132 CommentASTToHTMLConverter Converter(FC, HTML,
1133 Context.getCommentCommandTraits());
1134 Converter.visit(FC);
1137 void CommentToXMLConverter::convertHTMLTagNodeToText(
1138 const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text,
1139 const ASTContext &Context) {
1140 CommentASTToHTMLConverter Converter(nullptr, Text,
1141 Context.getCommentCommandTraits());
1142 Converter.visit(HTC);
1145 void CommentToXMLConverter::convertCommentToXML(const FullComment *FC,
1146 SmallVectorImpl<char> &XML,
1147 const ASTContext &Context) {
1148 CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
1149 Context.getSourceManager());
1150 Converter.visit(FC);