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