CXComment.cpp revision 4c494f4af76017e2bb389a3a96cbcdbb8973cf97
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 31using namespace clang; 32using namespace clang::comments; 33using namespace clang::cxcomment; 34 35extern "C" { 36 37enum 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 85unsigned 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 93CXComment 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 101unsigned 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 115unsigned 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 123CXString 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 131CXString 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 140enum CXCommentInlineCommandRenderKind 141clang_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 162unsigned 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 170CXString 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 179CXString 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 187unsigned 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 195unsigned 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 203CXString 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 211CXString 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 219CXString 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 228unsigned 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 236CXString 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 245CXComment 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 253CXString 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 261unsigned 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 269unsigned 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 277unsigned 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 285enum 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 304CXString 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 312unsigned 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 320unsigned 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 328unsigned 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 336CXString 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 345CXString 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 359namespace { 360 361/// This comparison will sort parameters with valid index by index and 362/// invalid (unresolved) parameters last. 363class ParamCommandCommentCompareIndex { 364public: 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. 382class TParamCommandCommentComparePosition { 383public: 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. 407struct 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 421FullCommentParts::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 520void 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 540class CommentASTToHTMLConverter : 541 public ConstCommentVisitor<CommentASTToHTMLConverter> { 542public: 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 575private: 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 584void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) { 585 appendToResultWithHTMLEscaping(C->getText()); 586} 587 588void 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 628void CommentASTToHTMLConverter::visitHTMLStartTagComment( 629 const HTMLStartTagComment *C) { 630 PrintHTMLStartTagComment(C, Result); 631} 632 633void CommentASTToHTMLConverter::visitHTMLEndTagComment( 634 const HTMLEndTagComment *C) { 635 Result << "</" << C->getTagName() << ">"; 636} 637 638void 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 651void 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 671void 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 695void 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 726void 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 741void CommentASTToHTMLConverter::visitVerbatimBlockLineComment( 742 const VerbatimBlockLineComment *C) { 743 llvm_unreachable("should not see this AST node"); 744} 745 746void CommentASTToHTMLConverter::visitVerbatimLineComment( 747 const VerbatimLineComment *C) { 748 Result << "<pre>"; 749 appendToResultWithHTMLEscaping(C->getText()); 750 Result << "</pre>"; 751} 752 753void 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 795void 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 806void 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 835extern "C" { 836 837CXString 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 848CXString 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 861namespace { 862class CommentASTToXMLConverter : 863 public ConstCommentVisitor<CommentASTToXMLConverter> { 864public: 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 903private: 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 915void 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 927void 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 959void CommentASTToXMLConverter::visitTextComment(const TextComment *C) { 960 appendToResultWithXMLEscaping(C->getText()); 961} 962 963void 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 1001void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) { 1002 Result << "<rawHTML><![CDATA["; 1003 PrintHTMLStartTagComment(C, Result); 1004 Result << "]]></rawHTML>"; 1005} 1006 1007void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) { 1008 Result << "<rawHTML></" << C->getTagName() << "></rawHTML>"; 1009} 1010 1011void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) { 1012 appendParagraphCommentWithKind(C, StringRef()); 1013} 1014 1015void 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 1033void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) { 1034 StringRef ParagraphKind; 1035 1036 switch (C->getCommandID()) { 1037 case CommandTraits::KCI_author: 1038 case CommandTraits::KCI_authors: 1039 case CommandTraits::KCI_bug: 1040 case CommandTraits::KCI_copyright: 1041 case CommandTraits::KCI_date: 1042 case CommandTraits::KCI_invariant: 1043 case CommandTraits::KCI_note: 1044 case CommandTraits::KCI_post: 1045 case CommandTraits::KCI_pre: 1046 case CommandTraits::KCI_remark: 1047 case CommandTraits::KCI_remarks: 1048 case CommandTraits::KCI_sa: 1049 case CommandTraits::KCI_see: 1050 case CommandTraits::KCI_since: 1051 case CommandTraits::KCI_todo: 1052 case CommandTraits::KCI_version: 1053 case CommandTraits::KCI_warning: 1054 ParagraphKind = C->getCommandName(Traits); 1055 default: 1056 break; 1057 } 1058 1059 appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind); 1060} 1061 1062void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) { 1063 Result << "<Parameter><Name>"; 1064 appendToResultWithXMLEscaping(C->isParamIndexValid() ? C->getParamName(FC) 1065 : C->getParamNameAsWritten()); 1066 Result << "</Name>"; 1067 1068 if (C->isParamIndexValid()) 1069 Result << "<Index>" << C->getParamIndex() << "</Index>"; 1070 1071 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">"; 1072 switch (C->getDirection()) { 1073 case ParamCommandComment::In: 1074 Result << "in"; 1075 break; 1076 case ParamCommandComment::Out: 1077 Result << "out"; 1078 break; 1079 case ParamCommandComment::InOut: 1080 Result << "in,out"; 1081 break; 1082 } 1083 Result << "</Direction><Discussion>"; 1084 visit(C->getParagraph()); 1085 Result << "</Discussion></Parameter>"; 1086} 1087 1088void CommentASTToXMLConverter::visitTParamCommandComment( 1089 const TParamCommandComment *C) { 1090 Result << "<Parameter><Name>"; 1091 appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC) 1092 : C->getParamNameAsWritten()); 1093 Result << "</Name>"; 1094 1095 if (C->isPositionValid() && C->getDepth() == 1) { 1096 Result << "<Index>" << C->getIndex(0) << "</Index>"; 1097 } 1098 1099 Result << "<Discussion>"; 1100 visit(C->getParagraph()); 1101 Result << "</Discussion></Parameter>"; 1102} 1103 1104void CommentASTToXMLConverter::visitVerbatimBlockComment( 1105 const VerbatimBlockComment *C) { 1106 unsigned NumLines = C->getNumLines(); 1107 if (NumLines == 0) 1108 return; 1109 1110 switch (C->getCommandID()) { 1111 case CommandTraits::KCI_code: 1112 Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">"; 1113 break; 1114 default: 1115 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">"; 1116 break; 1117 } 1118 for (unsigned i = 0; i != NumLines; ++i) { 1119 appendToResultWithXMLEscaping(C->getText(i)); 1120 if (i + 1 != NumLines) 1121 Result << '\n'; 1122 } 1123 Result << "</Verbatim>"; 1124} 1125 1126void CommentASTToXMLConverter::visitVerbatimBlockLineComment( 1127 const VerbatimBlockLineComment *C) { 1128 llvm_unreachable("should not see this AST node"); 1129} 1130 1131void CommentASTToXMLConverter::visitVerbatimLineComment( 1132 const VerbatimLineComment *C) { 1133 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">"; 1134 appendToResultWithXMLEscaping(C->getText()); 1135 Result << "</Verbatim>"; 1136} 1137 1138void CommentASTToXMLConverter::visitFullComment(const FullComment *C) { 1139 FullCommentParts Parts(C, Traits); 1140 1141 const DeclInfo *DI = C->getDeclInfo(); 1142 StringRef RootEndTag; 1143 if (DI) { 1144 switch (DI->getKind()) { 1145 case DeclInfo::OtherKind: 1146 RootEndTag = "</Other>"; 1147 Result << "<Other"; 1148 break; 1149 case DeclInfo::FunctionKind: 1150 RootEndTag = "</Function>"; 1151 Result << "<Function"; 1152 switch (DI->TemplateKind) { 1153 case DeclInfo::NotTemplate: 1154 break; 1155 case DeclInfo::Template: 1156 Result << " templateKind=\"template\""; 1157 break; 1158 case DeclInfo::TemplateSpecialization: 1159 Result << " templateKind=\"specialization\""; 1160 break; 1161 case DeclInfo::TemplatePartialSpecialization: 1162 llvm_unreachable("partial specializations of functions " 1163 "are not allowed in C++"); 1164 } 1165 if (DI->IsInstanceMethod) 1166 Result << " isInstanceMethod=\"1\""; 1167 if (DI->IsClassMethod) 1168 Result << " isClassMethod=\"1\""; 1169 break; 1170 case DeclInfo::ClassKind: 1171 RootEndTag = "</Class>"; 1172 Result << "<Class"; 1173 switch (DI->TemplateKind) { 1174 case DeclInfo::NotTemplate: 1175 break; 1176 case DeclInfo::Template: 1177 Result << " templateKind=\"template\""; 1178 break; 1179 case DeclInfo::TemplateSpecialization: 1180 Result << " templateKind=\"specialization\""; 1181 break; 1182 case DeclInfo::TemplatePartialSpecialization: 1183 Result << " templateKind=\"partialSpecialization\""; 1184 break; 1185 } 1186 break; 1187 case DeclInfo::VariableKind: 1188 RootEndTag = "</Variable>"; 1189 Result << "<Variable"; 1190 break; 1191 case DeclInfo::NamespaceKind: 1192 RootEndTag = "</Namespace>"; 1193 Result << "<Namespace"; 1194 break; 1195 case DeclInfo::TypedefKind: 1196 RootEndTag = "</Typedef>"; 1197 Result << "<Typedef"; 1198 break; 1199 case DeclInfo::EnumKind: 1200 RootEndTag = "</Enum>"; 1201 Result << "<Enum"; 1202 break; 1203 } 1204 1205 { 1206 // Print line and column number. 1207 SourceLocation Loc = DI->CurrentDecl->getLocation(); 1208 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); 1209 FileID FID = LocInfo.first; 1210 unsigned FileOffset = LocInfo.second; 1211 1212 if (!FID.isInvalid()) { 1213 if (const FileEntry *FE = SM.getFileEntryForID(FID)) { 1214 Result << " file=\""; 1215 appendToResultWithXMLEscaping(FE->getName()); 1216 Result << "\""; 1217 } 1218 Result << " line=\"" << SM.getLineNumber(FID, FileOffset) 1219 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset) 1220 << "\""; 1221 } 1222 } 1223 1224 // Finish the root tag. 1225 Result << ">"; 1226 1227 bool FoundName = false; 1228 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) { 1229 if (DeclarationName DeclName = ND->getDeclName()) { 1230 Result << "<Name>"; 1231 std::string Name = DeclName.getAsString(); 1232 appendToResultWithXMLEscaping(Name); 1233 FoundName = true; 1234 Result << "</Name>"; 1235 } 1236 } 1237 if (!FoundName) 1238 Result << "<Name><anonymous></Name>"; 1239 1240 { 1241 // Print USR. 1242 SmallString<128> USR; 1243 cxcursor::getDeclCursorUSR(DI->CommentDecl, USR); 1244 if (!USR.empty()) { 1245 Result << "<USR>"; 1246 appendToResultWithXMLEscaping(USR); 1247 Result << "</USR>"; 1248 } 1249 } 1250 } else { 1251 // No DeclInfo -- just emit some root tag and name tag. 1252 RootEndTag = "</Other>"; 1253 Result << "<Other><Name>unknown</Name>"; 1254 } 1255 1256 if (Parts.Headerfile) { 1257 Result << "<Headerfile>"; 1258 visit(Parts.Headerfile); 1259 Result << "</Headerfile>"; 1260 } 1261 1262 { 1263 // Pretty-print the declaration. 1264 Result << "<Declaration>"; 1265 SmallString<128> Declaration; 1266 getSourceTextOfDeclaration(DI, Declaration); 1267 formatTextOfDeclaration(DI, Declaration); 1268 appendToResultWithXMLEscaping(Declaration); 1269 1270 Result << "</Declaration>"; 1271 } 1272 1273 bool FirstParagraphIsBrief = false; 1274 if (Parts.Brief) { 1275 Result << "<Abstract>"; 1276 visit(Parts.Brief); 1277 Result << "</Abstract>"; 1278 } else if (Parts.FirstParagraph) { 1279 Result << "<Abstract>"; 1280 visit(Parts.FirstParagraph); 1281 Result << "</Abstract>"; 1282 FirstParagraphIsBrief = true; 1283 } 1284 1285 if (Parts.TParams.size() != 0) { 1286 Result << "<TemplateParameters>"; 1287 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i) 1288 visit(Parts.TParams[i]); 1289 Result << "</TemplateParameters>"; 1290 } 1291 1292 if (Parts.Params.size() != 0) { 1293 Result << "<Parameters>"; 1294 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i) 1295 visit(Parts.Params[i]); 1296 Result << "</Parameters>"; 1297 } 1298 1299 if (Parts.Returns) { 1300 Result << "<ResultDiscussion>"; 1301 visit(Parts.Returns); 1302 Result << "</ResultDiscussion>"; 1303 } 1304 1305 if (DI->CommentDecl->hasAttrs()) { 1306 const AttrVec &Attrs = DI->CommentDecl->getAttrs(); 1307 for (unsigned i = 0, e = Attrs.size(); i != e; i++) { 1308 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]); 1309 if (!AA) { 1310 if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) { 1311 if (DA->getMessage().empty()) 1312 Result << "<Deprecated/>"; 1313 else { 1314 Result << "<Deprecated>"; 1315 appendToResultWithXMLEscaping(DA->getMessage()); 1316 Result << "</Deprecated>"; 1317 } 1318 } 1319 else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) { 1320 if (UA->getMessage().empty()) 1321 Result << "<Unavailable/>"; 1322 else { 1323 Result << "<Unavailable>"; 1324 appendToResultWithXMLEscaping(UA->getMessage()); 1325 Result << "</Unavailable>"; 1326 } 1327 } 1328 continue; 1329 } 1330 1331 // 'availability' attribute. 1332 Result << "<Availability"; 1333 StringRef Distribution; 1334 if (AA->getPlatform()) { 1335 Distribution = AvailabilityAttr::getPrettyPlatformName( 1336 AA->getPlatform()->getName()); 1337 if (Distribution.empty()) 1338 Distribution = AA->getPlatform()->getName(); 1339 } 1340 Result << " distribution=\"" << Distribution << "\">"; 1341 VersionTuple IntroducedInVersion = AA->getIntroduced(); 1342 if (!IntroducedInVersion.empty()) { 1343 Result << "<IntroducedInVersion>" 1344 << IntroducedInVersion.getAsString() 1345 << "</IntroducedInVersion>"; 1346 } 1347 VersionTuple DeprecatedInVersion = AA->getDeprecated(); 1348 if (!DeprecatedInVersion.empty()) { 1349 Result << "<DeprecatedInVersion>" 1350 << DeprecatedInVersion.getAsString() 1351 << "</DeprecatedInVersion>"; 1352 } 1353 VersionTuple RemovedAfterVersion = AA->getObsoleted(); 1354 if (!RemovedAfterVersion.empty()) { 1355 Result << "<RemovedAfterVersion>" 1356 << RemovedAfterVersion.getAsString() 1357 << "</RemovedAfterVersion>"; 1358 } 1359 StringRef DeprecationSummary = AA->getMessage(); 1360 if (!DeprecationSummary.empty()) { 1361 Result << "<DeprecationSummary>"; 1362 appendToResultWithXMLEscaping(DeprecationSummary); 1363 Result << "</DeprecationSummary>"; 1364 } 1365 if (AA->getUnavailable()) 1366 Result << "<Unavailable/>"; 1367 Result << "</Availability>"; 1368 } 1369 } 1370 1371 { 1372 bool StartTagEmitted = false; 1373 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) { 1374 const Comment *C = Parts.MiscBlocks[i]; 1375 if (FirstParagraphIsBrief && C == Parts.FirstParagraph) 1376 continue; 1377 if (!StartTagEmitted) { 1378 Result << "<Discussion>"; 1379 StartTagEmitted = true; 1380 } 1381 visit(C); 1382 } 1383 if (StartTagEmitted) 1384 Result << "</Discussion>"; 1385 } 1386 1387 Result << RootEndTag; 1388 1389 Result.flush(); 1390} 1391 1392void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) { 1393 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) { 1394 const char C = *I; 1395 switch (C) { 1396 case '&': 1397 Result << "&"; 1398 break; 1399 case '<': 1400 Result << "<"; 1401 break; 1402 case '>': 1403 Result << ">"; 1404 break; 1405 case '"': 1406 Result << """; 1407 break; 1408 case '\'': 1409 Result << "'"; 1410 break; 1411 default: 1412 Result << C; 1413 break; 1414 } 1415 } 1416} 1417 1418extern "C" { 1419 1420CXString clang_FullComment_getAsXML(CXComment CXC) { 1421 const FullComment *FC = getASTNodeAs<FullComment>(CXC); 1422 if (!FC) 1423 return cxstring::createNull(); 1424 ASTContext &Context = FC->getDeclInfo()->CurrentDecl->getASTContext(); 1425 CXTranslationUnit TU = CXC.TranslationUnit; 1426 SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager(); 1427 1428 if (!TU->FormatContext) { 1429 TU->FormatContext = new SimpleFormatContext(Context.getLangOpts()); 1430 } else if ((TU->FormatInMemoryUniqueId % 1000) == 0) { 1431 // Delete after some number of iterators, so the buffers don't grow 1432 // too large. 1433 delete TU->FormatContext; 1434 TU->FormatContext = new SimpleFormatContext(Context.getLangOpts()); 1435 } 1436 1437 SmallString<1024> XML; 1438 CommentASTToXMLConverter Converter(FC, XML, getCommandTraits(CXC), SM, 1439 *TU->FormatContext, 1440 TU->FormatInMemoryUniqueId++); 1441 Converter.visit(FC); 1442 return cxstring::createDup(XML.str()); 1443} 1444 1445} // end extern "C" 1446 1447