CXComment.cpp revision 40902d817e5a73850045d8a0c9795bc5047ee000
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 "llvm/ADT/StringExtras.h" 24#include "llvm/ADT/StringSwitch.h" 25#include "llvm/Support/ErrorHandling.h" 26#include "llvm/Support/raw_ostream.h" 27#include "clang/Format/Format.h" 28#include "clang/Lex/Lexer.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. This conditional is TEMPORARY. We don't want to break multiple 921 // large tests each time Format.cpp changes. This condition will 922 // go away and formatting will happen for all declarations. 923 if (!getenv("LIBCLANG_ACTIVATE_FORMAT")) 924 return; 925 926 // FIXME. formatting API expects null terminated input string. 927 // There might be more efficient way of doing this. 928 std::string StringDecl = Declaration.str(); 929 930 // Formatter specific code. 931 // Form a unique in memory buffer name. 932 llvm::SmallString<128> filename; 933 filename += "xmldecl"; 934 filename += llvm::utostr(FormatInMemoryUniqueId); 935 filename += ".xd"; 936 FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl); 937 SourceLocation Start = 938 FormatRewriterContext.Sources.getLocForStartOfFile(ID).getLocWithOffset(0); 939 unsigned Length = Declaration.size(); 940 941 std::vector<CharSourceRange> 942 Ranges(1, CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length))); 943 ASTContext &Context = DI->CurrentDecl->getASTContext(); 944 const LangOptions &LangOpts = Context.getLangOpts(); 945 Lexer Lex(ID, FormatRewriterContext.Sources.getBuffer(ID), 946 FormatRewriterContext.Sources, LangOpts); 947 tooling::Replacements Replace = 948 reformat(format::getLLVMStyle(), Lex, FormatRewriterContext.Sources, Ranges); 949 applyAllReplacements(Replace, FormatRewriterContext.Rewrite); 950 Declaration = FormatRewriterContext.getRewrittenText(ID); 951} 952 953} // end unnamed namespace 954 955void CommentASTToXMLConverter::visitTextComment(const TextComment *C) { 956 appendToResultWithXMLEscaping(C->getText()); 957} 958 959void CommentASTToXMLConverter::visitInlineCommandComment(const InlineCommandComment *C) { 960 // Nothing to render if no arguments supplied. 961 if (C->getNumArgs() == 0) 962 return; 963 964 // Nothing to render if argument is empty. 965 StringRef Arg0 = C->getArgText(0); 966 if (Arg0.empty()) 967 return; 968 969 switch (C->getRenderKind()) { 970 case InlineCommandComment::RenderNormal: 971 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) { 972 appendToResultWithXMLEscaping(C->getArgText(i)); 973 Result << " "; 974 } 975 return; 976 case InlineCommandComment::RenderBold: 977 assert(C->getNumArgs() == 1); 978 Result << "<bold>"; 979 appendToResultWithXMLEscaping(Arg0); 980 Result << "</bold>"; 981 return; 982 case InlineCommandComment::RenderMonospaced: 983 assert(C->getNumArgs() == 1); 984 Result << "<monospaced>"; 985 appendToResultWithXMLEscaping(Arg0); 986 Result << "</monospaced>"; 987 return; 988 case InlineCommandComment::RenderEmphasized: 989 assert(C->getNumArgs() == 1); 990 Result << "<emphasized>"; 991 appendToResultWithXMLEscaping(Arg0); 992 Result << "</emphasized>"; 993 return; 994 } 995} 996 997void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) { 998 Result << "<rawHTML><![CDATA["; 999 PrintHTMLStartTagComment(C, Result); 1000 Result << "]]></rawHTML>"; 1001} 1002 1003void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) { 1004 Result << "<rawHTML></" << C->getTagName() << "></rawHTML>"; 1005} 1006 1007void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) { 1008 if (C->isWhitespace()) 1009 return; 1010 1011 Result << "<Para>"; 1012 for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); 1013 I != E; ++I) { 1014 visit(*I); 1015 } 1016 Result << "</Para>"; 1017} 1018 1019void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) { 1020 visit(C->getParagraph()); 1021} 1022 1023void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) { 1024 Result << "<Parameter><Name>"; 1025 appendToResultWithXMLEscaping(C->isParamIndexValid() ? C->getParamName(FC) 1026 : C->getParamNameAsWritten()); 1027 Result << "</Name>"; 1028 1029 if (C->isParamIndexValid()) 1030 Result << "<Index>" << C->getParamIndex() << "</Index>"; 1031 1032 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">"; 1033 switch (C->getDirection()) { 1034 case ParamCommandComment::In: 1035 Result << "in"; 1036 break; 1037 case ParamCommandComment::Out: 1038 Result << "out"; 1039 break; 1040 case ParamCommandComment::InOut: 1041 Result << "in,out"; 1042 break; 1043 } 1044 Result << "</Direction><Discussion>"; 1045 visit(C->getParagraph()); 1046 Result << "</Discussion></Parameter>"; 1047} 1048 1049void CommentASTToXMLConverter::visitTParamCommandComment( 1050 const TParamCommandComment *C) { 1051 Result << "<Parameter><Name>"; 1052 appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC) 1053 : C->getParamNameAsWritten()); 1054 Result << "</Name>"; 1055 1056 if (C->isPositionValid() && C->getDepth() == 1) { 1057 Result << "<Index>" << C->getIndex(0) << "</Index>"; 1058 } 1059 1060 Result << "<Discussion>"; 1061 visit(C->getParagraph()); 1062 Result << "</Discussion></Parameter>"; 1063} 1064 1065void CommentASTToXMLConverter::visitVerbatimBlockComment( 1066 const VerbatimBlockComment *C) { 1067 unsigned NumLines = C->getNumLines(); 1068 if (NumLines == 0) 1069 return; 1070 1071 Result << llvm::StringSwitch<const char *>(C->getCommandName(Traits)) 1072 .Case("code", "<Verbatim xml:space=\"preserve\" kind=\"code\">") 1073 .Default("<Verbatim xml:space=\"preserve\" kind=\"verbatim\">"); 1074 for (unsigned i = 0; i != NumLines; ++i) { 1075 appendToResultWithXMLEscaping(C->getText(i)); 1076 if (i + 1 != NumLines) 1077 Result << '\n'; 1078 } 1079 Result << "</Verbatim>"; 1080} 1081 1082void CommentASTToXMLConverter::visitVerbatimBlockLineComment( 1083 const VerbatimBlockLineComment *C) { 1084 llvm_unreachable("should not see this AST node"); 1085} 1086 1087void CommentASTToXMLConverter::visitVerbatimLineComment( 1088 const VerbatimLineComment *C) { 1089 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">"; 1090 appendToResultWithXMLEscaping(C->getText()); 1091 Result << "</Verbatim>"; 1092} 1093 1094void CommentASTToXMLConverter::visitFullComment(const FullComment *C) { 1095 FullCommentParts Parts(C, Traits); 1096 1097 const DeclInfo *DI = C->getDeclInfo(); 1098 StringRef RootEndTag; 1099 if (DI) { 1100 switch (DI->getKind()) { 1101 case DeclInfo::OtherKind: 1102 RootEndTag = "</Other>"; 1103 Result << "<Other"; 1104 break; 1105 case DeclInfo::FunctionKind: 1106 RootEndTag = "</Function>"; 1107 Result << "<Function"; 1108 switch (DI->TemplateKind) { 1109 case DeclInfo::NotTemplate: 1110 break; 1111 case DeclInfo::Template: 1112 Result << " templateKind=\"template\""; 1113 break; 1114 case DeclInfo::TemplateSpecialization: 1115 Result << " templateKind=\"specialization\""; 1116 break; 1117 case DeclInfo::TemplatePartialSpecialization: 1118 llvm_unreachable("partial specializations of functions " 1119 "are not allowed in C++"); 1120 } 1121 if (DI->IsInstanceMethod) 1122 Result << " isInstanceMethod=\"1\""; 1123 if (DI->IsClassMethod) 1124 Result << " isClassMethod=\"1\""; 1125 break; 1126 case DeclInfo::ClassKind: 1127 RootEndTag = "</Class>"; 1128 Result << "<Class"; 1129 switch (DI->TemplateKind) { 1130 case DeclInfo::NotTemplate: 1131 break; 1132 case DeclInfo::Template: 1133 Result << " templateKind=\"template\""; 1134 break; 1135 case DeclInfo::TemplateSpecialization: 1136 Result << " templateKind=\"specialization\""; 1137 break; 1138 case DeclInfo::TemplatePartialSpecialization: 1139 Result << " templateKind=\"partialSpecialization\""; 1140 break; 1141 } 1142 break; 1143 case DeclInfo::VariableKind: 1144 RootEndTag = "</Variable>"; 1145 Result << "<Variable"; 1146 break; 1147 case DeclInfo::NamespaceKind: 1148 RootEndTag = "</Namespace>"; 1149 Result << "<Namespace"; 1150 break; 1151 case DeclInfo::TypedefKind: 1152 RootEndTag = "</Typedef>"; 1153 Result << "<Typedef"; 1154 break; 1155 case DeclInfo::EnumKind: 1156 RootEndTag = "</Enum>"; 1157 Result << "<Enum"; 1158 break; 1159 } 1160 1161 { 1162 // Print line and column number. 1163 SourceLocation Loc = DI->CurrentDecl->getLocation(); 1164 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); 1165 FileID FID = LocInfo.first; 1166 unsigned FileOffset = LocInfo.second; 1167 1168 if (!FID.isInvalid()) { 1169 if (const FileEntry *FE = SM.getFileEntryForID(FID)) { 1170 Result << " file=\""; 1171 appendToResultWithXMLEscaping(FE->getName()); 1172 Result << "\""; 1173 } 1174 Result << " line=\"" << SM.getLineNumber(FID, FileOffset) 1175 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset) 1176 << "\""; 1177 } 1178 } 1179 1180 // Finish the root tag. 1181 Result << ">"; 1182 1183 bool FoundName = false; 1184 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) { 1185 if (DeclarationName DeclName = ND->getDeclName()) { 1186 Result << "<Name>"; 1187 std::string Name = DeclName.getAsString(); 1188 appendToResultWithXMLEscaping(Name); 1189 FoundName = true; 1190 Result << "</Name>"; 1191 } 1192 } 1193 if (!FoundName) 1194 Result << "<Name><anonymous></Name>"; 1195 1196 { 1197 // Print USR. 1198 SmallString<128> USR; 1199 cxcursor::getDeclCursorUSR(DI->CommentDecl, USR); 1200 if (!USR.empty()) { 1201 Result << "<USR>"; 1202 appendToResultWithXMLEscaping(USR); 1203 Result << "</USR>"; 1204 } 1205 } 1206 } else { 1207 // No DeclInfo -- just emit some root tag and name tag. 1208 RootEndTag = "</Other>"; 1209 Result << "<Other><Name>unknown</Name>"; 1210 } 1211 1212 { 1213 // Pretty-print the declaration. 1214 Result << "<Declaration>"; 1215 SmallString<128> Declaration; 1216 getSourceTextOfDeclaration(DI, Declaration); 1217 formatTextOfDeclaration(DI, Declaration); 1218 appendToResultWithXMLEscaping(Declaration); 1219 1220 Result << "</Declaration>"; 1221 } 1222 1223 bool FirstParagraphIsBrief = false; 1224 if (Parts.Brief) { 1225 Result << "<Abstract>"; 1226 visit(Parts.Brief); 1227 Result << "</Abstract>"; 1228 } else if (Parts.FirstParagraph) { 1229 Result << "<Abstract>"; 1230 visit(Parts.FirstParagraph); 1231 Result << "</Abstract>"; 1232 FirstParagraphIsBrief = true; 1233 } 1234 1235 if (Parts.TParams.size() != 0) { 1236 Result << "<TemplateParameters>"; 1237 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i) 1238 visit(Parts.TParams[i]); 1239 Result << "</TemplateParameters>"; 1240 } 1241 1242 if (Parts.Params.size() != 0) { 1243 Result << "<Parameters>"; 1244 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i) 1245 visit(Parts.Params[i]); 1246 Result << "</Parameters>"; 1247 } 1248 1249 if (Parts.Returns) { 1250 Result << "<ResultDiscussion>"; 1251 visit(Parts.Returns); 1252 Result << "</ResultDiscussion>"; 1253 } 1254 1255 if (DI->CommentDecl->hasAttrs()) { 1256 const AttrVec &Attrs = DI->CommentDecl->getAttrs(); 1257 for (unsigned i = 0, e = Attrs.size(); i != e; i++) { 1258 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]); 1259 if (!AA) { 1260 if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) { 1261 if (DA->getMessage().empty()) 1262 Result << "<Deprecated/>"; 1263 else { 1264 Result << "<Deprecated>"; 1265 appendToResultWithXMLEscaping(DA->getMessage()); 1266 Result << "</Deprecated>"; 1267 } 1268 } 1269 else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) { 1270 if (UA->getMessage().empty()) 1271 Result << "<Unavailable/>"; 1272 else { 1273 Result << "<Unavailable>"; 1274 appendToResultWithXMLEscaping(UA->getMessage()); 1275 Result << "</Unavailable>"; 1276 } 1277 } 1278 continue; 1279 } 1280 1281 // 'availability' attribute. 1282 Result << "<Availability"; 1283 StringRef Distribution; 1284 if (AA->getPlatform()) { 1285 Distribution = AvailabilityAttr::getPrettyPlatformName( 1286 AA->getPlatform()->getName()); 1287 if (Distribution.empty()) 1288 Distribution = AA->getPlatform()->getName(); 1289 } 1290 Result << " distribution=\"" << Distribution << "\">"; 1291 VersionTuple IntroducedInVersion = AA->getIntroduced(); 1292 if (!IntroducedInVersion.empty()) { 1293 Result << "<IntroducedInVersion>" 1294 << IntroducedInVersion.getAsString() 1295 << "</IntroducedInVersion>"; 1296 } 1297 VersionTuple DeprecatedInVersion = AA->getDeprecated(); 1298 if (!DeprecatedInVersion.empty()) { 1299 Result << "<DeprecatedInVersion>" 1300 << DeprecatedInVersion.getAsString() 1301 << "</DeprecatedInVersion>"; 1302 } 1303 VersionTuple RemovedAfterVersion = AA->getObsoleted(); 1304 if (!RemovedAfterVersion.empty()) { 1305 Result << "<RemovedAfterVersion>" 1306 << RemovedAfterVersion.getAsString() 1307 << "</RemovedAfterVersion>"; 1308 } 1309 StringRef DeprecationSummary = AA->getMessage(); 1310 if (!DeprecationSummary.empty()) { 1311 Result << "<DeprecationSummary>"; 1312 appendToResultWithXMLEscaping(DeprecationSummary); 1313 Result << "</DeprecationSummary>"; 1314 } 1315 if (AA->getUnavailable()) 1316 Result << "<Unavailable/>"; 1317 Result << "</Availability>"; 1318 } 1319 } 1320 1321 { 1322 bool StartTagEmitted = false; 1323 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) { 1324 const Comment *C = Parts.MiscBlocks[i]; 1325 if (FirstParagraphIsBrief && C == Parts.FirstParagraph) 1326 continue; 1327 if (!StartTagEmitted) { 1328 Result << "<Discussion>"; 1329 StartTagEmitted = true; 1330 } 1331 visit(C); 1332 } 1333 if (StartTagEmitted) 1334 Result << "</Discussion>"; 1335 } 1336 1337 Result << RootEndTag; 1338 1339 Result.flush(); 1340} 1341 1342void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) { 1343 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) { 1344 const char C = *I; 1345 switch (C) { 1346 case '&': 1347 Result << "&"; 1348 break; 1349 case '<': 1350 Result << "<"; 1351 break; 1352 case '>': 1353 Result << ">"; 1354 break; 1355 case '"': 1356 Result << """; 1357 break; 1358 case '\'': 1359 Result << "'"; 1360 break; 1361 default: 1362 Result << C; 1363 break; 1364 } 1365 } 1366} 1367 1368extern "C" { 1369 1370CXString clang_FullComment_getAsXML(CXComment CXC) { 1371 const FullComment *FC = getASTNodeAs<FullComment>(CXC); 1372 if (!FC) 1373 return createCXString((const char *) 0); 1374 ASTContext &Context = FC->getDeclInfo()->CurrentDecl->getASTContext(); 1375 CXTranslationUnit TU = CXC.TranslationUnit; 1376 SourceManager &SM = static_cast<ASTUnit *>(TU->TUData)->getSourceManager(); 1377 1378 SimpleFormatContext *SFC = 1379 static_cast<SimpleFormatContext*>(TU->FormatContext); 1380 if (!SFC) { 1381 SFC = new SimpleFormatContext(Context.getLangOpts()); 1382 TU->FormatContext = SFC; 1383 } else if ((TU->FormatInMemoryUniqueId % 1000) == 0) { 1384 // Delete after some number of iterators, so the buffers don't grow 1385 // too large. 1386 delete SFC; 1387 SFC = new SimpleFormatContext(Context.getLangOpts()); 1388 TU->FormatContext = SFC; 1389 } 1390 1391 SmallString<1024> XML; 1392 CommentASTToXMLConverter Converter(FC, XML, getCommandTraits(CXC), SM, 1393 *SFC, TU->FormatInMemoryUniqueId++); 1394 Converter.visit(FC); 1395 return createCXString(XML.str(), /* DupString = */ true); 1396} 1397 1398} // end extern "C" 1399 1400