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 "SimpleFormatContext.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/AST/Attr.h"
14 #include "clang/AST/Comment.h"
15 #include "clang/AST/CommentVisitor.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;
28 /// This comparison will sort parameters with valid index by index, then vararg
29 /// parameters, and invalid (unresolved) parameters last.
30 class ParamCommandCommentCompareIndex {
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;
41 LHSIndex = LHS->getParamIndex();
43 if (RHS->isParamIndexValid()) {
44 if (RHS->isVarArgParam())
45 RHSIndex = UINT_MAX - 1;
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 {
59 bool operator()(const TParamCommandComment *LHS,
60 const TParamCommandComment *RHS) const {
61 // Sort unresolved names last.
62 if (!LHS->isPositionValid())
64 if (!RHS->isPositionValid())
67 if (LHS->getDepth() > 1)
69 if (RHS->getDepth() > 1)
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.
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();
102 const Comment *Child = *I;
105 switch (Child->getCommentKind()) {
106 case Comment::NoCommentKind:
109 case Comment::ParagraphCommentKind: {
110 const ParagraphComment *PC = cast<ParagraphComment>(Child);
111 if (PC->isWhitespace())
116 MiscBlocks.push_back(PC);
120 case Comment::BlockCommandCommentKind: {
121 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
122 const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
123 if (!Brief && Info->IsBriefCommand) {
127 if (!Headerfile && Info->IsHeaderfileCommand) {
131 if (Info->IsReturnsCommand) {
132 Returns.push_back(BCC);
135 if (Info->IsThrowsCommand) {
136 Exceptions.push_back(BCC);
139 MiscBlocks.push_back(BCC);
143 case Comment::ParamCommandCommentKind: {
144 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
145 if (!PCC->hasParamName())
148 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
151 Params.push_back(PCC);
155 case Comment::TParamCommandCommentKind: {
156 const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
157 if (!TPCC->hasParamName())
160 if (!TPCC->hasNonWhitespaceParagraph())
163 TParams.push_back(TPCC);
167 case Comment::VerbatimBlockCommentKind:
168 MiscBlocks.push_back(cast<BlockCommandComment>(Child));
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);
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 "
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 std::stable_sort(Params.begin(), Params.end(),
194 ParamCommandCommentCompareIndex());
196 std::stable_sort(TParams.begin(), TParams.end(),
197 TParamCommandCommentComparePosition());
200 void printHTMLStartTagComment(const HTMLStartTagComment *C,
201 llvm::raw_svector_ostream &Result) {
202 Result << "<" << C->getTagName();
204 if (C->getNumAttrs() != 0) {
205 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
207 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
209 if (!Attr.Value.empty())
210 Result << "=\"" << Attr.Value << "\"";
214 if (!C->isSelfClosing())
220 class CommentASTToHTMLConverter :
221 public ConstCommentVisitor<CommentASTToHTMLConverter> {
223 /// \param Str accumulator for HTML.
224 CommentASTToHTMLConverter(const FullComment *FC,
225 SmallVectorImpl<char> &Str,
226 const CommandTraits &Traits) :
227 FC(FC), Result(Str), Traits(Traits)
231 void visitTextComment(const TextComment *C);
232 void visitInlineCommandComment(const InlineCommandComment *C);
233 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
234 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
237 void visitParagraphComment(const ParagraphComment *C);
238 void visitBlockCommandComment(const BlockCommandComment *C);
239 void visitParamCommandComment(const ParamCommandComment *C);
240 void visitTParamCommandComment(const TParamCommandComment *C);
241 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
242 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
243 void visitVerbatimLineComment(const VerbatimLineComment *C);
245 void visitFullComment(const FullComment *C);
249 /// Convert a paragraph that is not a block by itself (an argument to some
251 void visitNonStandaloneParagraphComment(const ParagraphComment *C);
253 void appendToResultWithHTMLEscaping(StringRef S);
256 const FullComment *FC;
257 /// Output stream for HTML.
258 llvm::raw_svector_ostream Result;
260 const CommandTraits &Traits;
262 } // end unnamed namespace
264 void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
265 appendToResultWithHTMLEscaping(C->getText());
268 void CommentASTToHTMLConverter::visitInlineCommandComment(
269 const InlineCommandComment *C) {
270 // Nothing to render if no arguments supplied.
271 if (C->getNumArgs() == 0)
274 // Nothing to render if argument is empty.
275 StringRef Arg0 = C->getArgText(0);
279 switch (C->getRenderKind()) {
280 case InlineCommandComment::RenderNormal:
281 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
282 appendToResultWithHTMLEscaping(C->getArgText(i));
287 case InlineCommandComment::RenderBold:
288 assert(C->getNumArgs() == 1);
290 appendToResultWithHTMLEscaping(Arg0);
293 case InlineCommandComment::RenderMonospaced:
294 assert(C->getNumArgs() == 1);
296 appendToResultWithHTMLEscaping(Arg0);
299 case InlineCommandComment::RenderEmphasized:
300 assert(C->getNumArgs() == 1);
302 appendToResultWithHTMLEscaping(Arg0);
308 void CommentASTToHTMLConverter::visitHTMLStartTagComment(
309 const HTMLStartTagComment *C) {
310 printHTMLStartTagComment(C, Result);
313 void CommentASTToHTMLConverter::visitHTMLEndTagComment(
314 const HTMLEndTagComment *C) {
315 Result << "</" << C->getTagName() << ">";
318 void CommentASTToHTMLConverter::visitParagraphComment(
319 const ParagraphComment *C) {
320 if (C->isWhitespace())
324 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
331 void CommentASTToHTMLConverter::visitBlockCommandComment(
332 const BlockCommandComment *C) {
333 const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
334 if (Info->IsBriefCommand) {
335 Result << "<p class=\"para-brief\">";
336 visitNonStandaloneParagraphComment(C->getParagraph());
340 if (Info->IsReturnsCommand) {
341 Result << "<p class=\"para-returns\">"
342 "<span class=\"word-returns\">Returns</span> ";
343 visitNonStandaloneParagraphComment(C->getParagraph());
347 // We don't know anything about this command. Just render the paragraph.
348 visit(C->getParagraph());
351 void CommentASTToHTMLConverter::visitParamCommandComment(
352 const ParamCommandComment *C) {
353 if (C->isParamIndexValid()) {
354 if (C->isVarArgParam()) {
355 Result << "<dt class=\"param-name-index-vararg\">";
356 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
358 Result << "<dt class=\"param-name-index-"
359 << C->getParamIndex()
361 appendToResultWithHTMLEscaping(C->getParamName(FC));
364 Result << "<dt class=\"param-name-index-invalid\">";
365 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
369 if (C->isParamIndexValid()) {
370 if (C->isVarArgParam())
371 Result << "<dd class=\"param-descr-index-vararg\">";
373 Result << "<dd class=\"param-descr-index-"
374 << C->getParamIndex()
377 Result << "<dd class=\"param-descr-index-invalid\">";
379 visitNonStandaloneParagraphComment(C->getParagraph());
383 void CommentASTToHTMLConverter::visitTParamCommandComment(
384 const TParamCommandComment *C) {
385 if (C->isPositionValid()) {
386 if (C->getDepth() == 1)
387 Result << "<dt class=\"tparam-name-index-"
391 Result << "<dt class=\"tparam-name-index-other\">";
392 appendToResultWithHTMLEscaping(C->getParamName(FC));
394 Result << "<dt class=\"tparam-name-index-invalid\">";
395 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
400 if (C->isPositionValid()) {
401 if (C->getDepth() == 1)
402 Result << "<dd class=\"tparam-descr-index-"
406 Result << "<dd class=\"tparam-descr-index-other\">";
408 Result << "<dd class=\"tparam-descr-index-invalid\">";
410 visitNonStandaloneParagraphComment(C->getParagraph());
414 void CommentASTToHTMLConverter::visitVerbatimBlockComment(
415 const VerbatimBlockComment *C) {
416 unsigned NumLines = C->getNumLines();
421 for (unsigned i = 0; i != NumLines; ++i) {
422 appendToResultWithHTMLEscaping(C->getText(i));
423 if (i + 1 != NumLines)
429 void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
430 const VerbatimBlockLineComment *C) {
431 llvm_unreachable("should not see this AST node");
434 void CommentASTToHTMLConverter::visitVerbatimLineComment(
435 const VerbatimLineComment *C) {
437 appendToResultWithHTMLEscaping(C->getText());
441 void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
442 FullCommentParts Parts(C, Traits);
444 bool FirstParagraphIsBrief = false;
445 if (Parts.Headerfile)
446 visit(Parts.Headerfile);
449 else if (Parts.FirstParagraph) {
450 Result << "<p class=\"para-brief\">";
451 visitNonStandaloneParagraphComment(Parts.FirstParagraph);
453 FirstParagraphIsBrief = true;
456 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
457 const Comment *C = Parts.MiscBlocks[i];
458 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
463 if (Parts.TParams.size() != 0) {
465 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
466 visit(Parts.TParams[i]);
470 if (Parts.Params.size() != 0) {
472 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
473 visit(Parts.Params[i]);
477 if (Parts.Returns.size() != 0) {
478 Result << "<div class=\"result-discussion\">";
479 for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
480 visit(Parts.Returns[i]);
486 void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
487 const ParagraphComment *C) {
491 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
497 void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
498 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
527 class CommentASTToXMLConverter :
528 public ConstCommentVisitor<CommentASTToXMLConverter> {
530 /// \param Str accumulator for XML.
531 CommentASTToXMLConverter(const FullComment *FC,
532 SmallVectorImpl<char> &Str,
533 const CommandTraits &Traits,
534 const SourceManager &SM,
535 SimpleFormatContext &SFC,
537 FC(FC), Result(Str), Traits(Traits), SM(SM),
538 FormatRewriterContext(SFC),
539 FormatInMemoryUniqueId(FUID) { }
542 void visitTextComment(const TextComment *C);
543 void visitInlineCommandComment(const InlineCommandComment *C);
544 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
545 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
548 void visitParagraphComment(const ParagraphComment *C);
550 void appendParagraphCommentWithKind(const ParagraphComment *C,
553 void visitBlockCommandComment(const BlockCommandComment *C);
554 void visitParamCommandComment(const ParamCommandComment *C);
555 void visitTParamCommandComment(const TParamCommandComment *C);
556 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
557 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
558 void visitVerbatimLineComment(const VerbatimLineComment *C);
560 void visitFullComment(const FullComment *C);
563 void appendToResultWithXMLEscaping(StringRef S);
564 void appendToResultWithCDATAEscaping(StringRef S);
566 void formatTextOfDeclaration(const DeclInfo *DI,
567 SmallString<128> &Declaration);
570 const FullComment *FC;
572 /// Output stream for XML.
573 llvm::raw_svector_ostream Result;
575 const CommandTraits &Traits;
576 const SourceManager &SM;
577 SimpleFormatContext &FormatRewriterContext;
578 unsigned FormatInMemoryUniqueId;
581 void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
582 SmallVectorImpl<char> &Str) {
583 ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
584 const LangOptions &LangOpts = Context.getLangOpts();
585 llvm::raw_svector_ostream OS(Str);
586 PrintingPolicy PPolicy(LangOpts);
587 PPolicy.PolishForDeclaration = true;
588 PPolicy.TerseOutput = true;
589 ThisDecl->CurrentDecl->print(OS, PPolicy,
590 /*Indentation*/0, /*PrintInstantiation*/false);
593 void CommentASTToXMLConverter::formatTextOfDeclaration(
594 const DeclInfo *DI, SmallString<128> &Declaration) {
595 // FIXME. formatting API expects null terminated input string.
596 // There might be more efficient way of doing this.
597 std::string StringDecl = Declaration.str();
599 // Formatter specific code.
600 // Form a unique in memory buffer name.
601 SmallString<128> filename;
602 filename += "xmldecl";
603 filename += llvm::utostr(FormatInMemoryUniqueId);
605 FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl);
606 SourceLocation Start = FormatRewriterContext.Sources.getLocForStartOfFile(ID)
607 .getLocWithOffset(0);
608 unsigned Length = Declaration.size();
610 tooling::Replacements Replace = reformat(
611 format::getLLVMStyle(), FormatRewriterContext.Sources, ID,
612 CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length)));
613 applyAllReplacements(Replace, FormatRewriterContext.Rewrite);
614 Declaration = FormatRewriterContext.getRewrittenText(ID);
617 } // end unnamed namespace
619 void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
620 appendToResultWithXMLEscaping(C->getText());
623 void CommentASTToXMLConverter::visitInlineCommandComment(
624 const InlineCommandComment *C) {
625 // Nothing to render if no arguments supplied.
626 if (C->getNumArgs() == 0)
629 // Nothing to render if argument is empty.
630 StringRef Arg0 = C->getArgText(0);
634 switch (C->getRenderKind()) {
635 case InlineCommandComment::RenderNormal:
636 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
637 appendToResultWithXMLEscaping(C->getArgText(i));
641 case InlineCommandComment::RenderBold:
642 assert(C->getNumArgs() == 1);
644 appendToResultWithXMLEscaping(Arg0);
647 case InlineCommandComment::RenderMonospaced:
648 assert(C->getNumArgs() == 1);
649 Result << "<monospaced>";
650 appendToResultWithXMLEscaping(Arg0);
651 Result << "</monospaced>";
653 case InlineCommandComment::RenderEmphasized:
654 assert(C->getNumArgs() == 1);
655 Result << "<emphasized>";
656 appendToResultWithXMLEscaping(Arg0);
657 Result << "</emphasized>";
662 void CommentASTToXMLConverter::visitHTMLStartTagComment(
663 const HTMLStartTagComment *C) {
664 Result << "<rawHTML";
665 if (C->isMalformed())
666 Result << " isMalformed=\"1\"";
671 llvm::raw_svector_ostream TagOS(Tag);
672 printHTMLStartTagComment(C, TagOS);
674 appendToResultWithCDATAEscaping(Tag);
676 Result << "</rawHTML>";
680 CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
681 Result << "<rawHTML";
682 if (C->isMalformed())
683 Result << " isMalformed=\"1\"";
684 Result << "></" << C->getTagName() << "></rawHTML>";
688 CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
689 appendParagraphCommentWithKind(C, StringRef());
692 void CommentASTToXMLConverter::appendParagraphCommentWithKind(
693 const ParagraphComment *C,
694 StringRef ParagraphKind) {
695 if (C->isWhitespace())
698 if (ParagraphKind.empty())
701 Result << "<Para kind=\"" << ParagraphKind << "\">";
703 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
710 void CommentASTToXMLConverter::visitBlockCommandComment(
711 const BlockCommandComment *C) {
712 StringRef ParagraphKind;
714 switch (C->getCommandID()) {
715 case CommandTraits::KCI_attention:
716 case CommandTraits::KCI_author:
717 case CommandTraits::KCI_authors:
718 case CommandTraits::KCI_bug:
719 case CommandTraits::KCI_copyright:
720 case CommandTraits::KCI_date:
721 case CommandTraits::KCI_invariant:
722 case CommandTraits::KCI_note:
723 case CommandTraits::KCI_post:
724 case CommandTraits::KCI_pre:
725 case CommandTraits::KCI_remark:
726 case CommandTraits::KCI_remarks:
727 case CommandTraits::KCI_sa:
728 case CommandTraits::KCI_see:
729 case CommandTraits::KCI_since:
730 case CommandTraits::KCI_todo:
731 case CommandTraits::KCI_version:
732 case CommandTraits::KCI_warning:
733 ParagraphKind = C->getCommandName(Traits);
738 appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
741 void CommentASTToXMLConverter::visitParamCommandComment(
742 const ParamCommandComment *C) {
743 Result << "<Parameter><Name>";
744 appendToResultWithXMLEscaping(C->isParamIndexValid()
745 ? C->getParamName(FC)
746 : C->getParamNameAsWritten());
749 if (C->isParamIndexValid()) {
750 if (C->isVarArgParam())
751 Result << "<IsVarArg />";
753 Result << "<Index>" << C->getParamIndex() << "</Index>";
756 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
757 switch (C->getDirection()) {
758 case ParamCommandComment::In:
761 case ParamCommandComment::Out:
764 case ParamCommandComment::InOut:
768 Result << "</Direction><Discussion>";
769 visit(C->getParagraph());
770 Result << "</Discussion></Parameter>";
773 void CommentASTToXMLConverter::visitTParamCommandComment(
774 const TParamCommandComment *C) {
775 Result << "<Parameter><Name>";
776 appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
777 : C->getParamNameAsWritten());
780 if (C->isPositionValid() && C->getDepth() == 1) {
781 Result << "<Index>" << C->getIndex(0) << "</Index>";
784 Result << "<Discussion>";
785 visit(C->getParagraph());
786 Result << "</Discussion></Parameter>";
789 void CommentASTToXMLConverter::visitVerbatimBlockComment(
790 const VerbatimBlockComment *C) {
791 unsigned NumLines = C->getNumLines();
795 switch (C->getCommandID()) {
796 case CommandTraits::KCI_code:
797 Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
800 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
803 for (unsigned i = 0; i != NumLines; ++i) {
804 appendToResultWithXMLEscaping(C->getText(i));
805 if (i + 1 != NumLines)
808 Result << "</Verbatim>";
811 void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
812 const VerbatimBlockLineComment *C) {
813 llvm_unreachable("should not see this AST node");
816 void CommentASTToXMLConverter::visitVerbatimLineComment(
817 const VerbatimLineComment *C) {
818 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
819 appendToResultWithXMLEscaping(C->getText());
820 Result << "</Verbatim>";
823 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
824 FullCommentParts Parts(C, Traits);
826 const DeclInfo *DI = C->getDeclInfo();
827 StringRef RootEndTag;
829 switch (DI->getKind()) {
830 case DeclInfo::OtherKind:
831 RootEndTag = "</Other>";
834 case DeclInfo::FunctionKind:
835 RootEndTag = "</Function>";
836 Result << "<Function";
837 switch (DI->TemplateKind) {
838 case DeclInfo::NotTemplate:
840 case DeclInfo::Template:
841 Result << " templateKind=\"template\"";
843 case DeclInfo::TemplateSpecialization:
844 Result << " templateKind=\"specialization\"";
846 case DeclInfo::TemplatePartialSpecialization:
847 llvm_unreachable("partial specializations of functions "
848 "are not allowed in C++");
850 if (DI->IsInstanceMethod)
851 Result << " isInstanceMethod=\"1\"";
852 if (DI->IsClassMethod)
853 Result << " isClassMethod=\"1\"";
855 case DeclInfo::ClassKind:
856 RootEndTag = "</Class>";
858 switch (DI->TemplateKind) {
859 case DeclInfo::NotTemplate:
861 case DeclInfo::Template:
862 Result << " templateKind=\"template\"";
864 case DeclInfo::TemplateSpecialization:
865 Result << " templateKind=\"specialization\"";
867 case DeclInfo::TemplatePartialSpecialization:
868 Result << " templateKind=\"partialSpecialization\"";
872 case DeclInfo::VariableKind:
873 RootEndTag = "</Variable>";
874 Result << "<Variable";
876 case DeclInfo::NamespaceKind:
877 RootEndTag = "</Namespace>";
878 Result << "<Namespace";
880 case DeclInfo::TypedefKind:
881 RootEndTag = "</Typedef>";
882 Result << "<Typedef";
884 case DeclInfo::EnumKind:
885 RootEndTag = "</Enum>";
891 // Print line and column number.
892 SourceLocation Loc = DI->CurrentDecl->getLocation();
893 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
894 FileID FID = LocInfo.first;
895 unsigned FileOffset = LocInfo.second;
898 if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
899 Result << " file=\"";
900 appendToResultWithXMLEscaping(FE->getName());
903 Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
904 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
909 // Finish the root tag.
912 bool FoundName = false;
913 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
914 if (DeclarationName DeclName = ND->getDeclName()) {
916 std::string Name = DeclName.getAsString();
917 appendToResultWithXMLEscaping(Name);
923 Result << "<Name><anonymous></Name>";
927 SmallString<128> USR;
928 generateUSRForDecl(DI->CommentDecl, USR);
931 appendToResultWithXMLEscaping(USR);
936 // No DeclInfo -- just emit some root tag and name tag.
937 RootEndTag = "</Other>";
938 Result << "<Other><Name>unknown</Name>";
941 if (Parts.Headerfile) {
942 Result << "<Headerfile>";
943 visit(Parts.Headerfile);
944 Result << "</Headerfile>";
948 // Pretty-print the declaration.
949 Result << "<Declaration>";
950 SmallString<128> Declaration;
951 getSourceTextOfDeclaration(DI, Declaration);
952 formatTextOfDeclaration(DI, Declaration);
953 appendToResultWithXMLEscaping(Declaration);
954 Result << "</Declaration>";
957 bool FirstParagraphIsBrief = false;
959 Result << "<Abstract>";
961 Result << "</Abstract>";
962 } else if (Parts.FirstParagraph) {
963 Result << "<Abstract>";
964 visit(Parts.FirstParagraph);
965 Result << "</Abstract>";
966 FirstParagraphIsBrief = true;
969 if (Parts.TParams.size() != 0) {
970 Result << "<TemplateParameters>";
971 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
972 visit(Parts.TParams[i]);
973 Result << "</TemplateParameters>";
976 if (Parts.Params.size() != 0) {
977 Result << "<Parameters>";
978 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
979 visit(Parts.Params[i]);
980 Result << "</Parameters>";
983 if (Parts.Exceptions.size() != 0) {
984 Result << "<Exceptions>";
985 for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
986 visit(Parts.Exceptions[i]);
987 Result << "</Exceptions>";
990 if (Parts.Returns.size() != 0) {
991 Result << "<ResultDiscussion>";
992 for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
993 visit(Parts.Returns[i]);
994 Result << "</ResultDiscussion>";
997 if (DI->CommentDecl->hasAttrs()) {
998 const AttrVec &Attrs = DI->CommentDecl->getAttrs();
999 for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
1000 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
1002 if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
1003 if (DA->getMessage().empty())
1004 Result << "<Deprecated/>";
1006 Result << "<Deprecated>";
1007 appendToResultWithXMLEscaping(DA->getMessage());
1008 Result << "</Deprecated>";
1011 else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1012 if (UA->getMessage().empty())
1013 Result << "<Unavailable/>";
1015 Result << "<Unavailable>";
1016 appendToResultWithXMLEscaping(UA->getMessage());
1017 Result << "</Unavailable>";
1023 // 'availability' attribute.
1024 Result << "<Availability";
1025 StringRef Distribution;
1026 if (AA->getPlatform()) {
1027 Distribution = AvailabilityAttr::getPrettyPlatformName(
1028 AA->getPlatform()->getName());
1029 if (Distribution.empty())
1030 Distribution = AA->getPlatform()->getName();
1032 Result << " distribution=\"" << Distribution << "\">";
1033 VersionTuple IntroducedInVersion = AA->getIntroduced();
1034 if (!IntroducedInVersion.empty()) {
1035 Result << "<IntroducedInVersion>"
1036 << IntroducedInVersion.getAsString()
1037 << "</IntroducedInVersion>";
1039 VersionTuple DeprecatedInVersion = AA->getDeprecated();
1040 if (!DeprecatedInVersion.empty()) {
1041 Result << "<DeprecatedInVersion>"
1042 << DeprecatedInVersion.getAsString()
1043 << "</DeprecatedInVersion>";
1045 VersionTuple RemovedAfterVersion = AA->getObsoleted();
1046 if (!RemovedAfterVersion.empty()) {
1047 Result << "<RemovedAfterVersion>"
1048 << RemovedAfterVersion.getAsString()
1049 << "</RemovedAfterVersion>";
1051 StringRef DeprecationSummary = AA->getMessage();
1052 if (!DeprecationSummary.empty()) {
1053 Result << "<DeprecationSummary>";
1054 appendToResultWithXMLEscaping(DeprecationSummary);
1055 Result << "</DeprecationSummary>";
1057 if (AA->getUnavailable())
1058 Result << "<Unavailable/>";
1059 Result << "</Availability>";
1064 bool StartTagEmitted = false;
1065 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1066 const Comment *C = Parts.MiscBlocks[i];
1067 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1069 if (!StartTagEmitted) {
1070 Result << "<Discussion>";
1071 StartTagEmitted = true;
1075 if (StartTagEmitted)
1076 Result << "</Discussion>";
1079 Result << RootEndTag;
1082 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1083 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1108 void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) {
1112 Result << "<![CDATA[";
1113 while (!S.empty()) {
1114 size_t Pos = S.find("]]>");
1116 Result << "]]]]><![CDATA[>";
1117 S = S.drop_front(3);
1120 if (Pos == StringRef::npos)
1123 Result << S.substr(0, Pos);
1125 S = S.drop_front(Pos);
1130 CommentToXMLConverter::CommentToXMLConverter() : FormatInMemoryUniqueId(0) {}
1131 CommentToXMLConverter::~CommentToXMLConverter() {}
1133 void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC,
1134 SmallVectorImpl<char> &HTML,
1135 const ASTContext &Context) {
1136 CommentASTToHTMLConverter Converter(FC, HTML,
1137 Context.getCommentCommandTraits());
1138 Converter.visit(FC);
1141 void CommentToXMLConverter::convertHTMLTagNodeToText(
1142 const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text,
1143 const ASTContext &Context) {
1144 CommentASTToHTMLConverter Converter(nullptr, Text,
1145 Context.getCommentCommandTraits());
1146 Converter.visit(HTC);
1149 void CommentToXMLConverter::convertCommentToXML(const FullComment *FC,
1150 SmallVectorImpl<char> &XML,
1151 const ASTContext &Context) {
1152 if (!FormatContext || (FormatInMemoryUniqueId % 1000) == 0) {
1153 // Create a new format context, or re-create it after some number of
1154 // iterations, so the buffers don't grow too large.
1155 FormatContext.reset(new SimpleFormatContext(Context.getLangOpts()));
1158 CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
1159 Context.getSourceManager(), *FormatContext,
1160 FormatInMemoryUniqueId++);
1161 Converter.visit(FC);