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