clang API Documentation

CommentToXML.cpp
Go to the documentation of this file.
00001 //===--- CommentToXML.cpp - Convert comments to XML representation --------===//
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 
00010 #include "clang/Index/CommentToXML.h"
00011 #include "SimpleFormatContext.h"
00012 #include "clang/AST/ASTContext.h"
00013 #include "clang/AST/Attr.h"
00014 #include "clang/AST/Comment.h"
00015 #include "clang/AST/CommentVisitor.h"
00016 #include "clang/Format/Format.h"
00017 #include "clang/Index/USRGeneration.h"
00018 #include "llvm/ADT/StringExtras.h"
00019 #include "llvm/ADT/TinyPtrVector.h"
00020 #include "llvm/Support/raw_ostream.h"
00021 
00022 using namespace clang;
00023 using namespace clang::comments;
00024 using namespace clang::index;
00025 
00026 namespace {
00027 
00028 /// This comparison will sort parameters with valid index by index, then vararg
00029 /// parameters, and invalid (unresolved) parameters last.
00030 class ParamCommandCommentCompareIndex {
00031 public:
00032   bool operator()(const ParamCommandComment *LHS,
00033                   const ParamCommandComment *RHS) const {
00034     unsigned LHSIndex = UINT_MAX;
00035     unsigned RHSIndex = UINT_MAX;
00036 
00037     if (LHS->isParamIndexValid()) {
00038       if (LHS->isVarArgParam())
00039         LHSIndex = UINT_MAX - 1;
00040       else
00041         LHSIndex = LHS->getParamIndex();
00042     }
00043     if (RHS->isParamIndexValid()) {
00044       if (RHS->isVarArgParam())
00045         RHSIndex = UINT_MAX - 1;
00046       else
00047         RHSIndex = RHS->getParamIndex();
00048     }
00049     return LHSIndex < RHSIndex;
00050   }
00051 };
00052 
00053 /// This comparison will sort template parameters in the following order:
00054 /// \li real template parameters (depth = 1) in index order;
00055 /// \li all other names (depth > 1);
00056 /// \li unresolved names.
00057 class TParamCommandCommentComparePosition {
00058 public:
00059   bool operator()(const TParamCommandComment *LHS,
00060                   const TParamCommandComment *RHS) const {
00061     // Sort unresolved names last.
00062     if (!LHS->isPositionValid())
00063       return false;
00064     if (!RHS->isPositionValid())
00065       return true;
00066 
00067     if (LHS->getDepth() > 1)
00068       return false;
00069     if (RHS->getDepth() > 1)
00070       return true;
00071 
00072     // Sort template parameters in index order.
00073     if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
00074       return LHS->getIndex(0) < RHS->getIndex(0);
00075 
00076     // Leave all other names in source order.
00077     return true;
00078   }
00079 };
00080 
00081 /// Separate parts of a FullComment.
00082 struct FullCommentParts {
00083   /// Take a full comment apart and initialize members accordingly.
00084   FullCommentParts(const FullComment *C,
00085                    const CommandTraits &Traits);
00086 
00087   const BlockContentComment *Brief;
00088   const BlockContentComment *Headerfile;
00089   const ParagraphComment *FirstParagraph;
00090   SmallVector<const BlockCommandComment *, 4> Returns;
00091   SmallVector<const ParamCommandComment *, 8> Params;
00092   SmallVector<const TParamCommandComment *, 4> TParams;
00093   llvm::TinyPtrVector<const BlockCommandComment *> Exceptions;
00094   SmallVector<const BlockContentComment *, 8> MiscBlocks;
00095 };
00096 
00097 FullCommentParts::FullCommentParts(const FullComment *C,
00098                                    const CommandTraits &Traits) :
00099     Brief(nullptr), Headerfile(nullptr), FirstParagraph(nullptr) {
00100   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
00101        I != E; ++I) {
00102     const Comment *Child = *I;
00103     if (!Child)
00104       continue;
00105     switch (Child->getCommentKind()) {
00106     case Comment::NoCommentKind:
00107       continue;
00108 
00109     case Comment::ParagraphCommentKind: {
00110       const ParagraphComment *PC = cast<ParagraphComment>(Child);
00111       if (PC->isWhitespace())
00112         break;
00113       if (!FirstParagraph)
00114         FirstParagraph = PC;
00115 
00116       MiscBlocks.push_back(PC);
00117       break;
00118     }
00119 
00120     case Comment::BlockCommandCommentKind: {
00121       const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
00122       const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
00123       if (!Brief && Info->IsBriefCommand) {
00124         Brief = BCC;
00125         break;
00126       }
00127       if (!Headerfile && Info->IsHeaderfileCommand) {
00128         Headerfile = BCC;
00129         break;
00130       }
00131       if (Info->IsReturnsCommand) {
00132         Returns.push_back(BCC);
00133         break;
00134       }
00135       if (Info->IsThrowsCommand) {
00136         Exceptions.push_back(BCC);
00137         break;
00138       }
00139       MiscBlocks.push_back(BCC);
00140       break;
00141     }
00142 
00143     case Comment::ParamCommandCommentKind: {
00144       const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
00145       if (!PCC->hasParamName())
00146         break;
00147 
00148       if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
00149         break;
00150 
00151       Params.push_back(PCC);
00152       break;
00153     }
00154 
00155     case Comment::TParamCommandCommentKind: {
00156       const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
00157       if (!TPCC->hasParamName())
00158         break;
00159 
00160       if (!TPCC->hasNonWhitespaceParagraph())
00161         break;
00162 
00163       TParams.push_back(TPCC);
00164       break;
00165     }
00166 
00167     case Comment::VerbatimBlockCommentKind:
00168       MiscBlocks.push_back(cast<BlockCommandComment>(Child));
00169       break;
00170 
00171     case Comment::VerbatimLineCommentKind: {
00172       const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
00173       const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());
00174       if (!Info->IsDeclarationCommand)
00175         MiscBlocks.push_back(VLC);
00176       break;
00177     }
00178 
00179     case Comment::TextCommentKind:
00180     case Comment::InlineCommandCommentKind:
00181     case Comment::HTMLStartTagCommentKind:
00182     case Comment::HTMLEndTagCommentKind:
00183     case Comment::VerbatimBlockLineCommentKind:
00184     case Comment::FullCommentKind:
00185       llvm_unreachable("AST node of this kind can't be a child of "
00186                        "a FullComment");
00187     }
00188   }
00189 
00190   // Sort params in order they are declared in the function prototype.
00191   // Unresolved parameters are put at the end of the list in the same order
00192   // they were seen in the comment.
00193   std::stable_sort(Params.begin(), Params.end(),
00194                    ParamCommandCommentCompareIndex());
00195 
00196   std::stable_sort(TParams.begin(), TParams.end(),
00197                    TParamCommandCommentComparePosition());
00198 }
00199 
00200 void printHTMLStartTagComment(const HTMLStartTagComment *C,
00201                               llvm::raw_svector_ostream &Result) {
00202   Result << "<" << C->getTagName();
00203 
00204   if (C->getNumAttrs() != 0) {
00205     for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
00206       Result << " ";
00207       const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
00208       Result << Attr.Name;
00209       if (!Attr.Value.empty())
00210         Result << "=\"" << Attr.Value << "\"";
00211     }
00212   }
00213 
00214   if (!C->isSelfClosing())
00215     Result << ">";
00216   else
00217     Result << "/>";
00218 }
00219 
00220 class CommentASTToHTMLConverter :
00221     public ConstCommentVisitor<CommentASTToHTMLConverter> {
00222 public:
00223   /// \param Str accumulator for HTML.
00224   CommentASTToHTMLConverter(const FullComment *FC,
00225                             SmallVectorImpl<char> &Str,
00226                             const CommandTraits &Traits) :
00227       FC(FC), Result(Str), Traits(Traits)
00228   { }
00229 
00230   // Inline content.
00231   void visitTextComment(const TextComment *C);
00232   void visitInlineCommandComment(const InlineCommandComment *C);
00233   void visitHTMLStartTagComment(const HTMLStartTagComment *C);
00234   void visitHTMLEndTagComment(const HTMLEndTagComment *C);
00235 
00236   // Block content.
00237   void visitParagraphComment(const ParagraphComment *C);
00238   void visitBlockCommandComment(const BlockCommandComment *C);
00239   void visitParamCommandComment(const ParamCommandComment *C);
00240   void visitTParamCommandComment(const TParamCommandComment *C);
00241   void visitVerbatimBlockComment(const VerbatimBlockComment *C);
00242   void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
00243   void visitVerbatimLineComment(const VerbatimLineComment *C);
00244 
00245   void visitFullComment(const FullComment *C);
00246 
00247   // Helpers.
00248 
00249   /// Convert a paragraph that is not a block by itself (an argument to some
00250   /// command).
00251   void visitNonStandaloneParagraphComment(const ParagraphComment *C);
00252 
00253   void appendToResultWithHTMLEscaping(StringRef S);
00254 
00255 private:
00256   const FullComment *FC;
00257   /// Output stream for HTML.
00258   llvm::raw_svector_ostream Result;
00259 
00260   const CommandTraits &Traits;
00261 };
00262 } // end unnamed namespace
00263 
00264 void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
00265   appendToResultWithHTMLEscaping(C->getText());
00266 }
00267 
00268 void CommentASTToHTMLConverter::visitInlineCommandComment(
00269                                   const InlineCommandComment *C) {
00270   // Nothing to render if no arguments supplied.
00271   if (C->getNumArgs() == 0)
00272     return;
00273 
00274   // Nothing to render if argument is empty.
00275   StringRef Arg0 = C->getArgText(0);
00276   if (Arg0.empty())
00277     return;
00278 
00279   switch (C->getRenderKind()) {
00280   case InlineCommandComment::RenderNormal:
00281     for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
00282       appendToResultWithHTMLEscaping(C->getArgText(i));
00283       Result << " ";
00284     }
00285     return;
00286 
00287   case InlineCommandComment::RenderBold:
00288     assert(C->getNumArgs() == 1);
00289     Result << "<b>";
00290     appendToResultWithHTMLEscaping(Arg0);
00291     Result << "</b>";
00292     return;
00293   case InlineCommandComment::RenderMonospaced:
00294     assert(C->getNumArgs() == 1);
00295     Result << "<tt>";
00296     appendToResultWithHTMLEscaping(Arg0);
00297     Result<< "</tt>";
00298     return;
00299   case InlineCommandComment::RenderEmphasized:
00300     assert(C->getNumArgs() == 1);
00301     Result << "<em>";
00302     appendToResultWithHTMLEscaping(Arg0);
00303     Result << "</em>";
00304     return;
00305   }
00306 }
00307 
00308 void CommentASTToHTMLConverter::visitHTMLStartTagComment(
00309                                   const HTMLStartTagComment *C) {
00310   printHTMLStartTagComment(C, Result);
00311 }
00312 
00313 void CommentASTToHTMLConverter::visitHTMLEndTagComment(
00314                                   const HTMLEndTagComment *C) {
00315   Result << "</" << C->getTagName() << ">";
00316 }
00317 
00318 void CommentASTToHTMLConverter::visitParagraphComment(
00319                                   const ParagraphComment *C) {
00320   if (C->isWhitespace())
00321     return;
00322 
00323   Result << "<p>";
00324   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
00325        I != E; ++I) {
00326     visit(*I);
00327   }
00328   Result << "</p>";
00329 }
00330 
00331 void CommentASTToHTMLConverter::visitBlockCommandComment(
00332                                   const BlockCommandComment *C) {
00333   const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
00334   if (Info->IsBriefCommand) {
00335     Result << "<p class=\"para-brief\">";
00336     visitNonStandaloneParagraphComment(C->getParagraph());
00337     Result << "</p>";
00338     return;
00339   }
00340   if (Info->IsReturnsCommand) {
00341     Result << "<p class=\"para-returns\">"
00342               "<span class=\"word-returns\">Returns</span> ";
00343     visitNonStandaloneParagraphComment(C->getParagraph());
00344     Result << "</p>";
00345     return;
00346   }
00347   // We don't know anything about this command.  Just render the paragraph.
00348   visit(C->getParagraph());
00349 }
00350 
00351 void CommentASTToHTMLConverter::visitParamCommandComment(
00352                                   const ParamCommandComment *C) {
00353   if (C->isParamIndexValid()) {
00354     if (C->isVarArgParam()) {
00355       Result << "<dt class=\"param-name-index-vararg\">";
00356       appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
00357     } else {
00358       Result << "<dt class=\"param-name-index-"
00359              << C->getParamIndex()
00360              << "\">";
00361       appendToResultWithHTMLEscaping(C->getParamName(FC));
00362     }
00363   } else {
00364     Result << "<dt class=\"param-name-index-invalid\">";
00365     appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
00366   }
00367   Result << "</dt>";
00368 
00369   if (C->isParamIndexValid()) {
00370     if (C->isVarArgParam())
00371       Result << "<dd class=\"param-descr-index-vararg\">";
00372     else
00373       Result << "<dd class=\"param-descr-index-"
00374              << C->getParamIndex()
00375              << "\">";
00376   } else
00377     Result << "<dd class=\"param-descr-index-invalid\">";
00378 
00379   visitNonStandaloneParagraphComment(C->getParagraph());
00380   Result << "</dd>";
00381 }
00382 
00383 void CommentASTToHTMLConverter::visitTParamCommandComment(
00384                                   const TParamCommandComment *C) {
00385   if (C->isPositionValid()) {
00386     if (C->getDepth() == 1)
00387       Result << "<dt class=\"tparam-name-index-"
00388              << C->getIndex(0)
00389              << "\">";
00390     else
00391       Result << "<dt class=\"tparam-name-index-other\">";
00392     appendToResultWithHTMLEscaping(C->getParamName(FC));
00393   } else {
00394     Result << "<dt class=\"tparam-name-index-invalid\">";
00395     appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
00396   }
00397 
00398   Result << "</dt>";
00399 
00400   if (C->isPositionValid()) {
00401     if (C->getDepth() == 1)
00402       Result << "<dd class=\"tparam-descr-index-"
00403              << C->getIndex(0)
00404              << "\">";
00405     else
00406       Result << "<dd class=\"tparam-descr-index-other\">";
00407   } else
00408     Result << "<dd class=\"tparam-descr-index-invalid\">";
00409 
00410   visitNonStandaloneParagraphComment(C->getParagraph());
00411   Result << "</dd>";
00412 }
00413 
00414 void CommentASTToHTMLConverter::visitVerbatimBlockComment(
00415                                   const VerbatimBlockComment *C) {
00416   unsigned NumLines = C->getNumLines();
00417   if (NumLines == 0)
00418     return;
00419 
00420   Result << "<pre>";
00421   for (unsigned i = 0; i != NumLines; ++i) {
00422     appendToResultWithHTMLEscaping(C->getText(i));
00423     if (i + 1 != NumLines)
00424       Result << '\n';
00425   }
00426   Result << "</pre>";
00427 }
00428 
00429 void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
00430                                   const VerbatimBlockLineComment *C) {
00431   llvm_unreachable("should not see this AST node");
00432 }
00433 
00434 void CommentASTToHTMLConverter::visitVerbatimLineComment(
00435                                   const VerbatimLineComment *C) {
00436   Result << "<pre>";
00437   appendToResultWithHTMLEscaping(C->getText());
00438   Result << "</pre>";
00439 }
00440 
00441 void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
00442   FullCommentParts Parts(C, Traits);
00443 
00444   bool FirstParagraphIsBrief = false;
00445   if (Parts.Headerfile)
00446     visit(Parts.Headerfile);
00447   if (Parts.Brief)
00448     visit(Parts.Brief);
00449   else if (Parts.FirstParagraph) {
00450     Result << "<p class=\"para-brief\">";
00451     visitNonStandaloneParagraphComment(Parts.FirstParagraph);
00452     Result << "</p>";
00453     FirstParagraphIsBrief = true;
00454   }
00455 
00456   for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
00457     const Comment *C = Parts.MiscBlocks[i];
00458     if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
00459       continue;
00460     visit(C);
00461   }
00462 
00463   if (Parts.TParams.size() != 0) {
00464     Result << "<dl>";
00465     for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
00466       visit(Parts.TParams[i]);
00467     Result << "</dl>";
00468   }
00469 
00470   if (Parts.Params.size() != 0) {
00471     Result << "<dl>";
00472     for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
00473       visit(Parts.Params[i]);
00474     Result << "</dl>";
00475   }
00476 
00477   if (Parts.Returns.size() != 0) {
00478     Result << "<div class=\"result-discussion\">";
00479     for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
00480       visit(Parts.Returns[i]);
00481     Result << "</div>";
00482   }
00483 
00484   Result.flush();
00485 }
00486 
00487 void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
00488                                   const ParagraphComment *C) {
00489   if (!C)
00490     return;
00491 
00492   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
00493        I != E; ++I) {
00494     visit(*I);
00495   }
00496 }
00497 
00498 void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
00499   for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
00500     const char C = *I;
00501     switch (C) {
00502     case '&':
00503       Result << "&amp;";
00504       break;
00505     case '<':
00506       Result << "&lt;";
00507       break;
00508     case '>':
00509       Result << "&gt;";
00510       break;
00511     case '"':
00512       Result << "&quot;";
00513       break;
00514     case '\'':
00515       Result << "&#39;";
00516       break;
00517     case '/':
00518       Result << "&#47;";
00519       break;
00520     default:
00521       Result << C;
00522       break;
00523     }
00524   }
00525 }
00526 
00527 namespace {
00528 class CommentASTToXMLConverter :
00529     public ConstCommentVisitor<CommentASTToXMLConverter> {
00530 public:
00531   /// \param Str accumulator for XML.
00532   CommentASTToXMLConverter(const FullComment *FC,
00533                            SmallVectorImpl<char> &Str,
00534                            const CommandTraits &Traits,
00535                            const SourceManager &SM,
00536                            SimpleFormatContext &SFC,
00537                            unsigned FUID) :
00538       FC(FC), Result(Str), Traits(Traits), SM(SM),
00539       FormatRewriterContext(SFC),
00540       FormatInMemoryUniqueId(FUID) { }
00541 
00542   // Inline content.
00543   void visitTextComment(const TextComment *C);
00544   void visitInlineCommandComment(const InlineCommandComment *C);
00545   void visitHTMLStartTagComment(const HTMLStartTagComment *C);
00546   void visitHTMLEndTagComment(const HTMLEndTagComment *C);
00547 
00548   // Block content.
00549   void visitParagraphComment(const ParagraphComment *C);
00550 
00551   void appendParagraphCommentWithKind(const ParagraphComment *C,
00552                                       StringRef Kind);
00553 
00554   void visitBlockCommandComment(const BlockCommandComment *C);
00555   void visitParamCommandComment(const ParamCommandComment *C);
00556   void visitTParamCommandComment(const TParamCommandComment *C);
00557   void visitVerbatimBlockComment(const VerbatimBlockComment *C);
00558   void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
00559   void visitVerbatimLineComment(const VerbatimLineComment *C);
00560 
00561   void visitFullComment(const FullComment *C);
00562 
00563   // Helpers.
00564   void appendToResultWithXMLEscaping(StringRef S);
00565   void appendToResultWithCDATAEscaping(StringRef S);
00566 
00567   void formatTextOfDeclaration(const DeclInfo *DI,
00568                                SmallString<128> &Declaration);
00569 
00570 private:
00571   const FullComment *FC;
00572 
00573   /// Output stream for XML.
00574   llvm::raw_svector_ostream Result;
00575 
00576   const CommandTraits &Traits;
00577   const SourceManager &SM;
00578   SimpleFormatContext &FormatRewriterContext;
00579   unsigned FormatInMemoryUniqueId;
00580 };
00581 
00582 void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
00583                                 SmallVectorImpl<char> &Str) {
00584   ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
00585   const LangOptions &LangOpts = Context.getLangOpts();
00586   llvm::raw_svector_ostream OS(Str);
00587   PrintingPolicy PPolicy(LangOpts);
00588   PPolicy.PolishForDeclaration = true;
00589   PPolicy.TerseOutput = true;
00590   ThisDecl->CurrentDecl->print(OS, PPolicy,
00591                                /*Indentation*/0, /*PrintInstantiation*/false);
00592 }
00593 
00594 void CommentASTToXMLConverter::formatTextOfDeclaration(
00595     const DeclInfo *DI, SmallString<128> &Declaration) {
00596   // FIXME. formatting API expects null terminated input string.
00597   // There might be more efficient way of doing this.
00598   std::string StringDecl = Declaration.str();
00599 
00600   // Formatter specific code.
00601   // Form a unique in memory buffer name.
00602   SmallString<128> filename;
00603   filename += "xmldecl";
00604   filename += llvm::utostr(FormatInMemoryUniqueId);
00605   filename += ".xd";
00606   FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl);
00607   SourceLocation Start = FormatRewriterContext.Sources.getLocForStartOfFile(ID)
00608       .getLocWithOffset(0);
00609   unsigned Length = Declaration.size();
00610 
00611   tooling::Replacements Replace = reformat(
00612       format::getLLVMStyle(), FormatRewriterContext.Sources, ID,
00613       CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length)));
00614   applyAllReplacements(Replace, FormatRewriterContext.Rewrite);
00615   Declaration = FormatRewriterContext.getRewrittenText(ID);
00616 }
00617 
00618 } // end unnamed namespace
00619 
00620 void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
00621   appendToResultWithXMLEscaping(C->getText());
00622 }
00623 
00624 void CommentASTToXMLConverter::visitInlineCommandComment(
00625     const InlineCommandComment *C) {
00626   // Nothing to render if no arguments supplied.
00627   if (C->getNumArgs() == 0)
00628     return;
00629 
00630   // Nothing to render if argument is empty.
00631   StringRef Arg0 = C->getArgText(0);
00632   if (Arg0.empty())
00633     return;
00634 
00635   switch (C->getRenderKind()) {
00636   case InlineCommandComment::RenderNormal:
00637     for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
00638       appendToResultWithXMLEscaping(C->getArgText(i));
00639       Result << " ";
00640     }
00641     return;
00642   case InlineCommandComment::RenderBold:
00643     assert(C->getNumArgs() == 1);
00644     Result << "<bold>";
00645     appendToResultWithXMLEscaping(Arg0);
00646     Result << "</bold>";
00647     return;
00648   case InlineCommandComment::RenderMonospaced:
00649     assert(C->getNumArgs() == 1);
00650     Result << "<monospaced>";
00651     appendToResultWithXMLEscaping(Arg0);
00652     Result << "</monospaced>";
00653     return;
00654   case InlineCommandComment::RenderEmphasized:
00655     assert(C->getNumArgs() == 1);
00656     Result << "<emphasized>";
00657     appendToResultWithXMLEscaping(Arg0);
00658     Result << "</emphasized>";
00659     return;
00660   }
00661 }
00662 
00663 void CommentASTToXMLConverter::visitHTMLStartTagComment(
00664     const HTMLStartTagComment *C) {
00665   Result << "<rawHTML";
00666   if (C->isMalformed())
00667     Result << " isMalformed=\"1\"";
00668   Result << ">";
00669   {
00670     SmallString<32> Tag;
00671     {
00672       llvm::raw_svector_ostream TagOS(Tag);
00673       printHTMLStartTagComment(C, TagOS);
00674     }
00675     appendToResultWithCDATAEscaping(Tag);
00676   }
00677   Result << "</rawHTML>";
00678 }
00679 
00680 void
00681 CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
00682   Result << "<rawHTML";
00683   if (C->isMalformed())
00684     Result << " isMalformed=\"1\"";
00685   Result << ">&lt;/" << C->getTagName() << "&gt;</rawHTML>";
00686 }
00687 
00688 void
00689 CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
00690   appendParagraphCommentWithKind(C, StringRef());
00691 }
00692 
00693 void CommentASTToXMLConverter::appendParagraphCommentWithKind(
00694                                   const ParagraphComment *C,
00695                                   StringRef ParagraphKind) {
00696   if (C->isWhitespace())
00697     return;
00698 
00699   if (ParagraphKind.empty())
00700     Result << "<Para>";
00701   else
00702     Result << "<Para kind=\"" << ParagraphKind << "\">";
00703 
00704   for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
00705        I != E; ++I) {
00706     visit(*I);
00707   }
00708   Result << "</Para>";
00709 }
00710 
00711 void CommentASTToXMLConverter::visitBlockCommandComment(
00712     const BlockCommandComment *C) {
00713   StringRef ParagraphKind;
00714 
00715   switch (C->getCommandID()) {
00716   case CommandTraits::KCI_attention:
00717   case CommandTraits::KCI_author:
00718   case CommandTraits::KCI_authors:
00719   case CommandTraits::KCI_bug:
00720   case CommandTraits::KCI_copyright:
00721   case CommandTraits::KCI_date:
00722   case CommandTraits::KCI_invariant:
00723   case CommandTraits::KCI_note:
00724   case CommandTraits::KCI_post:
00725   case CommandTraits::KCI_pre:
00726   case CommandTraits::KCI_remark:
00727   case CommandTraits::KCI_remarks:
00728   case CommandTraits::KCI_sa:
00729   case CommandTraits::KCI_see:
00730   case CommandTraits::KCI_since:
00731   case CommandTraits::KCI_todo:
00732   case CommandTraits::KCI_version:
00733   case CommandTraits::KCI_warning:
00734     ParagraphKind = C->getCommandName(Traits);
00735   default:
00736     break;
00737   }
00738 
00739   appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind);
00740 }
00741 
00742 void CommentASTToXMLConverter::visitParamCommandComment(
00743     const ParamCommandComment *C) {
00744   Result << "<Parameter><Name>";
00745   appendToResultWithXMLEscaping(C->isParamIndexValid()
00746                                     ? C->getParamName(FC)
00747                                     : C->getParamNameAsWritten());
00748   Result << "</Name>";
00749 
00750   if (C->isParamIndexValid()) {
00751     if (C->isVarArgParam())
00752       Result << "<IsVarArg />";
00753     else
00754       Result << "<Index>" << C->getParamIndex() << "</Index>";
00755   }
00756 
00757   Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
00758   switch (C->getDirection()) {
00759   case ParamCommandComment::In:
00760     Result << "in";
00761     break;
00762   case ParamCommandComment::Out:
00763     Result << "out";
00764     break;
00765   case ParamCommandComment::InOut:
00766     Result << "in,out";
00767     break;
00768   }
00769   Result << "</Direction><Discussion>";
00770   visit(C->getParagraph());
00771   Result << "</Discussion></Parameter>";
00772 }
00773 
00774 void CommentASTToXMLConverter::visitTParamCommandComment(
00775                                   const TParamCommandComment *C) {
00776   Result << "<Parameter><Name>";
00777   appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
00778                                 : C->getParamNameAsWritten());
00779   Result << "</Name>";
00780 
00781   if (C->isPositionValid() && C->getDepth() == 1) {
00782     Result << "<Index>" << C->getIndex(0) << "</Index>";
00783   }
00784 
00785   Result << "<Discussion>";
00786   visit(C->getParagraph());
00787   Result << "</Discussion></Parameter>";
00788 }
00789 
00790 void CommentASTToXMLConverter::visitVerbatimBlockComment(
00791                                   const VerbatimBlockComment *C) {
00792   unsigned NumLines = C->getNumLines();
00793   if (NumLines == 0)
00794     return;
00795 
00796   switch (C->getCommandID()) {
00797   case CommandTraits::KCI_code:
00798     Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">";
00799     break;
00800   default:
00801     Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
00802     break;
00803   }
00804   for (unsigned i = 0; i != NumLines; ++i) {
00805     appendToResultWithXMLEscaping(C->getText(i));
00806     if (i + 1 != NumLines)
00807       Result << '\n';
00808   }
00809   Result << "</Verbatim>";
00810 }
00811 
00812 void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
00813                                   const VerbatimBlockLineComment *C) {
00814   llvm_unreachable("should not see this AST node");
00815 }
00816 
00817 void CommentASTToXMLConverter::visitVerbatimLineComment(
00818                                   const VerbatimLineComment *C) {
00819   Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
00820   appendToResultWithXMLEscaping(C->getText());
00821   Result << "</Verbatim>";
00822 }
00823 
00824 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
00825   FullCommentParts Parts(C, Traits);
00826 
00827   const DeclInfo *DI = C->getDeclInfo();
00828   StringRef RootEndTag;
00829   if (DI) {
00830     switch (DI->getKind()) {
00831     case DeclInfo::OtherKind:
00832       RootEndTag = "</Other>";
00833       Result << "<Other";
00834       break;
00835     case DeclInfo::FunctionKind:
00836       RootEndTag = "</Function>";
00837       Result << "<Function";
00838       switch (DI->TemplateKind) {
00839       case DeclInfo::NotTemplate:
00840         break;
00841       case DeclInfo::Template:
00842         Result << " templateKind=\"template\"";
00843         break;
00844       case DeclInfo::TemplateSpecialization:
00845         Result << " templateKind=\"specialization\"";
00846         break;
00847       case DeclInfo::TemplatePartialSpecialization:
00848         llvm_unreachable("partial specializations of functions "
00849                          "are not allowed in C++");
00850       }
00851       if (DI->IsInstanceMethod)
00852         Result << " isInstanceMethod=\"1\"";
00853       if (DI->IsClassMethod)
00854         Result << " isClassMethod=\"1\"";
00855       break;
00856     case DeclInfo::ClassKind:
00857       RootEndTag = "</Class>";
00858       Result << "<Class";
00859       switch (DI->TemplateKind) {
00860       case DeclInfo::NotTemplate:
00861         break;
00862       case DeclInfo::Template:
00863         Result << " templateKind=\"template\"";
00864         break;
00865       case DeclInfo::TemplateSpecialization:
00866         Result << " templateKind=\"specialization\"";
00867         break;
00868       case DeclInfo::TemplatePartialSpecialization:
00869         Result << " templateKind=\"partialSpecialization\"";
00870         break;
00871       }
00872       break;
00873     case DeclInfo::VariableKind:
00874       RootEndTag = "</Variable>";
00875       Result << "<Variable";
00876       break;
00877     case DeclInfo::NamespaceKind:
00878       RootEndTag = "</Namespace>";
00879       Result << "<Namespace";
00880       break;
00881     case DeclInfo::TypedefKind:
00882       RootEndTag = "</Typedef>";
00883       Result << "<Typedef";
00884       break;
00885     case DeclInfo::EnumKind:
00886       RootEndTag = "</Enum>";
00887       Result << "<Enum";
00888       break;
00889     }
00890 
00891     {
00892       // Print line and column number.
00893       SourceLocation Loc = DI->CurrentDecl->getLocation();
00894       std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
00895       FileID FID = LocInfo.first;
00896       unsigned FileOffset = LocInfo.second;
00897 
00898       if (!FID.isInvalid()) {
00899         if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
00900           Result << " file=\"";
00901           appendToResultWithXMLEscaping(FE->getName());
00902           Result << "\"";
00903         }
00904         Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
00905                << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
00906                << "\"";
00907       }
00908     }
00909 
00910     // Finish the root tag.
00911     Result << ">";
00912 
00913     bool FoundName = false;
00914     if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
00915       if (DeclarationName DeclName = ND->getDeclName()) {
00916         Result << "<Name>";
00917         std::string Name = DeclName.getAsString();
00918         appendToResultWithXMLEscaping(Name);
00919         FoundName = true;
00920         Result << "</Name>";
00921       }
00922     }
00923     if (!FoundName)
00924       Result << "<Name>&lt;anonymous&gt;</Name>";
00925 
00926     {
00927       // Print USR.
00928       SmallString<128> USR;
00929       generateUSRForDecl(DI->CommentDecl, USR);
00930       if (!USR.empty()) {
00931         Result << "<USR>";
00932         appendToResultWithXMLEscaping(USR);
00933         Result << "</USR>";
00934       }
00935     }
00936   } else {
00937     // No DeclInfo -- just emit some root tag and name tag.
00938     RootEndTag = "</Other>";
00939     Result << "<Other><Name>unknown</Name>";
00940   }
00941 
00942   if (Parts.Headerfile) {
00943     Result << "<Headerfile>";
00944     visit(Parts.Headerfile);
00945     Result << "</Headerfile>";
00946   }
00947 
00948   {
00949     // Pretty-print the declaration.
00950     Result << "<Declaration>";
00951     SmallString<128> Declaration;
00952     getSourceTextOfDeclaration(DI, Declaration);
00953     formatTextOfDeclaration(DI, Declaration);
00954     appendToResultWithXMLEscaping(Declaration);
00955     Result << "</Declaration>";
00956   }
00957 
00958   bool FirstParagraphIsBrief = false;
00959   if (Parts.Brief) {
00960     Result << "<Abstract>";
00961     visit(Parts.Brief);
00962     Result << "</Abstract>";
00963   } else if (Parts.FirstParagraph) {
00964     Result << "<Abstract>";
00965     visit(Parts.FirstParagraph);
00966     Result << "</Abstract>";
00967     FirstParagraphIsBrief = true;
00968   }
00969 
00970   if (Parts.TParams.size() != 0) {
00971     Result << "<TemplateParameters>";
00972     for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
00973       visit(Parts.TParams[i]);
00974     Result << "</TemplateParameters>";
00975   }
00976 
00977   if (Parts.Params.size() != 0) {
00978     Result << "<Parameters>";
00979     for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
00980       visit(Parts.Params[i]);
00981     Result << "</Parameters>";
00982   }
00983 
00984   if (Parts.Exceptions.size() != 0) {
00985     Result << "<Exceptions>";
00986     for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i)
00987       visit(Parts.Exceptions[i]);
00988     Result << "</Exceptions>";
00989   }
00990 
00991   if (Parts.Returns.size() != 0) {
00992     Result << "<ResultDiscussion>";
00993     for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i)
00994       visit(Parts.Returns[i]);
00995     Result << "</ResultDiscussion>";
00996   }
00997 
00998   if (DI->CommentDecl->hasAttrs()) {
00999     const AttrVec &Attrs = DI->CommentDecl->getAttrs();
01000     for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
01001       const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
01002       if (!AA) {
01003         if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
01004           if (DA->getMessage().empty())
01005             Result << "<Deprecated/>";
01006           else {
01007             Result << "<Deprecated>";
01008             appendToResultWithXMLEscaping(DA->getMessage());
01009             Result << "</Deprecated>";
01010           }
01011         }
01012         else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
01013           if (UA->getMessage().empty())
01014             Result << "<Unavailable/>";
01015           else {
01016             Result << "<Unavailable>";
01017             appendToResultWithXMLEscaping(UA->getMessage());
01018             Result << "</Unavailable>";
01019           }
01020         }
01021         continue;
01022       }
01023 
01024       // 'availability' attribute.
01025       Result << "<Availability";
01026       StringRef Distribution;
01027       if (AA->getPlatform()) {
01028         Distribution = AvailabilityAttr::getPrettyPlatformName(
01029                                         AA->getPlatform()->getName());
01030         if (Distribution.empty())
01031           Distribution = AA->getPlatform()->getName();
01032       }
01033       Result << " distribution=\"" << Distribution << "\">";
01034       VersionTuple IntroducedInVersion = AA->getIntroduced();
01035       if (!IntroducedInVersion.empty()) {
01036         Result << "<IntroducedInVersion>"
01037                << IntroducedInVersion.getAsString()
01038                << "</IntroducedInVersion>";
01039       }
01040       VersionTuple DeprecatedInVersion = AA->getDeprecated();
01041       if (!DeprecatedInVersion.empty()) {
01042         Result << "<DeprecatedInVersion>"
01043                << DeprecatedInVersion.getAsString()
01044                << "</DeprecatedInVersion>";
01045       }
01046       VersionTuple RemovedAfterVersion = AA->getObsoleted();
01047       if (!RemovedAfterVersion.empty()) {
01048         Result << "<RemovedAfterVersion>"
01049                << RemovedAfterVersion.getAsString()
01050                << "</RemovedAfterVersion>";
01051       }
01052       StringRef DeprecationSummary = AA->getMessage();
01053       if (!DeprecationSummary.empty()) {
01054         Result << "<DeprecationSummary>";
01055         appendToResultWithXMLEscaping(DeprecationSummary);
01056         Result << "</DeprecationSummary>";
01057       }
01058       if (AA->getUnavailable())
01059         Result << "<Unavailable/>";
01060       Result << "</Availability>";
01061     }
01062   }
01063 
01064   {
01065     bool StartTagEmitted = false;
01066     for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
01067       const Comment *C = Parts.MiscBlocks[i];
01068       if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
01069         continue;
01070       if (!StartTagEmitted) {
01071         Result << "<Discussion>";
01072         StartTagEmitted = true;
01073       }
01074       visit(C);
01075     }
01076     if (StartTagEmitted)
01077       Result << "</Discussion>";
01078   }
01079 
01080   Result << RootEndTag;
01081 
01082   Result.flush();
01083 }
01084 
01085 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
01086   for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
01087     const char C = *I;
01088     switch (C) {
01089     case '&':
01090       Result << "&amp;";
01091       break;
01092     case '<':
01093       Result << "&lt;";
01094       break;
01095     case '>':
01096       Result << "&gt;";
01097       break;
01098     case '"':
01099       Result << "&quot;";
01100       break;
01101     case '\'':
01102       Result << "&apos;";
01103       break;
01104     default:
01105       Result << C;
01106       break;
01107     }
01108   }
01109 }
01110 
01111 void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) {
01112   if (S.empty())
01113     return;
01114 
01115   Result << "<![CDATA[";
01116   while (!S.empty()) {
01117     size_t Pos = S.find("]]>");
01118     if (Pos == 0) {
01119       Result << "]]]]><![CDATA[>";
01120       S = S.drop_front(3);
01121       continue;
01122     }
01123     if (Pos == StringRef::npos)
01124       Pos = S.size();
01125 
01126     Result << S.substr(0, Pos);
01127 
01128     S = S.drop_front(Pos);
01129   }
01130   Result << "]]>";
01131 }
01132 
01133 CommentToXMLConverter::CommentToXMLConverter() : FormatInMemoryUniqueId(0) {}
01134 CommentToXMLConverter::~CommentToXMLConverter() {}
01135 
01136 void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC,
01137                                                  SmallVectorImpl<char> &HTML,
01138                                                  const ASTContext &Context) {
01139   CommentASTToHTMLConverter Converter(FC, HTML,
01140                                       Context.getCommentCommandTraits());
01141   Converter.visit(FC);
01142 }
01143 
01144 void CommentToXMLConverter::convertHTMLTagNodeToText(
01145     const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text,
01146     const ASTContext &Context) {
01147   CommentASTToHTMLConverter Converter(nullptr, Text,
01148                                       Context.getCommentCommandTraits());
01149   Converter.visit(HTC);
01150 }
01151 
01152 void CommentToXMLConverter::convertCommentToXML(const FullComment *FC,
01153                                                 SmallVectorImpl<char> &XML,
01154                                                 const ASTContext &Context) {
01155   if (!FormatContext || (FormatInMemoryUniqueId % 1000) == 0) {
01156     // Create a new format context, or re-create it after some number of
01157     // iterations, so the buffers don't grow too large.
01158     FormatContext.reset(new SimpleFormatContext(Context.getLangOpts()));
01159   }
01160 
01161   CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(),
01162                                      Context.getSourceManager(), *FormatContext,
01163                                      FormatInMemoryUniqueId++);
01164   Converter.visit(FC);
01165 }
01166