17330f729Sjoerg //===--- CommentToXML.cpp - Convert comments to XML representation --------===//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg
97330f729Sjoerg #include "clang/Index/CommentToXML.h"
107330f729Sjoerg #include "clang/AST/ASTContext.h"
117330f729Sjoerg #include "clang/AST/Attr.h"
127330f729Sjoerg #include "clang/AST/Comment.h"
137330f729Sjoerg #include "clang/AST/CommentVisitor.h"
14*e038c9c4Sjoerg #include "clang/Basic/FileManager.h"
15*e038c9c4Sjoerg #include "clang/Basic/SourceManager.h"
167330f729Sjoerg #include "clang/Format/Format.h"
177330f729Sjoerg #include "clang/Index/USRGeneration.h"
187330f729Sjoerg #include "llvm/ADT/StringExtras.h"
197330f729Sjoerg #include "llvm/ADT/TinyPtrVector.h"
207330f729Sjoerg #include "llvm/Support/raw_ostream.h"
217330f729Sjoerg
227330f729Sjoerg using namespace clang;
237330f729Sjoerg using namespace clang::comments;
247330f729Sjoerg using namespace clang::index;
257330f729Sjoerg
267330f729Sjoerg namespace {
277330f729Sjoerg
287330f729Sjoerg /// This comparison will sort parameters with valid index by index, then vararg
297330f729Sjoerg /// parameters, and invalid (unresolved) parameters last.
307330f729Sjoerg class ParamCommandCommentCompareIndex {
317330f729Sjoerg public:
operator ()(const ParamCommandComment * LHS,const ParamCommandComment * RHS) const327330f729Sjoerg bool operator()(const ParamCommandComment *LHS,
337330f729Sjoerg const ParamCommandComment *RHS) const {
347330f729Sjoerg unsigned LHSIndex = UINT_MAX;
357330f729Sjoerg unsigned RHSIndex = UINT_MAX;
367330f729Sjoerg
377330f729Sjoerg if (LHS->isParamIndexValid()) {
387330f729Sjoerg if (LHS->isVarArgParam())
397330f729Sjoerg LHSIndex = UINT_MAX - 1;
407330f729Sjoerg else
417330f729Sjoerg LHSIndex = LHS->getParamIndex();
427330f729Sjoerg }
437330f729Sjoerg if (RHS->isParamIndexValid()) {
447330f729Sjoerg if (RHS->isVarArgParam())
457330f729Sjoerg RHSIndex = UINT_MAX - 1;
467330f729Sjoerg else
477330f729Sjoerg RHSIndex = RHS->getParamIndex();
487330f729Sjoerg }
497330f729Sjoerg return LHSIndex < RHSIndex;
507330f729Sjoerg }
517330f729Sjoerg };
527330f729Sjoerg
537330f729Sjoerg /// This comparison will sort template parameters in the following order:
547330f729Sjoerg /// \li real template parameters (depth = 1) in index order;
557330f729Sjoerg /// \li all other names (depth > 1);
567330f729Sjoerg /// \li unresolved names.
577330f729Sjoerg class TParamCommandCommentComparePosition {
587330f729Sjoerg public:
operator ()(const TParamCommandComment * LHS,const TParamCommandComment * RHS) const597330f729Sjoerg bool operator()(const TParamCommandComment *LHS,
607330f729Sjoerg const TParamCommandComment *RHS) const {
617330f729Sjoerg // Sort unresolved names last.
627330f729Sjoerg if (!LHS->isPositionValid())
637330f729Sjoerg return false;
647330f729Sjoerg if (!RHS->isPositionValid())
657330f729Sjoerg return true;
667330f729Sjoerg
677330f729Sjoerg if (LHS->getDepth() > 1)
687330f729Sjoerg return false;
697330f729Sjoerg if (RHS->getDepth() > 1)
707330f729Sjoerg return true;
717330f729Sjoerg
727330f729Sjoerg // Sort template parameters in index order.
737330f729Sjoerg if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
747330f729Sjoerg return LHS->getIndex(0) < RHS->getIndex(0);
757330f729Sjoerg
767330f729Sjoerg // Leave all other names in source order.
777330f729Sjoerg return true;
787330f729Sjoerg }
797330f729Sjoerg };
807330f729Sjoerg
817330f729Sjoerg /// Separate parts of a FullComment.
827330f729Sjoerg struct FullCommentParts {
837330f729Sjoerg /// Take a full comment apart and initialize members accordingly.
847330f729Sjoerg FullCommentParts(const FullComment *C,
857330f729Sjoerg const CommandTraits &Traits);
867330f729Sjoerg
877330f729Sjoerg const BlockContentComment *Brief;
887330f729Sjoerg const BlockContentComment *Headerfile;
897330f729Sjoerg const ParagraphComment *FirstParagraph;
907330f729Sjoerg SmallVector<const BlockCommandComment *, 4> Returns;
917330f729Sjoerg SmallVector<const ParamCommandComment *, 8> Params;
927330f729Sjoerg SmallVector<const TParamCommandComment *, 4> TParams;
937330f729Sjoerg llvm::TinyPtrVector<const BlockCommandComment *> Exceptions;
947330f729Sjoerg SmallVector<const BlockContentComment *, 8> MiscBlocks;
957330f729Sjoerg };
967330f729Sjoerg
FullCommentParts(const FullComment * C,const CommandTraits & Traits)977330f729Sjoerg FullCommentParts::FullCommentParts(const FullComment *C,
987330f729Sjoerg const CommandTraits &Traits) :
997330f729Sjoerg Brief(nullptr), Headerfile(nullptr), FirstParagraph(nullptr) {
1007330f729Sjoerg for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
1017330f729Sjoerg I != E; ++I) {
1027330f729Sjoerg const Comment *Child = *I;
1037330f729Sjoerg if (!Child)
1047330f729Sjoerg continue;
1057330f729Sjoerg switch (Child->getCommentKind()) {
1067330f729Sjoerg case Comment::NoCommentKind:
1077330f729Sjoerg continue;
1087330f729Sjoerg
1097330f729Sjoerg case Comment::ParagraphCommentKind: {
1107330f729Sjoerg const ParagraphComment *PC = cast<ParagraphComment>(Child);
1117330f729Sjoerg if (PC->isWhitespace())
1127330f729Sjoerg break;
1137330f729Sjoerg if (!FirstParagraph)
1147330f729Sjoerg FirstParagraph = PC;
1157330f729Sjoerg
1167330f729Sjoerg MiscBlocks.push_back(PC);
1177330f729Sjoerg break;
1187330f729Sjoerg }
1197330f729Sjoerg
1207330f729Sjoerg case Comment::BlockCommandCommentKind: {
1217330f729Sjoerg const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
1227330f729Sjoerg const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
1237330f729Sjoerg if (!Brief && Info->IsBriefCommand) {
1247330f729Sjoerg Brief = BCC;
1257330f729Sjoerg break;
1267330f729Sjoerg }
1277330f729Sjoerg if (!Headerfile && Info->IsHeaderfileCommand) {
1287330f729Sjoerg Headerfile = BCC;
1297330f729Sjoerg break;
1307330f729Sjoerg }
1317330f729Sjoerg if (Info->IsReturnsCommand) {
1327330f729Sjoerg Returns.push_back(BCC);
1337330f729Sjoerg break;
1347330f729Sjoerg }
1357330f729Sjoerg if (Info->IsThrowsCommand) {
1367330f729Sjoerg Exceptions.push_back(BCC);
1377330f729Sjoerg break;
1387330f729Sjoerg }
1397330f729Sjoerg MiscBlocks.push_back(BCC);
1407330f729Sjoerg break;
1417330f729Sjoerg }
1427330f729Sjoerg
1437330f729Sjoerg case Comment::ParamCommandCommentKind: {
1447330f729Sjoerg const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
1457330f729Sjoerg if (!PCC->hasParamName())
1467330f729Sjoerg break;
1477330f729Sjoerg
1487330f729Sjoerg if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
1497330f729Sjoerg break;
1507330f729Sjoerg
1517330f729Sjoerg Params.push_back(PCC);
1527330f729Sjoerg break;
1537330f729Sjoerg }
1547330f729Sjoerg
1557330f729Sjoerg case Comment::TParamCommandCommentKind: {
1567330f729Sjoerg const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
1577330f729Sjoerg if (!TPCC->hasParamName())
1587330f729Sjoerg break;
1597330f729Sjoerg
1607330f729Sjoerg if (!TPCC->hasNonWhitespaceParagraph())
1617330f729Sjoerg break;
1627330f729Sjoerg
1637330f729Sjoerg TParams.push_back(TPCC);
1647330f729Sjoerg break;
1657330f729Sjoerg }
1667330f729Sjoerg
1677330f729Sjoerg case Comment::VerbatimBlockCommentKind:
1687330f729Sjoerg MiscBlocks.push_back(cast<BlockCommandComment>(Child));
1697330f729Sjoerg break;
1707330f729Sjoerg
1717330f729Sjoerg case Comment::VerbatimLineCommentKind: {
1727330f729Sjoerg const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
1737330f729Sjoerg const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());
1747330f729Sjoerg if (!Info->IsDeclarationCommand)
1757330f729Sjoerg MiscBlocks.push_back(VLC);
1767330f729Sjoerg break;
1777330f729Sjoerg }
1787330f729Sjoerg
1797330f729Sjoerg case Comment::TextCommentKind:
1807330f729Sjoerg case Comment::InlineCommandCommentKind:
1817330f729Sjoerg case Comment::HTMLStartTagCommentKind:
1827330f729Sjoerg case Comment::HTMLEndTagCommentKind:
1837330f729Sjoerg case Comment::VerbatimBlockLineCommentKind:
1847330f729Sjoerg case Comment::FullCommentKind:
1857330f729Sjoerg llvm_unreachable("AST node of this kind can't be a child of "
1867330f729Sjoerg "a FullComment");
1877330f729Sjoerg }
1887330f729Sjoerg }
1897330f729Sjoerg
1907330f729Sjoerg // Sort params in order they are declared in the function prototype.
1917330f729Sjoerg // Unresolved parameters are put at the end of the list in the same order
1927330f729Sjoerg // they were seen in the comment.
1937330f729Sjoerg llvm::stable_sort(Params, ParamCommandCommentCompareIndex());
1947330f729Sjoerg llvm::stable_sort(TParams, TParamCommandCommentComparePosition());
1957330f729Sjoerg }
1967330f729Sjoerg
printHTMLStartTagComment(const HTMLStartTagComment * C,llvm::raw_svector_ostream & Result)1977330f729Sjoerg void printHTMLStartTagComment(const HTMLStartTagComment *C,
1987330f729Sjoerg llvm::raw_svector_ostream &Result) {
1997330f729Sjoerg Result << "<" << C->getTagName();
2007330f729Sjoerg
2017330f729Sjoerg if (C->getNumAttrs() != 0) {
2027330f729Sjoerg for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
2037330f729Sjoerg Result << " ";
2047330f729Sjoerg const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
2057330f729Sjoerg Result << Attr.Name;
2067330f729Sjoerg if (!Attr.Value.empty())
2077330f729Sjoerg Result << "=\"" << Attr.Value << "\"";
2087330f729Sjoerg }
2097330f729Sjoerg }
2107330f729Sjoerg
2117330f729Sjoerg if (!C->isSelfClosing())
2127330f729Sjoerg Result << ">";
2137330f729Sjoerg else
2147330f729Sjoerg Result << "/>";
2157330f729Sjoerg }
2167330f729Sjoerg
2177330f729Sjoerg class CommentASTToHTMLConverter :
2187330f729Sjoerg public ConstCommentVisitor<CommentASTToHTMLConverter> {
2197330f729Sjoerg public:
2207330f729Sjoerg /// \param Str accumulator for HTML.
CommentASTToHTMLConverter(const FullComment * FC,SmallVectorImpl<char> & Str,const CommandTraits & Traits)2217330f729Sjoerg CommentASTToHTMLConverter(const FullComment *FC,
2227330f729Sjoerg SmallVectorImpl<char> &Str,
2237330f729Sjoerg const CommandTraits &Traits) :
2247330f729Sjoerg FC(FC), Result(Str), Traits(Traits)
2257330f729Sjoerg { }
2267330f729Sjoerg
2277330f729Sjoerg // Inline content.
2287330f729Sjoerg void visitTextComment(const TextComment *C);
2297330f729Sjoerg void visitInlineCommandComment(const InlineCommandComment *C);
2307330f729Sjoerg void visitHTMLStartTagComment(const HTMLStartTagComment *C);
2317330f729Sjoerg void visitHTMLEndTagComment(const HTMLEndTagComment *C);
2327330f729Sjoerg
2337330f729Sjoerg // Block content.
2347330f729Sjoerg void visitParagraphComment(const ParagraphComment *C);
2357330f729Sjoerg void visitBlockCommandComment(const BlockCommandComment *C);
2367330f729Sjoerg void visitParamCommandComment(const ParamCommandComment *C);
2377330f729Sjoerg void visitTParamCommandComment(const TParamCommandComment *C);
2387330f729Sjoerg void visitVerbatimBlockComment(const VerbatimBlockComment *C);
2397330f729Sjoerg void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
2407330f729Sjoerg void visitVerbatimLineComment(const VerbatimLineComment *C);
2417330f729Sjoerg
2427330f729Sjoerg void visitFullComment(const FullComment *C);
2437330f729Sjoerg
2447330f729Sjoerg // Helpers.
2457330f729Sjoerg
2467330f729Sjoerg /// Convert a paragraph that is not a block by itself (an argument to some
2477330f729Sjoerg /// command).
2487330f729Sjoerg void visitNonStandaloneParagraphComment(const ParagraphComment *C);
2497330f729Sjoerg
2507330f729Sjoerg void appendToResultWithHTMLEscaping(StringRef S);
2517330f729Sjoerg
2527330f729Sjoerg private:
2537330f729Sjoerg const FullComment *FC;
2547330f729Sjoerg /// Output stream for HTML.
2557330f729Sjoerg llvm::raw_svector_ostream Result;
2567330f729Sjoerg
2577330f729Sjoerg const CommandTraits &Traits;
2587330f729Sjoerg };
2597330f729Sjoerg } // end unnamed namespace
2607330f729Sjoerg
visitTextComment(const TextComment * C)2617330f729Sjoerg void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
2627330f729Sjoerg appendToResultWithHTMLEscaping(C->getText());
2637330f729Sjoerg }
2647330f729Sjoerg
visitInlineCommandComment(const InlineCommandComment * C)2657330f729Sjoerg void CommentASTToHTMLConverter::visitInlineCommandComment(
2667330f729Sjoerg const InlineCommandComment *C) {
2677330f729Sjoerg // Nothing to render if no arguments supplied.
2687330f729Sjoerg if (C->getNumArgs() == 0)
2697330f729Sjoerg return;
2707330f729Sjoerg
2717330f729Sjoerg // Nothing to render if argument is empty.
2727330f729Sjoerg StringRef Arg0 = C->getArgText(0);
2737330f729Sjoerg if (Arg0.empty())
2747330f729Sjoerg return;
2757330f729Sjoerg
2767330f729Sjoerg switch (C->getRenderKind()) {
2777330f729Sjoerg case InlineCommandComment::RenderNormal:
2787330f729Sjoerg for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
2797330f729Sjoerg appendToResultWithHTMLEscaping(C->getArgText(i));
2807330f729Sjoerg Result << " ";
2817330f729Sjoerg }
2827330f729Sjoerg return;
2837330f729Sjoerg
2847330f729Sjoerg case InlineCommandComment::RenderBold:
2857330f729Sjoerg assert(C->getNumArgs() == 1);
2867330f729Sjoerg Result << "<b>";
2877330f729Sjoerg appendToResultWithHTMLEscaping(Arg0);
2887330f729Sjoerg Result << "</b>";
2897330f729Sjoerg return;
2907330f729Sjoerg case InlineCommandComment::RenderMonospaced:
2917330f729Sjoerg assert(C->getNumArgs() == 1);
2927330f729Sjoerg Result << "<tt>";
2937330f729Sjoerg appendToResultWithHTMLEscaping(Arg0);
2947330f729Sjoerg Result<< "</tt>";
2957330f729Sjoerg return;
2967330f729Sjoerg case InlineCommandComment::RenderEmphasized:
2977330f729Sjoerg assert(C->getNumArgs() == 1);
2987330f729Sjoerg Result << "<em>";
2997330f729Sjoerg appendToResultWithHTMLEscaping(Arg0);
3007330f729Sjoerg Result << "</em>";
3017330f729Sjoerg return;
302*e038c9c4Sjoerg case InlineCommandComment::RenderAnchor:
303*e038c9c4Sjoerg assert(C->getNumArgs() == 1);
304*e038c9c4Sjoerg Result << "<span id=\"" << Arg0 << "\"></span>";
305*e038c9c4Sjoerg return;
3067330f729Sjoerg }
3077330f729Sjoerg }
3087330f729Sjoerg
visitHTMLStartTagComment(const HTMLStartTagComment * C)3097330f729Sjoerg void CommentASTToHTMLConverter::visitHTMLStartTagComment(
3107330f729Sjoerg const HTMLStartTagComment *C) {
3117330f729Sjoerg printHTMLStartTagComment(C, Result);
3127330f729Sjoerg }
3137330f729Sjoerg
visitHTMLEndTagComment(const HTMLEndTagComment * C)3147330f729Sjoerg void CommentASTToHTMLConverter::visitHTMLEndTagComment(
3157330f729Sjoerg const HTMLEndTagComment *C) {
3167330f729Sjoerg Result << "</" << C->getTagName() << ">";
3177330f729Sjoerg }
3187330f729Sjoerg
visitParagraphComment(const ParagraphComment * C)3197330f729Sjoerg void CommentASTToHTMLConverter::visitParagraphComment(
3207330f729Sjoerg const ParagraphComment *C) {
3217330f729Sjoerg if (C->isWhitespace())
3227330f729Sjoerg return;
3237330f729Sjoerg
3247330f729Sjoerg Result << "<p>";
3257330f729Sjoerg for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
3267330f729Sjoerg I != E; ++I) {
3277330f729Sjoerg visit(*I);
3287330f729Sjoerg }
3297330f729Sjoerg Result << "</p>";
3307330f729Sjoerg }
3317330f729Sjoerg
visitBlockCommandComment(const BlockCommandComment * C)3327330f729Sjoerg void CommentASTToHTMLConverter::visitBlockCommandComment(
3337330f729Sjoerg const BlockCommandComment *C) {
3347330f729Sjoerg const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
3357330f729Sjoerg if (Info->IsBriefCommand) {
3367330f729Sjoerg Result << "<p class=\"para-brief\">";
3377330f729Sjoerg visitNonStandaloneParagraphComment(C->getParagraph());
3387330f729Sjoerg Result << "</p>";
3397330f729Sjoerg return;
3407330f729Sjoerg }
3417330f729Sjoerg if (Info->IsReturnsCommand) {
3427330f729Sjoerg Result << "<p class=\"para-returns\">"
3437330f729Sjoerg "<span class=\"word-returns\">Returns</span> ";
3447330f729Sjoerg visitNonStandaloneParagraphComment(C->getParagraph());
3457330f729Sjoerg Result << "</p>";
3467330f729Sjoerg return;
3477330f729Sjoerg }
3487330f729Sjoerg // We don't know anything about this command. Just render the paragraph.
3497330f729Sjoerg visit(C->getParagraph());
3507330f729Sjoerg }
3517330f729Sjoerg
visitParamCommandComment(const ParamCommandComment * C)3527330f729Sjoerg void CommentASTToHTMLConverter::visitParamCommandComment(
3537330f729Sjoerg const ParamCommandComment *C) {
3547330f729Sjoerg if (C->isParamIndexValid()) {
3557330f729Sjoerg if (C->isVarArgParam()) {
3567330f729Sjoerg Result << "<dt class=\"param-name-index-vararg\">";
3577330f729Sjoerg appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
3587330f729Sjoerg } else {
3597330f729Sjoerg Result << "<dt class=\"param-name-index-"
3607330f729Sjoerg << C->getParamIndex()
3617330f729Sjoerg << "\">";
3627330f729Sjoerg appendToResultWithHTMLEscaping(C->getParamName(FC));
3637330f729Sjoerg }
3647330f729Sjoerg } else {
3657330f729Sjoerg Result << "<dt class=\"param-name-index-invalid\">";
3667330f729Sjoerg appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
3677330f729Sjoerg }
3687330f729Sjoerg Result << "</dt>";
3697330f729Sjoerg
3707330f729Sjoerg if (C->isParamIndexValid()) {
3717330f729Sjoerg if (C->isVarArgParam())
3727330f729Sjoerg Result << "<dd class=\"param-descr-index-vararg\">";
3737330f729Sjoerg else
3747330f729Sjoerg Result << "<dd class=\"param-descr-index-"
3757330f729Sjoerg << C->getParamIndex()
3767330f729Sjoerg << "\">";
3777330f729Sjoerg } else
3787330f729Sjoerg Result << "<dd class=\"param-descr-index-invalid\">";
3797330f729Sjoerg
3807330f729Sjoerg visitNonStandaloneParagraphComment(C->getParagraph());
3817330f729Sjoerg Result << "</dd>";
3827330f729Sjoerg }
3837330f729Sjoerg
visitTParamCommandComment(const TParamCommandComment * C)3847330f729Sjoerg void CommentASTToHTMLConverter::visitTParamCommandComment(
3857330f729Sjoerg const TParamCommandComment *C) {
3867330f729Sjoerg if (C->isPositionValid()) {
3877330f729Sjoerg if (C->getDepth() == 1)
3887330f729Sjoerg Result << "<dt class=\"tparam-name-index-"
3897330f729Sjoerg << C->getIndex(0)
3907330f729Sjoerg << "\">";
3917330f729Sjoerg else
3927330f729Sjoerg Result << "<dt class=\"tparam-name-index-other\">";
3937330f729Sjoerg appendToResultWithHTMLEscaping(C->getParamName(FC));
3947330f729Sjoerg } else {
3957330f729Sjoerg Result << "<dt class=\"tparam-name-index-invalid\">";
3967330f729Sjoerg appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
3977330f729Sjoerg }
3987330f729Sjoerg
3997330f729Sjoerg Result << "</dt>";
4007330f729Sjoerg
4017330f729Sjoerg if (C->isPositionValid()) {
4027330f729Sjoerg if (C->getDepth() == 1)
4037330f729Sjoerg Result << "<dd class=\"tparam-descr-index-"
4047330f729Sjoerg << C->getIndex(0)
4057330f729Sjoerg << "\">";
4067330f729Sjoerg else
4077330f729Sjoerg Result << "<dd class=\"tparam-descr-index-other\">";
4087330f729Sjoerg } else
4097330f729Sjoerg Result << "<dd class=\"tparam-descr-index-invalid\">";
4107330f729Sjoerg
4117330f729Sjoerg visitNonStandaloneParagraphComment(C->getParagraph());
4127330f729Sjoerg Result << "</dd>";
4137330f729Sjoerg }
4147330f729Sjoerg
visitVerbatimBlockComment(const VerbatimBlockComment * C)4157330f729Sjoerg void CommentASTToHTMLConverter::visitVerbatimBlockComment(
4167330f729Sjoerg const VerbatimBlockComment *C) {
4177330f729Sjoerg unsigned NumLines = C->getNumLines();
4187330f729Sjoerg if (NumLines == 0)
4197330f729Sjoerg return;
4207330f729Sjoerg
4217330f729Sjoerg Result << "<pre>";
4227330f729Sjoerg for (unsigned i = 0; i != NumLines; ++i) {
4237330f729Sjoerg appendToResultWithHTMLEscaping(C->getText(i));
4247330f729Sjoerg if (i + 1 != NumLines)
4257330f729Sjoerg Result << '\n';
4267330f729Sjoerg }
4277330f729Sjoerg Result << "</pre>";
4287330f729Sjoerg }
4297330f729Sjoerg
visitVerbatimBlockLineComment(const VerbatimBlockLineComment * C)4307330f729Sjoerg void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
4317330f729Sjoerg const VerbatimBlockLineComment *C) {
4327330f729Sjoerg llvm_unreachable("should not see this AST node");
4337330f729Sjoerg }
4347330f729Sjoerg
visitVerbatimLineComment(const VerbatimLineComment * C)4357330f729Sjoerg void CommentASTToHTMLConverter::visitVerbatimLineComment(
4367330f729Sjoerg const VerbatimLineComment *C) {
4377330f729Sjoerg Result << "<pre>";
4387330f729Sjoerg appendToResultWithHTMLEscaping(C->getText());
4397330f729Sjoerg Result << "</pre>";
4407330f729Sjoerg }
4417330f729Sjoerg
visitFullComment(const FullComment * C)4427330f729Sjoerg void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
4437330f729Sjoerg FullCommentParts Parts(C, Traits);
4447330f729Sjoerg
4457330f729Sjoerg bool FirstParagraphIsBrief = false;
4467330f729Sjoerg if (Parts.Headerfile)
4477330f729Sjoerg visit(Parts.Headerfile);
4487330f729Sjoerg if (Parts.Brief)
4497330f729Sjoerg visit(Parts.Brief);
4507330f729Sjoerg else if (Parts.FirstParagraph) {
4517330f729Sjoerg Result << "<p class=\"para-brief\">";
4527330f729Sjoerg visitNonStandaloneParagraphComment(Parts.FirstParagraph);
4537330f729Sjoerg Result << "</p>";
4547330f729Sjoerg FirstParagraphIsBrief = true;
4557330f729Sjoerg }
4567330f729Sjoerg
4577330f729Sjoerg for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
4587330f729Sjoerg const Comment *C = Parts.MiscBlocks[i];
4597330f729Sjoerg if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
4607330f729Sjoerg continue;
4617330f729Sjoerg visit(C);
4627330f729Sjoerg }
4637330f729Sjoerg
4647330f729Sjoerg if (Parts.TParams.size() != 0) {
4657330f729Sjoerg Result << "<dl>";
4667330f729Sjoerg for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
4677330f729Sjoerg visit(Parts.TParams[i]);
4687330f729Sjoerg Result << "</dl>";
4697330f729Sjoerg }
4707330f729Sjoerg
4717330f729Sjoerg if (Parts.Params.size() != 0) {
4727330f729Sjoerg Result << "<dl>";
4737330f729Sjoerg for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
4747330f729Sjoerg visit(Parts.Params[i]);
4757330f729Sjoerg Result << "</dl>";
4767330f729Sjoerg }
4777330f729Sjoerg
4787330f729Sjoerg if (Parts.Returns.size() != 0) {
4797330f729Sjoerg Result << "<div class=\"result-discussion\">";
4807330f729Sjoerg for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
4817330f729Sjoerg visit(Parts.Returns[i]);
4827330f729Sjoerg Result << "</div>";
4837330f729Sjoerg }
4847330f729Sjoerg
4857330f729Sjoerg }
4867330f729Sjoerg
visitNonStandaloneParagraphComment(const ParagraphComment * C)4877330f729Sjoerg void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
4887330f729Sjoerg const ParagraphComment *C) {
4897330f729Sjoerg if (!C)
4907330f729Sjoerg return;
4917330f729Sjoerg
4927330f729Sjoerg for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
4937330f729Sjoerg I != E; ++I) {
4947330f729Sjoerg visit(*I);
4957330f729Sjoerg }
4967330f729Sjoerg }
4977330f729Sjoerg
appendToResultWithHTMLEscaping(StringRef S)4987330f729Sjoerg void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
4997330f729Sjoerg for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
5007330f729Sjoerg const char C = *I;
5017330f729Sjoerg switch (C) {
5027330f729Sjoerg case '&':
5037330f729Sjoerg Result << "&";
5047330f729Sjoerg break;
5057330f729Sjoerg case '<':
5067330f729Sjoerg Result << "<";
5077330f729Sjoerg break;
5087330f729Sjoerg case '>':
5097330f729Sjoerg Result << ">";
5107330f729Sjoerg break;
5117330f729Sjoerg case '"':
5127330f729Sjoerg Result << """;
5137330f729Sjoerg break;
5147330f729Sjoerg case '\'':
5157330f729Sjoerg Result << "'";
5167330f729Sjoerg break;
5177330f729Sjoerg case '/':
5187330f729Sjoerg Result << "/";
5197330f729Sjoerg break;
5207330f729Sjoerg default:
5217330f729Sjoerg Result << C;
5227330f729Sjoerg break;
5237330f729Sjoerg }
5247330f729Sjoerg }
5257330f729Sjoerg }
5267330f729Sjoerg
5277330f729Sjoerg namespace {
5287330f729Sjoerg class CommentASTToXMLConverter :
5297330f729Sjoerg public ConstCommentVisitor<CommentASTToXMLConverter> {
5307330f729Sjoerg public:
5317330f729Sjoerg /// \param Str accumulator for XML.
CommentASTToXMLConverter(const FullComment * FC,SmallVectorImpl<char> & Str,const CommandTraits & Traits,const SourceManager & SM)5327330f729Sjoerg CommentASTToXMLConverter(const FullComment *FC,
5337330f729Sjoerg SmallVectorImpl<char> &Str,
5347330f729Sjoerg const CommandTraits &Traits,
5357330f729Sjoerg const SourceManager &SM) :
5367330f729Sjoerg FC(FC), Result(Str), Traits(Traits), SM(SM) { }
5377330f729Sjoerg
5387330f729Sjoerg // Inline content.
5397330f729Sjoerg void visitTextComment(const TextComment *C);
5407330f729Sjoerg void visitInlineCommandComment(const InlineCommandComment *C);
5417330f729Sjoerg void visitHTMLStartTagComment(const HTMLStartTagComment *C);
5427330f729Sjoerg void visitHTMLEndTagComment(const HTMLEndTagComment *C);
5437330f729Sjoerg
5447330f729Sjoerg // Block content.
5457330f729Sjoerg void visitParagraphComment(const ParagraphComment *C);
5467330f729Sjoerg
5477330f729Sjoerg void appendParagraphCommentWithKind(const ParagraphComment *C,
5487330f729Sjoerg StringRef Kind);
5497330f729Sjoerg
5507330f729Sjoerg void visitBlockCommandComment(const BlockCommandComment *C);
5517330f729Sjoerg void visitParamCommandComment(const ParamCommandComment *C);
5527330f729Sjoerg void visitTParamCommandComment(const TParamCommandComment *C);
5537330f729Sjoerg void visitVerbatimBlockComment(const VerbatimBlockComment *C);
5547330f729Sjoerg void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
5557330f729Sjoerg void visitVerbatimLineComment(const VerbatimLineComment *C);
5567330f729Sjoerg
5577330f729Sjoerg void visitFullComment(const FullComment *C);
5587330f729Sjoerg
5597330f729Sjoerg // Helpers.
5607330f729Sjoerg void appendToResultWithXMLEscaping(StringRef S);
5617330f729Sjoerg void appendToResultWithCDATAEscaping(StringRef S);
5627330f729Sjoerg
5637330f729Sjoerg void formatTextOfDeclaration(const DeclInfo *DI,
5647330f729Sjoerg SmallString<128> &Declaration);
5657330f729Sjoerg
5667330f729Sjoerg private:
5677330f729Sjoerg const FullComment *FC;
5687330f729Sjoerg
5697330f729Sjoerg /// Output stream for XML.
5707330f729Sjoerg llvm::raw_svector_ostream Result;
5717330f729Sjoerg
5727330f729Sjoerg const CommandTraits &Traits;
5737330f729Sjoerg const SourceManager &SM;
5747330f729Sjoerg };
5757330f729Sjoerg
getSourceTextOfDeclaration(const DeclInfo * ThisDecl,SmallVectorImpl<char> & Str)5767330f729Sjoerg void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
5777330f729Sjoerg SmallVectorImpl<char> &Str) {
5787330f729Sjoerg ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
5797330f729Sjoerg const LangOptions &LangOpts = Context.getLangOpts();
5807330f729Sjoerg llvm::raw_svector_ostream OS(Str);
5817330f729Sjoerg PrintingPolicy PPolicy(LangOpts);
5827330f729Sjoerg PPolicy.PolishForDeclaration = true;
5837330f729Sjoerg PPolicy.TerseOutput = true;
5847330f729Sjoerg PPolicy.ConstantsAsWritten = true;
5857330f729Sjoerg ThisDecl->CurrentDecl->print(OS, PPolicy,
5867330f729Sjoerg /*Indentation*/0, /*PrintInstantiation*/false);
5877330f729Sjoerg }
5887330f729Sjoerg
formatTextOfDeclaration(const DeclInfo * DI,SmallString<128> & Declaration)5897330f729Sjoerg void CommentASTToXMLConverter::formatTextOfDeclaration(
5907330f729Sjoerg const DeclInfo *DI, SmallString<128> &Declaration) {
5917330f729Sjoerg // Formatting API expects null terminated input string.
5927330f729Sjoerg StringRef StringDecl(Declaration.c_str(), Declaration.size());
5937330f729Sjoerg
5947330f729Sjoerg // Formatter specific code.
5957330f729Sjoerg unsigned Offset = 0;
5967330f729Sjoerg unsigned Length = Declaration.size();
5977330f729Sjoerg
5987330f729Sjoerg format::FormatStyle Style = format::getLLVMStyle();
5997330f729Sjoerg Style.FixNamespaceComments = false;
6007330f729Sjoerg tooling::Replacements Replaces =
6017330f729Sjoerg reformat(Style, StringDecl, tooling::Range(Offset, Length), "xmldecl.xd");
6027330f729Sjoerg auto FormattedStringDecl = applyAllReplacements(StringDecl, Replaces);
6037330f729Sjoerg if (static_cast<bool>(FormattedStringDecl)) {
6047330f729Sjoerg Declaration = *FormattedStringDecl;
6057330f729Sjoerg }
6067330f729Sjoerg }
6077330f729Sjoerg
6087330f729Sjoerg } // end unnamed namespace
6097330f729Sjoerg
visitTextComment(const TextComment * C)6107330f729Sjoerg void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
6117330f729Sjoerg appendToResultWithXMLEscaping(C->getText());
6127330f729Sjoerg }
6137330f729Sjoerg
visitInlineCommandComment(const InlineCommandComment * C)6147330f729Sjoerg void CommentASTToXMLConverter::visitInlineCommandComment(
6157330f729Sjoerg const InlineCommandComment *C) {
6167330f729Sjoerg // Nothing to render if no arguments supplied.
6177330f729Sjoerg if (C->getNumArgs() == 0)
6187330f729Sjoerg return;
6197330f729Sjoerg
6207330f729Sjoerg // Nothing to render if argument is empty.
6217330f729Sjoerg StringRef Arg0 = C->getArgText(0);
6227330f729Sjoerg if (Arg0.empty())
6237330f729Sjoerg return;
6247330f729Sjoerg
6257330f729Sjoerg switch (C->getRenderKind()) {
6267330f729Sjoerg case InlineCommandComment::RenderNormal:
6277330f729Sjoerg for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
6287330f729Sjoerg appendToResultWithXMLEscaping(C->getArgText(i));
6297330f729Sjoerg Result << " ";
6307330f729Sjoerg }
6317330f729Sjoerg return;
6327330f729Sjoerg case InlineCommandComment::RenderBold:
6337330f729Sjoerg assert(C->getNumArgs() == 1);
6347330f729Sjoerg Result << "<bold>";
6357330f729Sjoerg appendToResultWithXMLEscaping(Arg0);
6367330f729Sjoerg Result << "</bold>";
6377330f729Sjoerg return;
6387330f729Sjoerg case InlineCommandComment::RenderMonospaced:
6397330f729Sjoerg assert(C->getNumArgs() == 1);
6407330f729Sjoerg Result << "<monospaced>";
6417330f729Sjoerg appendToResultWithXMLEscaping(Arg0);
6427330f729Sjoerg Result << "</monospaced>";
6437330f729Sjoerg return;
6447330f729Sjoerg case InlineCommandComment::RenderEmphasized:
6457330f729Sjoerg assert(C->getNumArgs() == 1);
6467330f729Sjoerg Result << "<emphasized>";
6477330f729Sjoerg appendToResultWithXMLEscaping(Arg0);
6487330f729Sjoerg Result << "</emphasized>";
6497330f729Sjoerg return;
650*e038c9c4Sjoerg case InlineCommandComment::RenderAnchor:
651*e038c9c4Sjoerg assert(C->getNumArgs() == 1);
652*e038c9c4Sjoerg Result << "<anchor id=\"" << Arg0 << "\"></anchor>";
653*e038c9c4Sjoerg return;
6547330f729Sjoerg }
6557330f729Sjoerg }
6567330f729Sjoerg
visitHTMLStartTagComment(const HTMLStartTagComment * C)6577330f729Sjoerg void CommentASTToXMLConverter::visitHTMLStartTagComment(
6587330f729Sjoerg const HTMLStartTagComment *C) {
6597330f729Sjoerg Result << "<rawHTML";
6607330f729Sjoerg if (C->isMalformed())
6617330f729Sjoerg Result << " isMalformed=\"1\"";
6627330f729Sjoerg Result << ">";
6637330f729Sjoerg {
6647330f729Sjoerg SmallString<32> Tag;
6657330f729Sjoerg {
6667330f729Sjoerg llvm::raw_svector_ostream TagOS(Tag);
6677330f729Sjoerg printHTMLStartTagComment(C, TagOS);
6687330f729Sjoerg }
6697330f729Sjoerg appendToResultWithCDATAEscaping(Tag);
6707330f729Sjoerg }
6717330f729Sjoerg Result << "</rawHTML>";
6727330f729Sjoerg }
6737330f729Sjoerg
6747330f729Sjoerg void
visitHTMLEndTagComment(const HTMLEndTagComment * C)6757330f729Sjoerg CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
6767330f729Sjoerg Result << "<rawHTML";
6777330f729Sjoerg if (C->isMalformed())
6787330f729Sjoerg Result << " isMalformed=\"1\"";
6797330f729Sjoerg Result << "></" << C->getTagName() << "></rawHTML>";
6807330f729Sjoerg }
6817330f729Sjoerg
6827330f729Sjoerg void
visitParagraphComment(const ParagraphComment * C)6837330f729Sjoerg CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
6847330f729Sjoerg appendParagraphCommentWithKind(C, StringRef());
6857330f729Sjoerg }
6867330f729Sjoerg
appendParagraphCommentWithKind(const ParagraphComment * C,StringRef ParagraphKind)6877330f729Sjoerg void CommentASTToXMLConverter::appendParagraphCommentWithKind(
6887330f729Sjoerg const ParagraphComment *C,
6897330f729Sjoerg StringRef ParagraphKind) {
6907330f729Sjoerg if (C->isWhitespace())
6917330f729Sjoerg return;
6927330f729Sjoerg
6937330f729Sjoerg if (ParagraphKind.empty())
6947330f729Sjoerg Result << "<Para>";
6957330f729Sjoerg else
6967330f729Sjoerg Result << "<Para kind=\"" << ParagraphKind << "\">";
6977330f729Sjoerg
6987330f729Sjoerg for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
6997330f729Sjoerg I != E; ++I) {
7007330f729Sjoerg visit(*I);
7017330f729Sjoerg }
7027330f729Sjoerg Result << "</Para>";
7037330f729Sjoerg }
7047330f729Sjoerg
visitBlockCommandComment(const BlockCommandComment * C)7057330f729Sjoerg void CommentASTToXMLConverter::visitBlockCommandComment(
7067330f729Sjoerg const BlockCommandComment *C) {
7077330f729Sjoerg StringRef ParagraphKind;
7087330f729Sjoerg
7097330f729Sjoerg switch (C->getCommandID()) {
7107330f729Sjoerg case CommandTraits::KCI_attention:
7117330f729Sjoerg case CommandTraits::KCI_author:
7127330f729Sjoerg case CommandTraits::KCI_authors:
7137330f729Sjoerg case CommandTraits::KCI_bug:
7147330f729Sjoerg case CommandTraits::KCI_copyright:
7157330f729Sjoerg case CommandTraits::KCI_date:
7167330f729Sjoerg case CommandTraits::KCI_invariant:
7177330f729Sjoerg case CommandTraits::KCI_note:
7187330f729Sjoerg case CommandTraits::KCI_post:
7197330f729Sjoerg case CommandTraits::KCI_pre:
7207330f729Sjoerg case CommandTraits::KCI_remark:
7217330f729Sjoerg case CommandTraits::KCI_remarks:
7227330f729Sjoerg case CommandTraits::KCI_sa:
7237330f729Sjoerg case CommandTraits::KCI_see:
7247330f729Sjoerg case CommandTraits::KCI_since:
7257330f729Sjoerg case CommandTraits::KCI_todo:
7267330f729Sjoerg case CommandTraits::KCI_version:
7277330f729Sjoerg case CommandTraits::KCI_warning:
7287330f729Sjoerg ParagraphKind = C->getCommandName(Traits);
7297330f729Sjoerg break;
7307330f729Sjoerg default:
7317330f729Sjoerg break;
7327330f729Sjoerg }
7337330f729Sjoerg
7347330f729Sjoerg appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
7357330f729Sjoerg }
7367330f729Sjoerg
visitParamCommandComment(const ParamCommandComment * C)7377330f729Sjoerg void CommentASTToXMLConverter::visitParamCommandComment(
7387330f729Sjoerg const ParamCommandComment *C) {
7397330f729Sjoerg Result << "<Parameter><Name>";
7407330f729Sjoerg appendToResultWithXMLEscaping(C->isParamIndexValid()
7417330f729Sjoerg ? C->getParamName(FC)
7427330f729Sjoerg : C->getParamNameAsWritten());
7437330f729Sjoerg Result << "</Name>";
7447330f729Sjoerg
7457330f729Sjoerg if (C->isParamIndexValid()) {
7467330f729Sjoerg if (C->isVarArgParam())
7477330f729Sjoerg Result << "<IsVarArg />";
7487330f729Sjoerg else
7497330f729Sjoerg Result << "<Index>" << C->getParamIndex() << "</Index>";
7507330f729Sjoerg }
7517330f729Sjoerg
7527330f729Sjoerg Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
7537330f729Sjoerg switch (C->getDirection()) {
7547330f729Sjoerg case ParamCommandComment::In:
7557330f729Sjoerg Result << "in";
7567330f729Sjoerg break;
7577330f729Sjoerg case ParamCommandComment::Out:
7587330f729Sjoerg Result << "out";
7597330f729Sjoerg break;
7607330f729Sjoerg case ParamCommandComment::InOut:
7617330f729Sjoerg Result << "in,out";
7627330f729Sjoerg break;
7637330f729Sjoerg }
7647330f729Sjoerg Result << "</Direction><Discussion>";
7657330f729Sjoerg visit(C->getParagraph());
7667330f729Sjoerg Result << "</Discussion></Parameter>";
7677330f729Sjoerg }
7687330f729Sjoerg
visitTParamCommandComment(const TParamCommandComment * C)7697330f729Sjoerg void CommentASTToXMLConverter::visitTParamCommandComment(
7707330f729Sjoerg const TParamCommandComment *C) {
7717330f729Sjoerg Result << "<Parameter><Name>";
7727330f729Sjoerg appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
7737330f729Sjoerg : C->getParamNameAsWritten());
7747330f729Sjoerg Result << "</Name>";
7757330f729Sjoerg
7767330f729Sjoerg if (C->isPositionValid() && C->getDepth() == 1) {
7777330f729Sjoerg Result << "<Index>" << C->getIndex(0) << "</Index>";
7787330f729Sjoerg }
7797330f729Sjoerg
7807330f729Sjoerg Result << "<Discussion>";
7817330f729Sjoerg visit(C->getParagraph());
7827330f729Sjoerg Result << "</Discussion></Parameter>";
7837330f729Sjoerg }
7847330f729Sjoerg
visitVerbatimBlockComment(const VerbatimBlockComment * C)7857330f729Sjoerg void CommentASTToXMLConverter::visitVerbatimBlockComment(
7867330f729Sjoerg const VerbatimBlockComment *C) {
7877330f729Sjoerg unsigned NumLines = C->getNumLines();
7887330f729Sjoerg if (NumLines == 0)
7897330f729Sjoerg return;
7907330f729Sjoerg
7917330f729Sjoerg switch (C->getCommandID()) {
7927330f729Sjoerg case CommandTraits::KCI_code:
7937330f729Sjoerg Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
7947330f729Sjoerg break;
7957330f729Sjoerg default:
7967330f729Sjoerg Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
7977330f729Sjoerg break;
7987330f729Sjoerg }
7997330f729Sjoerg for (unsigned i = 0; i != NumLines; ++i) {
8007330f729Sjoerg appendToResultWithXMLEscaping(C->getText(i));
8017330f729Sjoerg if (i + 1 != NumLines)
8027330f729Sjoerg Result << '\n';
8037330f729Sjoerg }
8047330f729Sjoerg Result << "</Verbatim>";
8057330f729Sjoerg }
8067330f729Sjoerg
visitVerbatimBlockLineComment(const VerbatimBlockLineComment * C)8077330f729Sjoerg void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
8087330f729Sjoerg const VerbatimBlockLineComment *C) {
8097330f729Sjoerg llvm_unreachable("should not see this AST node");
8107330f729Sjoerg }
8117330f729Sjoerg
visitVerbatimLineComment(const VerbatimLineComment * C)8127330f729Sjoerg void CommentASTToXMLConverter::visitVerbatimLineComment(
8137330f729Sjoerg const VerbatimLineComment *C) {
8147330f729Sjoerg Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
8157330f729Sjoerg appendToResultWithXMLEscaping(C->getText());
8167330f729Sjoerg Result << "</Verbatim>";
8177330f729Sjoerg }
8187330f729Sjoerg
visitFullComment(const FullComment * C)8197330f729Sjoerg void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
8207330f729Sjoerg FullCommentParts Parts(C, Traits);
8217330f729Sjoerg
8227330f729Sjoerg const DeclInfo *DI = C->getDeclInfo();
8237330f729Sjoerg StringRef RootEndTag;
8247330f729Sjoerg if (DI) {
8257330f729Sjoerg switch (DI->getKind()) {
8267330f729Sjoerg case DeclInfo::OtherKind:
8277330f729Sjoerg RootEndTag = "</Other>";
8287330f729Sjoerg Result << "<Other";
8297330f729Sjoerg break;
8307330f729Sjoerg case DeclInfo::FunctionKind:
8317330f729Sjoerg RootEndTag = "</Function>";
8327330f729Sjoerg Result << "<Function";
8337330f729Sjoerg switch (DI->TemplateKind) {
8347330f729Sjoerg case DeclInfo::NotTemplate:
8357330f729Sjoerg break;
8367330f729Sjoerg case DeclInfo::Template:
8377330f729Sjoerg Result << " templateKind=\"template\"";
8387330f729Sjoerg break;
8397330f729Sjoerg case DeclInfo::TemplateSpecialization:
8407330f729Sjoerg Result << " templateKind=\"specialization\"";
8417330f729Sjoerg break;
8427330f729Sjoerg case DeclInfo::TemplatePartialSpecialization:
8437330f729Sjoerg llvm_unreachable("partial specializations of functions "
8447330f729Sjoerg "are not allowed in C++");
8457330f729Sjoerg }
8467330f729Sjoerg if (DI->IsInstanceMethod)
8477330f729Sjoerg Result << " isInstanceMethod=\"1\"";
8487330f729Sjoerg if (DI->IsClassMethod)
8497330f729Sjoerg Result << " isClassMethod=\"1\"";
8507330f729Sjoerg break;
8517330f729Sjoerg case DeclInfo::ClassKind:
8527330f729Sjoerg RootEndTag = "</Class>";
8537330f729Sjoerg Result << "<Class";
8547330f729Sjoerg switch (DI->TemplateKind) {
8557330f729Sjoerg case DeclInfo::NotTemplate:
8567330f729Sjoerg break;
8577330f729Sjoerg case DeclInfo::Template:
8587330f729Sjoerg Result << " templateKind=\"template\"";
8597330f729Sjoerg break;
8607330f729Sjoerg case DeclInfo::TemplateSpecialization:
8617330f729Sjoerg Result << " templateKind=\"specialization\"";
8627330f729Sjoerg break;
8637330f729Sjoerg case DeclInfo::TemplatePartialSpecialization:
8647330f729Sjoerg Result << " templateKind=\"partialSpecialization\"";
8657330f729Sjoerg break;
8667330f729Sjoerg }
8677330f729Sjoerg break;
8687330f729Sjoerg case DeclInfo::VariableKind:
8697330f729Sjoerg RootEndTag = "</Variable>";
8707330f729Sjoerg Result << "<Variable";
8717330f729Sjoerg break;
8727330f729Sjoerg case DeclInfo::NamespaceKind:
8737330f729Sjoerg RootEndTag = "</Namespace>";
8747330f729Sjoerg Result << "<Namespace";
8757330f729Sjoerg break;
8767330f729Sjoerg case DeclInfo::TypedefKind:
8777330f729Sjoerg RootEndTag = "</Typedef>";
8787330f729Sjoerg Result << "<Typedef";
8797330f729Sjoerg break;
8807330f729Sjoerg case DeclInfo::EnumKind:
8817330f729Sjoerg RootEndTag = "</Enum>";
8827330f729Sjoerg Result << "<Enum";
8837330f729Sjoerg break;
8847330f729Sjoerg }
8857330f729Sjoerg
8867330f729Sjoerg {
8877330f729Sjoerg // Print line and column number.
8887330f729Sjoerg SourceLocation Loc = DI->CurrentDecl->getLocation();
8897330f729Sjoerg std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
8907330f729Sjoerg FileID FID = LocInfo.first;
8917330f729Sjoerg unsigned FileOffset = LocInfo.second;
8927330f729Sjoerg
8937330f729Sjoerg if (FID.isValid()) {
8947330f729Sjoerg if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
8957330f729Sjoerg Result << " file=\"";
8967330f729Sjoerg appendToResultWithXMLEscaping(FE->getName());
8977330f729Sjoerg Result << "\"";
8987330f729Sjoerg }
8997330f729Sjoerg Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
9007330f729Sjoerg << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
9017330f729Sjoerg << "\"";
9027330f729Sjoerg }
9037330f729Sjoerg }
9047330f729Sjoerg
9057330f729Sjoerg // Finish the root tag.
9067330f729Sjoerg Result << ">";
9077330f729Sjoerg
9087330f729Sjoerg bool FoundName = false;
9097330f729Sjoerg if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
9107330f729Sjoerg if (DeclarationName DeclName = ND->getDeclName()) {
9117330f729Sjoerg Result << "<Name>";
9127330f729Sjoerg std::string Name = DeclName.getAsString();
9137330f729Sjoerg appendToResultWithXMLEscaping(Name);
9147330f729Sjoerg FoundName = true;
9157330f729Sjoerg Result << "</Name>";
9167330f729Sjoerg }
9177330f729Sjoerg }
9187330f729Sjoerg if (!FoundName)
9197330f729Sjoerg Result << "<Name><anonymous></Name>";
9207330f729Sjoerg
9217330f729Sjoerg {
9227330f729Sjoerg // Print USR.
9237330f729Sjoerg SmallString<128> USR;
9247330f729Sjoerg generateUSRForDecl(DI->CommentDecl, USR);
9257330f729Sjoerg if (!USR.empty()) {
9267330f729Sjoerg Result << "<USR>";
9277330f729Sjoerg appendToResultWithXMLEscaping(USR);
9287330f729Sjoerg Result << "</USR>";
9297330f729Sjoerg }
9307330f729Sjoerg }
9317330f729Sjoerg } else {
9327330f729Sjoerg // No DeclInfo -- just emit some root tag and name tag.
9337330f729Sjoerg RootEndTag = "</Other>";
9347330f729Sjoerg Result << "<Other><Name>unknown</Name>";
9357330f729Sjoerg }
9367330f729Sjoerg
9377330f729Sjoerg if (Parts.Headerfile) {
9387330f729Sjoerg Result << "<Headerfile>";
9397330f729Sjoerg visit(Parts.Headerfile);
9407330f729Sjoerg Result << "</Headerfile>";
9417330f729Sjoerg }
9427330f729Sjoerg
9437330f729Sjoerg {
9447330f729Sjoerg // Pretty-print the declaration.
9457330f729Sjoerg Result << "<Declaration>";
9467330f729Sjoerg SmallString<128> Declaration;
9477330f729Sjoerg getSourceTextOfDeclaration(DI, Declaration);
9487330f729Sjoerg formatTextOfDeclaration(DI, Declaration);
9497330f729Sjoerg appendToResultWithXMLEscaping(Declaration);
9507330f729Sjoerg Result << "</Declaration>";
9517330f729Sjoerg }
9527330f729Sjoerg
9537330f729Sjoerg bool FirstParagraphIsBrief = false;
9547330f729Sjoerg if (Parts.Brief) {
9557330f729Sjoerg Result << "<Abstract>";
9567330f729Sjoerg visit(Parts.Brief);
9577330f729Sjoerg Result << "</Abstract>";
9587330f729Sjoerg } else if (Parts.FirstParagraph) {
9597330f729Sjoerg Result << "<Abstract>";
9607330f729Sjoerg visit(Parts.FirstParagraph);
9617330f729Sjoerg Result << "</Abstract>";
9627330f729Sjoerg FirstParagraphIsBrief = true;
9637330f729Sjoerg }
9647330f729Sjoerg
9657330f729Sjoerg if (Parts.TParams.size() != 0) {
9667330f729Sjoerg Result << "<TemplateParameters>";
9677330f729Sjoerg for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
9687330f729Sjoerg visit(Parts.TParams[i]);
9697330f729Sjoerg Result << "</TemplateParameters>";
9707330f729Sjoerg }
9717330f729Sjoerg
9727330f729Sjoerg if (Parts.Params.size() != 0) {
9737330f729Sjoerg Result << "<Parameters>";
9747330f729Sjoerg for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
9757330f729Sjoerg visit(Parts.Params[i]);
9767330f729Sjoerg Result << "</Parameters>";
9777330f729Sjoerg }
9787330f729Sjoerg
9797330f729Sjoerg if (Parts.Exceptions.size() != 0) {
9807330f729Sjoerg Result << "<Exceptions>";
9817330f729Sjoerg for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
9827330f729Sjoerg visit(Parts.Exceptions[i]);
9837330f729Sjoerg Result << "</Exceptions>";
9847330f729Sjoerg }
9857330f729Sjoerg
9867330f729Sjoerg if (Parts.Returns.size() != 0) {
9877330f729Sjoerg Result << "<ResultDiscussion>";
9887330f729Sjoerg for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
9897330f729Sjoerg visit(Parts.Returns[i]);
9907330f729Sjoerg Result << "</ResultDiscussion>";
9917330f729Sjoerg }
9927330f729Sjoerg
9937330f729Sjoerg if (DI->CommentDecl->hasAttrs()) {
9947330f729Sjoerg const AttrVec &Attrs = DI->CommentDecl->getAttrs();
9957330f729Sjoerg for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
9967330f729Sjoerg const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
9977330f729Sjoerg if (!AA) {
9987330f729Sjoerg if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
9997330f729Sjoerg if (DA->getMessage().empty())
10007330f729Sjoerg Result << "<Deprecated/>";
10017330f729Sjoerg else {
10027330f729Sjoerg Result << "<Deprecated>";
10037330f729Sjoerg appendToResultWithXMLEscaping(DA->getMessage());
10047330f729Sjoerg Result << "</Deprecated>";
10057330f729Sjoerg }
10067330f729Sjoerg }
10077330f729Sjoerg else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
10087330f729Sjoerg if (UA->getMessage().empty())
10097330f729Sjoerg Result << "<Unavailable/>";
10107330f729Sjoerg else {
10117330f729Sjoerg Result << "<Unavailable>";
10127330f729Sjoerg appendToResultWithXMLEscaping(UA->getMessage());
10137330f729Sjoerg Result << "</Unavailable>";
10147330f729Sjoerg }
10157330f729Sjoerg }
10167330f729Sjoerg continue;
10177330f729Sjoerg }
10187330f729Sjoerg
10197330f729Sjoerg // 'availability' attribute.
10207330f729Sjoerg Result << "<Availability";
10217330f729Sjoerg StringRef Distribution;
10227330f729Sjoerg if (AA->getPlatform()) {
10237330f729Sjoerg Distribution = AvailabilityAttr::getPrettyPlatformName(
10247330f729Sjoerg AA->getPlatform()->getName());
10257330f729Sjoerg if (Distribution.empty())
10267330f729Sjoerg Distribution = AA->getPlatform()->getName();
10277330f729Sjoerg }
10287330f729Sjoerg Result << " distribution=\"" << Distribution << "\">";
10297330f729Sjoerg VersionTuple IntroducedInVersion = AA->getIntroduced();
10307330f729Sjoerg if (!IntroducedInVersion.empty()) {
10317330f729Sjoerg Result << "<IntroducedInVersion>"
10327330f729Sjoerg << IntroducedInVersion.getAsString()
10337330f729Sjoerg << "</IntroducedInVersion>";
10347330f729Sjoerg }
10357330f729Sjoerg VersionTuple DeprecatedInVersion = AA->getDeprecated();
10367330f729Sjoerg if (!DeprecatedInVersion.empty()) {
10377330f729Sjoerg Result << "<DeprecatedInVersion>"
10387330f729Sjoerg << DeprecatedInVersion.getAsString()
10397330f729Sjoerg << "</DeprecatedInVersion>";
10407330f729Sjoerg }
10417330f729Sjoerg VersionTuple RemovedAfterVersion = AA->getObsoleted();
10427330f729Sjoerg if (!RemovedAfterVersion.empty()) {
10437330f729Sjoerg Result << "<RemovedAfterVersion>"
10447330f729Sjoerg << RemovedAfterVersion.getAsString()
10457330f729Sjoerg << "</RemovedAfterVersion>";
10467330f729Sjoerg }
10477330f729Sjoerg StringRef DeprecationSummary = AA->getMessage();
10487330f729Sjoerg if (!DeprecationSummary.empty()) {
10497330f729Sjoerg Result << "<DeprecationSummary>";
10507330f729Sjoerg appendToResultWithXMLEscaping(DeprecationSummary);
10517330f729Sjoerg Result << "</DeprecationSummary>";
10527330f729Sjoerg }
10537330f729Sjoerg if (AA->getUnavailable())
10547330f729Sjoerg Result << "<Unavailable/>";
10557330f729Sjoerg Result << "</Availability>";
10567330f729Sjoerg }
10577330f729Sjoerg }
10587330f729Sjoerg
10597330f729Sjoerg {
10607330f729Sjoerg bool StartTagEmitted = false;
10617330f729Sjoerg for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
10627330f729Sjoerg const Comment *C = Parts.MiscBlocks[i];
10637330f729Sjoerg if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
10647330f729Sjoerg continue;
10657330f729Sjoerg if (!StartTagEmitted) {
10667330f729Sjoerg Result << "<Discussion>";
10677330f729Sjoerg StartTagEmitted = true;
10687330f729Sjoerg }
10697330f729Sjoerg visit(C);
10707330f729Sjoerg }
10717330f729Sjoerg if (StartTagEmitted)
10727330f729Sjoerg Result << "</Discussion>";
10737330f729Sjoerg }
10747330f729Sjoerg
10757330f729Sjoerg Result << RootEndTag;
10767330f729Sjoerg }
10777330f729Sjoerg
appendToResultWithXMLEscaping(StringRef S)10787330f729Sjoerg void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
10797330f729Sjoerg for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
10807330f729Sjoerg const char C = *I;
10817330f729Sjoerg switch (C) {
10827330f729Sjoerg case '&':
10837330f729Sjoerg Result << "&";
10847330f729Sjoerg break;
10857330f729Sjoerg case '<':
10867330f729Sjoerg Result << "<";
10877330f729Sjoerg break;
10887330f729Sjoerg case '>':
10897330f729Sjoerg Result << ">";
10907330f729Sjoerg break;
10917330f729Sjoerg case '"':
10927330f729Sjoerg Result << """;
10937330f729Sjoerg break;
10947330f729Sjoerg case '\'':
10957330f729Sjoerg Result << "'";
10967330f729Sjoerg break;
10977330f729Sjoerg default:
10987330f729Sjoerg Result << C;
10997330f729Sjoerg break;
11007330f729Sjoerg }
11017330f729Sjoerg }
11027330f729Sjoerg }
11037330f729Sjoerg
appendToResultWithCDATAEscaping(StringRef S)11047330f729Sjoerg void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) {
11057330f729Sjoerg if (S.empty())
11067330f729Sjoerg return;
11077330f729Sjoerg
11087330f729Sjoerg Result << "<![CDATA[";
11097330f729Sjoerg while (!S.empty()) {
11107330f729Sjoerg size_t Pos = S.find("]]>");
11117330f729Sjoerg if (Pos == 0) {
11127330f729Sjoerg Result << "]]]]><![CDATA[>";
11137330f729Sjoerg S = S.drop_front(3);
11147330f729Sjoerg continue;
11157330f729Sjoerg }
11167330f729Sjoerg if (Pos == StringRef::npos)
11177330f729Sjoerg Pos = S.size();
11187330f729Sjoerg
11197330f729Sjoerg Result << S.substr(0, Pos);
11207330f729Sjoerg
11217330f729Sjoerg S = S.drop_front(Pos);
11227330f729Sjoerg }
11237330f729Sjoerg Result << "]]>";
11247330f729Sjoerg }
11257330f729Sjoerg
CommentToXMLConverter()11267330f729Sjoerg CommentToXMLConverter::CommentToXMLConverter() {}
~CommentToXMLConverter()11277330f729Sjoerg CommentToXMLConverter::~CommentToXMLConverter() {}
11287330f729Sjoerg
convertCommentToHTML(const FullComment * FC,SmallVectorImpl<char> & HTML,const ASTContext & Context)11297330f729Sjoerg void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC,
11307330f729Sjoerg SmallVectorImpl<char> &HTML,
11317330f729Sjoerg const ASTContext &Context) {
11327330f729Sjoerg CommentASTToHTMLConverter Converter(FC, HTML,
11337330f729Sjoerg Context.getCommentCommandTraits());
11347330f729Sjoerg Converter.visit(FC);
11357330f729Sjoerg }
11367330f729Sjoerg
convertHTMLTagNodeToText(const comments::HTMLTagComment * HTC,SmallVectorImpl<char> & Text,const ASTContext & Context)11377330f729Sjoerg void CommentToXMLConverter::convertHTMLTagNodeToText(
11387330f729Sjoerg const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text,
11397330f729Sjoerg const ASTContext &Context) {
11407330f729Sjoerg CommentASTToHTMLConverter Converter(nullptr, Text,
11417330f729Sjoerg Context.getCommentCommandTraits());
11427330f729Sjoerg Converter.visit(HTC);
11437330f729Sjoerg }
11447330f729Sjoerg
convertCommentToXML(const FullComment * FC,SmallVectorImpl<char> & XML,const ASTContext & Context)11457330f729Sjoerg void CommentToXMLConverter::convertCommentToXML(const FullComment *FC,
11467330f729Sjoerg SmallVectorImpl<char> &XML,
11477330f729Sjoerg const ASTContext &Context) {
11487330f729Sjoerg CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
11497330f729Sjoerg Context.getSourceManager());
11507330f729Sjoerg Converter.visit(FC);
11517330f729Sjoerg }
1152