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