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 default: 724 break; 725 } 726 727 appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind); 728 } 729 730 void CommentASTToXMLConverter::visitParamCommandComment( 731 const ParamCommandComment *C) { 732 Result << "<Parameter><Name>"; 733 appendToResultWithXMLEscaping(C->isParamIndexValid() 734 ? C->getParamName(FC) 735 : C->getParamNameAsWritten()); 736 Result << "</Name>"; 737 738 if (C->isParamIndexValid()) { 739 if (C->isVarArgParam()) 740 Result << "<IsVarArg />"; 741 else 742 Result << "<Index>" << C->getParamIndex() << "</Index>"; 743 } 744 745 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">"; 746 switch (C->getDirection()) { 747 case ParamCommandComment::In: 748 Result << "in"; 749 break; 750 case ParamCommandComment::Out: 751 Result << "out"; 752 break; 753 case ParamCommandComment::InOut: 754 Result << "in,out"; 755 break; 756 } 757 Result << "</Direction><Discussion>"; 758 visit(C->getParagraph()); 759 Result << "</Discussion></Parameter>"; 760 } 761 762 void CommentASTToXMLConverter::visitTParamCommandComment( 763 const TParamCommandComment *C) { 764 Result << "<Parameter><Name>"; 765 appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC) 766 : C->getParamNameAsWritten()); 767 Result << "</Name>"; 768 769 if (C->isPositionValid() && C->getDepth() == 1) { 770 Result << "<Index>" << C->getIndex(0) << "</Index>"; 771 } 772 773 Result << "<Discussion>"; 774 visit(C->getParagraph()); 775 Result << "</Discussion></Parameter>"; 776 } 777 778 void CommentASTToXMLConverter::visitVerbatimBlockComment( 779 const VerbatimBlockComment *C) { 780 unsigned NumLines = C->getNumLines(); 781 if (NumLines == 0) 782 return; 783 784 switch (C->getCommandID()) { 785 case CommandTraits::KCI_code: 786 Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">"; 787 break; 788 default: 789 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">"; 790 break; 791 } 792 for (unsigned i = 0; i != NumLines; ++i) { 793 appendToResultWithXMLEscaping(C->getText(i)); 794 if (i + 1 != NumLines) 795 Result << '\n'; 796 } 797 Result << "</Verbatim>"; 798 } 799 800 void CommentASTToXMLConverter::visitVerbatimBlockLineComment( 801 const VerbatimBlockLineComment *C) { 802 llvm_unreachable("should not see this AST node"); 803 } 804 805 void CommentASTToXMLConverter::visitVerbatimLineComment( 806 const VerbatimLineComment *C) { 807 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">"; 808 appendToResultWithXMLEscaping(C->getText()); 809 Result << "</Verbatim>"; 810 } 811 812 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) { 813 FullCommentParts Parts(C, Traits); 814 815 const DeclInfo *DI = C->getDeclInfo(); 816 StringRef RootEndTag; 817 if (DI) { 818 switch (DI->getKind()) { 819 case DeclInfo::OtherKind: 820 RootEndTag = "</Other>"; 821 Result << "<Other"; 822 break; 823 case DeclInfo::FunctionKind: 824 RootEndTag = "</Function>"; 825 Result << "<Function"; 826 switch (DI->TemplateKind) { 827 case DeclInfo::NotTemplate: 828 break; 829 case DeclInfo::Template: 830 Result << " templateKind=\"template\""; 831 break; 832 case DeclInfo::TemplateSpecialization: 833 Result << " templateKind=\"specialization\""; 834 break; 835 case DeclInfo::TemplatePartialSpecialization: 836 llvm_unreachable("partial specializations of functions " 837 "are not allowed in C++"); 838 } 839 if (DI->IsInstanceMethod) 840 Result << " isInstanceMethod=\"1\""; 841 if (DI->IsClassMethod) 842 Result << " isClassMethod=\"1\""; 843 break; 844 case DeclInfo::ClassKind: 845 RootEndTag = "</Class>"; 846 Result << "<Class"; 847 switch (DI->TemplateKind) { 848 case DeclInfo::NotTemplate: 849 break; 850 case DeclInfo::Template: 851 Result << " templateKind=\"template\""; 852 break; 853 case DeclInfo::TemplateSpecialization: 854 Result << " templateKind=\"specialization\""; 855 break; 856 case DeclInfo::TemplatePartialSpecialization: 857 Result << " templateKind=\"partialSpecialization\""; 858 break; 859 } 860 break; 861 case DeclInfo::VariableKind: 862 RootEndTag = "</Variable>"; 863 Result << "<Variable"; 864 break; 865 case DeclInfo::NamespaceKind: 866 RootEndTag = "</Namespace>"; 867 Result << "<Namespace"; 868 break; 869 case DeclInfo::TypedefKind: 870 RootEndTag = "</Typedef>"; 871 Result << "<Typedef"; 872 break; 873 case DeclInfo::EnumKind: 874 RootEndTag = "</Enum>"; 875 Result << "<Enum"; 876 break; 877 } 878 879 { 880 // Print line and column number. 881 SourceLocation Loc = DI->CurrentDecl->getLocation(); 882 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); 883 FileID FID = LocInfo.first; 884 unsigned FileOffset = LocInfo.second; 885 886 if (FID.isValid()) { 887 if (const FileEntry *FE = SM.getFileEntryForID(FID)) { 888 Result << " file=\""; 889 appendToResultWithXMLEscaping(FE->getName()); 890 Result << "\""; 891 } 892 Result << " line=\"" << SM.getLineNumber(FID, FileOffset) 893 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset) 894 << "\""; 895 } 896 } 897 898 // Finish the root tag. 899 Result << ">"; 900 901 bool FoundName = false; 902 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) { 903 if (DeclarationName DeclName = ND->getDeclName()) { 904 Result << "<Name>"; 905 std::string Name = DeclName.getAsString(); 906 appendToResultWithXMLEscaping(Name); 907 FoundName = true; 908 Result << "</Name>"; 909 } 910 } 911 if (!FoundName) 912 Result << "<Name><anonymous></Name>"; 913 914 { 915 // Print USR. 916 SmallString<128> USR; 917 generateUSRForDecl(DI->CommentDecl, USR); 918 if (!USR.empty()) { 919 Result << "<USR>"; 920 appendToResultWithXMLEscaping(USR); 921 Result << "</USR>"; 922 } 923 } 924 } else { 925 // No DeclInfo -- just emit some root tag and name tag. 926 RootEndTag = "</Other>"; 927 Result << "<Other><Name>unknown</Name>"; 928 } 929 930 if (Parts.Headerfile) { 931 Result << "<Headerfile>"; 932 visit(Parts.Headerfile); 933 Result << "</Headerfile>"; 934 } 935 936 { 937 // Pretty-print the declaration. 938 Result << "<Declaration>"; 939 SmallString<128> Declaration; 940 getSourceTextOfDeclaration(DI, Declaration); 941 formatTextOfDeclaration(DI, Declaration); 942 appendToResultWithXMLEscaping(Declaration); 943 Result << "</Declaration>"; 944 } 945 946 bool FirstParagraphIsBrief = false; 947 if (Parts.Brief) { 948 Result << "<Abstract>"; 949 visit(Parts.Brief); 950 Result << "</Abstract>"; 951 } else if (Parts.FirstParagraph) { 952 Result << "<Abstract>"; 953 visit(Parts.FirstParagraph); 954 Result << "</Abstract>"; 955 FirstParagraphIsBrief = true; 956 } 957 958 if (Parts.TParams.size() != 0) { 959 Result << "<TemplateParameters>"; 960 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i) 961 visit(Parts.TParams[i]); 962 Result << "</TemplateParameters>"; 963 } 964 965 if (Parts.Params.size() != 0) { 966 Result << "<Parameters>"; 967 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i) 968 visit(Parts.Params[i]); 969 Result << "</Parameters>"; 970 } 971 972 if (Parts.Exceptions.size() != 0) { 973 Result << "<Exceptions>"; 974 for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i) 975 visit(Parts.Exceptions[i]); 976 Result << "</Exceptions>"; 977 } 978 979 if (Parts.Returns.size() != 0) { 980 Result << "<ResultDiscussion>"; 981 for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i) 982 visit(Parts.Returns[i]); 983 Result << "</ResultDiscussion>"; 984 } 985 986 if (DI->CommentDecl->hasAttrs()) { 987 const AttrVec &Attrs = DI->CommentDecl->getAttrs(); 988 for (unsigned i = 0, e = Attrs.size(); i != e; i++) { 989 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]); 990 if (!AA) { 991 if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) { 992 if (DA->getMessage().empty()) 993 Result << "<Deprecated/>"; 994 else { 995 Result << "<Deprecated>"; 996 appendToResultWithXMLEscaping(DA->getMessage()); 997 Result << "</Deprecated>"; 998 } 999 } 1000 else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) { 1001 if (UA->getMessage().empty()) 1002 Result << "<Unavailable/>"; 1003 else { 1004 Result << "<Unavailable>"; 1005 appendToResultWithXMLEscaping(UA->getMessage()); 1006 Result << "</Unavailable>"; 1007 } 1008 } 1009 continue; 1010 } 1011 1012 // 'availability' attribute. 1013 Result << "<Availability"; 1014 StringRef Distribution; 1015 if (AA->getPlatform()) { 1016 Distribution = AvailabilityAttr::getPrettyPlatformName( 1017 AA->getPlatform()->getName()); 1018 if (Distribution.empty()) 1019 Distribution = AA->getPlatform()->getName(); 1020 } 1021 Result << " distribution=\"" << Distribution << "\">"; 1022 VersionTuple IntroducedInVersion = AA->getIntroduced(); 1023 if (!IntroducedInVersion.empty()) { 1024 Result << "<IntroducedInVersion>" 1025 << IntroducedInVersion.getAsString() 1026 << "</IntroducedInVersion>"; 1027 } 1028 VersionTuple DeprecatedInVersion = AA->getDeprecated(); 1029 if (!DeprecatedInVersion.empty()) { 1030 Result << "<DeprecatedInVersion>" 1031 << DeprecatedInVersion.getAsString() 1032 << "</DeprecatedInVersion>"; 1033 } 1034 VersionTuple RemovedAfterVersion = AA->getObsoleted(); 1035 if (!RemovedAfterVersion.empty()) { 1036 Result << "<RemovedAfterVersion>" 1037 << RemovedAfterVersion.getAsString() 1038 << "</RemovedAfterVersion>"; 1039 } 1040 StringRef DeprecationSummary = AA->getMessage(); 1041 if (!DeprecationSummary.empty()) { 1042 Result << "<DeprecationSummary>"; 1043 appendToResultWithXMLEscaping(DeprecationSummary); 1044 Result << "</DeprecationSummary>"; 1045 } 1046 if (AA->getUnavailable()) 1047 Result << "<Unavailable/>"; 1048 Result << "</Availability>"; 1049 } 1050 } 1051 1052 { 1053 bool StartTagEmitted = false; 1054 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) { 1055 const Comment *C = Parts.MiscBlocks[i]; 1056 if (FirstParagraphIsBrief && C == Parts.FirstParagraph) 1057 continue; 1058 if (!StartTagEmitted) { 1059 Result << "<Discussion>"; 1060 StartTagEmitted = true; 1061 } 1062 visit(C); 1063 } 1064 if (StartTagEmitted) 1065 Result << "</Discussion>"; 1066 } 1067 1068 Result << RootEndTag; 1069 } 1070 1071 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) { 1072 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) { 1073 const char C = *I; 1074 switch (C) { 1075 case '&': 1076 Result << "&"; 1077 break; 1078 case '<': 1079 Result << "<"; 1080 break; 1081 case '>': 1082 Result << ">"; 1083 break; 1084 case '"': 1085 Result << """; 1086 break; 1087 case '\'': 1088 Result << "'"; 1089 break; 1090 default: 1091 Result << C; 1092 break; 1093 } 1094 } 1095 } 1096 1097 void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) { 1098 if (S.empty()) 1099 return; 1100 1101 Result << "<![CDATA["; 1102 while (!S.empty()) { 1103 size_t Pos = S.find("]]>"); 1104 if (Pos == 0) { 1105 Result << "]]]]><![CDATA[>"; 1106 S = S.drop_front(3); 1107 continue; 1108 } 1109 if (Pos == StringRef::npos) 1110 Pos = S.size(); 1111 1112 Result << S.substr(0, Pos); 1113 1114 S = S.drop_front(Pos); 1115 } 1116 Result << "]]>"; 1117 } 1118 1119 CommentToXMLConverter::CommentToXMLConverter() {} 1120 CommentToXMLConverter::~CommentToXMLConverter() {} 1121 1122 void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC, 1123 SmallVectorImpl<char> &HTML, 1124 const ASTContext &Context) { 1125 CommentASTToHTMLConverter Converter(FC, HTML, 1126 Context.getCommentCommandTraits()); 1127 Converter.visit(FC); 1128 } 1129 1130 void CommentToXMLConverter::convertHTMLTagNodeToText( 1131 const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text, 1132 const ASTContext &Context) { 1133 CommentASTToHTMLConverter Converter(nullptr, Text, 1134 Context.getCommentCommandTraits()); 1135 Converter.visit(HTC); 1136 } 1137 1138 void CommentToXMLConverter::convertCommentToXML(const FullComment *FC, 1139 SmallVectorImpl<char> &XML, 1140 const ASTContext &Context) { 1141 CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(), 1142 Context.getSourceManager()); 1143 Converter.visit(FC); 1144 } 1145