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