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())
    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 and
    362 /// 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     if (LHS->isParamIndexValid())
    370       LHSIndex = LHS->getParamIndex();
    371     if (RHS->isParamIndexValid())
    372       RHSIndex = RHS->getParamIndex();
    373 
    374     return LHSIndex < RHSIndex;
    375   }
    376 };
    377 
    378 /// This comparison will sort template parameters in the following order:
    379 /// \li real template parameters (depth = 1) in index order;
    380 /// \li all other names (depth > 1);
    381 /// \li unresolved names.
    382 class TParamCommandCommentComparePosition {
    383 public:
    384   bool operator()(const TParamCommandComment *LHS,
    385                   const TParamCommandComment *RHS) const {
    386     // Sort unresolved names last.
    387     if (!LHS->isPositionValid())
    388       return false;
    389     if (!RHS->isPositionValid())
    390       return true;
    391 
    392     if (LHS->getDepth() > 1)
    393       return false;
    394     if (RHS->getDepth() > 1)
    395       return true;
    396 
    397     // Sort template parameters in index order.
    398     if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
    399       return LHS->getIndex(0) < RHS->getIndex(0);
    400 
    401     // Leave all other names in source order.
    402     return true;
    403   }
    404 };
    405 
    406 /// Separate parts of a FullComment.
    407 struct FullCommentParts {
    408   /// Take a full comment apart and initialize members accordingly.
    409   FullCommentParts(const FullComment *C,
    410                    const CommandTraits &Traits);
    411 
    412   const BlockContentComment *Brief;
    413   const BlockContentComment *Headerfile;
    414   const ParagraphComment *FirstParagraph;
    415   const BlockCommandComment *Returns;
    416   SmallVector<const ParamCommandComment *, 8> Params;
    417   SmallVector<const TParamCommandComment *, 4> TParams;
    418   SmallVector<const BlockContentComment *, 8> MiscBlocks;
    419 };
    420 
    421 FullCommentParts::FullCommentParts(const FullComment *C,
    422                                    const CommandTraits &Traits) :
    423     Brief(NULL), Headerfile(NULL), FirstParagraph(NULL), Returns(NULL) {
    424   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
    425        I != E; ++I) {
    426     const Comment *Child = *I;
    427     if (!Child)
    428       continue;
    429     switch (Child->getCommentKind()) {
    430     case Comment::NoCommentKind:
    431       continue;
    432 
    433     case Comment::ParagraphCommentKind: {
    434       const ParagraphComment *PC = cast<ParagraphComment>(Child);
    435       if (PC->isWhitespace())
    436         break;
    437       if (!FirstParagraph)
    438         FirstParagraph = PC;
    439 
    440       MiscBlocks.push_back(PC);
    441       break;
    442     }
    443 
    444     case Comment::BlockCommandCommentKind: {
    445       const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
    446       const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
    447       if (!Brief && Info->IsBriefCommand) {
    448         Brief = BCC;
    449         break;
    450       }
    451       if (!Headerfile && Info->IsHeaderfileCommand) {
    452         Headerfile = BCC;
    453         break;
    454       }
    455       if (!Returns && Info->IsReturnsCommand) {
    456         Returns = BCC;
    457         break;
    458       }
    459       MiscBlocks.push_back(BCC);
    460       break;
    461     }
    462 
    463     case Comment::ParamCommandCommentKind: {
    464       const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
    465       if (!PCC->hasParamName())
    466         break;
    467 
    468       if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
    469         break;
    470 
    471       Params.push_back(PCC);
    472       break;
    473     }
    474 
    475     case Comment::TParamCommandCommentKind: {
    476       const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
    477       if (!TPCC->hasParamName())
    478         break;
    479 
    480       if (!TPCC->hasNonWhitespaceParagraph())
    481         break;
    482 
    483       TParams.push_back(TPCC);
    484       break;
    485     }
    486 
    487     case Comment::VerbatimBlockCommentKind:
    488       MiscBlocks.push_back(cast<BlockCommandComment>(Child));
    489       break;
    490 
    491     case Comment::VerbatimLineCommentKind: {
    492       const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
    493       const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());
    494       if (!Info->IsDeclarationCommand)
    495         MiscBlocks.push_back(VLC);
    496       break;
    497     }
    498 
    499     case Comment::TextCommentKind:
    500     case Comment::InlineCommandCommentKind:
    501     case Comment::HTMLStartTagCommentKind:
    502     case Comment::HTMLEndTagCommentKind:
    503     case Comment::VerbatimBlockLineCommentKind:
    504     case Comment::FullCommentKind:
    505       llvm_unreachable("AST node of this kind can't be a child of "
    506                        "a FullComment");
    507     }
    508   }
    509 
    510   // Sort params in order they are declared in the function prototype.
    511   // Unresolved parameters are put at the end of the list in the same order
    512   // they were seen in the comment.
    513   std::stable_sort(Params.begin(), Params.end(),
    514                    ParamCommandCommentCompareIndex());
    515 
    516   std::stable_sort(TParams.begin(), TParams.end(),
    517                    TParamCommandCommentComparePosition());
    518 }
    519 
    520 void PrintHTMLStartTagComment(const HTMLStartTagComment *C,
    521                               llvm::raw_svector_ostream &Result) {
    522   Result << "<" << C->getTagName();
    523 
    524   if (C->getNumAttrs() != 0) {
    525     for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
    526       Result << " ";
    527       const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
    528       Result << Attr.Name;
    529       if (!Attr.Value.empty())
    530         Result << "=\"" << Attr.Value << "\"";
    531     }
    532   }
    533 
    534   if (!C->isSelfClosing())
    535     Result << ">";
    536   else
    537     Result << "/>";
    538 }
    539 
    540 class CommentASTToHTMLConverter :
    541     public ConstCommentVisitor<CommentASTToHTMLConverter> {
    542 public:
    543   /// \param Str accumulator for HTML.
    544   CommentASTToHTMLConverter(const FullComment *FC,
    545                             SmallVectorImpl<char> &Str,
    546                             const CommandTraits &Traits) :
    547       FC(FC), Result(Str), Traits(Traits)
    548   { }
    549 
    550   // Inline content.
    551   void visitTextComment(const TextComment *C);
    552   void visitInlineCommandComment(const InlineCommandComment *C);
    553   void visitHTMLStartTagComment(const HTMLStartTagComment *C);
    554   void visitHTMLEndTagComment(const HTMLEndTagComment *C);
    555 
    556   // Block content.
    557   void visitParagraphComment(const ParagraphComment *C);
    558   void visitBlockCommandComment(const BlockCommandComment *C);
    559   void visitParamCommandComment(const ParamCommandComment *C);
    560   void visitTParamCommandComment(const TParamCommandComment *C);
    561   void visitVerbatimBlockComment(const VerbatimBlockComment *C);
    562   void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
    563   void visitVerbatimLineComment(const VerbatimLineComment *C);
    564 
    565   void visitFullComment(const FullComment *C);
    566 
    567   // Helpers.
    568 
    569   /// Convert a paragraph that is not a block by itself (an argument to some
    570   /// command).
    571   void visitNonStandaloneParagraphComment(const ParagraphComment *C);
    572 
    573   void appendToResultWithHTMLEscaping(StringRef S);
    574 
    575 private:
    576   const FullComment *FC;
    577   /// Output stream for HTML.
    578   llvm::raw_svector_ostream Result;
    579 
    580   const CommandTraits &Traits;
    581 };
    582 } // end unnamed namespace
    583 
    584 void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
    585   appendToResultWithHTMLEscaping(C->getText());
    586 }
    587 
    588 void CommentASTToHTMLConverter::visitInlineCommandComment(
    589                                   const InlineCommandComment *C) {
    590   // Nothing to render if no arguments supplied.
    591   if (C->getNumArgs() == 0)
    592     return;
    593 
    594   // Nothing to render if argument is empty.
    595   StringRef Arg0 = C->getArgText(0);
    596   if (Arg0.empty())
    597     return;
    598 
    599   switch (C->getRenderKind()) {
    600   case InlineCommandComment::RenderNormal:
    601     for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
    602       appendToResultWithHTMLEscaping(C->getArgText(i));
    603       Result << " ";
    604     }
    605     return;
    606 
    607   case InlineCommandComment::RenderBold:
    608     assert(C->getNumArgs() == 1);
    609     Result << "<b>";
    610     appendToResultWithHTMLEscaping(Arg0);
    611     Result << "</b>";
    612     return;
    613   case InlineCommandComment::RenderMonospaced:
    614     assert(C->getNumArgs() == 1);
    615     Result << "<tt>";
    616     appendToResultWithHTMLEscaping(Arg0);
    617     Result<< "</tt>";
    618     return;
    619   case InlineCommandComment::RenderEmphasized:
    620     assert(C->getNumArgs() == 1);
    621     Result << "<em>";
    622     appendToResultWithHTMLEscaping(Arg0);
    623     Result << "</em>";
    624     return;
    625   }
    626 }
    627 
    628 void CommentASTToHTMLConverter::visitHTMLStartTagComment(
    629                                   const HTMLStartTagComment *C) {
    630   PrintHTMLStartTagComment(C, Result);
    631 }
    632 
    633 void CommentASTToHTMLConverter::visitHTMLEndTagComment(
    634                                   const HTMLEndTagComment *C) {
    635   Result << "</" << C->getTagName() << ">";
    636 }
    637 
    638 void CommentASTToHTMLConverter::visitParagraphComment(
    639                                   const ParagraphComment *C) {
    640   if (C->isWhitespace())
    641     return;
    642 
    643   Result << "<p>";
    644   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
    645        I != E; ++I) {
    646     visit(*I);
    647   }
    648   Result << "</p>";
    649 }
    650 
    651 void CommentASTToHTMLConverter::visitBlockCommandComment(
    652                                   const BlockCommandComment *C) {
    653   const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
    654   if (Info->IsBriefCommand) {
    655     Result << "<p class=\"para-brief\">";
    656     visitNonStandaloneParagraphComment(C->getParagraph());
    657     Result << "</p>";
    658     return;
    659   }
    660   if (Info->IsReturnsCommand) {
    661     Result << "<p class=\"para-returns\">"
    662               "<span class=\"word-returns\">Returns</span> ";
    663     visitNonStandaloneParagraphComment(C->getParagraph());
    664     Result << "</p>";
    665     return;
    666   }
    667   // We don't know anything about this command.  Just render the paragraph.
    668   visit(C->getParagraph());
    669 }
    670 
    671 void CommentASTToHTMLConverter::visitParamCommandComment(
    672                                   const ParamCommandComment *C) {
    673   if (C->isParamIndexValid()) {
    674     Result << "<dt class=\"param-name-index-"
    675            << C->getParamIndex()
    676            << "\">";
    677     appendToResultWithHTMLEscaping(C->getParamName(FC));
    678   } else {
    679     Result << "<dt class=\"param-name-index-invalid\">";
    680     appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
    681   }
    682   Result << "</dt>";
    683 
    684   if (C->isParamIndexValid()) {
    685     Result << "<dd class=\"param-descr-index-"
    686            << C->getParamIndex()
    687            << "\">";
    688   } else
    689     Result << "<dd class=\"param-descr-index-invalid\">";
    690 
    691   visitNonStandaloneParagraphComment(C->getParagraph());
    692   Result << "</dd>";
    693 }
    694 
    695 void CommentASTToHTMLConverter::visitTParamCommandComment(
    696                                   const TParamCommandComment *C) {
    697   if (C->isPositionValid()) {
    698     if (C->getDepth() == 1)
    699       Result << "<dt class=\"tparam-name-index-"
    700              << C->getIndex(0)
    701              << "\">";
    702     else
    703       Result << "<dt class=\"tparam-name-index-other\">";
    704     appendToResultWithHTMLEscaping(C->getParamName(FC));
    705   } else {
    706     Result << "<dt class=\"tparam-name-index-invalid\">";
    707     appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
    708   }
    709 
    710   Result << "</dt>";
    711 
    712   if (C->isPositionValid()) {
    713     if (C->getDepth() == 1)
    714       Result << "<dd class=\"tparam-descr-index-"
    715              << C->getIndex(0)
    716              << "\">";
    717     else
    718       Result << "<dd class=\"tparam-descr-index-other\">";
    719   } else
    720     Result << "<dd class=\"tparam-descr-index-invalid\">";
    721 
    722   visitNonStandaloneParagraphComment(C->getParagraph());
    723   Result << "</dd>";
    724 }
    725 
    726 void CommentASTToHTMLConverter::visitVerbatimBlockComment(
    727                                   const VerbatimBlockComment *C) {
    728   unsigned NumLines = C->getNumLines();
    729   if (NumLines == 0)
    730     return;
    731 
    732   Result << "<pre>";
    733   for (unsigned i = 0; i != NumLines; ++i) {
    734     appendToResultWithHTMLEscaping(C->getText(i));
    735     if (i + 1 != NumLines)
    736       Result << '\n';
    737   }
    738   Result << "</pre>";
    739 }
    740 
    741 void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
    742                                   const VerbatimBlockLineComment *C) {
    743   llvm_unreachable("should not see this AST node");
    744 }
    745 
    746 void CommentASTToHTMLConverter::visitVerbatimLineComment(
    747                                   const VerbatimLineComment *C) {
    748   Result << "<pre>";
    749   appendToResultWithHTMLEscaping(C->getText());
    750   Result << "</pre>";
    751 }
    752 
    753 void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
    754   FullCommentParts Parts(C, Traits);
    755 
    756   bool FirstParagraphIsBrief = false;
    757   if (Parts.Headerfile)
    758     visit(Parts.Headerfile);
    759   if (Parts.Brief)
    760     visit(Parts.Brief);
    761   else if (Parts.FirstParagraph) {
    762     Result << "<p class=\"para-brief\">";
    763     visitNonStandaloneParagraphComment(Parts.FirstParagraph);
    764     Result << "</p>";
    765     FirstParagraphIsBrief = true;
    766   }
    767 
    768   for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
    769     const Comment *C = Parts.MiscBlocks[i];
    770     if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
    771       continue;
    772     visit(C);
    773   }
    774 
    775   if (Parts.TParams.size() != 0) {
    776     Result << "<dl>";
    777     for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
    778       visit(Parts.TParams[i]);
    779     Result << "</dl>";
    780   }
    781 
    782   if (Parts.Params.size() != 0) {
    783     Result << "<dl>";
    784     for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
    785       visit(Parts.Params[i]);
    786     Result << "</dl>";
    787   }
    788 
    789   if (Parts.Returns)
    790     visit(Parts.Returns);
    791 
    792   Result.flush();
    793 }
    794 
    795 void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
    796                                   const ParagraphComment *C) {
    797   if (!C)
    798     return;
    799 
    800   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
    801        I != E; ++I) {
    802     visit(*I);
    803   }
    804 }
    805 
    806 void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
    807   for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
    808     const char C = *I;
    809     switch (C) {
    810       case '&':
    811         Result << "&amp;";
    812         break;
    813       case '<':
    814         Result << "&lt;";
    815         break;
    816       case '>':
    817         Result << "&gt;";
    818         break;
    819       case '"':
    820         Result << "&quot;";
    821         break;
    822       case '\'':
    823         Result << "&#39;";
    824         break;
    825       case '/':
    826         Result << "&#47;";
    827         break;
    828       default:
    829         Result << C;
    830         break;
    831     }
    832   }
    833 }
    834 
    835 extern "C" {
    836 
    837 CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
    838   const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
    839   if (!HTC)
    840     return cxstring::createNull();
    841 
    842   SmallString<128> HTML;
    843   CommentASTToHTMLConverter Converter(0, HTML, getCommandTraits(CXC));
    844   Converter.visit(HTC);
    845   return cxstring::createDup(HTML.str());
    846 }
    847 
    848 CXString clang_FullComment_getAsHTML(CXComment CXC) {
    849   const FullComment *FC = getASTNodeAs<FullComment>(CXC);
    850   if (!FC)
    851     return cxstring::createNull();
    852 
    853   SmallString<1024> HTML;
    854   CommentASTToHTMLConverter Converter(FC, HTML, getCommandTraits(CXC));
    855   Converter.visit(FC);
    856   return cxstring::createDup(HTML.str());
    857 }
    858 
    859 } // end extern "C"
    860 
    861 namespace {
    862 class CommentASTToXMLConverter :
    863     public ConstCommentVisitor<CommentASTToXMLConverter> {
    864 public:
    865   /// \param Str accumulator for XML.
    866   CommentASTToXMLConverter(const FullComment *FC,
    867                            SmallVectorImpl<char> &Str,
    868                            const CommandTraits &Traits,
    869                            const SourceManager &SM,
    870                            SimpleFormatContext &SFC,
    871                            unsigned FUID) :
    872       FC(FC), Result(Str), Traits(Traits), SM(SM),
    873       FormatRewriterContext(SFC),
    874       FormatInMemoryUniqueId(FUID) { }
    875 
    876   // Inline content.
    877   void visitTextComment(const TextComment *C);
    878   void visitInlineCommandComment(const InlineCommandComment *C);
    879   void visitHTMLStartTagComment(const HTMLStartTagComment *C);
    880   void visitHTMLEndTagComment(const HTMLEndTagComment *C);
    881 
    882   // Block content.
    883   void visitParagraphComment(const ParagraphComment *C);
    884 
    885   void appendParagraphCommentWithKind(const ParagraphComment *C,
    886                                       StringRef Kind);
    887 
    888   void visitBlockCommandComment(const BlockCommandComment *C);
    889   void visitParamCommandComment(const ParamCommandComment *C);
    890   void visitTParamCommandComment(const TParamCommandComment *C);
    891   void visitVerbatimBlockComment(const VerbatimBlockComment *C);
    892   void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
    893   void visitVerbatimLineComment(const VerbatimLineComment *C);
    894 
    895   void visitFullComment(const FullComment *C);
    896 
    897   // Helpers.
    898   void appendToResultWithXMLEscaping(StringRef S);
    899 
    900   void formatTextOfDeclaration(const DeclInfo *DI,
    901                                SmallString<128> &Declaration);
    902 
    903 private:
    904   const FullComment *FC;
    905 
    906   /// Output stream for XML.
    907   llvm::raw_svector_ostream Result;
    908 
    909   const CommandTraits &Traits;
    910   const SourceManager &SM;
    911   SimpleFormatContext &FormatRewriterContext;
    912   unsigned FormatInMemoryUniqueId;
    913 };
    914 
    915 void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
    916                                 SmallVectorImpl<char> &Str) {
    917   ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
    918   const LangOptions &LangOpts = Context.getLangOpts();
    919   llvm::raw_svector_ostream OS(Str);
    920   PrintingPolicy PPolicy(LangOpts);
    921   PPolicy.PolishForDeclaration = true;
    922   PPolicy.TerseOutput = true;
    923   ThisDecl->CurrentDecl->print(OS, PPolicy,
    924                                /*Indentation*/0, /*PrintInstantiation*/false);
    925 }
    926 
    927 void CommentASTToXMLConverter::formatTextOfDeclaration(
    928                                               const DeclInfo *DI,
    929                                               SmallString<128> &Declaration) {
    930   // FIXME. formatting API expects null terminated input string.
    931   // There might be more efficient way of doing this.
    932   std::string StringDecl = Declaration.str();
    933 
    934   // Formatter specific code.
    935   // Form a unique in memory buffer name.
    936   SmallString<128> filename;
    937   filename += "xmldecl";
    938   filename += llvm::utostr(FormatInMemoryUniqueId);
    939   filename += ".xd";
    940   FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl);
    941   SourceLocation Start =
    942     FormatRewriterContext.Sources.getLocForStartOfFile(ID).getLocWithOffset(0);
    943   unsigned Length = Declaration.size();
    944 
    945   std::vector<CharSourceRange>
    946     Ranges(1, CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length)));
    947   ASTContext &Context = DI->CurrentDecl->getASTContext();
    948   const LangOptions &LangOpts = Context.getLangOpts();
    949   Lexer Lex(ID, FormatRewriterContext.Sources.getBuffer(ID),
    950             FormatRewriterContext.Sources, LangOpts);
    951   tooling::Replacements Replace =
    952     reformat(format::getLLVMStyle(), Lex, FormatRewriterContext.Sources, Ranges);
    953   applyAllReplacements(Replace, FormatRewriterContext.Rewrite);
    954   Declaration = FormatRewriterContext.getRewrittenText(ID);
    955 }
    956 
    957 } // end unnamed namespace
    958 
    959 void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
    960   appendToResultWithXMLEscaping(C->getText());
    961 }
    962 
    963 void CommentASTToXMLConverter::visitInlineCommandComment(const InlineCommandComment *C) {
    964   // Nothing to render if no arguments supplied.
    965   if (C->getNumArgs() == 0)
    966     return;
    967 
    968   // Nothing to render if argument is empty.
    969   StringRef Arg0 = C->getArgText(0);
    970   if (Arg0.empty())
    971     return;
    972 
    973   switch (C->getRenderKind()) {
    974   case InlineCommandComment::RenderNormal:
    975     for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
    976       appendToResultWithXMLEscaping(C->getArgText(i));
    977       Result << " ";
    978     }
    979     return;
    980   case InlineCommandComment::RenderBold:
    981     assert(C->getNumArgs() == 1);
    982     Result << "<bold>";
    983     appendToResultWithXMLEscaping(Arg0);
    984     Result << "</bold>";
    985     return;
    986   case InlineCommandComment::RenderMonospaced:
    987     assert(C->getNumArgs() == 1);
    988     Result << "<monospaced>";
    989     appendToResultWithXMLEscaping(Arg0);
    990     Result << "</monospaced>";
    991     return;
    992   case InlineCommandComment::RenderEmphasized:
    993     assert(C->getNumArgs() == 1);
    994     Result << "<emphasized>";
    995     appendToResultWithXMLEscaping(Arg0);
    996     Result << "</emphasized>";
    997     return;
    998   }
    999 }
   1000 
   1001 void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) {
   1002   Result << "<rawHTML><![CDATA[";
   1003   PrintHTMLStartTagComment(C, Result);
   1004   Result << "]]></rawHTML>";
   1005 }
   1006 
   1007 void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
   1008   Result << "<rawHTML>&lt;/" << C->getTagName() << "&gt;</rawHTML>";
   1009 }
   1010 
   1011 void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
   1012   appendParagraphCommentWithKind(C, StringRef());
   1013 }
   1014 
   1015 void CommentASTToXMLConverter::appendParagraphCommentWithKind(
   1016                                   const ParagraphComment *C,
   1017                                   StringRef ParagraphKind) {
   1018   if (C->isWhitespace())
   1019     return;
   1020 
   1021   if (ParagraphKind.empty())
   1022     Result << "<Para>";
   1023   else
   1024     Result << "<Para kind=\"" << ParagraphKind << "\">";
   1025 
   1026   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
   1027        I != E; ++I) {
   1028     visit(*I);
   1029   }
   1030   Result << "</Para>";
   1031 }
   1032 
   1033 void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) {
   1034   StringRef ParagraphKind;
   1035 
   1036   switch (C->getCommandID()) {
   1037   case CommandTraits::KCI_attention:
   1038   case CommandTraits::KCI_author:
   1039   case CommandTraits::KCI_authors:
   1040   case CommandTraits::KCI_bug:
   1041   case CommandTraits::KCI_copyright:
   1042   case CommandTraits::KCI_date:
   1043   case CommandTraits::KCI_invariant:
   1044   case CommandTraits::KCI_note:
   1045   case CommandTraits::KCI_post:
   1046   case CommandTraits::KCI_pre:
   1047   case CommandTraits::KCI_remark:
   1048   case CommandTraits::KCI_remarks:
   1049   case CommandTraits::KCI_sa:
   1050   case CommandTraits::KCI_see:
   1051   case CommandTraits::KCI_since:
   1052   case CommandTraits::KCI_todo:
   1053   case CommandTraits::KCI_version:
   1054   case CommandTraits::KCI_warning:
   1055     ParagraphKind = C->getCommandName(Traits);
   1056   default:
   1057     break;
   1058   }
   1059 
   1060   appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
   1061 }
   1062 
   1063 void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) {
   1064   Result << "<Parameter><Name>";
   1065   appendToResultWithXMLEscaping(C->isParamIndexValid() ? C->getParamName(FC)
   1066                                                        : C->getParamNameAsWritten());
   1067   Result << "</Name>";
   1068 
   1069   if (C->isParamIndexValid())
   1070     Result << "<Index>" << C->getParamIndex() << "</Index>";
   1071 
   1072   Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
   1073   switch (C->getDirection()) {
   1074   case ParamCommandComment::In:
   1075     Result << "in";
   1076     break;
   1077   case ParamCommandComment::Out:
   1078     Result << "out";
   1079     break;
   1080   case ParamCommandComment::InOut:
   1081     Result << "in,out";
   1082     break;
   1083   }
   1084   Result << "</Direction><Discussion>";
   1085   visit(C->getParagraph());
   1086   Result << "</Discussion></Parameter>";
   1087 }
   1088 
   1089 void CommentASTToXMLConverter::visitTParamCommandComment(
   1090                                   const TParamCommandComment *C) {
   1091   Result << "<Parameter><Name>";
   1092   appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
   1093                                 : C->getParamNameAsWritten());
   1094   Result << "</Name>";
   1095 
   1096   if (C->isPositionValid() && C->getDepth() == 1) {
   1097     Result << "<Index>" << C->getIndex(0) << "</Index>";
   1098   }
   1099 
   1100   Result << "<Discussion>";
   1101   visit(C->getParagraph());
   1102   Result << "</Discussion></Parameter>";
   1103 }
   1104 
   1105 void CommentASTToXMLConverter::visitVerbatimBlockComment(
   1106                                   const VerbatimBlockComment *C) {
   1107   unsigned NumLines = C->getNumLines();
   1108   if (NumLines == 0)
   1109     return;
   1110 
   1111   switch (C->getCommandID()) {
   1112   case CommandTraits::KCI_code:
   1113     Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
   1114     break;
   1115   default:
   1116     Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
   1117     break;
   1118   }
   1119   for (unsigned i = 0; i != NumLines; ++i) {
   1120     appendToResultWithXMLEscaping(C->getText(i));
   1121     if (i + 1 != NumLines)
   1122       Result << '\n';
   1123   }
   1124   Result << "</Verbatim>";
   1125 }
   1126 
   1127 void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
   1128                                   const VerbatimBlockLineComment *C) {
   1129   llvm_unreachable("should not see this AST node");
   1130 }
   1131 
   1132 void CommentASTToXMLConverter::visitVerbatimLineComment(
   1133                                   const VerbatimLineComment *C) {
   1134   Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
   1135   appendToResultWithXMLEscaping(C->getText());
   1136   Result << "</Verbatim>";
   1137 }
   1138 
   1139 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
   1140   FullCommentParts Parts(C, Traits);
   1141 
   1142   const DeclInfo *DI = C->getDeclInfo();
   1143   StringRef RootEndTag;
   1144   if (DI) {
   1145     switch (DI->getKind()) {
   1146     case DeclInfo::OtherKind:
   1147       RootEndTag = "</Other>";
   1148       Result << "<Other";
   1149       break;
   1150     case DeclInfo::FunctionKind:
   1151       RootEndTag = "</Function>";
   1152       Result << "<Function";
   1153       switch (DI->TemplateKind) {
   1154       case DeclInfo::NotTemplate:
   1155         break;
   1156       case DeclInfo::Template:
   1157         Result << " templateKind=\"template\"";
   1158         break;
   1159       case DeclInfo::TemplateSpecialization:
   1160         Result << " templateKind=\"specialization\"";
   1161         break;
   1162       case DeclInfo::TemplatePartialSpecialization:
   1163         llvm_unreachable("partial specializations of functions "
   1164                          "are not allowed in C++");
   1165       }
   1166       if (DI->IsInstanceMethod)
   1167         Result << " isInstanceMethod=\"1\"";
   1168       if (DI->IsClassMethod)
   1169         Result << " isClassMethod=\"1\"";
   1170       break;
   1171     case DeclInfo::ClassKind:
   1172       RootEndTag = "</Class>";
   1173       Result << "<Class";
   1174       switch (DI->TemplateKind) {
   1175       case DeclInfo::NotTemplate:
   1176         break;
   1177       case DeclInfo::Template:
   1178         Result << " templateKind=\"template\"";
   1179         break;
   1180       case DeclInfo::TemplateSpecialization:
   1181         Result << " templateKind=\"specialization\"";
   1182         break;
   1183       case DeclInfo::TemplatePartialSpecialization:
   1184         Result << " templateKind=\"partialSpecialization\"";
   1185         break;
   1186       }
   1187       break;
   1188     case DeclInfo::VariableKind:
   1189       RootEndTag = "</Variable>";
   1190       Result << "<Variable";
   1191       break;
   1192     case DeclInfo::NamespaceKind:
   1193       RootEndTag = "</Namespace>";
   1194       Result << "<Namespace";
   1195       break;
   1196     case DeclInfo::TypedefKind:
   1197       RootEndTag = "</Typedef>";
   1198       Result << "<Typedef";
   1199       break;
   1200     case DeclInfo::EnumKind:
   1201       RootEndTag = "</Enum>";
   1202       Result << "<Enum";
   1203       break;
   1204     }
   1205 
   1206     {
   1207       // Print line and column number.
   1208       SourceLocation Loc = DI->CurrentDecl->getLocation();
   1209       std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
   1210       FileID FID = LocInfo.first;
   1211       unsigned FileOffset = LocInfo.second;
   1212 
   1213       if (!FID.isInvalid()) {
   1214         if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
   1215           Result << " file=\"";
   1216           appendToResultWithXMLEscaping(FE->getName());
   1217           Result << "\"";
   1218         }
   1219         Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
   1220                << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
   1221                << "\"";
   1222       }
   1223     }
   1224 
   1225     // Finish the root tag.
   1226     Result << ">";
   1227 
   1228     bool FoundName = false;
   1229     if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
   1230       if (DeclarationName DeclName = ND->getDeclName()) {
   1231         Result << "<Name>";
   1232         std::string Name = DeclName.getAsString();
   1233         appendToResultWithXMLEscaping(Name);
   1234         FoundName = true;
   1235         Result << "</Name>";
   1236       }
   1237     }
   1238     if (!FoundName)
   1239       Result << "<Name>&lt;anonymous&gt;</Name>";
   1240 
   1241     {
   1242       // Print USR.
   1243       SmallString<128> USR;
   1244       cxcursor::getDeclCursorUSR(DI->CommentDecl, USR);
   1245       if (!USR.empty()) {
   1246         Result << "<USR>";
   1247         appendToResultWithXMLEscaping(USR);
   1248         Result << "</USR>";
   1249       }
   1250     }
   1251   } else {
   1252     // No DeclInfo -- just emit some root tag and name tag.
   1253     RootEndTag = "</Other>";
   1254     Result << "<Other><Name>unknown</Name>";
   1255   }
   1256 
   1257   if (Parts.Headerfile) {
   1258     Result << "<Headerfile>";
   1259     visit(Parts.Headerfile);
   1260     Result << "</Headerfile>";
   1261   }
   1262 
   1263   {
   1264     // Pretty-print the declaration.
   1265     Result << "<Declaration>";
   1266     SmallString<128> Declaration;
   1267     getSourceTextOfDeclaration(DI, Declaration);
   1268     formatTextOfDeclaration(DI, Declaration);
   1269     appendToResultWithXMLEscaping(Declaration);
   1270 
   1271     Result << "</Declaration>";
   1272   }
   1273 
   1274   bool FirstParagraphIsBrief = false;
   1275   if (Parts.Brief) {
   1276     Result << "<Abstract>";
   1277     visit(Parts.Brief);
   1278     Result << "</Abstract>";
   1279   } else if (Parts.FirstParagraph) {
   1280     Result << "<Abstract>";
   1281     visit(Parts.FirstParagraph);
   1282     Result << "</Abstract>";
   1283     FirstParagraphIsBrief = true;
   1284   }
   1285 
   1286   if (Parts.TParams.size() != 0) {
   1287     Result << "<TemplateParameters>";
   1288     for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
   1289       visit(Parts.TParams[i]);
   1290     Result << "</TemplateParameters>";
   1291   }
   1292 
   1293   if (Parts.Params.size() != 0) {
   1294     Result << "<Parameters>";
   1295     for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
   1296       visit(Parts.Params[i]);
   1297     Result << "</Parameters>";
   1298   }
   1299 
   1300   if (Parts.Returns) {
   1301     Result << "<ResultDiscussion>";
   1302     visit(Parts.Returns);
   1303     Result << "</ResultDiscussion>";
   1304   }
   1305 
   1306   if (DI->CommentDecl->hasAttrs()) {
   1307     const AttrVec &Attrs = DI->CommentDecl->getAttrs();
   1308     for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
   1309       const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
   1310       if (!AA) {
   1311         if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
   1312           if (DA->getMessage().empty())
   1313             Result << "<Deprecated/>";
   1314           else {
   1315             Result << "<Deprecated>";
   1316             appendToResultWithXMLEscaping(DA->getMessage());
   1317             Result << "</Deprecated>";
   1318           }
   1319         }
   1320         else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
   1321           if (UA->getMessage().empty())
   1322             Result << "<Unavailable/>";
   1323           else {
   1324             Result << "<Unavailable>";
   1325             appendToResultWithXMLEscaping(UA->getMessage());
   1326             Result << "</Unavailable>";
   1327           }
   1328         }
   1329         continue;
   1330       }
   1331 
   1332       // 'availability' attribute.
   1333       Result << "<Availability";
   1334       StringRef Distribution;
   1335       if (AA->getPlatform()) {
   1336         Distribution = AvailabilityAttr::getPrettyPlatformName(
   1337                                         AA->getPlatform()->getName());
   1338         if (Distribution.empty())
   1339           Distribution = AA->getPlatform()->getName();
   1340       }
   1341       Result << " distribution=\"" << Distribution << "\">";
   1342       VersionTuple IntroducedInVersion = AA->getIntroduced();
   1343       if (!IntroducedInVersion.empty()) {
   1344         Result << "<IntroducedInVersion>"
   1345                << IntroducedInVersion.getAsString()
   1346                << "</IntroducedInVersion>";
   1347       }
   1348       VersionTuple DeprecatedInVersion = AA->getDeprecated();
   1349       if (!DeprecatedInVersion.empty()) {
   1350         Result << "<DeprecatedInVersion>"
   1351                << DeprecatedInVersion.getAsString()
   1352                << "</DeprecatedInVersion>";
   1353       }
   1354       VersionTuple RemovedAfterVersion = AA->getObsoleted();
   1355       if (!RemovedAfterVersion.empty()) {
   1356         Result << "<RemovedAfterVersion>"
   1357                << RemovedAfterVersion.getAsString()
   1358                << "</RemovedAfterVersion>";
   1359       }
   1360       StringRef DeprecationSummary = AA->getMessage();
   1361       if (!DeprecationSummary.empty()) {
   1362         Result << "<DeprecationSummary>";
   1363         appendToResultWithXMLEscaping(DeprecationSummary);
   1364         Result << "</DeprecationSummary>";
   1365       }
   1366       if (AA->getUnavailable())
   1367         Result << "<Unavailable/>";
   1368       Result << "</Availability>";
   1369     }
   1370   }
   1371 
   1372   {
   1373     bool StartTagEmitted = false;
   1374     for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
   1375       const Comment *C = Parts.MiscBlocks[i];
   1376       if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
   1377         continue;
   1378       if (!StartTagEmitted) {
   1379         Result << "<Discussion>";
   1380         StartTagEmitted = true;
   1381       }
   1382       visit(C);
   1383     }
   1384     if (StartTagEmitted)
   1385       Result << "</Discussion>";
   1386   }
   1387 
   1388   Result << RootEndTag;
   1389 
   1390   Result.flush();
   1391 }
   1392 
   1393 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
   1394   for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
   1395     const char C = *I;
   1396     switch (C) {
   1397       case '&':
   1398         Result << "&amp;";
   1399         break;
   1400       case '<':
   1401         Result << "&lt;";
   1402         break;
   1403       case '>':
   1404         Result << "&gt;";
   1405         break;
   1406       case '"':
   1407         Result << "&quot;";
   1408         break;
   1409       case '\'':
   1410         Result << "&apos;";
   1411         break;
   1412       default:
   1413         Result << C;
   1414         break;
   1415     }
   1416   }
   1417 }
   1418 
   1419 extern "C" {
   1420 
   1421 CXString clang_FullComment_getAsXML(CXComment CXC) {
   1422   const FullComment *FC = getASTNodeAs<FullComment>(CXC);
   1423   if (!FC)
   1424     return cxstring::createNull();
   1425   ASTContext &Context = FC->getDeclInfo()->CurrentDecl->getASTContext();
   1426   CXTranslationUnit TU = CXC.TranslationUnit;
   1427   SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager();
   1428 
   1429   if (!TU->FormatContext) {
   1430     TU->FormatContext = new SimpleFormatContext(Context.getLangOpts());
   1431   } else if ((TU->FormatInMemoryUniqueId % 1000) == 0) {
   1432     // Delete after some number of iterators, so the buffers don't grow
   1433     // too large.
   1434     delete TU->FormatContext;
   1435     TU->FormatContext = new SimpleFormatContext(Context.getLangOpts());
   1436   }
   1437 
   1438   SmallString<1024> XML;
   1439   CommentASTToXMLConverter Converter(FC, XML, getCommandTraits(CXC), SM,
   1440                                      *TU->FormatContext,
   1441                                      TU->FormatInMemoryUniqueId++);
   1442   Converter.visit(FC);
   1443   return cxstring::createDup(XML.str());
   1444 }
   1445 
   1446 } // end extern "C"
   1447 
   1448