CXComment.cpp revision 2ff84b514e53f4273c7067a5aade680a155a9045
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 18#include "clang/AST/CommentVisitor.h" 19 20#include "llvm/Support/ErrorHandling.h" 21#include "llvm/Support/raw_ostream.h" 22 23#include <climits> 24 25using namespace clang; 26using namespace clang::cxstring; 27using namespace clang::comments; 28using namespace clang::cxcomment; 29 30extern "C" { 31 32enum CXCommentKind clang_Comment_getKind(CXComment CXC) { 33 const Comment *C = getASTNode(CXC); 34 if (!C) 35 return CXComment_Null; 36 37 switch (C->getCommentKind()) { 38 case Comment::NoCommentKind: 39 return CXComment_Null; 40 41 case Comment::TextCommentKind: 42 return CXComment_Text; 43 44 case Comment::InlineCommandCommentKind: 45 return CXComment_InlineCommand; 46 47 case Comment::HTMLStartTagCommentKind: 48 return CXComment_HTMLStartTag; 49 50 case Comment::HTMLEndTagCommentKind: 51 return CXComment_HTMLEndTag; 52 53 case Comment::ParagraphCommentKind: 54 return CXComment_Paragraph; 55 56 case Comment::BlockCommandCommentKind: 57 return CXComment_BlockCommand; 58 59 case Comment::ParamCommandCommentKind: 60 return CXComment_ParamCommand; 61 62 case Comment::TParamCommandCommentKind: 63 return CXComment_TParamCommand; 64 65 case Comment::VerbatimBlockCommentKind: 66 return CXComment_VerbatimBlockCommand; 67 68 case Comment::VerbatimBlockLineCommentKind: 69 return CXComment_VerbatimBlockLine; 70 71 case Comment::VerbatimLineCommentKind: 72 return CXComment_VerbatimLine; 73 74 case Comment::FullCommentKind: 75 return CXComment_FullComment; 76 } 77 llvm_unreachable("unknown CommentKind"); 78} 79 80unsigned clang_Comment_getNumChildren(CXComment CXC) { 81 const Comment *C = getASTNode(CXC); 82 if (!C) 83 return 0; 84 85 return C->child_count(); 86} 87 88CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) { 89 const Comment *C = getASTNode(CXC); 90 if (!C || ChildIdx >= C->child_count()) 91 return createCXComment(NULL); 92 93 return createCXComment(*(C->child_begin() + ChildIdx)); 94} 95 96unsigned clang_Comment_isWhitespace(CXComment CXC) { 97 const Comment *C = getASTNode(CXC); 98 if (!C) 99 return false; 100 101 if (const TextComment *TC = dyn_cast<TextComment>(C)) 102 return TC->isWhitespace(); 103 104 if (const ParagraphComment *PC = dyn_cast<ParagraphComment>(C)) 105 return PC->isWhitespace(); 106 107 return false; 108} 109 110unsigned clang_InlineContentComment_hasTrailingNewline(CXComment CXC) { 111 const InlineContentComment *ICC = getASTNodeAs<InlineContentComment>(CXC); 112 if (!ICC) 113 return false; 114 115 return ICC->hasTrailingNewline(); 116} 117 118CXString clang_TextComment_getText(CXComment CXC) { 119 const TextComment *TC = getASTNodeAs<TextComment>(CXC); 120 if (!TC) 121 return createCXString((const char *) 0); 122 123 return createCXString(TC->getText(), /*DupString=*/ false); 124} 125 126CXString clang_InlineCommandComment_getCommandName(CXComment CXC) { 127 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC); 128 if (!ICC) 129 return createCXString((const char *) 0); 130 131 return createCXString(ICC->getCommandName(), /*DupString=*/ false); 132} 133 134enum CXCommentInlineCommandRenderKind 135clang_InlineCommandComment_getRenderKind(CXComment CXC) { 136 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC); 137 if (!ICC) 138 return CXCommentInlineCommandRenderKind_Normal; 139 140 switch (ICC->getRenderKind()) { 141 case InlineCommandComment::RenderNormal: 142 return CXCommentInlineCommandRenderKind_Normal; 143 144 case InlineCommandComment::RenderBold: 145 return CXCommentInlineCommandRenderKind_Bold; 146 147 case InlineCommandComment::RenderMonospaced: 148 return CXCommentInlineCommandRenderKind_Monospaced; 149 150 case InlineCommandComment::RenderEmphasized: 151 return CXCommentInlineCommandRenderKind_Emphasized; 152 } 153 llvm_unreachable("unknown InlineCommandComment::RenderKind"); 154} 155 156unsigned clang_InlineCommandComment_getNumArgs(CXComment CXC) { 157 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC); 158 if (!ICC) 159 return 0; 160 161 return ICC->getNumArgs(); 162} 163 164CXString clang_InlineCommandComment_getArgText(CXComment CXC, 165 unsigned ArgIdx) { 166 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC); 167 if (!ICC || ArgIdx >= ICC->getNumArgs()) 168 return createCXString((const char *) 0); 169 170 return createCXString(ICC->getArgText(ArgIdx), /*DupString=*/ false); 171} 172 173CXString clang_HTMLTagComment_getTagName(CXComment CXC) { 174 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC); 175 if (!HTC) 176 return createCXString((const char *) 0); 177 178 return createCXString(HTC->getTagName(), /*DupString=*/ false); 179} 180 181unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment CXC) { 182 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC); 183 if (!HST) 184 return false; 185 186 return HST->isSelfClosing(); 187} 188 189unsigned clang_HTMLStartTag_getNumAttrs(CXComment CXC) { 190 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC); 191 if (!HST) 192 return 0; 193 194 return HST->getNumAttrs(); 195} 196 197CXString clang_HTMLStartTag_getAttrName(CXComment CXC, unsigned AttrIdx) { 198 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC); 199 if (!HST || AttrIdx >= HST->getNumAttrs()) 200 return createCXString((const char *) 0); 201 202 return createCXString(HST->getAttr(AttrIdx).Name, /*DupString=*/ false); 203} 204 205CXString clang_HTMLStartTag_getAttrValue(CXComment CXC, unsigned AttrIdx) { 206 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC); 207 if (!HST || AttrIdx >= HST->getNumAttrs()) 208 return createCXString((const char *) 0); 209 210 return createCXString(HST->getAttr(AttrIdx).Value, /*DupString=*/ false); 211} 212 213CXString clang_BlockCommandComment_getCommandName(CXComment CXC) { 214 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC); 215 if (!BCC) 216 return createCXString((const char *) 0); 217 218 return createCXString(BCC->getCommandName(), /*DupString=*/ false); 219} 220 221unsigned clang_BlockCommandComment_getNumArgs(CXComment CXC) { 222 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC); 223 if (!BCC) 224 return 0; 225 226 return BCC->getNumArgs(); 227} 228 229CXString clang_BlockCommandComment_getArgText(CXComment CXC, 230 unsigned ArgIdx) { 231 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC); 232 if (!BCC || ArgIdx >= BCC->getNumArgs()) 233 return createCXString((const char *) 0); 234 235 return createCXString(BCC->getArgText(ArgIdx), /*DupString=*/ false); 236} 237 238CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) { 239 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC); 240 if (!BCC) 241 return createCXComment(NULL); 242 243 return createCXComment(BCC->getParagraph()); 244} 245 246CXString clang_ParamCommandComment_getParamName(CXComment CXC) { 247 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC); 248 if (!PCC || !PCC->hasParamName()) 249 return createCXString((const char *) 0); 250 251 return createCXString(PCC->getParamName(), /*DupString=*/ false); 252} 253 254unsigned clang_ParamCommandComment_isParamIndexValid(CXComment CXC) { 255 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC); 256 if (!PCC) 257 return false; 258 259 return PCC->isParamIndexValid(); 260} 261 262unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) { 263 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC); 264 if (!PCC || !PCC->isParamIndexValid()) 265 return ParamCommandComment::InvalidParamIndex; 266 267 return PCC->getParamIndex(); 268} 269 270unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment CXC) { 271 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC); 272 if (!PCC) 273 return false; 274 275 return PCC->isDirectionExplicit(); 276} 277 278enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection( 279 CXComment CXC) { 280 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC); 281 if (!PCC) 282 return CXCommentParamPassDirection_In; 283 284 switch (PCC->getDirection()) { 285 case ParamCommandComment::In: 286 return CXCommentParamPassDirection_In; 287 288 case ParamCommandComment::Out: 289 return CXCommentParamPassDirection_Out; 290 291 case ParamCommandComment::InOut: 292 return CXCommentParamPassDirection_InOut; 293 } 294 llvm_unreachable("unknown ParamCommandComment::PassDirection"); 295} 296 297CXString clang_TParamCommandComment_getParamName(CXComment CXC) { 298 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC); 299 if (!TPCC || !TPCC->hasParamName()) 300 return createCXString((const char *) 0); 301 302 return createCXString(TPCC->getParamName(), /*DupString=*/ false); 303} 304 305unsigned clang_TParamCommandComment_isParamPositionValid(CXComment CXC) { 306 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC); 307 if (!TPCC) 308 return false; 309 310 return TPCC->isPositionValid(); 311} 312 313unsigned clang_TParamCommandComment_getDepth(CXComment CXC) { 314 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC); 315 if (!TPCC || !TPCC->isPositionValid()) 316 return 0; 317 318 return TPCC->getDepth(); 319} 320 321unsigned clang_TParamCommandComment_getIndex(CXComment CXC, unsigned Depth) { 322 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC); 323 if (!TPCC || !TPCC->isPositionValid() || Depth >= TPCC->getDepth()) 324 return 0; 325 326 return TPCC->getIndex(Depth); 327} 328 329CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) { 330 const VerbatimBlockLineComment *VBL = 331 getASTNodeAs<VerbatimBlockLineComment>(CXC); 332 if (!VBL) 333 return createCXString((const char *) 0); 334 335 return createCXString(VBL->getText(), /*DupString=*/ false); 336} 337 338CXString clang_VerbatimLineComment_getText(CXComment CXC) { 339 const VerbatimLineComment *VLC = getASTNodeAs<VerbatimLineComment>(CXC); 340 if (!VLC) 341 return createCXString((const char *) 0); 342 343 return createCXString(VLC->getText(), /*DupString=*/ false); 344} 345 346} // end extern "C" 347 348//===----------------------------------------------------------------------===// 349// Helpers for converting comment AST to HTML. 350//===----------------------------------------------------------------------===// 351 352namespace { 353 354/// This comparison will sort parameters with valid index by index and 355/// invalid (unresolved) parameters last. 356class ParamCommandCommentCompareIndex { 357public: 358 bool operator()(const ParamCommandComment *LHS, 359 const ParamCommandComment *RHS) const { 360 unsigned LHSIndex = UINT_MAX; 361 unsigned RHSIndex = UINT_MAX; 362 if (LHS->isParamIndexValid()) 363 LHSIndex = LHS->getParamIndex(); 364 if (RHS->isParamIndexValid()) 365 RHSIndex = RHS->getParamIndex(); 366 367 return LHSIndex < RHSIndex; 368 } 369}; 370 371/// This comparison will sort template parameters in the following order: 372/// \li real template parameters (depth = 1) in index order; 373/// \li all other names (depth > 1); 374/// \li unresolved names. 375class TParamCommandCommentComparePosition { 376public: 377 bool operator()(const TParamCommandComment *LHS, 378 const TParamCommandComment *RHS) const { 379 // Sort unresolved names last. 380 if (!LHS->isPositionValid()) 381 return false; 382 if (!RHS->isPositionValid()) 383 return true; 384 385 if (LHS->getDepth() > 1) 386 return false; 387 if (RHS->getDepth() > 1) 388 return true; 389 390 // Sort template parameters in index order. 391 if (LHS->getDepth() == 1 && RHS->getDepth() == 1) 392 return LHS->getIndex(0) < RHS->getIndex(0); 393 394 // Leave all other names in source order. 395 return true; 396 } 397}; 398 399/// Separate parts of a FullComment. 400struct FullCommentParts { 401 /// Take a full comment apart and initialize members accordingly. 402 FullCommentParts(const FullComment *C); 403 404 const BlockContentComment *Brief; 405 const ParagraphComment *FirstParagraph; 406 const BlockCommandComment *Returns; 407 SmallVector<const ParamCommandComment *, 8> Params; 408 SmallVector<const TParamCommandComment *, 4> TParams; 409 SmallVector<const BlockContentComment *, 8> MiscBlocks; 410}; 411 412FullCommentParts::FullCommentParts(const FullComment *C) : 413 Brief(NULL), FirstParagraph(NULL), Returns(NULL) { 414 for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); 415 I != E; ++I) { 416 const Comment *Child = *I; 417 if (!Child) 418 continue; 419 switch (Child->getCommentKind()) { 420 case Comment::NoCommentKind: 421 continue; 422 423 case Comment::ParagraphCommentKind: { 424 const ParagraphComment *PC = cast<ParagraphComment>(Child); 425 if (PC->isWhitespace()) 426 break; 427 if (!FirstParagraph) 428 FirstParagraph = PC; 429 430 MiscBlocks.push_back(PC); 431 break; 432 } 433 434 case Comment::BlockCommandCommentKind: { 435 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child); 436 StringRef CommandName = BCC->getCommandName(); 437 if (!Brief && (CommandName == "brief" || CommandName == "short")) { 438 Brief = BCC; 439 break; 440 } 441 if (!Returns && (CommandName == "returns" || CommandName == "return")) { 442 Returns = BCC; 443 break; 444 } 445 MiscBlocks.push_back(BCC); 446 break; 447 } 448 449 case Comment::ParamCommandCommentKind: { 450 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child); 451 if (!PCC->hasParamName()) 452 break; 453 454 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph()) 455 break; 456 457 Params.push_back(PCC); 458 break; 459 } 460 461 case Comment::TParamCommandCommentKind: { 462 const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child); 463 if (!TPCC->hasParamName()) 464 break; 465 466 if (!TPCC->hasNonWhitespaceParagraph()) 467 break; 468 469 TParams.push_back(TPCC); 470 break; 471 } 472 473 case Comment::VerbatimBlockCommentKind: 474 case Comment::VerbatimLineCommentKind: 475 MiscBlocks.push_back(cast<BlockCommandComment>(Child)); 476 break; 477 478 case Comment::TextCommentKind: 479 case Comment::InlineCommandCommentKind: 480 case Comment::HTMLStartTagCommentKind: 481 case Comment::HTMLEndTagCommentKind: 482 case Comment::VerbatimBlockLineCommentKind: 483 case Comment::FullCommentKind: 484 llvm_unreachable("AST node of this kind can't be a child of " 485 "a FullComment"); 486 } 487 } 488 489 // Sort params in order they are declared in the function prototype. 490 // Unresolved parameters are put at the end of the list in the same order 491 // they were seen in the comment. 492 std::stable_sort(Params.begin(), Params.end(), 493 ParamCommandCommentCompareIndex()); 494 495 std::stable_sort(TParams.begin(), TParams.end(), 496 TParamCommandCommentComparePosition()); 497} 498 499void PrintHTMLStartTagComment(const HTMLStartTagComment *C, 500 llvm::raw_svector_ostream &Result) { 501 Result << "<" << C->getTagName(); 502 503 if (C->getNumAttrs() != 0) { 504 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) { 505 Result << " "; 506 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i); 507 Result << Attr.Name; 508 if (!Attr.Value.empty()) 509 Result << "=\"" << Attr.Value << "\""; 510 } 511 } 512 513 if (!C->isSelfClosing()) 514 Result << ">"; 515 else 516 Result << "/>"; 517} 518 519class CommentASTToHTMLConverter : 520 public ConstCommentVisitor<CommentASTToHTMLConverter> { 521public: 522 /// \param Str accumulator for HTML. 523 CommentASTToHTMLConverter(SmallVectorImpl<char> &Str) : Result(Str) { } 524 525 // Inline content. 526 void visitTextComment(const TextComment *C); 527 void visitInlineCommandComment(const InlineCommandComment *C); 528 void visitHTMLStartTagComment(const HTMLStartTagComment *C); 529 void visitHTMLEndTagComment(const HTMLEndTagComment *C); 530 531 // Block content. 532 void visitParagraphComment(const ParagraphComment *C); 533 void visitBlockCommandComment(const BlockCommandComment *C); 534 void visitParamCommandComment(const ParamCommandComment *C); 535 void visitTParamCommandComment(const TParamCommandComment *C); 536 void visitVerbatimBlockComment(const VerbatimBlockComment *C); 537 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); 538 void visitVerbatimLineComment(const VerbatimLineComment *C); 539 540 void visitFullComment(const FullComment *C); 541 542 // Helpers. 543 544 /// Convert a paragraph that is not a block by itself (an argument to some 545 /// command). 546 void visitNonStandaloneParagraphComment(const ParagraphComment *C); 547 548 void appendToResultWithHTMLEscaping(StringRef S); 549 550private: 551 /// Output stream for HTML. 552 llvm::raw_svector_ostream Result; 553}; 554} // end unnamed namespace 555 556void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) { 557 appendToResultWithHTMLEscaping(C->getText()); 558} 559 560void CommentASTToHTMLConverter::visitInlineCommandComment( 561 const InlineCommandComment *C) { 562 // Nothing to render if no arguments supplied. 563 if (C->getNumArgs() == 0) 564 return; 565 566 // Nothing to render if argument is empty. 567 StringRef Arg0 = C->getArgText(0); 568 if (Arg0.empty()) 569 return; 570 571 switch (C->getRenderKind()) { 572 case InlineCommandComment::RenderNormal: 573 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) { 574 appendToResultWithHTMLEscaping(C->getArgText(i)); 575 Result << " "; 576 } 577 return; 578 579 case InlineCommandComment::RenderBold: 580 assert(C->getNumArgs() == 1); 581 Result << "<b>"; 582 appendToResultWithHTMLEscaping(Arg0); 583 Result << "</b>"; 584 return; 585 case InlineCommandComment::RenderMonospaced: 586 assert(C->getNumArgs() == 1); 587 Result << "<tt>"; 588 appendToResultWithHTMLEscaping(Arg0); 589 Result<< "</tt>"; 590 return; 591 case InlineCommandComment::RenderEmphasized: 592 assert(C->getNumArgs() == 1); 593 Result << "<em>"; 594 appendToResultWithHTMLEscaping(Arg0); 595 Result << "</em>"; 596 return; 597 } 598} 599 600void CommentASTToHTMLConverter::visitHTMLStartTagComment( 601 const HTMLStartTagComment *C) { 602 PrintHTMLStartTagComment(C, Result); 603} 604 605void CommentASTToHTMLConverter::visitHTMLEndTagComment( 606 const HTMLEndTagComment *C) { 607 Result << "</" << C->getTagName() << ">"; 608} 609 610void CommentASTToHTMLConverter::visitParagraphComment( 611 const ParagraphComment *C) { 612 if (C->isWhitespace()) 613 return; 614 615 Result << "<p>"; 616 for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); 617 I != E; ++I) { 618 visit(*I); 619 } 620 Result << "</p>"; 621} 622 623void CommentASTToHTMLConverter::visitBlockCommandComment( 624 const BlockCommandComment *C) { 625 StringRef CommandName = C->getCommandName(); 626 if (CommandName == "brief" || CommandName == "short") { 627 Result << "<p class=\"para-brief\">"; 628 visitNonStandaloneParagraphComment(C->getParagraph()); 629 Result << "</p>"; 630 return; 631 } 632 if (CommandName == "returns" || CommandName == "return" || 633 CommandName == "result") { 634 Result << "<p class=\"para-returns\">" 635 "<span class=\"word-returns\">Returns</span> "; 636 visitNonStandaloneParagraphComment(C->getParagraph()); 637 Result << "</p>"; 638 return; 639 } 640 // We don't know anything about this command. Just render the paragraph. 641 visit(C->getParagraph()); 642} 643 644void CommentASTToHTMLConverter::visitParamCommandComment( 645 const ParamCommandComment *C) { 646 if (C->isParamIndexValid()) { 647 Result << "<dt class=\"param-name-index-" 648 << C->getParamIndex() 649 << "\">"; 650 } else 651 Result << "<dt class=\"param-name-index-invalid\">"; 652 653 appendToResultWithHTMLEscaping(C->getParamName()); 654 Result << "</dt>"; 655 656 if (C->isParamIndexValid()) { 657 Result << "<dd class=\"param-descr-index-" 658 << C->getParamIndex() 659 << "\">"; 660 } else 661 Result << "<dd class=\"param-descr-index-invalid\">"; 662 663 visitNonStandaloneParagraphComment(C->getParagraph()); 664 Result << "</dd>"; 665} 666 667void CommentASTToHTMLConverter::visitTParamCommandComment( 668 const TParamCommandComment *C) { 669 if (C->isPositionValid()) { 670 if (C->getDepth() == 1) 671 Result << "<dt class=\"taram-name-index-" 672 << C->getIndex(0) 673 << "\">"; 674 else 675 Result << "<dt class=\"taram-name-index-other\">"; 676 } else 677 Result << "<dt class=\"tparam-name-index-invalid\">"; 678 679 appendToResultWithHTMLEscaping(C->getParamName()); 680 Result << "</dt>"; 681 682 if (C->isPositionValid()) { 683 if (C->getDepth() == 1) 684 Result << "<dd class=\"tparam-descr-index-" 685 << C->getIndex(0) 686 << "\">"; 687 else 688 Result << "<dd class=\"tparam-descr-index-other\">"; 689 } else 690 Result << "<dd class=\"tparam-descr-index-invalid\">"; 691 692 visitNonStandaloneParagraphComment(C->getParagraph()); 693 Result << "</dd>"; 694} 695 696void CommentASTToHTMLConverter::visitVerbatimBlockComment( 697 const VerbatimBlockComment *C) { 698 unsigned NumLines = C->getNumLines(); 699 if (NumLines == 0) 700 return; 701 702 Result << "<pre>"; 703 for (unsigned i = 0; i != NumLines; ++i) { 704 appendToResultWithHTMLEscaping(C->getText(i)); 705 if (i + 1 != NumLines) 706 Result << '\n'; 707 } 708 Result << "</pre>"; 709} 710 711void CommentASTToHTMLConverter::visitVerbatimBlockLineComment( 712 const VerbatimBlockLineComment *C) { 713 llvm_unreachable("should not see this AST node"); 714} 715 716void CommentASTToHTMLConverter::visitVerbatimLineComment( 717 const VerbatimLineComment *C) { 718 Result << "<pre>"; 719 appendToResultWithHTMLEscaping(C->getText()); 720 Result << "</pre>"; 721} 722 723void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) { 724 FullCommentParts Parts(C); 725 726 bool FirstParagraphIsBrief = false; 727 if (Parts.Brief) 728 visit(Parts.Brief); 729 else if (Parts.FirstParagraph) { 730 Result << "<p class=\"para-brief\">"; 731 visitNonStandaloneParagraphComment(Parts.FirstParagraph); 732 Result << "</p>"; 733 FirstParagraphIsBrief = true; 734 } 735 736 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) { 737 const Comment *C = Parts.MiscBlocks[i]; 738 if (FirstParagraphIsBrief && C == Parts.FirstParagraph) 739 continue; 740 visit(C); 741 } 742 743 if (Parts.TParams.size() != 0) { 744 Result << "<dl>"; 745 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i) 746 visit(Parts.TParams[i]); 747 Result << "</dl>"; 748 } 749 750 if (Parts.Params.size() != 0) { 751 Result << "<dl>"; 752 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i) 753 visit(Parts.Params[i]); 754 Result << "</dl>"; 755 } 756 757 if (Parts.Returns) 758 visit(Parts.Returns); 759 760 Result.flush(); 761} 762 763void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment( 764 const ParagraphComment *C) { 765 if (!C) 766 return; 767 768 for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); 769 I != E; ++I) { 770 visit(*I); 771 } 772} 773 774void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) { 775 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) { 776 const char C = *I; 777 switch (C) { 778 case '&': 779 Result << "&"; 780 break; 781 case '<': 782 Result << "<"; 783 break; 784 case '>': 785 Result << ">"; 786 break; 787 case '"': 788 Result << """; 789 break; 790 case '\'': 791 Result << "'"; 792 break; 793 case '/': 794 Result << "/"; 795 break; 796 default: 797 Result << C; 798 break; 799 } 800 } 801} 802 803extern "C" { 804 805CXString clang_HTMLTagComment_getAsString(CXComment CXC) { 806 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC); 807 if (!HTC) 808 return createCXString((const char *) 0); 809 810 SmallString<128> HTML; 811 CommentASTToHTMLConverter Converter(HTML); 812 Converter.visit(HTC); 813 return createCXString(HTML.str(), /* DupString = */ true); 814} 815 816CXString clang_FullComment_getAsHTML(CXComment CXC) { 817 const FullComment *FC = getASTNodeAs<FullComment>(CXC); 818 if (!FC) 819 return createCXString((const char *) 0); 820 821 SmallString<1024> HTML; 822 CommentASTToHTMLConverter Converter(HTML); 823 Converter.visit(FC); 824 return createCXString(HTML.str(), /* DupString = */ true); 825} 826 827} // end extern "C" 828 829