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   // FIXME. formatting API expects null terminated input string.
    596   // There might be more efficient way of doing this.
    597   std::string StringDecl = Declaration.str();
    598 
    599   // Formatter specific code.
    600   // Form a unique in memory buffer name.
    601   SmallString<128> filename;
    602   filename += "xmldecl";
    603   filename += llvm::utostr(FormatInMemoryUniqueId);
    604   filename += ".xd";
    605   FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl);
    606   SourceLocation Start = FormatRewriterContext.Sources.getLocForStartOfFile(ID)
    607       .getLocWithOffset(0);
    608   unsigned Length = Declaration.size();
    609 
    610   tooling::Replacements Replace = reformat(
    611       format::getLLVMStyle(), FormatRewriterContext.Sources, ID,
    612       CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length)));
    613   applyAllReplacements(Replace, FormatRewriterContext.Rewrite);
    614   Declaration = FormatRewriterContext.getRewrittenText(ID);
    615 }
    616 
    617 } // end unnamed namespace
    618 
    619 void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
    620   appendToResultWithXMLEscaping(C->getText());
    621 }
    622 
    623 void CommentASTToXMLConverter::visitInlineCommandComment(
    624     const InlineCommandComment *C) {
    625   // Nothing to render if no arguments supplied.
    626   if (C->getNumArgs() == 0)
    627     return;
    628 
    629   // Nothing to render if argument is empty.
    630   StringRef Arg0 = C->getArgText(0);
    631   if (Arg0.empty())
    632     return;
    633 
    634   switch (C->getRenderKind()) {
    635   case InlineCommandComment::RenderNormal:
    636     for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
    637       appendToResultWithXMLEscaping(C->getArgText(i));
    638       Result << " ";
    639     }
    640     return;
    641   case InlineCommandComment::RenderBold:
    642     assert(C->getNumArgs() == 1);
    643     Result << "<bold>";
    644     appendToResultWithXMLEscaping(Arg0);
    645     Result << "</bold>";
    646     return;
    647   case InlineCommandComment::RenderMonospaced:
    648     assert(C->getNumArgs() == 1);
    649     Result << "<monospaced>";
    650     appendToResultWithXMLEscaping(Arg0);
    651     Result << "</monospaced>";
    652     return;
    653   case InlineCommandComment::RenderEmphasized:
    654     assert(C->getNumArgs() == 1);
    655     Result << "<emphasized>";
    656     appendToResultWithXMLEscaping(Arg0);
    657     Result << "</emphasized>";
    658     return;
    659   }
    660 }
    661 
    662 void CommentASTToXMLConverter::visitHTMLStartTagComment(
    663     const HTMLStartTagComment *C) {
    664   Result << "<rawHTML";
    665   if (C->isMalformed())
    666     Result << " isMalformed=\"1\"";
    667   Result << ">";
    668   {
    669     SmallString<32> Tag;
    670     {
    671       llvm::raw_svector_ostream TagOS(Tag);
    672       printHTMLStartTagComment(C, TagOS);
    673     }
    674     appendToResultWithCDATAEscaping(Tag);
    675   }
    676   Result << "</rawHTML>";
    677 }
    678 
    679 void
    680 CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
    681   Result << "<rawHTML";
    682   if (C->isMalformed())
    683     Result << " isMalformed=\"1\"";
    684   Result << ">&lt;/" << C->getTagName() << "&gt;</rawHTML>";
    685 }
    686 
    687 void
    688 CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
    689   appendParagraphCommentWithKind(C, StringRef());
    690 }
    691 
    692 void CommentASTToXMLConverter::appendParagraphCommentWithKind(
    693                                   const ParagraphComment *C,
    694                                   StringRef ParagraphKind) {
    695   if (C->isWhitespace())
    696     return;
    697 
    698   if (ParagraphKind.empty())
    699     Result << "<Para>";
    700   else
    701     Result << "<Para kind=\"" << ParagraphKind << "\">";
    702 
    703   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
    704        I != E; ++I) {
    705     visit(*I);
    706   }
    707   Result << "</Para>";
    708 }
    709 
    710 void CommentASTToXMLConverter::visitBlockCommandComment(
    711     const BlockCommandComment *C) {
    712   StringRef ParagraphKind;
    713 
    714   switch (C->getCommandID()) {
    715   case CommandTraits::KCI_attention:
    716   case CommandTraits::KCI_author:
    717   case CommandTraits::KCI_authors:
    718   case CommandTraits::KCI_bug:
    719   case CommandTraits::KCI_copyright:
    720   case CommandTraits::KCI_date:
    721   case CommandTraits::KCI_invariant:
    722   case CommandTraits::KCI_note:
    723   case CommandTraits::KCI_post:
    724   case CommandTraits::KCI_pre:
    725   case CommandTraits::KCI_remark:
    726   case CommandTraits::KCI_remarks:
    727   case CommandTraits::KCI_sa:
    728   case CommandTraits::KCI_see:
    729   case CommandTraits::KCI_since:
    730   case CommandTraits::KCI_todo:
    731   case CommandTraits::KCI_version:
    732   case CommandTraits::KCI_warning:
    733     ParagraphKind = C->getCommandName(Traits);
    734   default:
    735     break;
    736   }
    737 
    738   appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
    739 }
    740 
    741 void CommentASTToXMLConverter::visitParamCommandComment(
    742     const ParamCommandComment *C) {
    743   Result << "<Parameter><Name>";
    744   appendToResultWithXMLEscaping(C->isParamIndexValid()
    745                                     ? C->getParamName(FC)
    746                                     : C->getParamNameAsWritten());
    747   Result << "</Name>";
    748 
    749   if (C->isParamIndexValid()) {
    750     if (C->isVarArgParam())
    751       Result << "<IsVarArg />";
    752     else
    753       Result << "<Index>" << C->getParamIndex() << "</Index>";
    754   }
    755 
    756   Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
    757   switch (C->getDirection()) {
    758   case ParamCommandComment::In:
    759     Result << "in";
    760     break;
    761   case ParamCommandComment::Out:
    762     Result << "out";
    763     break;
    764   case ParamCommandComment::InOut:
    765     Result << "in,out";
    766     break;
    767   }
    768   Result << "</Direction><Discussion>";
    769   visit(C->getParagraph());
    770   Result << "</Discussion></Parameter>";
    771 }
    772 
    773 void CommentASTToXMLConverter::visitTParamCommandComment(
    774                                   const TParamCommandComment *C) {
    775   Result << "<Parameter><Name>";
    776   appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
    777                                 : C->getParamNameAsWritten());
    778   Result << "</Name>";
    779 
    780   if (C->isPositionValid() && C->getDepth() == 1) {
    781     Result << "<Index>" << C->getIndex(0) << "</Index>";
    782   }
    783 
    784   Result << "<Discussion>";
    785   visit(C->getParagraph());
    786   Result << "</Discussion></Parameter>";
    787 }
    788 
    789 void CommentASTToXMLConverter::visitVerbatimBlockComment(
    790                                   const VerbatimBlockComment *C) {
    791   unsigned NumLines = C->getNumLines();
    792   if (NumLines == 0)
    793     return;
    794 
    795   switch (C->getCommandID()) {
    796   case CommandTraits::KCI_code:
    797     Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
    798     break;
    799   default:
    800     Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
    801     break;
    802   }
    803   for (unsigned i = 0; i != NumLines; ++i) {
    804     appendToResultWithXMLEscaping(C->getText(i));
    805     if (i + 1 != NumLines)
    806       Result << '\n';
    807   }
    808   Result << "</Verbatim>";
    809 }
    810 
    811 void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
    812                                   const VerbatimBlockLineComment *C) {
    813   llvm_unreachable("should not see this AST node");
    814 }
    815 
    816 void CommentASTToXMLConverter::visitVerbatimLineComment(
    817                                   const VerbatimLineComment *C) {
    818   Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
    819   appendToResultWithXMLEscaping(C->getText());
    820   Result << "</Verbatim>";
    821 }
    822 
    823 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
    824   FullCommentParts Parts(C, Traits);
    825 
    826   const DeclInfo *DI = C->getDeclInfo();
    827   StringRef RootEndTag;
    828   if (DI) {
    829     switch (DI->getKind()) {
    830     case DeclInfo::OtherKind:
    831       RootEndTag = "</Other>";
    832       Result << "<Other";
    833       break;
    834     case DeclInfo::FunctionKind:
    835       RootEndTag = "</Function>";
    836       Result << "<Function";
    837       switch (DI->TemplateKind) {
    838       case DeclInfo::NotTemplate:
    839         break;
    840       case DeclInfo::Template:
    841         Result << " templateKind=\"template\"";
    842         break;
    843       case DeclInfo::TemplateSpecialization:
    844         Result << " templateKind=\"specialization\"";
    845         break;
    846       case DeclInfo::TemplatePartialSpecialization:
    847         llvm_unreachable("partial specializations of functions "
    848                          "are not allowed in C++");
    849       }
    850       if (DI->IsInstanceMethod)
    851         Result << " isInstanceMethod=\"1\"";
    852       if (DI->IsClassMethod)
    853         Result << " isClassMethod=\"1\"";
    854       break;
    855     case DeclInfo::ClassKind:
    856       RootEndTag = "</Class>";
    857       Result << "<Class";
    858       switch (DI->TemplateKind) {
    859       case DeclInfo::NotTemplate:
    860         break;
    861       case DeclInfo::Template:
    862         Result << " templateKind=\"template\"";
    863         break;
    864       case DeclInfo::TemplateSpecialization:
    865         Result << " templateKind=\"specialization\"";
    866         break;
    867       case DeclInfo::TemplatePartialSpecialization:
    868         Result << " templateKind=\"partialSpecialization\"";
    869         break;
    870       }
    871       break;
    872     case DeclInfo::VariableKind:
    873       RootEndTag = "</Variable>";
    874       Result << "<Variable";
    875       break;
    876     case DeclInfo::NamespaceKind:
    877       RootEndTag = "</Namespace>";
    878       Result << "<Namespace";
    879       break;
    880     case DeclInfo::TypedefKind:
    881       RootEndTag = "</Typedef>";
    882       Result << "<Typedef";
    883       break;
    884     case DeclInfo::EnumKind:
    885       RootEndTag = "</Enum>";
    886       Result << "<Enum";
    887       break;
    888     }
    889 
    890     {
    891       // Print line and column number.
    892       SourceLocation Loc = DI->CurrentDecl->getLocation();
    893       std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
    894       FileID FID = LocInfo.first;
    895       unsigned FileOffset = LocInfo.second;
    896 
    897       if (FID.isValid()) {
    898         if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
    899           Result << " file=\"";
    900           appendToResultWithXMLEscaping(FE->getName());
    901           Result << "\"";
    902         }
    903         Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
    904                << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
    905                << "\"";
    906       }
    907     }
    908 
    909     // Finish the root tag.
    910     Result << ">";
    911 
    912     bool FoundName = false;
    913     if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
    914       if (DeclarationName DeclName = ND->getDeclName()) {
    915         Result << "<Name>";
    916         std::string Name = DeclName.getAsString();
    917         appendToResultWithXMLEscaping(Name);
    918         FoundName = true;
    919         Result << "</Name>";
    920       }
    921     }
    922     if (!FoundName)
    923       Result << "<Name>&lt;anonymous&gt;</Name>";
    924 
    925     {
    926       // Print USR.
    927       SmallString<128> USR;
    928       generateUSRForDecl(DI->CommentDecl, USR);
    929       if (!USR.empty()) {
    930         Result << "<USR>";
    931         appendToResultWithXMLEscaping(USR);
    932         Result << "</USR>";
    933       }
    934     }
    935   } else {
    936     // No DeclInfo -- just emit some root tag and name tag.
    937     RootEndTag = "</Other>";
    938     Result << "<Other><Name>unknown</Name>";
    939   }
    940 
    941   if (Parts.Headerfile) {
    942     Result << "<Headerfile>";
    943     visit(Parts.Headerfile);
    944     Result << "</Headerfile>";
    945   }
    946 
    947   {
    948     // Pretty-print the declaration.
    949     Result << "<Declaration>";
    950     SmallString<128> Declaration;
    951     getSourceTextOfDeclaration(DI, Declaration);
    952     formatTextOfDeclaration(DI, Declaration);
    953     appendToResultWithXMLEscaping(Declaration);
    954     Result << "</Declaration>";
    955   }
    956 
    957   bool FirstParagraphIsBrief = false;
    958   if (Parts.Brief) {
    959     Result << "<Abstract>";
    960     visit(Parts.Brief);
    961     Result << "</Abstract>";
    962   } else if (Parts.FirstParagraph) {
    963     Result << "<Abstract>";
    964     visit(Parts.FirstParagraph);
    965     Result << "</Abstract>";
    966     FirstParagraphIsBrief = true;
    967   }
    968 
    969   if (Parts.TParams.size() != 0) {
    970     Result << "<TemplateParameters>";
    971     for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
    972       visit(Parts.TParams[i]);
    973     Result << "</TemplateParameters>";
    974   }
    975 
    976   if (Parts.Params.size() != 0) {
    977     Result << "<Parameters>";
    978     for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
    979       visit(Parts.Params[i]);
    980     Result << "</Parameters>";
    981   }
    982 
    983   if (Parts.Exceptions.size() != 0) {
    984     Result << "<Exceptions>";
    985     for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
    986       visit(Parts.Exceptions[i]);
    987     Result << "</Exceptions>";
    988   }
    989 
    990   if (Parts.Returns.size() != 0) {
    991     Result << "<ResultDiscussion>";
    992     for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
    993       visit(Parts.Returns[i]);
    994     Result << "</ResultDiscussion>";
    995   }
    996 
    997   if (DI->CommentDecl->hasAttrs()) {
    998     const AttrVec &Attrs = DI->CommentDecl->getAttrs();
    999     for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
   1000       const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
   1001       if (!AA) {
   1002         if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
   1003           if (DA->getMessage().empty())
   1004             Result << "<Deprecated/>";
   1005           else {
   1006             Result << "<Deprecated>";
   1007             appendToResultWithXMLEscaping(DA->getMessage());
   1008             Result << "</Deprecated>";
   1009           }
   1010         }
   1011         else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
   1012           if (UA->getMessage().empty())
   1013             Result << "<Unavailable/>";
   1014           else {
   1015             Result << "<Unavailable>";
   1016             appendToResultWithXMLEscaping(UA->getMessage());
   1017             Result << "</Unavailable>";
   1018           }
   1019         }
   1020         continue;
   1021       }
   1022 
   1023       // 'availability' attribute.
   1024       Result << "<Availability";
   1025       StringRef Distribution;
   1026       if (AA->getPlatform()) {
   1027         Distribution = AvailabilityAttr::getPrettyPlatformName(
   1028                                         AA->getPlatform()->getName());
   1029         if (Distribution.empty())
   1030           Distribution = AA->getPlatform()->getName();
   1031       }
   1032       Result << " distribution=\"" << Distribution << "\">";
   1033       VersionTuple IntroducedInVersion = AA->getIntroduced();
   1034       if (!IntroducedInVersion.empty()) {
   1035         Result << "<IntroducedInVersion>"
   1036                << IntroducedInVersion.getAsString()
   1037                << "</IntroducedInVersion>";
   1038       }
   1039       VersionTuple DeprecatedInVersion = AA->getDeprecated();
   1040       if (!DeprecatedInVersion.empty()) {
   1041         Result << "<DeprecatedInVersion>"
   1042                << DeprecatedInVersion.getAsString()
   1043                << "</DeprecatedInVersion>";
   1044       }
   1045       VersionTuple RemovedAfterVersion = AA->getObsoleted();
   1046       if (!RemovedAfterVersion.empty()) {
   1047         Result << "<RemovedAfterVersion>"
   1048                << RemovedAfterVersion.getAsString()
   1049                << "</RemovedAfterVersion>";
   1050       }
   1051       StringRef DeprecationSummary = AA->getMessage();
   1052       if (!DeprecationSummary.empty()) {
   1053         Result << "<DeprecationSummary>";
   1054         appendToResultWithXMLEscaping(DeprecationSummary);
   1055         Result << "</DeprecationSummary>";
   1056       }
   1057       if (AA->getUnavailable())
   1058         Result << "<Unavailable/>";
   1059       Result << "</Availability>";
   1060     }
   1061   }
   1062 
   1063   {
   1064     bool StartTagEmitted = false;
   1065     for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
   1066       const Comment *C = Parts.MiscBlocks[i];
   1067       if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
   1068         continue;
   1069       if (!StartTagEmitted) {
   1070         Result << "<Discussion>";
   1071         StartTagEmitted = true;
   1072       }
   1073       visit(C);
   1074     }
   1075     if (StartTagEmitted)
   1076       Result << "</Discussion>";
   1077   }
   1078 
   1079   Result << RootEndTag;
   1080 }
   1081 
   1082 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
   1083   for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
   1084     const char C = *I;
   1085     switch (C) {
   1086     case '&':
   1087       Result << "&amp;";
   1088       break;
   1089     case '<':
   1090       Result << "&lt;";
   1091       break;
   1092     case '>':
   1093       Result << "&gt;";
   1094       break;
   1095     case '"':
   1096       Result << "&quot;";
   1097       break;
   1098     case '\'':
   1099       Result << "&apos;";
   1100       break;
   1101     default:
   1102       Result << C;
   1103       break;
   1104     }
   1105   }
   1106 }
   1107 
   1108 void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) {
   1109   if (S.empty())
   1110     return;
   1111 
   1112   Result << "<![CDATA[";
   1113   while (!S.empty()) {
   1114     size_t Pos = S.find("]]>");
   1115     if (Pos == 0) {
   1116       Result << "]]]]><![CDATA[>";
   1117       S = S.drop_front(3);
   1118       continue;
   1119     }
   1120     if (Pos == StringRef::npos)
   1121       Pos = S.size();
   1122 
   1123     Result << S.substr(0, Pos);
   1124 
   1125     S = S.drop_front(Pos);
   1126   }
   1127   Result << "]]>";
   1128 }
   1129 
   1130 CommentToXMLConverter::CommentToXMLConverter() : FormatInMemoryUniqueId(0) {}
   1131 CommentToXMLConverter::~CommentToXMLConverter() {}
   1132 
   1133 void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC,
   1134                                                  SmallVectorImpl<char> &HTML,
   1135                                                  const ASTContext &Context) {
   1136   CommentASTToHTMLConverter Converter(FC, HTML,
   1137                                       Context.getCommentCommandTraits());
   1138   Converter.visit(FC);
   1139 }
   1140 
   1141 void CommentToXMLConverter::convertHTMLTagNodeToText(
   1142     const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text,
   1143     const ASTContext &Context) {
   1144   CommentASTToHTMLConverter Converter(nullptr, Text,
   1145                                       Context.getCommentCommandTraits());
   1146   Converter.visit(HTC);
   1147 }
   1148 
   1149 void CommentToXMLConverter::convertCommentToXML(const FullComment *FC,
   1150                                                 SmallVectorImpl<char> &XML,
   1151                                                 const ASTContext &Context) {
   1152   if (!FormatContext || (FormatInMemoryUniqueId % 1000) == 0) {
   1153     // Create a new format context, or re-create it after some number of
   1154     // iterations, so the buffers don't grow too large.
   1155     FormatContext.reset(new SimpleFormatContext(Context.getLangOpts()));
   1156   }
   1157 
   1158   CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
   1159                                      Context.getSourceManager(), *FormatContext,
   1160                                      FormatInMemoryUniqueId++);
   1161   Converter.visit(FC);
   1162 }
   1163 
   1164