CommentToXML.cpp revision 259701
164562Sgshapiro//===--- CommentToXML.cpp - Convert comments to XML representation --------===// 2261363Sgshapiro// 364562Sgshapiro// The LLVM Compiler Infrastructure 464562Sgshapiro// 564562Sgshapiro// This file is distributed under the University of Illinois Open Source 664562Sgshapiro// License. See LICENSE.TXT for details. 764562Sgshapiro// 864562Sgshapiro//===----------------------------------------------------------------------===// 9266692Sgshapiro 1064562Sgshapiro#include "clang/Index/CommentToXML.h" 1164562Sgshapiro#include "SimpleFormatContext.h" 1264562Sgshapiro#include "clang/AST/Attr.h" 1364562Sgshapiro#include "clang/AST/ASTContext.h" 1464562Sgshapiro#include "clang/AST/Comment.h" 1564562Sgshapiro#include "clang/AST/CommentVisitor.h" 1664562Sgshapiro#include "clang/Format/Format.h" 1764562Sgshapiro#include "clang/Index/USRGeneration.h" 1864562Sgshapiro#include "clang/Lex/Lexer.h" 1964562Sgshapiro#include "llvm/ADT/StringExtras.h" 2064562Sgshapiro#include "llvm/ADT/TinyPtrVector.h" 2164562Sgshapiro#include "llvm/Support/raw_ostream.h" 2264562Sgshapiro 2364562Sgshapirousing namespace clang; 2464562Sgshapirousing namespace clang::comments; 2564562Sgshapirousing namespace clang::index; 2664562Sgshapiro 2764562Sgshapironamespace { 2864562Sgshapiro 2964562Sgshapiro/// This comparison will sort parameters with valid index by index, then vararg 3064562Sgshapiro/// parameters, and invalid (unresolved) parameters last. 3164562Sgshapiroclass ParamCommandCommentCompareIndex { 3264562Sgshapiropublic: 3390792Sgshapiro bool operator()(const ParamCommandComment *LHS, 34 const ParamCommandComment *RHS) const { 35 unsigned LHSIndex = UINT_MAX; 36 unsigned RHSIndex = UINT_MAX; 37 38 if (LHS->isParamIndexValid()) { 39 if (LHS->isVarArgParam()) 40 LHSIndex = UINT_MAX - 1; 41 else 42 LHSIndex = LHS->getParamIndex(); 43 } 44 if (RHS->isParamIndexValid()) { 45 if (RHS->isVarArgParam()) 46 RHSIndex = UINT_MAX - 1; 47 else 48 RHSIndex = RHS->getParamIndex(); 49 } 50 return LHSIndex < RHSIndex; 51 } 52}; 53 54/// This comparison will sort template parameters in the following order: 55/// \li real template parameters (depth = 1) in index order; 56/// \li all other names (depth > 1); 57/// \li unresolved names. 58class TParamCommandCommentComparePosition { 59public: 60 bool operator()(const TParamCommandComment *LHS, 61 const TParamCommandComment *RHS) const { 62 // Sort unresolved names last. 63 if (!LHS->isPositionValid()) 64 return false; 65 if (!RHS->isPositionValid()) 66 return true; 67 68 if (LHS->getDepth() > 1) 69 return false; 70 if (RHS->getDepth() > 1) 71 return true; 72 73 // Sort template parameters in index order. 74 if (LHS->getDepth() == 1 && RHS->getDepth() == 1) 75 return LHS->getIndex(0) < RHS->getIndex(0); 76 77 // Leave all other names in source order. 78 return true; 79 } 80}; 81 82/// Separate parts of a FullComment. 83struct FullCommentParts { 84 /// Take a full comment apart and initialize members accordingly. 85 FullCommentParts(const FullComment *C, 86 const CommandTraits &Traits); 87 88 const BlockContentComment *Brief; 89 const BlockContentComment *Headerfile; 90 const ParagraphComment *FirstParagraph; 91 SmallVector<const BlockCommandComment *, 4> Returns; 92 SmallVector<const ParamCommandComment *, 8> Params; 93 SmallVector<const TParamCommandComment *, 4> TParams; 94 llvm::TinyPtrVector<const BlockCommandComment *> Exceptions; 95 SmallVector<const BlockContentComment *, 8> MiscBlocks; 96}; 97 98FullCommentParts::FullCommentParts(const FullComment *C, 99 const CommandTraits &Traits) : 100 Brief(NULL), Headerfile(NULL), FirstParagraph(NULL) { 101 for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); 102 I != E; ++I) { 103 const Comment *Child = *I; 104 if (!Child) 105 continue; 106 switch (Child->getCommentKind()) { 107 case Comment::NoCommentKind: 108 continue; 109 110 case Comment::ParagraphCommentKind: { 111 const ParagraphComment *PC = cast<ParagraphComment>(Child); 112 if (PC->isWhitespace()) 113 break; 114 if (!FirstParagraph) 115 FirstParagraph = PC; 116 117 MiscBlocks.push_back(PC); 118 break; 119 } 120 121 case Comment::BlockCommandCommentKind: { 122 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child); 123 const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID()); 124 if (!Brief && Info->IsBriefCommand) { 125 Brief = BCC; 126 break; 127 } 128 if (!Headerfile && Info->IsHeaderfileCommand) { 129 Headerfile = BCC; 130 break; 131 } 132 if (Info->IsReturnsCommand) { 133 Returns.push_back(BCC); 134 break; 135 } 136 if (Info->IsThrowsCommand) { 137 Exceptions.push_back(BCC); 138 break; 139 } 140 MiscBlocks.push_back(BCC); 141 break; 142 } 143 144 case Comment::ParamCommandCommentKind: { 145 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child); 146 if (!PCC->hasParamName()) 147 break; 148 149 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph()) 150 break; 151 152 Params.push_back(PCC); 153 break; 154 } 155 156 case Comment::TParamCommandCommentKind: { 157 const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child); 158 if (!TPCC->hasParamName()) 159 break; 160 161 if (!TPCC->hasNonWhitespaceParagraph()) 162 break; 163 164 TParams.push_back(TPCC); 165 break; 166 } 167 168 case Comment::VerbatimBlockCommentKind: 169 MiscBlocks.push_back(cast<BlockCommandComment>(Child)); 170 break; 171 172 case Comment::VerbatimLineCommentKind: { 173 const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child); 174 const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID()); 175 if (!Info->IsDeclarationCommand) 176 MiscBlocks.push_back(VLC); 177 break; 178 } 179 180 case Comment::TextCommentKind: 181 case Comment::InlineCommandCommentKind: 182 case Comment::HTMLStartTagCommentKind: 183 case Comment::HTMLEndTagCommentKind: 184 case Comment::VerbatimBlockLineCommentKind: 185 case Comment::FullCommentKind: 186 llvm_unreachable("AST node of this kind can't be a child of " 187 "a FullComment"); 188 } 189 } 190 191 // Sort params in order they are declared in the function prototype. 192 // Unresolved parameters are put at the end of the list in the same order 193 // they were seen in the comment. 194 std::stable_sort(Params.begin(), Params.end(), 195 ParamCommandCommentCompareIndex()); 196 197 std::stable_sort(TParams.begin(), TParams.end(), 198 TParamCommandCommentComparePosition()); 199} 200 201void printHTMLStartTagComment(const HTMLStartTagComment *C, 202 llvm::raw_svector_ostream &Result) { 203 Result << "<" << C->getTagName(); 204 205 if (C->getNumAttrs() != 0) { 206 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) { 207 Result << " "; 208 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i); 209 Result << Attr.Name; 210 if (!Attr.Value.empty()) 211 Result << "=\"" << Attr.Value << "\""; 212 } 213 } 214 215 if (!C->isSelfClosing()) 216 Result << ">"; 217 else 218 Result << "/>"; 219} 220 221class CommentASTToHTMLConverter : 222 public ConstCommentVisitor<CommentASTToHTMLConverter> { 223public: 224 /// \param Str accumulator for HTML. 225 CommentASTToHTMLConverter(const FullComment *FC, 226 SmallVectorImpl<char> &Str, 227 const CommandTraits &Traits) : 228 FC(FC), Result(Str), Traits(Traits) 229 { } 230 231 // Inline content. 232 void visitTextComment(const TextComment *C); 233 void visitInlineCommandComment(const InlineCommandComment *C); 234 void visitHTMLStartTagComment(const HTMLStartTagComment *C); 235 void visitHTMLEndTagComment(const HTMLEndTagComment *C); 236 237 // Block content. 238 void visitParagraphComment(const ParagraphComment *C); 239 void visitBlockCommandComment(const BlockCommandComment *C); 240 void visitParamCommandComment(const ParamCommandComment *C); 241 void visitTParamCommandComment(const TParamCommandComment *C); 242 void visitVerbatimBlockComment(const VerbatimBlockComment *C); 243 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); 244 void visitVerbatimLineComment(const VerbatimLineComment *C); 245 246 void visitFullComment(const FullComment *C); 247 248 // Helpers. 249 250 /// Convert a paragraph that is not a block by itself (an argument to some 251 /// command). 252 void visitNonStandaloneParagraphComment(const ParagraphComment *C); 253 254 void appendToResultWithHTMLEscaping(StringRef S); 255 256private: 257 const FullComment *FC; 258 /// Output stream for HTML. 259 llvm::raw_svector_ostream Result; 260 261 const CommandTraits &Traits; 262}; 263} // end unnamed namespace 264 265void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) { 266 appendToResultWithHTMLEscaping(C->getText()); 267} 268 269void CommentASTToHTMLConverter::visitInlineCommandComment( 270 const InlineCommandComment *C) { 271 // Nothing to render if no arguments supplied. 272 if (C->getNumArgs() == 0) 273 return; 274 275 // Nothing to render if argument is empty. 276 StringRef Arg0 = C->getArgText(0); 277 if (Arg0.empty()) 278 return; 279 280 switch (C->getRenderKind()) { 281 case InlineCommandComment::RenderNormal: 282 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) { 283 appendToResultWithHTMLEscaping(C->getArgText(i)); 284 Result << " "; 285 } 286 return; 287 288 case InlineCommandComment::RenderBold: 289 assert(C->getNumArgs() == 1); 290 Result << "<b>"; 291 appendToResultWithHTMLEscaping(Arg0); 292 Result << "</b>"; 293 return; 294 case InlineCommandComment::RenderMonospaced: 295 assert(C->getNumArgs() == 1); 296 Result << "<tt>"; 297 appendToResultWithHTMLEscaping(Arg0); 298 Result<< "</tt>"; 299 return; 300 case InlineCommandComment::RenderEmphasized: 301 assert(C->getNumArgs() == 1); 302 Result << "<em>"; 303 appendToResultWithHTMLEscaping(Arg0); 304 Result << "</em>"; 305 return; 306 } 307} 308 309void CommentASTToHTMLConverter::visitHTMLStartTagComment( 310 const HTMLStartTagComment *C) { 311 printHTMLStartTagComment(C, Result); 312} 313 314void CommentASTToHTMLConverter::visitHTMLEndTagComment( 315 const HTMLEndTagComment *C) { 316 Result << "</" << C->getTagName() << ">"; 317} 318 319void CommentASTToHTMLConverter::visitParagraphComment( 320 const ParagraphComment *C) { 321 if (C->isWhitespace()) 322 return; 323 324 Result << "<p>"; 325 for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); 326 I != E; ++I) { 327 visit(*I); 328 } 329 Result << "</p>"; 330} 331 332void CommentASTToHTMLConverter::visitBlockCommandComment( 333 const BlockCommandComment *C) { 334 const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID()); 335 if (Info->IsBriefCommand) { 336 Result << "<p class=\"para-brief\">"; 337 visitNonStandaloneParagraphComment(C->getParagraph()); 338 Result << "</p>"; 339 return; 340 } 341 if (Info->IsReturnsCommand) { 342 Result << "<p class=\"para-returns\">" 343 "<span class=\"word-returns\">Returns</span> "; 344 visitNonStandaloneParagraphComment(C->getParagraph()); 345 Result << "</p>"; 346 return; 347 } 348 // We don't know anything about this command. Just render the paragraph. 349 visit(C->getParagraph()); 350} 351 352void CommentASTToHTMLConverter::visitParamCommandComment( 353 const ParamCommandComment *C) { 354 if (C->isParamIndexValid()) { 355 if (C->isVarArgParam()) { 356 Result << "<dt class=\"param-name-index-vararg\">"; 357 appendToResultWithHTMLEscaping(C->getParamNameAsWritten()); 358 } else { 359 Result << "<dt class=\"param-name-index-" 360 << C->getParamIndex() 361 << "\">"; 362 appendToResultWithHTMLEscaping(C->getParamName(FC)); 363 } 364 } else { 365 Result << "<dt class=\"param-name-index-invalid\">"; 366 appendToResultWithHTMLEscaping(C->getParamNameAsWritten()); 367 } 368 Result << "</dt>"; 369 370 if (C->isParamIndexValid()) { 371 if (C->isVarArgParam()) 372 Result << "<dd class=\"param-descr-index-vararg\">"; 373 else 374 Result << "<dd class=\"param-descr-index-" 375 << C->getParamIndex() 376 << "\">"; 377 } else 378 Result << "<dd class=\"param-descr-index-invalid\">"; 379 380 visitNonStandaloneParagraphComment(C->getParagraph()); 381 Result << "</dd>"; 382} 383 384void CommentASTToHTMLConverter::visitTParamCommandComment( 385 const TParamCommandComment *C) { 386 if (C->isPositionValid()) { 387 if (C->getDepth() == 1) 388 Result << "<dt class=\"tparam-name-index-" 389 << C->getIndex(0) 390 << "\">"; 391 else 392 Result << "<dt class=\"tparam-name-index-other\">"; 393 appendToResultWithHTMLEscaping(C->getParamName(FC)); 394 } else { 395 Result << "<dt class=\"tparam-name-index-invalid\">"; 396 appendToResultWithHTMLEscaping(C->getParamNameAsWritten()); 397 } 398 399 Result << "</dt>"; 400 401 if (C->isPositionValid()) { 402 if (C->getDepth() == 1) 403 Result << "<dd class=\"tparam-descr-index-" 404 << C->getIndex(0) 405 << "\">"; 406 else 407 Result << "<dd class=\"tparam-descr-index-other\">"; 408 } else 409 Result << "<dd class=\"tparam-descr-index-invalid\">"; 410 411 visitNonStandaloneParagraphComment(C->getParagraph()); 412 Result << "</dd>"; 413} 414 415void CommentASTToHTMLConverter::visitVerbatimBlockComment( 416 const VerbatimBlockComment *C) { 417 unsigned NumLines = C->getNumLines(); 418 if (NumLines == 0) 419 return; 420 421 Result << "<pre>"; 422 for (unsigned i = 0; i != NumLines; ++i) { 423 appendToResultWithHTMLEscaping(C->getText(i)); 424 if (i + 1 != NumLines) 425 Result << '\n'; 426 } 427 Result << "</pre>"; 428} 429 430void CommentASTToHTMLConverter::visitVerbatimBlockLineComment( 431 const VerbatimBlockLineComment *C) { 432 llvm_unreachable("should not see this AST node"); 433} 434 435void CommentASTToHTMLConverter::visitVerbatimLineComment( 436 const VerbatimLineComment *C) { 437 Result << "<pre>"; 438 appendToResultWithHTMLEscaping(C->getText()); 439 Result << "</pre>"; 440} 441 442void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) { 443 FullCommentParts Parts(C, Traits); 444 445 bool FirstParagraphIsBrief = false; 446 if (Parts.Headerfile) 447 visit(Parts.Headerfile); 448 if (Parts.Brief) 449 visit(Parts.Brief); 450 else if (Parts.FirstParagraph) { 451 Result << "<p class=\"para-brief\">"; 452 visitNonStandaloneParagraphComment(Parts.FirstParagraph); 453 Result << "</p>"; 454 FirstParagraphIsBrief = true; 455 } 456 457 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) { 458 const Comment *C = Parts.MiscBlocks[i]; 459 if (FirstParagraphIsBrief && C == Parts.FirstParagraph) 460 continue; 461 visit(C); 462 } 463 464 if (Parts.TParams.size() != 0) { 465 Result << "<dl>"; 466 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i) 467 visit(Parts.TParams[i]); 468 Result << "</dl>"; 469 } 470 471 if (Parts.Params.size() != 0) { 472 Result << "<dl>"; 473 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i) 474 visit(Parts.Params[i]); 475 Result << "</dl>"; 476 } 477 478 if (Parts.Returns.size() != 0) { 479 Result << "<div class=\"result-discussion\">"; 480 for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i) 481 visit(Parts.Returns[i]); 482 Result << "</div>"; 483 } 484 485 Result.flush(); 486} 487 488void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment( 489 const ParagraphComment *C) { 490 if (!C) 491 return; 492 493 for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); 494 I != E; ++I) { 495 visit(*I); 496 } 497} 498 499void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) { 500 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) { 501 const char C = *I; 502 switch (C) { 503 case '&': 504 Result << "&"; 505 break; 506 case '<': 507 Result << "<"; 508 break; 509 case '>': 510 Result << ">"; 511 break; 512 case '"': 513 Result << """; 514 break; 515 case '\'': 516 Result << "'"; 517 break; 518 case '/': 519 Result << "/"; 520 break; 521 default: 522 Result << C; 523 break; 524 } 525 } 526} 527 528namespace { 529class CommentASTToXMLConverter : 530 public ConstCommentVisitor<CommentASTToXMLConverter> { 531public: 532 /// \param Str accumulator for XML. 533 CommentASTToXMLConverter(const FullComment *FC, 534 SmallVectorImpl<char> &Str, 535 const CommandTraits &Traits, 536 const SourceManager &SM, 537 SimpleFormatContext &SFC, 538 unsigned FUID) : 539 FC(FC), Result(Str), Traits(Traits), SM(SM), 540 FormatRewriterContext(SFC), 541 FormatInMemoryUniqueId(FUID) { } 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 552 void appendParagraphCommentWithKind(const ParagraphComment *C, 553 StringRef Kind); 554 555 void visitBlockCommandComment(const BlockCommandComment *C); 556 void visitParamCommandComment(const ParamCommandComment *C); 557 void visitTParamCommandComment(const TParamCommandComment *C); 558 void visitVerbatimBlockComment(const VerbatimBlockComment *C); 559 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); 560 void visitVerbatimLineComment(const VerbatimLineComment *C); 561 562 void visitFullComment(const FullComment *C); 563 564 // Helpers. 565 void appendToResultWithXMLEscaping(StringRef S); 566 567 void formatTextOfDeclaration(const DeclInfo *DI, 568 SmallString<128> &Declaration); 569 570private: 571 const FullComment *FC; 572 573 /// Output stream for XML. 574 llvm::raw_svector_ostream Result; 575 576 const CommandTraits &Traits; 577 const SourceManager &SM; 578 SimpleFormatContext &FormatRewriterContext; 579 unsigned FormatInMemoryUniqueId; 580}; 581 582void getSourceTextOfDeclaration(const DeclInfo *ThisDecl, 583 SmallVectorImpl<char> &Str) { 584 ASTContext &Context = ThisDecl->CurrentDecl->getASTContext(); 585 const LangOptions &LangOpts = Context.getLangOpts(); 586 llvm::raw_svector_ostream OS(Str); 587 PrintingPolicy PPolicy(LangOpts); 588 PPolicy.PolishForDeclaration = true; 589 PPolicy.TerseOutput = true; 590 ThisDecl->CurrentDecl->print(OS, PPolicy, 591 /*Indentation*/0, /*PrintInstantiation*/false); 592} 593 594void CommentASTToXMLConverter::formatTextOfDeclaration( 595 const DeclInfo *DI, SmallString<128> &Declaration) { 596 // FIXME. formatting API expects null terminated input string. 597 // There might be more efficient way of doing this. 598 std::string StringDecl = Declaration.str(); 599 600 // Formatter specific code. 601 // Form a unique in memory buffer name. 602 SmallString<128> filename; 603 filename += "xmldecl"; 604 filename += llvm::utostr(FormatInMemoryUniqueId); 605 filename += ".xd"; 606 FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl); 607 SourceLocation Start = FormatRewriterContext.Sources.getLocForStartOfFile(ID) 608 .getLocWithOffset(0); 609 unsigned Length = Declaration.size(); 610 611 std::vector<CharSourceRange> Ranges( 612 1, CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length))); 613 ASTContext &Context = DI->CurrentDecl->getASTContext(); 614 const LangOptions &LangOpts = Context.getLangOpts(); 615 Lexer Lex(ID, FormatRewriterContext.Sources.getBuffer(ID), 616 FormatRewriterContext.Sources, LangOpts); 617 tooling::Replacements Replace = reformat( 618 format::getLLVMStyle(), Lex, FormatRewriterContext.Sources, Ranges); 619 applyAllReplacements(Replace, FormatRewriterContext.Rewrite); 620 Declaration = FormatRewriterContext.getRewrittenText(ID); 621} 622 623} // end unnamed namespace 624 625void CommentASTToXMLConverter::visitTextComment(const TextComment *C) { 626 appendToResultWithXMLEscaping(C->getText()); 627} 628 629void CommentASTToXMLConverter::visitInlineCommandComment( 630 const InlineCommandComment *C) { 631 // Nothing to render if no arguments supplied. 632 if (C->getNumArgs() == 0) 633 return; 634 635 // Nothing to render if argument is empty. 636 StringRef Arg0 = C->getArgText(0); 637 if (Arg0.empty()) 638 return; 639 640 switch (C->getRenderKind()) { 641 case InlineCommandComment::RenderNormal: 642 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) { 643 appendToResultWithXMLEscaping(C->getArgText(i)); 644 Result << " "; 645 } 646 return; 647 case InlineCommandComment::RenderBold: 648 assert(C->getNumArgs() == 1); 649 Result << "<bold>"; 650 appendToResultWithXMLEscaping(Arg0); 651 Result << "</bold>"; 652 return; 653 case InlineCommandComment::RenderMonospaced: 654 assert(C->getNumArgs() == 1); 655 Result << "<monospaced>"; 656 appendToResultWithXMLEscaping(Arg0); 657 Result << "</monospaced>"; 658 return; 659 case InlineCommandComment::RenderEmphasized: 660 assert(C->getNumArgs() == 1); 661 Result << "<emphasized>"; 662 appendToResultWithXMLEscaping(Arg0); 663 Result << "</emphasized>"; 664 return; 665 } 666} 667 668void CommentASTToXMLConverter::visitHTMLStartTagComment( 669 const HTMLStartTagComment *C) { 670 Result << "<rawHTML><![CDATA["; 671 printHTMLStartTagComment(C, Result); 672 Result << "]]></rawHTML>"; 673} 674 675void 676CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) { 677 Result << "<rawHTML></" << C->getTagName() << "></rawHTML>"; 678} 679 680void 681CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) { 682 appendParagraphCommentWithKind(C, StringRef()); 683} 684 685void CommentASTToXMLConverter::appendParagraphCommentWithKind( 686 const ParagraphComment *C, 687 StringRef ParagraphKind) { 688 if (C->isWhitespace()) 689 return; 690 691 if (ParagraphKind.empty()) 692 Result << "<Para>"; 693 else 694 Result << "<Para kind=\"" << ParagraphKind << "\">"; 695 696 for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); 697 I != E; ++I) { 698 visit(*I); 699 } 700 Result << "</Para>"; 701} 702 703void CommentASTToXMLConverter::visitBlockCommandComment( 704 const BlockCommandComment *C) { 705 StringRef ParagraphKind; 706 707 switch (C->getCommandID()) { 708 case CommandTraits::KCI_attention: 709 case CommandTraits::KCI_author: 710 case CommandTraits::KCI_authors: 711 case CommandTraits::KCI_bug: 712 case CommandTraits::KCI_copyright: 713 case CommandTraits::KCI_date: 714 case CommandTraits::KCI_invariant: 715 case CommandTraits::KCI_note: 716 case CommandTraits::KCI_post: 717 case CommandTraits::KCI_pre: 718 case CommandTraits::KCI_remark: 719 case CommandTraits::KCI_remarks: 720 case CommandTraits::KCI_sa: 721 case CommandTraits::KCI_see: 722 case CommandTraits::KCI_since: 723 case CommandTraits::KCI_todo: 724 case CommandTraits::KCI_version: 725 case CommandTraits::KCI_warning: 726 ParagraphKind = C->getCommandName(Traits); 727 default: 728 break; 729 } 730 731 appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind); 732} 733 734void CommentASTToXMLConverter::visitParamCommandComment( 735 const ParamCommandComment *C) { 736 Result << "<Parameter><Name>"; 737 appendToResultWithXMLEscaping(C->isParamIndexValid() 738 ? C->getParamName(FC) 739 : C->getParamNameAsWritten()); 740 Result << "</Name>"; 741 742 if (C->isParamIndexValid()) { 743 if (C->isVarArgParam()) 744 Result << "<IsVarArg />"; 745 else 746 Result << "<Index>" << C->getParamIndex() << "</Index>"; 747 } 748 749 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">"; 750 switch (C->getDirection()) { 751 case ParamCommandComment::In: 752 Result << "in"; 753 break; 754 case ParamCommandComment::Out: 755 Result << "out"; 756 break; 757 case ParamCommandComment::InOut: 758 Result << "in,out"; 759 break; 760 } 761 Result << "</Direction><Discussion>"; 762 visit(C->getParagraph()); 763 Result << "</Discussion></Parameter>"; 764} 765 766void CommentASTToXMLConverter::visitTParamCommandComment( 767 const TParamCommandComment *C) { 768 Result << "<Parameter><Name>"; 769 appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC) 770 : C->getParamNameAsWritten()); 771 Result << "</Name>"; 772 773 if (C->isPositionValid() && C->getDepth() == 1) { 774 Result << "<Index>" << C->getIndex(0) << "</Index>"; 775 } 776 777 Result << "<Discussion>"; 778 visit(C->getParagraph()); 779 Result << "</Discussion></Parameter>"; 780} 781 782void CommentASTToXMLConverter::visitVerbatimBlockComment( 783 const VerbatimBlockComment *C) { 784 unsigned NumLines = C->getNumLines(); 785 if (NumLines == 0) 786 return; 787 788 switch (C->getCommandID()) { 789 case CommandTraits::KCI_code: 790 Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">"; 791 break; 792 default: 793 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">"; 794 break; 795 } 796 for (unsigned i = 0; i != NumLines; ++i) { 797 appendToResultWithXMLEscaping(C->getText(i)); 798 if (i + 1 != NumLines) 799 Result << '\n'; 800 } 801 Result << "</Verbatim>"; 802} 803 804void CommentASTToXMLConverter::visitVerbatimBlockLineComment( 805 const VerbatimBlockLineComment *C) { 806 llvm_unreachable("should not see this AST node"); 807} 808 809void CommentASTToXMLConverter::visitVerbatimLineComment( 810 const VerbatimLineComment *C) { 811 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">"; 812 appendToResultWithXMLEscaping(C->getText()); 813 Result << "</Verbatim>"; 814} 815 816void CommentASTToXMLConverter::visitFullComment(const FullComment *C) { 817 FullCommentParts Parts(C, Traits); 818 819 const DeclInfo *DI = C->getDeclInfo(); 820 StringRef RootEndTag; 821 if (DI) { 822 switch (DI->getKind()) { 823 case DeclInfo::OtherKind: 824 RootEndTag = "</Other>"; 825 Result << "<Other"; 826 break; 827 case DeclInfo::FunctionKind: 828 RootEndTag = "</Function>"; 829 Result << "<Function"; 830 switch (DI->TemplateKind) { 831 case DeclInfo::NotTemplate: 832 break; 833 case DeclInfo::Template: 834 Result << " templateKind=\"template\""; 835 break; 836 case DeclInfo::TemplateSpecialization: 837 Result << " templateKind=\"specialization\""; 838 break; 839 case DeclInfo::TemplatePartialSpecialization: 840 llvm_unreachable("partial specializations of functions " 841 "are not allowed in C++"); 842 } 843 if (DI->IsInstanceMethod) 844 Result << " isInstanceMethod=\"1\""; 845 if (DI->IsClassMethod) 846 Result << " isClassMethod=\"1\""; 847 break; 848 case DeclInfo::ClassKind: 849 RootEndTag = "</Class>"; 850 Result << "<Class"; 851 switch (DI->TemplateKind) { 852 case DeclInfo::NotTemplate: 853 break; 854 case DeclInfo::Template: 855 Result << " templateKind=\"template\""; 856 break; 857 case DeclInfo::TemplateSpecialization: 858 Result << " templateKind=\"specialization\""; 859 break; 860 case DeclInfo::TemplatePartialSpecialization: 861 Result << " templateKind=\"partialSpecialization\""; 862 break; 863 } 864 break; 865 case DeclInfo::VariableKind: 866 RootEndTag = "</Variable>"; 867 Result << "<Variable"; 868 break; 869 case DeclInfo::NamespaceKind: 870 RootEndTag = "</Namespace>"; 871 Result << "<Namespace"; 872 break; 873 case DeclInfo::TypedefKind: 874 RootEndTag = "</Typedef>"; 875 Result << "<Typedef"; 876 break; 877 case DeclInfo::EnumKind: 878 RootEndTag = "</Enum>"; 879 Result << "<Enum"; 880 break; 881 } 882 883 { 884 // Print line and column number. 885 SourceLocation Loc = DI->CurrentDecl->getLocation(); 886 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); 887 FileID FID = LocInfo.first; 888 unsigned FileOffset = LocInfo.second; 889 890 if (!FID.isInvalid()) { 891 if (const FileEntry *FE = SM.getFileEntryForID(FID)) { 892 Result << " file=\""; 893 appendToResultWithXMLEscaping(FE->getName()); 894 Result << "\""; 895 } 896 Result << " line=\"" << SM.getLineNumber(FID, FileOffset) 897 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset) 898 << "\""; 899 } 900 } 901 902 // Finish the root tag. 903 Result << ">"; 904 905 bool FoundName = false; 906 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) { 907 if (DeclarationName DeclName = ND->getDeclName()) { 908 Result << "<Name>"; 909 std::string Name = DeclName.getAsString(); 910 appendToResultWithXMLEscaping(Name); 911 FoundName = true; 912 Result << "</Name>"; 913 } 914 } 915 if (!FoundName) 916 Result << "<Name><anonymous></Name>"; 917 918 { 919 // Print USR. 920 SmallString<128> USR; 921 generateUSRForDecl(DI->CommentDecl, USR); 922 if (!USR.empty()) { 923 Result << "<USR>"; 924 appendToResultWithXMLEscaping(USR); 925 Result << "</USR>"; 926 } 927 } 928 } else { 929 // No DeclInfo -- just emit some root tag and name tag. 930 RootEndTag = "</Other>"; 931 Result << "<Other><Name>unknown</Name>"; 932 } 933 934 if (Parts.Headerfile) { 935 Result << "<Headerfile>"; 936 visit(Parts.Headerfile); 937 Result << "</Headerfile>"; 938 } 939 940 { 941 // Pretty-print the declaration. 942 Result << "<Declaration>"; 943 SmallString<128> Declaration; 944 getSourceTextOfDeclaration(DI, Declaration); 945 formatTextOfDeclaration(DI, Declaration); 946 appendToResultWithXMLEscaping(Declaration); 947 Result << "</Declaration>"; 948 } 949 950 bool FirstParagraphIsBrief = false; 951 if (Parts.Brief) { 952 Result << "<Abstract>"; 953 visit(Parts.Brief); 954 Result << "</Abstract>"; 955 } else if (Parts.FirstParagraph) { 956 Result << "<Abstract>"; 957 visit(Parts.FirstParagraph); 958 Result << "</Abstract>"; 959 FirstParagraphIsBrief = true; 960 } 961 962 if (Parts.TParams.size() != 0) { 963 Result << "<TemplateParameters>"; 964 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i) 965 visit(Parts.TParams[i]); 966 Result << "</TemplateParameters>"; 967 } 968 969 if (Parts.Params.size() != 0) { 970 Result << "<Parameters>"; 971 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i) 972 visit(Parts.Params[i]); 973 Result << "</Parameters>"; 974 } 975 976 if (Parts.Exceptions.size() != 0) { 977 Result << "<Exceptions>"; 978 for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i) 979 visit(Parts.Exceptions[i]); 980 Result << "</Exceptions>"; 981 } 982 983 if (Parts.Returns.size() != 0) { 984 Result << "<ResultDiscussion>"; 985 for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i) 986 visit(Parts.Returns[i]); 987 Result << "</ResultDiscussion>"; 988 } 989 990 if (DI->CommentDecl->hasAttrs()) { 991 const AttrVec &Attrs = DI->CommentDecl->getAttrs(); 992 for (unsigned i = 0, e = Attrs.size(); i != e; i++) { 993 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]); 994 if (!AA) { 995 if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) { 996 if (DA->getMessage().empty()) 997 Result << "<Deprecated/>"; 998 else { 999 Result << "<Deprecated>"; 1000 appendToResultWithXMLEscaping(DA->getMessage()); 1001 Result << "</Deprecated>"; 1002 } 1003 } 1004 else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) { 1005 if (UA->getMessage().empty()) 1006 Result << "<Unavailable/>"; 1007 else { 1008 Result << "<Unavailable>"; 1009 appendToResultWithXMLEscaping(UA->getMessage()); 1010 Result << "</Unavailable>"; 1011 } 1012 } 1013 continue; 1014 } 1015 1016 // 'availability' attribute. 1017 Result << "<Availability"; 1018 StringRef Distribution; 1019 if (AA->getPlatform()) { 1020 Distribution = AvailabilityAttr::getPrettyPlatformName( 1021 AA->getPlatform()->getName()); 1022 if (Distribution.empty()) 1023 Distribution = AA->getPlatform()->getName(); 1024 } 1025 Result << " distribution=\"" << Distribution << "\">"; 1026 VersionTuple IntroducedInVersion = AA->getIntroduced(); 1027 if (!IntroducedInVersion.empty()) { 1028 Result << "<IntroducedInVersion>" 1029 << IntroducedInVersion.getAsString() 1030 << "</IntroducedInVersion>"; 1031 } 1032 VersionTuple DeprecatedInVersion = AA->getDeprecated(); 1033 if (!DeprecatedInVersion.empty()) { 1034 Result << "<DeprecatedInVersion>" 1035 << DeprecatedInVersion.getAsString() 1036 << "</DeprecatedInVersion>"; 1037 } 1038 VersionTuple RemovedAfterVersion = AA->getObsoleted(); 1039 if (!RemovedAfterVersion.empty()) { 1040 Result << "<RemovedAfterVersion>" 1041 << RemovedAfterVersion.getAsString() 1042 << "</RemovedAfterVersion>"; 1043 } 1044 StringRef DeprecationSummary = AA->getMessage(); 1045 if (!DeprecationSummary.empty()) { 1046 Result << "<DeprecationSummary>"; 1047 appendToResultWithXMLEscaping(DeprecationSummary); 1048 Result << "</DeprecationSummary>"; 1049 } 1050 if (AA->getUnavailable()) 1051 Result << "<Unavailable/>"; 1052 Result << "</Availability>"; 1053 } 1054 } 1055 1056 { 1057 bool StartTagEmitted = false; 1058 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) { 1059 const Comment *C = Parts.MiscBlocks[i]; 1060 if (FirstParagraphIsBrief && C == Parts.FirstParagraph) 1061 continue; 1062 if (!StartTagEmitted) { 1063 Result << "<Discussion>"; 1064 StartTagEmitted = true; 1065 } 1066 visit(C); 1067 } 1068 if (StartTagEmitted) 1069 Result << "</Discussion>"; 1070 } 1071 1072 Result << RootEndTag; 1073 1074 Result.flush(); 1075} 1076 1077void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) { 1078 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) { 1079 const char C = *I; 1080 switch (C) { 1081 case '&': 1082 Result << "&"; 1083 break; 1084 case '<': 1085 Result << "<"; 1086 break; 1087 case '>': 1088 Result << ">"; 1089 break; 1090 case '"': 1091 Result << """; 1092 break; 1093 case '\'': 1094 Result << "'"; 1095 break; 1096 default: 1097 Result << C; 1098 break; 1099 } 1100 } 1101} 1102 1103void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC, 1104 SmallVectorImpl<char> &HTML, 1105 const ASTContext &Context) { 1106 CommentASTToHTMLConverter Converter(FC, HTML, 1107 Context.getCommentCommandTraits()); 1108 Converter.visit(FC); 1109} 1110 1111void CommentToXMLConverter::convertHTMLTagNodeToText( 1112 const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text, 1113 const ASTContext &Context) { 1114 CommentASTToHTMLConverter Converter(0, Text, 1115 Context.getCommentCommandTraits()); 1116 Converter.visit(HTC); 1117} 1118 1119void CommentToXMLConverter::convertCommentToXML(const FullComment *FC, 1120 SmallVectorImpl<char> &XML, 1121 const ASTContext &Context) { 1122 if (!FormatContext) { 1123 FormatContext = new SimpleFormatContext(Context.getLangOpts()); 1124 } else if ((FormatInMemoryUniqueId % 1000) == 0) { 1125 // Delete after some number of iterations, so the buffers don't grow 1126 // too large. 1127 delete FormatContext; 1128 FormatContext = new SimpleFormatContext(Context.getLangOpts()); 1129 } 1130 1131 CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(), 1132 Context.getSourceManager(), *FormatContext, 1133 FormatInMemoryUniqueId++); 1134 Converter.visit(FC); 1135} 1136 1137