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