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 ThisDecl->CurrentDecl->print(OS, PPolicy,
583 /*Indentation*/0, /*PrintInstantiation*/false);
586 void CommentASTToXMLConverter::formatTextOfDeclaration(
587 const DeclInfo *DI, SmallString<128> &Declaration) {
588 // Formatting API expects null terminated input string.
589 StringRef StringDecl(Declaration.c_str(), Declaration.size());
591 // Formatter specific code.
593 unsigned Length = Declaration.size();
595 format::FormatStyle Style = format::getLLVMStyle();
596 Style.FixNamespaceComments = false;
597 tooling::Replacements Replaces =
598 reformat(Style, StringDecl, tooling::Range(Offset, Length), "xmldecl.xd");
599 auto FormattedStringDecl = applyAllReplacements(StringDecl, Replaces);
600 if (static_cast<bool>(FormattedStringDecl)) {
601 Declaration = *FormattedStringDecl;
605 } // end unnamed namespace
607 void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
608 appendToResultWithXMLEscaping(C->getText());
611 void CommentASTToXMLConverter::visitInlineCommandComment(
612 const InlineCommandComment *C) {
613 // Nothing to render if no arguments supplied.
614 if (C->getNumArgs() == 0)
617 // Nothing to render if argument is empty.
618 StringRef Arg0 = C->getArgText(0);
622 switch (C->getRenderKind()) {
623 case InlineCommandComment::RenderNormal:
624 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
625 appendToResultWithXMLEscaping(C->getArgText(i));
629 case InlineCommandComment::RenderBold:
630 assert(C->getNumArgs() == 1);
632 appendToResultWithXMLEscaping(Arg0);
635 case InlineCommandComment::RenderMonospaced:
636 assert(C->getNumArgs() == 1);
637 Result << "<monospaced>";
638 appendToResultWithXMLEscaping(Arg0);
639 Result << "</monospaced>";
641 case InlineCommandComment::RenderEmphasized:
642 assert(C->getNumArgs() == 1);
643 Result << "<emphasized>";
644 appendToResultWithXMLEscaping(Arg0);
645 Result << "</emphasized>";
650 void CommentASTToXMLConverter::visitHTMLStartTagComment(
651 const HTMLStartTagComment *C) {
652 Result << "<rawHTML";
653 if (C->isMalformed())
654 Result << " isMalformed=\"1\"";
659 llvm::raw_svector_ostream TagOS(Tag);
660 printHTMLStartTagComment(C, TagOS);
662 appendToResultWithCDATAEscaping(Tag);
664 Result << "</rawHTML>";
668 CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
669 Result << "<rawHTML";
670 if (C->isMalformed())
671 Result << " isMalformed=\"1\"";
672 Result << "></" << C->getTagName() << "></rawHTML>";
676 CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
677 appendParagraphCommentWithKind(C, StringRef());
680 void CommentASTToXMLConverter::appendParagraphCommentWithKind(
681 const ParagraphComment *C,
682 StringRef ParagraphKind) {
683 if (C->isWhitespace())
686 if (ParagraphKind.empty())
689 Result << "<Para kind=\"" << ParagraphKind << "\">";
691 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
698 void CommentASTToXMLConverter::visitBlockCommandComment(
699 const BlockCommandComment *C) {
700 StringRef ParagraphKind;
702 switch (C->getCommandID()) {
703 case CommandTraits::KCI_attention:
704 case CommandTraits::KCI_author:
705 case CommandTraits::KCI_authors:
706 case CommandTraits::KCI_bug:
707 case CommandTraits::KCI_copyright:
708 case CommandTraits::KCI_date:
709 case CommandTraits::KCI_invariant:
710 case CommandTraits::KCI_note:
711 case CommandTraits::KCI_post:
712 case CommandTraits::KCI_pre:
713 case CommandTraits::KCI_remark:
714 case CommandTraits::KCI_remarks:
715 case CommandTraits::KCI_sa:
716 case CommandTraits::KCI_see:
717 case CommandTraits::KCI_since:
718 case CommandTraits::KCI_todo:
719 case CommandTraits::KCI_version:
720 case CommandTraits::KCI_warning:
721 ParagraphKind = C->getCommandName(Traits);
726 appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
729 void CommentASTToXMLConverter::visitParamCommandComment(
730 const ParamCommandComment *C) {
731 Result << "<Parameter><Name>";
732 appendToResultWithXMLEscaping(C->isParamIndexValid()
733 ? C->getParamName(FC)
734 : C->getParamNameAsWritten());
737 if (C->isParamIndexValid()) {
738 if (C->isVarArgParam())
739 Result << "<IsVarArg />";
741 Result << "<Index>" << C->getParamIndex() << "</Index>";
744 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
745 switch (C->getDirection()) {
746 case ParamCommandComment::In:
749 case ParamCommandComment::Out:
752 case ParamCommandComment::InOut:
756 Result << "</Direction><Discussion>";
757 visit(C->getParagraph());
758 Result << "</Discussion></Parameter>";
761 void CommentASTToXMLConverter::visitTParamCommandComment(
762 const TParamCommandComment *C) {
763 Result << "<Parameter><Name>";
764 appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
765 : C->getParamNameAsWritten());
768 if (C->isPositionValid() && C->getDepth() == 1) {
769 Result << "<Index>" << C->getIndex(0) << "</Index>";
772 Result << "<Discussion>";
773 visit(C->getParagraph());
774 Result << "</Discussion></Parameter>";
777 void CommentASTToXMLConverter::visitVerbatimBlockComment(
778 const VerbatimBlockComment *C) {
779 unsigned NumLines = C->getNumLines();
783 switch (C->getCommandID()) {
784 case CommandTraits::KCI_code:
785 Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
788 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
791 for (unsigned i = 0; i != NumLines; ++i) {
792 appendToResultWithXMLEscaping(C->getText(i));
793 if (i + 1 != NumLines)
796 Result << "</Verbatim>";
799 void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
800 const VerbatimBlockLineComment *C) {
801 llvm_unreachable("should not see this AST node");
804 void CommentASTToXMLConverter::visitVerbatimLineComment(
805 const VerbatimLineComment *C) {
806 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
807 appendToResultWithXMLEscaping(C->getText());
808 Result << "</Verbatim>";
811 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
812 FullCommentParts Parts(C, Traits);
814 const DeclInfo *DI = C->getDeclInfo();
815 StringRef RootEndTag;
817 switch (DI->getKind()) {
818 case DeclInfo::OtherKind:
819 RootEndTag = "</Other>";
822 case DeclInfo::FunctionKind:
823 RootEndTag = "</Function>";
824 Result << "<Function";
825 switch (DI->TemplateKind) {
826 case DeclInfo::NotTemplate:
828 case DeclInfo::Template:
829 Result << " templateKind=\"template\"";
831 case DeclInfo::TemplateSpecialization:
832 Result << " templateKind=\"specialization\"";
834 case DeclInfo::TemplatePartialSpecialization:
835 llvm_unreachable("partial specializations of functions "
836 "are not allowed in C++");
838 if (DI->IsInstanceMethod)
839 Result << " isInstanceMethod=\"1\"";
840 if (DI->IsClassMethod)
841 Result << " isClassMethod=\"1\"";
843 case DeclInfo::ClassKind:
844 RootEndTag = "</Class>";
846 switch (DI->TemplateKind) {
847 case DeclInfo::NotTemplate:
849 case DeclInfo::Template:
850 Result << " templateKind=\"template\"";
852 case DeclInfo::TemplateSpecialization:
853 Result << " templateKind=\"specialization\"";
855 case DeclInfo::TemplatePartialSpecialization:
856 Result << " templateKind=\"partialSpecialization\"";
860 case DeclInfo::VariableKind:
861 RootEndTag = "</Variable>";
862 Result << "<Variable";
864 case DeclInfo::NamespaceKind:
865 RootEndTag = "</Namespace>";
866 Result << "<Namespace";
868 case DeclInfo::TypedefKind:
869 RootEndTag = "</Typedef>";
870 Result << "<Typedef";
872 case DeclInfo::EnumKind:
873 RootEndTag = "</Enum>";
879 // Print line and column number.
880 SourceLocation Loc = DI->CurrentDecl->getLocation();
881 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
882 FileID FID = LocInfo.first;
883 unsigned FileOffset = LocInfo.second;
886 if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
887 Result << " file=\"";
888 appendToResultWithXMLEscaping(FE->getName());
891 Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
892 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
897 // Finish the root tag.
900 bool FoundName = false;
901 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
902 if (DeclarationName DeclName = ND->getDeclName()) {
904 std::string Name = DeclName.getAsString();
905 appendToResultWithXMLEscaping(Name);
911 Result << "<Name><anonymous></Name>";
915 SmallString<128> USR;
916 generateUSRForDecl(DI->CommentDecl, USR);
919 appendToResultWithXMLEscaping(USR);
924 // No DeclInfo -- just emit some root tag and name tag.
925 RootEndTag = "</Other>";
926 Result << "<Other><Name>unknown</Name>";
929 if (Parts.Headerfile) {
930 Result << "<Headerfile>";
931 visit(Parts.Headerfile);
932 Result << "</Headerfile>";
936 // Pretty-print the declaration.
937 Result << "<Declaration>";
938 SmallString<128> Declaration;
939 getSourceTextOfDeclaration(DI, Declaration);
940 formatTextOfDeclaration(DI, Declaration);
941 appendToResultWithXMLEscaping(Declaration);
942 Result << "</Declaration>";
945 bool FirstParagraphIsBrief = false;
947 Result << "<Abstract>";
949 Result << "</Abstract>";
950 } else if (Parts.FirstParagraph) {
951 Result << "<Abstract>";
952 visit(Parts.FirstParagraph);
953 Result << "</Abstract>";
954 FirstParagraphIsBrief = true;
957 if (Parts.TParams.size() != 0) {
958 Result << "<TemplateParameters>";
959 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
960 visit(Parts.TParams[i]);
961 Result << "</TemplateParameters>";
964 if (Parts.Params.size() != 0) {
965 Result << "<Parameters>";
966 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
967 visit(Parts.Params[i]);
968 Result << "</Parameters>";
971 if (Parts.Exceptions.size() != 0) {
972 Result << "<Exceptions>";
973 for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
974 visit(Parts.Exceptions[i]);
975 Result << "</Exceptions>";
978 if (Parts.Returns.size() != 0) {
979 Result << "<ResultDiscussion>";
980 for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
981 visit(Parts.Returns[i]);
982 Result << "</ResultDiscussion>";
985 if (DI->CommentDecl->hasAttrs()) {
986 const AttrVec &Attrs = DI->CommentDecl->getAttrs();
987 for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
988 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
990 if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
991 if (DA->getMessage().empty())
992 Result << "<Deprecated/>";
994 Result << "<Deprecated>";
995 appendToResultWithXMLEscaping(DA->getMessage());
996 Result << "</Deprecated>";
999 else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1000 if (UA->getMessage().empty())
1001 Result << "<Unavailable/>";
1003 Result << "<Unavailable>";
1004 appendToResultWithXMLEscaping(UA->getMessage());
1005 Result << "</Unavailable>";
1011 // 'availability' attribute.
1012 Result << "<Availability";
1013 StringRef Distribution;
1014 if (AA->getPlatform()) {
1015 Distribution = AvailabilityAttr::getPrettyPlatformName(
1016 AA->getPlatform()->getName());
1017 if (Distribution.empty())
1018 Distribution = AA->getPlatform()->getName();
1020 Result << " distribution=\"" << Distribution << "\">";
1021 VersionTuple IntroducedInVersion = AA->getIntroduced();
1022 if (!IntroducedInVersion.empty()) {
1023 Result << "<IntroducedInVersion>"
1024 << IntroducedInVersion.getAsString()
1025 << "</IntroducedInVersion>";
1027 VersionTuple DeprecatedInVersion = AA->getDeprecated();
1028 if (!DeprecatedInVersion.empty()) {
1029 Result << "<DeprecatedInVersion>"
1030 << DeprecatedInVersion.getAsString()
1031 << "</DeprecatedInVersion>";
1033 VersionTuple RemovedAfterVersion = AA->getObsoleted();
1034 if (!RemovedAfterVersion.empty()) {
1035 Result << "<RemovedAfterVersion>"
1036 << RemovedAfterVersion.getAsString()
1037 << "</RemovedAfterVersion>";
1039 StringRef DeprecationSummary = AA->getMessage();
1040 if (!DeprecationSummary.empty()) {
1041 Result << "<DeprecationSummary>";
1042 appendToResultWithXMLEscaping(DeprecationSummary);
1043 Result << "</DeprecationSummary>";
1045 if (AA->getUnavailable())
1046 Result << "<Unavailable/>";
1047 Result << "</Availability>";
1052 bool StartTagEmitted = false;
1053 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1054 const Comment *C = Parts.MiscBlocks[i];
1055 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1057 if (!StartTagEmitted) {
1058 Result << "<Discussion>";
1059 StartTagEmitted = true;
1063 if (StartTagEmitted)
1064 Result << "</Discussion>";
1067 Result << RootEndTag;
1070 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1071 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1096 void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) {
1100 Result << "<![CDATA[";
1101 while (!S.empty()) {
1102 size_t Pos = S.find("]]>");
1104 Result << "]]]]><![CDATA[>";
1105 S = S.drop_front(3);
1108 if (Pos == StringRef::npos)
1111 Result << S.substr(0, Pos);
1113 S = S.drop_front(Pos);
1118 CommentToXMLConverter::CommentToXMLConverter() {}
1119 CommentToXMLConverter::~CommentToXMLConverter() {}
1121 void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC,
1122 SmallVectorImpl<char> &HTML,
1123 const ASTContext &Context) {
1124 CommentASTToHTMLConverter Converter(FC, HTML,
1125 Context.getCommentCommandTraits());
1126 Converter.visit(FC);
1129 void CommentToXMLConverter::convertHTMLTagNodeToText(
1130 const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text,
1131 const ASTContext &Context) {
1132 CommentASTToHTMLConverter Converter(nullptr, Text,
1133 Context.getCommentCommandTraits());
1134 Converter.visit(HTC);
1137 void CommentToXMLConverter::convertCommentToXML(const FullComment *FC,
1138 SmallVectorImpl<char> &XML,
1139 const ASTContext &Context) {
1140 CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
1141 Context.getSourceManager());
1142 Converter.visit(FC);