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