1 //===--- CommentToXML.cpp - Convert comments to XML representation --------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "clang/Index/CommentToXML.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/Attr.h"
12 #include "clang/AST/Comment.h"
13 #include "clang/AST/CommentVisitor.h"
14 #include "clang/Basic/FileManager.h"
15 #include "clang/Basic/SourceManager.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 llvm::stable_sort(Params
, ParamCommandCommentCompareIndex());
194 llvm::stable_sort(TParams
, TParamCommandCommentComparePosition());
197 void printHTMLStartTagComment(const HTMLStartTagComment
*C
,
198 llvm::raw_svector_ostream
&Result
) {
199 Result
<< "<" << C
->getTagName();
201 if (C
->getNumAttrs() != 0) {
202 for (unsigned i
= 0, e
= C
->getNumAttrs(); i
!= e
; i
++) {
204 const HTMLStartTagComment::Attribute
&Attr
= C
->getAttr(i
);
206 if (!Attr
.Value
.empty())
207 Result
<< "=\"" << Attr
.Value
<< "\"";
211 if (!C
->isSelfClosing())
217 class CommentASTToHTMLConverter
:
218 public ConstCommentVisitor
<CommentASTToHTMLConverter
> {
220 /// \param Str accumulator for HTML.
221 CommentASTToHTMLConverter(const FullComment
*FC
,
222 SmallVectorImpl
<char> &Str
,
223 const CommandTraits
&Traits
) :
224 FC(FC
), Result(Str
), Traits(Traits
)
228 void visitTextComment(const TextComment
*C
);
229 void visitInlineCommandComment(const InlineCommandComment
*C
);
230 void visitHTMLStartTagComment(const HTMLStartTagComment
*C
);
231 void visitHTMLEndTagComment(const HTMLEndTagComment
*C
);
234 void visitParagraphComment(const ParagraphComment
*C
);
235 void visitBlockCommandComment(const BlockCommandComment
*C
);
236 void visitParamCommandComment(const ParamCommandComment
*C
);
237 void visitTParamCommandComment(const TParamCommandComment
*C
);
238 void visitVerbatimBlockComment(const VerbatimBlockComment
*C
);
239 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment
*C
);
240 void visitVerbatimLineComment(const VerbatimLineComment
*C
);
242 void visitFullComment(const FullComment
*C
);
246 /// Convert a paragraph that is not a block by itself (an argument to some
248 void visitNonStandaloneParagraphComment(const ParagraphComment
*C
);
250 void appendToResultWithHTMLEscaping(StringRef S
);
253 const FullComment
*FC
;
254 /// Output stream for HTML.
255 llvm::raw_svector_ostream Result
;
257 const CommandTraits
&Traits
;
259 } // end unnamed namespace
261 void CommentASTToHTMLConverter::visitTextComment(const TextComment
*C
) {
262 appendToResultWithHTMLEscaping(C
->getText());
265 void CommentASTToHTMLConverter::visitInlineCommandComment(
266 const InlineCommandComment
*C
) {
267 // Nothing to render if no arguments supplied.
268 if (C
->getNumArgs() == 0)
271 // Nothing to render if argument is empty.
272 StringRef Arg0
= C
->getArgText(0);
276 switch (C
->getRenderKind()) {
277 case InlineCommandComment::RenderNormal
:
278 for (unsigned i
= 0, e
= C
->getNumArgs(); i
!= e
; ++i
) {
279 appendToResultWithHTMLEscaping(C
->getArgText(i
));
284 case InlineCommandComment::RenderBold
:
285 assert(C
->getNumArgs() == 1);
287 appendToResultWithHTMLEscaping(Arg0
);
290 case InlineCommandComment::RenderMonospaced
:
291 assert(C
->getNumArgs() == 1);
293 appendToResultWithHTMLEscaping(Arg0
);
296 case InlineCommandComment::RenderEmphasized
:
297 assert(C
->getNumArgs() == 1);
299 appendToResultWithHTMLEscaping(Arg0
);
302 case InlineCommandComment::RenderAnchor
:
303 assert(C
->getNumArgs() == 1);
304 Result
<< "<span id=\"" << Arg0
<< "\"></span>";
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
]);
487 void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
488 const ParagraphComment
*C
) {
492 for (Comment::child_iterator I
= C
->child_begin(), E
= C
->child_end();
498 void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S
) {
499 for (StringRef::iterator I
= S
.begin(), E
= S
.end(); I
!= E
; ++I
) {
528 class CommentASTToXMLConverter
:
529 public ConstCommentVisitor
<CommentASTToXMLConverter
> {
531 /// \param Str accumulator for XML.
532 CommentASTToXMLConverter(const FullComment
*FC
,
533 SmallVectorImpl
<char> &Str
,
534 const CommandTraits
&Traits
,
535 const SourceManager
&SM
) :
536 FC(FC
), Result(Str
), Traits(Traits
), SM(SM
) { }
539 void visitTextComment(const TextComment
*C
);
540 void visitInlineCommandComment(const InlineCommandComment
*C
);
541 void visitHTMLStartTagComment(const HTMLStartTagComment
*C
);
542 void visitHTMLEndTagComment(const HTMLEndTagComment
*C
);
545 void visitParagraphComment(const ParagraphComment
*C
);
547 void appendParagraphCommentWithKind(const ParagraphComment
*C
,
550 void visitBlockCommandComment(const BlockCommandComment
*C
);
551 void visitParamCommandComment(const ParamCommandComment
*C
);
552 void visitTParamCommandComment(const TParamCommandComment
*C
);
553 void visitVerbatimBlockComment(const VerbatimBlockComment
*C
);
554 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment
*C
);
555 void visitVerbatimLineComment(const VerbatimLineComment
*C
);
557 void visitFullComment(const FullComment
*C
);
560 void appendToResultWithXMLEscaping(StringRef S
);
561 void appendToResultWithCDATAEscaping(StringRef S
);
563 void formatTextOfDeclaration(const DeclInfo
*DI
,
564 SmallString
<128> &Declaration
);
567 const FullComment
*FC
;
569 /// Output stream for XML.
570 llvm::raw_svector_ostream Result
;
572 const CommandTraits
&Traits
;
573 const SourceManager
&SM
;
576 void getSourceTextOfDeclaration(const DeclInfo
*ThisDecl
,
577 SmallVectorImpl
<char> &Str
) {
578 ASTContext
&Context
= ThisDecl
->CurrentDecl
->getASTContext();
579 const LangOptions
&LangOpts
= Context
.getLangOpts();
580 llvm::raw_svector_ostream
OS(Str
);
581 PrintingPolicy
PPolicy(LangOpts
);
582 PPolicy
.PolishForDeclaration
= true;
583 PPolicy
.TerseOutput
= true;
584 PPolicy
.ConstantsAsWritten
= true;
585 ThisDecl
->CurrentDecl
->print(OS
, PPolicy
,
586 /*Indentation*/0, /*PrintInstantiation*/false);
589 void CommentASTToXMLConverter::formatTextOfDeclaration(
590 const DeclInfo
*DI
, SmallString
<128> &Declaration
) {
591 // Formatting API expects null terminated input string.
592 StringRef
StringDecl(Declaration
.c_str(), Declaration
.size());
594 // Formatter specific code.
596 unsigned Length
= Declaration
.size();
598 format::FormatStyle Style
= format::getLLVMStyle();
599 Style
.FixNamespaceComments
= false;
600 tooling::Replacements Replaces
=
601 reformat(Style
, StringDecl
, tooling::Range(Offset
, Length
), "xmldecl.xd");
602 auto FormattedStringDecl
= applyAllReplacements(StringDecl
, Replaces
);
603 if (static_cast<bool>(FormattedStringDecl
)) {
604 Declaration
= *FormattedStringDecl
;
608 } // end unnamed namespace
610 void CommentASTToXMLConverter::visitTextComment(const TextComment
*C
) {
611 appendToResultWithXMLEscaping(C
->getText());
614 void CommentASTToXMLConverter::visitInlineCommandComment(
615 const InlineCommandComment
*C
) {
616 // Nothing to render if no arguments supplied.
617 if (C
->getNumArgs() == 0)
620 // Nothing to render if argument is empty.
621 StringRef Arg0
= C
->getArgText(0);
625 switch (C
->getRenderKind()) {
626 case InlineCommandComment::RenderNormal
:
627 for (unsigned i
= 0, e
= C
->getNumArgs(); i
!= e
; ++i
) {
628 appendToResultWithXMLEscaping(C
->getArgText(i
));
632 case InlineCommandComment::RenderBold
:
633 assert(C
->getNumArgs() == 1);
635 appendToResultWithXMLEscaping(Arg0
);
638 case InlineCommandComment::RenderMonospaced
:
639 assert(C
->getNumArgs() == 1);
640 Result
<< "<monospaced>";
641 appendToResultWithXMLEscaping(Arg0
);
642 Result
<< "</monospaced>";
644 case InlineCommandComment::RenderEmphasized
:
645 assert(C
->getNumArgs() == 1);
646 Result
<< "<emphasized>";
647 appendToResultWithXMLEscaping(Arg0
);
648 Result
<< "</emphasized>";
650 case InlineCommandComment::RenderAnchor
:
651 assert(C
->getNumArgs() == 1);
652 Result
<< "<anchor id=\"" << Arg0
<< "\"></anchor>";
657 void CommentASTToXMLConverter::visitHTMLStartTagComment(
658 const HTMLStartTagComment
*C
) {
659 Result
<< "<rawHTML";
660 if (C
->isMalformed())
661 Result
<< " isMalformed=\"1\"";
666 llvm::raw_svector_ostream
TagOS(Tag
);
667 printHTMLStartTagComment(C
, TagOS
);
669 appendToResultWithCDATAEscaping(Tag
);
671 Result
<< "</rawHTML>";
675 CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment
*C
) {
676 Result
<< "<rawHTML";
677 if (C
->isMalformed())
678 Result
<< " isMalformed=\"1\"";
679 Result
<< "></" << C
->getTagName() << "></rawHTML>";
683 CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment
*C
) {
684 appendParagraphCommentWithKind(C
, StringRef());
687 void CommentASTToXMLConverter::appendParagraphCommentWithKind(
688 const ParagraphComment
*C
,
689 StringRef ParagraphKind
) {
690 if (C
->isWhitespace())
693 if (ParagraphKind
.empty())
696 Result
<< "<Para kind=\"" << ParagraphKind
<< "\">";
698 for (Comment::child_iterator I
= C
->child_begin(), E
= C
->child_end();
705 void CommentASTToXMLConverter::visitBlockCommandComment(
706 const BlockCommandComment
*C
) {
707 StringRef ParagraphKind
;
709 switch (C
->getCommandID()) {
710 case CommandTraits::KCI_attention
:
711 case CommandTraits::KCI_author
:
712 case CommandTraits::KCI_authors
:
713 case CommandTraits::KCI_bug
:
714 case CommandTraits::KCI_copyright
:
715 case CommandTraits::KCI_date
:
716 case CommandTraits::KCI_invariant
:
717 case CommandTraits::KCI_note
:
718 case CommandTraits::KCI_post
:
719 case CommandTraits::KCI_pre
:
720 case CommandTraits::KCI_remark
:
721 case CommandTraits::KCI_remarks
:
722 case CommandTraits::KCI_sa
:
723 case CommandTraits::KCI_see
:
724 case CommandTraits::KCI_since
:
725 case CommandTraits::KCI_todo
:
726 case CommandTraits::KCI_version
:
727 case CommandTraits::KCI_warning
:
728 ParagraphKind
= C
->getCommandName(Traits
);
734 appendParagraphCommentWithKind(C
->getParagraph(), ParagraphKind
);
737 void CommentASTToXMLConverter::visitParamCommandComment(
738 const ParamCommandComment
*C
) {
739 Result
<< "<Parameter><Name>";
740 appendToResultWithXMLEscaping(C
->isParamIndexValid()
741 ? C
->getParamName(FC
)
742 : C
->getParamNameAsWritten());
745 if (C
->isParamIndexValid()) {
746 if (C
->isVarArgParam())
747 Result
<< "<IsVarArg />";
749 Result
<< "<Index>" << C
->getParamIndex() << "</Index>";
752 Result
<< "<Direction isExplicit=\"" << C
->isDirectionExplicit() << "\">";
753 switch (C
->getDirection()) {
754 case ParamCommandComment::In
:
757 case ParamCommandComment::Out
:
760 case ParamCommandComment::InOut
:
764 Result
<< "</Direction><Discussion>";
765 visit(C
->getParagraph());
766 Result
<< "</Discussion></Parameter>";
769 void CommentASTToXMLConverter::visitTParamCommandComment(
770 const TParamCommandComment
*C
) {
771 Result
<< "<Parameter><Name>";
772 appendToResultWithXMLEscaping(C
->isPositionValid() ? C
->getParamName(FC
)
773 : C
->getParamNameAsWritten());
776 if (C
->isPositionValid() && C
->getDepth() == 1) {
777 Result
<< "<Index>" << C
->getIndex(0) << "</Index>";
780 Result
<< "<Discussion>";
781 visit(C
->getParagraph());
782 Result
<< "</Discussion></Parameter>";
785 void CommentASTToXMLConverter::visitVerbatimBlockComment(
786 const VerbatimBlockComment
*C
) {
787 unsigned NumLines
= C
->getNumLines();
791 switch (C
->getCommandID()) {
792 case CommandTraits::KCI_code
:
793 Result
<< "<Verbatim xml:space=\"preserve\" kind=\"code\">";
796 Result
<< "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
799 for (unsigned i
= 0; i
!= NumLines
; ++i
) {
800 appendToResultWithXMLEscaping(C
->getText(i
));
801 if (i
+ 1 != NumLines
)
804 Result
<< "</Verbatim>";
807 void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
808 const VerbatimBlockLineComment
*C
) {
809 llvm_unreachable("should not see this AST node");
812 void CommentASTToXMLConverter::visitVerbatimLineComment(
813 const VerbatimLineComment
*C
) {
814 Result
<< "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
815 appendToResultWithXMLEscaping(C
->getText());
816 Result
<< "</Verbatim>";
819 void CommentASTToXMLConverter::visitFullComment(const FullComment
*C
) {
820 FullCommentParts
Parts(C
, Traits
);
822 const DeclInfo
*DI
= C
->getDeclInfo();
823 StringRef RootEndTag
;
825 switch (DI
->getKind()) {
826 case DeclInfo::OtherKind
:
827 RootEndTag
= "</Other>";
830 case DeclInfo::FunctionKind
:
831 RootEndTag
= "</Function>";
832 Result
<< "<Function";
833 switch (DI
->TemplateKind
) {
834 case DeclInfo::NotTemplate
:
836 case DeclInfo::Template
:
837 Result
<< " templateKind=\"template\"";
839 case DeclInfo::TemplateSpecialization
:
840 Result
<< " templateKind=\"specialization\"";
842 case DeclInfo::TemplatePartialSpecialization
:
843 llvm_unreachable("partial specializations of functions "
844 "are not allowed in C++");
846 if (DI
->IsInstanceMethod
)
847 Result
<< " isInstanceMethod=\"1\"";
848 if (DI
->IsClassMethod
)
849 Result
<< " isClassMethod=\"1\"";
851 case DeclInfo::ClassKind
:
852 RootEndTag
= "</Class>";
854 switch (DI
->TemplateKind
) {
855 case DeclInfo::NotTemplate
:
857 case DeclInfo::Template
:
858 Result
<< " templateKind=\"template\"";
860 case DeclInfo::TemplateSpecialization
:
861 Result
<< " templateKind=\"specialization\"";
863 case DeclInfo::TemplatePartialSpecialization
:
864 Result
<< " templateKind=\"partialSpecialization\"";
868 case DeclInfo::VariableKind
:
869 RootEndTag
= "</Variable>";
870 Result
<< "<Variable";
872 case DeclInfo::NamespaceKind
:
873 RootEndTag
= "</Namespace>";
874 Result
<< "<Namespace";
876 case DeclInfo::TypedefKind
:
877 RootEndTag
= "</Typedef>";
878 Result
<< "<Typedef";
880 case DeclInfo::EnumKind
:
881 RootEndTag
= "</Enum>";
887 // Print line and column number.
888 SourceLocation Loc
= DI
->CurrentDecl
->getLocation();
889 std::pair
<FileID
, unsigned> LocInfo
= SM
.getDecomposedLoc(Loc
);
890 FileID FID
= LocInfo
.first
;
891 unsigned FileOffset
= LocInfo
.second
;
894 if (const FileEntry
*FE
= SM
.getFileEntryForID(FID
)) {
895 Result
<< " file=\"";
896 appendToResultWithXMLEscaping(FE
->getName());
899 Result
<< " line=\"" << SM
.getLineNumber(FID
, FileOffset
)
900 << "\" column=\"" << SM
.getColumnNumber(FID
, FileOffset
)
905 // Finish the root tag.
908 bool FoundName
= false;
909 if (const NamedDecl
*ND
= dyn_cast
<NamedDecl
>(DI
->CommentDecl
)) {
910 if (DeclarationName DeclName
= ND
->getDeclName()) {
912 std::string Name
= DeclName
.getAsString();
913 appendToResultWithXMLEscaping(Name
);
919 Result
<< "<Name><anonymous></Name>";
923 SmallString
<128> USR
;
924 generateUSRForDecl(DI
->CommentDecl
, USR
);
927 appendToResultWithXMLEscaping(USR
);
932 // No DeclInfo -- just emit some root tag and name tag.
933 RootEndTag
= "</Other>";
934 Result
<< "<Other><Name>unknown</Name>";
937 if (Parts
.Headerfile
) {
938 Result
<< "<Headerfile>";
939 visit(Parts
.Headerfile
);
940 Result
<< "</Headerfile>";
944 // Pretty-print the declaration.
945 Result
<< "<Declaration>";
946 SmallString
<128> Declaration
;
947 getSourceTextOfDeclaration(DI
, Declaration
);
948 formatTextOfDeclaration(DI
, Declaration
);
949 appendToResultWithXMLEscaping(Declaration
);
950 Result
<< "</Declaration>";
953 bool FirstParagraphIsBrief
= false;
955 Result
<< "<Abstract>";
957 Result
<< "</Abstract>";
958 } else if (Parts
.FirstParagraph
) {
959 Result
<< "<Abstract>";
960 visit(Parts
.FirstParagraph
);
961 Result
<< "</Abstract>";
962 FirstParagraphIsBrief
= true;
965 if (Parts
.TParams
.size() != 0) {
966 Result
<< "<TemplateParameters>";
967 for (unsigned i
= 0, e
= Parts
.TParams
.size(); i
!= e
; ++i
)
968 visit(Parts
.TParams
[i
]);
969 Result
<< "</TemplateParameters>";
972 if (Parts
.Params
.size() != 0) {
973 Result
<< "<Parameters>";
974 for (unsigned i
= 0, e
= Parts
.Params
.size(); i
!= e
; ++i
)
975 visit(Parts
.Params
[i
]);
976 Result
<< "</Parameters>";
979 if (Parts
.Exceptions
.size() != 0) {
980 Result
<< "<Exceptions>";
981 for (unsigned i
= 0, e
= Parts
.Exceptions
.size(); i
!= e
; ++i
)
982 visit(Parts
.Exceptions
[i
]);
983 Result
<< "</Exceptions>";
986 if (Parts
.Returns
.size() != 0) {
987 Result
<< "<ResultDiscussion>";
988 for (unsigned i
= 0, e
= Parts
.Returns
.size(); i
!= e
; ++i
)
989 visit(Parts
.Returns
[i
]);
990 Result
<< "</ResultDiscussion>";
993 if (DI
->CommentDecl
->hasAttrs()) {
994 const AttrVec
&Attrs
= DI
->CommentDecl
->getAttrs();
995 for (unsigned i
= 0, e
= Attrs
.size(); i
!= e
; i
++) {
996 const AvailabilityAttr
*AA
= dyn_cast
<AvailabilityAttr
>(Attrs
[i
]);
998 if (const DeprecatedAttr
*DA
= dyn_cast
<DeprecatedAttr
>(Attrs
[i
])) {
999 if (DA
->getMessage().empty())
1000 Result
<< "<Deprecated/>";
1002 Result
<< "<Deprecated>";
1003 appendToResultWithXMLEscaping(DA
->getMessage());
1004 Result
<< "</Deprecated>";
1007 else if (const UnavailableAttr
*UA
= dyn_cast
<UnavailableAttr
>(Attrs
[i
])) {
1008 if (UA
->getMessage().empty())
1009 Result
<< "<Unavailable/>";
1011 Result
<< "<Unavailable>";
1012 appendToResultWithXMLEscaping(UA
->getMessage());
1013 Result
<< "</Unavailable>";
1019 // 'availability' attribute.
1020 Result
<< "<Availability";
1021 StringRef Distribution
;
1022 if (AA
->getPlatform()) {
1023 Distribution
= AvailabilityAttr::getPrettyPlatformName(
1024 AA
->getPlatform()->getName());
1025 if (Distribution
.empty())
1026 Distribution
= AA
->getPlatform()->getName();
1028 Result
<< " distribution=\"" << Distribution
<< "\">";
1029 VersionTuple IntroducedInVersion
= AA
->getIntroduced();
1030 if (!IntroducedInVersion
.empty()) {
1031 Result
<< "<IntroducedInVersion>"
1032 << IntroducedInVersion
.getAsString()
1033 << "</IntroducedInVersion>";
1035 VersionTuple DeprecatedInVersion
= AA
->getDeprecated();
1036 if (!DeprecatedInVersion
.empty()) {
1037 Result
<< "<DeprecatedInVersion>"
1038 << DeprecatedInVersion
.getAsString()
1039 << "</DeprecatedInVersion>";
1041 VersionTuple RemovedAfterVersion
= AA
->getObsoleted();
1042 if (!RemovedAfterVersion
.empty()) {
1043 Result
<< "<RemovedAfterVersion>"
1044 << RemovedAfterVersion
.getAsString()
1045 << "</RemovedAfterVersion>";
1047 StringRef DeprecationSummary
= AA
->getMessage();
1048 if (!DeprecationSummary
.empty()) {
1049 Result
<< "<DeprecationSummary>";
1050 appendToResultWithXMLEscaping(DeprecationSummary
);
1051 Result
<< "</DeprecationSummary>";
1053 if (AA
->getUnavailable())
1054 Result
<< "<Unavailable/>";
1055 Result
<< "</Availability>";
1060 bool StartTagEmitted
= false;
1061 for (unsigned i
= 0, e
= Parts
.MiscBlocks
.size(); i
!= e
; ++i
) {
1062 const Comment
*C
= Parts
.MiscBlocks
[i
];
1063 if (FirstParagraphIsBrief
&& C
== Parts
.FirstParagraph
)
1065 if (!StartTagEmitted
) {
1066 Result
<< "<Discussion>";
1067 StartTagEmitted
= true;
1071 if (StartTagEmitted
)
1072 Result
<< "</Discussion>";
1075 Result
<< RootEndTag
;
1078 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S
) {
1079 for (StringRef::iterator I
= S
.begin(), E
= S
.end(); I
!= E
; ++I
) {
1104 void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S
) {
1108 Result
<< "<![CDATA[";
1109 while (!S
.empty()) {
1110 size_t Pos
= S
.find("]]>");
1112 Result
<< "]]]]><![CDATA[>";
1113 S
= S
.drop_front(3);
1116 if (Pos
== StringRef::npos
)
1119 Result
<< S
.substr(0, Pos
);
1121 S
= S
.drop_front(Pos
);
1126 CommentToXMLConverter::CommentToXMLConverter() {}
1127 CommentToXMLConverter::~CommentToXMLConverter() {}
1129 void CommentToXMLConverter::convertCommentToHTML(const FullComment
*FC
,
1130 SmallVectorImpl
<char> &HTML
,
1131 const ASTContext
&Context
) {
1132 CommentASTToHTMLConverter
Converter(FC
, HTML
,
1133 Context
.getCommentCommandTraits());
1134 Converter
.visit(FC
);
1137 void CommentToXMLConverter::convertHTMLTagNodeToText(
1138 const comments::HTMLTagComment
*HTC
, SmallVectorImpl
<char> &Text
,
1139 const ASTContext
&Context
) {
1140 CommentASTToHTMLConverter
Converter(nullptr, Text
,
1141 Context
.getCommentCommandTraits());
1142 Converter
.visit(HTC
);
1145 void CommentToXMLConverter::convertCommentToXML(const FullComment
*FC
,
1146 SmallVectorImpl
<char> &XML
,
1147 const ASTContext
&Context
) {
1148 CommentASTToXMLConverter
Converter(FC
, XML
, Context
.getCommentCommandTraits(),
1149 Context
.getSourceManager());
1150 Converter
.visit(FC
);