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