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