1 //===--- CommentToXML.cpp - Convert comments to XML representation --------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #include "clang/Index/CommentToXML.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/AST/Attr.h"
13 #include "clang/AST/Comment.h"
14 #include "clang/AST/CommentVisitor.h"
15 #include "clang/Format/Format.h"
16 #include "clang/Index/USRGeneration.h"
17 #include "llvm/ADT/StringExtras.h"
18 #include "llvm/ADT/TinyPtrVector.h"
19 #include "llvm/Support/raw_ostream.h"
21 using namespace clang;
22 using namespace clang::comments;
23 using namespace clang::index;
27 /// This comparison will sort parameters with valid index by index, then vararg
28 /// parameters, and invalid (unresolved) parameters last.
29 class ParamCommandCommentCompareIndex {
31 bool operator()(const ParamCommandComment *LHS,
32 const ParamCommandComment *RHS) const {
33 unsigned LHSIndex = UINT_MAX;
34 unsigned RHSIndex = UINT_MAX;
36 if (LHS->isParamIndexValid()) {
37 if (LHS->isVarArgParam())
38 LHSIndex = UINT_MAX - 1;
40 LHSIndex = LHS->getParamIndex();
42 if (RHS->isParamIndexValid()) {
43 if (RHS->isVarArgParam())
44 RHSIndex = UINT_MAX - 1;
46 RHSIndex = RHS->getParamIndex();
48 return LHSIndex < RHSIndex;
52 /// This comparison will sort template parameters in the following order:
53 /// \li real template parameters (depth = 1) in index order;
54 /// \li all other names (depth > 1);
55 /// \li unresolved names.
56 class TParamCommandCommentComparePosition {
58 bool operator()(const TParamCommandComment *LHS,
59 const TParamCommandComment *RHS) const {
60 // Sort unresolved names last.
61 if (!LHS->isPositionValid())
63 if (!RHS->isPositionValid())
66 if (LHS->getDepth() > 1)
68 if (RHS->getDepth() > 1)
71 // Sort template parameters in index order.
72 if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
73 return LHS->getIndex(0) < RHS->getIndex(0);
75 // Leave all other names in source order.
80 /// Separate parts of a FullComment.
81 struct FullCommentParts {
82 /// Take a full comment apart and initialize members accordingly.
83 FullCommentParts(const FullComment *C,
84 const CommandTraits &Traits);
86 const BlockContentComment *Brief;
87 const BlockContentComment *Headerfile;
88 const ParagraphComment *FirstParagraph;
89 SmallVector<const BlockCommandComment *, 4> Returns;
90 SmallVector<const ParamCommandComment *, 8> Params;
91 SmallVector<const TParamCommandComment *, 4> TParams;
92 llvm::TinyPtrVector<const BlockCommandComment *> Exceptions;
93 SmallVector<const BlockContentComment *, 8> MiscBlocks;
96 FullCommentParts::FullCommentParts(const FullComment *C,
97 const CommandTraits &Traits) :
98 Brief(nullptr), Headerfile(nullptr), FirstParagraph(nullptr) {
99 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
101 const Comment *Child = *I;
104 switch (Child->getCommentKind()) {
105 case Comment::NoCommentKind:
108 case Comment::ParagraphCommentKind: {
109 const ParagraphComment *PC = cast<ParagraphComment>(Child);
110 if (PC->isWhitespace())
115 MiscBlocks.push_back(PC);
119 case Comment::BlockCommandCommentKind: {
120 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
121 const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
122 if (!Brief && Info->IsBriefCommand) {
126 if (!Headerfile && Info->IsHeaderfileCommand) {
130 if (Info->IsReturnsCommand) {
131 Returns.push_back(BCC);
134 if (Info->IsThrowsCommand) {
135 Exceptions.push_back(BCC);
138 MiscBlocks.push_back(BCC);
142 case Comment::ParamCommandCommentKind: {
143 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
144 if (!PCC->hasParamName())
147 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
150 Params.push_back(PCC);
154 case Comment::TParamCommandCommentKind: {
155 const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
156 if (!TPCC->hasParamName())
159 if (!TPCC->hasNonWhitespaceParagraph())
162 TParams.push_back(TPCC);
166 case Comment::VerbatimBlockCommentKind:
167 MiscBlocks.push_back(cast<BlockCommandComment>(Child));
170 case Comment::VerbatimLineCommentKind: {
171 const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
172 const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());
173 if (!Info->IsDeclarationCommand)
174 MiscBlocks.push_back(VLC);
178 case Comment::TextCommentKind:
179 case Comment::InlineCommandCommentKind:
180 case Comment::HTMLStartTagCommentKind:
181 case Comment::HTMLEndTagCommentKind:
182 case Comment::VerbatimBlockLineCommentKind:
183 case Comment::FullCommentKind:
184 llvm_unreachable("AST node of this kind can't be a child of "
189 // Sort params in order they are declared in the function prototype.
190 // Unresolved parameters are put at the end of the list in the same order
191 // they were seen in the comment.
192 std::stable_sort(Params.begin(), Params.end(),
193 ParamCommandCommentCompareIndex());
195 std::stable_sort(TParams.begin(), TParams.end(),
196 TParamCommandCommentComparePosition());
199 void printHTMLStartTagComment(const HTMLStartTagComment *C,
200 llvm::raw_svector_ostream &Result) {
201 Result << "<" << C->getTagName();
203 if (C->getNumAttrs() != 0) {
204 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
206 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
208 if (!Attr.Value.empty())
209 Result << "=\"" << Attr.Value << "\"";
213 if (!C->isSelfClosing())
219 class CommentASTToHTMLConverter :
220 public ConstCommentVisitor<CommentASTToHTMLConverter> {
222 /// \param Str accumulator for HTML.
223 CommentASTToHTMLConverter(const FullComment *FC,
224 SmallVectorImpl<char> &Str,
225 const CommandTraits &Traits) :
226 FC(FC), Result(Str), Traits(Traits)
230 void visitTextComment(const TextComment *C);
231 void visitInlineCommandComment(const InlineCommandComment *C);
232 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
233 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
236 void visitParagraphComment(const ParagraphComment *C);
237 void visitBlockCommandComment(const BlockCommandComment *C);
238 void visitParamCommandComment(const ParamCommandComment *C);
239 void visitTParamCommandComment(const TParamCommandComment *C);
240 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
241 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
242 void visitVerbatimLineComment(const VerbatimLineComment *C);
244 void visitFullComment(const FullComment *C);
248 /// Convert a paragraph that is not a block by itself (an argument to some
250 void visitNonStandaloneParagraphComment(const ParagraphComment *C);
252 void appendToResultWithHTMLEscaping(StringRef S);
255 const FullComment *FC;
256 /// Output stream for HTML.
257 llvm::raw_svector_ostream Result;
259 const CommandTraits &Traits;
261 } // end unnamed namespace
263 void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
264 appendToResultWithHTMLEscaping(C->getText());
267 void CommentASTToHTMLConverter::visitInlineCommandComment(
268 const InlineCommandComment *C) {
269 // Nothing to render if no arguments supplied.
270 if (C->getNumArgs() == 0)
273 // Nothing to render if argument is empty.
274 StringRef Arg0 = C->getArgText(0);
278 switch (C->getRenderKind()) {
279 case InlineCommandComment::RenderNormal:
280 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
281 appendToResultWithHTMLEscaping(C->getArgText(i));
286 case InlineCommandComment::RenderBold:
287 assert(C->getNumArgs() == 1);
289 appendToResultWithHTMLEscaping(Arg0);
292 case InlineCommandComment::RenderMonospaced:
293 assert(C->getNumArgs() == 1);
295 appendToResultWithHTMLEscaping(Arg0);
298 case InlineCommandComment::RenderEmphasized:
299 assert(C->getNumArgs() == 1);
301 appendToResultWithHTMLEscaping(Arg0);
307 void CommentASTToHTMLConverter::visitHTMLStartTagComment(
308 const HTMLStartTagComment *C) {
309 printHTMLStartTagComment(C, Result);
312 void CommentASTToHTMLConverter::visitHTMLEndTagComment(
313 const HTMLEndTagComment *C) {
314 Result << "</" << C->getTagName() << ">";
317 void CommentASTToHTMLConverter::visitParagraphComment(
318 const ParagraphComment *C) {
319 if (C->isWhitespace())
323 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
330 void CommentASTToHTMLConverter::visitBlockCommandComment(
331 const BlockCommandComment *C) {
332 const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
333 if (Info->IsBriefCommand) {
334 Result << "<p class=\"para-brief\">";
335 visitNonStandaloneParagraphComment(C->getParagraph());
339 if (Info->IsReturnsCommand) {
340 Result << "<p class=\"para-returns\">"
341 "<span class=\"word-returns\">Returns</span> ";
342 visitNonStandaloneParagraphComment(C->getParagraph());
346 // We don't know anything about this command. Just render the paragraph.
347 visit(C->getParagraph());
350 void CommentASTToHTMLConverter::visitParamCommandComment(
351 const ParamCommandComment *C) {
352 if (C->isParamIndexValid()) {
353 if (C->isVarArgParam()) {
354 Result << "<dt class=\"param-name-index-vararg\">";
355 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
357 Result << "<dt class=\"param-name-index-"
358 << C->getParamIndex()
360 appendToResultWithHTMLEscaping(C->getParamName(FC));
363 Result << "<dt class=\"param-name-index-invalid\">";
364 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
368 if (C->isParamIndexValid()) {
369 if (C->isVarArgParam())
370 Result << "<dd class=\"param-descr-index-vararg\">";
372 Result << "<dd class=\"param-descr-index-"
373 << C->getParamIndex()
376 Result << "<dd class=\"param-descr-index-invalid\">";
378 visitNonStandaloneParagraphComment(C->getParagraph());
382 void CommentASTToHTMLConverter::visitTParamCommandComment(
383 const TParamCommandComment *C) {
384 if (C->isPositionValid()) {
385 if (C->getDepth() == 1)
386 Result << "<dt class=\"tparam-name-index-"
390 Result << "<dt class=\"tparam-name-index-other\">";
391 appendToResultWithHTMLEscaping(C->getParamName(FC));
393 Result << "<dt class=\"tparam-name-index-invalid\">";
394 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
399 if (C->isPositionValid()) {
400 if (C->getDepth() == 1)
401 Result << "<dd class=\"tparam-descr-index-"
405 Result << "<dd class=\"tparam-descr-index-other\">";
407 Result << "<dd class=\"tparam-descr-index-invalid\">";
409 visitNonStandaloneParagraphComment(C->getParagraph());
413 void CommentASTToHTMLConverter::visitVerbatimBlockComment(
414 const VerbatimBlockComment *C) {
415 unsigned NumLines = C->getNumLines();
420 for (unsigned i = 0; i != NumLines; ++i) {
421 appendToResultWithHTMLEscaping(C->getText(i));
422 if (i + 1 != NumLines)
428 void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
429 const VerbatimBlockLineComment *C) {
430 llvm_unreachable("should not see this AST node");
433 void CommentASTToHTMLConverter::visitVerbatimLineComment(
434 const VerbatimLineComment *C) {
436 appendToResultWithHTMLEscaping(C->getText());
440 void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
441 FullCommentParts Parts(C, Traits);
443 bool FirstParagraphIsBrief = false;
444 if (Parts.Headerfile)
445 visit(Parts.Headerfile);
448 else if (Parts.FirstParagraph) {
449 Result << "<p class=\"para-brief\">";
450 visitNonStandaloneParagraphComment(Parts.FirstParagraph);
452 FirstParagraphIsBrief = true;
455 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
456 const Comment *C = Parts.MiscBlocks[i];
457 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
462 if (Parts.TParams.size() != 0) {
464 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
465 visit(Parts.TParams[i]);
469 if (Parts.Params.size() != 0) {
471 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
472 visit(Parts.Params[i]);
476 if (Parts.Returns.size() != 0) {
477 Result << "<div class=\"result-discussion\">";
478 for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
479 visit(Parts.Returns[i]);
485 void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
486 const ParagraphComment *C) {
490 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
496 void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
497 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
526 class CommentASTToXMLConverter :
527 public ConstCommentVisitor<CommentASTToXMLConverter> {
529 /// \param Str accumulator for XML.
530 CommentASTToXMLConverter(const FullComment *FC,
531 SmallVectorImpl<char> &Str,
532 const CommandTraits &Traits,
533 const SourceManager &SM) :
534 FC(FC), Result(Str), Traits(Traits), SM(SM) { }
537 void visitTextComment(const TextComment *C);
538 void visitInlineCommandComment(const InlineCommandComment *C);
539 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
540 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
543 void visitParagraphComment(const ParagraphComment *C);
545 void appendParagraphCommentWithKind(const ParagraphComment *C,
548 void visitBlockCommandComment(const BlockCommandComment *C);
549 void visitParamCommandComment(const ParamCommandComment *C);
550 void visitTParamCommandComment(const TParamCommandComment *C);
551 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
552 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
553 void visitVerbatimLineComment(const VerbatimLineComment *C);
555 void visitFullComment(const FullComment *C);
558 void appendToResultWithXMLEscaping(StringRef S);
559 void appendToResultWithCDATAEscaping(StringRef S);
561 void formatTextOfDeclaration(const DeclInfo *DI,
562 SmallString<128> &Declaration);
565 const FullComment *FC;
567 /// Output stream for XML.
568 llvm::raw_svector_ostream Result;
570 const CommandTraits &Traits;
571 const SourceManager &SM;
574 void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
575 SmallVectorImpl<char> &Str) {
576 ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
577 const LangOptions &LangOpts = Context.getLangOpts();
578 llvm::raw_svector_ostream OS(Str);
579 PrintingPolicy PPolicy(LangOpts);
580 PPolicy.PolishForDeclaration = true;
581 PPolicy.TerseOutput = true;
582 PPolicy.ConstantsAsWritten = true;
583 ThisDecl->CurrentDecl->print(OS, PPolicy,
584 /*Indentation*/0, /*PrintInstantiation*/false);
587 void CommentASTToXMLConverter::formatTextOfDeclaration(
588 const DeclInfo *DI, SmallString<128> &Declaration) {
589 // Formatting API expects null terminated input string.
590 StringRef StringDecl(Declaration.c_str(), Declaration.size());
592 // Formatter specific code.
594 unsigned Length = Declaration.size();
596 format::FormatStyle Style = format::getLLVMStyle();
597 Style.FixNamespaceComments = false;
598 tooling::Replacements Replaces =
599 reformat(Style, StringDecl, tooling::Range(Offset, Length), "xmldecl.xd");
600 auto FormattedStringDecl = applyAllReplacements(StringDecl, Replaces);
601 if (static_cast<bool>(FormattedStringDecl)) {
602 Declaration = *FormattedStringDecl;
606 } // end unnamed namespace
608 void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
609 appendToResultWithXMLEscaping(C->getText());
612 void CommentASTToXMLConverter::visitInlineCommandComment(
613 const InlineCommandComment *C) {
614 // Nothing to render if no arguments supplied.
615 if (C->getNumArgs() == 0)
618 // Nothing to render if argument is empty.
619 StringRef Arg0 = C->getArgText(0);
623 switch (C->getRenderKind()) {
624 case InlineCommandComment::RenderNormal:
625 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
626 appendToResultWithXMLEscaping(C->getArgText(i));
630 case InlineCommandComment::RenderBold:
631 assert(C->getNumArgs() == 1);
633 appendToResultWithXMLEscaping(Arg0);
636 case InlineCommandComment::RenderMonospaced:
637 assert(C->getNumArgs() == 1);
638 Result << "<monospaced>";
639 appendToResultWithXMLEscaping(Arg0);
640 Result << "</monospaced>";
642 case InlineCommandComment::RenderEmphasized:
643 assert(C->getNumArgs() == 1);
644 Result << "<emphasized>";
645 appendToResultWithXMLEscaping(Arg0);
646 Result << "</emphasized>";
651 void CommentASTToXMLConverter::visitHTMLStartTagComment(
652 const HTMLStartTagComment *C) {
653 Result << "<rawHTML";
654 if (C->isMalformed())
655 Result << " isMalformed=\"1\"";
660 llvm::raw_svector_ostream TagOS(Tag);
661 printHTMLStartTagComment(C, TagOS);
663 appendToResultWithCDATAEscaping(Tag);
665 Result << "</rawHTML>";
669 CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
670 Result << "<rawHTML";
671 if (C->isMalformed())
672 Result << " isMalformed=\"1\"";
673 Result << "></" << C->getTagName() << "></rawHTML>";
677 CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
678 appendParagraphCommentWithKind(C, StringRef());
681 void CommentASTToXMLConverter::appendParagraphCommentWithKind(
682 const ParagraphComment *C,
683 StringRef ParagraphKind) {
684 if (C->isWhitespace())
687 if (ParagraphKind.empty())
690 Result << "<Para kind=\"" << ParagraphKind << "\">";
692 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
699 void CommentASTToXMLConverter::visitBlockCommandComment(
700 const BlockCommandComment *C) {
701 StringRef ParagraphKind;
703 switch (C->getCommandID()) {
704 case CommandTraits::KCI_attention:
705 case CommandTraits::KCI_author:
706 case CommandTraits::KCI_authors:
707 case CommandTraits::KCI_bug:
708 case CommandTraits::KCI_copyright:
709 case CommandTraits::KCI_date:
710 case CommandTraits::KCI_invariant:
711 case CommandTraits::KCI_note:
712 case CommandTraits::KCI_post:
713 case CommandTraits::KCI_pre:
714 case CommandTraits::KCI_remark:
715 case CommandTraits::KCI_remarks:
716 case CommandTraits::KCI_sa:
717 case CommandTraits::KCI_see:
718 case CommandTraits::KCI_since:
719 case CommandTraits::KCI_todo:
720 case CommandTraits::KCI_version:
721 case CommandTraits::KCI_warning:
722 ParagraphKind = C->getCommandName(Traits);
727 appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
730 void CommentASTToXMLConverter::visitParamCommandComment(
731 const ParamCommandComment *C) {
732 Result << "<Parameter><Name>";
733 appendToResultWithXMLEscaping(C->isParamIndexValid()
734 ? C->getParamName(FC)
735 : C->getParamNameAsWritten());
738 if (C->isParamIndexValid()) {
739 if (C->isVarArgParam())
740 Result << "<IsVarArg />";
742 Result << "<Index>" << C->getParamIndex() << "</Index>";
745 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
746 switch (C->getDirection()) {
747 case ParamCommandComment::In:
750 case ParamCommandComment::Out:
753 case ParamCommandComment::InOut:
757 Result << "</Direction><Discussion>";
758 visit(C->getParagraph());
759 Result << "</Discussion></Parameter>";
762 void CommentASTToXMLConverter::visitTParamCommandComment(
763 const TParamCommandComment *C) {
764 Result << "<Parameter><Name>";
765 appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
766 : C->getParamNameAsWritten());
769 if (C->isPositionValid() && C->getDepth() == 1) {
770 Result << "<Index>" << C->getIndex(0) << "</Index>";
773 Result << "<Discussion>";
774 visit(C->getParagraph());
775 Result << "</Discussion></Parameter>";
778 void CommentASTToXMLConverter::visitVerbatimBlockComment(
779 const VerbatimBlockComment *C) {
780 unsigned NumLines = C->getNumLines();
784 switch (C->getCommandID()) {
785 case CommandTraits::KCI_code:
786 Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
789 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
792 for (unsigned i = 0; i != NumLines; ++i) {
793 appendToResultWithXMLEscaping(C->getText(i));
794 if (i + 1 != NumLines)
797 Result << "</Verbatim>";
800 void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
801 const VerbatimBlockLineComment *C) {
802 llvm_unreachable("should not see this AST node");
805 void CommentASTToXMLConverter::visitVerbatimLineComment(
806 const VerbatimLineComment *C) {
807 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
808 appendToResultWithXMLEscaping(C->getText());
809 Result << "</Verbatim>";
812 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
813 FullCommentParts Parts(C, Traits);
815 const DeclInfo *DI = C->getDeclInfo();
816 StringRef RootEndTag;
818 switch (DI->getKind()) {
819 case DeclInfo::OtherKind:
820 RootEndTag = "</Other>";
823 case DeclInfo::FunctionKind:
824 RootEndTag = "</Function>";
825 Result << "<Function";
826 switch (DI->TemplateKind) {
827 case DeclInfo::NotTemplate:
829 case DeclInfo::Template:
830 Result << " templateKind=\"template\"";
832 case DeclInfo::TemplateSpecialization:
833 Result << " templateKind=\"specialization\"";
835 case DeclInfo::TemplatePartialSpecialization:
836 llvm_unreachable("partial specializations of functions "
837 "are not allowed in C++");
839 if (DI->IsInstanceMethod)
840 Result << " isInstanceMethod=\"1\"";
841 if (DI->IsClassMethod)
842 Result << " isClassMethod=\"1\"";
844 case DeclInfo::ClassKind:
845 RootEndTag = "</Class>";
847 switch (DI->TemplateKind) {
848 case DeclInfo::NotTemplate:
850 case DeclInfo::Template:
851 Result << " templateKind=\"template\"";
853 case DeclInfo::TemplateSpecialization:
854 Result << " templateKind=\"specialization\"";
856 case DeclInfo::TemplatePartialSpecialization:
857 Result << " templateKind=\"partialSpecialization\"";
861 case DeclInfo::VariableKind:
862 RootEndTag = "</Variable>";
863 Result << "<Variable";
865 case DeclInfo::NamespaceKind:
866 RootEndTag = "</Namespace>";
867 Result << "<Namespace";
869 case DeclInfo::TypedefKind:
870 RootEndTag = "</Typedef>";
871 Result << "<Typedef";
873 case DeclInfo::EnumKind:
874 RootEndTag = "</Enum>";
880 // Print line and column number.
881 SourceLocation Loc = DI->CurrentDecl->getLocation();
882 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
883 FileID FID = LocInfo.first;
884 unsigned FileOffset = LocInfo.second;
887 if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
888 Result << " file=\"";
889 appendToResultWithXMLEscaping(FE->getName());
892 Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
893 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
898 // Finish the root tag.
901 bool FoundName = false;
902 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
903 if (DeclarationName DeclName = ND->getDeclName()) {
905 std::string Name = DeclName.getAsString();
906 appendToResultWithXMLEscaping(Name);
912 Result << "<Name><anonymous></Name>";
916 SmallString<128> USR;
917 generateUSRForDecl(DI->CommentDecl, USR);
920 appendToResultWithXMLEscaping(USR);
925 // No DeclInfo -- just emit some root tag and name tag.
926 RootEndTag = "</Other>";
927 Result << "<Other><Name>unknown</Name>";
930 if (Parts.Headerfile) {
931 Result << "<Headerfile>";
932 visit(Parts.Headerfile);
933 Result << "</Headerfile>";
937 // Pretty-print the declaration.
938 Result << "<Declaration>";
939 SmallString<128> Declaration;
940 getSourceTextOfDeclaration(DI, Declaration);
941 formatTextOfDeclaration(DI, Declaration);
942 appendToResultWithXMLEscaping(Declaration);
943 Result << "</Declaration>";
946 bool FirstParagraphIsBrief = false;
948 Result << "<Abstract>";
950 Result << "</Abstract>";
951 } else if (Parts.FirstParagraph) {
952 Result << "<Abstract>";
953 visit(Parts.FirstParagraph);
954 Result << "</Abstract>";
955 FirstParagraphIsBrief = true;
958 if (Parts.TParams.size() != 0) {
959 Result << "<TemplateParameters>";
960 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
961 visit(Parts.TParams[i]);
962 Result << "</TemplateParameters>";
965 if (Parts.Params.size() != 0) {
966 Result << "<Parameters>";
967 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
968 visit(Parts.Params[i]);
969 Result << "</Parameters>";
972 if (Parts.Exceptions.size() != 0) {
973 Result << "<Exceptions>";
974 for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
975 visit(Parts.Exceptions[i]);
976 Result << "</Exceptions>";
979 if (Parts.Returns.size() != 0) {
980 Result << "<ResultDiscussion>";
981 for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
982 visit(Parts.Returns[i]);
983 Result << "</ResultDiscussion>";
986 if (DI->CommentDecl->hasAttrs()) {
987 const AttrVec &Attrs = DI->CommentDecl->getAttrs();
988 for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
989 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
991 if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
992 if (DA->getMessage().empty())
993 Result << "<Deprecated/>";
995 Result << "<Deprecated>";
996 appendToResultWithXMLEscaping(DA->getMessage());
997 Result << "</Deprecated>";
1000 else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1001 if (UA->getMessage().empty())
1002 Result << "<Unavailable/>";
1004 Result << "<Unavailable>";
1005 appendToResultWithXMLEscaping(UA->getMessage());
1006 Result << "</Unavailable>";
1012 // 'availability' attribute.
1013 Result << "<Availability";
1014 StringRef Distribution;
1015 if (AA->getPlatform()) {
1016 Distribution = AvailabilityAttr::getPrettyPlatformName(
1017 AA->getPlatform()->getName());
1018 if (Distribution.empty())
1019 Distribution = AA->getPlatform()->getName();
1021 Result << " distribution=\"" << Distribution << "\">";
1022 VersionTuple IntroducedInVersion = AA->getIntroduced();
1023 if (!IntroducedInVersion.empty()) {
1024 Result << "<IntroducedInVersion>"
1025 << IntroducedInVersion.getAsString()
1026 << "</IntroducedInVersion>";
1028 VersionTuple DeprecatedInVersion = AA->getDeprecated();
1029 if (!DeprecatedInVersion.empty()) {
1030 Result << "<DeprecatedInVersion>"
1031 << DeprecatedInVersion.getAsString()
1032 << "</DeprecatedInVersion>";
1034 VersionTuple RemovedAfterVersion = AA->getObsoleted();
1035 if (!RemovedAfterVersion.empty()) {
1036 Result << "<RemovedAfterVersion>"
1037 << RemovedAfterVersion.getAsString()
1038 << "</RemovedAfterVersion>";
1040 StringRef DeprecationSummary = AA->getMessage();
1041 if (!DeprecationSummary.empty()) {
1042 Result << "<DeprecationSummary>";
1043 appendToResultWithXMLEscaping(DeprecationSummary);
1044 Result << "</DeprecationSummary>";
1046 if (AA->getUnavailable())
1047 Result << "<Unavailable/>";
1048 Result << "</Availability>";
1053 bool StartTagEmitted = false;
1054 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1055 const Comment *C = Parts.MiscBlocks[i];
1056 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1058 if (!StartTagEmitted) {
1059 Result << "<Discussion>";
1060 StartTagEmitted = true;
1064 if (StartTagEmitted)
1065 Result << "</Discussion>";
1068 Result << RootEndTag;
1071 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1072 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1097 void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) {
1101 Result << "<![CDATA[";
1102 while (!S.empty()) {
1103 size_t Pos = S.find("]]>");
1105 Result << "]]]]><![CDATA[>";
1106 S = S.drop_front(3);
1109 if (Pos == StringRef::npos)
1112 Result << S.substr(0, Pos);
1114 S = S.drop_front(Pos);
1119 CommentToXMLConverter::CommentToXMLConverter() {}
1120 CommentToXMLConverter::~CommentToXMLConverter() {}
1122 void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC,
1123 SmallVectorImpl<char> &HTML,
1124 const ASTContext &Context) {
1125 CommentASTToHTMLConverter Converter(FC, HTML,
1126 Context.getCommentCommandTraits());
1127 Converter.visit(FC);
1130 void CommentToXMLConverter::convertHTMLTagNodeToText(
1131 const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text,
1132 const ASTContext &Context) {
1133 CommentASTToHTMLConverter Converter(nullptr, Text,
1134 Context.getCommentCommandTraits());
1135 Converter.visit(HTC);
1138 void CommentToXMLConverter::convertCommentToXML(const FullComment *FC,
1139 SmallVectorImpl<char> &XML,
1140 const ASTContext &Context) {
1141 CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
1142 Context.getSourceManager());
1143 Converter.visit(FC);