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()) 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 and 362 /// 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 if (LHS->isParamIndexValid()) 370 LHSIndex = LHS->getParamIndex(); 371 if (RHS->isParamIndexValid()) 372 RHSIndex = RHS->getParamIndex(); 373 374 return LHSIndex < RHSIndex; 375 } 376 }; 377 378 /// This comparison will sort template parameters in the following order: 379 /// \li real template parameters (depth = 1) in index order; 380 /// \li all other names (depth > 1); 381 /// \li unresolved names. 382 class TParamCommandCommentComparePosition { 383 public: 384 bool operator()(const TParamCommandComment *LHS, 385 const TParamCommandComment *RHS) const { 386 // Sort unresolved names last. 387 if (!LHS->isPositionValid()) 388 return false; 389 if (!RHS->isPositionValid()) 390 return true; 391 392 if (LHS->getDepth() > 1) 393 return false; 394 if (RHS->getDepth() > 1) 395 return true; 396 397 // Sort template parameters in index order. 398 if (LHS->getDepth() == 1 && RHS->getDepth() == 1) 399 return LHS->getIndex(0) < RHS->getIndex(0); 400 401 // Leave all other names in source order. 402 return true; 403 } 404 }; 405 406 /// Separate parts of a FullComment. 407 struct FullCommentParts { 408 /// Take a full comment apart and initialize members accordingly. 409 FullCommentParts(const FullComment *C, 410 const CommandTraits &Traits); 411 412 const BlockContentComment *Brief; 413 const BlockContentComment *Headerfile; 414 const ParagraphComment *FirstParagraph; 415 const BlockCommandComment *Returns; 416 SmallVector<const ParamCommandComment *, 8> Params; 417 SmallVector<const TParamCommandComment *, 4> TParams; 418 SmallVector<const BlockContentComment *, 8> MiscBlocks; 419 }; 420 421 FullCommentParts::FullCommentParts(const FullComment *C, 422 const CommandTraits &Traits) : 423 Brief(NULL), Headerfile(NULL), FirstParagraph(NULL), Returns(NULL) { 424 for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); 425 I != E; ++I) { 426 const Comment *Child = *I; 427 if (!Child) 428 continue; 429 switch (Child->getCommentKind()) { 430 case Comment::NoCommentKind: 431 continue; 432 433 case Comment::ParagraphCommentKind: { 434 const ParagraphComment *PC = cast<ParagraphComment>(Child); 435 if (PC->isWhitespace()) 436 break; 437 if (!FirstParagraph) 438 FirstParagraph = PC; 439 440 MiscBlocks.push_back(PC); 441 break; 442 } 443 444 case Comment::BlockCommandCommentKind: { 445 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child); 446 const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID()); 447 if (!Brief && Info->IsBriefCommand) { 448 Brief = BCC; 449 break; 450 } 451 if (!Headerfile && Info->IsHeaderfileCommand) { 452 Headerfile = BCC; 453 break; 454 } 455 if (!Returns && Info->IsReturnsCommand) { 456 Returns = BCC; 457 break; 458 } 459 MiscBlocks.push_back(BCC); 460 break; 461 } 462 463 case Comment::ParamCommandCommentKind: { 464 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child); 465 if (!PCC->hasParamName()) 466 break; 467 468 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph()) 469 break; 470 471 Params.push_back(PCC); 472 break; 473 } 474 475 case Comment::TParamCommandCommentKind: { 476 const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child); 477 if (!TPCC->hasParamName()) 478 break; 479 480 if (!TPCC->hasNonWhitespaceParagraph()) 481 break; 482 483 TParams.push_back(TPCC); 484 break; 485 } 486 487 case Comment::VerbatimBlockCommentKind: 488 MiscBlocks.push_back(cast<BlockCommandComment>(Child)); 489 break; 490 491 case Comment::VerbatimLineCommentKind: { 492 const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child); 493 const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID()); 494 if (!Info->IsDeclarationCommand) 495 MiscBlocks.push_back(VLC); 496 break; 497 } 498 499 case Comment::TextCommentKind: 500 case Comment::InlineCommandCommentKind: 501 case Comment::HTMLStartTagCommentKind: 502 case Comment::HTMLEndTagCommentKind: 503 case Comment::VerbatimBlockLineCommentKind: 504 case Comment::FullCommentKind: 505 llvm_unreachable("AST node of this kind can't be a child of " 506 "a FullComment"); 507 } 508 } 509 510 // Sort params in order they are declared in the function prototype. 511 // Unresolved parameters are put at the end of the list in the same order 512 // they were seen in the comment. 513 std::stable_sort(Params.begin(), Params.end(), 514 ParamCommandCommentCompareIndex()); 515 516 std::stable_sort(TParams.begin(), TParams.end(), 517 TParamCommandCommentComparePosition()); 518 } 519 520 void PrintHTMLStartTagComment(const HTMLStartTagComment *C, 521 llvm::raw_svector_ostream &Result) { 522 Result << "<" << C->getTagName(); 523 524 if (C->getNumAttrs() != 0) { 525 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) { 526 Result << " "; 527 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i); 528 Result << Attr.Name; 529 if (!Attr.Value.empty()) 530 Result << "=\"" << Attr.Value << "\""; 531 } 532 } 533 534 if (!C->isSelfClosing()) 535 Result << ">"; 536 else 537 Result << "/>"; 538 } 539 540 class CommentASTToHTMLConverter : 541 public ConstCommentVisitor<CommentASTToHTMLConverter> { 542 public: 543 /// \param Str accumulator for HTML. 544 CommentASTToHTMLConverter(const FullComment *FC, 545 SmallVectorImpl<char> &Str, 546 const CommandTraits &Traits) : 547 FC(FC), Result(Str), Traits(Traits) 548 { } 549 550 // Inline content. 551 void visitTextComment(const TextComment *C); 552 void visitInlineCommandComment(const InlineCommandComment *C); 553 void visitHTMLStartTagComment(const HTMLStartTagComment *C); 554 void visitHTMLEndTagComment(const HTMLEndTagComment *C); 555 556 // Block content. 557 void visitParagraphComment(const ParagraphComment *C); 558 void visitBlockCommandComment(const BlockCommandComment *C); 559 void visitParamCommandComment(const ParamCommandComment *C); 560 void visitTParamCommandComment(const TParamCommandComment *C); 561 void visitVerbatimBlockComment(const VerbatimBlockComment *C); 562 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); 563 void visitVerbatimLineComment(const VerbatimLineComment *C); 564 565 void visitFullComment(const FullComment *C); 566 567 // Helpers. 568 569 /// Convert a paragraph that is not a block by itself (an argument to some 570 /// command). 571 void visitNonStandaloneParagraphComment(const ParagraphComment *C); 572 573 void appendToResultWithHTMLEscaping(StringRef S); 574 575 private: 576 const FullComment *FC; 577 /// Output stream for HTML. 578 llvm::raw_svector_ostream Result; 579 580 const CommandTraits &Traits; 581 }; 582 } // end unnamed namespace 583 584 void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) { 585 appendToResultWithHTMLEscaping(C->getText()); 586 } 587 588 void CommentASTToHTMLConverter::visitInlineCommandComment( 589 const InlineCommandComment *C) { 590 // Nothing to render if no arguments supplied. 591 if (C->getNumArgs() == 0) 592 return; 593 594 // Nothing to render if argument is empty. 595 StringRef Arg0 = C->getArgText(0); 596 if (Arg0.empty()) 597 return; 598 599 switch (C->getRenderKind()) { 600 case InlineCommandComment::RenderNormal: 601 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) { 602 appendToResultWithHTMLEscaping(C->getArgText(i)); 603 Result << " "; 604 } 605 return; 606 607 case InlineCommandComment::RenderBold: 608 assert(C->getNumArgs() == 1); 609 Result << "<b>"; 610 appendToResultWithHTMLEscaping(Arg0); 611 Result << "</b>"; 612 return; 613 case InlineCommandComment::RenderMonospaced: 614 assert(C->getNumArgs() == 1); 615 Result << "<tt>"; 616 appendToResultWithHTMLEscaping(Arg0); 617 Result<< "</tt>"; 618 return; 619 case InlineCommandComment::RenderEmphasized: 620 assert(C->getNumArgs() == 1); 621 Result << "<em>"; 622 appendToResultWithHTMLEscaping(Arg0); 623 Result << "</em>"; 624 return; 625 } 626 } 627 628 void CommentASTToHTMLConverter::visitHTMLStartTagComment( 629 const HTMLStartTagComment *C) { 630 PrintHTMLStartTagComment(C, Result); 631 } 632 633 void CommentASTToHTMLConverter::visitHTMLEndTagComment( 634 const HTMLEndTagComment *C) { 635 Result << "</" << C->getTagName() << ">"; 636 } 637 638 void CommentASTToHTMLConverter::visitParagraphComment( 639 const ParagraphComment *C) { 640 if (C->isWhitespace()) 641 return; 642 643 Result << "<p>"; 644 for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); 645 I != E; ++I) { 646 visit(*I); 647 } 648 Result << "</p>"; 649 } 650 651 void CommentASTToHTMLConverter::visitBlockCommandComment( 652 const BlockCommandComment *C) { 653 const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID()); 654 if (Info->IsBriefCommand) { 655 Result << "<p class=\"para-brief\">"; 656 visitNonStandaloneParagraphComment(C->getParagraph()); 657 Result << "</p>"; 658 return; 659 } 660 if (Info->IsReturnsCommand) { 661 Result << "<p class=\"para-returns\">" 662 "<span class=\"word-returns\">Returns</span> "; 663 visitNonStandaloneParagraphComment(C->getParagraph()); 664 Result << "</p>"; 665 return; 666 } 667 // We don't know anything about this command. Just render the paragraph. 668 visit(C->getParagraph()); 669 } 670 671 void CommentASTToHTMLConverter::visitParamCommandComment( 672 const ParamCommandComment *C) { 673 if (C->isParamIndexValid()) { 674 Result << "<dt class=\"param-name-index-" 675 << C->getParamIndex() 676 << "\">"; 677 appendToResultWithHTMLEscaping(C->getParamName(FC)); 678 } else { 679 Result << "<dt class=\"param-name-index-invalid\">"; 680 appendToResultWithHTMLEscaping(C->getParamNameAsWritten()); 681 } 682 Result << "</dt>"; 683 684 if (C->isParamIndexValid()) { 685 Result << "<dd class=\"param-descr-index-" 686 << C->getParamIndex() 687 << "\">"; 688 } else 689 Result << "<dd class=\"param-descr-index-invalid\">"; 690 691 visitNonStandaloneParagraphComment(C->getParagraph()); 692 Result << "</dd>"; 693 } 694 695 void CommentASTToHTMLConverter::visitTParamCommandComment( 696 const TParamCommandComment *C) { 697 if (C->isPositionValid()) { 698 if (C->getDepth() == 1) 699 Result << "<dt class=\"tparam-name-index-" 700 << C->getIndex(0) 701 << "\">"; 702 else 703 Result << "<dt class=\"tparam-name-index-other\">"; 704 appendToResultWithHTMLEscaping(C->getParamName(FC)); 705 } else { 706 Result << "<dt class=\"tparam-name-index-invalid\">"; 707 appendToResultWithHTMLEscaping(C->getParamNameAsWritten()); 708 } 709 710 Result << "</dt>"; 711 712 if (C->isPositionValid()) { 713 if (C->getDepth() == 1) 714 Result << "<dd class=\"tparam-descr-index-" 715 << C->getIndex(0) 716 << "\">"; 717 else 718 Result << "<dd class=\"tparam-descr-index-other\">"; 719 } else 720 Result << "<dd class=\"tparam-descr-index-invalid\">"; 721 722 visitNonStandaloneParagraphComment(C->getParagraph()); 723 Result << "</dd>"; 724 } 725 726 void CommentASTToHTMLConverter::visitVerbatimBlockComment( 727 const VerbatimBlockComment *C) { 728 unsigned NumLines = C->getNumLines(); 729 if (NumLines == 0) 730 return; 731 732 Result << "<pre>"; 733 for (unsigned i = 0; i != NumLines; ++i) { 734 appendToResultWithHTMLEscaping(C->getText(i)); 735 if (i + 1 != NumLines) 736 Result << '\n'; 737 } 738 Result << "</pre>"; 739 } 740 741 void CommentASTToHTMLConverter::visitVerbatimBlockLineComment( 742 const VerbatimBlockLineComment *C) { 743 llvm_unreachable("should not see this AST node"); 744 } 745 746 void CommentASTToHTMLConverter::visitVerbatimLineComment( 747 const VerbatimLineComment *C) { 748 Result << "<pre>"; 749 appendToResultWithHTMLEscaping(C->getText()); 750 Result << "</pre>"; 751 } 752 753 void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) { 754 FullCommentParts Parts(C, Traits); 755 756 bool FirstParagraphIsBrief = false; 757 if (Parts.Headerfile) 758 visit(Parts.Headerfile); 759 if (Parts.Brief) 760 visit(Parts.Brief); 761 else if (Parts.FirstParagraph) { 762 Result << "<p class=\"para-brief\">"; 763 visitNonStandaloneParagraphComment(Parts.FirstParagraph); 764 Result << "</p>"; 765 FirstParagraphIsBrief = true; 766 } 767 768 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) { 769 const Comment *C = Parts.MiscBlocks[i]; 770 if (FirstParagraphIsBrief && C == Parts.FirstParagraph) 771 continue; 772 visit(C); 773 } 774 775 if (Parts.TParams.size() != 0) { 776 Result << "<dl>"; 777 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i) 778 visit(Parts.TParams[i]); 779 Result << "</dl>"; 780 } 781 782 if (Parts.Params.size() != 0) { 783 Result << "<dl>"; 784 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i) 785 visit(Parts.Params[i]); 786 Result << "</dl>"; 787 } 788 789 if (Parts.Returns) 790 visit(Parts.Returns); 791 792 Result.flush(); 793 } 794 795 void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment( 796 const ParagraphComment *C) { 797 if (!C) 798 return; 799 800 for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); 801 I != E; ++I) { 802 visit(*I); 803 } 804 } 805 806 void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) { 807 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) { 808 const char C = *I; 809 switch (C) { 810 case '&': 811 Result << "&"; 812 break; 813 case '<': 814 Result << "<"; 815 break; 816 case '>': 817 Result << ">"; 818 break; 819 case '"': 820 Result << """; 821 break; 822 case '\'': 823 Result << "'"; 824 break; 825 case '/': 826 Result << "/"; 827 break; 828 default: 829 Result << C; 830 break; 831 } 832 } 833 } 834 835 extern "C" { 836 837 CXString clang_HTMLTagComment_getAsString(CXComment CXC) { 838 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC); 839 if (!HTC) 840 return cxstring::createNull(); 841 842 SmallString<128> HTML; 843 CommentASTToHTMLConverter Converter(0, HTML, getCommandTraits(CXC)); 844 Converter.visit(HTC); 845 return cxstring::createDup(HTML.str()); 846 } 847 848 CXString clang_FullComment_getAsHTML(CXComment CXC) { 849 const FullComment *FC = getASTNodeAs<FullComment>(CXC); 850 if (!FC) 851 return cxstring::createNull(); 852 853 SmallString<1024> HTML; 854 CommentASTToHTMLConverter Converter(FC, HTML, getCommandTraits(CXC)); 855 Converter.visit(FC); 856 return cxstring::createDup(HTML.str()); 857 } 858 859 } // end extern "C" 860 861 namespace { 862 class CommentASTToXMLConverter : 863 public ConstCommentVisitor<CommentASTToXMLConverter> { 864 public: 865 /// \param Str accumulator for XML. 866 CommentASTToXMLConverter(const FullComment *FC, 867 SmallVectorImpl<char> &Str, 868 const CommandTraits &Traits, 869 const SourceManager &SM, 870 SimpleFormatContext &SFC, 871 unsigned FUID) : 872 FC(FC), Result(Str), Traits(Traits), SM(SM), 873 FormatRewriterContext(SFC), 874 FormatInMemoryUniqueId(FUID) { } 875 876 // Inline content. 877 void visitTextComment(const TextComment *C); 878 void visitInlineCommandComment(const InlineCommandComment *C); 879 void visitHTMLStartTagComment(const HTMLStartTagComment *C); 880 void visitHTMLEndTagComment(const HTMLEndTagComment *C); 881 882 // Block content. 883 void visitParagraphComment(const ParagraphComment *C); 884 885 void appendParagraphCommentWithKind(const ParagraphComment *C, 886 StringRef Kind); 887 888 void visitBlockCommandComment(const BlockCommandComment *C); 889 void visitParamCommandComment(const ParamCommandComment *C); 890 void visitTParamCommandComment(const TParamCommandComment *C); 891 void visitVerbatimBlockComment(const VerbatimBlockComment *C); 892 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); 893 void visitVerbatimLineComment(const VerbatimLineComment *C); 894 895 void visitFullComment(const FullComment *C); 896 897 // Helpers. 898 void appendToResultWithXMLEscaping(StringRef S); 899 900 void formatTextOfDeclaration(const DeclInfo *DI, 901 SmallString<128> &Declaration); 902 903 private: 904 const FullComment *FC; 905 906 /// Output stream for XML. 907 llvm::raw_svector_ostream Result; 908 909 const CommandTraits &Traits; 910 const SourceManager &SM; 911 SimpleFormatContext &FormatRewriterContext; 912 unsigned FormatInMemoryUniqueId; 913 }; 914 915 void getSourceTextOfDeclaration(const DeclInfo *ThisDecl, 916 SmallVectorImpl<char> &Str) { 917 ASTContext &Context = ThisDecl->CurrentDecl->getASTContext(); 918 const LangOptions &LangOpts = Context.getLangOpts(); 919 llvm::raw_svector_ostream OS(Str); 920 PrintingPolicy PPolicy(LangOpts); 921 PPolicy.PolishForDeclaration = true; 922 PPolicy.TerseOutput = true; 923 ThisDecl->CurrentDecl->print(OS, PPolicy, 924 /*Indentation*/0, /*PrintInstantiation*/false); 925 } 926 927 void CommentASTToXMLConverter::formatTextOfDeclaration( 928 const DeclInfo *DI, 929 SmallString<128> &Declaration) { 930 // FIXME. formatting API expects null terminated input string. 931 // There might be more efficient way of doing this. 932 std::string StringDecl = Declaration.str(); 933 934 // Formatter specific code. 935 // Form a unique in memory buffer name. 936 SmallString<128> filename; 937 filename += "xmldecl"; 938 filename += llvm::utostr(FormatInMemoryUniqueId); 939 filename += ".xd"; 940 FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl); 941 SourceLocation Start = 942 FormatRewriterContext.Sources.getLocForStartOfFile(ID).getLocWithOffset(0); 943 unsigned Length = Declaration.size(); 944 945 std::vector<CharSourceRange> 946 Ranges(1, CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length))); 947 ASTContext &Context = DI->CurrentDecl->getASTContext(); 948 const LangOptions &LangOpts = Context.getLangOpts(); 949 Lexer Lex(ID, FormatRewriterContext.Sources.getBuffer(ID), 950 FormatRewriterContext.Sources, LangOpts); 951 tooling::Replacements Replace = 952 reformat(format::getLLVMStyle(), Lex, FormatRewriterContext.Sources, Ranges); 953 applyAllReplacements(Replace, FormatRewriterContext.Rewrite); 954 Declaration = FormatRewriterContext.getRewrittenText(ID); 955 } 956 957 } // end unnamed namespace 958 959 void CommentASTToXMLConverter::visitTextComment(const TextComment *C) { 960 appendToResultWithXMLEscaping(C->getText()); 961 } 962 963 void CommentASTToXMLConverter::visitInlineCommandComment(const InlineCommandComment *C) { 964 // Nothing to render if no arguments supplied. 965 if (C->getNumArgs() == 0) 966 return; 967 968 // Nothing to render if argument is empty. 969 StringRef Arg0 = C->getArgText(0); 970 if (Arg0.empty()) 971 return; 972 973 switch (C->getRenderKind()) { 974 case InlineCommandComment::RenderNormal: 975 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) { 976 appendToResultWithXMLEscaping(C->getArgText(i)); 977 Result << " "; 978 } 979 return; 980 case InlineCommandComment::RenderBold: 981 assert(C->getNumArgs() == 1); 982 Result << "<bold>"; 983 appendToResultWithXMLEscaping(Arg0); 984 Result << "</bold>"; 985 return; 986 case InlineCommandComment::RenderMonospaced: 987 assert(C->getNumArgs() == 1); 988 Result << "<monospaced>"; 989 appendToResultWithXMLEscaping(Arg0); 990 Result << "</monospaced>"; 991 return; 992 case InlineCommandComment::RenderEmphasized: 993 assert(C->getNumArgs() == 1); 994 Result << "<emphasized>"; 995 appendToResultWithXMLEscaping(Arg0); 996 Result << "</emphasized>"; 997 return; 998 } 999 } 1000 1001 void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) { 1002 Result << "<rawHTML><![CDATA["; 1003 PrintHTMLStartTagComment(C, Result); 1004 Result << "]]></rawHTML>"; 1005 } 1006 1007 void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) { 1008 Result << "<rawHTML></" << C->getTagName() << "></rawHTML>"; 1009 } 1010 1011 void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) { 1012 appendParagraphCommentWithKind(C, StringRef()); 1013 } 1014 1015 void CommentASTToXMLConverter::appendParagraphCommentWithKind( 1016 const ParagraphComment *C, 1017 StringRef ParagraphKind) { 1018 if (C->isWhitespace()) 1019 return; 1020 1021 if (ParagraphKind.empty()) 1022 Result << "<Para>"; 1023 else 1024 Result << "<Para kind=\"" << ParagraphKind << "\">"; 1025 1026 for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); 1027 I != E; ++I) { 1028 visit(*I); 1029 } 1030 Result << "</Para>"; 1031 } 1032 1033 void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) { 1034 StringRef ParagraphKind; 1035 1036 switch (C->getCommandID()) { 1037 case CommandTraits::KCI_attention: 1038 case CommandTraits::KCI_author: 1039 case CommandTraits::KCI_authors: 1040 case CommandTraits::KCI_bug: 1041 case CommandTraits::KCI_copyright: 1042 case CommandTraits::KCI_date: 1043 case CommandTraits::KCI_invariant: 1044 case CommandTraits::KCI_note: 1045 case CommandTraits::KCI_post: 1046 case CommandTraits::KCI_pre: 1047 case CommandTraits::KCI_remark: 1048 case CommandTraits::KCI_remarks: 1049 case CommandTraits::KCI_sa: 1050 case CommandTraits::KCI_see: 1051 case CommandTraits::KCI_since: 1052 case CommandTraits::KCI_todo: 1053 case CommandTraits::KCI_version: 1054 case CommandTraits::KCI_warning: 1055 ParagraphKind = C->getCommandName(Traits); 1056 default: 1057 break; 1058 } 1059 1060 appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind); 1061 } 1062 1063 void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) { 1064 Result << "<Parameter><Name>"; 1065 appendToResultWithXMLEscaping(C->isParamIndexValid() ? C->getParamName(FC) 1066 : C->getParamNameAsWritten()); 1067 Result << "</Name>"; 1068 1069 if (C->isParamIndexValid()) 1070 Result << "<Index>" << C->getParamIndex() << "</Index>"; 1071 1072 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">"; 1073 switch (C->getDirection()) { 1074 case ParamCommandComment::In: 1075 Result << "in"; 1076 break; 1077 case ParamCommandComment::Out: 1078 Result << "out"; 1079 break; 1080 case ParamCommandComment::InOut: 1081 Result << "in,out"; 1082 break; 1083 } 1084 Result << "</Direction><Discussion>"; 1085 visit(C->getParagraph()); 1086 Result << "</Discussion></Parameter>"; 1087 } 1088 1089 void CommentASTToXMLConverter::visitTParamCommandComment( 1090 const TParamCommandComment *C) { 1091 Result << "<Parameter><Name>"; 1092 appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC) 1093 : C->getParamNameAsWritten()); 1094 Result << "</Name>"; 1095 1096 if (C->isPositionValid() && C->getDepth() == 1) { 1097 Result << "<Index>" << C->getIndex(0) << "</Index>"; 1098 } 1099 1100 Result << "<Discussion>"; 1101 visit(C->getParagraph()); 1102 Result << "</Discussion></Parameter>"; 1103 } 1104 1105 void CommentASTToXMLConverter::visitVerbatimBlockComment( 1106 const VerbatimBlockComment *C) { 1107 unsigned NumLines = C->getNumLines(); 1108 if (NumLines == 0) 1109 return; 1110 1111 switch (C->getCommandID()) { 1112 case CommandTraits::KCI_code: 1113 Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">"; 1114 break; 1115 default: 1116 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">"; 1117 break; 1118 } 1119 for (unsigned i = 0; i != NumLines; ++i) { 1120 appendToResultWithXMLEscaping(C->getText(i)); 1121 if (i + 1 != NumLines) 1122 Result << '\n'; 1123 } 1124 Result << "</Verbatim>"; 1125 } 1126 1127 void CommentASTToXMLConverter::visitVerbatimBlockLineComment( 1128 const VerbatimBlockLineComment *C) { 1129 llvm_unreachable("should not see this AST node"); 1130 } 1131 1132 void CommentASTToXMLConverter::visitVerbatimLineComment( 1133 const VerbatimLineComment *C) { 1134 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">"; 1135 appendToResultWithXMLEscaping(C->getText()); 1136 Result << "</Verbatim>"; 1137 } 1138 1139 void CommentASTToXMLConverter::visitFullComment(const FullComment *C) { 1140 FullCommentParts Parts(C, Traits); 1141 1142 const DeclInfo *DI = C->getDeclInfo(); 1143 StringRef RootEndTag; 1144 if (DI) { 1145 switch (DI->getKind()) { 1146 case DeclInfo::OtherKind: 1147 RootEndTag = "</Other>"; 1148 Result << "<Other"; 1149 break; 1150 case DeclInfo::FunctionKind: 1151 RootEndTag = "</Function>"; 1152 Result << "<Function"; 1153 switch (DI->TemplateKind) { 1154 case DeclInfo::NotTemplate: 1155 break; 1156 case DeclInfo::Template: 1157 Result << " templateKind=\"template\""; 1158 break; 1159 case DeclInfo::TemplateSpecialization: 1160 Result << " templateKind=\"specialization\""; 1161 break; 1162 case DeclInfo::TemplatePartialSpecialization: 1163 llvm_unreachable("partial specializations of functions " 1164 "are not allowed in C++"); 1165 } 1166 if (DI->IsInstanceMethod) 1167 Result << " isInstanceMethod=\"1\""; 1168 if (DI->IsClassMethod) 1169 Result << " isClassMethod=\"1\""; 1170 break; 1171 case DeclInfo::ClassKind: 1172 RootEndTag = "</Class>"; 1173 Result << "<Class"; 1174 switch (DI->TemplateKind) { 1175 case DeclInfo::NotTemplate: 1176 break; 1177 case DeclInfo::Template: 1178 Result << " templateKind=\"template\""; 1179 break; 1180 case DeclInfo::TemplateSpecialization: 1181 Result << " templateKind=\"specialization\""; 1182 break; 1183 case DeclInfo::TemplatePartialSpecialization: 1184 Result << " templateKind=\"partialSpecialization\""; 1185 break; 1186 } 1187 break; 1188 case DeclInfo::VariableKind: 1189 RootEndTag = "</Variable>"; 1190 Result << "<Variable"; 1191 break; 1192 case DeclInfo::NamespaceKind: 1193 RootEndTag = "</Namespace>"; 1194 Result << "<Namespace"; 1195 break; 1196 case DeclInfo::TypedefKind: 1197 RootEndTag = "</Typedef>"; 1198 Result << "<Typedef"; 1199 break; 1200 case DeclInfo::EnumKind: 1201 RootEndTag = "</Enum>"; 1202 Result << "<Enum"; 1203 break; 1204 } 1205 1206 { 1207 // Print line and column number. 1208 SourceLocation Loc = DI->CurrentDecl->getLocation(); 1209 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); 1210 FileID FID = LocInfo.first; 1211 unsigned FileOffset = LocInfo.second; 1212 1213 if (!FID.isInvalid()) { 1214 if (const FileEntry *FE = SM.getFileEntryForID(FID)) { 1215 Result << " file=\""; 1216 appendToResultWithXMLEscaping(FE->getName()); 1217 Result << "\""; 1218 } 1219 Result << " line=\"" << SM.getLineNumber(FID, FileOffset) 1220 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset) 1221 << "\""; 1222 } 1223 } 1224 1225 // Finish the root tag. 1226 Result << ">"; 1227 1228 bool FoundName = false; 1229 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) { 1230 if (DeclarationName DeclName = ND->getDeclName()) { 1231 Result << "<Name>"; 1232 std::string Name = DeclName.getAsString(); 1233 appendToResultWithXMLEscaping(Name); 1234 FoundName = true; 1235 Result << "</Name>"; 1236 } 1237 } 1238 if (!FoundName) 1239 Result << "<Name><anonymous></Name>"; 1240 1241 { 1242 // Print USR. 1243 SmallString<128> USR; 1244 cxcursor::getDeclCursorUSR(DI->CommentDecl, USR); 1245 if (!USR.empty()) { 1246 Result << "<USR>"; 1247 appendToResultWithXMLEscaping(USR); 1248 Result << "</USR>"; 1249 } 1250 } 1251 } else { 1252 // No DeclInfo -- just emit some root tag and name tag. 1253 RootEndTag = "</Other>"; 1254 Result << "<Other><Name>unknown</Name>"; 1255 } 1256 1257 if (Parts.Headerfile) { 1258 Result << "<Headerfile>"; 1259 visit(Parts.Headerfile); 1260 Result << "</Headerfile>"; 1261 } 1262 1263 { 1264 // Pretty-print the declaration. 1265 Result << "<Declaration>"; 1266 SmallString<128> Declaration; 1267 getSourceTextOfDeclaration(DI, Declaration); 1268 formatTextOfDeclaration(DI, Declaration); 1269 appendToResultWithXMLEscaping(Declaration); 1270 1271 Result << "</Declaration>"; 1272 } 1273 1274 bool FirstParagraphIsBrief = false; 1275 if (Parts.Brief) { 1276 Result << "<Abstract>"; 1277 visit(Parts.Brief); 1278 Result << "</Abstract>"; 1279 } else if (Parts.FirstParagraph) { 1280 Result << "<Abstract>"; 1281 visit(Parts.FirstParagraph); 1282 Result << "</Abstract>"; 1283 FirstParagraphIsBrief = true; 1284 } 1285 1286 if (Parts.TParams.size() != 0) { 1287 Result << "<TemplateParameters>"; 1288 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i) 1289 visit(Parts.TParams[i]); 1290 Result << "</TemplateParameters>"; 1291 } 1292 1293 if (Parts.Params.size() != 0) { 1294 Result << "<Parameters>"; 1295 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i) 1296 visit(Parts.Params[i]); 1297 Result << "</Parameters>"; 1298 } 1299 1300 if (Parts.Returns) { 1301 Result << "<ResultDiscussion>"; 1302 visit(Parts.Returns); 1303 Result << "</ResultDiscussion>"; 1304 } 1305 1306 if (DI->CommentDecl->hasAttrs()) { 1307 const AttrVec &Attrs = DI->CommentDecl->getAttrs(); 1308 for (unsigned i = 0, e = Attrs.size(); i != e; i++) { 1309 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]); 1310 if (!AA) { 1311 if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) { 1312 if (DA->getMessage().empty()) 1313 Result << "<Deprecated/>"; 1314 else { 1315 Result << "<Deprecated>"; 1316 appendToResultWithXMLEscaping(DA->getMessage()); 1317 Result << "</Deprecated>"; 1318 } 1319 } 1320 else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) { 1321 if (UA->getMessage().empty()) 1322 Result << "<Unavailable/>"; 1323 else { 1324 Result << "<Unavailable>"; 1325 appendToResultWithXMLEscaping(UA->getMessage()); 1326 Result << "</Unavailable>"; 1327 } 1328 } 1329 continue; 1330 } 1331 1332 // 'availability' attribute. 1333 Result << "<Availability"; 1334 StringRef Distribution; 1335 if (AA->getPlatform()) { 1336 Distribution = AvailabilityAttr::getPrettyPlatformName( 1337 AA->getPlatform()->getName()); 1338 if (Distribution.empty()) 1339 Distribution = AA->getPlatform()->getName(); 1340 } 1341 Result << " distribution=\"" << Distribution << "\">"; 1342 VersionTuple IntroducedInVersion = AA->getIntroduced(); 1343 if (!IntroducedInVersion.empty()) { 1344 Result << "<IntroducedInVersion>" 1345 << IntroducedInVersion.getAsString() 1346 << "</IntroducedInVersion>"; 1347 } 1348 VersionTuple DeprecatedInVersion = AA->getDeprecated(); 1349 if (!DeprecatedInVersion.empty()) { 1350 Result << "<DeprecatedInVersion>" 1351 << DeprecatedInVersion.getAsString() 1352 << "</DeprecatedInVersion>"; 1353 } 1354 VersionTuple RemovedAfterVersion = AA->getObsoleted(); 1355 if (!RemovedAfterVersion.empty()) { 1356 Result << "<RemovedAfterVersion>" 1357 << RemovedAfterVersion.getAsString() 1358 << "</RemovedAfterVersion>"; 1359 } 1360 StringRef DeprecationSummary = AA->getMessage(); 1361 if (!DeprecationSummary.empty()) { 1362 Result << "<DeprecationSummary>"; 1363 appendToResultWithXMLEscaping(DeprecationSummary); 1364 Result << "</DeprecationSummary>"; 1365 } 1366 if (AA->getUnavailable()) 1367 Result << "<Unavailable/>"; 1368 Result << "</Availability>"; 1369 } 1370 } 1371 1372 { 1373 bool StartTagEmitted = false; 1374 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) { 1375 const Comment *C = Parts.MiscBlocks[i]; 1376 if (FirstParagraphIsBrief && C == Parts.FirstParagraph) 1377 continue; 1378 if (!StartTagEmitted) { 1379 Result << "<Discussion>"; 1380 StartTagEmitted = true; 1381 } 1382 visit(C); 1383 } 1384 if (StartTagEmitted) 1385 Result << "</Discussion>"; 1386 } 1387 1388 Result << RootEndTag; 1389 1390 Result.flush(); 1391 } 1392 1393 void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) { 1394 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) { 1395 const char C = *I; 1396 switch (C) { 1397 case '&': 1398 Result << "&"; 1399 break; 1400 case '<': 1401 Result << "<"; 1402 break; 1403 case '>': 1404 Result << ">"; 1405 break; 1406 case '"': 1407 Result << """; 1408 break; 1409 case '\'': 1410 Result << "'"; 1411 break; 1412 default: 1413 Result << C; 1414 break; 1415 } 1416 } 1417 } 1418 1419 extern "C" { 1420 1421 CXString clang_FullComment_getAsXML(CXComment CXC) { 1422 const FullComment *FC = getASTNodeAs<FullComment>(CXC); 1423 if (!FC) 1424 return cxstring::createNull(); 1425 ASTContext &Context = FC->getDeclInfo()->CurrentDecl->getASTContext(); 1426 CXTranslationUnit TU = CXC.TranslationUnit; 1427 SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager(); 1428 1429 if (!TU->FormatContext) { 1430 TU->FormatContext = new SimpleFormatContext(Context.getLangOpts()); 1431 } else if ((TU->FormatInMemoryUniqueId % 1000) == 0) { 1432 // Delete after some number of iterators, so the buffers don't grow 1433 // too large. 1434 delete TU->FormatContext; 1435 TU->FormatContext = new SimpleFormatContext(Context.getLangOpts()); 1436 } 1437 1438 SmallString<1024> XML; 1439 CommentASTToXMLConverter Converter(FC, XML, getCommandTraits(CXC), SM, 1440 *TU->FormatContext, 1441 TU->FormatInMemoryUniqueId++); 1442 Converter.visit(FC); 1443 return cxstring::createDup(XML.str()); 1444 } 1445 1446 } // end extern "C" 1447 1448