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 "clang/Lex/Lexer.h"
19 #include "llvm/ADT/StringExtras.h"
20 #include "llvm/ADT/TinyPtrVector.h"
21 #include "llvm/Support/raw_ostream.h"
23 using namespace clang;
24 using namespace clang::comments;
25 using namespace clang::index;
29 /// This comparison will sort parameters with valid index by index, then vararg
30 /// parameters, and invalid (unresolved) parameters last.
31 class ParamCommandCommentCompareIndex {
33 bool operator()(const ParamCommandComment *LHS,
34 const ParamCommandComment *RHS) const {
35 unsigned LHSIndex = UINT_MAX;
36 unsigned RHSIndex = UINT_MAX;
38 if (LHS->isParamIndexValid()) {
39 if (LHS->isVarArgParam())
40 LHSIndex = UINT_MAX - 1;
42 LHSIndex = LHS->getParamIndex();
44 if (RHS->isParamIndexValid()) {
45 if (RHS->isVarArgParam())
46 RHSIndex = UINT_MAX - 1;
48 RHSIndex = RHS->getParamIndex();
50 return LHSIndex < RHSIndex;
54 /// This comparison will sort template parameters in the following order:
55 /// \li real template parameters (depth = 1) in index order;
56 /// \li all other names (depth > 1);
57 /// \li unresolved names.
58 class TParamCommandCommentComparePosition {
60 bool operator()(const TParamCommandComment *LHS,
61 const TParamCommandComment *RHS) const {
62 // Sort unresolved names last.
63 if (!LHS->isPositionValid())
65 if (!RHS->isPositionValid())
68 if (LHS->getDepth() > 1)
70 if (RHS->getDepth() > 1)
73 // Sort template parameters in index order.
74 if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
75 return LHS->getIndex(0) < RHS->getIndex(0);
77 // Leave all other names in source order.
82 /// Separate parts of a FullComment.
83 struct FullCommentParts {
84 /// Take a full comment apart and initialize members accordingly.
85 FullCommentParts(const FullComment *C,
86 const CommandTraits &Traits);
88 const BlockContentComment *Brief;
89 const BlockContentComment *Headerfile;
90 const ParagraphComment *FirstParagraph;
91 SmallVector<const BlockCommandComment *, 4> Returns;
92 SmallVector<const ParamCommandComment *, 8> Params;
93 SmallVector<const TParamCommandComment *, 4> TParams;
94 llvm::TinyPtrVector<const BlockCommandComment *> Exceptions;
95 SmallVector<const BlockContentComment *, 8> MiscBlocks;
98 FullCommentParts::FullCommentParts(const FullComment *C,
99 const CommandTraits &Traits) :
100 Brief(nullptr), Headerfile(nullptr), FirstParagraph(nullptr) {
101 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
103 const Comment *Child = *I;
106 switch (Child->getCommentKind()) {
107 case Comment::NoCommentKind:
110 case Comment::ParagraphCommentKind: {
111 const ParagraphComment *PC = cast<ParagraphComment>(Child);
112 if (PC->isWhitespace())
117 MiscBlocks.push_back(PC);
121 case Comment::BlockCommandCommentKind: {
122 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
123 const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
124 if (!Brief && Info->IsBriefCommand) {
128 if (!Headerfile && Info->IsHeaderfileCommand) {
132 if (Info->IsReturnsCommand) {
133 Returns.push_back(BCC);
136 if (Info->IsThrowsCommand) {
137 Exceptions.push_back(BCC);
140 MiscBlocks.push_back(BCC);
144 case Comment::ParamCommandCommentKind: {
145 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
146 if (!PCC->hasParamName())
149 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
152 Params.push_back(PCC);
156 case Comment::TParamCommandCommentKind: {
157 const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
158 if (!TPCC->hasParamName())
161 if (!TPCC->hasNonWhitespaceParagraph())
164 TParams.push_back(TPCC);
168 case Comment::VerbatimBlockCommentKind:
169 MiscBlocks.push_back(cast<BlockCommandComment>(Child));
172 case Comment::VerbatimLineCommentKind: {
173 const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
174 const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());
175 if (!Info->IsDeclarationCommand)
176 MiscBlocks.push_back(VLC);
180 case Comment::TextCommentKind:
181 case Comment::InlineCommandCommentKind:
182 case Comment::HTMLStartTagCommentKind:
183 case Comment::HTMLEndTagCommentKind:
184 case Comment::VerbatimBlockLineCommentKind:
185 case Comment::FullCommentKind:
186 llvm_unreachable("AST node of this kind can't be a child of "
191 // Sort params in order they are declared in the function prototype.
192 // Unresolved parameters are put at the end of the list in the same order
193 // they were seen in the comment.
194 std::stable_sort(Params.begin(), Params.end(),
195 ParamCommandCommentCompareIndex());
197 std::stable_sort(TParams.begin(), TParams.end(),
198 TParamCommandCommentComparePosition());
201 void printHTMLStartTagComment(const HTMLStartTagComment *C,
202 llvm::raw_svector_ostream &Result) {
203 Result << "<" << C->getTagName();
205 if (C->getNumAttrs() != 0) {
206 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
208 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
210 if (!Attr.Value.empty())
211 Result << "=\"" << Attr.Value << "\"";
215 if (!C->isSelfClosing())
221 class CommentASTToHTMLConverter :
222 public ConstCommentVisitor<CommentASTToHTMLConverter> {
224 /// \param Str accumulator for HTML.
225 CommentASTToHTMLConverter(const FullComment *FC,
226 SmallVectorImpl<char> &Str,
227 const CommandTraits &Traits) :
228 FC(FC), Result(Str), Traits(Traits)
232 void visitTextComment(const TextComment *C);
233 void visitInlineCommandComment(const InlineCommandComment *C);
234 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
235 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
238 void visitParagraphComment(const ParagraphComment *C);
239 void visitBlockCommandComment(const BlockCommandComment *C);
240 void visitParamCommandComment(const ParamCommandComment *C);
241 void visitTParamCommandComment(const TParamCommandComment *C);
242 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
243 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
244 void visitVerbatimLineComment(const VerbatimLineComment *C);
246 void visitFullComment(const FullComment *C);
250 /// Convert a paragraph that is not a block by itself (an argument to some
252 void visitNonStandaloneParagraphComment(const ParagraphComment *C);
254 void appendToResultWithHTMLEscaping(StringRef S);
257 const FullComment *FC;
258 /// Output stream for HTML.
259 llvm::raw_svector_ostream Result;
261 const CommandTraits &Traits;
263 } // end unnamed namespace
265 void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
266 appendToResultWithHTMLEscaping(C->getText());
269 void CommentASTToHTMLConverter::visitInlineCommandComment(
270 const InlineCommandComment *C) {
271 // Nothing to render if no arguments supplied.
272 if (C->getNumArgs() == 0)
275 // Nothing to render if argument is empty.
276 StringRef Arg0 = C->getArgText(0);
280 switch (C->getRenderKind()) {
281 case InlineCommandComment::RenderNormal:
282 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
283 appendToResultWithHTMLEscaping(C->getArgText(i));
288 case InlineCommandComment::RenderBold:
289 assert(C->getNumArgs() == 1);
291 appendToResultWithHTMLEscaping(Arg0);
294 case InlineCommandComment::RenderMonospaced:
295 assert(C->getNumArgs() == 1);
297 appendToResultWithHTMLEscaping(Arg0);
300 case InlineCommandComment::RenderEmphasized:
301 assert(C->getNumArgs() == 1);
303 appendToResultWithHTMLEscaping(Arg0);
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())
325 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
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());
341 if (Info->IsReturnsCommand) {
342 Result << "<p class=\"para-returns\">"
343 "<span class=\"word-returns\">Returns</span> ";
344 visitNonStandaloneParagraphComment(C->getParagraph());
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());
359 Result << "<dt class=\"param-name-index-"
360 << C->getParamIndex()
362 appendToResultWithHTMLEscaping(C->getParamName(FC));
365 Result << "<dt class=\"param-name-index-invalid\">";
366 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
370 if (C->isParamIndexValid()) {
371 if (C->isVarArgParam())
372 Result << "<dd class=\"param-descr-index-vararg\">";
374 Result << "<dd class=\"param-descr-index-"
375 << C->getParamIndex()
378 Result << "<dd class=\"param-descr-index-invalid\">";
380 visitNonStandaloneParagraphComment(C->getParagraph());
384 void CommentASTToHTMLConverter::visitTParamCommandComment(
385 const TParamCommandComment *C) {
386 if (C->isPositionValid()) {
387 if (C->getDepth() == 1)
388 Result << "<dt class=\"tparam-name-index-"
392 Result << "<dt class=\"tparam-name-index-other\">";
393 appendToResultWithHTMLEscaping(C->getParamName(FC));
395 Result << "<dt class=\"tparam-name-index-invalid\">";
396 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
401 if (C->isPositionValid()) {
402 if (C->getDepth() == 1)
403 Result << "<dd class=\"tparam-descr-index-"
407 Result << "<dd class=\"tparam-descr-index-other\">";
409 Result << "<dd class=\"tparam-descr-index-invalid\">";
411 visitNonStandaloneParagraphComment(C->getParagraph());
415 void CommentASTToHTMLConverter::visitVerbatimBlockComment(
416 const VerbatimBlockComment *C) {
417 unsigned NumLines = C->getNumLines();
422 for (unsigned i = 0; i != NumLines; ++i) {
423 appendToResultWithHTMLEscaping(C->getText(i));
424 if (i + 1 != NumLines)
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) {
438 appendToResultWithHTMLEscaping(C->getText());
442 void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
443 FullCommentParts Parts(C, Traits);
445 bool FirstParagraphIsBrief = false;
446 if (Parts.Headerfile)
447 visit(Parts.Headerfile);
450 else if (Parts.FirstParagraph) {
451 Result << "<p class=\"para-brief\">";
452 visitNonStandaloneParagraphComment(Parts.FirstParagraph);
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)
464 if (Parts.TParams.size() != 0) {
466 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
467 visit(Parts.TParams[i]);
471 if (Parts.Params.size() != 0) {
473 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
474 visit(Parts.Params[i]);
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]);
488 void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
489 const ParagraphComment *C) {
493 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
499 void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
500 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
529 class CommentASTToXMLConverter :
530 public ConstCommentVisitor<CommentASTToXMLConverter> {
532 /// \param Str accumulator for XML.
533 CommentASTToXMLConverter(const FullComment *FC,
534 SmallVectorImpl<char> &Str,
535 const CommandTraits &Traits,
536 const SourceManager &SM,
537 SimpleFormatContext &SFC,
539 FC(FC), Result(Str), Traits(Traits), SM(SM),
540 FormatRewriterContext(SFC),
541 FormatInMemoryUniqueId(FUID) { }
544 void visitTextComment(const TextComment *C);
545 void visitInlineCommandComment(const InlineCommandComment *C);
546 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
547 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
550 void visitParagraphComment(const ParagraphComment *C);
552 void appendParagraphCommentWithKind(const ParagraphComment *C,
555 void visitBlockCommandComment(const BlockCommandComment *C);
556 void visitParamCommandComment(const ParamCommandComment *C);
557 void visitTParamCommandComment(const TParamCommandComment *C);
558 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
559 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
560 void visitVerbatimLineComment(const VerbatimLineComment *C);
562 void visitFullComment(const FullComment *C);
565 void appendToResultWithXMLEscaping(StringRef S);
566 void appendToResultWithCDATAEscaping(StringRef S);
568 void formatTextOfDeclaration(const DeclInfo *DI,
569 SmallString<128> &Declaration);
572 const FullComment *FC;
574 /// Output stream for XML.
575 llvm::raw_svector_ostream Result;
577 const CommandTraits &Traits;
578 const SourceManager &SM;
579 SimpleFormatContext &FormatRewriterContext;
580 unsigned FormatInMemoryUniqueId;
583 void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
584 SmallVectorImpl<char> &Str) {
585 ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
586 const LangOptions &LangOpts = Context.getLangOpts();
587 llvm::raw_svector_ostream OS(Str);
588 PrintingPolicy PPolicy(LangOpts);
589 PPolicy.PolishForDeclaration = true;
590 PPolicy.TerseOutput = true;
591 ThisDecl->CurrentDecl->print(OS, PPolicy,
592 /*Indentation*/0, /*PrintInstantiation*/false);
595 void CommentASTToXMLConverter::formatTextOfDeclaration(
596 const DeclInfo *DI, SmallString<128> &Declaration) {
597 // FIXME. formatting API expects null terminated input string.
598 // There might be more efficient way of doing this.
599 std::string StringDecl = Declaration.str();
601 // Formatter specific code.
602 // Form a unique in memory buffer name.
603 SmallString<128> filename;
604 filename += "xmldecl";
605 filename += llvm::utostr(FormatInMemoryUniqueId);
607 FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl);
608 SourceLocation Start = FormatRewriterContext.Sources.getLocForStartOfFile(ID)
609 .getLocWithOffset(0);
610 unsigned Length = Declaration.size();
612 std::vector<CharSourceRange> Ranges(
613 1, CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length)));
614 ASTContext &Context = DI->CurrentDecl->getASTContext();
615 const LangOptions &LangOpts = Context.getLangOpts();
616 Lexer Lex(ID, FormatRewriterContext.Sources.getBuffer(ID),
617 FormatRewriterContext.Sources, LangOpts);
618 tooling::Replacements Replace = reformat(
619 format::getLLVMStyle(), Lex, FormatRewriterContext.Sources, Ranges);
620 applyAllReplacements(Replace, FormatRewriterContext.Rewrite);
621 Declaration = FormatRewriterContext.getRewrittenText(ID);
624 } // end unnamed namespace
626 void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
627 appendToResultWithXMLEscaping(C->getText());
630 void CommentASTToXMLConverter::visitInlineCommandComment(
631 const InlineCommandComment *C) {
632 // Nothing to render if no arguments supplied.
633 if (C->getNumArgs() == 0)
636 // Nothing to render if argument is empty.
637 StringRef Arg0 = C->getArgText(0);
641 switch (C->getRenderKind()) {
642 case InlineCommandComment::RenderNormal:
643 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
644 appendToResultWithXMLEscaping(C->getArgText(i));
648 case InlineCommandComment::RenderBold:
649 assert(C->getNumArgs() == 1);
651 appendToResultWithXMLEscaping(Arg0);
654 case InlineCommandComment::RenderMonospaced:
655 assert(C->getNumArgs() == 1);
656 Result << "<monospaced>";
657 appendToResultWithXMLEscaping(Arg0);
658 Result << "</monospaced>";
660 case InlineCommandComment::RenderEmphasized:
661 assert(C->getNumArgs() == 1);
662 Result << "<emphasized>";
663 appendToResultWithXMLEscaping(Arg0);
664 Result << "</emphasized>";
669 void CommentASTToXMLConverter::visitHTMLStartTagComment(
670 const HTMLStartTagComment *C) {
671 Result << "<rawHTML";
672 if (C->isMalformed())
673 Result << " isMalformed=\"1\"";
678 llvm::raw_svector_ostream TagOS(Tag);
679 printHTMLStartTagComment(C, TagOS);
681 appendToResultWithCDATAEscaping(Tag);
683 Result << "</rawHTML>";
687 CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
688 Result << "<rawHTML";
689 if (C->isMalformed())
690 Result << " isMalformed=\"1\"";
691 Result << "></" << C->getTagName() << "></rawHTML>";
695 CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
696 appendParagraphCommentWithKind(C, StringRef());
699 void CommentASTToXMLConverter::appendParagraphCommentWithKind(
700 const ParagraphComment *C,
701 StringRef ParagraphKind) {
702 if (C->isWhitespace())
705 if (ParagraphKind.empty())
708 Result << "<Para kind=\"" << ParagraphKind << "\">";
710 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
717 void CommentASTToXMLConverter::visitBlockCommandComment(
718 const BlockCommandComment *C) {
719 StringRef ParagraphKind;
721 switch (C->getCommandID()) {
722 case CommandTraits::KCI_attention:
723 case CommandTraits::KCI_author:
724 case CommandTraits::KCI_authors:
725 case CommandTraits::KCI_bug:
726 case CommandTraits::KCI_copyright:
727 case CommandTraits::KCI_date:
728 case CommandTraits::KCI_invariant:
729 case CommandTraits::KCI_note:
730 case CommandTraits::KCI_post:
731 case CommandTraits::KCI_pre:
732 case CommandTraits::KCI_remark:
733 case CommandTraits::KCI_remarks:
734 case CommandTraits::KCI_sa:
735 case CommandTraits::KCI_see:
736 case CommandTraits::KCI_since:
737 case CommandTraits::KCI_todo:
738 case CommandTraits::KCI_version:
739 case CommandTraits::KCI_warning:
740 ParagraphKind = C->getCommandName(Traits);
745 appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
748 void CommentASTToXMLConverter::visitParamCommandComment(
749 const ParamCommandComment *C) {
750 Result << "<Parameter><Name>";
751 appendToResultWithXMLEscaping(C->isParamIndexValid()
752 ? C->getParamName(FC)
753 : C->getParamNameAsWritten());
756 if (C->isParamIndexValid()) {
757 if (C->isVarArgParam())
758 Result << "<IsVarArg />";
760 Result << "<Index>" << C->getParamIndex() << "</Index>";
763 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
764 switch (C->getDirection()) {
765 case ParamCommandComment::In:
768 case ParamCommandComment::Out:
771 case ParamCommandComment::InOut:
775 Result << "</Direction><Discussion>";
776 visit(C->getParagraph());
777 Result << "</Discussion></Parameter>";
780 void CommentASTToXMLConverter::visitTParamCommandComment(
781 const TParamCommandComment *C) {
782 Result << "<Parameter><Name>";
783 appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
784 : C->getParamNameAsWritten());
787 if (C->isPositionValid() && C->getDepth() == 1) {
788 Result << "<Index>" << C->getIndex(0) << "</Index>";
791 Result << "<Discussion>";
792 visit(C->getParagraph());
793 Result << "</Discussion></Parameter>";
796 void CommentASTToXMLConverter::visitVerbatimBlockComment(
797 const VerbatimBlockComment *C) {
798 unsigned NumLines = C->getNumLines();
802 switch (C->getCommandID()) {
803 case CommandTraits::KCI_code:
804 Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
807 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
810 for (unsigned i = 0; i != NumLines; ++i) {
811 appendToResultWithXMLEscaping(C->getText(i));
812 if (i + 1 != NumLines)
815 Result << "</Verbatim>";
818 void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
819 const VerbatimBlockLineComment *C) {
820 llvm_unreachable("should not see this AST node");
823 void CommentASTToXMLConverter::visitVerbatimLineComment(
824 const VerbatimLineComment *C) {
825 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
826 appendToResultWithXMLEscaping(C->getText());
827 Result << "</Verbatim>";
830 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
831 FullCommentParts Parts(C, Traits);
833 const DeclInfo *DI = C->getDeclInfo();
834 StringRef RootEndTag;
836 switch (DI->getKind()) {
837 case DeclInfo::OtherKind:
838 RootEndTag = "</Other>";
841 case DeclInfo::FunctionKind:
842 RootEndTag = "</Function>";
843 Result << "<Function";
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 llvm_unreachable("partial specializations of functions "
855 "are not allowed in C++");
857 if (DI->IsInstanceMethod)
858 Result << " isInstanceMethod=\"1\"";
859 if (DI->IsClassMethod)
860 Result << " isClassMethod=\"1\"";
862 case DeclInfo::ClassKind:
863 RootEndTag = "</Class>";
865 switch (DI->TemplateKind) {
866 case DeclInfo::NotTemplate:
868 case DeclInfo::Template:
869 Result << " templateKind=\"template\"";
871 case DeclInfo::TemplateSpecialization:
872 Result << " templateKind=\"specialization\"";
874 case DeclInfo::TemplatePartialSpecialization:
875 Result << " templateKind=\"partialSpecialization\"";
879 case DeclInfo::VariableKind:
880 RootEndTag = "</Variable>";
881 Result << "<Variable";
883 case DeclInfo::NamespaceKind:
884 RootEndTag = "</Namespace>";
885 Result << "<Namespace";
887 case DeclInfo::TypedefKind:
888 RootEndTag = "</Typedef>";
889 Result << "<Typedef";
891 case DeclInfo::EnumKind:
892 RootEndTag = "</Enum>";
898 // Print line and column number.
899 SourceLocation Loc = DI->CurrentDecl->getLocation();
900 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
901 FileID FID = LocInfo.first;
902 unsigned FileOffset = LocInfo.second;
904 if (!FID.isInvalid()) {
905 if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
906 Result << " file=\"";
907 appendToResultWithXMLEscaping(FE->getName());
910 Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
911 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
916 // Finish the root tag.
919 bool FoundName = false;
920 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
921 if (DeclarationName DeclName = ND->getDeclName()) {
923 std::string Name = DeclName.getAsString();
924 appendToResultWithXMLEscaping(Name);
930 Result << "<Name><anonymous></Name>";
934 SmallString<128> USR;
935 generateUSRForDecl(DI->CommentDecl, USR);
938 appendToResultWithXMLEscaping(USR);
943 // No DeclInfo -- just emit some root tag and name tag.
944 RootEndTag = "</Other>";
945 Result << "<Other><Name>unknown</Name>";
948 if (Parts.Headerfile) {
949 Result << "<Headerfile>";
950 visit(Parts.Headerfile);
951 Result << "</Headerfile>";
955 // Pretty-print the declaration.
956 Result << "<Declaration>";
957 SmallString<128> Declaration;
958 getSourceTextOfDeclaration(DI, Declaration);
959 formatTextOfDeclaration(DI, Declaration);
960 appendToResultWithXMLEscaping(Declaration);
961 Result << "</Declaration>";
964 bool FirstParagraphIsBrief = false;
966 Result << "<Abstract>";
968 Result << "</Abstract>";
969 } else if (Parts.FirstParagraph) {
970 Result << "<Abstract>";
971 visit(Parts.FirstParagraph);
972 Result << "</Abstract>";
973 FirstParagraphIsBrief = true;
976 if (Parts.TParams.size() != 0) {
977 Result << "<TemplateParameters>";
978 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
979 visit(Parts.TParams[i]);
980 Result << "</TemplateParameters>";
983 if (Parts.Params.size() != 0) {
984 Result << "<Parameters>";
985 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
986 visit(Parts.Params[i]);
987 Result << "</Parameters>";
990 if (Parts.Exceptions.size() != 0) {
991 Result << "<Exceptions>";
992 for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
993 visit(Parts.Exceptions[i]);
994 Result << "</Exceptions>";
997 if (Parts.Returns.size() != 0) {
998 Result << "<ResultDiscussion>";
999 for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
1000 visit(Parts.Returns[i]);
1001 Result << "</ResultDiscussion>";
1004 if (DI->CommentDecl->hasAttrs()) {
1005 const AttrVec &Attrs = DI->CommentDecl->getAttrs();
1006 for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
1007 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
1009 if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
1010 if (DA->getMessage().empty())
1011 Result << "<Deprecated/>";
1013 Result << "<Deprecated>";
1014 appendToResultWithXMLEscaping(DA->getMessage());
1015 Result << "</Deprecated>";
1018 else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1019 if (UA->getMessage().empty())
1020 Result << "<Unavailable/>";
1022 Result << "<Unavailable>";
1023 appendToResultWithXMLEscaping(UA->getMessage());
1024 Result << "</Unavailable>";
1030 // 'availability' attribute.
1031 Result << "<Availability";
1032 StringRef Distribution;
1033 if (AA->getPlatform()) {
1034 Distribution = AvailabilityAttr::getPrettyPlatformName(
1035 AA->getPlatform()->getName());
1036 if (Distribution.empty())
1037 Distribution = AA->getPlatform()->getName();
1039 Result << " distribution=\"" << Distribution << "\">";
1040 VersionTuple IntroducedInVersion = AA->getIntroduced();
1041 if (!IntroducedInVersion.empty()) {
1042 Result << "<IntroducedInVersion>"
1043 << IntroducedInVersion.getAsString()
1044 << "</IntroducedInVersion>";
1046 VersionTuple DeprecatedInVersion = AA->getDeprecated();
1047 if (!DeprecatedInVersion.empty()) {
1048 Result << "<DeprecatedInVersion>"
1049 << DeprecatedInVersion.getAsString()
1050 << "</DeprecatedInVersion>";
1052 VersionTuple RemovedAfterVersion = AA->getObsoleted();
1053 if (!RemovedAfterVersion.empty()) {
1054 Result << "<RemovedAfterVersion>"
1055 << RemovedAfterVersion.getAsString()
1056 << "</RemovedAfterVersion>";
1058 StringRef DeprecationSummary = AA->getMessage();
1059 if (!DeprecationSummary.empty()) {
1060 Result << "<DeprecationSummary>";
1061 appendToResultWithXMLEscaping(DeprecationSummary);
1062 Result << "</DeprecationSummary>";
1064 if (AA->getUnavailable())
1065 Result << "<Unavailable/>";
1066 Result << "</Availability>";
1071 bool StartTagEmitted = false;
1072 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1073 const Comment *C = Parts.MiscBlocks[i];
1074 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1076 if (!StartTagEmitted) {
1077 Result << "<Discussion>";
1078 StartTagEmitted = true;
1082 if (StartTagEmitted)
1083 Result << "</Discussion>";
1086 Result << RootEndTag;
1091 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1092 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1117 void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) {
1121 Result << "<![CDATA[";
1122 while (!S.empty()) {
1123 size_t Pos = S.find("]]>");
1125 Result << "]]]]><![CDATA[>";
1126 S = S.drop_front(3);
1129 if (Pos == StringRef::npos)
1132 Result << S.substr(0, Pos);
1134 S = S.drop_front(Pos);
1139 CommentToXMLConverter::CommentToXMLConverter() : FormatInMemoryUniqueId(0) {}
1140 CommentToXMLConverter::~CommentToXMLConverter() {}
1142 void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC,
1143 SmallVectorImpl<char> &HTML,
1144 const ASTContext &Context) {
1145 CommentASTToHTMLConverter Converter(FC, HTML,
1146 Context.getCommentCommandTraits());
1147 Converter.visit(FC);
1150 void CommentToXMLConverter::convertHTMLTagNodeToText(
1151 const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text,
1152 const ASTContext &Context) {
1153 CommentASTToHTMLConverter Converter(nullptr, Text,
1154 Context.getCommentCommandTraits());
1155 Converter.visit(HTC);
1158 void CommentToXMLConverter::convertCommentToXML(const FullComment *FC,
1159 SmallVectorImpl<char> &XML,
1160 const ASTContext &Context) {
1161 if (!FormatContext || (FormatInMemoryUniqueId % 1000) == 0) {
1162 // Create a new format context, or re-create it after some number of
1163 // iterations, so the buffers don't grow too large.
1164 FormatContext.reset(new SimpleFormatContext(Context.getLangOpts()));
1167 CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
1168 Context.getSourceManager(), *FormatContext,
1169 FormatInMemoryUniqueId++);
1170 Converter.visit(FC);