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 "CXComment.h" 16 #include "CXCursor.h" 17 #include "CXString.h" 18 #include "SimpleFormatContext.h" 19 #include "clang/AST/CommentCommandTraits.h" 20 #include "clang/AST/CommentVisitor.h" 21 #include "clang/AST/Decl.h" 22 #include "clang/AST/PrettyPrinter.h" 23 #include "clang/Format/Format.h" 24 #include "clang/Lex/Lexer.h" 25 #include "llvm/ADT/StringExtras.h" 26 #include "llvm/ADT/StringSwitch.h" 27 #include "llvm/Support/ErrorHandling.h" 28 #include "llvm/Support/raw_ostream.h" 29 #include <climits> 30 31 using namespace clang; 32 using namespace clang::comments; 33 using namespace clang::cxcomment; 34 35 extern "C" { 36 37 enum CXCommentKind clang_Comment_getKind(CXComment CXC) { 38 const Comment *C = getASTNode(CXC); 39 if (!C) 40 return CXComment_Null; 41 42 switch (C->getCommentKind()) { 43 case Comment::NoCommentKind: 44 return CXComment_Null; 45 46 case Comment::TextCommentKind: 47 return CXComment_Text; 48 49 case Comment::InlineCommandCommentKind: 50 return CXComment_InlineCommand; 51 52 case Comment::HTMLStartTagCommentKind: 53 return CXComment_HTMLStartTag; 54 55 case Comment::HTMLEndTagCommentKind: 56 return CXComment_HTMLEndTag; 57 58 case Comment::ParagraphCommentKind: 59 return CXComment_Paragraph; 60 61 case Comment::BlockCommandCommentKind: 62 return CXComment_BlockCommand; 63 64 case Comment::ParamCommandCommentKind: 65 return CXComment_ParamCommand; 66 67 case Comment::TParamCommandCommentKind: 68 return CXComment_TParamCommand; 69 70 case Comment::VerbatimBlockCommentKind: 71 return CXComment_VerbatimBlockCommand; 72 73 case Comment::VerbatimBlockLineCommentKind: 74 return CXComment_VerbatimBlockLine; 75 76 case Comment::VerbatimLineCommentKind: 77 return CXComment_VerbatimLine; 78 79 case Comment::FullCommentKind: 80 return CXComment_FullComment; 81 } 82 llvm_unreachable("unknown CommentKind"); 83 } 84 85 unsigned clang_Comment_getNumChildren(CXComment CXC) { 86 const Comment *C = getASTNode(CXC); 87 if (!C) 88 return 0; 89 90 return C->child_count(); 91 } 92 93 CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) { 94 const Comment *C = getASTNode(CXC); 95 if (!C || ChildIdx >= C->child_count()) 96 return createCXComment(NULL, NULL); 97 98 return createCXComment(*(C->child_begin() + ChildIdx), CXC.TranslationUnit); 99 } 100 101 unsigned clang_Comment_isWhitespace(CXComment CXC) { 102 const Comment *C = getASTNode(CXC); 103 if (!C) 104 return false; 105 106 if (const TextComment *TC = dyn_cast<TextComment>(C)) 107 return TC->isWhitespace(); 108 109 if (const ParagraphComment *PC = dyn_cast<ParagraphComment>(C)) 110 return PC->isWhitespace(); 111 112 return false; 113 } 114 115 unsigned clang_InlineContentComment_hasTrailingNewline(CXComment CXC) { 116 const InlineContentComment *ICC = getASTNodeAs<InlineContentComment>(CXC); 117 if (!ICC) 118 return false; 119 120 return ICC->hasTrailingNewline(); 121 } 122 123 CXString clang_TextComment_getText(CXComment CXC) { 124 const TextComment *TC = getASTNodeAs<TextComment>(CXC); 125 if (!TC) 126 return cxstring::createNull(); 127 128 return cxstring::createRef(TC->getText()); 129 } 130 131 CXString clang_InlineCommandComment_getCommandName(CXComment CXC) { 132 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC); 133 if (!ICC) 134 return cxstring::createNull(); 135 136 const CommandTraits &Traits = getCommandTraits(CXC); 137 return cxstring::createRef(ICC->getCommandName(Traits)); 138 } 139 140 enum CXCommentInlineCommandRenderKind 141 clang_InlineCommandComment_getRenderKind(CXComment CXC) { 142 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC); 143 if (!ICC) 144 return CXCommentInlineCommandRenderKind_Normal; 145 146 switch (ICC->getRenderKind()) { 147 case InlineCommandComment::RenderNormal: 148 return CXCommentInlineCommandRenderKind_Normal; 149 150 case InlineCommandComment::RenderBold: 151 return CXCommentInlineCommandRenderKind_Bold; 152 153 case InlineCommandComment::RenderMonospaced: 154 return CXCommentInlineCommandRenderKind_Monospaced; 155 156 case InlineCommandComment::RenderEmphasized: 157 return CXCommentInlineCommandRenderKind_Emphasized; 158 } 159 llvm_unreachable("unknown InlineCommandComment::RenderKind"); 160 } 161 162 unsigned clang_InlineCommandComment_getNumArgs(CXComment CXC) { 163 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC); 164 if (!ICC) 165 return 0; 166 167 return ICC->getNumArgs(); 168 } 169 170 CXString clang_InlineCommandComment_getArgText(CXComment CXC, 171 unsigned ArgIdx) { 172 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC); 173 if (!ICC || ArgIdx >= ICC->getNumArgs()) 174 return cxstring::createNull(); 175 176 return cxstring::createRef(ICC->getArgText(ArgIdx)); 177 } 178 179 CXString clang_HTMLTagComment_getTagName(CXComment CXC) { 180 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC); 181 if (!HTC) 182 return cxstring::createNull(); 183 184 return cxstring::createRef(HTC->getTagName()); 185 } 186 187 unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment CXC) { 188 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC); 189 if (!HST) 190 return false; 191 192 return HST->isSelfClosing(); 193 } 194 195 unsigned clang_HTMLStartTag_getNumAttrs(CXComment CXC) { 196 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC); 197 if (!HST) 198 return 0; 199 200 return HST->getNumAttrs(); 201 } 202 203 CXString clang_HTMLStartTag_getAttrName(CXComment CXC, unsigned AttrIdx) { 204 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC); 205 if (!HST || AttrIdx >= HST->getNumAttrs()) 206 return cxstring::createNull(); 207 208 return cxstring::createRef(HST->getAttr(AttrIdx).Name); 209 } 210 211 CXString clang_HTMLStartTag_getAttrValue(CXComment CXC, unsigned AttrIdx) { 212 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC); 213 if (!HST || AttrIdx >= HST->getNumAttrs()) 214 return cxstring::createNull(); 215 216 return cxstring::createRef(HST->getAttr(AttrIdx).Value); 217 } 218 219 CXString clang_BlockCommandComment_getCommandName(CXComment CXC) { 220 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC); 221 if (!BCC) 222 return cxstring::createNull(); 223 224 const CommandTraits &Traits = getCommandTraits(CXC); 225 return cxstring::createRef(BCC->getCommandName(Traits)); 226 } 227 228 unsigned clang_BlockCommandComment_getNumArgs(CXComment CXC) { 229 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC); 230 if (!BCC) 231 return 0; 232 233 return BCC->getNumArgs(); 234 } 235 236 CXString clang_BlockCommandComment_getArgText(CXComment CXC, 237 unsigned ArgIdx) { 238 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC); 239 if (!BCC || ArgIdx >= BCC->getNumArgs()) 240 return cxstring::createNull(); 241 242 return cxstring::createRef(BCC->getArgText(ArgIdx)); 243 } 244 245 CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) { 246 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC); 247 if (!BCC) 248 return createCXComment(NULL, NULL); 249 250 return createCXComment(BCC->getParagraph(), CXC.TranslationUnit); 251 } 252 253 CXString clang_ParamCommandComment_getParamName(CXComment CXC) { 254 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC); 255 if (!PCC || !PCC->hasParamName()) 256 return cxstring::createNull(); 257 258 return cxstring::createRef(PCC->getParamNameAsWritten()); 259 } 260 261 unsigned clang_ParamCommandComment_isParamIndexValid(CXComment CXC) { 262 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC); 263 if (!PCC) 264 return false; 265 266 return PCC->isParamIndexValid(); 267 } 268 269 unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) { 270 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC); 271 if (!PCC || !PCC->isParamIndexValid() || PCC->isVarArgParam()) 272 return ParamCommandComment::InvalidParamIndex; 273 274 return PCC->getParamIndex(); 275 } 276 277 unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment CXC) { 278 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC); 279 if (!PCC) 280 return false; 281 282 return PCC->isDirectionExplicit(); 283 } 284 285 enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection( 286 CXComment CXC) { 287 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC); 288 if (!PCC) 289 return CXCommentParamPassDirection_In; 290 291 switch (PCC->getDirection()) { 292 case ParamCommandComment::In: 293 return CXCommentParamPassDirection_In; 294 295 case ParamCommandComment::Out: 296 return CXCommentParamPassDirection_Out; 297 298 case ParamCommandComment::InOut: 299 return CXCommentParamPassDirection_InOut; 300 } 301 llvm_unreachable("unknown ParamCommandComment::PassDirection"); 302 } 303 304 CXString clang_TParamCommandComment_getParamName(CXComment CXC) { 305 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC); 306 if (!TPCC || !TPCC->hasParamName()) 307 return cxstring::createNull(); 308 309 return cxstring::createRef(TPCC->getParamNameAsWritten()); 310 } 311 312 unsigned clang_TParamCommandComment_isParamPositionValid(CXComment CXC) { 313 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC); 314 if (!TPCC) 315 return false; 316 317 return TPCC->isPositionValid(); 318 } 319 320 unsigned clang_TParamCommandComment_getDepth(CXComment CXC) { 321 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC); 322 if (!TPCC || !TPCC->isPositionValid()) 323 return 0; 324 325 return TPCC->getDepth(); 326 } 327 328 unsigned clang_TParamCommandComment_getIndex(CXComment CXC, unsigned Depth) { 329 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC); 330 if (!TPCC || !TPCC->isPositionValid() || Depth >= TPCC->getDepth()) 331 return 0; 332 333 return TPCC->getIndex(Depth); 334 } 335 336 CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) { 337 const VerbatimBlockLineComment *VBL = 338 getASTNodeAs<VerbatimBlockLineComment>(CXC); 339 if (!VBL) 340 return cxstring::createNull(); 341 342 return cxstring::createRef(VBL->getText()); 343 } 344 345 CXString clang_VerbatimLineComment_getText(CXComment CXC) { 346 const VerbatimLineComment *VLC = getASTNodeAs<VerbatimLineComment>(CXC); 347 if (!VLC) 348 return cxstring::createNull(); 349 350 return cxstring::createRef(VLC->getText()); 351 } 352 353 } // end extern "C" 354 355 //===----------------------------------------------------------------------===// 356 // Helpers for converting comment AST to HTML. 357 //===----------------------------------------------------------------------===// 358 359 namespace { 360 361 /// This comparison will sort parameters with valid index by index, then vararg 362 /// parameters, and invalid (unresolved) parameters last. 363 class ParamCommandCommentCompareIndex { 364 public: 365 bool operator()(const ParamCommandComment *LHS, 366 const ParamCommandComment *RHS) const { 367 unsigned LHSIndex = UINT_MAX; 368 unsigned RHSIndex = UINT_MAX; 369 370 if (LHS->isParamIndexValid()) { 371 if (LHS->isVarArgParam()) 372 LHSIndex = UINT_MAX - 1; 373 else 374 LHSIndex = LHS->getParamIndex(); 375 } 376 if (RHS->isParamIndexValid()) { 377 if (RHS->isVarArgParam()) 378 RHSIndex = UINT_MAX - 1; 379 else 380 RHSIndex = RHS->getParamIndex(); 381 } 382 return LHSIndex < RHSIndex; 383 } 384 }; 385 386 /// This comparison will sort template parameters in the following order: 387 /// \li real template parameters (depth = 1) in index order; 388 /// \li all other names (depth > 1); 389 /// \li unresolved names. 390 class TParamCommandCommentComparePosition { 391 public: 392 bool operator()(const TParamCommandComment *LHS, 393 const TParamCommandComment *RHS) const { 394 // Sort unresolved names last. 395 if (!LHS->isPositionValid()) 396 return false; 397 if (!RHS->isPositionValid()) 398 return true; 399 400 if (LHS->getDepth() > 1) 401 return false; 402 if (RHS->getDepth() > 1) 403 return true; 404 405 // Sort template parameters in index order. 406 if (LHS->getDepth() == 1 && RHS->getDepth() == 1) 407 return LHS->getIndex(0) < RHS->getIndex(0); 408 409 // Leave all other names in source order. 410 return true; 411 } 412 }; 413 414 /// Separate parts of a FullComment. 415 struct FullCommentParts { 416 /// Take a full comment apart and initialize members accordingly. 417 FullCommentParts(const FullComment *C, 418 const CommandTraits &Traits); 419 420 const BlockContentComment *Brief; 421 const BlockContentComment *Headerfile; 422 const ParagraphComment *FirstParagraph; 423 SmallVector<const BlockCommandComment *, 4> Returns; 424 SmallVector<const ParamCommandComment *, 8> Params; 425 SmallVector<const TParamCommandComment *, 4> TParams; 426 SmallVector<const BlockContentComment *, 8> MiscBlocks; 427 }; 428 429 FullCommentParts::FullCommentParts(const FullComment *C, 430 const CommandTraits &Traits) : 431 Brief(NULL), Headerfile(NULL), FirstParagraph(NULL) { 432 for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); 433 I != E; ++I) { 434 const Comment *Child = *I; 435 if (!Child) 436 continue; 437 switch (Child->getCommentKind()) { 438 case Comment::NoCommentKind: 439 continue; 440 441 case Comment::ParagraphCommentKind: { 442 const ParagraphComment *PC = cast<ParagraphComment>(Child); 443 if (PC->isWhitespace()) 444 break; 445 if (!FirstParagraph) 446 FirstParagraph = PC; 447 448 MiscBlocks.push_back(PC); 449 break; 450 } 451 452 case Comment::BlockCommandCommentKind: { 453 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child); 454 const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID()); 455 if (!Brief && Info->IsBriefCommand) { 456 Brief = BCC; 457 break; 458 } 459 if (!Headerfile && Info->IsHeaderfileCommand) { 460 Headerfile = BCC; 461 break; 462 } 463 if (Info->IsReturnsCommand) { 464 Returns.push_back(BCC); 465 break; 466 } 467 MiscBlocks.push_back(BCC); 468 break; 469 } 470 471 case Comment::ParamCommandCommentKind: { 472 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child); 473 if (!PCC->hasParamName()) 474 break; 475 476 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph()) 477 break; 478 479 Params.push_back(PCC); 480 break; 481 } 482 483 case Comment::TParamCommandCommentKind: { 484 const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child); 485 if (!TPCC->hasParamName()) 486 break; 487 488 if (!TPCC->hasNonWhitespaceParagraph()) 489 break; 490 491 TParams.push_back(TPCC); 492 break; 493 } 494 495 case Comment::VerbatimBlockCommentKind: 496 MiscBlocks.push_back(cast<BlockCommandComment>(Child)); 497 break; 498 499 case Comment::VerbatimLineCommentKind: { 500 const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child); 501 const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID()); 502 if (!Info->IsDeclarationCommand) 503 MiscBlocks.push_back(VLC); 504 break; 505 } 506 507 case Comment::TextCommentKind: 508 case Comment::InlineCommandCommentKind: 509 case Comment::HTMLStartTagCommentKind: 510 case Comment::HTMLEndTagCommentKind: 511 case Comment::VerbatimBlockLineCommentKind: 512 case Comment::FullCommentKind: 513 llvm_unreachable("AST node of this kind can't be a child of " 514 "a FullComment"); 515 } 516 } 517 518 // Sort params in order they are declared in the function prototype. 519 // Unresolved parameters are put at the end of the list in the same order 520 // they were seen in the comment. 521 std::stable_sort(Params.begin(), Params.end(), 522 ParamCommandCommentCompareIndex()); 523 524 std::stable_sort(TParams.begin(), TParams.end(), 525 TParamCommandCommentComparePosition()); 526 } 527 528 void PrintHTMLStartTagComment(const HTMLStartTagComment *C, 529 llvm::raw_svector_ostream &Result) { 530 Result << "<" << C->getTagName(); 531 532 if (C->getNumAttrs() != 0) { 533 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) { 534 Result << " "; 535 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i); 536 Result << Attr.Name; 537 if (!Attr.Value.empty()) 538 Result << "=\"" << Attr.Value << "\""; 539 } 540 } 541 542 if (!C->isSelfClosing()) 543 Result << ">"; 544 else 545 Result << "/>"; 546 } 547 548 class CommentASTToHTMLConverter : 549 public ConstCommentVisitor<CommentASTToHTMLConverter> { 550 public: 551 /// \param Str accumulator for HTML. 552 CommentASTToHTMLConverter(const FullComment *FC, 553 SmallVectorImpl<char> &Str, 554 const CommandTraits &Traits) : 555 FC(FC), Result(Str), Traits(Traits) 556 { } 557 558 // Inline content. 559 void visitTextComment(const TextComment *C); 560 void visitInlineCommandComment(const InlineCommandComment *C); 561 void visitHTMLStartTagComment(const HTMLStartTagComment *C); 562 void visitHTMLEndTagComment(const HTMLEndTagComment *C); 563 564 // Block content. 565 void visitParagraphComment(const ParagraphComment *C); 566 void visitBlockCommandComment(const BlockCommandComment *C); 567 void visitParamCommandComment(const ParamCommandComment *C); 568 void visitTParamCommandComment(const TParamCommandComment *C); 569 void visitVerbatimBlockComment(const VerbatimBlockComment *C); 570 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); 571 void visitVerbatimLineComment(const VerbatimLineComment *C); 572 573 void visitFullComment(const FullComment *C); 574 575 // Helpers. 576 577 /// Convert a paragraph that is not a block by itself (an argument to some 578 /// command). 579 void visitNonStandaloneParagraphComment(const ParagraphComment *C); 580 581 void appendToResultWithHTMLEscaping(StringRef S); 582 583 private: 584 const FullComment *FC; 585 /// Output stream for HTML. 586 llvm::raw_svector_ostream Result; 587 588 const CommandTraits &Traits; 589 }; 590 } // end unnamed namespace 591 592 void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) { 593 appendToResultWithHTMLEscaping(C->getText()); 594 } 595 596 void CommentASTToHTMLConverter::visitInlineCommandComment( 597 const InlineCommandComment *C) { 598 // Nothing to render if no arguments supplied. 599 if (C->getNumArgs() == 0) 600 return; 601 602 // Nothing to render if argument is empty. 603 StringRef Arg0 = C->getArgText(0); 604 if (Arg0.empty()) 605 return; 606 607 switch (C->getRenderKind()) { 608 case InlineCommandComment::RenderNormal: 609 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) { 610 appendToResultWithHTMLEscaping(C->getArgText(i)); 611 Result << " "; 612 } 613 return; 614 615 case InlineCommandComment::RenderBold: 616 assert(C->getNumArgs() == 1); 617 Result << "<b>"; 618 appendToResultWithHTMLEscaping(Arg0); 619 Result << "</b>"; 620 return; 621 case InlineCommandComment::RenderMonospaced: 622 assert(C->getNumArgs() == 1); 623 Result << "<tt>"; 624 appendToResultWithHTMLEscaping(Arg0); 625 Result<< "</tt>"; 626 return; 627 case InlineCommandComment::RenderEmphasized: 628 assert(C->getNumArgs() == 1); 629 Result << "<em>"; 630 appendToResultWithHTMLEscaping(Arg0); 631 Result << "</em>"; 632 return; 633 } 634 } 635 636 void CommentASTToHTMLConverter::visitHTMLStartTagComment( 637 const HTMLStartTagComment *C) { 638 PrintHTMLStartTagComment(C, Result); 639 } 640 641 void CommentASTToHTMLConverter::visitHTMLEndTagComment( 642 const HTMLEndTagComment *C) { 643 Result << "</" << C->getTagName() << ">"; 644 } 645 646 void CommentASTToHTMLConverter::visitParagraphComment( 647 const ParagraphComment *C) { 648 if (C->isWhitespace()) 649 return; 650 651 Result << "<p>"; 652 for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); 653 I != E; ++I) { 654 visit(*I); 655 } 656 Result << "</p>"; 657 } 658 659 void CommentASTToHTMLConverter::visitBlockCommandComment( 660 const BlockCommandComment *C) { 661 const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID()); 662 if (Info->IsBriefCommand) { 663 Result << "<p class=\"para-brief\">"; 664 visitNonStandaloneParagraphComment(C->getParagraph()); 665 Result << "</p>"; 666 return; 667 } 668 if (Info->IsReturnsCommand) { 669 Result << "<p class=\"para-returns\">" 670 "<span class=\"word-returns\">Returns</span> "; 671 visitNonStandaloneParagraphComment(C->getParagraph()); 672 Result << "</p>"; 673 return; 674 } 675 // We don't know anything about this command. Just render the paragraph. 676 visit(C->getParagraph()); 677 } 678 679 void CommentASTToHTMLConverter::visitParamCommandComment( 680 const ParamCommandComment *C) { 681 if (C->isParamIndexValid()) { 682 if (C->isVarArgParam()) { 683 Result << "<dt class=\"param-name-index-vararg\">"; 684 appendToResultWithHTMLEscaping(C->getParamNameAsWritten()); 685 } else { 686 Result << "<dt class=\"param-name-index-" 687 << C->getParamIndex() 688 << "\">"; 689 appendToResultWithHTMLEscaping(C->getParamName(FC)); 690 } 691 } else { 692 Result << "<dt class=\"param-name-index-invalid\">"; 693 appendToResultWithHTMLEscaping(C->getParamNameAsWritten()); 694 } 695 Result << "</dt>"; 696 697 if (C->isParamIndexValid()) { 698 if (C->isVarArgParam()) 699 Result << "<dd class=\"param-descr-index-vararg\">"; 700 else 701 Result << "<dd class=\"param-descr-index-" 702 << C->getParamIndex() 703 << "\">"; 704 } else 705 Result << "<dd class=\"param-descr-index-invalid\">"; 706 707 visitNonStandaloneParagraphComment(C->getParagraph()); 708 Result << "</dd>"; 709 } 710 711 void CommentASTToHTMLConverter::visitTParamCommandComment( 712 const TParamCommandComment *C) { 713 if (C->isPositionValid()) { 714 if (C->getDepth() == 1) 715 Result << "<dt class=\"tparam-name-index-" 716 << C->getIndex(0) 717 << "\">"; 718 else 719 Result << "<dt class=\"tparam-name-index-other\">"; 720 appendToResultWithHTMLEscaping(C->getParamName(FC)); 721 } else { 722 Result << "<dt class=\"tparam-name-index-invalid\">"; 723 appendToResultWithHTMLEscaping(C->getParamNameAsWritten()); 724 } 725 726 Result << "</dt>"; 727 728 if (C->isPositionValid()) { 729 if (C->getDepth() == 1) 730 Result << "<dd class=\"tparam-descr-index-" 731 << C->getIndex(0) 732 << "\">"; 733 else 734 Result << "<dd class=\"tparam-descr-index-other\">"; 735 } else 736 Result << "<dd class=\"tparam-descr-index-invalid\">"; 737 738 visitNonStandaloneParagraphComment(C->getParagraph()); 739 Result << "</dd>"; 740 } 741 742 void CommentASTToHTMLConverter::visitVerbatimBlockComment( 743 const VerbatimBlockComment *C) { 744 unsigned NumLines = C->getNumLines(); 745 if (NumLines == 0) 746 return; 747 748 Result << "<pre>"; 749 for (unsigned i = 0; i != NumLines; ++i) { 750 appendToResultWithHTMLEscaping(C->getText(i)); 751 if (i + 1 != NumLines) 752 Result << '\n'; 753 } 754 Result << "</pre>"; 755 } 756 757 void CommentASTToHTMLConverter::visitVerbatimBlockLineComment( 758 const VerbatimBlockLineComment *C) { 759 llvm_unreachable("should not see this AST node"); 760 } 761 762 void CommentASTToHTMLConverter::visitVerbatimLineComment( 763 const VerbatimLineComment *C) { 764 Result << "<pre>"; 765 appendToResultWithHTMLEscaping(C->getText()); 766 Result << "</pre>"; 767 } 768 769 void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) { 770 FullCommentParts Parts(C, Traits); 771 772 bool FirstParagraphIsBrief = false; 773 if (Parts.Headerfile) 774 visit(Parts.Headerfile); 775 if (Parts.Brief) 776 visit(Parts.Brief); 777 else if (Parts.FirstParagraph) { 778 Result << "<p class=\"para-brief\">"; 779 visitNonStandaloneParagraphComment(Parts.FirstParagraph); 780 Result << "</p>"; 781 FirstParagraphIsBrief = true; 782 } 783 784 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) { 785 const Comment *C = Parts.MiscBlocks[i]; 786 if (FirstParagraphIsBrief && C == Parts.FirstParagraph) 787 continue; 788 visit(C); 789 } 790 791 if (Parts.TParams.size() != 0) { 792 Result << "<dl>"; 793 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i) 794 visit(Parts.TParams[i]); 795 Result << "</dl>"; 796 } 797 798 if (Parts.Params.size() != 0) { 799 Result << "<dl>"; 800 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i) 801 visit(Parts.Params[i]); 802 Result << "</dl>"; 803 } 804 805 if (Parts.Returns.size() != 0) { 806 Result << "<div class=\"result-discussion\">"; 807 for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i) 808 visit(Parts.Returns[i]); 809 Result << "</div>"; 810 } 811 812 Result.flush(); 813 } 814 815 void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment( 816 const ParagraphComment *C) { 817 if (!C) 818 return; 819 820 for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); 821 I != E; ++I) { 822 visit(*I); 823 } 824 } 825 826 void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) { 827 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) { 828 const char C = *I; 829 switch (C) { 830 case '&': 831 Result << "&"; 832 break; 833 case '<': 834 Result << "<"; 835 break; 836 case '>': 837 Result << ">"; 838 break; 839 case '"': 840 Result << """; 841 break; 842 case '\'': 843 Result << "'"; 844 break; 845 case '/': 846 Result << "/"; 847 break; 848 default: 849 Result << C; 850 break; 851 } 852 } 853 } 854 855 extern "C" { 856 857 CXString clang_HTMLTagComment_getAsString(CXComment CXC) { 858 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC); 859 if (!HTC) 860 return cxstring::createNull(); 861 862 SmallString<128> HTML; 863 CommentASTToHTMLConverter Converter(0, HTML, getCommandTraits(CXC)); 864 Converter.visit(HTC); 865 return cxstring::createDup(HTML.str()); 866 } 867 868 CXString clang_FullComment_getAsHTML(CXComment CXC) { 869 const FullComment *FC = getASTNodeAs<FullComment>(CXC); 870 if (!FC) 871 return cxstring::createNull(); 872 873 SmallString<1024> HTML; 874 CommentASTToHTMLConverter Converter(FC, HTML, getCommandTraits(CXC)); 875 Converter.visit(FC); 876 return cxstring::createDup(HTML.str()); 877 } 878 879 } // end extern "C" 880 881 namespace { 882 class CommentASTToXMLConverter : 883 public ConstCommentVisitor<CommentASTToXMLConverter> { 884 public: 885 /// \param Str accumulator for XML. 886 CommentASTToXMLConverter(const FullComment *FC, 887 SmallVectorImpl<char> &Str, 888 const CommandTraits &Traits, 889 const SourceManager &SM, 890 SimpleFormatContext &SFC, 891 unsigned FUID) : 892 FC(FC), Result(Str), Traits(Traits), SM(SM), 893 FormatRewriterContext(SFC), 894 FormatInMemoryUniqueId(FUID) { } 895 896 // Inline content. 897 void visitTextComment(const TextComment *C); 898 void visitInlineCommandComment(const InlineCommandComment *C); 899 void visitHTMLStartTagComment(const HTMLStartTagComment *C); 900 void visitHTMLEndTagComment(const HTMLEndTagComment *C); 901 902 // Block content. 903 void visitParagraphComment(const ParagraphComment *C); 904 905 void appendParagraphCommentWithKind(const ParagraphComment *C, 906 StringRef Kind); 907 908 void visitBlockCommandComment(const BlockCommandComment *C); 909 void visitParamCommandComment(const ParamCommandComment *C); 910 void visitTParamCommandComment(const TParamCommandComment *C); 911 void visitVerbatimBlockComment(const VerbatimBlockComment *C); 912 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); 913 void visitVerbatimLineComment(const VerbatimLineComment *C); 914 915 void visitFullComment(const FullComment *C); 916 917 // Helpers. 918 void appendToResultWithXMLEscaping(StringRef S); 919 920 void formatTextOfDeclaration(const DeclInfo *DI, 921 SmallString<128> &Declaration); 922 923 private: 924 const FullComment *FC; 925 926 /// Output stream for XML. 927 llvm::raw_svector_ostream Result; 928 929 const CommandTraits &Traits; 930 const SourceManager &SM; 931 SimpleFormatContext &FormatRewriterContext; 932 unsigned FormatInMemoryUniqueId; 933 }; 934 935 void getSourceTextOfDeclaration(const DeclInfo *ThisDecl, 936 SmallVectorImpl<char> &Str) { 937 ASTContext &Context = ThisDecl->CurrentDecl->getASTContext(); 938 const LangOptions &LangOpts = Context.getLangOpts(); 939 llvm::raw_svector_ostream OS(Str); 940 PrintingPolicy PPolicy(LangOpts); 941 PPolicy.PolishForDeclaration = true; 942 PPolicy.TerseOutput = true; 943 ThisDecl->CurrentDecl->print(OS, PPolicy, 944 /*Indentation*/0, /*PrintInstantiation*/false); 945 } 946 947 void CommentASTToXMLConverter::formatTextOfDeclaration( 948 const DeclInfo *DI, 949 SmallString<128> &Declaration) { 950 // FIXME. formatting API expects null terminated input string. 951 // There might be more efficient way of doing this. 952 std::string StringDecl = Declaration.str(); 953 954 // Formatter specific code. 955 // Form a unique in memory buffer name. 956 SmallString<128> filename; 957 filename += "xmldecl"; 958 filename += llvm::utostr(FormatInMemoryUniqueId); 959 filename += ".xd"; 960 FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl); 961 SourceLocation Start = 962 FormatRewriterContext.Sources.getLocForStartOfFile(ID).getLocWithOffset(0); 963 unsigned Length = Declaration.size(); 964 965 std::vector<CharSourceRange> 966 Ranges(1, CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length))); 967 ASTContext &Context = DI->CurrentDecl->getASTContext(); 968 const LangOptions &LangOpts = Context.getLangOpts(); 969 Lexer Lex(ID, FormatRewriterContext.Sources.getBuffer(ID), 970 FormatRewriterContext.Sources, LangOpts); 971 tooling::Replacements Replace = 972 reformat(format::getLLVMStyle(), Lex, FormatRewriterContext.Sources, Ranges); 973 applyAllReplacements(Replace, FormatRewriterContext.Rewrite); 974 Declaration = FormatRewriterContext.getRewrittenText(ID); 975 } 976 977 } // end unnamed namespace 978 979 void CommentASTToXMLConverter::visitTextComment(const TextComment *C) { 980 appendToResultWithXMLEscaping(C->getText()); 981 } 982 983 void CommentASTToXMLConverter::visitInlineCommandComment(const InlineCommandComment *C) { 984 // Nothing to render if no arguments supplied. 985 if (C->getNumArgs() == 0) 986 return; 987 988 // Nothing to render if argument is empty. 989 StringRef Arg0 = C->getArgText(0); 990 if (Arg0.empty()) 991 return; 992 993 switch (C->getRenderKind()) { 994 case InlineCommandComment::RenderNormal: 995 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) { 996 appendToResultWithXMLEscaping(C->getArgText(i)); 997 Result << " "; 998 } 999 return; 1000 case InlineCommandComment::RenderBold: 1001 assert(C->getNumArgs() == 1); 1002 Result << "<bold>"; 1003 appendToResultWithXMLEscaping(Arg0); 1004 Result << "</bold>"; 1005 return; 1006 case InlineCommandComment::RenderMonospaced: 1007 assert(C->getNumArgs() == 1); 1008 Result << "<monospaced>"; 1009 appendToResultWithXMLEscaping(Arg0); 1010 Result << "</monospaced>"; 1011 return; 1012 case InlineCommandComment::RenderEmphasized: 1013 assert(C->getNumArgs() == 1); 1014 Result << "<emphasized>"; 1015 appendToResultWithXMLEscaping(Arg0); 1016 Result << "</emphasized>"; 1017 return; 1018 } 1019 } 1020 1021 void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) { 1022 Result << "<rawHTML><![CDATA["; 1023 PrintHTMLStartTagComment(C, Result); 1024 Result << "]]></rawHTML>"; 1025 } 1026 1027 void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) { 1028 Result << "<rawHTML></" << C->getTagName() << "></rawHTML>"; 1029 } 1030 1031 void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) { 1032 appendParagraphCommentWithKind(C, StringRef()); 1033 } 1034 1035 void CommentASTToXMLConverter::appendParagraphCommentWithKind( 1036 const ParagraphComment *C, 1037 StringRef ParagraphKind) { 1038 if (C->isWhitespace()) 1039 return; 1040 1041 if (ParagraphKind.empty()) 1042 Result << "<Para>"; 1043 else 1044 Result << "<Para kind=\"" << ParagraphKind << "\">"; 1045 1046 for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); 1047 I != E; ++I) { 1048 visit(*I); 1049 } 1050 Result << "</Para>"; 1051 } 1052 1053 void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) { 1054 StringRef ParagraphKind; 1055 1056 switch (C->getCommandID()) { 1057 case CommandTraits::KCI_attention: 1058 case CommandTraits::KCI_author: 1059 case CommandTraits::KCI_authors: 1060 case CommandTraits::KCI_bug: 1061 case CommandTraits::KCI_copyright: 1062 case CommandTraits::KCI_date: 1063 case CommandTraits::KCI_invariant: 1064 case CommandTraits::KCI_note: 1065 case CommandTraits::KCI_post: 1066 case CommandTraits::KCI_pre: 1067 case CommandTraits::KCI_remark: 1068 case CommandTraits::KCI_remarks: 1069 case CommandTraits::KCI_sa: 1070 case CommandTraits::KCI_see: 1071 case CommandTraits::KCI_since: 1072 case CommandTraits::KCI_todo: 1073 case CommandTraits::KCI_version: 1074 case CommandTraits::KCI_warning: 1075 ParagraphKind = C->getCommandName(Traits); 1076 default: 1077 break; 1078 } 1079 1080 appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind); 1081 } 1082 1083 void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) { 1084 Result << "<Parameter><Name>"; 1085 appendToResultWithXMLEscaping(C->isParamIndexValid() ? C->getParamName(FC) 1086 : C->getParamNameAsWritten()); 1087 Result << "</Name>"; 1088 1089 if (C->isParamIndexValid()) { 1090 if (C->isVarArgParam()) 1091 Result << "<IsVarArg />"; 1092 else 1093 Result << "<Index>" << C->getParamIndex() << "</Index>"; 1094 } 1095 1096 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">"; 1097 switch (C->getDirection()) { 1098 case ParamCommandComment::In: 1099 Result << "in"; 1100 break; 1101 case ParamCommandComment::Out: 1102 Result << "out"; 1103 break; 1104 case ParamCommandComment::InOut: 1105 Result << "in,out"; 1106 break; 1107 } 1108 Result << "</Direction><Discussion>"; 1109 visit(C->getParagraph()); 1110 Result << "</Discussion></Parameter>"; 1111 } 1112 1113 void CommentASTToXMLConverter::visitTParamCommandComment( 1114 const TParamCommandComment *C) { 1115 Result << "<Parameter><Name>"; 1116 appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC) 1117 : C->getParamNameAsWritten()); 1118 Result << "</Name>"; 1119 1120 if (C->isPositionValid() && C->getDepth() == 1) { 1121 Result << "<Index>" << C->getIndex(0) << "</Index>"; 1122 } 1123 1124 Result << "<Discussion>"; 1125 visit(C->getParagraph()); 1126 Result << "</Discussion></Parameter>"; 1127 } 1128 1129 void CommentASTToXMLConverter::visitVerbatimBlockComment( 1130 const VerbatimBlockComment *C) { 1131 unsigned NumLines = C->getNumLines(); 1132 if (NumLines == 0) 1133 return; 1134 1135 switch (C->getCommandID()) { 1136 case CommandTraits::KCI_code: 1137 Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">"; 1138 break; 1139 default: 1140 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">"; 1141 break; 1142 } 1143 for (unsigned i = 0; i != NumLines; ++i) { 1144 appendToResultWithXMLEscaping(C->getText(i)); 1145 if (i + 1 != NumLines) 1146 Result << '\n'; 1147 } 1148 Result << "</Verbatim>"; 1149 } 1150 1151 void CommentASTToXMLConverter::visitVerbatimBlockLineComment( 1152 const VerbatimBlockLineComment *C) { 1153 llvm_unreachable("should not see this AST node"); 1154 } 1155 1156 void CommentASTToXMLConverter::visitVerbatimLineComment( 1157 const VerbatimLineComment *C) { 1158 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">"; 1159 appendToResultWithXMLEscaping(C->getText()); 1160 Result << "</Verbatim>"; 1161 } 1162 1163 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) { 1164 FullCommentParts Parts(C, Traits); 1165 1166 const DeclInfo *DI = C->getDeclInfo(); 1167 StringRef RootEndTag; 1168 if (DI) { 1169 switch (DI->getKind()) { 1170 case DeclInfo::OtherKind: 1171 RootEndTag = "</Other>"; 1172 Result << "<Other"; 1173 break; 1174 case DeclInfo::FunctionKind: 1175 RootEndTag = "</Function>"; 1176 Result << "<Function"; 1177 switch (DI->TemplateKind) { 1178 case DeclInfo::NotTemplate: 1179 break; 1180 case DeclInfo::Template: 1181 Result << " templateKind=\"template\""; 1182 break; 1183 case DeclInfo::TemplateSpecialization: 1184 Result << " templateKind=\"specialization\""; 1185 break; 1186 case DeclInfo::TemplatePartialSpecialization: 1187 llvm_unreachable("partial specializations of functions " 1188 "are not allowed in C++"); 1189 } 1190 if (DI->IsInstanceMethod) 1191 Result << " isInstanceMethod=\"1\""; 1192 if (DI->IsClassMethod) 1193 Result << " isClassMethod=\"1\""; 1194 break; 1195 case DeclInfo::ClassKind: 1196 RootEndTag = "</Class>"; 1197 Result << "<Class"; 1198 switch (DI->TemplateKind) { 1199 case DeclInfo::NotTemplate: 1200 break; 1201 case DeclInfo::Template: 1202 Result << " templateKind=\"template\""; 1203 break; 1204 case DeclInfo::TemplateSpecialization: 1205 Result << " templateKind=\"specialization\""; 1206 break; 1207 case DeclInfo::TemplatePartialSpecialization: 1208 Result << " templateKind=\"partialSpecialization\""; 1209 break; 1210 } 1211 break; 1212 case DeclInfo::VariableKind: 1213 RootEndTag = "</Variable>"; 1214 Result << "<Variable"; 1215 break; 1216 case DeclInfo::NamespaceKind: 1217 RootEndTag = "</Namespace>"; 1218 Result << "<Namespace"; 1219 break; 1220 case DeclInfo::TypedefKind: 1221 RootEndTag = "</Typedef>"; 1222 Result << "<Typedef"; 1223 break; 1224 case DeclInfo::EnumKind: 1225 RootEndTag = "</Enum>"; 1226 Result << "<Enum"; 1227 break; 1228 } 1229 1230 { 1231 // Print line and column number. 1232 SourceLocation Loc = DI->CurrentDecl->getLocation(); 1233 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); 1234 FileID FID = LocInfo.first; 1235 unsigned FileOffset = LocInfo.second; 1236 1237 if (!FID.isInvalid()) { 1238 if (const FileEntry *FE = SM.getFileEntryForID(FID)) { 1239 Result << " file=\""; 1240 appendToResultWithXMLEscaping(FE->getName()); 1241 Result << "\""; 1242 } 1243 Result << " line=\"" << SM.getLineNumber(FID, FileOffset) 1244 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset) 1245 << "\""; 1246 } 1247 } 1248 1249 // Finish the root tag. 1250 Result << ">"; 1251 1252 bool FoundName = false; 1253 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) { 1254 if (DeclarationName DeclName = ND->getDeclName()) { 1255 Result << "<Name>"; 1256 std::string Name = DeclName.getAsString(); 1257 appendToResultWithXMLEscaping(Name); 1258 FoundName = true; 1259 Result << "</Name>"; 1260 } 1261 } 1262 if (!FoundName) 1263 Result << "<Name><anonymous></Name>"; 1264 1265 { 1266 // Print USR. 1267 SmallString<128> USR; 1268 cxcursor::getDeclCursorUSR(DI->CommentDecl, USR); 1269 if (!USR.empty()) { 1270 Result << "<USR>"; 1271 appendToResultWithXMLEscaping(USR); 1272 Result << "</USR>"; 1273 } 1274 } 1275 } else { 1276 // No DeclInfo -- just emit some root tag and name tag. 1277 RootEndTag = "</Other>"; 1278 Result << "<Other><Name>unknown</Name>"; 1279 } 1280 1281 if (Parts.Headerfile) { 1282 Result << "<Headerfile>"; 1283 visit(Parts.Headerfile); 1284 Result << "</Headerfile>"; 1285 } 1286 1287 { 1288 // Pretty-print the declaration. 1289 Result << "<Declaration>"; 1290 SmallString<128> Declaration; 1291 getSourceTextOfDeclaration(DI, Declaration); 1292 formatTextOfDeclaration(DI, Declaration); 1293 appendToResultWithXMLEscaping(Declaration); 1294 1295 Result << "</Declaration>"; 1296 } 1297 1298 bool FirstParagraphIsBrief = false; 1299 if (Parts.Brief) { 1300 Result << "<Abstract>"; 1301 visit(Parts.Brief); 1302 Result << "</Abstract>"; 1303 } else if (Parts.FirstParagraph) { 1304 Result << "<Abstract>"; 1305 visit(Parts.FirstParagraph); 1306 Result << "</Abstract>"; 1307 FirstParagraphIsBrief = true; 1308 } 1309 1310 if (Parts.TParams.size() != 0) { 1311 Result << "<TemplateParameters>"; 1312 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i) 1313 visit(Parts.TParams[i]); 1314 Result << "</TemplateParameters>"; 1315 } 1316 1317 if (Parts.Params.size() != 0) { 1318 Result << "<Parameters>"; 1319 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i) 1320 visit(Parts.Params[i]); 1321 Result << "</Parameters>"; 1322 } 1323 1324 if (Parts.Returns.size() != 0) { 1325 Result << "<ResultDiscussion>"; 1326 for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i) 1327 visit(Parts.Returns[i]); 1328 Result << "</ResultDiscussion>"; 1329 } 1330 1331 if (DI->CommentDecl->hasAttrs()) { 1332 const AttrVec &Attrs = DI->CommentDecl->getAttrs(); 1333 for (unsigned i = 0, e = Attrs.size(); i != e; i++) { 1334 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]); 1335 if (!AA) { 1336 if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) { 1337 if (DA->getMessage().empty()) 1338 Result << "<Deprecated/>"; 1339 else { 1340 Result << "<Deprecated>"; 1341 appendToResultWithXMLEscaping(DA->getMessage()); 1342 Result << "</Deprecated>"; 1343 } 1344 } 1345 else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) { 1346 if (UA->getMessage().empty()) 1347 Result << "<Unavailable/>"; 1348 else { 1349 Result << "<Unavailable>"; 1350 appendToResultWithXMLEscaping(UA->getMessage()); 1351 Result << "</Unavailable>"; 1352 } 1353 } 1354 continue; 1355 } 1356 1357 // 'availability' attribute. 1358 Result << "<Availability"; 1359 StringRef Distribution; 1360 if (AA->getPlatform()) { 1361 Distribution = AvailabilityAttr::getPrettyPlatformName( 1362 AA->getPlatform()->getName()); 1363 if (Distribution.empty()) 1364 Distribution = AA->getPlatform()->getName(); 1365 } 1366 Result << " distribution=\"" << Distribution << "\">"; 1367 VersionTuple IntroducedInVersion = AA->getIntroduced(); 1368 if (!IntroducedInVersion.empty()) { 1369 Result << "<IntroducedInVersion>" 1370 << IntroducedInVersion.getAsString() 1371 << "</IntroducedInVersion>"; 1372 } 1373 VersionTuple DeprecatedInVersion = AA->getDeprecated(); 1374 if (!DeprecatedInVersion.empty()) { 1375 Result << "<DeprecatedInVersion>" 1376 << DeprecatedInVersion.getAsString() 1377 << "</DeprecatedInVersion>"; 1378 } 1379 VersionTuple RemovedAfterVersion = AA->getObsoleted(); 1380 if (!RemovedAfterVersion.empty()) { 1381 Result << "<RemovedAfterVersion>" 1382 << RemovedAfterVersion.getAsString() 1383 << "</RemovedAfterVersion>"; 1384 } 1385 StringRef DeprecationSummary = AA->getMessage(); 1386 if (!DeprecationSummary.empty()) { 1387 Result << "<DeprecationSummary>"; 1388 appendToResultWithXMLEscaping(DeprecationSummary); 1389 Result << "</DeprecationSummary>"; 1390 } 1391 if (AA->getUnavailable()) 1392 Result << "<Unavailable/>"; 1393 Result << "</Availability>"; 1394 } 1395 } 1396 1397 { 1398 bool StartTagEmitted = false; 1399 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) { 1400 const Comment *C = Parts.MiscBlocks[i]; 1401 if (FirstParagraphIsBrief && C == Parts.FirstParagraph) 1402 continue; 1403 if (!StartTagEmitted) { 1404 Result << "<Discussion>"; 1405 StartTagEmitted = true; 1406 } 1407 visit(C); 1408 } 1409 if (StartTagEmitted) 1410 Result << "</Discussion>"; 1411 } 1412 1413 Result << RootEndTag; 1414 1415 Result.flush(); 1416 } 1417 1418 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) { 1419 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) { 1420 const char C = *I; 1421 switch (C) { 1422 case '&': 1423 Result << "&"; 1424 break; 1425 case '<': 1426 Result << "<"; 1427 break; 1428 case '>': 1429 Result << ">"; 1430 break; 1431 case '"': 1432 Result << """; 1433 break; 1434 case '\'': 1435 Result << "'"; 1436 break; 1437 default: 1438 Result << C; 1439 break; 1440 } 1441 } 1442 } 1443 1444 extern "C" { 1445 1446 CXString clang_FullComment_getAsXML(CXComment CXC) { 1447 const FullComment *FC = getASTNodeAs<FullComment>(CXC); 1448 if (!FC) 1449 return cxstring::createNull(); 1450 ASTContext &Context = FC->getDeclInfo()->CurrentDecl->getASTContext(); 1451 CXTranslationUnit TU = CXC.TranslationUnit; 1452 SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager(); 1453 1454 if (!TU->FormatContext) { 1455 TU->FormatContext = new SimpleFormatContext(Context.getLangOpts()); 1456 } else if ((TU->FormatInMemoryUniqueId % 1000) == 0) { 1457 // Delete after some number of iterators, so the buffers don't grow 1458 // too large. 1459 delete TU->FormatContext; 1460 TU->FormatContext = new SimpleFormatContext(Context.getLangOpts()); 1461 } 1462 1463 SmallString<1024> XML; 1464 CommentASTToXMLConverter Converter(FC, XML, getCommandTraits(CXC), SM, 1465 *TU->FormatContext, 1466 TU->FormatInMemoryUniqueId++); 1467 Converter.visit(FC); 1468 return cxstring::createDup(XML.str()); 1469 } 1470 1471 } // end extern "C" 1472 1473