xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/Index/CommentToXML.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
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 << "&amp;";
5047330f729Sjoerg       break;
5057330f729Sjoerg     case '<':
5067330f729Sjoerg       Result << "&lt;";
5077330f729Sjoerg       break;
5087330f729Sjoerg     case '>':
5097330f729Sjoerg       Result << "&gt;";
5107330f729Sjoerg       break;
5117330f729Sjoerg     case '"':
5127330f729Sjoerg       Result << "&quot;";
5137330f729Sjoerg       break;
5147330f729Sjoerg     case '\'':
5157330f729Sjoerg       Result << "&#39;";
5167330f729Sjoerg       break;
5177330f729Sjoerg     case '/':
5187330f729Sjoerg       Result << "&#47;";
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 << ">&lt;/" << C->getTagName() << "&gt;</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>&lt;anonymous&gt;</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 << "&amp;";
10847330f729Sjoerg       break;
10857330f729Sjoerg     case '<':
10867330f729Sjoerg       Result << "&lt;";
10877330f729Sjoerg       break;
10887330f729Sjoerg     case '>':
10897330f729Sjoerg       Result << "&gt;";
10907330f729Sjoerg       break;
10917330f729Sjoerg     case '"':
10927330f729Sjoerg       Result << "&quot;";
10937330f729Sjoerg       break;
10947330f729Sjoerg     case '\'':
10957330f729Sjoerg       Result << "&apos;";
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