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