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