Home | History | Annotate | Download | only in libclang
      1 //===- CXComment.cpp - libclang APIs for manipulating CXComments ----------===//
      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 // This file defines all libclang APIs related to walking comment AST.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #include "clang-c/Index.h"
     15 #include "CXComment.h"
     16 #include "CXCursor.h"
     17 #include "CXString.h"
     18 #include "SimpleFormatContext.h"
     19 #include "clang/AST/CommentCommandTraits.h"
     20 #include "clang/AST/CommentVisitor.h"
     21 #include "clang/AST/Decl.h"
     22 #include "clang/AST/PrettyPrinter.h"
     23 #include "clang/Format/Format.h"
     24 #include "clang/Lex/Lexer.h"
     25 #include "llvm/ADT/StringExtras.h"
     26 #include "llvm/ADT/StringSwitch.h"
     27 #include "llvm/Support/ErrorHandling.h"
     28 #include "llvm/Support/raw_ostream.h"
     29 #include <climits>
     30 
     31 using namespace clang;
     32 using namespace clang::comments;
     33 using namespace clang::cxcomment;
     34 
     35 extern "C" {
     36 
     37 enum CXCommentKind clang_Comment_getKind(CXComment CXC) {
     38   const Comment *C = getASTNode(CXC);
     39   if (!C)
     40     return CXComment_Null;
     41 
     42   switch (C->getCommentKind()) {
     43   case Comment::NoCommentKind:
     44     return CXComment_Null;
     45 
     46   case Comment::TextCommentKind:
     47     return CXComment_Text;
     48 
     49   case Comment::InlineCommandCommentKind:
     50     return CXComment_InlineCommand;
     51 
     52   case Comment::HTMLStartTagCommentKind:
     53     return CXComment_HTMLStartTag;
     54 
     55   case Comment::HTMLEndTagCommentKind:
     56     return CXComment_HTMLEndTag;
     57 
     58   case Comment::ParagraphCommentKind:
     59     return CXComment_Paragraph;
     60 
     61   case Comment::BlockCommandCommentKind:
     62     return CXComment_BlockCommand;
     63 
     64   case Comment::ParamCommandCommentKind:
     65     return CXComment_ParamCommand;
     66 
     67   case Comment::TParamCommandCommentKind:
     68     return CXComment_TParamCommand;
     69 
     70   case Comment::VerbatimBlockCommentKind:
     71     return CXComment_VerbatimBlockCommand;
     72 
     73   case Comment::VerbatimBlockLineCommentKind:
     74     return CXComment_VerbatimBlockLine;
     75 
     76   case Comment::VerbatimLineCommentKind:
     77     return CXComment_VerbatimLine;
     78 
     79   case Comment::FullCommentKind:
     80     return CXComment_FullComment;
     81   }
     82   llvm_unreachable("unknown CommentKind");
     83 }
     84 
     85 unsigned clang_Comment_getNumChildren(CXComment CXC) {
     86   const Comment *C = getASTNode(CXC);
     87   if (!C)
     88     return 0;
     89 
     90   return C->child_count();
     91 }
     92 
     93 CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) {
     94   const Comment *C = getASTNode(CXC);
     95   if (!C || ChildIdx >= C->child_count())
     96     return createCXComment(NULL, NULL);
     97 
     98   return createCXComment(*(C->child_begin() + ChildIdx), CXC.TranslationUnit);
     99 }
    100 
    101 unsigned clang_Comment_isWhitespace(CXComment CXC) {
    102   const Comment *C = getASTNode(CXC);
    103   if (!C)
    104     return false;
    105 
    106   if (const TextComment *TC = dyn_cast<TextComment>(C))
    107     return TC->isWhitespace();
    108 
    109   if (const ParagraphComment *PC = dyn_cast<ParagraphComment>(C))
    110     return PC->isWhitespace();
    111 
    112   return false;
    113 }
    114 
    115 unsigned clang_InlineContentComment_hasTrailingNewline(CXComment CXC) {
    116   const InlineContentComment *ICC = getASTNodeAs<InlineContentComment>(CXC);
    117   if (!ICC)
    118     return false;
    119 
    120   return ICC->hasTrailingNewline();
    121 }
    122 
    123 CXString clang_TextComment_getText(CXComment CXC) {
    124   const TextComment *TC = getASTNodeAs<TextComment>(CXC);
    125   if (!TC)
    126     return cxstring::createNull();
    127 
    128   return cxstring::createRef(TC->getText());
    129 }
    130 
    131 CXString clang_InlineCommandComment_getCommandName(CXComment CXC) {
    132   const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
    133   if (!ICC)
    134     return cxstring::createNull();
    135 
    136   const CommandTraits &Traits = getCommandTraits(CXC);
    137   return cxstring::createRef(ICC->getCommandName(Traits));
    138 }
    139 
    140 enum CXCommentInlineCommandRenderKind
    141 clang_InlineCommandComment_getRenderKind(CXComment CXC) {
    142   const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
    143   if (!ICC)
    144     return CXCommentInlineCommandRenderKind_Normal;
    145 
    146   switch (ICC->getRenderKind()) {
    147   case InlineCommandComment::RenderNormal:
    148     return CXCommentInlineCommandRenderKind_Normal;
    149 
    150   case InlineCommandComment::RenderBold:
    151     return CXCommentInlineCommandRenderKind_Bold;
    152 
    153   case InlineCommandComment::RenderMonospaced:
    154     return CXCommentInlineCommandRenderKind_Monospaced;
    155 
    156   case InlineCommandComment::RenderEmphasized:
    157     return CXCommentInlineCommandRenderKind_Emphasized;
    158   }
    159   llvm_unreachable("unknown InlineCommandComment::RenderKind");
    160 }
    161 
    162 unsigned clang_InlineCommandComment_getNumArgs(CXComment CXC) {
    163   const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
    164   if (!ICC)
    165     return 0;
    166 
    167   return ICC->getNumArgs();
    168 }
    169 
    170 CXString clang_InlineCommandComment_getArgText(CXComment CXC,
    171                                                unsigned ArgIdx) {
    172   const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
    173   if (!ICC || ArgIdx >= ICC->getNumArgs())
    174     return cxstring::createNull();
    175 
    176   return cxstring::createRef(ICC->getArgText(ArgIdx));
    177 }
    178 
    179 CXString clang_HTMLTagComment_getTagName(CXComment CXC) {
    180   const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
    181   if (!HTC)
    182     return cxstring::createNull();
    183 
    184   return cxstring::createRef(HTC->getTagName());
    185 }
    186 
    187 unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment CXC) {
    188   const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
    189   if (!HST)
    190     return false;
    191 
    192   return HST->isSelfClosing();
    193 }
    194 
    195 unsigned clang_HTMLStartTag_getNumAttrs(CXComment CXC) {
    196   const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
    197   if (!HST)
    198     return 0;
    199 
    200   return HST->getNumAttrs();
    201 }
    202 
    203 CXString clang_HTMLStartTag_getAttrName(CXComment CXC, unsigned AttrIdx) {
    204   const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
    205   if (!HST || AttrIdx >= HST->getNumAttrs())
    206     return cxstring::createNull();
    207 
    208   return cxstring::createRef(HST->getAttr(AttrIdx).Name);
    209 }
    210 
    211 CXString clang_HTMLStartTag_getAttrValue(CXComment CXC, unsigned AttrIdx) {
    212   const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
    213   if (!HST || AttrIdx >= HST->getNumAttrs())
    214     return cxstring::createNull();
    215 
    216   return cxstring::createRef(HST->getAttr(AttrIdx).Value);
    217 }
    218 
    219 CXString clang_BlockCommandComment_getCommandName(CXComment CXC) {
    220   const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
    221   if (!BCC)
    222     return cxstring::createNull();
    223 
    224   const CommandTraits &Traits = getCommandTraits(CXC);
    225   return cxstring::createRef(BCC->getCommandName(Traits));
    226 }
    227 
    228 unsigned clang_BlockCommandComment_getNumArgs(CXComment CXC) {
    229   const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
    230   if (!BCC)
    231     return 0;
    232 
    233   return BCC->getNumArgs();
    234 }
    235 
    236 CXString clang_BlockCommandComment_getArgText(CXComment CXC,
    237                                               unsigned ArgIdx) {
    238   const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
    239   if (!BCC || ArgIdx >= BCC->getNumArgs())
    240     return cxstring::createNull();
    241 
    242   return cxstring::createRef(BCC->getArgText(ArgIdx));
    243 }
    244 
    245 CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) {
    246   const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
    247   if (!BCC)
    248     return createCXComment(NULL, NULL);
    249 
    250   return createCXComment(BCC->getParagraph(), CXC.TranslationUnit);
    251 }
    252 
    253 CXString clang_ParamCommandComment_getParamName(CXComment CXC) {
    254   const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
    255   if (!PCC || !PCC->hasParamName())
    256     return cxstring::createNull();
    257 
    258   return cxstring::createRef(PCC->getParamNameAsWritten());
    259 }
    260 
    261 unsigned clang_ParamCommandComment_isParamIndexValid(CXComment CXC) {
    262   const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
    263   if (!PCC)
    264     return false;
    265 
    266   return PCC->isParamIndexValid();
    267 }
    268 
    269 unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) {
    270   const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
    271   if (!PCC || !PCC->isParamIndexValid() || PCC->isVarArgParam())
    272     return ParamCommandComment::InvalidParamIndex;
    273 
    274   return PCC->getParamIndex();
    275 }
    276 
    277 unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment CXC) {
    278   const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
    279   if (!PCC)
    280     return false;
    281 
    282   return PCC->isDirectionExplicit();
    283 }
    284 
    285 enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection(
    286                                                             CXComment CXC) {
    287   const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
    288   if (!PCC)
    289     return CXCommentParamPassDirection_In;
    290 
    291   switch (PCC->getDirection()) {
    292   case ParamCommandComment::In:
    293     return CXCommentParamPassDirection_In;
    294 
    295   case ParamCommandComment::Out:
    296     return CXCommentParamPassDirection_Out;
    297 
    298   case ParamCommandComment::InOut:
    299     return CXCommentParamPassDirection_InOut;
    300   }
    301   llvm_unreachable("unknown ParamCommandComment::PassDirection");
    302 }
    303 
    304 CXString clang_TParamCommandComment_getParamName(CXComment CXC) {
    305   const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
    306   if (!TPCC || !TPCC->hasParamName())
    307     return cxstring::createNull();
    308 
    309   return cxstring::createRef(TPCC->getParamNameAsWritten());
    310 }
    311 
    312 unsigned clang_TParamCommandComment_isParamPositionValid(CXComment CXC) {
    313   const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
    314   if (!TPCC)
    315     return false;
    316 
    317   return TPCC->isPositionValid();
    318 }
    319 
    320 unsigned clang_TParamCommandComment_getDepth(CXComment CXC) {
    321   const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
    322   if (!TPCC || !TPCC->isPositionValid())
    323     return 0;
    324 
    325   return TPCC->getDepth();
    326 }
    327 
    328 unsigned clang_TParamCommandComment_getIndex(CXComment CXC, unsigned Depth) {
    329   const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
    330   if (!TPCC || !TPCC->isPositionValid() || Depth >= TPCC->getDepth())
    331     return 0;
    332 
    333   return TPCC->getIndex(Depth);
    334 }
    335 
    336 CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) {
    337   const VerbatimBlockLineComment *VBL =
    338       getASTNodeAs<VerbatimBlockLineComment>(CXC);
    339   if (!VBL)
    340     return cxstring::createNull();
    341 
    342   return cxstring::createRef(VBL->getText());
    343 }
    344 
    345 CXString clang_VerbatimLineComment_getText(CXComment CXC) {
    346   const VerbatimLineComment *VLC = getASTNodeAs<VerbatimLineComment>(CXC);
    347   if (!VLC)
    348     return cxstring::createNull();
    349 
    350   return cxstring::createRef(VLC->getText());
    351 }
    352 
    353 } // end extern "C"
    354 
    355 //===----------------------------------------------------------------------===//
    356 // Helpers for converting comment AST to HTML.
    357 //===----------------------------------------------------------------------===//
    358 
    359 namespace {
    360 
    361 /// This comparison will sort parameters with valid index by index, then vararg
    362 /// parameters, and invalid (unresolved) parameters last.
    363 class ParamCommandCommentCompareIndex {
    364 public:
    365   bool operator()(const ParamCommandComment *LHS,
    366                   const ParamCommandComment *RHS) const {
    367     unsigned LHSIndex = UINT_MAX;
    368     unsigned RHSIndex = UINT_MAX;
    369 
    370     if (LHS->isParamIndexValid()) {
    371       if (LHS->isVarArgParam())
    372         LHSIndex = UINT_MAX - 1;
    373       else
    374         LHSIndex = LHS->getParamIndex();
    375     }
    376     if (RHS->isParamIndexValid()) {
    377       if (RHS->isVarArgParam())
    378         RHSIndex = UINT_MAX - 1;
    379       else
    380         RHSIndex = RHS->getParamIndex();
    381     }
    382     return LHSIndex < RHSIndex;
    383   }
    384 };
    385 
    386 /// This comparison will sort template parameters in the following order:
    387 /// \li real template parameters (depth = 1) in index order;
    388 /// \li all other names (depth > 1);
    389 /// \li unresolved names.
    390 class TParamCommandCommentComparePosition {
    391 public:
    392   bool operator()(const TParamCommandComment *LHS,
    393                   const TParamCommandComment *RHS) const {
    394     // Sort unresolved names last.
    395     if (!LHS->isPositionValid())
    396       return false;
    397     if (!RHS->isPositionValid())
    398       return true;
    399 
    400     if (LHS->getDepth() > 1)
    401       return false;
    402     if (RHS->getDepth() > 1)
    403       return true;
    404 
    405     // Sort template parameters in index order.
    406     if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
    407       return LHS->getIndex(0) < RHS->getIndex(0);
    408 
    409     // Leave all other names in source order.
    410     return true;
    411   }
    412 };
    413 
    414 /// Separate parts of a FullComment.
    415 struct FullCommentParts {
    416   /// Take a full comment apart and initialize members accordingly.
    417   FullCommentParts(const FullComment *C,
    418                    const CommandTraits &Traits);
    419 
    420   const BlockContentComment *Brief;
    421   const BlockContentComment *Headerfile;
    422   const ParagraphComment *FirstParagraph;
    423   SmallVector<const BlockCommandComment *, 4> Returns;
    424   SmallVector<const ParamCommandComment *, 8> Params;
    425   SmallVector<const TParamCommandComment *, 4> TParams;
    426   SmallVector<const BlockContentComment *, 8> MiscBlocks;
    427 };
    428 
    429 FullCommentParts::FullCommentParts(const FullComment *C,
    430                                    const CommandTraits &Traits) :
    431     Brief(NULL), Headerfile(NULL), FirstParagraph(NULL) {
    432   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
    433        I != E; ++I) {
    434     const Comment *Child = *I;
    435     if (!Child)
    436       continue;
    437     switch (Child->getCommentKind()) {
    438     case Comment::NoCommentKind:
    439       continue;
    440 
    441     case Comment::ParagraphCommentKind: {
    442       const ParagraphComment *PC = cast<ParagraphComment>(Child);
    443       if (PC->isWhitespace())
    444         break;
    445       if (!FirstParagraph)
    446         FirstParagraph = PC;
    447 
    448       MiscBlocks.push_back(PC);
    449       break;
    450     }
    451 
    452     case Comment::BlockCommandCommentKind: {
    453       const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
    454       const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
    455       if (!Brief && Info->IsBriefCommand) {
    456         Brief = BCC;
    457         break;
    458       }
    459       if (!Headerfile && Info->IsHeaderfileCommand) {
    460         Headerfile = BCC;
    461         break;
    462       }
    463       if (Info->IsReturnsCommand) {
    464         Returns.push_back(BCC);
    465         break;
    466       }
    467       MiscBlocks.push_back(BCC);
    468       break;
    469     }
    470 
    471     case Comment::ParamCommandCommentKind: {
    472       const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
    473       if (!PCC->hasParamName())
    474         break;
    475 
    476       if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
    477         break;
    478 
    479       Params.push_back(PCC);
    480       break;
    481     }
    482 
    483     case Comment::TParamCommandCommentKind: {
    484       const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
    485       if (!TPCC->hasParamName())
    486         break;
    487 
    488       if (!TPCC->hasNonWhitespaceParagraph())
    489         break;
    490 
    491       TParams.push_back(TPCC);
    492       break;
    493     }
    494 
    495     case Comment::VerbatimBlockCommentKind:
    496       MiscBlocks.push_back(cast<BlockCommandComment>(Child));
    497       break;
    498 
    499     case Comment::VerbatimLineCommentKind: {
    500       const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
    501       const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());
    502       if (!Info->IsDeclarationCommand)
    503         MiscBlocks.push_back(VLC);
    504       break;
    505     }
    506 
    507     case Comment::TextCommentKind:
    508     case Comment::InlineCommandCommentKind:
    509     case Comment::HTMLStartTagCommentKind:
    510     case Comment::HTMLEndTagCommentKind:
    511     case Comment::VerbatimBlockLineCommentKind:
    512     case Comment::FullCommentKind:
    513       llvm_unreachable("AST node of this kind can't be a child of "
    514                        "a FullComment");
    515     }
    516   }
    517 
    518   // Sort params in order they are declared in the function prototype.
    519   // Unresolved parameters are put at the end of the list in the same order
    520   // they were seen in the comment.
    521   std::stable_sort(Params.begin(), Params.end(),
    522                    ParamCommandCommentCompareIndex());
    523 
    524   std::stable_sort(TParams.begin(), TParams.end(),
    525                    TParamCommandCommentComparePosition());
    526 }
    527 
    528 void PrintHTMLStartTagComment(const HTMLStartTagComment *C,
    529                               llvm::raw_svector_ostream &Result) {
    530   Result << "<" << C->getTagName();
    531 
    532   if (C->getNumAttrs() != 0) {
    533     for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
    534       Result << " ";
    535       const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
    536       Result << Attr.Name;
    537       if (!Attr.Value.empty())
    538         Result << "=\"" << Attr.Value << "\"";
    539     }
    540   }
    541 
    542   if (!C->isSelfClosing())
    543     Result << ">";
    544   else
    545     Result << "/>";
    546 }
    547 
    548 class CommentASTToHTMLConverter :
    549     public ConstCommentVisitor<CommentASTToHTMLConverter> {
    550 public:
    551   /// \param Str accumulator for HTML.
    552   CommentASTToHTMLConverter(const FullComment *FC,
    553                             SmallVectorImpl<char> &Str,
    554                             const CommandTraits &Traits) :
    555       FC(FC), Result(Str), Traits(Traits)
    556   { }
    557 
    558   // Inline content.
    559   void visitTextComment(const TextComment *C);
    560   void visitInlineCommandComment(const InlineCommandComment *C);
    561   void visitHTMLStartTagComment(const HTMLStartTagComment *C);
    562   void visitHTMLEndTagComment(const HTMLEndTagComment *C);
    563 
    564   // Block content.
    565   void visitParagraphComment(const ParagraphComment *C);
    566   void visitBlockCommandComment(const BlockCommandComment *C);
    567   void visitParamCommandComment(const ParamCommandComment *C);
    568   void visitTParamCommandComment(const TParamCommandComment *C);
    569   void visitVerbatimBlockComment(const VerbatimBlockComment *C);
    570   void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
    571   void visitVerbatimLineComment(const VerbatimLineComment *C);
    572 
    573   void visitFullComment(const FullComment *C);
    574 
    575   // Helpers.
    576 
    577   /// Convert a paragraph that is not a block by itself (an argument to some
    578   /// command).
    579   void visitNonStandaloneParagraphComment(const ParagraphComment *C);
    580 
    581   void appendToResultWithHTMLEscaping(StringRef S);
    582 
    583 private:
    584   const FullComment *FC;
    585   /// Output stream for HTML.
    586   llvm::raw_svector_ostream Result;
    587 
    588   const CommandTraits &Traits;
    589 };
    590 } // end unnamed namespace
    591 
    592 void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
    593   appendToResultWithHTMLEscaping(C->getText());
    594 }
    595 
    596 void CommentASTToHTMLConverter::visitInlineCommandComment(
    597                                   const InlineCommandComment *C) {
    598   // Nothing to render if no arguments supplied.
    599   if (C->getNumArgs() == 0)
    600     return;
    601 
    602   // Nothing to render if argument is empty.
    603   StringRef Arg0 = C->getArgText(0);
    604   if (Arg0.empty())
    605     return;
    606 
    607   switch (C->getRenderKind()) {
    608   case InlineCommandComment::RenderNormal:
    609     for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
    610       appendToResultWithHTMLEscaping(C->getArgText(i));
    611       Result << " ";
    612     }
    613     return;
    614 
    615   case InlineCommandComment::RenderBold:
    616     assert(C->getNumArgs() == 1);
    617     Result << "<b>";
    618     appendToResultWithHTMLEscaping(Arg0);
    619     Result << "</b>";
    620     return;
    621   case InlineCommandComment::RenderMonospaced:
    622     assert(C->getNumArgs() == 1);
    623     Result << "<tt>";
    624     appendToResultWithHTMLEscaping(Arg0);
    625     Result<< "</tt>";
    626     return;
    627   case InlineCommandComment::RenderEmphasized:
    628     assert(C->getNumArgs() == 1);
    629     Result << "<em>";
    630     appendToResultWithHTMLEscaping(Arg0);
    631     Result << "</em>";
    632     return;
    633   }
    634 }
    635 
    636 void CommentASTToHTMLConverter::visitHTMLStartTagComment(
    637                                   const HTMLStartTagComment *C) {
    638   PrintHTMLStartTagComment(C, Result);
    639 }
    640 
    641 void CommentASTToHTMLConverter::visitHTMLEndTagComment(
    642                                   const HTMLEndTagComment *C) {
    643   Result << "</" << C->getTagName() << ">";
    644 }
    645 
    646 void CommentASTToHTMLConverter::visitParagraphComment(
    647                                   const ParagraphComment *C) {
    648   if (C->isWhitespace())
    649     return;
    650 
    651   Result << "<p>";
    652   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
    653        I != E; ++I) {
    654     visit(*I);
    655   }
    656   Result << "</p>";
    657 }
    658 
    659 void CommentASTToHTMLConverter::visitBlockCommandComment(
    660                                   const BlockCommandComment *C) {
    661   const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
    662   if (Info->IsBriefCommand) {
    663     Result << "<p class=\"para-brief\">";
    664     visitNonStandaloneParagraphComment(C->getParagraph());
    665     Result << "</p>";
    666     return;
    667   }
    668   if (Info->IsReturnsCommand) {
    669     Result << "<p class=\"para-returns\">"
    670               "<span class=\"word-returns\">Returns</span> ";
    671     visitNonStandaloneParagraphComment(C->getParagraph());
    672     Result << "</p>";
    673     return;
    674   }
    675   // We don't know anything about this command.  Just render the paragraph.
    676   visit(C->getParagraph());
    677 }
    678 
    679 void CommentASTToHTMLConverter::visitParamCommandComment(
    680                                   const ParamCommandComment *C) {
    681   if (C->isParamIndexValid()) {
    682     if (C->isVarArgParam()) {
    683       Result << "<dt class=\"param-name-index-vararg\">";
    684       appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
    685     } else {
    686       Result << "<dt class=\"param-name-index-"
    687              << C->getParamIndex()
    688              << "\">";
    689       appendToResultWithHTMLEscaping(C->getParamName(FC));
    690     }
    691   } else {
    692     Result << "<dt class=\"param-name-index-invalid\">";
    693     appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
    694   }
    695   Result << "</dt>";
    696 
    697   if (C->isParamIndexValid()) {
    698     if (C->isVarArgParam())
    699       Result << "<dd class=\"param-descr-index-vararg\">";
    700     else
    701       Result << "<dd class=\"param-descr-index-"
    702              << C->getParamIndex()
    703              << "\">";
    704   } else
    705     Result << "<dd class=\"param-descr-index-invalid\">";
    706 
    707   visitNonStandaloneParagraphComment(C->getParagraph());
    708   Result << "</dd>";
    709 }
    710 
    711 void CommentASTToHTMLConverter::visitTParamCommandComment(
    712                                   const TParamCommandComment *C) {
    713   if (C->isPositionValid()) {
    714     if (C->getDepth() == 1)
    715       Result << "<dt class=\"tparam-name-index-"
    716              << C->getIndex(0)
    717              << "\">";
    718     else
    719       Result << "<dt class=\"tparam-name-index-other\">";
    720     appendToResultWithHTMLEscaping(C->getParamName(FC));
    721   } else {
    722     Result << "<dt class=\"tparam-name-index-invalid\">";
    723     appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
    724   }
    725 
    726   Result << "</dt>";
    727 
    728   if (C->isPositionValid()) {
    729     if (C->getDepth() == 1)
    730       Result << "<dd class=\"tparam-descr-index-"
    731              << C->getIndex(0)
    732              << "\">";
    733     else
    734       Result << "<dd class=\"tparam-descr-index-other\">";
    735   } else
    736     Result << "<dd class=\"tparam-descr-index-invalid\">";
    737 
    738   visitNonStandaloneParagraphComment(C->getParagraph());
    739   Result << "</dd>";
    740 }
    741 
    742 void CommentASTToHTMLConverter::visitVerbatimBlockComment(
    743                                   const VerbatimBlockComment *C) {
    744   unsigned NumLines = C->getNumLines();
    745   if (NumLines == 0)
    746     return;
    747 
    748   Result << "<pre>";
    749   for (unsigned i = 0; i != NumLines; ++i) {
    750     appendToResultWithHTMLEscaping(C->getText(i));
    751     if (i + 1 != NumLines)
    752       Result << '\n';
    753   }
    754   Result << "</pre>";
    755 }
    756 
    757 void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
    758                                   const VerbatimBlockLineComment *C) {
    759   llvm_unreachable("should not see this AST node");
    760 }
    761 
    762 void CommentASTToHTMLConverter::visitVerbatimLineComment(
    763                                   const VerbatimLineComment *C) {
    764   Result << "<pre>";
    765   appendToResultWithHTMLEscaping(C->getText());
    766   Result << "</pre>";
    767 }
    768 
    769 void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
    770   FullCommentParts Parts(C, Traits);
    771 
    772   bool FirstParagraphIsBrief = false;
    773   if (Parts.Headerfile)
    774     visit(Parts.Headerfile);
    775   if (Parts.Brief)
    776     visit(Parts.Brief);
    777   else if (Parts.FirstParagraph) {
    778     Result << "<p class=\"para-brief\">";
    779     visitNonStandaloneParagraphComment(Parts.FirstParagraph);
    780     Result << "</p>";
    781     FirstParagraphIsBrief = true;
    782   }
    783 
    784   for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
    785     const Comment *C = Parts.MiscBlocks[i];
    786     if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
    787       continue;
    788     visit(C);
    789   }
    790 
    791   if (Parts.TParams.size() != 0) {
    792     Result << "<dl>";
    793     for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
    794       visit(Parts.TParams[i]);
    795     Result << "</dl>";
    796   }
    797 
    798   if (Parts.Params.size() != 0) {
    799     Result << "<dl>";
    800     for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
    801       visit(Parts.Params[i]);
    802     Result << "</dl>";
    803   }
    804 
    805   if (Parts.Returns.size() != 0) {
    806     Result << "<div class=\"result-discussion\">";
    807     for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
    808       visit(Parts.Returns[i]);
    809     Result << "</div>";
    810   }
    811 
    812   Result.flush();
    813 }
    814 
    815 void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
    816                                   const ParagraphComment *C) {
    817   if (!C)
    818     return;
    819 
    820   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
    821        I != E; ++I) {
    822     visit(*I);
    823   }
    824 }
    825 
    826 void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
    827   for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
    828     const char C = *I;
    829     switch (C) {
    830       case '&':
    831         Result << "&amp;";
    832         break;
    833       case '<':
    834         Result << "&lt;";
    835         break;
    836       case '>':
    837         Result << "&gt;";
    838         break;
    839       case '"':
    840         Result << "&quot;";
    841         break;
    842       case '\'':
    843         Result << "&#39;";
    844         break;
    845       case '/':
    846         Result << "&#47;";
    847         break;
    848       default:
    849         Result << C;
    850         break;
    851     }
    852   }
    853 }
    854 
    855 extern "C" {
    856 
    857 CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
    858   const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
    859   if (!HTC)
    860     return cxstring::createNull();
    861 
    862   SmallString<128> HTML;
    863   CommentASTToHTMLConverter Converter(0, HTML, getCommandTraits(CXC));
    864   Converter.visit(HTC);
    865   return cxstring::createDup(HTML.str());
    866 }
    867 
    868 CXString clang_FullComment_getAsHTML(CXComment CXC) {
    869   const FullComment *FC = getASTNodeAs<FullComment>(CXC);
    870   if (!FC)
    871     return cxstring::createNull();
    872 
    873   SmallString<1024> HTML;
    874   CommentASTToHTMLConverter Converter(FC, HTML, getCommandTraits(CXC));
    875   Converter.visit(FC);
    876   return cxstring::createDup(HTML.str());
    877 }
    878 
    879 } // end extern "C"
    880 
    881 namespace {
    882 class CommentASTToXMLConverter :
    883     public ConstCommentVisitor<CommentASTToXMLConverter> {
    884 public:
    885   /// \param Str accumulator for XML.
    886   CommentASTToXMLConverter(const FullComment *FC,
    887                            SmallVectorImpl<char> &Str,
    888                            const CommandTraits &Traits,
    889                            const SourceManager &SM,
    890                            SimpleFormatContext &SFC,
    891                            unsigned FUID) :
    892       FC(FC), Result(Str), Traits(Traits), SM(SM),
    893       FormatRewriterContext(SFC),
    894       FormatInMemoryUniqueId(FUID) { }
    895 
    896   // Inline content.
    897   void visitTextComment(const TextComment *C);
    898   void visitInlineCommandComment(const InlineCommandComment *C);
    899   void visitHTMLStartTagComment(const HTMLStartTagComment *C);
    900   void visitHTMLEndTagComment(const HTMLEndTagComment *C);
    901 
    902   // Block content.
    903   void visitParagraphComment(const ParagraphComment *C);
    904 
    905   void appendParagraphCommentWithKind(const ParagraphComment *C,
    906                                       StringRef Kind);
    907 
    908   void visitBlockCommandComment(const BlockCommandComment *C);
    909   void visitParamCommandComment(const ParamCommandComment *C);
    910   void visitTParamCommandComment(const TParamCommandComment *C);
    911   void visitVerbatimBlockComment(const VerbatimBlockComment *C);
    912   void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
    913   void visitVerbatimLineComment(const VerbatimLineComment *C);
    914 
    915   void visitFullComment(const FullComment *C);
    916 
    917   // Helpers.
    918   void appendToResultWithXMLEscaping(StringRef S);
    919 
    920   void formatTextOfDeclaration(const DeclInfo *DI,
    921                                SmallString<128> &Declaration);
    922 
    923 private:
    924   const FullComment *FC;
    925 
    926   /// Output stream for XML.
    927   llvm::raw_svector_ostream Result;
    928 
    929   const CommandTraits &Traits;
    930   const SourceManager &SM;
    931   SimpleFormatContext &FormatRewriterContext;
    932   unsigned FormatInMemoryUniqueId;
    933 };
    934 
    935 void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
    936                                 SmallVectorImpl<char> &Str) {
    937   ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
    938   const LangOptions &LangOpts = Context.getLangOpts();
    939   llvm::raw_svector_ostream OS(Str);
    940   PrintingPolicy PPolicy(LangOpts);
    941   PPolicy.PolishForDeclaration = true;
    942   PPolicy.TerseOutput = true;
    943   ThisDecl->CurrentDecl->print(OS, PPolicy,
    944                                /*Indentation*/0, /*PrintInstantiation*/false);
    945 }
    946 
    947 void CommentASTToXMLConverter::formatTextOfDeclaration(
    948                                               const DeclInfo *DI,
    949                                               SmallString<128> &Declaration) {
    950   // FIXME. formatting API expects null terminated input string.
    951   // There might be more efficient way of doing this.
    952   std::string StringDecl = Declaration.str();
    953 
    954   // Formatter specific code.
    955   // Form a unique in memory buffer name.
    956   SmallString<128> filename;
    957   filename += "xmldecl";
    958   filename += llvm::utostr(FormatInMemoryUniqueId);
    959   filename += ".xd";
    960   FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl);
    961   SourceLocation Start =
    962     FormatRewriterContext.Sources.getLocForStartOfFile(ID).getLocWithOffset(0);
    963   unsigned Length = Declaration.size();
    964 
    965   std::vector<CharSourceRange>
    966     Ranges(1, CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length)));
    967   ASTContext &Context = DI->CurrentDecl->getASTContext();
    968   const LangOptions &LangOpts = Context.getLangOpts();
    969   Lexer Lex(ID, FormatRewriterContext.Sources.getBuffer(ID),
    970             FormatRewriterContext.Sources, LangOpts);
    971   tooling::Replacements Replace =
    972     reformat(format::getLLVMStyle(), Lex, FormatRewriterContext.Sources, Ranges);
    973   applyAllReplacements(Replace, FormatRewriterContext.Rewrite);
    974   Declaration = FormatRewriterContext.getRewrittenText(ID);
    975 }
    976 
    977 } // end unnamed namespace
    978 
    979 void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
    980   appendToResultWithXMLEscaping(C->getText());
    981 }
    982 
    983 void CommentASTToXMLConverter::visitInlineCommandComment(const InlineCommandComment *C) {
    984   // Nothing to render if no arguments supplied.
    985   if (C->getNumArgs() == 0)
    986     return;
    987 
    988   // Nothing to render if argument is empty.
    989   StringRef Arg0 = C->getArgText(0);
    990   if (Arg0.empty())
    991     return;
    992 
    993   switch (C->getRenderKind()) {
    994   case InlineCommandComment::RenderNormal:
    995     for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
    996       appendToResultWithXMLEscaping(C->getArgText(i));
    997       Result << " ";
    998     }
    999     return;
   1000   case InlineCommandComment::RenderBold:
   1001     assert(C->getNumArgs() == 1);
   1002     Result << "<bold>";
   1003     appendToResultWithXMLEscaping(Arg0);
   1004     Result << "</bold>";
   1005     return;
   1006   case InlineCommandComment::RenderMonospaced:
   1007     assert(C->getNumArgs() == 1);
   1008     Result << "<monospaced>";
   1009     appendToResultWithXMLEscaping(Arg0);
   1010     Result << "</monospaced>";
   1011     return;
   1012   case InlineCommandComment::RenderEmphasized:
   1013     assert(C->getNumArgs() == 1);
   1014     Result << "<emphasized>";
   1015     appendToResultWithXMLEscaping(Arg0);
   1016     Result << "</emphasized>";
   1017     return;
   1018   }
   1019 }
   1020 
   1021 void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) {
   1022   Result << "<rawHTML><![CDATA[";
   1023   PrintHTMLStartTagComment(C, Result);
   1024   Result << "]]></rawHTML>";
   1025 }
   1026 
   1027 void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
   1028   Result << "<rawHTML>&lt;/" << C->getTagName() << "&gt;</rawHTML>";
   1029 }
   1030 
   1031 void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
   1032   appendParagraphCommentWithKind(C, StringRef());
   1033 }
   1034 
   1035 void CommentASTToXMLConverter::appendParagraphCommentWithKind(
   1036                                   const ParagraphComment *C,
   1037                                   StringRef ParagraphKind) {
   1038   if (C->isWhitespace())
   1039     return;
   1040 
   1041   if (ParagraphKind.empty())
   1042     Result << "<Para>";
   1043   else
   1044     Result << "<Para kind=\"" << ParagraphKind << "\">";
   1045 
   1046   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
   1047        I != E; ++I) {
   1048     visit(*I);
   1049   }
   1050   Result << "</Para>";
   1051 }
   1052 
   1053 void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) {
   1054   StringRef ParagraphKind;
   1055 
   1056   switch (C->getCommandID()) {
   1057   case CommandTraits::KCI_attention:
   1058   case CommandTraits::KCI_author:
   1059   case CommandTraits::KCI_authors:
   1060   case CommandTraits::KCI_bug:
   1061   case CommandTraits::KCI_copyright:
   1062   case CommandTraits::KCI_date:
   1063   case CommandTraits::KCI_invariant:
   1064   case CommandTraits::KCI_note:
   1065   case CommandTraits::KCI_post:
   1066   case CommandTraits::KCI_pre:
   1067   case CommandTraits::KCI_remark:
   1068   case CommandTraits::KCI_remarks:
   1069   case CommandTraits::KCI_sa:
   1070   case CommandTraits::KCI_see:
   1071   case CommandTraits::KCI_since:
   1072   case CommandTraits::KCI_todo:
   1073   case CommandTraits::KCI_version:
   1074   case CommandTraits::KCI_warning:
   1075     ParagraphKind = C->getCommandName(Traits);
   1076   default:
   1077     break;
   1078   }
   1079 
   1080   appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
   1081 }
   1082 
   1083 void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) {
   1084   Result << "<Parameter><Name>";
   1085   appendToResultWithXMLEscaping(C->isParamIndexValid() ? C->getParamName(FC)
   1086                                                        : C->getParamNameAsWritten());
   1087   Result << "</Name>";
   1088 
   1089   if (C->isParamIndexValid()) {
   1090     if (C->isVarArgParam())
   1091       Result << "<IsVarArg />";
   1092     else
   1093       Result << "<Index>" << C->getParamIndex() << "</Index>";
   1094   }
   1095 
   1096   Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
   1097   switch (C->getDirection()) {
   1098   case ParamCommandComment::In:
   1099     Result << "in";
   1100     break;
   1101   case ParamCommandComment::Out:
   1102     Result << "out";
   1103     break;
   1104   case ParamCommandComment::InOut:
   1105     Result << "in,out";
   1106     break;
   1107   }
   1108   Result << "</Direction><Discussion>";
   1109   visit(C->getParagraph());
   1110   Result << "</Discussion></Parameter>";
   1111 }
   1112 
   1113 void CommentASTToXMLConverter::visitTParamCommandComment(
   1114                                   const TParamCommandComment *C) {
   1115   Result << "<Parameter><Name>";
   1116   appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
   1117                                 : C->getParamNameAsWritten());
   1118   Result << "</Name>";
   1119 
   1120   if (C->isPositionValid() && C->getDepth() == 1) {
   1121     Result << "<Index>" << C->getIndex(0) << "</Index>";
   1122   }
   1123 
   1124   Result << "<Discussion>";
   1125   visit(C->getParagraph());
   1126   Result << "</Discussion></Parameter>";
   1127 }
   1128 
   1129 void CommentASTToXMLConverter::visitVerbatimBlockComment(
   1130                                   const VerbatimBlockComment *C) {
   1131   unsigned NumLines = C->getNumLines();
   1132   if (NumLines == 0)
   1133     return;
   1134 
   1135   switch (C->getCommandID()) {
   1136   case CommandTraits::KCI_code:
   1137     Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
   1138     break;
   1139   default:
   1140     Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
   1141     break;
   1142   }
   1143   for (unsigned i = 0; i != NumLines; ++i) {
   1144     appendToResultWithXMLEscaping(C->getText(i));
   1145     if (i + 1 != NumLines)
   1146       Result << '\n';
   1147   }
   1148   Result << "</Verbatim>";
   1149 }
   1150 
   1151 void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
   1152                                   const VerbatimBlockLineComment *C) {
   1153   llvm_unreachable("should not see this AST node");
   1154 }
   1155 
   1156 void CommentASTToXMLConverter::visitVerbatimLineComment(
   1157                                   const VerbatimLineComment *C) {
   1158   Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
   1159   appendToResultWithXMLEscaping(C->getText());
   1160   Result << "</Verbatim>";
   1161 }
   1162 
   1163 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
   1164   FullCommentParts Parts(C, Traits);
   1165 
   1166   const DeclInfo *DI = C->getDeclInfo();
   1167   StringRef RootEndTag;
   1168   if (DI) {
   1169     switch (DI->getKind()) {
   1170     case DeclInfo::OtherKind:
   1171       RootEndTag = "</Other>";
   1172       Result << "<Other";
   1173       break;
   1174     case DeclInfo::FunctionKind:
   1175       RootEndTag = "</Function>";
   1176       Result << "<Function";
   1177       switch (DI->TemplateKind) {
   1178       case DeclInfo::NotTemplate:
   1179         break;
   1180       case DeclInfo::Template:
   1181         Result << " templateKind=\"template\"";
   1182         break;
   1183       case DeclInfo::TemplateSpecialization:
   1184         Result << " templateKind=\"specialization\"";
   1185         break;
   1186       case DeclInfo::TemplatePartialSpecialization:
   1187         llvm_unreachable("partial specializations of functions "
   1188                          "are not allowed in C++");
   1189       }
   1190       if (DI->IsInstanceMethod)
   1191         Result << " isInstanceMethod=\"1\"";
   1192       if (DI->IsClassMethod)
   1193         Result << " isClassMethod=\"1\"";
   1194       break;
   1195     case DeclInfo::ClassKind:
   1196       RootEndTag = "</Class>";
   1197       Result << "<Class";
   1198       switch (DI->TemplateKind) {
   1199       case DeclInfo::NotTemplate:
   1200         break;
   1201       case DeclInfo::Template:
   1202         Result << " templateKind=\"template\"";
   1203         break;
   1204       case DeclInfo::TemplateSpecialization:
   1205         Result << " templateKind=\"specialization\"";
   1206         break;
   1207       case DeclInfo::TemplatePartialSpecialization:
   1208         Result << " templateKind=\"partialSpecialization\"";
   1209         break;
   1210       }
   1211       break;
   1212     case DeclInfo::VariableKind:
   1213       RootEndTag = "</Variable>";
   1214       Result << "<Variable";
   1215       break;
   1216     case DeclInfo::NamespaceKind:
   1217       RootEndTag = "</Namespace>";
   1218       Result << "<Namespace";
   1219       break;
   1220     case DeclInfo::TypedefKind:
   1221       RootEndTag = "</Typedef>";
   1222       Result << "<Typedef";
   1223       break;
   1224     case DeclInfo::EnumKind:
   1225       RootEndTag = "</Enum>";
   1226       Result << "<Enum";
   1227       break;
   1228     }
   1229 
   1230     {
   1231       // Print line and column number.
   1232       SourceLocation Loc = DI->CurrentDecl->getLocation();
   1233       std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
   1234       FileID FID = LocInfo.first;
   1235       unsigned FileOffset = LocInfo.second;
   1236 
   1237       if (!FID.isInvalid()) {
   1238         if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
   1239           Result << " file=\"";
   1240           appendToResultWithXMLEscaping(FE->getName());
   1241           Result << "\"";
   1242         }
   1243         Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
   1244                << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
   1245                << "\"";
   1246       }
   1247     }
   1248 
   1249     // Finish the root tag.
   1250     Result << ">";
   1251 
   1252     bool FoundName = false;
   1253     if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
   1254       if (DeclarationName DeclName = ND->getDeclName()) {
   1255         Result << "<Name>";
   1256         std::string Name = DeclName.getAsString();
   1257         appendToResultWithXMLEscaping(Name);
   1258         FoundName = true;
   1259         Result << "</Name>";
   1260       }
   1261     }
   1262     if (!FoundName)
   1263       Result << "<Name>&lt;anonymous&gt;</Name>";
   1264 
   1265     {
   1266       // Print USR.
   1267       SmallString<128> USR;
   1268       cxcursor::getDeclCursorUSR(DI->CommentDecl, USR);
   1269       if (!USR.empty()) {
   1270         Result << "<USR>";
   1271         appendToResultWithXMLEscaping(USR);
   1272         Result << "</USR>";
   1273       }
   1274     }
   1275   } else {
   1276     // No DeclInfo -- just emit some root tag and name tag.
   1277     RootEndTag = "</Other>";
   1278     Result << "<Other><Name>unknown</Name>";
   1279   }
   1280 
   1281   if (Parts.Headerfile) {
   1282     Result << "<Headerfile>";
   1283     visit(Parts.Headerfile);
   1284     Result << "</Headerfile>";
   1285   }
   1286 
   1287   {
   1288     // Pretty-print the declaration.
   1289     Result << "<Declaration>";
   1290     SmallString<128> Declaration;
   1291     getSourceTextOfDeclaration(DI, Declaration);
   1292     formatTextOfDeclaration(DI, Declaration);
   1293     appendToResultWithXMLEscaping(Declaration);
   1294 
   1295     Result << "</Declaration>";
   1296   }
   1297 
   1298   bool FirstParagraphIsBrief = false;
   1299   if (Parts.Brief) {
   1300     Result << "<Abstract>";
   1301     visit(Parts.Brief);
   1302     Result << "</Abstract>";
   1303   } else if (Parts.FirstParagraph) {
   1304     Result << "<Abstract>";
   1305     visit(Parts.FirstParagraph);
   1306     Result << "</Abstract>";
   1307     FirstParagraphIsBrief = true;
   1308   }
   1309 
   1310   if (Parts.TParams.size() != 0) {
   1311     Result << "<TemplateParameters>";
   1312     for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
   1313       visit(Parts.TParams[i]);
   1314     Result << "</TemplateParameters>";
   1315   }
   1316 
   1317   if (Parts.Params.size() != 0) {
   1318     Result << "<Parameters>";
   1319     for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
   1320       visit(Parts.Params[i]);
   1321     Result << "</Parameters>";
   1322   }
   1323 
   1324   if (Parts.Returns.size() != 0) {
   1325     Result << "<ResultDiscussion>";
   1326     for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
   1327       visit(Parts.Returns[i]);
   1328     Result << "</ResultDiscussion>";
   1329   }
   1330 
   1331   if (DI->CommentDecl->hasAttrs()) {
   1332     const AttrVec &Attrs = DI->CommentDecl->getAttrs();
   1333     for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
   1334       const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
   1335       if (!AA) {
   1336         if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
   1337           if (DA->getMessage().empty())
   1338             Result << "<Deprecated/>";
   1339           else {
   1340             Result << "<Deprecated>";
   1341             appendToResultWithXMLEscaping(DA->getMessage());
   1342             Result << "</Deprecated>";
   1343           }
   1344         }
   1345         else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
   1346           if (UA->getMessage().empty())
   1347             Result << "<Unavailable/>";
   1348           else {
   1349             Result << "<Unavailable>";
   1350             appendToResultWithXMLEscaping(UA->getMessage());
   1351             Result << "</Unavailable>";
   1352           }
   1353         }
   1354         continue;
   1355       }
   1356 
   1357       // 'availability' attribute.
   1358       Result << "<Availability";
   1359       StringRef Distribution;
   1360       if (AA->getPlatform()) {
   1361         Distribution = AvailabilityAttr::getPrettyPlatformName(
   1362                                         AA->getPlatform()->getName());
   1363         if (Distribution.empty())
   1364           Distribution = AA->getPlatform()->getName();
   1365       }
   1366       Result << " distribution=\"" << Distribution << "\">";
   1367       VersionTuple IntroducedInVersion = AA->getIntroduced();
   1368       if (!IntroducedInVersion.empty()) {
   1369         Result << "<IntroducedInVersion>"
   1370                << IntroducedInVersion.getAsString()
   1371                << "</IntroducedInVersion>";
   1372       }
   1373       VersionTuple DeprecatedInVersion = AA->getDeprecated();
   1374       if (!DeprecatedInVersion.empty()) {
   1375         Result << "<DeprecatedInVersion>"
   1376                << DeprecatedInVersion.getAsString()
   1377                << "</DeprecatedInVersion>";
   1378       }
   1379       VersionTuple RemovedAfterVersion = AA->getObsoleted();
   1380       if (!RemovedAfterVersion.empty()) {
   1381         Result << "<RemovedAfterVersion>"
   1382                << RemovedAfterVersion.getAsString()
   1383                << "</RemovedAfterVersion>";
   1384       }
   1385       StringRef DeprecationSummary = AA->getMessage();
   1386       if (!DeprecationSummary.empty()) {
   1387         Result << "<DeprecationSummary>";
   1388         appendToResultWithXMLEscaping(DeprecationSummary);
   1389         Result << "</DeprecationSummary>";
   1390       }
   1391       if (AA->getUnavailable())
   1392         Result << "<Unavailable/>";
   1393       Result << "</Availability>";
   1394     }
   1395   }
   1396 
   1397   {
   1398     bool StartTagEmitted = false;
   1399     for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
   1400       const Comment *C = Parts.MiscBlocks[i];
   1401       if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
   1402         continue;
   1403       if (!StartTagEmitted) {
   1404         Result << "<Discussion>";
   1405         StartTagEmitted = true;
   1406       }
   1407       visit(C);
   1408     }
   1409     if (StartTagEmitted)
   1410       Result << "</Discussion>";
   1411   }
   1412 
   1413   Result << RootEndTag;
   1414 
   1415   Result.flush();
   1416 }
   1417 
   1418 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
   1419   for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
   1420     const char C = *I;
   1421     switch (C) {
   1422       case '&':
   1423         Result << "&amp;";
   1424         break;
   1425       case '<':
   1426         Result << "&lt;";
   1427         break;
   1428       case '>':
   1429         Result << "&gt;";
   1430         break;
   1431       case '"':
   1432         Result << "&quot;";
   1433         break;
   1434       case '\'':
   1435         Result << "&apos;";
   1436         break;
   1437       default:
   1438         Result << C;
   1439         break;
   1440     }
   1441   }
   1442 }
   1443 
   1444 extern "C" {
   1445 
   1446 CXString clang_FullComment_getAsXML(CXComment CXC) {
   1447   const FullComment *FC = getASTNodeAs<FullComment>(CXC);
   1448   if (!FC)
   1449     return cxstring::createNull();
   1450   ASTContext &Context = FC->getDeclInfo()->CurrentDecl->getASTContext();
   1451   CXTranslationUnit TU = CXC.TranslationUnit;
   1452   SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager();
   1453 
   1454   if (!TU->FormatContext) {
   1455     TU->FormatContext = new SimpleFormatContext(Context.getLangOpts());
   1456   } else if ((TU->FormatInMemoryUniqueId % 1000) == 0) {
   1457     // Delete after some number of iterators, so the buffers don't grow
   1458     // too large.
   1459     delete TU->FormatContext;
   1460     TU->FormatContext = new SimpleFormatContext(Context.getLangOpts());
   1461   }
   1462 
   1463   SmallString<1024> XML;
   1464   CommentASTToXMLConverter Converter(FC, XML, getCommandTraits(CXC), SM,
   1465                                      *TU->FormatContext,
   1466                                      TU->FormatInMemoryUniqueId++);
   1467   Converter.visit(FC);
   1468   return cxstring::createDup(XML.str());
   1469 }
   1470 
   1471 } // end extern "C"
   1472 
   1473