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 ThisDecl->CurrentDecl->print(OS, PPolicy, 583 /*Indentation*/0, /*PrintInstantiation*/false); 584 } 585 586 void CommentASTToXMLConverter::formatTextOfDeclaration( 587 const DeclInfo *DI, SmallString<128> &Declaration) { 588 // Formatting API expects null terminated input string. 589 StringRef StringDecl(Declaration.c_str(), Declaration.size()); 590 591 // Formatter specific code. 592 unsigned Offset = 0; 593 unsigned Length = Declaration.size(); 594 595 format::FormatStyle Style = format::getLLVMStyle(); 596 Style.FixNamespaceComments = false; 597 tooling::Replacements Replaces = 598 reformat(Style, StringDecl, tooling::Range(Offset, Length), "xmldecl.xd"); 599 auto FormattedStringDecl = applyAllReplacements(StringDecl, Replaces); 600 if (static_cast<bool>(FormattedStringDecl)) { 601 Declaration = *FormattedStringDecl; 602 } 603 } 604 605 } // end unnamed namespace 606 607 void CommentASTToXMLConverter::visitTextComment(const TextComment *C) { 608 appendToResultWithXMLEscaping(C->getText()); 609 } 610 611 void CommentASTToXMLConverter::visitInlineCommandComment( 612 const InlineCommandComment *C) { 613 // Nothing to render if no arguments supplied. 614 if (C->getNumArgs() == 0) 615 return; 616 617 // Nothing to render if argument is empty. 618 StringRef Arg0 = C->getArgText(0); 619 if (Arg0.empty()) 620 return; 621 622 switch (C->getRenderKind()) { 623 case InlineCommandComment::RenderNormal: 624 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) { 625 appendToResultWithXMLEscaping(C->getArgText(i)); 626 Result << " "; 627 } 628 return; 629 case InlineCommandComment::RenderBold: 630 assert(C->getNumArgs() == 1); 631 Result << "<bold>"; 632 appendToResultWithXMLEscaping(Arg0); 633 Result << "</bold>"; 634 return; 635 case InlineCommandComment::RenderMonospaced: 636 assert(C->getNumArgs() == 1); 637 Result << "<monospaced>"; 638 appendToResultWithXMLEscaping(Arg0); 639 Result << "</monospaced>"; 640 return; 641 case InlineCommandComment::RenderEmphasized: 642 assert(C->getNumArgs() == 1); 643 Result << "<emphasized>"; 644 appendToResultWithXMLEscaping(Arg0); 645 Result << "</emphasized>"; 646 return; 647 } 648 } 649 650 void CommentASTToXMLConverter::visitHTMLStartTagComment( 651 const HTMLStartTagComment *C) { 652 Result << "<rawHTML"; 653 if (C->isMalformed()) 654 Result << " isMalformed=\"1\""; 655 Result << ">"; 656 { 657 SmallString<32> Tag; 658 { 659 llvm::raw_svector_ostream TagOS(Tag); 660 printHTMLStartTagComment(C, TagOS); 661 } 662 appendToResultWithCDATAEscaping(Tag); 663 } 664 Result << "</rawHTML>"; 665 } 666 667 void 668 CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) { 669 Result << "<rawHTML"; 670 if (C->isMalformed()) 671 Result << " isMalformed=\"1\""; 672 Result << "></" << C->getTagName() << "></rawHTML>"; 673 } 674 675 void 676 CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) { 677 appendParagraphCommentWithKind(C, StringRef()); 678 } 679 680 void CommentASTToXMLConverter::appendParagraphCommentWithKind( 681 const ParagraphComment *C, 682 StringRef ParagraphKind) { 683 if (C->isWhitespace()) 684 return; 685 686 if (ParagraphKind.empty()) 687 Result << "<Para>"; 688 else 689 Result << "<Para kind=\"" << ParagraphKind << "\">"; 690 691 for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); 692 I != E; ++I) { 693 visit(*I); 694 } 695 Result << "</Para>"; 696 } 697 698 void CommentASTToXMLConverter::visitBlockCommandComment( 699 const BlockCommandComment *C) { 700 StringRef ParagraphKind; 701 702 switch (C->getCommandID()) { 703 case CommandTraits::KCI_attention: 704 case CommandTraits::KCI_author: 705 case CommandTraits::KCI_authors: 706 case CommandTraits::KCI_bug: 707 case CommandTraits::KCI_copyright: 708 case CommandTraits::KCI_date: 709 case CommandTraits::KCI_invariant: 710 case CommandTraits::KCI_note: 711 case CommandTraits::KCI_post: 712 case CommandTraits::KCI_pre: 713 case CommandTraits::KCI_remark: 714 case CommandTraits::KCI_remarks: 715 case CommandTraits::KCI_sa: 716 case CommandTraits::KCI_see: 717 case CommandTraits::KCI_since: 718 case CommandTraits::KCI_todo: 719 case CommandTraits::KCI_version: 720 case CommandTraits::KCI_warning: 721 ParagraphKind = C->getCommandName(Traits); 722 default: 723 break; 724 } 725 726 appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind); 727 } 728 729 void CommentASTToXMLConverter::visitParamCommandComment( 730 const ParamCommandComment *C) { 731 Result << "<Parameter><Name>"; 732 appendToResultWithXMLEscaping(C->isParamIndexValid() 733 ? C->getParamName(FC) 734 : C->getParamNameAsWritten()); 735 Result << "</Name>"; 736 737 if (C->isParamIndexValid()) { 738 if (C->isVarArgParam()) 739 Result << "<IsVarArg />"; 740 else 741 Result << "<Index>" << C->getParamIndex() << "</Index>"; 742 } 743 744 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">"; 745 switch (C->getDirection()) { 746 case ParamCommandComment::In: 747 Result << "in"; 748 break; 749 case ParamCommandComment::Out: 750 Result << "out"; 751 break; 752 case ParamCommandComment::InOut: 753 Result << "in,out"; 754 break; 755 } 756 Result << "</Direction><Discussion>"; 757 visit(C->getParagraph()); 758 Result << "</Discussion></Parameter>"; 759 } 760 761 void CommentASTToXMLConverter::visitTParamCommandComment( 762 const TParamCommandComment *C) { 763 Result << "<Parameter><Name>"; 764 appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC) 765 : C->getParamNameAsWritten()); 766 Result << "</Name>"; 767 768 if (C->isPositionValid() && C->getDepth() == 1) { 769 Result << "<Index>" << C->getIndex(0) << "</Index>"; 770 } 771 772 Result << "<Discussion>"; 773 visit(C->getParagraph()); 774 Result << "</Discussion></Parameter>"; 775 } 776 777 void CommentASTToXMLConverter::visitVerbatimBlockComment( 778 const VerbatimBlockComment *C) { 779 unsigned NumLines = C->getNumLines(); 780 if (NumLines == 0) 781 return; 782 783 switch (C->getCommandID()) { 784 case CommandTraits::KCI_code: 785 Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">"; 786 break; 787 default: 788 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">"; 789 break; 790 } 791 for (unsigned i = 0; i != NumLines; ++i) { 792 appendToResultWithXMLEscaping(C->getText(i)); 793 if (i + 1 != NumLines) 794 Result << '\n'; 795 } 796 Result << "</Verbatim>"; 797 } 798 799 void CommentASTToXMLConverter::visitVerbatimBlockLineComment( 800 const VerbatimBlockLineComment *C) { 801 llvm_unreachable("should not see this AST node"); 802 } 803 804 void CommentASTToXMLConverter::visitVerbatimLineComment( 805 const VerbatimLineComment *C) { 806 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">"; 807 appendToResultWithXMLEscaping(C->getText()); 808 Result << "</Verbatim>"; 809 } 810 811 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) { 812 FullCommentParts Parts(C, Traits); 813 814 const DeclInfo *DI = C->getDeclInfo(); 815 StringRef RootEndTag; 816 if (DI) { 817 switch (DI->getKind()) { 818 case DeclInfo::OtherKind: 819 RootEndTag = "</Other>"; 820 Result << "<Other"; 821 break; 822 case DeclInfo::FunctionKind: 823 RootEndTag = "</Function>"; 824 Result << "<Function"; 825 switch (DI->TemplateKind) { 826 case DeclInfo::NotTemplate: 827 break; 828 case DeclInfo::Template: 829 Result << " templateKind=\"template\""; 830 break; 831 case DeclInfo::TemplateSpecialization: 832 Result << " templateKind=\"specialization\""; 833 break; 834 case DeclInfo::TemplatePartialSpecialization: 835 llvm_unreachable("partial specializations of functions " 836 "are not allowed in C++"); 837 } 838 if (DI->IsInstanceMethod) 839 Result << " isInstanceMethod=\"1\""; 840 if (DI->IsClassMethod) 841 Result << " isClassMethod=\"1\""; 842 break; 843 case DeclInfo::ClassKind: 844 RootEndTag = "</Class>"; 845 Result << "<Class"; 846 switch (DI->TemplateKind) { 847 case DeclInfo::NotTemplate: 848 break; 849 case DeclInfo::Template: 850 Result << " templateKind=\"template\""; 851 break; 852 case DeclInfo::TemplateSpecialization: 853 Result << " templateKind=\"specialization\""; 854 break; 855 case DeclInfo::TemplatePartialSpecialization: 856 Result << " templateKind=\"partialSpecialization\""; 857 break; 858 } 859 break; 860 case DeclInfo::VariableKind: 861 RootEndTag = "</Variable>"; 862 Result << "<Variable"; 863 break; 864 case DeclInfo::NamespaceKind: 865 RootEndTag = "</Namespace>"; 866 Result << "<Namespace"; 867 break; 868 case DeclInfo::TypedefKind: 869 RootEndTag = "</Typedef>"; 870 Result << "<Typedef"; 871 break; 872 case DeclInfo::EnumKind: 873 RootEndTag = "</Enum>"; 874 Result << "<Enum"; 875 break; 876 } 877 878 { 879 // Print line and column number. 880 SourceLocation Loc = DI->CurrentDecl->getLocation(); 881 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); 882 FileID FID = LocInfo.first; 883 unsigned FileOffset = LocInfo.second; 884 885 if (FID.isValid()) { 886 if (const FileEntry *FE = SM.getFileEntryForID(FID)) { 887 Result << " file=\""; 888 appendToResultWithXMLEscaping(FE->getName()); 889 Result << "\""; 890 } 891 Result << " line=\"" << SM.getLineNumber(FID, FileOffset) 892 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset) 893 << "\""; 894 } 895 } 896 897 // Finish the root tag. 898 Result << ">"; 899 900 bool FoundName = false; 901 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) { 902 if (DeclarationName DeclName = ND->getDeclName()) { 903 Result << "<Name>"; 904 std::string Name = DeclName.getAsString(); 905 appendToResultWithXMLEscaping(Name); 906 FoundName = true; 907 Result << "</Name>"; 908 } 909 } 910 if (!FoundName) 911 Result << "<Name><anonymous></Name>"; 912 913 { 914 // Print USR. 915 SmallString<128> USR; 916 generateUSRForDecl(DI->CommentDecl, USR); 917 if (!USR.empty()) { 918 Result << "<USR>"; 919 appendToResultWithXMLEscaping(USR); 920 Result << "</USR>"; 921 } 922 } 923 } else { 924 // No DeclInfo -- just emit some root tag and name tag. 925 RootEndTag = "</Other>"; 926 Result << "<Other><Name>unknown</Name>"; 927 } 928 929 if (Parts.Headerfile) { 930 Result << "<Headerfile>"; 931 visit(Parts.Headerfile); 932 Result << "</Headerfile>"; 933 } 934 935 { 936 // Pretty-print the declaration. 937 Result << "<Declaration>"; 938 SmallString<128> Declaration; 939 getSourceTextOfDeclaration(DI, Declaration); 940 formatTextOfDeclaration(DI, Declaration); 941 appendToResultWithXMLEscaping(Declaration); 942 Result << "</Declaration>"; 943 } 944 945 bool FirstParagraphIsBrief = false; 946 if (Parts.Brief) { 947 Result << "<Abstract>"; 948 visit(Parts.Brief); 949 Result << "</Abstract>"; 950 } else if (Parts.FirstParagraph) { 951 Result << "<Abstract>"; 952 visit(Parts.FirstParagraph); 953 Result << "</Abstract>"; 954 FirstParagraphIsBrief = true; 955 } 956 957 if (Parts.TParams.size() != 0) { 958 Result << "<TemplateParameters>"; 959 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i) 960 visit(Parts.TParams[i]); 961 Result << "</TemplateParameters>"; 962 } 963 964 if (Parts.Params.size() != 0) { 965 Result << "<Parameters>"; 966 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i) 967 visit(Parts.Params[i]); 968 Result << "</Parameters>"; 969 } 970 971 if (Parts.Exceptions.size() != 0) { 972 Result << "<Exceptions>"; 973 for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i) 974 visit(Parts.Exceptions[i]); 975 Result << "</Exceptions>"; 976 } 977 978 if (Parts.Returns.size() != 0) { 979 Result << "<ResultDiscussion>"; 980 for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i) 981 visit(Parts.Returns[i]); 982 Result << "</ResultDiscussion>"; 983 } 984 985 if (DI->CommentDecl->hasAttrs()) { 986 const AttrVec &Attrs = DI->CommentDecl->getAttrs(); 987 for (unsigned i = 0, e = Attrs.size(); i != e; i++) { 988 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]); 989 if (!AA) { 990 if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) { 991 if (DA->getMessage().empty()) 992 Result << "<Deprecated/>"; 993 else { 994 Result << "<Deprecated>"; 995 appendToResultWithXMLEscaping(DA->getMessage()); 996 Result << "</Deprecated>"; 997 } 998 } 999 else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) { 1000 if (UA->getMessage().empty()) 1001 Result << "<Unavailable/>"; 1002 else { 1003 Result << "<Unavailable>"; 1004 appendToResultWithXMLEscaping(UA->getMessage()); 1005 Result << "</Unavailable>"; 1006 } 1007 } 1008 continue; 1009 } 1010 1011 // 'availability' attribute. 1012 Result << "<Availability"; 1013 StringRef Distribution; 1014 if (AA->getPlatform()) { 1015 Distribution = AvailabilityAttr::getPrettyPlatformName( 1016 AA->getPlatform()->getName()); 1017 if (Distribution.empty()) 1018 Distribution = AA->getPlatform()->getName(); 1019 } 1020 Result << " distribution=\"" << Distribution << "\">"; 1021 VersionTuple IntroducedInVersion = AA->getIntroduced(); 1022 if (!IntroducedInVersion.empty()) { 1023 Result << "<IntroducedInVersion>" 1024 << IntroducedInVersion.getAsString() 1025 << "</IntroducedInVersion>"; 1026 } 1027 VersionTuple DeprecatedInVersion = AA->getDeprecated(); 1028 if (!DeprecatedInVersion.empty()) { 1029 Result << "<DeprecatedInVersion>" 1030 << DeprecatedInVersion.getAsString() 1031 << "</DeprecatedInVersion>"; 1032 } 1033 VersionTuple RemovedAfterVersion = AA->getObsoleted(); 1034 if (!RemovedAfterVersion.empty()) { 1035 Result << "<RemovedAfterVersion>" 1036 << RemovedAfterVersion.getAsString() 1037 << "</RemovedAfterVersion>"; 1038 } 1039 StringRef DeprecationSummary = AA->getMessage(); 1040 if (!DeprecationSummary.empty()) { 1041 Result << "<DeprecationSummary>"; 1042 appendToResultWithXMLEscaping(DeprecationSummary); 1043 Result << "</DeprecationSummary>"; 1044 } 1045 if (AA->getUnavailable()) 1046 Result << "<Unavailable/>"; 1047 Result << "</Availability>"; 1048 } 1049 } 1050 1051 { 1052 bool StartTagEmitted = false; 1053 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) { 1054 const Comment *C = Parts.MiscBlocks[i]; 1055 if (FirstParagraphIsBrief && C == Parts.FirstParagraph) 1056 continue; 1057 if (!StartTagEmitted) { 1058 Result << "<Discussion>"; 1059 StartTagEmitted = true; 1060 } 1061 visit(C); 1062 } 1063 if (StartTagEmitted) 1064 Result << "</Discussion>"; 1065 } 1066 1067 Result << RootEndTag; 1068 } 1069 1070 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) { 1071 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) { 1072 const char C = *I; 1073 switch (C) { 1074 case '&': 1075 Result << "&"; 1076 break; 1077 case '<': 1078 Result << "<"; 1079 break; 1080 case '>': 1081 Result << ">"; 1082 break; 1083 case '"': 1084 Result << """; 1085 break; 1086 case '\'': 1087 Result << "'"; 1088 break; 1089 default: 1090 Result << C; 1091 break; 1092 } 1093 } 1094 } 1095 1096 void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) { 1097 if (S.empty()) 1098 return; 1099 1100 Result << "<![CDATA["; 1101 while (!S.empty()) { 1102 size_t Pos = S.find("]]>"); 1103 if (Pos == 0) { 1104 Result << "]]]]><![CDATA[>"; 1105 S = S.drop_front(3); 1106 continue; 1107 } 1108 if (Pos == StringRef::npos) 1109 Pos = S.size(); 1110 1111 Result << S.substr(0, Pos); 1112 1113 S = S.drop_front(Pos); 1114 } 1115 Result << "]]>"; 1116 } 1117 1118 CommentToXMLConverter::CommentToXMLConverter() {} 1119 CommentToXMLConverter::~CommentToXMLConverter() {} 1120 1121 void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC, 1122 SmallVectorImpl<char> &HTML, 1123 const ASTContext &Context) { 1124 CommentASTToHTMLConverter Converter(FC, HTML, 1125 Context.getCommentCommandTraits()); 1126 Converter.visit(FC); 1127 } 1128 1129 void CommentToXMLConverter::convertHTMLTagNodeToText( 1130 const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text, 1131 const ASTContext &Context) { 1132 CommentASTToHTMLConverter Converter(nullptr, Text, 1133 Context.getCommentCommandTraits()); 1134 Converter.visit(HTC); 1135 } 1136 1137 void CommentToXMLConverter::convertCommentToXML(const FullComment *FC, 1138 SmallVectorImpl<char> &XML, 1139 const ASTContext &Context) { 1140 CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(), 1141 Context.getSourceManager()); 1142 Converter.visit(FC); 1143 } 1144