1259701Sdim//===--- CommentToXML.cpp - Convert comments to XML representation --------===// 2259701Sdim// 3259701Sdim// The LLVM Compiler Infrastructure 4259701Sdim// 5259701Sdim// This file is distributed under the University of Illinois Open Source 6259701Sdim// License. See LICENSE.TXT for details. 7259701Sdim// 8259701Sdim//===----------------------------------------------------------------------===// 9259701Sdim 10259701Sdim#include "clang/Index/CommentToXML.h" 11259701Sdim#include "SimpleFormatContext.h" 12276479Sdim#include "clang/AST/ASTContext.h" 13259701Sdim#include "clang/AST/Attr.h" 14259701Sdim#include "clang/AST/Comment.h" 15259701Sdim#include "clang/AST/CommentVisitor.h" 16259701Sdim#include "clang/Format/Format.h" 17259701Sdim#include "clang/Index/USRGeneration.h" 18259701Sdim#include "llvm/ADT/StringExtras.h" 19259701Sdim#include "llvm/ADT/TinyPtrVector.h" 20259701Sdim#include "llvm/Support/raw_ostream.h" 21259701Sdim 22259701Sdimusing namespace clang; 23259701Sdimusing namespace clang::comments; 24259701Sdimusing namespace clang::index; 25259701Sdim 26259701Sdimnamespace { 27259701Sdim 28259701Sdim/// This comparison will sort parameters with valid index by index, then vararg 29259701Sdim/// parameters, and invalid (unresolved) parameters last. 30259701Sdimclass ParamCommandCommentCompareIndex { 31259701Sdimpublic: 32259701Sdim bool operator()(const ParamCommandComment *LHS, 33259701Sdim const ParamCommandComment *RHS) const { 34259701Sdim unsigned LHSIndex = UINT_MAX; 35259701Sdim unsigned RHSIndex = UINT_MAX; 36259701Sdim 37259701Sdim if (LHS->isParamIndexValid()) { 38259701Sdim if (LHS->isVarArgParam()) 39259701Sdim LHSIndex = UINT_MAX - 1; 40259701Sdim else 41259701Sdim LHSIndex = LHS->getParamIndex(); 42259701Sdim } 43259701Sdim if (RHS->isParamIndexValid()) { 44259701Sdim if (RHS->isVarArgParam()) 45259701Sdim RHSIndex = UINT_MAX - 1; 46259701Sdim else 47259701Sdim RHSIndex = RHS->getParamIndex(); 48259701Sdim } 49259701Sdim return LHSIndex < RHSIndex; 50259701Sdim } 51259701Sdim}; 52259701Sdim 53259701Sdim/// This comparison will sort template parameters in the following order: 54259701Sdim/// \li real template parameters (depth = 1) in index order; 55259701Sdim/// \li all other names (depth > 1); 56259701Sdim/// \li unresolved names. 57259701Sdimclass TParamCommandCommentComparePosition { 58259701Sdimpublic: 59259701Sdim bool operator()(const TParamCommandComment *LHS, 60259701Sdim const TParamCommandComment *RHS) const { 61259701Sdim // Sort unresolved names last. 62259701Sdim if (!LHS->isPositionValid()) 63259701Sdim return false; 64259701Sdim if (!RHS->isPositionValid()) 65259701Sdim return true; 66259701Sdim 67259701Sdim if (LHS->getDepth() > 1) 68259701Sdim return false; 69259701Sdim if (RHS->getDepth() > 1) 70259701Sdim return true; 71259701Sdim 72259701Sdim // Sort template parameters in index order. 73259701Sdim if (LHS->getDepth() == 1 && RHS->getDepth() == 1) 74259701Sdim return LHS->getIndex(0) < RHS->getIndex(0); 75259701Sdim 76259701Sdim // Leave all other names in source order. 77259701Sdim return true; 78259701Sdim } 79259701Sdim}; 80259701Sdim 81259701Sdim/// Separate parts of a FullComment. 82259701Sdimstruct FullCommentParts { 83259701Sdim /// Take a full comment apart and initialize members accordingly. 84259701Sdim FullCommentParts(const FullComment *C, 85259701Sdim const CommandTraits &Traits); 86259701Sdim 87259701Sdim const BlockContentComment *Brief; 88259701Sdim const BlockContentComment *Headerfile; 89259701Sdim const ParagraphComment *FirstParagraph; 90259701Sdim SmallVector<const BlockCommandComment *, 4> Returns; 91259701Sdim SmallVector<const ParamCommandComment *, 8> Params; 92259701Sdim SmallVector<const TParamCommandComment *, 4> TParams; 93259701Sdim llvm::TinyPtrVector<const BlockCommandComment *> Exceptions; 94259701Sdim SmallVector<const BlockContentComment *, 8> MiscBlocks; 95259701Sdim}; 96259701Sdim 97259701SdimFullCommentParts::FullCommentParts(const FullComment *C, 98259701Sdim const CommandTraits &Traits) : 99276479Sdim Brief(nullptr), Headerfile(nullptr), FirstParagraph(nullptr) { 100259701Sdim for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); 101259701Sdim I != E; ++I) { 102259701Sdim const Comment *Child = *I; 103259701Sdim if (!Child) 104259701Sdim continue; 105259701Sdim switch (Child->getCommentKind()) { 106259701Sdim case Comment::NoCommentKind: 107259701Sdim continue; 108259701Sdim 109259701Sdim case Comment::ParagraphCommentKind: { 110259701Sdim const ParagraphComment *PC = cast<ParagraphComment>(Child); 111259701Sdim if (PC->isWhitespace()) 112259701Sdim break; 113259701Sdim if (!FirstParagraph) 114259701Sdim FirstParagraph = PC; 115259701Sdim 116259701Sdim MiscBlocks.push_back(PC); 117259701Sdim break; 118259701Sdim } 119259701Sdim 120259701Sdim case Comment::BlockCommandCommentKind: { 121259701Sdim const BlockCommandComment *BCC = cast<BlockCommandComment>(Child); 122259701Sdim const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID()); 123259701Sdim if (!Brief && Info->IsBriefCommand) { 124259701Sdim Brief = BCC; 125259701Sdim break; 126259701Sdim } 127259701Sdim if (!Headerfile && Info->IsHeaderfileCommand) { 128259701Sdim Headerfile = BCC; 129259701Sdim break; 130259701Sdim } 131259701Sdim if (Info->IsReturnsCommand) { 132259701Sdim Returns.push_back(BCC); 133259701Sdim break; 134259701Sdim } 135259701Sdim if (Info->IsThrowsCommand) { 136259701Sdim Exceptions.push_back(BCC); 137259701Sdim break; 138259701Sdim } 139259701Sdim MiscBlocks.push_back(BCC); 140259701Sdim break; 141259701Sdim } 142259701Sdim 143259701Sdim case Comment::ParamCommandCommentKind: { 144259701Sdim const ParamCommandComment *PCC = cast<ParamCommandComment>(Child); 145259701Sdim if (!PCC->hasParamName()) 146259701Sdim break; 147259701Sdim 148259701Sdim if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph()) 149259701Sdim break; 150259701Sdim 151259701Sdim Params.push_back(PCC); 152259701Sdim break; 153259701Sdim } 154259701Sdim 155259701Sdim case Comment::TParamCommandCommentKind: { 156259701Sdim const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child); 157259701Sdim if (!TPCC->hasParamName()) 158259701Sdim break; 159259701Sdim 160259701Sdim if (!TPCC->hasNonWhitespaceParagraph()) 161259701Sdim break; 162259701Sdim 163259701Sdim TParams.push_back(TPCC); 164259701Sdim break; 165259701Sdim } 166259701Sdim 167259701Sdim case Comment::VerbatimBlockCommentKind: 168259701Sdim MiscBlocks.push_back(cast<BlockCommandComment>(Child)); 169259701Sdim break; 170259701Sdim 171259701Sdim case Comment::VerbatimLineCommentKind: { 172259701Sdim const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child); 173259701Sdim const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID()); 174259701Sdim if (!Info->IsDeclarationCommand) 175259701Sdim MiscBlocks.push_back(VLC); 176259701Sdim break; 177259701Sdim } 178259701Sdim 179259701Sdim case Comment::TextCommentKind: 180259701Sdim case Comment::InlineCommandCommentKind: 181259701Sdim case Comment::HTMLStartTagCommentKind: 182259701Sdim case Comment::HTMLEndTagCommentKind: 183259701Sdim case Comment::VerbatimBlockLineCommentKind: 184259701Sdim case Comment::FullCommentKind: 185259701Sdim llvm_unreachable("AST node of this kind can't be a child of " 186259701Sdim "a FullComment"); 187259701Sdim } 188259701Sdim } 189259701Sdim 190259701Sdim // Sort params in order they are declared in the function prototype. 191259701Sdim // Unresolved parameters are put at the end of the list in the same order 192259701Sdim // they were seen in the comment. 193259701Sdim std::stable_sort(Params.begin(), Params.end(), 194259701Sdim ParamCommandCommentCompareIndex()); 195259701Sdim 196259701Sdim std::stable_sort(TParams.begin(), TParams.end(), 197259701Sdim TParamCommandCommentComparePosition()); 198259701Sdim} 199259701Sdim 200259701Sdimvoid printHTMLStartTagComment(const HTMLStartTagComment *C, 201259701Sdim llvm::raw_svector_ostream &Result) { 202259701Sdim Result << "<" << C->getTagName(); 203259701Sdim 204259701Sdim if (C->getNumAttrs() != 0) { 205259701Sdim for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) { 206259701Sdim Result << " "; 207259701Sdim const HTMLStartTagComment::Attribute &Attr = C->getAttr(i); 208259701Sdim Result << Attr.Name; 209259701Sdim if (!Attr.Value.empty()) 210259701Sdim Result << "=\"" << Attr.Value << "\""; 211259701Sdim } 212259701Sdim } 213259701Sdim 214259701Sdim if (!C->isSelfClosing()) 215259701Sdim Result << ">"; 216259701Sdim else 217259701Sdim Result << "/>"; 218259701Sdim} 219259701Sdim 220259701Sdimclass CommentASTToHTMLConverter : 221259701Sdim public ConstCommentVisitor<CommentASTToHTMLConverter> { 222259701Sdimpublic: 223259701Sdim /// \param Str accumulator for HTML. 224259701Sdim CommentASTToHTMLConverter(const FullComment *FC, 225259701Sdim SmallVectorImpl<char> &Str, 226259701Sdim const CommandTraits &Traits) : 227259701Sdim FC(FC), Result(Str), Traits(Traits) 228259701Sdim { } 229259701Sdim 230259701Sdim // Inline content. 231259701Sdim void visitTextComment(const TextComment *C); 232259701Sdim void visitInlineCommandComment(const InlineCommandComment *C); 233259701Sdim void visitHTMLStartTagComment(const HTMLStartTagComment *C); 234259701Sdim void visitHTMLEndTagComment(const HTMLEndTagComment *C); 235259701Sdim 236259701Sdim // Block content. 237259701Sdim void visitParagraphComment(const ParagraphComment *C); 238259701Sdim void visitBlockCommandComment(const BlockCommandComment *C); 239259701Sdim void visitParamCommandComment(const ParamCommandComment *C); 240259701Sdim void visitTParamCommandComment(const TParamCommandComment *C); 241259701Sdim void visitVerbatimBlockComment(const VerbatimBlockComment *C); 242259701Sdim void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); 243259701Sdim void visitVerbatimLineComment(const VerbatimLineComment *C); 244259701Sdim 245259701Sdim void visitFullComment(const FullComment *C); 246259701Sdim 247259701Sdim // Helpers. 248259701Sdim 249259701Sdim /// Convert a paragraph that is not a block by itself (an argument to some 250259701Sdim /// command). 251259701Sdim void visitNonStandaloneParagraphComment(const ParagraphComment *C); 252259701Sdim 253259701Sdim void appendToResultWithHTMLEscaping(StringRef S); 254259701Sdim 255259701Sdimprivate: 256259701Sdim const FullComment *FC; 257259701Sdim /// Output stream for HTML. 258259701Sdim llvm::raw_svector_ostream Result; 259259701Sdim 260259701Sdim const CommandTraits &Traits; 261259701Sdim}; 262259701Sdim} // end unnamed namespace 263259701Sdim 264259701Sdimvoid CommentASTToHTMLConverter::visitTextComment(const TextComment *C) { 265259701Sdim appendToResultWithHTMLEscaping(C->getText()); 266259701Sdim} 267259701Sdim 268259701Sdimvoid CommentASTToHTMLConverter::visitInlineCommandComment( 269259701Sdim const InlineCommandComment *C) { 270259701Sdim // Nothing to render if no arguments supplied. 271259701Sdim if (C->getNumArgs() == 0) 272259701Sdim return; 273259701Sdim 274259701Sdim // Nothing to render if argument is empty. 275259701Sdim StringRef Arg0 = C->getArgText(0); 276259701Sdim if (Arg0.empty()) 277259701Sdim return; 278259701Sdim 279259701Sdim switch (C->getRenderKind()) { 280259701Sdim case InlineCommandComment::RenderNormal: 281259701Sdim for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) { 282259701Sdim appendToResultWithHTMLEscaping(C->getArgText(i)); 283259701Sdim Result << " "; 284259701Sdim } 285259701Sdim return; 286259701Sdim 287259701Sdim case InlineCommandComment::RenderBold: 288259701Sdim assert(C->getNumArgs() == 1); 289259701Sdim Result << "<b>"; 290259701Sdim appendToResultWithHTMLEscaping(Arg0); 291259701Sdim Result << "</b>"; 292259701Sdim return; 293259701Sdim case InlineCommandComment::RenderMonospaced: 294259701Sdim assert(C->getNumArgs() == 1); 295259701Sdim Result << "<tt>"; 296259701Sdim appendToResultWithHTMLEscaping(Arg0); 297259701Sdim Result<< "</tt>"; 298259701Sdim return; 299259701Sdim case InlineCommandComment::RenderEmphasized: 300259701Sdim assert(C->getNumArgs() == 1); 301259701Sdim Result << "<em>"; 302259701Sdim appendToResultWithHTMLEscaping(Arg0); 303259701Sdim Result << "</em>"; 304259701Sdim return; 305259701Sdim } 306259701Sdim} 307259701Sdim 308259701Sdimvoid CommentASTToHTMLConverter::visitHTMLStartTagComment( 309259701Sdim const HTMLStartTagComment *C) { 310259701Sdim printHTMLStartTagComment(C, Result); 311259701Sdim} 312259701Sdim 313259701Sdimvoid CommentASTToHTMLConverter::visitHTMLEndTagComment( 314259701Sdim const HTMLEndTagComment *C) { 315259701Sdim Result << "</" << C->getTagName() << ">"; 316259701Sdim} 317259701Sdim 318259701Sdimvoid CommentASTToHTMLConverter::visitParagraphComment( 319259701Sdim const ParagraphComment *C) { 320259701Sdim if (C->isWhitespace()) 321259701Sdim return; 322259701Sdim 323259701Sdim Result << "<p>"; 324259701Sdim for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); 325259701Sdim I != E; ++I) { 326259701Sdim visit(*I); 327259701Sdim } 328259701Sdim Result << "</p>"; 329259701Sdim} 330259701Sdim 331259701Sdimvoid CommentASTToHTMLConverter::visitBlockCommandComment( 332259701Sdim const BlockCommandComment *C) { 333259701Sdim const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID()); 334259701Sdim if (Info->IsBriefCommand) { 335259701Sdim Result << "<p class=\"para-brief\">"; 336259701Sdim visitNonStandaloneParagraphComment(C->getParagraph()); 337259701Sdim Result << "</p>"; 338259701Sdim return; 339259701Sdim } 340259701Sdim if (Info->IsReturnsCommand) { 341259701Sdim Result << "<p class=\"para-returns\">" 342259701Sdim "<span class=\"word-returns\">Returns</span> "; 343259701Sdim visitNonStandaloneParagraphComment(C->getParagraph()); 344259701Sdim Result << "</p>"; 345259701Sdim return; 346259701Sdim } 347259701Sdim // We don't know anything about this command. Just render the paragraph. 348259701Sdim visit(C->getParagraph()); 349259701Sdim} 350259701Sdim 351259701Sdimvoid CommentASTToHTMLConverter::visitParamCommandComment( 352259701Sdim const ParamCommandComment *C) { 353259701Sdim if (C->isParamIndexValid()) { 354259701Sdim if (C->isVarArgParam()) { 355259701Sdim Result << "<dt class=\"param-name-index-vararg\">"; 356259701Sdim appendToResultWithHTMLEscaping(C->getParamNameAsWritten()); 357259701Sdim } else { 358259701Sdim Result << "<dt class=\"param-name-index-" 359259701Sdim << C->getParamIndex() 360259701Sdim << "\">"; 361259701Sdim appendToResultWithHTMLEscaping(C->getParamName(FC)); 362259701Sdim } 363259701Sdim } else { 364259701Sdim Result << "<dt class=\"param-name-index-invalid\">"; 365259701Sdim appendToResultWithHTMLEscaping(C->getParamNameAsWritten()); 366259701Sdim } 367259701Sdim Result << "</dt>"; 368259701Sdim 369259701Sdim if (C->isParamIndexValid()) { 370259701Sdim if (C->isVarArgParam()) 371259701Sdim Result << "<dd class=\"param-descr-index-vararg\">"; 372259701Sdim else 373259701Sdim Result << "<dd class=\"param-descr-index-" 374259701Sdim << C->getParamIndex() 375259701Sdim << "\">"; 376259701Sdim } else 377259701Sdim Result << "<dd class=\"param-descr-index-invalid\">"; 378259701Sdim 379259701Sdim visitNonStandaloneParagraphComment(C->getParagraph()); 380259701Sdim Result << "</dd>"; 381259701Sdim} 382259701Sdim 383259701Sdimvoid CommentASTToHTMLConverter::visitTParamCommandComment( 384259701Sdim const TParamCommandComment *C) { 385259701Sdim if (C->isPositionValid()) { 386259701Sdim if (C->getDepth() == 1) 387259701Sdim Result << "<dt class=\"tparam-name-index-" 388259701Sdim << C->getIndex(0) 389259701Sdim << "\">"; 390259701Sdim else 391259701Sdim Result << "<dt class=\"tparam-name-index-other\">"; 392259701Sdim appendToResultWithHTMLEscaping(C->getParamName(FC)); 393259701Sdim } else { 394259701Sdim Result << "<dt class=\"tparam-name-index-invalid\">"; 395259701Sdim appendToResultWithHTMLEscaping(C->getParamNameAsWritten()); 396259701Sdim } 397259701Sdim 398259701Sdim Result << "</dt>"; 399259701Sdim 400259701Sdim if (C->isPositionValid()) { 401259701Sdim if (C->getDepth() == 1) 402259701Sdim Result << "<dd class=\"tparam-descr-index-" 403259701Sdim << C->getIndex(0) 404259701Sdim << "\">"; 405259701Sdim else 406259701Sdim Result << "<dd class=\"tparam-descr-index-other\">"; 407259701Sdim } else 408259701Sdim Result << "<dd class=\"tparam-descr-index-invalid\">"; 409259701Sdim 410259701Sdim visitNonStandaloneParagraphComment(C->getParagraph()); 411259701Sdim Result << "</dd>"; 412259701Sdim} 413259701Sdim 414259701Sdimvoid CommentASTToHTMLConverter::visitVerbatimBlockComment( 415259701Sdim const VerbatimBlockComment *C) { 416259701Sdim unsigned NumLines = C->getNumLines(); 417259701Sdim if (NumLines == 0) 418259701Sdim return; 419259701Sdim 420259701Sdim Result << "<pre>"; 421259701Sdim for (unsigned i = 0; i != NumLines; ++i) { 422259701Sdim appendToResultWithHTMLEscaping(C->getText(i)); 423259701Sdim if (i + 1 != NumLines) 424259701Sdim Result << '\n'; 425259701Sdim } 426259701Sdim Result << "</pre>"; 427259701Sdim} 428259701Sdim 429259701Sdimvoid CommentASTToHTMLConverter::visitVerbatimBlockLineComment( 430259701Sdim const VerbatimBlockLineComment *C) { 431259701Sdim llvm_unreachable("should not see this AST node"); 432259701Sdim} 433259701Sdim 434259701Sdimvoid CommentASTToHTMLConverter::visitVerbatimLineComment( 435259701Sdim const VerbatimLineComment *C) { 436259701Sdim Result << "<pre>"; 437259701Sdim appendToResultWithHTMLEscaping(C->getText()); 438259701Sdim Result << "</pre>"; 439259701Sdim} 440259701Sdim 441259701Sdimvoid CommentASTToHTMLConverter::visitFullComment(const FullComment *C) { 442259701Sdim FullCommentParts Parts(C, Traits); 443259701Sdim 444259701Sdim bool FirstParagraphIsBrief = false; 445259701Sdim if (Parts.Headerfile) 446259701Sdim visit(Parts.Headerfile); 447259701Sdim if (Parts.Brief) 448259701Sdim visit(Parts.Brief); 449259701Sdim else if (Parts.FirstParagraph) { 450259701Sdim Result << "<p class=\"para-brief\">"; 451259701Sdim visitNonStandaloneParagraphComment(Parts.FirstParagraph); 452259701Sdim Result << "</p>"; 453259701Sdim FirstParagraphIsBrief = true; 454259701Sdim } 455259701Sdim 456259701Sdim for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) { 457259701Sdim const Comment *C = Parts.MiscBlocks[i]; 458259701Sdim if (FirstParagraphIsBrief && C == Parts.FirstParagraph) 459259701Sdim continue; 460259701Sdim visit(C); 461259701Sdim } 462259701Sdim 463259701Sdim if (Parts.TParams.size() != 0) { 464259701Sdim Result << "<dl>"; 465259701Sdim for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i) 466259701Sdim visit(Parts.TParams[i]); 467259701Sdim Result << "</dl>"; 468259701Sdim } 469259701Sdim 470259701Sdim if (Parts.Params.size() != 0) { 471259701Sdim Result << "<dl>"; 472259701Sdim for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i) 473259701Sdim visit(Parts.Params[i]); 474259701Sdim Result << "</dl>"; 475259701Sdim } 476259701Sdim 477259701Sdim if (Parts.Returns.size() != 0) { 478259701Sdim Result << "<div class=\"result-discussion\">"; 479259701Sdim for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i) 480259701Sdim visit(Parts.Returns[i]); 481259701Sdim Result << "</div>"; 482259701Sdim } 483259701Sdim 484259701Sdim} 485259701Sdim 486259701Sdimvoid CommentASTToHTMLConverter::visitNonStandaloneParagraphComment( 487259701Sdim const ParagraphComment *C) { 488259701Sdim if (!C) 489259701Sdim return; 490259701Sdim 491259701Sdim for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); 492259701Sdim I != E; ++I) { 493259701Sdim visit(*I); 494259701Sdim } 495259701Sdim} 496259701Sdim 497259701Sdimvoid CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) { 498259701Sdim for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) { 499259701Sdim const char C = *I; 500259701Sdim switch (C) { 501259701Sdim case '&': 502259701Sdim Result << "&"; 503259701Sdim break; 504259701Sdim case '<': 505259701Sdim Result << "<"; 506259701Sdim break; 507259701Sdim case '>': 508259701Sdim Result << ">"; 509259701Sdim break; 510259701Sdim case '"': 511259701Sdim Result << """; 512259701Sdim break; 513259701Sdim case '\'': 514259701Sdim Result << "'"; 515259701Sdim break; 516259701Sdim case '/': 517259701Sdim Result << "/"; 518259701Sdim break; 519259701Sdim default: 520259701Sdim Result << C; 521259701Sdim break; 522259701Sdim } 523259701Sdim } 524259701Sdim} 525259701Sdim 526259701Sdimnamespace { 527259701Sdimclass CommentASTToXMLConverter : 528259701Sdim public ConstCommentVisitor<CommentASTToXMLConverter> { 529259701Sdimpublic: 530259701Sdim /// \param Str accumulator for XML. 531259701Sdim CommentASTToXMLConverter(const FullComment *FC, 532259701Sdim SmallVectorImpl<char> &Str, 533259701Sdim const CommandTraits &Traits, 534259701Sdim const SourceManager &SM, 535259701Sdim SimpleFormatContext &SFC, 536259701Sdim unsigned FUID) : 537259701Sdim FC(FC), Result(Str), Traits(Traits), SM(SM), 538259701Sdim FormatRewriterContext(SFC), 539259701Sdim FormatInMemoryUniqueId(FUID) { } 540259701Sdim 541259701Sdim // Inline content. 542259701Sdim void visitTextComment(const TextComment *C); 543259701Sdim void visitInlineCommandComment(const InlineCommandComment *C); 544259701Sdim void visitHTMLStartTagComment(const HTMLStartTagComment *C); 545259701Sdim void visitHTMLEndTagComment(const HTMLEndTagComment *C); 546259701Sdim 547259701Sdim // Block content. 548259701Sdim void visitParagraphComment(const ParagraphComment *C); 549259701Sdim 550259701Sdim void appendParagraphCommentWithKind(const ParagraphComment *C, 551259701Sdim StringRef Kind); 552259701Sdim 553259701Sdim void visitBlockCommandComment(const BlockCommandComment *C); 554259701Sdim void visitParamCommandComment(const ParamCommandComment *C); 555259701Sdim void visitTParamCommandComment(const TParamCommandComment *C); 556259701Sdim void visitVerbatimBlockComment(const VerbatimBlockComment *C); 557259701Sdim void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); 558259701Sdim void visitVerbatimLineComment(const VerbatimLineComment *C); 559259701Sdim 560259701Sdim void visitFullComment(const FullComment *C); 561259701Sdim 562259701Sdim // Helpers. 563259701Sdim void appendToResultWithXMLEscaping(StringRef S); 564276479Sdim void appendToResultWithCDATAEscaping(StringRef S); 565259701Sdim 566259701Sdim void formatTextOfDeclaration(const DeclInfo *DI, 567259701Sdim SmallString<128> &Declaration); 568259701Sdim 569259701Sdimprivate: 570259701Sdim const FullComment *FC; 571259701Sdim 572259701Sdim /// Output stream for XML. 573259701Sdim llvm::raw_svector_ostream Result; 574259701Sdim 575259701Sdim const CommandTraits &Traits; 576259701Sdim const SourceManager &SM; 577259701Sdim SimpleFormatContext &FormatRewriterContext; 578259701Sdim unsigned FormatInMemoryUniqueId; 579259701Sdim}; 580259701Sdim 581259701Sdimvoid getSourceTextOfDeclaration(const DeclInfo *ThisDecl, 582259701Sdim SmallVectorImpl<char> &Str) { 583259701Sdim ASTContext &Context = ThisDecl->CurrentDecl->getASTContext(); 584259701Sdim const LangOptions &LangOpts = Context.getLangOpts(); 585259701Sdim llvm::raw_svector_ostream OS(Str); 586259701Sdim PrintingPolicy PPolicy(LangOpts); 587259701Sdim PPolicy.PolishForDeclaration = true; 588259701Sdim PPolicy.TerseOutput = true; 589259701Sdim ThisDecl->CurrentDecl->print(OS, PPolicy, 590259701Sdim /*Indentation*/0, /*PrintInstantiation*/false); 591259701Sdim} 592259701Sdim 593259701Sdimvoid CommentASTToXMLConverter::formatTextOfDeclaration( 594259701Sdim const DeclInfo *DI, SmallString<128> &Declaration) { 595259701Sdim // FIXME. formatting API expects null terminated input string. 596259701Sdim // There might be more efficient way of doing this. 597259701Sdim std::string StringDecl = Declaration.str(); 598259701Sdim 599259701Sdim // Formatter specific code. 600259701Sdim // Form a unique in memory buffer name. 601259701Sdim SmallString<128> filename; 602259701Sdim filename += "xmldecl"; 603259701Sdim filename += llvm::utostr(FormatInMemoryUniqueId); 604259701Sdim filename += ".xd"; 605259701Sdim FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl); 606259701Sdim SourceLocation Start = FormatRewriterContext.Sources.getLocForStartOfFile(ID) 607259701Sdim .getLocWithOffset(0); 608259701Sdim unsigned Length = Declaration.size(); 609259701Sdim 610259701Sdim tooling::Replacements Replace = reformat( 611280031Sdim format::getLLVMStyle(), FormatRewriterContext.Sources, ID, 612280031Sdim CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length))); 613259701Sdim applyAllReplacements(Replace, FormatRewriterContext.Rewrite); 614259701Sdim Declaration = FormatRewriterContext.getRewrittenText(ID); 615259701Sdim} 616259701Sdim 617259701Sdim} // end unnamed namespace 618259701Sdim 619259701Sdimvoid CommentASTToXMLConverter::visitTextComment(const TextComment *C) { 620259701Sdim appendToResultWithXMLEscaping(C->getText()); 621259701Sdim} 622259701Sdim 623259701Sdimvoid CommentASTToXMLConverter::visitInlineCommandComment( 624259701Sdim const InlineCommandComment *C) { 625259701Sdim // Nothing to render if no arguments supplied. 626259701Sdim if (C->getNumArgs() == 0) 627259701Sdim return; 628259701Sdim 629259701Sdim // Nothing to render if argument is empty. 630259701Sdim StringRef Arg0 = C->getArgText(0); 631259701Sdim if (Arg0.empty()) 632259701Sdim return; 633259701Sdim 634259701Sdim switch (C->getRenderKind()) { 635259701Sdim case InlineCommandComment::RenderNormal: 636259701Sdim for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) { 637259701Sdim appendToResultWithXMLEscaping(C->getArgText(i)); 638259701Sdim Result << " "; 639259701Sdim } 640259701Sdim return; 641259701Sdim case InlineCommandComment::RenderBold: 642259701Sdim assert(C->getNumArgs() == 1); 643259701Sdim Result << "<bold>"; 644259701Sdim appendToResultWithXMLEscaping(Arg0); 645259701Sdim Result << "</bold>"; 646259701Sdim return; 647259701Sdim case InlineCommandComment::RenderMonospaced: 648259701Sdim assert(C->getNumArgs() == 1); 649259701Sdim Result << "<monospaced>"; 650259701Sdim appendToResultWithXMLEscaping(Arg0); 651259701Sdim Result << "</monospaced>"; 652259701Sdim return; 653259701Sdim case InlineCommandComment::RenderEmphasized: 654259701Sdim assert(C->getNumArgs() == 1); 655259701Sdim Result << "<emphasized>"; 656259701Sdim appendToResultWithXMLEscaping(Arg0); 657259701Sdim Result << "</emphasized>"; 658259701Sdim return; 659259701Sdim } 660259701Sdim} 661259701Sdim 662259701Sdimvoid CommentASTToXMLConverter::visitHTMLStartTagComment( 663259701Sdim const HTMLStartTagComment *C) { 664276479Sdim Result << "<rawHTML"; 665276479Sdim if (C->isMalformed()) 666276479Sdim Result << " isMalformed=\"1\""; 667276479Sdim Result << ">"; 668276479Sdim { 669276479Sdim SmallString<32> Tag; 670276479Sdim { 671276479Sdim llvm::raw_svector_ostream TagOS(Tag); 672276479Sdim printHTMLStartTagComment(C, TagOS); 673276479Sdim } 674276479Sdim appendToResultWithCDATAEscaping(Tag); 675276479Sdim } 676276479Sdim Result << "</rawHTML>"; 677259701Sdim} 678259701Sdim 679259701Sdimvoid 680259701SdimCommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) { 681276479Sdim Result << "<rawHTML"; 682276479Sdim if (C->isMalformed()) 683276479Sdim Result << " isMalformed=\"1\""; 684276479Sdim Result << "></" << C->getTagName() << "></rawHTML>"; 685259701Sdim} 686259701Sdim 687259701Sdimvoid 688259701SdimCommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) { 689259701Sdim appendParagraphCommentWithKind(C, StringRef()); 690259701Sdim} 691259701Sdim 692259701Sdimvoid CommentASTToXMLConverter::appendParagraphCommentWithKind( 693259701Sdim const ParagraphComment *C, 694259701Sdim StringRef ParagraphKind) { 695259701Sdim if (C->isWhitespace()) 696259701Sdim return; 697259701Sdim 698259701Sdim if (ParagraphKind.empty()) 699259701Sdim Result << "<Para>"; 700259701Sdim else 701259701Sdim Result << "<Para kind=\"" << ParagraphKind << "\">"; 702259701Sdim 703259701Sdim for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); 704259701Sdim I != E; ++I) { 705259701Sdim visit(*I); 706259701Sdim } 707259701Sdim Result << "</Para>"; 708259701Sdim} 709259701Sdim 710259701Sdimvoid CommentASTToXMLConverter::visitBlockCommandComment( 711259701Sdim const BlockCommandComment *C) { 712259701Sdim StringRef ParagraphKind; 713259701Sdim 714259701Sdim switch (C->getCommandID()) { 715259701Sdim case CommandTraits::KCI_attention: 716259701Sdim case CommandTraits::KCI_author: 717259701Sdim case CommandTraits::KCI_authors: 718259701Sdim case CommandTraits::KCI_bug: 719259701Sdim case CommandTraits::KCI_copyright: 720259701Sdim case CommandTraits::KCI_date: 721259701Sdim case CommandTraits::KCI_invariant: 722259701Sdim case CommandTraits::KCI_note: 723259701Sdim case CommandTraits::KCI_post: 724259701Sdim case CommandTraits::KCI_pre: 725259701Sdim case CommandTraits::KCI_remark: 726259701Sdim case CommandTraits::KCI_remarks: 727259701Sdim case CommandTraits::KCI_sa: 728259701Sdim case CommandTraits::KCI_see: 729259701Sdim case CommandTraits::KCI_since: 730259701Sdim case CommandTraits::KCI_todo: 731259701Sdim case CommandTraits::KCI_version: 732259701Sdim case CommandTraits::KCI_warning: 733259701Sdim ParagraphKind = C->getCommandName(Traits); 734259701Sdim default: 735259701Sdim break; 736259701Sdim } 737259701Sdim 738259701Sdim appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind); 739259701Sdim} 740259701Sdim 741259701Sdimvoid CommentASTToXMLConverter::visitParamCommandComment( 742259701Sdim const ParamCommandComment *C) { 743259701Sdim Result << "<Parameter><Name>"; 744259701Sdim appendToResultWithXMLEscaping(C->isParamIndexValid() 745259701Sdim ? C->getParamName(FC) 746259701Sdim : C->getParamNameAsWritten()); 747259701Sdim Result << "</Name>"; 748259701Sdim 749259701Sdim if (C->isParamIndexValid()) { 750259701Sdim if (C->isVarArgParam()) 751259701Sdim Result << "<IsVarArg />"; 752259701Sdim else 753259701Sdim Result << "<Index>" << C->getParamIndex() << "</Index>"; 754259701Sdim } 755259701Sdim 756259701Sdim Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">"; 757259701Sdim switch (C->getDirection()) { 758259701Sdim case ParamCommandComment::In: 759259701Sdim Result << "in"; 760259701Sdim break; 761259701Sdim case ParamCommandComment::Out: 762259701Sdim Result << "out"; 763259701Sdim break; 764259701Sdim case ParamCommandComment::InOut: 765259701Sdim Result << "in,out"; 766259701Sdim break; 767259701Sdim } 768259701Sdim Result << "</Direction><Discussion>"; 769259701Sdim visit(C->getParagraph()); 770259701Sdim Result << "</Discussion></Parameter>"; 771259701Sdim} 772259701Sdim 773259701Sdimvoid CommentASTToXMLConverter::visitTParamCommandComment( 774259701Sdim const TParamCommandComment *C) { 775259701Sdim Result << "<Parameter><Name>"; 776259701Sdim appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC) 777259701Sdim : C->getParamNameAsWritten()); 778259701Sdim Result << "</Name>"; 779259701Sdim 780259701Sdim if (C->isPositionValid() && C->getDepth() == 1) { 781259701Sdim Result << "<Index>" << C->getIndex(0) << "</Index>"; 782259701Sdim } 783259701Sdim 784259701Sdim Result << "<Discussion>"; 785259701Sdim visit(C->getParagraph()); 786259701Sdim Result << "</Discussion></Parameter>"; 787259701Sdim} 788259701Sdim 789259701Sdimvoid CommentASTToXMLConverter::visitVerbatimBlockComment( 790259701Sdim const VerbatimBlockComment *C) { 791259701Sdim unsigned NumLines = C->getNumLines(); 792259701Sdim if (NumLines == 0) 793259701Sdim return; 794259701Sdim 795259701Sdim switch (C->getCommandID()) { 796259701Sdim case CommandTraits::KCI_code: 797259701Sdim Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">"; 798259701Sdim break; 799259701Sdim default: 800259701Sdim Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">"; 801259701Sdim break; 802259701Sdim } 803259701Sdim for (unsigned i = 0; i != NumLines; ++i) { 804259701Sdim appendToResultWithXMLEscaping(C->getText(i)); 805259701Sdim if (i + 1 != NumLines) 806259701Sdim Result << '\n'; 807259701Sdim } 808259701Sdim Result << "</Verbatim>"; 809259701Sdim} 810259701Sdim 811259701Sdimvoid CommentASTToXMLConverter::visitVerbatimBlockLineComment( 812259701Sdim const VerbatimBlockLineComment *C) { 813259701Sdim llvm_unreachable("should not see this AST node"); 814259701Sdim} 815259701Sdim 816259701Sdimvoid CommentASTToXMLConverter::visitVerbatimLineComment( 817259701Sdim const VerbatimLineComment *C) { 818259701Sdim Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">"; 819259701Sdim appendToResultWithXMLEscaping(C->getText()); 820259701Sdim Result << "</Verbatim>"; 821259701Sdim} 822259701Sdim 823259701Sdimvoid CommentASTToXMLConverter::visitFullComment(const FullComment *C) { 824259701Sdim FullCommentParts Parts(C, Traits); 825259701Sdim 826259701Sdim const DeclInfo *DI = C->getDeclInfo(); 827259701Sdim StringRef RootEndTag; 828259701Sdim if (DI) { 829259701Sdim switch (DI->getKind()) { 830259701Sdim case DeclInfo::OtherKind: 831259701Sdim RootEndTag = "</Other>"; 832259701Sdim Result << "<Other"; 833259701Sdim break; 834259701Sdim case DeclInfo::FunctionKind: 835259701Sdim RootEndTag = "</Function>"; 836259701Sdim Result << "<Function"; 837259701Sdim switch (DI->TemplateKind) { 838259701Sdim case DeclInfo::NotTemplate: 839259701Sdim break; 840259701Sdim case DeclInfo::Template: 841259701Sdim Result << " templateKind=\"template\""; 842259701Sdim break; 843259701Sdim case DeclInfo::TemplateSpecialization: 844259701Sdim Result << " templateKind=\"specialization\""; 845259701Sdim break; 846259701Sdim case DeclInfo::TemplatePartialSpecialization: 847259701Sdim llvm_unreachable("partial specializations of functions " 848259701Sdim "are not allowed in C++"); 849259701Sdim } 850259701Sdim if (DI->IsInstanceMethod) 851259701Sdim Result << " isInstanceMethod=\"1\""; 852259701Sdim if (DI->IsClassMethod) 853259701Sdim Result << " isClassMethod=\"1\""; 854259701Sdim break; 855259701Sdim case DeclInfo::ClassKind: 856259701Sdim RootEndTag = "</Class>"; 857259701Sdim Result << "<Class"; 858259701Sdim switch (DI->TemplateKind) { 859259701Sdim case DeclInfo::NotTemplate: 860259701Sdim break; 861259701Sdim case DeclInfo::Template: 862259701Sdim Result << " templateKind=\"template\""; 863259701Sdim break; 864259701Sdim case DeclInfo::TemplateSpecialization: 865259701Sdim Result << " templateKind=\"specialization\""; 866259701Sdim break; 867259701Sdim case DeclInfo::TemplatePartialSpecialization: 868259701Sdim Result << " templateKind=\"partialSpecialization\""; 869259701Sdim break; 870259701Sdim } 871259701Sdim break; 872259701Sdim case DeclInfo::VariableKind: 873259701Sdim RootEndTag = "</Variable>"; 874259701Sdim Result << "<Variable"; 875259701Sdim break; 876259701Sdim case DeclInfo::NamespaceKind: 877259701Sdim RootEndTag = "</Namespace>"; 878259701Sdim Result << "<Namespace"; 879259701Sdim break; 880259701Sdim case DeclInfo::TypedefKind: 881259701Sdim RootEndTag = "</Typedef>"; 882259701Sdim Result << "<Typedef"; 883259701Sdim break; 884259701Sdim case DeclInfo::EnumKind: 885259701Sdim RootEndTag = "</Enum>"; 886259701Sdim Result << "<Enum"; 887259701Sdim break; 888259701Sdim } 889259701Sdim 890259701Sdim { 891259701Sdim // Print line and column number. 892259701Sdim SourceLocation Loc = DI->CurrentDecl->getLocation(); 893259701Sdim std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); 894259701Sdim FileID FID = LocInfo.first; 895259701Sdim unsigned FileOffset = LocInfo.second; 896259701Sdim 897296417Sdim if (FID.isValid()) { 898259701Sdim if (const FileEntry *FE = SM.getFileEntryForID(FID)) { 899259701Sdim Result << " file=\""; 900259701Sdim appendToResultWithXMLEscaping(FE->getName()); 901259701Sdim Result << "\""; 902259701Sdim } 903259701Sdim Result << " line=\"" << SM.getLineNumber(FID, FileOffset) 904259701Sdim << "\" column=\"" << SM.getColumnNumber(FID, FileOffset) 905259701Sdim << "\""; 906259701Sdim } 907259701Sdim } 908259701Sdim 909259701Sdim // Finish the root tag. 910259701Sdim Result << ">"; 911259701Sdim 912259701Sdim bool FoundName = false; 913259701Sdim if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) { 914259701Sdim if (DeclarationName DeclName = ND->getDeclName()) { 915259701Sdim Result << "<Name>"; 916259701Sdim std::string Name = DeclName.getAsString(); 917259701Sdim appendToResultWithXMLEscaping(Name); 918259701Sdim FoundName = true; 919259701Sdim Result << "</Name>"; 920259701Sdim } 921259701Sdim } 922259701Sdim if (!FoundName) 923259701Sdim Result << "<Name><anonymous></Name>"; 924259701Sdim 925259701Sdim { 926259701Sdim // Print USR. 927259701Sdim SmallString<128> USR; 928259701Sdim generateUSRForDecl(DI->CommentDecl, USR); 929259701Sdim if (!USR.empty()) { 930259701Sdim Result << "<USR>"; 931259701Sdim appendToResultWithXMLEscaping(USR); 932259701Sdim Result << "</USR>"; 933259701Sdim } 934259701Sdim } 935259701Sdim } else { 936259701Sdim // No DeclInfo -- just emit some root tag and name tag. 937259701Sdim RootEndTag = "</Other>"; 938259701Sdim Result << "<Other><Name>unknown</Name>"; 939259701Sdim } 940259701Sdim 941259701Sdim if (Parts.Headerfile) { 942259701Sdim Result << "<Headerfile>"; 943259701Sdim visit(Parts.Headerfile); 944259701Sdim Result << "</Headerfile>"; 945259701Sdim } 946259701Sdim 947259701Sdim { 948259701Sdim // Pretty-print the declaration. 949259701Sdim Result << "<Declaration>"; 950259701Sdim SmallString<128> Declaration; 951259701Sdim getSourceTextOfDeclaration(DI, Declaration); 952259701Sdim formatTextOfDeclaration(DI, Declaration); 953259701Sdim appendToResultWithXMLEscaping(Declaration); 954259701Sdim Result << "</Declaration>"; 955259701Sdim } 956259701Sdim 957259701Sdim bool FirstParagraphIsBrief = false; 958259701Sdim if (Parts.Brief) { 959259701Sdim Result << "<Abstract>"; 960259701Sdim visit(Parts.Brief); 961259701Sdim Result << "</Abstract>"; 962259701Sdim } else if (Parts.FirstParagraph) { 963259701Sdim Result << "<Abstract>"; 964259701Sdim visit(Parts.FirstParagraph); 965259701Sdim Result << "</Abstract>"; 966259701Sdim FirstParagraphIsBrief = true; 967259701Sdim } 968259701Sdim 969259701Sdim if (Parts.TParams.size() != 0) { 970259701Sdim Result << "<TemplateParameters>"; 971259701Sdim for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i) 972259701Sdim visit(Parts.TParams[i]); 973259701Sdim Result << "</TemplateParameters>"; 974259701Sdim } 975259701Sdim 976259701Sdim if (Parts.Params.size() != 0) { 977259701Sdim Result << "<Parameters>"; 978259701Sdim for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i) 979259701Sdim visit(Parts.Params[i]); 980259701Sdim Result << "</Parameters>"; 981259701Sdim } 982259701Sdim 983259701Sdim if (Parts.Exceptions.size() != 0) { 984259701Sdim Result << "<Exceptions>"; 985259701Sdim for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i) 986259701Sdim visit(Parts.Exceptions[i]); 987259701Sdim Result << "</Exceptions>"; 988259701Sdim } 989259701Sdim 990259701Sdim if (Parts.Returns.size() != 0) { 991259701Sdim Result << "<ResultDiscussion>"; 992259701Sdim for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i) 993259701Sdim visit(Parts.Returns[i]); 994259701Sdim Result << "</ResultDiscussion>"; 995259701Sdim } 996259701Sdim 997259701Sdim if (DI->CommentDecl->hasAttrs()) { 998259701Sdim const AttrVec &Attrs = DI->CommentDecl->getAttrs(); 999259701Sdim for (unsigned i = 0, e = Attrs.size(); i != e; i++) { 1000259701Sdim const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]); 1001259701Sdim if (!AA) { 1002259701Sdim if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) { 1003259701Sdim if (DA->getMessage().empty()) 1004259701Sdim Result << "<Deprecated/>"; 1005259701Sdim else { 1006259701Sdim Result << "<Deprecated>"; 1007259701Sdim appendToResultWithXMLEscaping(DA->getMessage()); 1008259701Sdim Result << "</Deprecated>"; 1009259701Sdim } 1010259701Sdim } 1011259701Sdim else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) { 1012259701Sdim if (UA->getMessage().empty()) 1013259701Sdim Result << "<Unavailable/>"; 1014259701Sdim else { 1015259701Sdim Result << "<Unavailable>"; 1016259701Sdim appendToResultWithXMLEscaping(UA->getMessage()); 1017259701Sdim Result << "</Unavailable>"; 1018259701Sdim } 1019259701Sdim } 1020259701Sdim continue; 1021259701Sdim } 1022259701Sdim 1023259701Sdim // 'availability' attribute. 1024259701Sdim Result << "<Availability"; 1025259701Sdim StringRef Distribution; 1026259701Sdim if (AA->getPlatform()) { 1027259701Sdim Distribution = AvailabilityAttr::getPrettyPlatformName( 1028259701Sdim AA->getPlatform()->getName()); 1029259701Sdim if (Distribution.empty()) 1030259701Sdim Distribution = AA->getPlatform()->getName(); 1031259701Sdim } 1032259701Sdim Result << " distribution=\"" << Distribution << "\">"; 1033259701Sdim VersionTuple IntroducedInVersion = AA->getIntroduced(); 1034259701Sdim if (!IntroducedInVersion.empty()) { 1035259701Sdim Result << "<IntroducedInVersion>" 1036259701Sdim << IntroducedInVersion.getAsString() 1037259701Sdim << "</IntroducedInVersion>"; 1038259701Sdim } 1039259701Sdim VersionTuple DeprecatedInVersion = AA->getDeprecated(); 1040259701Sdim if (!DeprecatedInVersion.empty()) { 1041259701Sdim Result << "<DeprecatedInVersion>" 1042259701Sdim << DeprecatedInVersion.getAsString() 1043259701Sdim << "</DeprecatedInVersion>"; 1044259701Sdim } 1045259701Sdim VersionTuple RemovedAfterVersion = AA->getObsoleted(); 1046259701Sdim if (!RemovedAfterVersion.empty()) { 1047259701Sdim Result << "<RemovedAfterVersion>" 1048259701Sdim << RemovedAfterVersion.getAsString() 1049259701Sdim << "</RemovedAfterVersion>"; 1050259701Sdim } 1051259701Sdim StringRef DeprecationSummary = AA->getMessage(); 1052259701Sdim if (!DeprecationSummary.empty()) { 1053259701Sdim Result << "<DeprecationSummary>"; 1054259701Sdim appendToResultWithXMLEscaping(DeprecationSummary); 1055259701Sdim Result << "</DeprecationSummary>"; 1056259701Sdim } 1057259701Sdim if (AA->getUnavailable()) 1058259701Sdim Result << "<Unavailable/>"; 1059259701Sdim Result << "</Availability>"; 1060259701Sdim } 1061259701Sdim } 1062259701Sdim 1063259701Sdim { 1064259701Sdim bool StartTagEmitted = false; 1065259701Sdim for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) { 1066259701Sdim const Comment *C = Parts.MiscBlocks[i]; 1067259701Sdim if (FirstParagraphIsBrief && C == Parts.FirstParagraph) 1068259701Sdim continue; 1069259701Sdim if (!StartTagEmitted) { 1070259701Sdim Result << "<Discussion>"; 1071259701Sdim StartTagEmitted = true; 1072259701Sdim } 1073259701Sdim visit(C); 1074259701Sdim } 1075259701Sdim if (StartTagEmitted) 1076259701Sdim Result << "</Discussion>"; 1077259701Sdim } 1078259701Sdim 1079259701Sdim Result << RootEndTag; 1080259701Sdim} 1081259701Sdim 1082259701Sdimvoid CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) { 1083259701Sdim for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) { 1084259701Sdim const char C = *I; 1085259701Sdim switch (C) { 1086259701Sdim case '&': 1087259701Sdim Result << "&"; 1088259701Sdim break; 1089259701Sdim case '<': 1090259701Sdim Result << "<"; 1091259701Sdim break; 1092259701Sdim case '>': 1093259701Sdim Result << ">"; 1094259701Sdim break; 1095259701Sdim case '"': 1096259701Sdim Result << """; 1097259701Sdim break; 1098259701Sdim case '\'': 1099259701Sdim Result << "'"; 1100259701Sdim break; 1101259701Sdim default: 1102259701Sdim Result << C; 1103259701Sdim break; 1104259701Sdim } 1105259701Sdim } 1106259701Sdim} 1107259701Sdim 1108276479Sdimvoid CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) { 1109276479Sdim if (S.empty()) 1110276479Sdim return; 1111276479Sdim 1112276479Sdim Result << "<![CDATA["; 1113276479Sdim while (!S.empty()) { 1114276479Sdim size_t Pos = S.find("]]>"); 1115276479Sdim if (Pos == 0) { 1116276479Sdim Result << "]]]]><![CDATA[>"; 1117276479Sdim S = S.drop_front(3); 1118276479Sdim continue; 1119276479Sdim } 1120276479Sdim if (Pos == StringRef::npos) 1121276479Sdim Pos = S.size(); 1122276479Sdim 1123276479Sdim Result << S.substr(0, Pos); 1124276479Sdim 1125276479Sdim S = S.drop_front(Pos); 1126276479Sdim } 1127276479Sdim Result << "]]>"; 1128276479Sdim} 1129276479Sdim 1130276479SdimCommentToXMLConverter::CommentToXMLConverter() : FormatInMemoryUniqueId(0) {} 1131276479SdimCommentToXMLConverter::~CommentToXMLConverter() {} 1132276479Sdim 1133259701Sdimvoid CommentToXMLConverter::convertCommentToHTML(const FullComment *FC, 1134259701Sdim SmallVectorImpl<char> &HTML, 1135259701Sdim const ASTContext &Context) { 1136259701Sdim CommentASTToHTMLConverter Converter(FC, HTML, 1137259701Sdim Context.getCommentCommandTraits()); 1138259701Sdim Converter.visit(FC); 1139259701Sdim} 1140259701Sdim 1141259701Sdimvoid CommentToXMLConverter::convertHTMLTagNodeToText( 1142259701Sdim const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text, 1143259701Sdim const ASTContext &Context) { 1144276479Sdim CommentASTToHTMLConverter Converter(nullptr, Text, 1145259701Sdim Context.getCommentCommandTraits()); 1146259701Sdim Converter.visit(HTC); 1147259701Sdim} 1148259701Sdim 1149259701Sdimvoid CommentToXMLConverter::convertCommentToXML(const FullComment *FC, 1150259701Sdim SmallVectorImpl<char> &XML, 1151259701Sdim const ASTContext &Context) { 1152276479Sdim if (!FormatContext || (FormatInMemoryUniqueId % 1000) == 0) { 1153276479Sdim // Create a new format context, or re-create it after some number of 1154276479Sdim // iterations, so the buffers don't grow too large. 1155276479Sdim FormatContext.reset(new SimpleFormatContext(Context.getLangOpts())); 1156259701Sdim } 1157259701Sdim 1158259701Sdim CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(), 1159259701Sdim Context.getSourceManager(), *FormatContext, 1160259701Sdim FormatInMemoryUniqueId++); 1161259701Sdim Converter.visit(FC); 1162259701Sdim} 1163259701Sdim 1164