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