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