1 //===--- CommentToXML.cpp - Convert comments to XML representation --------===//
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
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/Format/Format.h"
15 #include "clang/Index/USRGeneration.h"
16 #include "llvm/ADT/StringExtras.h"
17 #include "llvm/ADT/TinyPtrVector.h"
18 #include "llvm/Support/raw_ostream.h"
20 using namespace clang;
21 using namespace clang::comments;
22 using namespace clang::index;
26 /// This comparison will sort parameters with valid index by index, then vararg
27 /// parameters, and invalid (unresolved) parameters last.
28 class ParamCommandCommentCompareIndex {
30 bool operator()(const ParamCommandComment *LHS,
31 const ParamCommandComment *RHS) const {
32 unsigned LHSIndex = UINT_MAX;
33 unsigned RHSIndex = UINT_MAX;
35 if (LHS->isParamIndexValid()) {
36 if (LHS->isVarArgParam())
37 LHSIndex = UINT_MAX - 1;
39 LHSIndex = LHS->getParamIndex();
41 if (RHS->isParamIndexValid()) {
42 if (RHS->isVarArgParam())
43 RHSIndex = UINT_MAX - 1;
45 RHSIndex = RHS->getParamIndex();
47 return LHSIndex < RHSIndex;
51 /// This comparison will sort template parameters in the following order:
52 /// \li real template parameters (depth = 1) in index order;
53 /// \li all other names (depth > 1);
54 /// \li unresolved names.
55 class TParamCommandCommentComparePosition {
57 bool operator()(const TParamCommandComment *LHS,
58 const TParamCommandComment *RHS) const {
59 // Sort unresolved names last.
60 if (!LHS->isPositionValid())
62 if (!RHS->isPositionValid())
65 if (LHS->getDepth() > 1)
67 if (RHS->getDepth() > 1)
70 // Sort template parameters in index order.
71 if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
72 return LHS->getIndex(0) < RHS->getIndex(0);
74 // Leave all other names in source order.
79 /// Separate parts of a FullComment.
80 struct FullCommentParts {
81 /// Take a full comment apart and initialize members accordingly.
82 FullCommentParts(const FullComment *C,
83 const CommandTraits &Traits);
85 const BlockContentComment *Brief;
86 const BlockContentComment *Headerfile;
87 const ParagraphComment *FirstParagraph;
88 SmallVector<const BlockCommandComment *, 4> Returns;
89 SmallVector<const ParamCommandComment *, 8> Params;
90 SmallVector<const TParamCommandComment *, 4> TParams;
91 llvm::TinyPtrVector<const BlockCommandComment *> Exceptions;
92 SmallVector<const BlockContentComment *, 8> MiscBlocks;
95 FullCommentParts::FullCommentParts(const FullComment *C,
96 const CommandTraits &Traits) :
97 Brief(nullptr), Headerfile(nullptr), FirstParagraph(nullptr) {
98 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
100 const Comment *Child = *I;
103 switch (Child->getCommentKind()) {
104 case Comment::NoCommentKind:
107 case Comment::ParagraphCommentKind: {
108 const ParagraphComment *PC = cast<ParagraphComment>(Child);
109 if (PC->isWhitespace())
114 MiscBlocks.push_back(PC);
118 case Comment::BlockCommandCommentKind: {
119 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
120 const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
121 if (!Brief && Info->IsBriefCommand) {
125 if (!Headerfile && Info->IsHeaderfileCommand) {
129 if (Info->IsReturnsCommand) {
130 Returns.push_back(BCC);
133 if (Info->IsThrowsCommand) {
134 Exceptions.push_back(BCC);
137 MiscBlocks.push_back(BCC);
141 case Comment::ParamCommandCommentKind: {
142 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
143 if (!PCC->hasParamName())
146 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
149 Params.push_back(PCC);
153 case Comment::TParamCommandCommentKind: {
154 const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
155 if (!TPCC->hasParamName())
158 if (!TPCC->hasNonWhitespaceParagraph())
161 TParams.push_back(TPCC);
165 case Comment::VerbatimBlockCommentKind:
166 MiscBlocks.push_back(cast<BlockCommandComment>(Child));
169 case Comment::VerbatimLineCommentKind: {
170 const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
171 const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());
172 if (!Info->IsDeclarationCommand)
173 MiscBlocks.push_back(VLC);
177 case Comment::TextCommentKind:
178 case Comment::InlineCommandCommentKind:
179 case Comment::HTMLStartTagCommentKind:
180 case Comment::HTMLEndTagCommentKind:
181 case Comment::VerbatimBlockLineCommentKind:
182 case Comment::FullCommentKind:
183 llvm_unreachable("AST node of this kind can't be a child of "
188 // Sort params in order they are declared in the function prototype.
189 // Unresolved parameters are put at the end of the list in the same order
190 // they were seen in the comment.
191 llvm::stable_sort(Params, ParamCommandCommentCompareIndex());
192 llvm::stable_sort(TParams, TParamCommandCommentComparePosition());
195 void printHTMLStartTagComment(const HTMLStartTagComment *C,
196 llvm::raw_svector_ostream &Result) {
197 Result << "<" << C->getTagName();
199 if (C->getNumAttrs() != 0) {
200 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
202 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
204 if (!Attr.Value.empty())
205 Result << "=\"" << Attr.Value << "\"";
209 if (!C->isSelfClosing())
215 class CommentASTToHTMLConverter :
216 public ConstCommentVisitor<CommentASTToHTMLConverter> {
218 /// \param Str accumulator for HTML.
219 CommentASTToHTMLConverter(const FullComment *FC,
220 SmallVectorImpl<char> &Str,
221 const CommandTraits &Traits) :
222 FC(FC), Result(Str), Traits(Traits)
226 void visitTextComment(const TextComment *C);
227 void visitInlineCommandComment(const InlineCommandComment *C);
228 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
229 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
232 void visitParagraphComment(const ParagraphComment *C);
233 void visitBlockCommandComment(const BlockCommandComment *C);
234 void visitParamCommandComment(const ParamCommandComment *C);
235 void visitTParamCommandComment(const TParamCommandComment *C);
236 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
237 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
238 void visitVerbatimLineComment(const VerbatimLineComment *C);
240 void visitFullComment(const FullComment *C);
244 /// Convert a paragraph that is not a block by itself (an argument to some
246 void visitNonStandaloneParagraphComment(const ParagraphComment *C);
248 void appendToResultWithHTMLEscaping(StringRef S);
251 const FullComment *FC;
252 /// Output stream for HTML.
253 llvm::raw_svector_ostream Result;
255 const CommandTraits &Traits;
257 } // end unnamed namespace
259 void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
260 appendToResultWithHTMLEscaping(C->getText());
263 void CommentASTToHTMLConverter::visitInlineCommandComment(
264 const InlineCommandComment *C) {
265 // Nothing to render if no arguments supplied.
266 if (C->getNumArgs() == 0)
269 // Nothing to render if argument is empty.
270 StringRef Arg0 = C->getArgText(0);
274 switch (C->getRenderKind()) {
275 case InlineCommandComment::RenderNormal:
276 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
277 appendToResultWithHTMLEscaping(C->getArgText(i));
282 case InlineCommandComment::RenderBold:
283 assert(C->getNumArgs() == 1);
285 appendToResultWithHTMLEscaping(Arg0);
288 case InlineCommandComment::RenderMonospaced:
289 assert(C->getNumArgs() == 1);
291 appendToResultWithHTMLEscaping(Arg0);
294 case InlineCommandComment::RenderEmphasized:
295 assert(C->getNumArgs() == 1);
297 appendToResultWithHTMLEscaping(Arg0);
303 void CommentASTToHTMLConverter::visitHTMLStartTagComment(
304 const HTMLStartTagComment *C) {
305 printHTMLStartTagComment(C, Result);
308 void CommentASTToHTMLConverter::visitHTMLEndTagComment(
309 const HTMLEndTagComment *C) {
310 Result << "</" << C->getTagName() << ">";
313 void CommentASTToHTMLConverter::visitParagraphComment(
314 const ParagraphComment *C) {
315 if (C->isWhitespace())
319 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
326 void CommentASTToHTMLConverter::visitBlockCommandComment(
327 const BlockCommandComment *C) {
328 const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
329 if (Info->IsBriefCommand) {
330 Result << "<p class=\"para-brief\">";
331 visitNonStandaloneParagraphComment(C->getParagraph());
335 if (Info->IsReturnsCommand) {
336 Result << "<p class=\"para-returns\">"
337 "<span class=\"word-returns\">Returns</span> ";
338 visitNonStandaloneParagraphComment(C->getParagraph());
342 // We don't know anything about this command. Just render the paragraph.
343 visit(C->getParagraph());
346 void CommentASTToHTMLConverter::visitParamCommandComment(
347 const ParamCommandComment *C) {
348 if (C->isParamIndexValid()) {
349 if (C->isVarArgParam()) {
350 Result << "<dt class=\"param-name-index-vararg\">";
351 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
353 Result << "<dt class=\"param-name-index-"
354 << C->getParamIndex()
356 appendToResultWithHTMLEscaping(C->getParamName(FC));
359 Result << "<dt class=\"param-name-index-invalid\">";
360 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
364 if (C->isParamIndexValid()) {
365 if (C->isVarArgParam())
366 Result << "<dd class=\"param-descr-index-vararg\">";
368 Result << "<dd class=\"param-descr-index-"
369 << C->getParamIndex()
372 Result << "<dd class=\"param-descr-index-invalid\">";
374 visitNonStandaloneParagraphComment(C->getParagraph());
378 void CommentASTToHTMLConverter::visitTParamCommandComment(
379 const TParamCommandComment *C) {
380 if (C->isPositionValid()) {
381 if (C->getDepth() == 1)
382 Result << "<dt class=\"tparam-name-index-"
386 Result << "<dt class=\"tparam-name-index-other\">";
387 appendToResultWithHTMLEscaping(C->getParamName(FC));
389 Result << "<dt class=\"tparam-name-index-invalid\">";
390 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
395 if (C->isPositionValid()) {
396 if (C->getDepth() == 1)
397 Result << "<dd class=\"tparam-descr-index-"
401 Result << "<dd class=\"tparam-descr-index-other\">";
403 Result << "<dd class=\"tparam-descr-index-invalid\">";
405 visitNonStandaloneParagraphComment(C->getParagraph());
409 void CommentASTToHTMLConverter::visitVerbatimBlockComment(
410 const VerbatimBlockComment *C) {
411 unsigned NumLines = C->getNumLines();
416 for (unsigned i = 0; i != NumLines; ++i) {
417 appendToResultWithHTMLEscaping(C->getText(i));
418 if (i + 1 != NumLines)
424 void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
425 const VerbatimBlockLineComment *C) {
426 llvm_unreachable("should not see this AST node");
429 void CommentASTToHTMLConverter::visitVerbatimLineComment(
430 const VerbatimLineComment *C) {
432 appendToResultWithHTMLEscaping(C->getText());
436 void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
437 FullCommentParts Parts(C, Traits);
439 bool FirstParagraphIsBrief = false;
440 if (Parts.Headerfile)
441 visit(Parts.Headerfile);
444 else if (Parts.FirstParagraph) {
445 Result << "<p class=\"para-brief\">";
446 visitNonStandaloneParagraphComment(Parts.FirstParagraph);
448 FirstParagraphIsBrief = true;
451 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
452 const Comment *C = Parts.MiscBlocks[i];
453 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
458 if (Parts.TParams.size() != 0) {
460 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
461 visit(Parts.TParams[i]);
465 if (Parts.Params.size() != 0) {
467 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
468 visit(Parts.Params[i]);
472 if (Parts.Returns.size() != 0) {
473 Result << "<div class=\"result-discussion\">";
474 for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
475 visit(Parts.Returns[i]);
481 void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
482 const ParagraphComment *C) {
486 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
492 void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
493 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
522 class CommentASTToXMLConverter :
523 public ConstCommentVisitor<CommentASTToXMLConverter> {
525 /// \param Str accumulator for XML.
526 CommentASTToXMLConverter(const FullComment *FC,
527 SmallVectorImpl<char> &Str,
528 const CommandTraits &Traits,
529 const SourceManager &SM) :
530 FC(FC), Result(Str), Traits(Traits), SM(SM) { }
533 void visitTextComment(const TextComment *C);
534 void visitInlineCommandComment(const InlineCommandComment *C);
535 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
536 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
539 void visitParagraphComment(const ParagraphComment *C);
541 void appendParagraphCommentWithKind(const ParagraphComment *C,
544 void visitBlockCommandComment(const BlockCommandComment *C);
545 void visitParamCommandComment(const ParamCommandComment *C);
546 void visitTParamCommandComment(const TParamCommandComment *C);
547 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
548 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
549 void visitVerbatimLineComment(const VerbatimLineComment *C);
551 void visitFullComment(const FullComment *C);
554 void appendToResultWithXMLEscaping(StringRef S);
555 void appendToResultWithCDATAEscaping(StringRef S);
557 void formatTextOfDeclaration(const DeclInfo *DI,
558 SmallString<128> &Declaration);
561 const FullComment *FC;
563 /// Output stream for XML.
564 llvm::raw_svector_ostream Result;
566 const CommandTraits &Traits;
567 const SourceManager &SM;
570 void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
571 SmallVectorImpl<char> &Str) {
572 ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
573 const LangOptions &LangOpts = Context.getLangOpts();
574 llvm::raw_svector_ostream OS(Str);
575 PrintingPolicy PPolicy(LangOpts);
576 PPolicy.PolishForDeclaration = true;
577 PPolicy.TerseOutput = true;
578 PPolicy.ConstantsAsWritten = true;
579 ThisDecl->CurrentDecl->print(OS, PPolicy,
580 /*Indentation*/0, /*PrintInstantiation*/false);
583 void CommentASTToXMLConverter::formatTextOfDeclaration(
584 const DeclInfo *DI, SmallString<128> &Declaration) {
585 // Formatting API expects null terminated input string.
586 StringRef StringDecl(Declaration.c_str(), Declaration.size());
588 // Formatter specific code.
590 unsigned Length = Declaration.size();
592 format::FormatStyle Style = format::getLLVMStyle();
593 Style.FixNamespaceComments = false;
594 tooling::Replacements Replaces =
595 reformat(Style, StringDecl, tooling::Range(Offset, Length), "xmldecl.xd");
596 auto FormattedStringDecl = applyAllReplacements(StringDecl, Replaces);
597 if (static_cast<bool>(FormattedStringDecl)) {
598 Declaration = *FormattedStringDecl;
602 } // end unnamed namespace
604 void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
605 appendToResultWithXMLEscaping(C->getText());
608 void CommentASTToXMLConverter::visitInlineCommandComment(
609 const InlineCommandComment *C) {
610 // Nothing to render if no arguments supplied.
611 if (C->getNumArgs() == 0)
614 // Nothing to render if argument is empty.
615 StringRef Arg0 = C->getArgText(0);
619 switch (C->getRenderKind()) {
620 case InlineCommandComment::RenderNormal:
621 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
622 appendToResultWithXMLEscaping(C->getArgText(i));
626 case InlineCommandComment::RenderBold:
627 assert(C->getNumArgs() == 1);
629 appendToResultWithXMLEscaping(Arg0);
632 case InlineCommandComment::RenderMonospaced:
633 assert(C->getNumArgs() == 1);
634 Result << "<monospaced>";
635 appendToResultWithXMLEscaping(Arg0);
636 Result << "</monospaced>";
638 case InlineCommandComment::RenderEmphasized:
639 assert(C->getNumArgs() == 1);
640 Result << "<emphasized>";
641 appendToResultWithXMLEscaping(Arg0);
642 Result << "</emphasized>";
647 void CommentASTToXMLConverter::visitHTMLStartTagComment(
648 const HTMLStartTagComment *C) {
649 Result << "<rawHTML";
650 if (C->isMalformed())
651 Result << " isMalformed=\"1\"";
656 llvm::raw_svector_ostream TagOS(Tag);
657 printHTMLStartTagComment(C, TagOS);
659 appendToResultWithCDATAEscaping(Tag);
661 Result << "</rawHTML>";
665 CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
666 Result << "<rawHTML";
667 if (C->isMalformed())
668 Result << " isMalformed=\"1\"";
669 Result << "></" << C->getTagName() << "></rawHTML>";
673 CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
674 appendParagraphCommentWithKind(C, StringRef());
677 void CommentASTToXMLConverter::appendParagraphCommentWithKind(
678 const ParagraphComment *C,
679 StringRef ParagraphKind) {
680 if (C->isWhitespace())
683 if (ParagraphKind.empty())
686 Result << "<Para kind=\"" << ParagraphKind << "\">";
688 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
695 void CommentASTToXMLConverter::visitBlockCommandComment(
696 const BlockCommandComment *C) {
697 StringRef ParagraphKind;
699 switch (C->getCommandID()) {
700 case CommandTraits::KCI_attention:
701 case CommandTraits::KCI_author:
702 case CommandTraits::KCI_authors:
703 case CommandTraits::KCI_bug:
704 case CommandTraits::KCI_copyright:
705 case CommandTraits::KCI_date:
706 case CommandTraits::KCI_invariant:
707 case CommandTraits::KCI_note:
708 case CommandTraits::KCI_post:
709 case CommandTraits::KCI_pre:
710 case CommandTraits::KCI_remark:
711 case CommandTraits::KCI_remarks:
712 case CommandTraits::KCI_sa:
713 case CommandTraits::KCI_see:
714 case CommandTraits::KCI_since:
715 case CommandTraits::KCI_todo:
716 case CommandTraits::KCI_version:
717 case CommandTraits::KCI_warning:
718 ParagraphKind = C->getCommandName(Traits);
724 appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
727 void CommentASTToXMLConverter::visitParamCommandComment(
728 const ParamCommandComment *C) {
729 Result << "<Parameter><Name>";
730 appendToResultWithXMLEscaping(C->isParamIndexValid()
731 ? C->getParamName(FC)
732 : C->getParamNameAsWritten());
735 if (C->isParamIndexValid()) {
736 if (C->isVarArgParam())
737 Result << "<IsVarArg />";
739 Result << "<Index>" << C->getParamIndex() << "</Index>";
742 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
743 switch (C->getDirection()) {
744 case ParamCommandComment::In:
747 case ParamCommandComment::Out:
750 case ParamCommandComment::InOut:
754 Result << "</Direction><Discussion>";
755 visit(C->getParagraph());
756 Result << "</Discussion></Parameter>";
759 void CommentASTToXMLConverter::visitTParamCommandComment(
760 const TParamCommandComment *C) {
761 Result << "<Parameter><Name>";
762 appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
763 : C->getParamNameAsWritten());
766 if (C->isPositionValid() && C->getDepth() == 1) {
767 Result << "<Index>" << C->getIndex(0) << "</Index>";
770 Result << "<Discussion>";
771 visit(C->getParagraph());
772 Result << "</Discussion></Parameter>";
775 void CommentASTToXMLConverter::visitVerbatimBlockComment(
776 const VerbatimBlockComment *C) {
777 unsigned NumLines = C->getNumLines();
781 switch (C->getCommandID()) {
782 case CommandTraits::KCI_code:
783 Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
786 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
789 for (unsigned i = 0; i != NumLines; ++i) {
790 appendToResultWithXMLEscaping(C->getText(i));
791 if (i + 1 != NumLines)
794 Result << "</Verbatim>";
797 void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
798 const VerbatimBlockLineComment *C) {
799 llvm_unreachable("should not see this AST node");
802 void CommentASTToXMLConverter::visitVerbatimLineComment(
803 const VerbatimLineComment *C) {
804 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
805 appendToResultWithXMLEscaping(C->getText());
806 Result << "</Verbatim>";
809 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
810 FullCommentParts Parts(C, Traits);
812 const DeclInfo *DI = C->getDeclInfo();
813 StringRef RootEndTag;
815 switch (DI->getKind()) {
816 case DeclInfo::OtherKind:
817 RootEndTag = "</Other>";
820 case DeclInfo::FunctionKind:
821 RootEndTag = "</Function>";
822 Result << "<Function";
823 switch (DI->TemplateKind) {
824 case DeclInfo::NotTemplate:
826 case DeclInfo::Template:
827 Result << " templateKind=\"template\"";
829 case DeclInfo::TemplateSpecialization:
830 Result << " templateKind=\"specialization\"";
832 case DeclInfo::TemplatePartialSpecialization:
833 llvm_unreachable("partial specializations of functions "
834 "are not allowed in C++");
836 if (DI->IsInstanceMethod)
837 Result << " isInstanceMethod=\"1\"";
838 if (DI->IsClassMethod)
839 Result << " isClassMethod=\"1\"";
841 case DeclInfo::ClassKind:
842 RootEndTag = "</Class>";
844 switch (DI->TemplateKind) {
845 case DeclInfo::NotTemplate:
847 case DeclInfo::Template:
848 Result << " templateKind=\"template\"";
850 case DeclInfo::TemplateSpecialization:
851 Result << " templateKind=\"specialization\"";
853 case DeclInfo::TemplatePartialSpecialization:
854 Result << " templateKind=\"partialSpecialization\"";
858 case DeclInfo::VariableKind:
859 RootEndTag = "</Variable>";
860 Result << "<Variable";
862 case DeclInfo::NamespaceKind:
863 RootEndTag = "</Namespace>";
864 Result << "<Namespace";
866 case DeclInfo::TypedefKind:
867 RootEndTag = "</Typedef>";
868 Result << "<Typedef";
870 case DeclInfo::EnumKind:
871 RootEndTag = "</Enum>";
877 // Print line and column number.
878 SourceLocation Loc = DI->CurrentDecl->getLocation();
879 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
880 FileID FID = LocInfo.first;
881 unsigned FileOffset = LocInfo.second;
884 if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
885 Result << " file=\"";
886 appendToResultWithXMLEscaping(FE->getName());
889 Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
890 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
895 // Finish the root tag.
898 bool FoundName = false;
899 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
900 if (DeclarationName DeclName = ND->getDeclName()) {
902 std::string Name = DeclName.getAsString();
903 appendToResultWithXMLEscaping(Name);
909 Result << "<Name><anonymous></Name>";
913 SmallString<128> USR;
914 generateUSRForDecl(DI->CommentDecl, USR);
917 appendToResultWithXMLEscaping(USR);
922 // No DeclInfo -- just emit some root tag and name tag.
923 RootEndTag = "</Other>";
924 Result << "<Other><Name>unknown</Name>";
927 if (Parts.Headerfile) {
928 Result << "<Headerfile>";
929 visit(Parts.Headerfile);
930 Result << "</Headerfile>";
934 // Pretty-print the declaration.
935 Result << "<Declaration>";
936 SmallString<128> Declaration;
937 getSourceTextOfDeclaration(DI, Declaration);
938 formatTextOfDeclaration(DI, Declaration);
939 appendToResultWithXMLEscaping(Declaration);
940 Result << "</Declaration>";
943 bool FirstParagraphIsBrief = false;
945 Result << "<Abstract>";
947 Result << "</Abstract>";
948 } else if (Parts.FirstParagraph) {
949 Result << "<Abstract>";
950 visit(Parts.FirstParagraph);
951 Result << "</Abstract>";
952 FirstParagraphIsBrief = true;
955 if (Parts.TParams.size() != 0) {
956 Result << "<TemplateParameters>";
957 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
958 visit(Parts.TParams[i]);
959 Result << "</TemplateParameters>";
962 if (Parts.Params.size() != 0) {
963 Result << "<Parameters>";
964 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
965 visit(Parts.Params[i]);
966 Result << "</Parameters>";
969 if (Parts.Exceptions.size() != 0) {
970 Result << "<Exceptions>";
971 for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
972 visit(Parts.Exceptions[i]);
973 Result << "</Exceptions>";
976 if (Parts.Returns.size() != 0) {
977 Result << "<ResultDiscussion>";
978 for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
979 visit(Parts.Returns[i]);
980 Result << "</ResultDiscussion>";
983 if (DI->CommentDecl->hasAttrs()) {
984 const AttrVec &Attrs = DI->CommentDecl->getAttrs();
985 for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
986 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
988 if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
989 if (DA->getMessage().empty())
990 Result << "<Deprecated/>";
992 Result << "<Deprecated>";
993 appendToResultWithXMLEscaping(DA->getMessage());
994 Result << "</Deprecated>";
997 else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
998 if (UA->getMessage().empty())
999 Result << "<Unavailable/>";
1001 Result << "<Unavailable>";
1002 appendToResultWithXMLEscaping(UA->getMessage());
1003 Result << "</Unavailable>";
1009 // 'availability' attribute.
1010 Result << "<Availability";
1011 StringRef Distribution;
1012 if (AA->getPlatform()) {
1013 Distribution = AvailabilityAttr::getPrettyPlatformName(
1014 AA->getPlatform()->getName());
1015 if (Distribution.empty())
1016 Distribution = AA->getPlatform()->getName();
1018 Result << " distribution=\"" << Distribution << "\">";
1019 VersionTuple IntroducedInVersion = AA->getIntroduced();
1020 if (!IntroducedInVersion.empty()) {
1021 Result << "<IntroducedInVersion>"
1022 << IntroducedInVersion.getAsString()
1023 << "</IntroducedInVersion>";
1025 VersionTuple DeprecatedInVersion = AA->getDeprecated();
1026 if (!DeprecatedInVersion.empty()) {
1027 Result << "<DeprecatedInVersion>"
1028 << DeprecatedInVersion.getAsString()
1029 << "</DeprecatedInVersion>";
1031 VersionTuple RemovedAfterVersion = AA->getObsoleted();
1032 if (!RemovedAfterVersion.empty()) {
1033 Result << "<RemovedAfterVersion>"
1034 << RemovedAfterVersion.getAsString()
1035 << "</RemovedAfterVersion>";
1037 StringRef DeprecationSummary = AA->getMessage();
1038 if (!DeprecationSummary.empty()) {
1039 Result << "<DeprecationSummary>";
1040 appendToResultWithXMLEscaping(DeprecationSummary);
1041 Result << "</DeprecationSummary>";
1043 if (AA->getUnavailable())
1044 Result << "<Unavailable/>";
1045 Result << "</Availability>";
1050 bool StartTagEmitted = false;
1051 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1052 const Comment *C = Parts.MiscBlocks[i];
1053 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1055 if (!StartTagEmitted) {
1056 Result << "<Discussion>";
1057 StartTagEmitted = true;
1061 if (StartTagEmitted)
1062 Result << "</Discussion>";
1065 Result << RootEndTag;
1068 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1069 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1094 void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) {
1098 Result << "<![CDATA[";
1099 while (!S.empty()) {
1100 size_t Pos = S.find("]]>");
1102 Result << "]]]]><![CDATA[>";
1103 S = S.drop_front(3);
1106 if (Pos == StringRef::npos)
1109 Result << S.substr(0, Pos);
1111 S = S.drop_front(Pos);
1116 CommentToXMLConverter::CommentToXMLConverter() {}
1117 CommentToXMLConverter::~CommentToXMLConverter() {}
1119 void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC,
1120 SmallVectorImpl<char> &HTML,
1121 const ASTContext &Context) {
1122 CommentASTToHTMLConverter Converter(FC, HTML,
1123 Context.getCommentCommandTraits());
1124 Converter.visit(FC);
1127 void CommentToXMLConverter::convertHTMLTagNodeToText(
1128 const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text,
1129 const ASTContext &Context) {
1130 CommentASTToHTMLConverter Converter(nullptr, Text,
1131 Context.getCommentCommandTraits());
1132 Converter.visit(HTC);
1135 void CommentToXMLConverter::convertCommentToXML(const FullComment *FC,
1136 SmallVectorImpl<char> &XML,
1137 const ASTContext &Context) {
1138 CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
1139 Context.getSourceManager());
1140 Converter.visit(FC);