clang API Documentation
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 << "&"; 00504 break; 00505 case '<': 00506 Result << "<"; 00507 break; 00508 case '>': 00509 Result << ">"; 00510 break; 00511 case '"': 00512 Result << """; 00513 break; 00514 case '\'': 00515 Result << "'"; 00516 break; 00517 case '/': 00518 Result << "/"; 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 << "></" << C->getTagName() << "></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><anonymous></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 << "&"; 01091 break; 01092 case '<': 01093 Result << "<"; 01094 break; 01095 case '>': 01096 Result << ">"; 01097 break; 01098 case '"': 01099 Result << """; 01100 break; 01101 case '\'': 01102 Result << "'"; 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