"Fossies" - the Fresh Open Source Software Archive

Member "cfe-9.0.0.src/lib/Index/CommentToXML.cpp" (24 Apr 2019, 34451 Bytes) of package /linux/misc/cfe-9.0.0.src.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "CommentToXML.cpp": 8.0.1_vs_9.0.0.

    1 //===--- CommentToXML.cpp - Convert comments to XML representation --------===//
    2 //
    3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
    4 // See https://llvm.org/LICENSE.txt for license information.
    5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
    6 //
    7 //===----------------------------------------------------------------------===//
    8 
    9 #include "clang/Index/CommentToXML.h"
   10 #include "clang/AST/ASTContext.h"
   11 #include "clang/AST/Attr.h"
   12 #include "clang/AST/Comment.h"
   13 #include "clang/AST/CommentVisitor.h"
   14 #include "clang/Format/Format.h"
   15 #include "clang/Index/USRGeneration.h"
   16 #include "llvm/ADT/StringExtras.h"
   17 #include "llvm/ADT/TinyPtrVector.h"
   18 #include "llvm/Support/raw_ostream.h"
   19 
   20 using namespace clang;
   21 using namespace clang::comments;
   22 using namespace clang::index;
   23 
   24 namespace {
   25 
   26 /// This comparison will sort parameters with valid index by index, then vararg
   27 /// parameters, and invalid (unresolved) parameters last.
   28 class ParamCommandCommentCompareIndex {
   29 public:
   30   bool operator()(const ParamCommandComment *LHS,
   31                   const ParamCommandComment *RHS) const {
   32     unsigned LHSIndex = UINT_MAX;
   33     unsigned RHSIndex = UINT_MAX;
   34 
   35     if (LHS->isParamIndexValid()) {
   36       if (LHS->isVarArgParam())
   37         LHSIndex = UINT_MAX - 1;
   38       else
   39         LHSIndex = LHS->getParamIndex();
   40     }
   41     if (RHS->isParamIndexValid()) {
   42       if (RHS->isVarArgParam())
   43         RHSIndex = UINT_MAX - 1;
   44       else
   45         RHSIndex = RHS->getParamIndex();
   46     }
   47     return LHSIndex < RHSIndex;
   48   }
   49 };
   50 
   51 /// This comparison will sort template parameters in the following order:
   52 /// \li real template parameters (depth = 1) in index order;
   53 /// \li all other names (depth > 1);
   54 /// \li unresolved names.
   55 class TParamCommandCommentComparePosition {
   56 public:
   57   bool operator()(const TParamCommandComment *LHS,
   58                   const TParamCommandComment *RHS) const {
   59     // Sort unresolved names last.
   60     if (!LHS->isPositionValid())
   61       return false;
   62     if (!RHS->isPositionValid())
   63       return true;
   64 
   65     if (LHS->getDepth() > 1)
   66       return false;
   67     if (RHS->getDepth() > 1)
   68       return true;
   69 
   70     // Sort template parameters in index order.
   71     if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
   72       return LHS->getIndex(0) < RHS->getIndex(0);
   73 
   74     // Leave all other names in source order.
   75     return true;
   76   }
   77 };
   78 
   79 /// Separate parts of a FullComment.
   80 struct FullCommentParts {
   81   /// Take a full comment apart and initialize members accordingly.
   82   FullCommentParts(const FullComment *C,
   83                    const CommandTraits &Traits);
   84 
   85   const BlockContentComment *Brief;
   86   const BlockContentComment *Headerfile;
   87   const ParagraphComment *FirstParagraph;
   88   SmallVector<const BlockCommandComment *, 4> Returns;
   89   SmallVector<const ParamCommandComment *, 8> Params;
   90   SmallVector<const TParamCommandComment *, 4> TParams;
   91   llvm::TinyPtrVector<const BlockCommandComment *> Exceptions;
   92   SmallVector<const BlockContentComment *, 8> MiscBlocks;
   93 };
   94 
   95 FullCommentParts::FullCommentParts(const FullComment *C,
   96                                    const CommandTraits &Traits) :
   97     Brief(nullptr), Headerfile(nullptr), FirstParagraph(nullptr) {
   98   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
   99        I != E; ++I) {
  100     const Comment *Child = *I;
  101     if (!Child)
  102       continue;
  103     switch (Child->getCommentKind()) {
  104     case Comment::NoCommentKind:
  105       continue;
  106 
  107     case Comment::ParagraphCommentKind: {
  108       const ParagraphComment *PC = cast<ParagraphComment>(Child);
  109       if (PC->isWhitespace())
  110         break;
  111       if (!FirstParagraph)
  112         FirstParagraph = PC;
  113 
  114       MiscBlocks.push_back(PC);
  115       break;
  116     }
  117 
  118     case Comment::BlockCommandCommentKind: {
  119       const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
  120       const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
  121       if (!Brief && Info->IsBriefCommand) {
  122         Brief = BCC;
  123         break;
  124       }
  125       if (!Headerfile && Info->IsHeaderfileCommand) {
  126         Headerfile = BCC;
  127         break;
  128       }
  129       if (Info->IsReturnsCommand) {
  130         Returns.push_back(BCC);
  131         break;
  132       }
  133       if (Info->IsThrowsCommand) {
  134         Exceptions.push_back(BCC);
  135         break;
  136       }
  137       MiscBlocks.push_back(BCC);
  138       break;
  139     }
  140 
  141     case Comment::ParamCommandCommentKind: {
  142       const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
  143       if (!PCC->hasParamName())
  144         break;
  145 
  146       if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
  147         break;
  148 
  149       Params.push_back(PCC);
  150       break;
  151     }
  152 
  153     case Comment::TParamCommandCommentKind: {
  154       const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
  155       if (!TPCC->hasParamName())
  156         break;
  157 
  158       if (!TPCC->hasNonWhitespaceParagraph())
  159         break;
  160 
  161       TParams.push_back(TPCC);
  162       break;
  163     }
  164 
  165     case Comment::VerbatimBlockCommentKind:
  166       MiscBlocks.push_back(cast<BlockCommandComment>(Child));
  167       break;
  168 
  169     case Comment::VerbatimLineCommentKind: {
  170       const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
  171       const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());
  172       if (!Info->IsDeclarationCommand)
  173         MiscBlocks.push_back(VLC);
  174       break;
  175     }
  176 
  177     case Comment::TextCommentKind:
  178     case Comment::InlineCommandCommentKind:
  179     case Comment::HTMLStartTagCommentKind:
  180     case Comment::HTMLEndTagCommentKind:
  181     case Comment::VerbatimBlockLineCommentKind:
  182     case Comment::FullCommentKind:
  183       llvm_unreachable("AST node of this kind can't be a child of "
  184                        "a FullComment");
  185     }
  186   }
  187 
  188   // Sort params in order they are declared in the function prototype.
  189   // Unresolved parameters are put at the end of the list in the same order
  190   // they were seen in the comment.
  191   llvm::stable_sort(Params, ParamCommandCommentCompareIndex());
  192   llvm::stable_sort(TParams, TParamCommandCommentComparePosition());
  193 }
  194 
  195 void printHTMLStartTagComment(const HTMLStartTagComment *C,
  196                               llvm::raw_svector_ostream &Result) {
  197   Result << "<" << C->getTagName();
  198 
  199   if (C->getNumAttrs() != 0) {
  200     for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
  201       Result << " ";
  202       const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
  203       Result << Attr.Name;
  204       if (!Attr.Value.empty())
  205         Result << "=\"" << Attr.Value << "\"";
  206     }
  207   }
  208 
  209   if (!C->isSelfClosing())
  210     Result << ">";
  211   else
  212     Result << "/>";
  213 }
  214 
  215 class CommentASTToHTMLConverter :
  216     public ConstCommentVisitor<CommentASTToHTMLConverter> {
  217 public:
  218   /// \param Str accumulator for HTML.
  219   CommentASTToHTMLConverter(const FullComment *FC,
  220                             SmallVectorImpl<char> &Str,
  221                             const CommandTraits &Traits) :
  222       FC(FC), Result(Str), Traits(Traits)
  223   { }
  224 
  225   // Inline content.
  226   void visitTextComment(const TextComment *C);
  227   void visitInlineCommandComment(const InlineCommandComment *C);
  228   void visitHTMLStartTagComment(const HTMLStartTagComment *C);
  229   void visitHTMLEndTagComment(const HTMLEndTagComment *C);
  230 
  231   // Block content.
  232   void visitParagraphComment(const ParagraphComment *C);
  233   void visitBlockCommandComment(const BlockCommandComment *C);
  234   void visitParamCommandComment(const ParamCommandComment *C);
  235   void visitTParamCommandComment(const TParamCommandComment *C);
  236   void visitVerbatimBlockComment(const VerbatimBlockComment *C);
  237   void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
  238   void visitVerbatimLineComment(const VerbatimLineComment *C);
  239 
  240   void visitFullComment(const FullComment *C);
  241 
  242   // Helpers.
  243 
  244   /// Convert a paragraph that is not a block by itself (an argument to some
  245   /// command).
  246   void visitNonStandaloneParagraphComment(const ParagraphComment *C);
  247 
  248   void appendToResultWithHTMLEscaping(StringRef S);
  249 
  250 private:
  251   const FullComment *FC;
  252   /// Output stream for HTML.
  253   llvm::raw_svector_ostream Result;
  254 
  255   const CommandTraits &Traits;
  256 };
  257 } // end unnamed namespace
  258 
  259 void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
  260   appendToResultWithHTMLEscaping(C->getText());
  261 }
  262 
  263 void CommentASTToHTMLConverter::visitInlineCommandComment(
  264                                   const InlineCommandComment *C) {
  265   // Nothing to render if no arguments supplied.
  266   if (C->getNumArgs() == 0)
  267     return;
  268 
  269   // Nothing to render if argument is empty.
  270   StringRef Arg0 = C->getArgText(0);
  271   if (Arg0.empty())
  272     return;
  273 
  274   switch (C->getRenderKind()) {
  275   case InlineCommandComment::RenderNormal:
  276     for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
  277       appendToResultWithHTMLEscaping(C->getArgText(i));
  278       Result << " ";
  279     }
  280     return;
  281 
  282   case InlineCommandComment::RenderBold:
  283     assert(C->getNumArgs() == 1);
  284     Result << "<b>";
  285     appendToResultWithHTMLEscaping(Arg0);
  286     Result << "</b>";
  287     return;
  288   case InlineCommandComment::RenderMonospaced:
  289     assert(C->getNumArgs() == 1);
  290     Result << "<tt>";
  291     appendToResultWithHTMLEscaping(Arg0);
  292     Result<< "</tt>";
  293     return;
  294   case InlineCommandComment::RenderEmphasized:
  295     assert(C->getNumArgs() == 1);
  296     Result << "<em>";
  297     appendToResultWithHTMLEscaping(Arg0);
  298     Result << "</em>";
  299     return;
  300   }
  301 }
  302 
  303 void CommentASTToHTMLConverter::visitHTMLStartTagComment(
  304                                   const HTMLStartTagComment *C) {
  305   printHTMLStartTagComment(C, Result);
  306 }
  307 
  308 void CommentASTToHTMLConverter::visitHTMLEndTagComment(
  309                                   const HTMLEndTagComment *C) {
  310   Result << "</" << C->getTagName() << ">";
  311 }
  312 
  313 void CommentASTToHTMLConverter::visitParagraphComment(
  314                                   const ParagraphComment *C) {
  315   if (C->isWhitespace())
  316     return;
  317 
  318   Result << "<p>";
  319   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
  320        I != E; ++I) {
  321     visit(*I);
  322   }
  323   Result << "</p>";
  324 }
  325 
  326 void CommentASTToHTMLConverter::visitBlockCommandComment(
  327                                   const BlockCommandComment *C) {
  328   const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
  329   if (Info->IsBriefCommand) {
  330     Result << "<p class=\"para-brief\">";
  331     visitNonStandaloneParagraphComment(C->getParagraph());
  332     Result << "</p>";
  333     return;
  334   }
  335   if (Info->IsReturnsCommand) {
  336     Result << "<p class=\"para-returns\">"
  337               "<span class=\"word-returns\">Returns</span> ";
  338     visitNonStandaloneParagraphComment(C->getParagraph());
  339     Result << "</p>";
  340     return;
  341   }
  342   // We don't know anything about this command.  Just render the paragraph.
  343   visit(C->getParagraph());
  344 }
  345 
  346 void CommentASTToHTMLConverter::visitParamCommandComment(
  347                                   const ParamCommandComment *C) {
  348   if (C->isParamIndexValid()) {
  349     if (C->isVarArgParam()) {
  350       Result << "<dt class=\"param-name-index-vararg\">";
  351       appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
  352     } else {
  353       Result << "<dt class=\"param-name-index-"
  354              << C->getParamIndex()
  355              << "\">";
  356       appendToResultWithHTMLEscaping(C->getParamName(FC));
  357     }
  358   } else {
  359     Result << "<dt class=\"param-name-index-invalid\">";
  360     appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
  361   }
  362   Result << "</dt>";
  363 
  364   if (C->isParamIndexValid()) {
  365     if (C->isVarArgParam())
  366       Result << "<dd class=\"param-descr-index-vararg\">";
  367     else
  368       Result << "<dd class=\"param-descr-index-"
  369              << C->getParamIndex()
  370              << "\">";
  371   } else
  372     Result << "<dd class=\"param-descr-index-invalid\">";
  373 
  374   visitNonStandaloneParagraphComment(C->getParagraph());
  375   Result << "</dd>";
  376 }
  377 
  378 void CommentASTToHTMLConverter::visitTParamCommandComment(
  379                                   const TParamCommandComment *C) {
  380   if (C->isPositionValid()) {
  381     if (C->getDepth() == 1)
  382       Result << "<dt class=\"tparam-name-index-"
  383              << C->getIndex(0)
  384              << "\">";
  385     else
  386       Result << "<dt class=\"tparam-name-index-other\">";
  387     appendToResultWithHTMLEscaping(C->getParamName(FC));
  388   } else {
  389     Result << "<dt class=\"tparam-name-index-invalid\">";
  390     appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
  391   }
  392 
  393   Result << "</dt>";
  394 
  395   if (C->isPositionValid()) {
  396     if (C->getDepth() == 1)
  397       Result << "<dd class=\"tparam-descr-index-"
  398              << C->getIndex(0)
  399              << "\">";
  400     else
  401       Result << "<dd class=\"tparam-descr-index-other\">";
  402   } else
  403     Result << "<dd class=\"tparam-descr-index-invalid\">";
  404 
  405   visitNonStandaloneParagraphComment(C->getParagraph());
  406   Result << "</dd>";
  407 }
  408 
  409 void CommentASTToHTMLConverter::visitVerbatimBlockComment(
  410                                   const VerbatimBlockComment *C) {
  411   unsigned NumLines = C->getNumLines();
  412   if (NumLines == 0)
  413     return;
  414 
  415   Result << "<pre>";
  416   for (unsigned i = 0; i != NumLines; ++i) {
  417     appendToResultWithHTMLEscaping(C->getText(i));
  418     if (i + 1 != NumLines)
  419       Result << '\n';
  420   }
  421   Result << "</pre>";
  422 }
  423 
  424 void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
  425                                   const VerbatimBlockLineComment *C) {
  426   llvm_unreachable("should not see this AST node");
  427 }
  428 
  429 void CommentASTToHTMLConverter::visitVerbatimLineComment(
  430                                   const VerbatimLineComment *C) {
  431   Result << "<pre>";
  432   appendToResultWithHTMLEscaping(C->getText());
  433   Result << "</pre>";
  434 }
  435 
  436 void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
  437   FullCommentParts Parts(C, Traits);
  438 
  439   bool FirstParagraphIsBrief = false;
  440   if (Parts.Headerfile)
  441     visit(Parts.Headerfile);
  442   if (Parts.Brief)
  443     visit(Parts.Brief);
  444   else if (Parts.FirstParagraph) {
  445     Result << "<p class=\"para-brief\">";
  446     visitNonStandaloneParagraphComment(Parts.FirstParagraph);
  447     Result << "</p>";
  448     FirstParagraphIsBrief = true;
  449   }
  450 
  451   for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
  452     const Comment *C = Parts.MiscBlocks[i];
  453     if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
  454       continue;
  455     visit(C);
  456   }
  457 
  458   if (Parts.TParams.size() != 0) {
  459     Result << "<dl>";
  460     for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
  461       visit(Parts.TParams[i]);
  462     Result << "</dl>";
  463   }
  464 
  465   if (Parts.Params.size() != 0) {
  466     Result << "<dl>";
  467     for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
  468       visit(Parts.Params[i]);
  469     Result << "</dl>";
  470   }
  471 
  472   if (Parts.Returns.size() != 0) {
  473     Result << "<div class=\"result-discussion\">";
  474     for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
  475       visit(Parts.Returns[i]);
  476     Result << "</div>";
  477   }
  478 
  479 }
  480 
  481 void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
  482                                   const ParagraphComment *C) {
  483   if (!C)
  484     return;
  485 
  486   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
  487        I != E; ++I) {
  488     visit(*I);
  489   }
  490 }
  491 
  492 void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
  493   for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
  494     const char C = *I;
  495     switch (C) {
  496     case '&':
  497       Result << "&amp;";
  498       break;
  499     case '<':
  500       Result << "&lt;";
  501       break;
  502     case '>':
  503       Result << "&gt;";
  504       break;
  505     case '"':
  506       Result << "&quot;";
  507       break;
  508     case '\'':
  509       Result << "&#39;";
  510       break;
  511     case '/':
  512       Result << "&#47;";
  513       break;
  514     default:
  515       Result << C;
  516       break;
  517     }
  518   }
  519 }
  520 
  521 namespace {
  522 class CommentASTToXMLConverter :
  523     public ConstCommentVisitor<CommentASTToXMLConverter> {
  524 public:
  525   /// \param Str accumulator for XML.
  526   CommentASTToXMLConverter(const FullComment *FC,
  527                            SmallVectorImpl<char> &Str,
  528                            const CommandTraits &Traits,
  529                            const SourceManager &SM) :
  530       FC(FC), Result(Str), Traits(Traits), SM(SM) { }
  531 
  532   // Inline content.
  533   void visitTextComment(const TextComment *C);
  534   void visitInlineCommandComment(const InlineCommandComment *C);
  535   void visitHTMLStartTagComment(const HTMLStartTagComment *C);
  536   void visitHTMLEndTagComment(const HTMLEndTagComment *C);
  537 
  538   // Block content.
  539   void visitParagraphComment(const ParagraphComment *C);
  540 
  541   void appendParagraphCommentWithKind(const ParagraphComment *C,
  542                                       StringRef Kind);
  543 
  544   void visitBlockCommandComment(const BlockCommandComment *C);
  545   void visitParamCommandComment(const ParamCommandComment *C);
  546   void visitTParamCommandComment(const TParamCommandComment *C);
  547   void visitVerbatimBlockComment(const VerbatimBlockComment *C);
  548   void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
  549   void visitVerbatimLineComment(const VerbatimLineComment *C);
  550 
  551   void visitFullComment(const FullComment *C);
  552 
  553   // Helpers.
  554   void appendToResultWithXMLEscaping(StringRef S);
  555   void appendToResultWithCDATAEscaping(StringRef S);
  556 
  557   void formatTextOfDeclaration(const DeclInfo *DI,
  558                                SmallString<128> &Declaration);
  559 
  560 private:
  561   const FullComment *FC;
  562 
  563   /// Output stream for XML.
  564   llvm::raw_svector_ostream Result;
  565 
  566   const CommandTraits &Traits;
  567   const SourceManager &SM;
  568 };
  569 
  570 void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
  571                                 SmallVectorImpl<char> &Str) {
  572   ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
  573   const LangOptions &LangOpts = Context.getLangOpts();
  574   llvm::raw_svector_ostream OS(Str);
  575   PrintingPolicy PPolicy(LangOpts);
  576   PPolicy.PolishForDeclaration = true;
  577   PPolicy.TerseOutput = true;
  578   PPolicy.ConstantsAsWritten = true;
  579   ThisDecl->CurrentDecl->print(OS, PPolicy,
  580                                /*Indentation*/0, /*PrintInstantiation*/false);
  581 }
  582 
  583 void CommentASTToXMLConverter::formatTextOfDeclaration(
  584     const DeclInfo *DI, SmallString<128> &Declaration) {
  585   // Formatting API expects null terminated input string.
  586   StringRef StringDecl(Declaration.c_str(), Declaration.size());
  587 
  588   // Formatter specific code.
  589   unsigned Offset = 0;
  590   unsigned Length = Declaration.size();
  591 
  592   format::FormatStyle Style = format::getLLVMStyle();
  593   Style.FixNamespaceComments = false;
  594   tooling::Replacements Replaces =
  595       reformat(Style, StringDecl, tooling::Range(Offset, Length), "xmldecl.xd");
  596   auto FormattedStringDecl = applyAllReplacements(StringDecl, Replaces);
  597   if (static_cast<bool>(FormattedStringDecl)) {
  598     Declaration = *FormattedStringDecl;
  599   }
  600 }
  601 
  602 } // end unnamed namespace
  603 
  604 void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
  605   appendToResultWithXMLEscaping(C->getText());
  606 }
  607 
  608 void CommentASTToXMLConverter::visitInlineCommandComment(
  609     const InlineCommandComment *C) {
  610   // Nothing to render if no arguments supplied.
  611   if (C->getNumArgs() == 0)
  612     return;
  613 
  614   // Nothing to render if argument is empty.
  615   StringRef Arg0 = C->getArgText(0);
  616   if (Arg0.empty())
  617     return;
  618 
  619   switch (C->getRenderKind()) {
  620   case InlineCommandComment::RenderNormal:
  621     for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
  622       appendToResultWithXMLEscaping(C->getArgText(i));
  623       Result << " ";
  624     }
  625     return;
  626   case InlineCommandComment::RenderBold:
  627     assert(C->getNumArgs() == 1);
  628     Result << "<bold>";
  629     appendToResultWithXMLEscaping(Arg0);
  630     Result << "</bold>";
  631     return;
  632   case InlineCommandComment::RenderMonospaced:
  633     assert(C->getNumArgs() == 1);
  634     Result << "<monospaced>";
  635     appendToResultWithXMLEscaping(Arg0);
  636     Result << "</monospaced>";
  637     return;
  638   case InlineCommandComment::RenderEmphasized:
  639     assert(C->getNumArgs() == 1);
  640     Result << "<emphasized>";
  641     appendToResultWithXMLEscaping(Arg0);
  642     Result << "</emphasized>";
  643     return;
  644   }
  645 }
  646 
  647 void CommentASTToXMLConverter::visitHTMLStartTagComment(
  648     const HTMLStartTagComment *C) {
  649   Result << "<rawHTML";
  650   if (C->isMalformed())
  651     Result << " isMalformed=\"1\"";
  652   Result << ">";
  653   {
  654     SmallString<32> Tag;
  655     {
  656       llvm::raw_svector_ostream TagOS(Tag);
  657       printHTMLStartTagComment(C, TagOS);
  658     }
  659     appendToResultWithCDATAEscaping(Tag);
  660   }
  661   Result << "</rawHTML>";
  662 }
  663 
  664 void
  665 CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
  666   Result << "<rawHTML";
  667   if (C->isMalformed())
  668     Result << " isMalformed=\"1\"";
  669   Result << ">&lt;/" << C->getTagName() << "&gt;</rawHTML>";
  670 }
  671 
  672 void
  673 CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
  674   appendParagraphCommentWithKind(C, StringRef());
  675 }
  676 
  677 void CommentASTToXMLConverter::appendParagraphCommentWithKind(
  678                                   const ParagraphComment *C,
  679                                   StringRef ParagraphKind) {
  680   if (C->isWhitespace())
  681     return;
  682 
  683   if (ParagraphKind.empty())
  684     Result << "<Para>";
  685   else
  686     Result << "<Para kind=\"" << ParagraphKind << "\">";
  687 
  688   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
  689        I != E; ++I) {
  690     visit(*I);
  691   }
  692   Result << "</Para>";
  693 }
  694 
  695 void CommentASTToXMLConverter::visitBlockCommandComment(
  696     const BlockCommandComment *C) {
  697   StringRef ParagraphKind;
  698 
  699   switch (C->getCommandID()) {
  700   case CommandTraits::KCI_attention:
  701   case CommandTraits::KCI_author:
  702   case CommandTraits::KCI_authors:
  703   case CommandTraits::KCI_bug:
  704   case CommandTraits::KCI_copyright:
  705   case CommandTraits::KCI_date:
  706   case CommandTraits::KCI_invariant:
  707   case CommandTraits::KCI_note:
  708   case CommandTraits::KCI_post:
  709   case CommandTraits::KCI_pre:
  710   case CommandTraits::KCI_remark:
  711   case CommandTraits::KCI_remarks:
  712   case CommandTraits::KCI_sa:
  713   case CommandTraits::KCI_see:
  714   case CommandTraits::KCI_since:
  715   case CommandTraits::KCI_todo:
  716   case CommandTraits::KCI_version:
  717   case CommandTraits::KCI_warning:
  718     ParagraphKind = C->getCommandName(Traits);
  719     break;
  720   default:
  721     break;
  722   }
  723 
  724   appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
  725 }
  726 
  727 void CommentASTToXMLConverter::visitParamCommandComment(
  728     const ParamCommandComment *C) {
  729   Result << "<Parameter><Name>";
  730   appendToResultWithXMLEscaping(C->isParamIndexValid()
  731                                     ? C->getParamName(FC)
  732                                     : C->getParamNameAsWritten());
  733   Result << "</Name>";
  734 
  735   if (C->isParamIndexValid()) {
  736     if (C->isVarArgParam())
  737       Result << "<IsVarArg />";
  738     else
  739       Result << "<Index>" << C->getParamIndex() << "</Index>";
  740   }
  741 
  742   Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
  743   switch (C->getDirection()) {
  744   case ParamCommandComment::In:
  745     Result << "in";
  746     break;
  747   case ParamCommandComment::Out:
  748     Result << "out";
  749     break;
  750   case ParamCommandComment::InOut:
  751     Result << "in,out";
  752     break;
  753   }
  754   Result << "</Direction><Discussion>";
  755   visit(C->getParagraph());
  756   Result << "</Discussion></Parameter>";
  757 }
  758 
  759 void CommentASTToXMLConverter::visitTParamCommandComment(
  760                                   const TParamCommandComment *C) {
  761   Result << "<Parameter><Name>";
  762   appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
  763                                 : C->getParamNameAsWritten());
  764   Result << "</Name>";
  765 
  766   if (C->isPositionValid() && C->getDepth() == 1) {
  767     Result << "<Index>" << C->getIndex(0) << "</Index>";
  768   }
  769 
  770   Result << "<Discussion>";
  771   visit(C->getParagraph());
  772   Result << "</Discussion></Parameter>";
  773 }
  774 
  775 void CommentASTToXMLConverter::visitVerbatimBlockComment(
  776                                   const VerbatimBlockComment *C) {
  777   unsigned NumLines = C->getNumLines();
  778   if (NumLines == 0)
  779     return;
  780 
  781   switch (C->getCommandID()) {
  782   case CommandTraits::KCI_code:
  783     Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
  784     break;
  785   default:
  786     Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
  787     break;
  788   }
  789   for (unsigned i = 0; i != NumLines; ++i) {
  790     appendToResultWithXMLEscaping(C->getText(i));
  791     if (i + 1 != NumLines)
  792       Result << '\n';
  793   }
  794   Result << "</Verbatim>";
  795 }
  796 
  797 void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
  798                                   const VerbatimBlockLineComment *C) {
  799   llvm_unreachable("should not see this AST node");
  800 }
  801 
  802 void CommentASTToXMLConverter::visitVerbatimLineComment(
  803                                   const VerbatimLineComment *C) {
  804   Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
  805   appendToResultWithXMLEscaping(C->getText());
  806   Result << "</Verbatim>";
  807 }
  808 
  809 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
  810   FullCommentParts Parts(C, Traits);
  811 
  812   const DeclInfo *DI = C->getDeclInfo();
  813   StringRef RootEndTag;
  814   if (DI) {
  815     switch (DI->getKind()) {
  816     case DeclInfo::OtherKind:
  817       RootEndTag = "</Other>";
  818       Result << "<Other";
  819       break;
  820     case DeclInfo::FunctionKind:
  821       RootEndTag = "</Function>";
  822       Result << "<Function";
  823       switch (DI->TemplateKind) {
  824       case DeclInfo::NotTemplate:
  825         break;
  826       case DeclInfo::Template:
  827         Result << " templateKind=\"template\"";
  828         break;
  829       case DeclInfo::TemplateSpecialization:
  830         Result << " templateKind=\"specialization\"";
  831         break;
  832       case DeclInfo::TemplatePartialSpecialization:
  833         llvm_unreachable("partial specializations of functions "
  834                          "are not allowed in C++");
  835       }
  836       if (DI->IsInstanceMethod)
  837         Result << " isInstanceMethod=\"1\"";
  838       if (DI->IsClassMethod)
  839         Result << " isClassMethod=\"1\"";
  840       break;
  841     case DeclInfo::ClassKind:
  842       RootEndTag = "</Class>";
  843       Result << "<Class";
  844       switch (DI->TemplateKind) {
  845       case DeclInfo::NotTemplate:
  846         break;
  847       case DeclInfo::Template:
  848         Result << " templateKind=\"template\"";
  849         break;
  850       case DeclInfo::TemplateSpecialization:
  851         Result << " templateKind=\"specialization\"";
  852         break;
  853       case DeclInfo::TemplatePartialSpecialization:
  854         Result << " templateKind=\"partialSpecialization\"";
  855         break;
  856       }
  857       break;
  858     case DeclInfo::VariableKind:
  859       RootEndTag = "</Variable>";
  860       Result << "<Variable";
  861       break;
  862     case DeclInfo::NamespaceKind:
  863       RootEndTag = "</Namespace>";
  864       Result << "<Namespace";
  865       break;
  866     case DeclInfo::TypedefKind:
  867       RootEndTag = "</Typedef>";
  868       Result << "<Typedef";
  869       break;
  870     case DeclInfo::EnumKind:
  871       RootEndTag = "</Enum>";
  872       Result << "<Enum";
  873       break;
  874     }
  875 
  876     {
  877       // Print line and column number.
  878       SourceLocation Loc = DI->CurrentDecl->getLocation();
  879       std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
  880       FileID FID = LocInfo.first;
  881       unsigned FileOffset = LocInfo.second;
  882 
  883       if (FID.isValid()) {
  884         if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
  885           Result << " file=\"";
  886           appendToResultWithXMLEscaping(FE->getName());
  887           Result << "\"";
  888         }
  889         Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
  890                << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
  891                << "\"";
  892       }
  893     }
  894 
  895     // Finish the root tag.
  896     Result << ">";
  897 
  898     bool FoundName = false;
  899     if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
  900       if (DeclarationName DeclName = ND->getDeclName()) {
  901         Result << "<Name>";
  902         std::string Name = DeclName.getAsString();
  903         appendToResultWithXMLEscaping(Name);
  904         FoundName = true;
  905         Result << "</Name>";
  906       }
  907     }
  908     if (!FoundName)
  909       Result << "<Name>&lt;anonymous&gt;</Name>";
  910 
  911     {
  912       // Print USR.
  913       SmallString<128> USR;
  914       generateUSRForDecl(DI->CommentDecl, USR);
  915       if (!USR.empty()) {
  916         Result << "<USR>";
  917         appendToResultWithXMLEscaping(USR);
  918         Result << "</USR>";
  919       }
  920     }
  921   } else {
  922     // No DeclInfo -- just emit some root tag and name tag.
  923     RootEndTag = "</Other>";
  924     Result << "<Other><Name>unknown</Name>";
  925   }
  926 
  927   if (Parts.Headerfile) {
  928     Result << "<Headerfile>";
  929     visit(Parts.Headerfile);
  930     Result << "</Headerfile>";
  931   }
  932 
  933   {
  934     // Pretty-print the declaration.
  935     Result << "<Declaration>";
  936     SmallString<128> Declaration;
  937     getSourceTextOfDeclaration(DI, Declaration);
  938     formatTextOfDeclaration(DI, Declaration);
  939     appendToResultWithXMLEscaping(Declaration);
  940     Result << "</Declaration>";
  941   }
  942 
  943   bool FirstParagraphIsBrief = false;
  944   if (Parts.Brief) {
  945     Result << "<Abstract>";
  946     visit(Parts.Brief);
  947     Result << "</Abstract>";
  948   } else if (Parts.FirstParagraph) {
  949     Result << "<Abstract>";
  950     visit(Parts.FirstParagraph);
  951     Result << "</Abstract>";
  952     FirstParagraphIsBrief = true;
  953   }
  954 
  955   if (Parts.TParams.size() != 0) {
  956     Result << "<TemplateParameters>";
  957     for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
  958       visit(Parts.TParams[i]);
  959     Result << "</TemplateParameters>";
  960   }
  961 
  962   if (Parts.Params.size() != 0) {
  963     Result << "<Parameters>";
  964     for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
  965       visit(Parts.Params[i]);
  966     Result << "</Parameters>";
  967   }
  968 
  969   if (Parts.Exceptions.size() != 0) {
  970     Result << "<Exceptions>";
  971     for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
  972       visit(Parts.Exceptions[i]);
  973     Result << "</Exceptions>";
  974   }
  975 
  976   if (Parts.Returns.size() != 0) {
  977     Result << "<ResultDiscussion>";
  978     for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
  979       visit(Parts.Returns[i]);
  980     Result << "</ResultDiscussion>";
  981   }
  982 
  983   if (DI->CommentDecl->hasAttrs()) {
  984     const AttrVec &Attrs = DI->CommentDecl->getAttrs();
  985     for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
  986       const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
  987       if (!AA) {
  988         if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
  989           if (DA->getMessage().empty())
  990             Result << "<Deprecated/>";
  991           else {
  992             Result << "<Deprecated>";
  993             appendToResultWithXMLEscaping(DA->getMessage());
  994             Result << "</Deprecated>";
  995           }
  996         }
  997         else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
  998           if (UA->getMessage().empty())
  999             Result << "<Unavailable/>";
 1000           else {
 1001             Result << "<Unavailable>";
 1002             appendToResultWithXMLEscaping(UA->getMessage());
 1003             Result << "</Unavailable>";
 1004           }
 1005         }
 1006         continue;
 1007       }
 1008 
 1009       // 'availability' attribute.
 1010       Result << "<Availability";
 1011       StringRef Distribution;
 1012       if (AA->getPlatform()) {
 1013         Distribution = AvailabilityAttr::getPrettyPlatformName(
 1014                                         AA->getPlatform()->getName());
 1015         if (Distribution.empty())
 1016           Distribution = AA->getPlatform()->getName();
 1017       }
 1018       Result << " distribution=\"" << Distribution << "\">";
 1019       VersionTuple IntroducedInVersion = AA->getIntroduced();
 1020       if (!IntroducedInVersion.empty()) {
 1021         Result << "<IntroducedInVersion>"
 1022                << IntroducedInVersion.getAsString()
 1023                << "</IntroducedInVersion>";
 1024       }
 1025       VersionTuple DeprecatedInVersion = AA->getDeprecated();
 1026       if (!DeprecatedInVersion.empty()) {
 1027         Result << "<DeprecatedInVersion>"
 1028                << DeprecatedInVersion.getAsString()
 1029                << "</DeprecatedInVersion>";
 1030       }
 1031       VersionTuple RemovedAfterVersion = AA->getObsoleted();
 1032       if (!RemovedAfterVersion.empty()) {
 1033         Result << "<RemovedAfterVersion>"
 1034                << RemovedAfterVersion.getAsString()
 1035                << "</RemovedAfterVersion>";
 1036       }
 1037       StringRef DeprecationSummary = AA->getMessage();
 1038       if (!DeprecationSummary.empty()) {
 1039         Result << "<DeprecationSummary>";
 1040         appendToResultWithXMLEscaping(DeprecationSummary);
 1041         Result << "</DeprecationSummary>";
 1042       }
 1043       if (AA->getUnavailable())
 1044         Result << "<Unavailable/>";
 1045       Result << "</Availability>";
 1046     }
 1047   }
 1048 
 1049   {
 1050     bool StartTagEmitted = false;
 1051     for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
 1052       const Comment *C = Parts.MiscBlocks[i];
 1053       if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
 1054         continue;
 1055       if (!StartTagEmitted) {
 1056         Result << "<Discussion>";
 1057         StartTagEmitted = true;
 1058       }
 1059       visit(C);
 1060     }
 1061     if (StartTagEmitted)
 1062       Result << "</Discussion>";
 1063   }
 1064 
 1065   Result << RootEndTag;
 1066 }
 1067 
 1068 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
 1069   for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
 1070     const char C = *I;
 1071     switch (C) {
 1072     case '&':
 1073       Result << "&amp;";
 1074       break;
 1075     case '<':
 1076       Result << "&lt;";
 1077       break;
 1078     case '>':
 1079       Result << "&gt;";
 1080       break;
 1081     case '"':
 1082       Result << "&quot;";
 1083       break;
 1084     case '\'':
 1085       Result << "&apos;";
 1086       break;
 1087     default:
 1088       Result << C;
 1089       break;
 1090     }
 1091   }
 1092 }
 1093 
 1094 void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) {
 1095   if (S.empty())
 1096     return;
 1097 
 1098   Result << "<![CDATA[";
 1099   while (!S.empty()) {
 1100     size_t Pos = S.find("]]>");
 1101     if (Pos == 0) {
 1102       Result << "]]]]><![CDATA[>";
 1103       S = S.drop_front(3);
 1104       continue;
 1105     }
 1106     if (Pos == StringRef::npos)
 1107       Pos = S.size();
 1108 
 1109     Result << S.substr(0, Pos);
 1110 
 1111     S = S.drop_front(Pos);
 1112   }
 1113   Result << "]]>";
 1114 }
 1115 
 1116 CommentToXMLConverter::CommentToXMLConverter() {}
 1117 CommentToXMLConverter::~CommentToXMLConverter() {}
 1118 
 1119 void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC,
 1120                                                  SmallVectorImpl<char> &HTML,
 1121                                                  const ASTContext &Context) {
 1122   CommentASTToHTMLConverter Converter(FC, HTML,
 1123                                       Context.getCommentCommandTraits());
 1124   Converter.visit(FC);
 1125 }
 1126 
 1127 void CommentToXMLConverter::convertHTMLTagNodeToText(
 1128     const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text,
 1129     const ASTContext &Context) {
 1130   CommentASTToHTMLConverter Converter(nullptr, Text,
 1131                                       Context.getCommentCommandTraits());
 1132   Converter.visit(HTC);
 1133 }
 1134 
 1135 void CommentToXMLConverter::convertCommentToXML(const FullComment *FC,
 1136                                                 SmallVectorImpl<char> &XML,
 1137                                                 const ASTContext &Context) {
 1138   CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
 1139                                      Context.getSourceManager());
 1140   Converter.visit(FC);
 1141 }