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 bool IncompleteFormat = false;
596 format::FormatStyle Style = format::getLLVMStyle();
597 Style.FixNamespaceComments = false;
598 tooling::Replacements Replaces =
599 reformat(Style, StringDecl, tooling::Range(Offset, Length), "xmldecl.xd",
601 auto FormattedStringDecl = applyAllReplacements(StringDecl, Replaces);
602 if (static_cast<bool>(FormattedStringDecl)) {
603 Declaration = *FormattedStringDecl;
607 } // end unnamed namespace
609 void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
610 appendToResultWithXMLEscaping(C->getText());
613 void CommentASTToXMLConverter::visitInlineCommandComment(
614 const InlineCommandComment *C) {
615 // Nothing to render if no arguments supplied.
616 if (C->getNumArgs() == 0)
619 // Nothing to render if argument is empty.
620 StringRef Arg0 = C->getArgText(0);
624 switch (C->getRenderKind()) {
625 case InlineCommandComment::RenderNormal:
626 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
627 appendToResultWithXMLEscaping(C->getArgText(i));
631 case InlineCommandComment::RenderBold:
632 assert(C->getNumArgs() == 1);
634 appendToResultWithXMLEscaping(Arg0);
637 case InlineCommandComment::RenderMonospaced:
638 assert(C->getNumArgs() == 1);
639 Result << "<monospaced>";
640 appendToResultWithXMLEscaping(Arg0);
641 Result << "</monospaced>";
643 case InlineCommandComment::RenderEmphasized:
644 assert(C->getNumArgs() == 1);
645 Result << "<emphasized>";
646 appendToResultWithXMLEscaping(Arg0);
647 Result << "</emphasized>";
652 void CommentASTToXMLConverter::visitHTMLStartTagComment(
653 const HTMLStartTagComment *C) {
654 Result << "<rawHTML";
655 if (C->isMalformed())
656 Result << " isMalformed=\"1\"";
661 llvm::raw_svector_ostream TagOS(Tag);
662 printHTMLStartTagComment(C, TagOS);
664 appendToResultWithCDATAEscaping(Tag);
666 Result << "</rawHTML>";
670 CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
671 Result << "<rawHTML";
672 if (C->isMalformed())
673 Result << " isMalformed=\"1\"";
674 Result << "></" << C->getTagName() << "></rawHTML>";
678 CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
679 appendParagraphCommentWithKind(C, StringRef());
682 void CommentASTToXMLConverter::appendParagraphCommentWithKind(
683 const ParagraphComment *C,
684 StringRef ParagraphKind) {
685 if (C->isWhitespace())
688 if (ParagraphKind.empty())
691 Result << "<Para kind=\"" << ParagraphKind << "\">";
693 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
700 void CommentASTToXMLConverter::visitBlockCommandComment(
701 const BlockCommandComment *C) {
702 StringRef ParagraphKind;
704 switch (C->getCommandID()) {
705 case CommandTraits::KCI_attention:
706 case CommandTraits::KCI_author:
707 case CommandTraits::KCI_authors:
708 case CommandTraits::KCI_bug:
709 case CommandTraits::KCI_copyright:
710 case CommandTraits::KCI_date:
711 case CommandTraits::KCI_invariant:
712 case CommandTraits::KCI_note:
713 case CommandTraits::KCI_post:
714 case CommandTraits::KCI_pre:
715 case CommandTraits::KCI_remark:
716 case CommandTraits::KCI_remarks:
717 case CommandTraits::KCI_sa:
718 case CommandTraits::KCI_see:
719 case CommandTraits::KCI_since:
720 case CommandTraits::KCI_todo:
721 case CommandTraits::KCI_version:
722 case CommandTraits::KCI_warning:
723 ParagraphKind = C->getCommandName(Traits);
728 appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
731 void CommentASTToXMLConverter::visitParamCommandComment(
732 const ParamCommandComment *C) {
733 Result << "<Parameter><Name>";
734 appendToResultWithXMLEscaping(C->isParamIndexValid()
735 ? C->getParamName(FC)
736 : C->getParamNameAsWritten());
739 if (C->isParamIndexValid()) {
740 if (C->isVarArgParam())
741 Result << "<IsVarArg />";
743 Result << "<Index>" << C->getParamIndex() << "</Index>";
746 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
747 switch (C->getDirection()) {
748 case ParamCommandComment::In:
751 case ParamCommandComment::Out:
754 case ParamCommandComment::InOut:
758 Result << "</Direction><Discussion>";
759 visit(C->getParagraph());
760 Result << "</Discussion></Parameter>";
763 void CommentASTToXMLConverter::visitTParamCommandComment(
764 const TParamCommandComment *C) {
765 Result << "<Parameter><Name>";
766 appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
767 : C->getParamNameAsWritten());
770 if (C->isPositionValid() && C->getDepth() == 1) {
771 Result << "<Index>" << C->getIndex(0) << "</Index>";
774 Result << "<Discussion>";
775 visit(C->getParagraph());
776 Result << "</Discussion></Parameter>";
779 void CommentASTToXMLConverter::visitVerbatimBlockComment(
780 const VerbatimBlockComment *C) {
781 unsigned NumLines = C->getNumLines();
785 switch (C->getCommandID()) {
786 case CommandTraits::KCI_code:
787 Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
790 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
793 for (unsigned i = 0; i != NumLines; ++i) {
794 appendToResultWithXMLEscaping(C->getText(i));
795 if (i + 1 != NumLines)
798 Result << "</Verbatim>";
801 void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
802 const VerbatimBlockLineComment *C) {
803 llvm_unreachable("should not see this AST node");
806 void CommentASTToXMLConverter::visitVerbatimLineComment(
807 const VerbatimLineComment *C) {
808 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
809 appendToResultWithXMLEscaping(C->getText());
810 Result << "</Verbatim>";
813 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
814 FullCommentParts Parts(C, Traits);
816 const DeclInfo *DI = C->getDeclInfo();
817 StringRef RootEndTag;
819 switch (DI->getKind()) {
820 case DeclInfo::OtherKind:
821 RootEndTag = "</Other>";
824 case DeclInfo::FunctionKind:
825 RootEndTag = "</Function>";
826 Result << "<Function";
827 switch (DI->TemplateKind) {
828 case DeclInfo::NotTemplate:
830 case DeclInfo::Template:
831 Result << " templateKind=\"template\"";
833 case DeclInfo::TemplateSpecialization:
834 Result << " templateKind=\"specialization\"";
836 case DeclInfo::TemplatePartialSpecialization:
837 llvm_unreachable("partial specializations of functions "
838 "are not allowed in C++");
840 if (DI->IsInstanceMethod)
841 Result << " isInstanceMethod=\"1\"";
842 if (DI->IsClassMethod)
843 Result << " isClassMethod=\"1\"";
845 case DeclInfo::ClassKind:
846 RootEndTag = "</Class>";
848 switch (DI->TemplateKind) {
849 case DeclInfo::NotTemplate:
851 case DeclInfo::Template:
852 Result << " templateKind=\"template\"";
854 case DeclInfo::TemplateSpecialization:
855 Result << " templateKind=\"specialization\"";
857 case DeclInfo::TemplatePartialSpecialization:
858 Result << " templateKind=\"partialSpecialization\"";
862 case DeclInfo::VariableKind:
863 RootEndTag = "</Variable>";
864 Result << "<Variable";
866 case DeclInfo::NamespaceKind:
867 RootEndTag = "</Namespace>";
868 Result << "<Namespace";
870 case DeclInfo::TypedefKind:
871 RootEndTag = "</Typedef>";
872 Result << "<Typedef";
874 case DeclInfo::EnumKind:
875 RootEndTag = "</Enum>";
881 // Print line and column number.
882 SourceLocation Loc = DI->CurrentDecl->getLocation();
883 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
884 FileID FID = LocInfo.first;
885 unsigned FileOffset = LocInfo.second;
888 if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
889 Result << " file=\"";
890 appendToResultWithXMLEscaping(FE->getName());
893 Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
894 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
899 // Finish the root tag.
902 bool FoundName = false;
903 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
904 if (DeclarationName DeclName = ND->getDeclName()) {
906 std::string Name = DeclName.getAsString();
907 appendToResultWithXMLEscaping(Name);
913 Result << "<Name><anonymous></Name>";
917 SmallString<128> USR;
918 generateUSRForDecl(DI->CommentDecl, USR);
921 appendToResultWithXMLEscaping(USR);
926 // No DeclInfo -- just emit some root tag and name tag.
927 RootEndTag = "</Other>";
928 Result << "<Other><Name>unknown</Name>";
931 if (Parts.Headerfile) {
932 Result << "<Headerfile>";
933 visit(Parts.Headerfile);
934 Result << "</Headerfile>";
938 // Pretty-print the declaration.
939 Result << "<Declaration>";
940 SmallString<128> Declaration;
941 getSourceTextOfDeclaration(DI, Declaration);
942 formatTextOfDeclaration(DI, Declaration);
943 appendToResultWithXMLEscaping(Declaration);
944 Result << "</Declaration>";
947 bool FirstParagraphIsBrief = false;
949 Result << "<Abstract>";
951 Result << "</Abstract>";
952 } else if (Parts.FirstParagraph) {
953 Result << "<Abstract>";
954 visit(Parts.FirstParagraph);
955 Result << "</Abstract>";
956 FirstParagraphIsBrief = true;
959 if (Parts.TParams.size() != 0) {
960 Result << "<TemplateParameters>";
961 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
962 visit(Parts.TParams[i]);
963 Result << "</TemplateParameters>";
966 if (Parts.Params.size() != 0) {
967 Result << "<Parameters>";
968 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
969 visit(Parts.Params[i]);
970 Result << "</Parameters>";
973 if (Parts.Exceptions.size() != 0) {
974 Result << "<Exceptions>";
975 for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
976 visit(Parts.Exceptions[i]);
977 Result << "</Exceptions>";
980 if (Parts.Returns.size() != 0) {
981 Result << "<ResultDiscussion>";
982 for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
983 visit(Parts.Returns[i]);
984 Result << "</ResultDiscussion>";
987 if (DI->CommentDecl->hasAttrs()) {
988 const AttrVec &Attrs = DI->CommentDecl->getAttrs();
989 for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
990 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
992 if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
993 if (DA->getMessage().empty())
994 Result << "<Deprecated/>";
996 Result << "<Deprecated>";
997 appendToResultWithXMLEscaping(DA->getMessage());
998 Result << "</Deprecated>";
1001 else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1002 if (UA->getMessage().empty())
1003 Result << "<Unavailable/>";
1005 Result << "<Unavailable>";
1006 appendToResultWithXMLEscaping(UA->getMessage());
1007 Result << "</Unavailable>";
1013 // 'availability' attribute.
1014 Result << "<Availability";
1015 StringRef Distribution;
1016 if (AA->getPlatform()) {
1017 Distribution = AvailabilityAttr::getPrettyPlatformName(
1018 AA->getPlatform()->getName());
1019 if (Distribution.empty())
1020 Distribution = AA->getPlatform()->getName();
1022 Result << " distribution=\"" << Distribution << "\">";
1023 VersionTuple IntroducedInVersion = AA->getIntroduced();
1024 if (!IntroducedInVersion.empty()) {
1025 Result << "<IntroducedInVersion>"
1026 << IntroducedInVersion.getAsString()
1027 << "</IntroducedInVersion>";
1029 VersionTuple DeprecatedInVersion = AA->getDeprecated();
1030 if (!DeprecatedInVersion.empty()) {
1031 Result << "<DeprecatedInVersion>"
1032 << DeprecatedInVersion.getAsString()
1033 << "</DeprecatedInVersion>";
1035 VersionTuple RemovedAfterVersion = AA->getObsoleted();
1036 if (!RemovedAfterVersion.empty()) {
1037 Result << "<RemovedAfterVersion>"
1038 << RemovedAfterVersion.getAsString()
1039 << "</RemovedAfterVersion>";
1041 StringRef DeprecationSummary = AA->getMessage();
1042 if (!DeprecationSummary.empty()) {
1043 Result << "<DeprecationSummary>";
1044 appendToResultWithXMLEscaping(DeprecationSummary);
1045 Result << "</DeprecationSummary>";
1047 if (AA->getUnavailable())
1048 Result << "<Unavailable/>";
1049 Result << "</Availability>";
1054 bool StartTagEmitted = false;
1055 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1056 const Comment *C = Parts.MiscBlocks[i];
1057 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1059 if (!StartTagEmitted) {
1060 Result << "<Discussion>";
1061 StartTagEmitted = true;
1065 if (StartTagEmitted)
1066 Result << "</Discussion>";
1069 Result << RootEndTag;
1072 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1073 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1098 void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) {
1102 Result << "<![CDATA[";
1103 while (!S.empty()) {
1104 size_t Pos = S.find("]]>");
1106 Result << "]]]]><![CDATA[>";
1107 S = S.drop_front(3);
1110 if (Pos == StringRef::npos)
1113 Result << S.substr(0, Pos);
1115 S = S.drop_front(Pos);
1120 CommentToXMLConverter::CommentToXMLConverter() {}
1121 CommentToXMLConverter::~CommentToXMLConverter() {}
1123 void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC,
1124 SmallVectorImpl<char> &HTML,
1125 const ASTContext &Context) {
1126 CommentASTToHTMLConverter Converter(FC, HTML,
1127 Context.getCommentCommandTraits());
1128 Converter.visit(FC);
1131 void CommentToXMLConverter::convertHTMLTagNodeToText(
1132 const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text,
1133 const ASTContext &Context) {
1134 CommentASTToHTMLConverter Converter(nullptr, Text,
1135 Context.getCommentCommandTraits());
1136 Converter.visit(HTC);
1139 void CommentToXMLConverter::convertCommentToXML(const FullComment *FC,
1140 SmallVectorImpl<char> &XML,
1141 const ASTContext &Context) {
1142 CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
1143 Context.getSourceManager());
1144 Converter.visit(FC);