CXComment.cpp revision e5db09cf6772332c14dc7dc541de53f918f89125
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::VerbatimBlockCommentKind: 63 return CXComment_VerbatimBlockCommand; 64 65 case Comment::VerbatimBlockLineCommentKind: 66 return CXComment_VerbatimBlockLine; 67 68 case Comment::VerbatimLineCommentKind: 69 return CXComment_VerbatimLine; 70 71 case Comment::FullCommentKind: 72 return CXComment_FullComment; 73 } 74 llvm_unreachable("unknown CommentKind"); 75} 76 77unsigned clang_Comment_getNumChildren(CXComment CXC) { 78 const Comment *C = getASTNode(CXC); 79 if (!C) 80 return 0; 81 82 return C->child_count(); 83} 84 85CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) { 86 const Comment *C = getASTNode(CXC); 87 if (!C || ChildIdx >= C->child_count()) 88 return createCXComment(NULL); 89 90 return createCXComment(*(C->child_begin() + ChildIdx)); 91} 92 93unsigned clang_Comment_isWhitespace(CXComment CXC) { 94 const Comment *C = getASTNode(CXC); 95 if (!C) 96 return false; 97 98 if (const TextComment *TC = dyn_cast<TextComment>(C)) 99 return TC->isWhitespace(); 100 101 if (const ParagraphComment *PC = dyn_cast<ParagraphComment>(C)) 102 return PC->isWhitespace(); 103 104 return false; 105} 106 107unsigned clang_InlineContentComment_hasTrailingNewline(CXComment CXC) { 108 const InlineContentComment *ICC = getASTNodeAs<InlineContentComment>(CXC); 109 if (!ICC) 110 return false; 111 112 return ICC->hasTrailingNewline(); 113} 114 115CXString clang_TextComment_getText(CXComment CXC) { 116 const TextComment *TC = getASTNodeAs<TextComment>(CXC); 117 if (!TC) 118 return createCXString((const char *) 0); 119 120 return createCXString(TC->getText(), /*DupString=*/ false); 121} 122 123CXString clang_InlineCommandComment_getCommandName(CXComment CXC) { 124 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC); 125 if (!ICC) 126 return createCXString((const char *) 0); 127 128 return createCXString(ICC->getCommandName(), /*DupString=*/ false); 129} 130 131enum CXCommentInlineCommandRenderKind 132clang_InlineCommandComment_getRenderKind(CXComment CXC) { 133 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC); 134 if (!ICC) 135 return CXCommentInlineCommandRenderKind_Normal; 136 137 switch (ICC->getRenderKind()) { 138 case InlineCommandComment::RenderNormal: 139 return CXCommentInlineCommandRenderKind_Normal; 140 141 case InlineCommandComment::RenderBold: 142 return CXCommentInlineCommandRenderKind_Bold; 143 144 case InlineCommandComment::RenderMonospaced: 145 return CXCommentInlineCommandRenderKind_Monospaced; 146 147 case InlineCommandComment::RenderEmphasized: 148 return CXCommentInlineCommandRenderKind_Emphasized; 149 } 150 llvm_unreachable("unknown InlineCommandComment::RenderKind"); 151} 152 153unsigned clang_InlineCommandComment_getNumArgs(CXComment CXC) { 154 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC); 155 if (!ICC) 156 return 0; 157 158 return ICC->getNumArgs(); 159} 160 161CXString clang_InlineCommandComment_getArgText(CXComment CXC, 162 unsigned ArgIdx) { 163 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC); 164 if (!ICC || ArgIdx >= ICC->getNumArgs()) 165 return createCXString((const char *) 0); 166 167 return createCXString(ICC->getArgText(ArgIdx), /*DupString=*/ false); 168} 169 170CXString clang_HTMLTagComment_getTagName(CXComment CXC) { 171 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC); 172 if (!HTC) 173 return createCXString((const char *) 0); 174 175 return createCXString(HTC->getTagName(), /*DupString=*/ false); 176} 177 178unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment CXC) { 179 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC); 180 if (!HST) 181 return false; 182 183 return HST->isSelfClosing(); 184} 185 186unsigned clang_HTMLStartTag_getNumAttrs(CXComment CXC) { 187 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC); 188 if (!HST) 189 return 0; 190 191 return HST->getNumAttrs(); 192} 193 194CXString clang_HTMLStartTag_getAttrName(CXComment CXC, unsigned AttrIdx) { 195 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC); 196 if (!HST || AttrIdx >= HST->getNumAttrs()) 197 return createCXString((const char *) 0); 198 199 return createCXString(HST->getAttr(AttrIdx).Name, /*DupString=*/ false); 200} 201 202CXString clang_HTMLStartTag_getAttrValue(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).Value, /*DupString=*/ false); 208} 209 210CXString clang_BlockCommandComment_getCommandName(CXComment CXC) { 211 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC); 212 if (!BCC) 213 return createCXString((const char *) 0); 214 215 return createCXString(BCC->getCommandName(), /*DupString=*/ false); 216} 217 218unsigned clang_BlockCommandComment_getNumArgs(CXComment CXC) { 219 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC); 220 if (!BCC) 221 return 0; 222 223 return BCC->getNumArgs(); 224} 225 226CXString clang_BlockCommandComment_getArgText(CXComment CXC, 227 unsigned ArgIdx) { 228 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC); 229 if (!BCC || ArgIdx >= BCC->getNumArgs()) 230 return createCXString((const char *) 0); 231 232 return createCXString(BCC->getArgText(ArgIdx), /*DupString=*/ false); 233} 234 235CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) { 236 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC); 237 if (!BCC) 238 return createCXComment(NULL); 239 240 return createCXComment(BCC->getParagraph()); 241} 242 243CXString clang_ParamCommandComment_getParamName(CXComment CXC) { 244 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC); 245 if (!PCC || !PCC->hasParamName()) 246 return createCXString((const char *) 0); 247 248 return createCXString(PCC->getParamName(), /*DupString=*/ false); 249} 250 251unsigned clang_ParamCommandComment_isParamIndexValid(CXComment CXC) { 252 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC); 253 if (!PCC) 254 return false; 255 256 return PCC->isParamIndexValid(); 257} 258 259unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) { 260 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC); 261 if (!PCC || !PCC->isParamIndexValid()) 262 return ParamCommandComment::InvalidParamIndex; 263 264 return PCC->getParamIndex(); 265} 266 267unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment CXC) { 268 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC); 269 if (!PCC) 270 return false; 271 272 return PCC->isDirectionExplicit(); 273} 274 275enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection( 276 CXComment CXC) { 277 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC); 278 if (!PCC) 279 return CXCommentParamPassDirection_In; 280 281 switch (PCC->getDirection()) { 282 case ParamCommandComment::In: 283 return CXCommentParamPassDirection_In; 284 285 case ParamCommandComment::Out: 286 return CXCommentParamPassDirection_Out; 287 288 case ParamCommandComment::InOut: 289 return CXCommentParamPassDirection_InOut; 290 } 291 llvm_unreachable("unknown ParamCommandComment::PassDirection"); 292} 293 294CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) { 295 const VerbatimBlockLineComment *VBL = 296 getASTNodeAs<VerbatimBlockLineComment>(CXC); 297 if (!VBL) 298 return createCXString((const char *) 0); 299 300 return createCXString(VBL->getText(), /*DupString=*/ false); 301} 302 303CXString clang_VerbatimLineComment_getText(CXComment CXC) { 304 const VerbatimLineComment *VLC = getASTNodeAs<VerbatimLineComment>(CXC); 305 if (!VLC) 306 return createCXString((const char *) 0); 307 308 return createCXString(VLC->getText(), /*DupString=*/ false); 309} 310 311} // end extern "C" 312 313//===----------------------------------------------------------------------===// 314// Helpers for converting comment AST to HTML. 315//===----------------------------------------------------------------------===// 316 317namespace { 318 319/// This comparison will sort parameters with valid index by index and 320/// invalid (unresolved) parameters last. 321class ParamCommandCommentCompareIndex { 322public: 323 bool operator()(const ParamCommandComment *LHS, 324 const ParamCommandComment *RHS) const { 325 unsigned LHSIndex = UINT_MAX; 326 unsigned RHSIndex = UINT_MAX; 327 if (LHS->isParamIndexValid()) 328 LHSIndex = LHS->getParamIndex(); 329 if (RHS->isParamIndexValid()) 330 RHSIndex = RHS->getParamIndex(); 331 332 return LHSIndex < RHSIndex; 333 } 334}; 335 336class CommentASTToHTMLConverter : 337 public ConstCommentVisitor<CommentASTToHTMLConverter> { 338public: 339 /// \param Str accumulator for HTML. 340 CommentASTToHTMLConverter(SmallVectorImpl<char> &Str) : Result(Str) { } 341 342 // Inline content. 343 void visitTextComment(const TextComment *C); 344 void visitInlineCommandComment(const InlineCommandComment *C); 345 void visitHTMLStartTagComment(const HTMLStartTagComment *C); 346 void visitHTMLEndTagComment(const HTMLEndTagComment *C); 347 348 // Block content. 349 void visitParagraphComment(const ParagraphComment *C); 350 void visitBlockCommandComment(const BlockCommandComment *C); 351 void visitParamCommandComment(const ParamCommandComment *C); 352 void visitVerbatimBlockComment(const VerbatimBlockComment *C); 353 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); 354 void visitVerbatimLineComment(const VerbatimLineComment *C); 355 356 void visitFullComment(const FullComment *C); 357 358 // Helpers. 359 360 /// Convert a paragraph that is not a block by itself (an argument to some 361 /// command). 362 void visitNonStandaloneParagraphComment(const ParagraphComment *C); 363 364 void appendToResultWithHTMLEscaping(StringRef S); 365 366private: 367 /// Output stream for HTML. 368 llvm::raw_svector_ostream Result; 369}; 370} // end unnamed namespace 371 372void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) { 373 appendToResultWithHTMLEscaping(C->getText()); 374} 375 376void CommentASTToHTMLConverter::visitInlineCommandComment( 377 const InlineCommandComment *C) { 378 // Nothing to render if no arguments supplied. 379 if (C->getNumArgs() == 0) 380 return; 381 382 // Nothing to render if argument is empty. 383 StringRef Arg0 = C->getArgText(0); 384 if (Arg0.empty()) 385 return; 386 387 switch (C->getRenderKind()) { 388 case InlineCommandComment::RenderNormal: 389 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) 390 Result << C->getArgText(i) << " "; 391 return; 392 393 case InlineCommandComment::RenderBold: 394 assert(C->getNumArgs() == 1); 395 Result << "<b>" << Arg0 << "</b>"; 396 return; 397 case InlineCommandComment::RenderMonospaced: 398 assert(C->getNumArgs() == 1); 399 Result << "<tt>" << Arg0 << "</tt>"; 400 return; 401 case InlineCommandComment::RenderEmphasized: 402 assert(C->getNumArgs() == 1); 403 Result << "<em>" << Arg0 << "</em>"; 404 return; 405 } 406} 407 408void CommentASTToHTMLConverter::visitHTMLStartTagComment( 409 const HTMLStartTagComment *C) { 410 Result << "<" << C->getTagName(); 411 412 if (C->getNumAttrs() != 0) { 413 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) { 414 Result << " "; 415 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i); 416 Result << Attr.Name; 417 if (!Attr.Value.empty()) 418 Result << "=\"" << Attr.Value << "\""; 419 } 420 } 421 422 if (!C->isSelfClosing()) 423 Result << ">"; 424 else 425 Result << "/>"; 426} 427 428void CommentASTToHTMLConverter::visitHTMLEndTagComment( 429 const HTMLEndTagComment *C) { 430 Result << "</" << C->getTagName() << ">"; 431} 432 433void CommentASTToHTMLConverter::visitParagraphComment( 434 const ParagraphComment *C) { 435 if (C->isWhitespace()) 436 return; 437 438 Result << "<p>"; 439 for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); 440 I != E; ++I) { 441 visit(*I); 442 } 443 Result << "</p>"; 444} 445 446void CommentASTToHTMLConverter::visitBlockCommandComment( 447 const BlockCommandComment *C) { 448 StringRef CommandName = C->getCommandName(); 449 if (CommandName == "brief" || CommandName == "short") { 450 Result << "<p class=\"para-brief\">"; 451 visitNonStandaloneParagraphComment(C->getParagraph()); 452 Result << "</p>"; 453 return; 454 } 455 if (CommandName == "returns" || CommandName == "return" || 456 CommandName == "result") { 457 Result << "<p class=\"para-returns\">" 458 "<span class=\"word-returns\">Returns</span> "; 459 visitNonStandaloneParagraphComment(C->getParagraph()); 460 Result << "</p>"; 461 return; 462 } 463 // We don't know anything about this command. Just render the paragraph. 464 visit(C->getParagraph()); 465} 466 467void CommentASTToHTMLConverter::visitParamCommandComment( 468 const ParamCommandComment *C) { 469 if (C->isParamIndexValid()) { 470 Result << "<dt class=\"param-name-index-" 471 << C->getParamIndex() 472 << "\">"; 473 } else 474 Result << "<dt class=\"param-name-index-invalid\">"; 475 476 Result << C->getParamName() << "</dt>"; 477 478 if (C->isParamIndexValid()) { 479 Result << "<dd class=\"param-descr-index-" 480 << C->getParamIndex() 481 << "\">"; 482 } else 483 Result << "<dd class=\"param-descr-index-invalid\">"; 484 485 visitNonStandaloneParagraphComment(C->getParagraph()); 486 Result << "</dd>"; 487} 488 489void CommentASTToHTMLConverter::visitVerbatimBlockComment( 490 const VerbatimBlockComment *C) { 491 unsigned NumLines = C->getNumLines(); 492 if (NumLines == 0) 493 return; 494 495 Result << "<pre>"; 496 for (unsigned i = 0; i != NumLines; ++i) { 497 appendToResultWithHTMLEscaping(C->getText(i)); 498 if (i + 1 != NumLines) 499 Result << '\n'; 500 } 501 Result << "</pre>"; 502} 503 504void CommentASTToHTMLConverter::visitVerbatimBlockLineComment( 505 const VerbatimBlockLineComment *C) { 506 llvm_unreachable("should not see this AST node"); 507} 508 509void CommentASTToHTMLConverter::visitVerbatimLineComment( 510 const VerbatimLineComment *C) { 511 Result << "<pre>"; 512 appendToResultWithHTMLEscaping(C->getText()); 513 Result << "</pre>"; 514} 515 516void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) { 517 const BlockContentComment *Brief = NULL; 518 const ParagraphComment *FirstParagraph = NULL; 519 const BlockCommandComment *Returns = NULL; 520 SmallVector<const ParamCommandComment *, 8> Params; 521 SmallVector<const BlockContentComment *, 8> MiscBlocks; 522 523 // Extract various blocks into separate variables and vectors above. 524 for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); 525 I != E; ++I) { 526 const Comment *Child = *I; 527 if (!Child) 528 continue; 529 switch (Child->getCommentKind()) { 530 case Comment::NoCommentKind: 531 continue; 532 533 case Comment::ParagraphCommentKind: { 534 const ParagraphComment *PC = cast<ParagraphComment>(Child); 535 if (PC->isWhitespace()) 536 break; 537 if (!FirstParagraph) 538 FirstParagraph = PC; 539 540 MiscBlocks.push_back(PC); 541 break; 542 } 543 544 case Comment::BlockCommandCommentKind: { 545 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child); 546 StringRef CommandName = BCC->getCommandName(); 547 if (!Brief && (CommandName == "brief" || CommandName == "short")) { 548 Brief = BCC; 549 break; 550 } 551 if (!Returns && (CommandName == "returns" || CommandName == "return")) { 552 Returns = BCC; 553 break; 554 } 555 MiscBlocks.push_back(BCC); 556 break; 557 } 558 559 case Comment::ParamCommandCommentKind: { 560 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child); 561 if (!PCC->hasParamName()) 562 break; 563 564 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph()) 565 break; 566 567 Params.push_back(PCC); 568 break; 569 } 570 571 case Comment::VerbatimBlockCommentKind: 572 case Comment::VerbatimLineCommentKind: 573 MiscBlocks.push_back(cast<BlockCommandComment>(Child)); 574 break; 575 576 case Comment::TextCommentKind: 577 case Comment::InlineCommandCommentKind: 578 case Comment::HTMLStartTagCommentKind: 579 case Comment::HTMLEndTagCommentKind: 580 case Comment::VerbatimBlockLineCommentKind: 581 case Comment::FullCommentKind: 582 llvm_unreachable("AST node of this kind can't be a child of " 583 "a FullComment"); 584 } 585 } 586 587 // Sort params in order they are declared in the function prototype. 588 // Unresolved parameters are put at the end of the list in the same order 589 // they were seen in the comment. 590 std::stable_sort(Params.begin(), Params.end(), 591 ParamCommandCommentCompareIndex()); 592 593 bool FirstParagraphIsBrief = false; 594 if (Brief) 595 visit(Brief); 596 else if (FirstParagraph) { 597 Result << "<p class=\"para-brief\">"; 598 visitNonStandaloneParagraphComment(FirstParagraph); 599 Result << "</p>"; 600 FirstParagraphIsBrief = true; 601 } 602 603 for (unsigned i = 0, e = MiscBlocks.size(); i != e; ++i) { 604 const Comment *C = MiscBlocks[i]; 605 if (FirstParagraphIsBrief && C == FirstParagraph) 606 continue; 607 visit(C); 608 } 609 610 if (Params.size() != 0) { 611 Result << "<dl>"; 612 for (unsigned i = 0, e = Params.size(); i != e; ++i) 613 visit(Params[i]); 614 Result << "</dl>"; 615 } 616 617 if (Returns) 618 visit(Returns); 619 620 Result.flush(); 621} 622 623void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment( 624 const ParagraphComment *C) { 625 if (!C) 626 return; 627 628 for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); 629 I != E; ++I) { 630 visit(*I); 631 } 632} 633 634void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) { 635 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) { 636 const char C = *I; 637 switch (C) { 638 case '&': 639 Result << "&"; 640 break; 641 case '<': 642 Result << "<"; 643 break; 644 case '>': 645 Result << ">"; 646 break; 647 case '"': 648 Result << """; 649 break; 650 case '\'': 651 Result << "'"; 652 break; 653 case '/': 654 Result << "/"; 655 break; 656 default: 657 Result << C; 658 break; 659 } 660 } 661} 662 663extern "C" { 664 665CXString clang_HTMLTagComment_getAsString(CXComment CXC) { 666 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC); 667 if (!HTC) 668 return createCXString((const char *) 0); 669 670 SmallString<128> HTML; 671 CommentASTToHTMLConverter Converter(HTML); 672 Converter.visit(HTC); 673 return createCXString(HTML.str(), /* DupString = */ true); 674} 675 676CXString clang_FullComment_getAsHTML(CXComment CXC) { 677 const FullComment *FC = getASTNodeAs<FullComment>(CXC); 678 if (!FC) 679 return createCXString((const char *) 0); 680 681 SmallString<1024> HTML; 682 CommentASTToHTMLConverter Converter(HTML); 683 Converter.visit(FC); 684 return createCXString(HTML.str(), /* DupString = */ true); 685} 686 687} // end extern "C" 688 689