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