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