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