CXComment.cpp revision 97e5bc2643dd1478ca10d1b9a6581f332801c958
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 SmallVector<const BlockCommandComment *, 4> 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) { 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 (Info->IsReturnsCommand) { 456 Returns.push_back(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.size() != 0) { 790 Result << "<div class=\"result-discussion\">"; 791 for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i) 792 visit(Parts.Returns[i]); 793 Result << "</div>"; 794 } 795 796 Result.flush(); 797} 798 799void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment( 800 const ParagraphComment *C) { 801 if (!C) 802 return; 803 804 for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); 805 I != E; ++I) { 806 visit(*I); 807 } 808} 809 810void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) { 811 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) { 812 const char C = *I; 813 switch (C) { 814 case '&': 815 Result << "&"; 816 break; 817 case '<': 818 Result << "<"; 819 break; 820 case '>': 821 Result << ">"; 822 break; 823 case '"': 824 Result << """; 825 break; 826 case '\'': 827 Result << "'"; 828 break; 829 case '/': 830 Result << "/"; 831 break; 832 default: 833 Result << C; 834 break; 835 } 836 } 837} 838 839extern "C" { 840 841CXString clang_HTMLTagComment_getAsString(CXComment CXC) { 842 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC); 843 if (!HTC) 844 return cxstring::createNull(); 845 846 SmallString<128> HTML; 847 CommentASTToHTMLConverter Converter(0, HTML, getCommandTraits(CXC)); 848 Converter.visit(HTC); 849 return cxstring::createDup(HTML.str()); 850} 851 852CXString clang_FullComment_getAsHTML(CXComment CXC) { 853 const FullComment *FC = getASTNodeAs<FullComment>(CXC); 854 if (!FC) 855 return cxstring::createNull(); 856 857 SmallString<1024> HTML; 858 CommentASTToHTMLConverter Converter(FC, HTML, getCommandTraits(CXC)); 859 Converter.visit(FC); 860 return cxstring::createDup(HTML.str()); 861} 862 863} // end extern "C" 864 865namespace { 866class CommentASTToXMLConverter : 867 public ConstCommentVisitor<CommentASTToXMLConverter> { 868public: 869 /// \param Str accumulator for XML. 870 CommentASTToXMLConverter(const FullComment *FC, 871 SmallVectorImpl<char> &Str, 872 const CommandTraits &Traits, 873 const SourceManager &SM, 874 SimpleFormatContext &SFC, 875 unsigned FUID) : 876 FC(FC), Result(Str), Traits(Traits), SM(SM), 877 FormatRewriterContext(SFC), 878 FormatInMemoryUniqueId(FUID) { } 879 880 // Inline content. 881 void visitTextComment(const TextComment *C); 882 void visitInlineCommandComment(const InlineCommandComment *C); 883 void visitHTMLStartTagComment(const HTMLStartTagComment *C); 884 void visitHTMLEndTagComment(const HTMLEndTagComment *C); 885 886 // Block content. 887 void visitParagraphComment(const ParagraphComment *C); 888 889 void appendParagraphCommentWithKind(const ParagraphComment *C, 890 StringRef Kind); 891 892 void visitBlockCommandComment(const BlockCommandComment *C); 893 void visitParamCommandComment(const ParamCommandComment *C); 894 void visitTParamCommandComment(const TParamCommandComment *C); 895 void visitVerbatimBlockComment(const VerbatimBlockComment *C); 896 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); 897 void visitVerbatimLineComment(const VerbatimLineComment *C); 898 899 void visitFullComment(const FullComment *C); 900 901 // Helpers. 902 void appendToResultWithXMLEscaping(StringRef S); 903 904 void formatTextOfDeclaration(const DeclInfo *DI, 905 SmallString<128> &Declaration); 906 907private: 908 const FullComment *FC; 909 910 /// Output stream for XML. 911 llvm::raw_svector_ostream Result; 912 913 const CommandTraits &Traits; 914 const SourceManager &SM; 915 SimpleFormatContext &FormatRewriterContext; 916 unsigned FormatInMemoryUniqueId; 917}; 918 919void getSourceTextOfDeclaration(const DeclInfo *ThisDecl, 920 SmallVectorImpl<char> &Str) { 921 ASTContext &Context = ThisDecl->CurrentDecl->getASTContext(); 922 const LangOptions &LangOpts = Context.getLangOpts(); 923 llvm::raw_svector_ostream OS(Str); 924 PrintingPolicy PPolicy(LangOpts); 925 PPolicy.PolishForDeclaration = true; 926 PPolicy.TerseOutput = true; 927 ThisDecl->CurrentDecl->print(OS, PPolicy, 928 /*Indentation*/0, /*PrintInstantiation*/false); 929} 930 931void CommentASTToXMLConverter::formatTextOfDeclaration( 932 const DeclInfo *DI, 933 SmallString<128> &Declaration) { 934 // FIXME. formatting API expects null terminated input string. 935 // There might be more efficient way of doing this. 936 std::string StringDecl = Declaration.str(); 937 938 // Formatter specific code. 939 // Form a unique in memory buffer name. 940 SmallString<128> filename; 941 filename += "xmldecl"; 942 filename += llvm::utostr(FormatInMemoryUniqueId); 943 filename += ".xd"; 944 FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl); 945 SourceLocation Start = 946 FormatRewriterContext.Sources.getLocForStartOfFile(ID).getLocWithOffset(0); 947 unsigned Length = Declaration.size(); 948 949 std::vector<CharSourceRange> 950 Ranges(1, CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length))); 951 ASTContext &Context = DI->CurrentDecl->getASTContext(); 952 const LangOptions &LangOpts = Context.getLangOpts(); 953 Lexer Lex(ID, FormatRewriterContext.Sources.getBuffer(ID), 954 FormatRewriterContext.Sources, LangOpts); 955 tooling::Replacements Replace = 956 reformat(format::getLLVMStyle(), Lex, FormatRewriterContext.Sources, Ranges); 957 applyAllReplacements(Replace, FormatRewriterContext.Rewrite); 958 Declaration = FormatRewriterContext.getRewrittenText(ID); 959} 960 961} // end unnamed namespace 962 963void CommentASTToXMLConverter::visitTextComment(const TextComment *C) { 964 appendToResultWithXMLEscaping(C->getText()); 965} 966 967void CommentASTToXMLConverter::visitInlineCommandComment(const InlineCommandComment *C) { 968 // Nothing to render if no arguments supplied. 969 if (C->getNumArgs() == 0) 970 return; 971 972 // Nothing to render if argument is empty. 973 StringRef Arg0 = C->getArgText(0); 974 if (Arg0.empty()) 975 return; 976 977 switch (C->getRenderKind()) { 978 case InlineCommandComment::RenderNormal: 979 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) { 980 appendToResultWithXMLEscaping(C->getArgText(i)); 981 Result << " "; 982 } 983 return; 984 case InlineCommandComment::RenderBold: 985 assert(C->getNumArgs() == 1); 986 Result << "<bold>"; 987 appendToResultWithXMLEscaping(Arg0); 988 Result << "</bold>"; 989 return; 990 case InlineCommandComment::RenderMonospaced: 991 assert(C->getNumArgs() == 1); 992 Result << "<monospaced>"; 993 appendToResultWithXMLEscaping(Arg0); 994 Result << "</monospaced>"; 995 return; 996 case InlineCommandComment::RenderEmphasized: 997 assert(C->getNumArgs() == 1); 998 Result << "<emphasized>"; 999 appendToResultWithXMLEscaping(Arg0); 1000 Result << "</emphasized>"; 1001 return; 1002 } 1003} 1004 1005void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) { 1006 Result << "<rawHTML><![CDATA["; 1007 PrintHTMLStartTagComment(C, Result); 1008 Result << "]]></rawHTML>"; 1009} 1010 1011void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) { 1012 Result << "<rawHTML></" << C->getTagName() << "></rawHTML>"; 1013} 1014 1015void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) { 1016 appendParagraphCommentWithKind(C, StringRef()); 1017} 1018 1019void CommentASTToXMLConverter::appendParagraphCommentWithKind( 1020 const ParagraphComment *C, 1021 StringRef ParagraphKind) { 1022 if (C->isWhitespace()) 1023 return; 1024 1025 if (ParagraphKind.empty()) 1026 Result << "<Para>"; 1027 else 1028 Result << "<Para kind=\"" << ParagraphKind << "\">"; 1029 1030 for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); 1031 I != E; ++I) { 1032 visit(*I); 1033 } 1034 Result << "</Para>"; 1035} 1036 1037void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) { 1038 StringRef ParagraphKind; 1039 1040 switch (C->getCommandID()) { 1041 case CommandTraits::KCI_attention: 1042 case CommandTraits::KCI_author: 1043 case CommandTraits::KCI_authors: 1044 case CommandTraits::KCI_bug: 1045 case CommandTraits::KCI_copyright: 1046 case CommandTraits::KCI_date: 1047 case CommandTraits::KCI_invariant: 1048 case CommandTraits::KCI_note: 1049 case CommandTraits::KCI_post: 1050 case CommandTraits::KCI_pre: 1051 case CommandTraits::KCI_remark: 1052 case CommandTraits::KCI_remarks: 1053 case CommandTraits::KCI_sa: 1054 case CommandTraits::KCI_see: 1055 case CommandTraits::KCI_since: 1056 case CommandTraits::KCI_todo: 1057 case CommandTraits::KCI_version: 1058 case CommandTraits::KCI_warning: 1059 ParagraphKind = C->getCommandName(Traits); 1060 default: 1061 break; 1062 } 1063 1064 appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind); 1065} 1066 1067void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) { 1068 Result << "<Parameter><Name>"; 1069 appendToResultWithXMLEscaping(C->isParamIndexValid() ? C->getParamName(FC) 1070 : C->getParamNameAsWritten()); 1071 Result << "</Name>"; 1072 1073 if (C->isParamIndexValid()) 1074 Result << "<Index>" << C->getParamIndex() << "</Index>"; 1075 1076 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">"; 1077 switch (C->getDirection()) { 1078 case ParamCommandComment::In: 1079 Result << "in"; 1080 break; 1081 case ParamCommandComment::Out: 1082 Result << "out"; 1083 break; 1084 case ParamCommandComment::InOut: 1085 Result << "in,out"; 1086 break; 1087 } 1088 Result << "</Direction><Discussion>"; 1089 visit(C->getParagraph()); 1090 Result << "</Discussion></Parameter>"; 1091} 1092 1093void CommentASTToXMLConverter::visitTParamCommandComment( 1094 const TParamCommandComment *C) { 1095 Result << "<Parameter><Name>"; 1096 appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC) 1097 : C->getParamNameAsWritten()); 1098 Result << "</Name>"; 1099 1100 if (C->isPositionValid() && C->getDepth() == 1) { 1101 Result << "<Index>" << C->getIndex(0) << "</Index>"; 1102 } 1103 1104 Result << "<Discussion>"; 1105 visit(C->getParagraph()); 1106 Result << "</Discussion></Parameter>"; 1107} 1108 1109void CommentASTToXMLConverter::visitVerbatimBlockComment( 1110 const VerbatimBlockComment *C) { 1111 unsigned NumLines = C->getNumLines(); 1112 if (NumLines == 0) 1113 return; 1114 1115 switch (C->getCommandID()) { 1116 case CommandTraits::KCI_code: 1117 Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">"; 1118 break; 1119 default: 1120 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">"; 1121 break; 1122 } 1123 for (unsigned i = 0; i != NumLines; ++i) { 1124 appendToResultWithXMLEscaping(C->getText(i)); 1125 if (i + 1 != NumLines) 1126 Result << '\n'; 1127 } 1128 Result << "</Verbatim>"; 1129} 1130 1131void CommentASTToXMLConverter::visitVerbatimBlockLineComment( 1132 const VerbatimBlockLineComment *C) { 1133 llvm_unreachable("should not see this AST node"); 1134} 1135 1136void CommentASTToXMLConverter::visitVerbatimLineComment( 1137 const VerbatimLineComment *C) { 1138 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">"; 1139 appendToResultWithXMLEscaping(C->getText()); 1140 Result << "</Verbatim>"; 1141} 1142 1143void CommentASTToXMLConverter::visitFullComment(const FullComment *C) { 1144 FullCommentParts Parts(C, Traits); 1145 1146 const DeclInfo *DI = C->getDeclInfo(); 1147 StringRef RootEndTag; 1148 if (DI) { 1149 switch (DI->getKind()) { 1150 case DeclInfo::OtherKind: 1151 RootEndTag = "</Other>"; 1152 Result << "<Other"; 1153 break; 1154 case DeclInfo::FunctionKind: 1155 RootEndTag = "</Function>"; 1156 Result << "<Function"; 1157 switch (DI->TemplateKind) { 1158 case DeclInfo::NotTemplate: 1159 break; 1160 case DeclInfo::Template: 1161 Result << " templateKind=\"template\""; 1162 break; 1163 case DeclInfo::TemplateSpecialization: 1164 Result << " templateKind=\"specialization\""; 1165 break; 1166 case DeclInfo::TemplatePartialSpecialization: 1167 llvm_unreachable("partial specializations of functions " 1168 "are not allowed in C++"); 1169 } 1170 if (DI->IsInstanceMethod) 1171 Result << " isInstanceMethod=\"1\""; 1172 if (DI->IsClassMethod) 1173 Result << " isClassMethod=\"1\""; 1174 break; 1175 case DeclInfo::ClassKind: 1176 RootEndTag = "</Class>"; 1177 Result << "<Class"; 1178 switch (DI->TemplateKind) { 1179 case DeclInfo::NotTemplate: 1180 break; 1181 case DeclInfo::Template: 1182 Result << " templateKind=\"template\""; 1183 break; 1184 case DeclInfo::TemplateSpecialization: 1185 Result << " templateKind=\"specialization\""; 1186 break; 1187 case DeclInfo::TemplatePartialSpecialization: 1188 Result << " templateKind=\"partialSpecialization\""; 1189 break; 1190 } 1191 break; 1192 case DeclInfo::VariableKind: 1193 RootEndTag = "</Variable>"; 1194 Result << "<Variable"; 1195 break; 1196 case DeclInfo::NamespaceKind: 1197 RootEndTag = "</Namespace>"; 1198 Result << "<Namespace"; 1199 break; 1200 case DeclInfo::TypedefKind: 1201 RootEndTag = "</Typedef>"; 1202 Result << "<Typedef"; 1203 break; 1204 case DeclInfo::EnumKind: 1205 RootEndTag = "</Enum>"; 1206 Result << "<Enum"; 1207 break; 1208 } 1209 1210 { 1211 // Print line and column number. 1212 SourceLocation Loc = DI->CurrentDecl->getLocation(); 1213 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); 1214 FileID FID = LocInfo.first; 1215 unsigned FileOffset = LocInfo.second; 1216 1217 if (!FID.isInvalid()) { 1218 if (const FileEntry *FE = SM.getFileEntryForID(FID)) { 1219 Result << " file=\""; 1220 appendToResultWithXMLEscaping(FE->getName()); 1221 Result << "\""; 1222 } 1223 Result << " line=\"" << SM.getLineNumber(FID, FileOffset) 1224 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset) 1225 << "\""; 1226 } 1227 } 1228 1229 // Finish the root tag. 1230 Result << ">"; 1231 1232 bool FoundName = false; 1233 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) { 1234 if (DeclarationName DeclName = ND->getDeclName()) { 1235 Result << "<Name>"; 1236 std::string Name = DeclName.getAsString(); 1237 appendToResultWithXMLEscaping(Name); 1238 FoundName = true; 1239 Result << "</Name>"; 1240 } 1241 } 1242 if (!FoundName) 1243 Result << "<Name><anonymous></Name>"; 1244 1245 { 1246 // Print USR. 1247 SmallString<128> USR; 1248 cxcursor::getDeclCursorUSR(DI->CommentDecl, USR); 1249 if (!USR.empty()) { 1250 Result << "<USR>"; 1251 appendToResultWithXMLEscaping(USR); 1252 Result << "</USR>"; 1253 } 1254 } 1255 } else { 1256 // No DeclInfo -- just emit some root tag and name tag. 1257 RootEndTag = "</Other>"; 1258 Result << "<Other><Name>unknown</Name>"; 1259 } 1260 1261 if (Parts.Headerfile) { 1262 Result << "<Headerfile>"; 1263 visit(Parts.Headerfile); 1264 Result << "</Headerfile>"; 1265 } 1266 1267 { 1268 // Pretty-print the declaration. 1269 Result << "<Declaration>"; 1270 SmallString<128> Declaration; 1271 getSourceTextOfDeclaration(DI, Declaration); 1272 formatTextOfDeclaration(DI, Declaration); 1273 appendToResultWithXMLEscaping(Declaration); 1274 1275 Result << "</Declaration>"; 1276 } 1277 1278 bool FirstParagraphIsBrief = false; 1279 if (Parts.Brief) { 1280 Result << "<Abstract>"; 1281 visit(Parts.Brief); 1282 Result << "</Abstract>"; 1283 } else if (Parts.FirstParagraph) { 1284 Result << "<Abstract>"; 1285 visit(Parts.FirstParagraph); 1286 Result << "</Abstract>"; 1287 FirstParagraphIsBrief = true; 1288 } 1289 1290 if (Parts.TParams.size() != 0) { 1291 Result << "<TemplateParameters>"; 1292 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i) 1293 visit(Parts.TParams[i]); 1294 Result << "</TemplateParameters>"; 1295 } 1296 1297 if (Parts.Params.size() != 0) { 1298 Result << "<Parameters>"; 1299 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i) 1300 visit(Parts.Params[i]); 1301 Result << "</Parameters>"; 1302 } 1303 1304 if (Parts.Returns.size() != 0) { 1305 Result << "<ResultDiscussion>"; 1306 for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i) 1307 visit(Parts.Returns[i]); 1308 Result << "</ResultDiscussion>"; 1309 } 1310 1311 if (DI->CommentDecl->hasAttrs()) { 1312 const AttrVec &Attrs = DI->CommentDecl->getAttrs(); 1313 for (unsigned i = 0, e = Attrs.size(); i != e; i++) { 1314 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]); 1315 if (!AA) { 1316 if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) { 1317 if (DA->getMessage().empty()) 1318 Result << "<Deprecated/>"; 1319 else { 1320 Result << "<Deprecated>"; 1321 appendToResultWithXMLEscaping(DA->getMessage()); 1322 Result << "</Deprecated>"; 1323 } 1324 } 1325 else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) { 1326 if (UA->getMessage().empty()) 1327 Result << "<Unavailable/>"; 1328 else { 1329 Result << "<Unavailable>"; 1330 appendToResultWithXMLEscaping(UA->getMessage()); 1331 Result << "</Unavailable>"; 1332 } 1333 } 1334 continue; 1335 } 1336 1337 // 'availability' attribute. 1338 Result << "<Availability"; 1339 StringRef Distribution; 1340 if (AA->getPlatform()) { 1341 Distribution = AvailabilityAttr::getPrettyPlatformName( 1342 AA->getPlatform()->getName()); 1343 if (Distribution.empty()) 1344 Distribution = AA->getPlatform()->getName(); 1345 } 1346 Result << " distribution=\"" << Distribution << "\">"; 1347 VersionTuple IntroducedInVersion = AA->getIntroduced(); 1348 if (!IntroducedInVersion.empty()) { 1349 Result << "<IntroducedInVersion>" 1350 << IntroducedInVersion.getAsString() 1351 << "</IntroducedInVersion>"; 1352 } 1353 VersionTuple DeprecatedInVersion = AA->getDeprecated(); 1354 if (!DeprecatedInVersion.empty()) { 1355 Result << "<DeprecatedInVersion>" 1356 << DeprecatedInVersion.getAsString() 1357 << "</DeprecatedInVersion>"; 1358 } 1359 VersionTuple RemovedAfterVersion = AA->getObsoleted(); 1360 if (!RemovedAfterVersion.empty()) { 1361 Result << "<RemovedAfterVersion>" 1362 << RemovedAfterVersion.getAsString() 1363 << "</RemovedAfterVersion>"; 1364 } 1365 StringRef DeprecationSummary = AA->getMessage(); 1366 if (!DeprecationSummary.empty()) { 1367 Result << "<DeprecationSummary>"; 1368 appendToResultWithXMLEscaping(DeprecationSummary); 1369 Result << "</DeprecationSummary>"; 1370 } 1371 if (AA->getUnavailable()) 1372 Result << "<Unavailable/>"; 1373 Result << "</Availability>"; 1374 } 1375 } 1376 1377 { 1378 bool StartTagEmitted = false; 1379 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) { 1380 const Comment *C = Parts.MiscBlocks[i]; 1381 if (FirstParagraphIsBrief && C == Parts.FirstParagraph) 1382 continue; 1383 if (!StartTagEmitted) { 1384 Result << "<Discussion>"; 1385 StartTagEmitted = true; 1386 } 1387 visit(C); 1388 } 1389 if (StartTagEmitted) 1390 Result << "</Discussion>"; 1391 } 1392 1393 Result << RootEndTag; 1394 1395 Result.flush(); 1396} 1397 1398void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) { 1399 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) { 1400 const char C = *I; 1401 switch (C) { 1402 case '&': 1403 Result << "&"; 1404 break; 1405 case '<': 1406 Result << "<"; 1407 break; 1408 case '>': 1409 Result << ">"; 1410 break; 1411 case '"': 1412 Result << """; 1413 break; 1414 case '\'': 1415 Result << "'"; 1416 break; 1417 default: 1418 Result << C; 1419 break; 1420 } 1421 } 1422} 1423 1424extern "C" { 1425 1426CXString clang_FullComment_getAsXML(CXComment CXC) { 1427 const FullComment *FC = getASTNodeAs<FullComment>(CXC); 1428 if (!FC) 1429 return cxstring::createNull(); 1430 ASTContext &Context = FC->getDeclInfo()->CurrentDecl->getASTContext(); 1431 CXTranslationUnit TU = CXC.TranslationUnit; 1432 SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager(); 1433 1434 if (!TU->FormatContext) { 1435 TU->FormatContext = new SimpleFormatContext(Context.getLangOpts()); 1436 } else if ((TU->FormatInMemoryUniqueId % 1000) == 0) { 1437 // Delete after some number of iterators, so the buffers don't grow 1438 // too large. 1439 delete TU->FormatContext; 1440 TU->FormatContext = new SimpleFormatContext(Context.getLangOpts()); 1441 } 1442 1443 SmallString<1024> XML; 1444 CommentASTToXMLConverter Converter(FC, XML, getCommandTraits(CXC), SM, 1445 *TU->FormatContext, 1446 TU->FormatInMemoryUniqueId++); 1447 Converter.visit(FC); 1448 return cxstring::createDup(XML.str()); 1449} 1450 1451} // end extern "C" 1452 1453