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