|  | //===--- CommentToXML.cpp - Convert comments to XML representation --------===// | 
|  | // | 
|  | //                     The LLVM Compiler Infrastructure | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "clang/Index/CommentToXML.h" | 
|  | #include "clang/AST/ASTContext.h" | 
|  | #include "clang/AST/Attr.h" | 
|  | #include "clang/AST/Comment.h" | 
|  | #include "clang/AST/CommentVisitor.h" | 
|  | #include "clang/Format/Format.h" | 
|  | #include "clang/Index/USRGeneration.h" | 
|  | #include "llvm/ADT/StringExtras.h" | 
|  | #include "llvm/ADT/TinyPtrVector.h" | 
|  | #include "llvm/Support/raw_ostream.h" | 
|  |  | 
|  | using namespace clang; | 
|  | using namespace clang::comments; | 
|  | using namespace clang::index; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | /// This comparison will sort parameters with valid index by index, then vararg | 
|  | /// parameters, and invalid (unresolved) parameters last. | 
|  | class ParamCommandCommentCompareIndex { | 
|  | public: | 
|  | bool operator()(const ParamCommandComment *LHS, | 
|  | const ParamCommandComment *RHS) const { | 
|  | unsigned LHSIndex = UINT_MAX; | 
|  | unsigned RHSIndex = UINT_MAX; | 
|  |  | 
|  | if (LHS->isParamIndexValid()) { | 
|  | if (LHS->isVarArgParam()) | 
|  | LHSIndex = UINT_MAX - 1; | 
|  | else | 
|  | LHSIndex = LHS->getParamIndex(); | 
|  | } | 
|  | if (RHS->isParamIndexValid()) { | 
|  | if (RHS->isVarArgParam()) | 
|  | RHSIndex = UINT_MAX - 1; | 
|  | else | 
|  | RHSIndex = RHS->getParamIndex(); | 
|  | } | 
|  | return LHSIndex < RHSIndex; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /// This comparison will sort template parameters in the following order: | 
|  | /// \li real template parameters (depth = 1) in index order; | 
|  | /// \li all other names (depth > 1); | 
|  | /// \li unresolved names. | 
|  | class TParamCommandCommentComparePosition { | 
|  | public: | 
|  | bool operator()(const TParamCommandComment *LHS, | 
|  | const TParamCommandComment *RHS) const { | 
|  | // Sort unresolved names last. | 
|  | if (!LHS->isPositionValid()) | 
|  | return false; | 
|  | if (!RHS->isPositionValid()) | 
|  | return true; | 
|  |  | 
|  | if (LHS->getDepth() > 1) | 
|  | return false; | 
|  | if (RHS->getDepth() > 1) | 
|  | return true; | 
|  |  | 
|  | // Sort template parameters in index order. | 
|  | if (LHS->getDepth() == 1 && RHS->getDepth() == 1) | 
|  | return LHS->getIndex(0) < RHS->getIndex(0); | 
|  |  | 
|  | // Leave all other names in source order. | 
|  | return true; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /// Separate parts of a FullComment. | 
|  | struct FullCommentParts { | 
|  | /// Take a full comment apart and initialize members accordingly. | 
|  | FullCommentParts(const FullComment *C, | 
|  | const CommandTraits &Traits); | 
|  |  | 
|  | const BlockContentComment *Brief; | 
|  | const BlockContentComment *Headerfile; | 
|  | const ParagraphComment *FirstParagraph; | 
|  | SmallVector<const BlockCommandComment *, 4> Returns; | 
|  | SmallVector<const ParamCommandComment *, 8> Params; | 
|  | SmallVector<const TParamCommandComment *, 4> TParams; | 
|  | llvm::TinyPtrVector<const BlockCommandComment *> Exceptions; | 
|  | SmallVector<const BlockContentComment *, 8> MiscBlocks; | 
|  | }; | 
|  |  | 
|  | FullCommentParts::FullCommentParts(const FullComment *C, | 
|  | const CommandTraits &Traits) : | 
|  | Brief(nullptr), Headerfile(nullptr), FirstParagraph(nullptr) { | 
|  | for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); | 
|  | I != E; ++I) { | 
|  | const Comment *Child = *I; | 
|  | if (!Child) | 
|  | continue; | 
|  | switch (Child->getCommentKind()) { | 
|  | case Comment::NoCommentKind: | 
|  | continue; | 
|  |  | 
|  | case Comment::ParagraphCommentKind: { | 
|  | const ParagraphComment *PC = cast<ParagraphComment>(Child); | 
|  | if (PC->isWhitespace()) | 
|  | break; | 
|  | if (!FirstParagraph) | 
|  | FirstParagraph = PC; | 
|  |  | 
|  | MiscBlocks.push_back(PC); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case Comment::BlockCommandCommentKind: { | 
|  | const BlockCommandComment *BCC = cast<BlockCommandComment>(Child); | 
|  | const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID()); | 
|  | if (!Brief && Info->IsBriefCommand) { | 
|  | Brief = BCC; | 
|  | break; | 
|  | } | 
|  | if (!Headerfile && Info->IsHeaderfileCommand) { | 
|  | Headerfile = BCC; | 
|  | break; | 
|  | } | 
|  | if (Info->IsReturnsCommand) { | 
|  | Returns.push_back(BCC); | 
|  | break; | 
|  | } | 
|  | if (Info->IsThrowsCommand) { | 
|  | Exceptions.push_back(BCC); | 
|  | break; | 
|  | } | 
|  | MiscBlocks.push_back(BCC); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case Comment::ParamCommandCommentKind: { | 
|  | const ParamCommandComment *PCC = cast<ParamCommandComment>(Child); | 
|  | if (!PCC->hasParamName()) | 
|  | break; | 
|  |  | 
|  | if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph()) | 
|  | break; | 
|  |  | 
|  | Params.push_back(PCC); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case Comment::TParamCommandCommentKind: { | 
|  | const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child); | 
|  | if (!TPCC->hasParamName()) | 
|  | break; | 
|  |  | 
|  | if (!TPCC->hasNonWhitespaceParagraph()) | 
|  | break; | 
|  |  | 
|  | TParams.push_back(TPCC); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case Comment::VerbatimBlockCommentKind: | 
|  | MiscBlocks.push_back(cast<BlockCommandComment>(Child)); | 
|  | break; | 
|  |  | 
|  | case Comment::VerbatimLineCommentKind: { | 
|  | const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child); | 
|  | const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID()); | 
|  | if (!Info->IsDeclarationCommand) | 
|  | MiscBlocks.push_back(VLC); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case Comment::TextCommentKind: | 
|  | case Comment::InlineCommandCommentKind: | 
|  | case Comment::HTMLStartTagCommentKind: | 
|  | case Comment::HTMLEndTagCommentKind: | 
|  | case Comment::VerbatimBlockLineCommentKind: | 
|  | case Comment::FullCommentKind: | 
|  | llvm_unreachable("AST node of this kind can't be a child of " | 
|  | "a FullComment"); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Sort params in order they are declared in the function prototype. | 
|  | // Unresolved parameters are put at the end of the list in the same order | 
|  | // they were seen in the comment. | 
|  | std::stable_sort(Params.begin(), Params.end(), | 
|  | ParamCommandCommentCompareIndex()); | 
|  |  | 
|  | std::stable_sort(TParams.begin(), TParams.end(), | 
|  | TParamCommandCommentComparePosition()); | 
|  | } | 
|  |  | 
|  | void printHTMLStartTagComment(const HTMLStartTagComment *C, | 
|  | llvm::raw_svector_ostream &Result) { | 
|  | Result << "<" << C->getTagName(); | 
|  |  | 
|  | if (C->getNumAttrs() != 0) { | 
|  | for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) { | 
|  | Result << " "; | 
|  | const HTMLStartTagComment::Attribute &Attr = C->getAttr(i); | 
|  | Result << Attr.Name; | 
|  | if (!Attr.Value.empty()) | 
|  | Result << "=\"" << Attr.Value << "\""; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!C->isSelfClosing()) | 
|  | Result << ">"; | 
|  | else | 
|  | Result << "/>"; | 
|  | } | 
|  |  | 
|  | class CommentASTToHTMLConverter : | 
|  | public ConstCommentVisitor<CommentASTToHTMLConverter> { | 
|  | public: | 
|  | /// \param Str accumulator for HTML. | 
|  | CommentASTToHTMLConverter(const FullComment *FC, | 
|  | SmallVectorImpl<char> &Str, | 
|  | const CommandTraits &Traits) : | 
|  | FC(FC), Result(Str), Traits(Traits) | 
|  | { } | 
|  |  | 
|  | // Inline content. | 
|  | void visitTextComment(const TextComment *C); | 
|  | void visitInlineCommandComment(const InlineCommandComment *C); | 
|  | void visitHTMLStartTagComment(const HTMLStartTagComment *C); | 
|  | void visitHTMLEndTagComment(const HTMLEndTagComment *C); | 
|  |  | 
|  | // Block content. | 
|  | void visitParagraphComment(const ParagraphComment *C); | 
|  | void visitBlockCommandComment(const BlockCommandComment *C); | 
|  | void visitParamCommandComment(const ParamCommandComment *C); | 
|  | void visitTParamCommandComment(const TParamCommandComment *C); | 
|  | void visitVerbatimBlockComment(const VerbatimBlockComment *C); | 
|  | void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); | 
|  | void visitVerbatimLineComment(const VerbatimLineComment *C); | 
|  |  | 
|  | void visitFullComment(const FullComment *C); | 
|  |  | 
|  | // Helpers. | 
|  |  | 
|  | /// Convert a paragraph that is not a block by itself (an argument to some | 
|  | /// command). | 
|  | void visitNonStandaloneParagraphComment(const ParagraphComment *C); | 
|  |  | 
|  | void appendToResultWithHTMLEscaping(StringRef S); | 
|  |  | 
|  | private: | 
|  | const FullComment *FC; | 
|  | /// Output stream for HTML. | 
|  | llvm::raw_svector_ostream Result; | 
|  |  | 
|  | const CommandTraits &Traits; | 
|  | }; | 
|  | } // end unnamed namespace | 
|  |  | 
|  | void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) { | 
|  | appendToResultWithHTMLEscaping(C->getText()); | 
|  | } | 
|  |  | 
|  | void CommentASTToHTMLConverter::visitInlineCommandComment( | 
|  | const InlineCommandComment *C) { | 
|  | // Nothing to render if no arguments supplied. | 
|  | if (C->getNumArgs() == 0) | 
|  | return; | 
|  |  | 
|  | // Nothing to render if argument is empty. | 
|  | StringRef Arg0 = C->getArgText(0); | 
|  | if (Arg0.empty()) | 
|  | return; | 
|  |  | 
|  | switch (C->getRenderKind()) { | 
|  | case InlineCommandComment::RenderNormal: | 
|  | for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) { | 
|  | appendToResultWithHTMLEscaping(C->getArgText(i)); | 
|  | Result << " "; | 
|  | } | 
|  | return; | 
|  |  | 
|  | case InlineCommandComment::RenderBold: | 
|  | assert(C->getNumArgs() == 1); | 
|  | Result << "<b>"; | 
|  | appendToResultWithHTMLEscaping(Arg0); | 
|  | Result << "</b>"; | 
|  | return; | 
|  | case InlineCommandComment::RenderMonospaced: | 
|  | assert(C->getNumArgs() == 1); | 
|  | Result << "<tt>"; | 
|  | appendToResultWithHTMLEscaping(Arg0); | 
|  | Result<< "</tt>"; | 
|  | return; | 
|  | case InlineCommandComment::RenderEmphasized: | 
|  | assert(C->getNumArgs() == 1); | 
|  | Result << "<em>"; | 
|  | appendToResultWithHTMLEscaping(Arg0); | 
|  | Result << "</em>"; | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | void CommentASTToHTMLConverter::visitHTMLStartTagComment( | 
|  | const HTMLStartTagComment *C) { | 
|  | printHTMLStartTagComment(C, Result); | 
|  | } | 
|  |  | 
|  | void CommentASTToHTMLConverter::visitHTMLEndTagComment( | 
|  | const HTMLEndTagComment *C) { | 
|  | Result << "</" << C->getTagName() << ">"; | 
|  | } | 
|  |  | 
|  | void CommentASTToHTMLConverter::visitParagraphComment( | 
|  | const ParagraphComment *C) { | 
|  | if (C->isWhitespace()) | 
|  | return; | 
|  |  | 
|  | Result << "<p>"; | 
|  | for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); | 
|  | I != E; ++I) { | 
|  | visit(*I); | 
|  | } | 
|  | Result << "</p>"; | 
|  | } | 
|  |  | 
|  | void CommentASTToHTMLConverter::visitBlockCommandComment( | 
|  | const BlockCommandComment *C) { | 
|  | const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID()); | 
|  | if (Info->IsBriefCommand) { | 
|  | Result << "<p class=\"para-brief\">"; | 
|  | visitNonStandaloneParagraphComment(C->getParagraph()); | 
|  | Result << "</p>"; | 
|  | return; | 
|  | } | 
|  | if (Info->IsReturnsCommand) { | 
|  | Result << "<p class=\"para-returns\">" | 
|  | "<span class=\"word-returns\">Returns</span> "; | 
|  | visitNonStandaloneParagraphComment(C->getParagraph()); | 
|  | Result << "</p>"; | 
|  | return; | 
|  | } | 
|  | // We don't know anything about this command.  Just render the paragraph. | 
|  | visit(C->getParagraph()); | 
|  | } | 
|  |  | 
|  | void CommentASTToHTMLConverter::visitParamCommandComment( | 
|  | const ParamCommandComment *C) { | 
|  | if (C->isParamIndexValid()) { | 
|  | if (C->isVarArgParam()) { | 
|  | Result << "<dt class=\"param-name-index-vararg\">"; | 
|  | appendToResultWithHTMLEscaping(C->getParamNameAsWritten()); | 
|  | } else { | 
|  | Result << "<dt class=\"param-name-index-" | 
|  | << C->getParamIndex() | 
|  | << "\">"; | 
|  | appendToResultWithHTMLEscaping(C->getParamName(FC)); | 
|  | } | 
|  | } else { | 
|  | Result << "<dt class=\"param-name-index-invalid\">"; | 
|  | appendToResultWithHTMLEscaping(C->getParamNameAsWritten()); | 
|  | } | 
|  | Result << "</dt>"; | 
|  |  | 
|  | if (C->isParamIndexValid()) { | 
|  | if (C->isVarArgParam()) | 
|  | Result << "<dd class=\"param-descr-index-vararg\">"; | 
|  | else | 
|  | Result << "<dd class=\"param-descr-index-" | 
|  | << C->getParamIndex() | 
|  | << "\">"; | 
|  | } else | 
|  | Result << "<dd class=\"param-descr-index-invalid\">"; | 
|  |  | 
|  | visitNonStandaloneParagraphComment(C->getParagraph()); | 
|  | Result << "</dd>"; | 
|  | } | 
|  |  | 
|  | void CommentASTToHTMLConverter::visitTParamCommandComment( | 
|  | const TParamCommandComment *C) { | 
|  | if (C->isPositionValid()) { | 
|  | if (C->getDepth() == 1) | 
|  | Result << "<dt class=\"tparam-name-index-" | 
|  | << C->getIndex(0) | 
|  | << "\">"; | 
|  | else | 
|  | Result << "<dt class=\"tparam-name-index-other\">"; | 
|  | appendToResultWithHTMLEscaping(C->getParamName(FC)); | 
|  | } else { | 
|  | Result << "<dt class=\"tparam-name-index-invalid\">"; | 
|  | appendToResultWithHTMLEscaping(C->getParamNameAsWritten()); | 
|  | } | 
|  |  | 
|  | Result << "</dt>"; | 
|  |  | 
|  | if (C->isPositionValid()) { | 
|  | if (C->getDepth() == 1) | 
|  | Result << "<dd class=\"tparam-descr-index-" | 
|  | << C->getIndex(0) | 
|  | << "\">"; | 
|  | else | 
|  | Result << "<dd class=\"tparam-descr-index-other\">"; | 
|  | } else | 
|  | Result << "<dd class=\"tparam-descr-index-invalid\">"; | 
|  |  | 
|  | visitNonStandaloneParagraphComment(C->getParagraph()); | 
|  | Result << "</dd>"; | 
|  | } | 
|  |  | 
|  | void CommentASTToHTMLConverter::visitVerbatimBlockComment( | 
|  | const VerbatimBlockComment *C) { | 
|  | unsigned NumLines = C->getNumLines(); | 
|  | if (NumLines == 0) | 
|  | return; | 
|  |  | 
|  | Result << "<pre>"; | 
|  | for (unsigned i = 0; i != NumLines; ++i) { | 
|  | appendToResultWithHTMLEscaping(C->getText(i)); | 
|  | if (i + 1 != NumLines) | 
|  | Result << '\n'; | 
|  | } | 
|  | Result << "</pre>"; | 
|  | } | 
|  |  | 
|  | void CommentASTToHTMLConverter::visitVerbatimBlockLineComment( | 
|  | const VerbatimBlockLineComment *C) { | 
|  | llvm_unreachable("should not see this AST node"); | 
|  | } | 
|  |  | 
|  | void CommentASTToHTMLConverter::visitVerbatimLineComment( | 
|  | const VerbatimLineComment *C) { | 
|  | Result << "<pre>"; | 
|  | appendToResultWithHTMLEscaping(C->getText()); | 
|  | Result << "</pre>"; | 
|  | } | 
|  |  | 
|  | void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) { | 
|  | FullCommentParts Parts(C, Traits); | 
|  |  | 
|  | bool FirstParagraphIsBrief = false; | 
|  | if (Parts.Headerfile) | 
|  | visit(Parts.Headerfile); | 
|  | if (Parts.Brief) | 
|  | visit(Parts.Brief); | 
|  | else if (Parts.FirstParagraph) { | 
|  | Result << "<p class=\"para-brief\">"; | 
|  | visitNonStandaloneParagraphComment(Parts.FirstParagraph); | 
|  | Result << "</p>"; | 
|  | FirstParagraphIsBrief = true; | 
|  | } | 
|  |  | 
|  | for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) { | 
|  | const Comment *C = Parts.MiscBlocks[i]; | 
|  | if (FirstParagraphIsBrief && C == Parts.FirstParagraph) | 
|  | continue; | 
|  | visit(C); | 
|  | } | 
|  |  | 
|  | if (Parts.TParams.size() != 0) { | 
|  | Result << "<dl>"; | 
|  | for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i) | 
|  | visit(Parts.TParams[i]); | 
|  | Result << "</dl>"; | 
|  | } | 
|  |  | 
|  | if (Parts.Params.size() != 0) { | 
|  | Result << "<dl>"; | 
|  | for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i) | 
|  | visit(Parts.Params[i]); | 
|  | Result << "</dl>"; | 
|  | } | 
|  |  | 
|  | if (Parts.Returns.size() != 0) { | 
|  | Result << "<div class=\"result-discussion\">"; | 
|  | for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i) | 
|  | visit(Parts.Returns[i]); | 
|  | Result << "</div>"; | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment( | 
|  | const ParagraphComment *C) { | 
|  | if (!C) | 
|  | return; | 
|  |  | 
|  | for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); | 
|  | I != E; ++I) { | 
|  | visit(*I); | 
|  | } | 
|  | } | 
|  |  | 
|  | void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) { | 
|  | for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) { | 
|  | const char C = *I; | 
|  | switch (C) { | 
|  | case '&': | 
|  | Result << "&"; | 
|  | break; | 
|  | case '<': | 
|  | Result << "<"; | 
|  | break; | 
|  | case '>': | 
|  | Result << ">"; | 
|  | break; | 
|  | case '"': | 
|  | Result << """; | 
|  | break; | 
|  | case '\'': | 
|  | Result << "'"; | 
|  | break; | 
|  | case '/': | 
|  | Result << "/"; | 
|  | break; | 
|  | default: | 
|  | Result << C; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | class CommentASTToXMLConverter : | 
|  | public ConstCommentVisitor<CommentASTToXMLConverter> { | 
|  | public: | 
|  | /// \param Str accumulator for XML. | 
|  | CommentASTToXMLConverter(const FullComment *FC, | 
|  | SmallVectorImpl<char> &Str, | 
|  | const CommandTraits &Traits, | 
|  | const SourceManager &SM) : | 
|  | FC(FC), Result(Str), Traits(Traits), SM(SM) { } | 
|  |  | 
|  | // Inline content. | 
|  | void visitTextComment(const TextComment *C); | 
|  | void visitInlineCommandComment(const InlineCommandComment *C); | 
|  | void visitHTMLStartTagComment(const HTMLStartTagComment *C); | 
|  | void visitHTMLEndTagComment(const HTMLEndTagComment *C); | 
|  |  | 
|  | // Block content. | 
|  | void visitParagraphComment(const ParagraphComment *C); | 
|  |  | 
|  | void appendParagraphCommentWithKind(const ParagraphComment *C, | 
|  | StringRef Kind); | 
|  |  | 
|  | void visitBlockCommandComment(const BlockCommandComment *C); | 
|  | void visitParamCommandComment(const ParamCommandComment *C); | 
|  | void visitTParamCommandComment(const TParamCommandComment *C); | 
|  | void visitVerbatimBlockComment(const VerbatimBlockComment *C); | 
|  | void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); | 
|  | void visitVerbatimLineComment(const VerbatimLineComment *C); | 
|  |  | 
|  | void visitFullComment(const FullComment *C); | 
|  |  | 
|  | // Helpers. | 
|  | void appendToResultWithXMLEscaping(StringRef S); | 
|  | void appendToResultWithCDATAEscaping(StringRef S); | 
|  |  | 
|  | void formatTextOfDeclaration(const DeclInfo *DI, | 
|  | SmallString<128> &Declaration); | 
|  |  | 
|  | private: | 
|  | const FullComment *FC; | 
|  |  | 
|  | /// Output stream for XML. | 
|  | llvm::raw_svector_ostream Result; | 
|  |  | 
|  | const CommandTraits &Traits; | 
|  | const SourceManager &SM; | 
|  | }; | 
|  |  | 
|  | void getSourceTextOfDeclaration(const DeclInfo *ThisDecl, | 
|  | SmallVectorImpl<char> &Str) { | 
|  | ASTContext &Context = ThisDecl->CurrentDecl->getASTContext(); | 
|  | const LangOptions &LangOpts = Context.getLangOpts(); | 
|  | llvm::raw_svector_ostream OS(Str); | 
|  | PrintingPolicy PPolicy(LangOpts); | 
|  | PPolicy.PolishForDeclaration = true; | 
|  | PPolicy.TerseOutput = true; | 
|  | PPolicy.ConstantsAsWritten = true; | 
|  | ThisDecl->CurrentDecl->print(OS, PPolicy, | 
|  | /*Indentation*/0, /*PrintInstantiation*/false); | 
|  | } | 
|  |  | 
|  | void CommentASTToXMLConverter::formatTextOfDeclaration( | 
|  | const DeclInfo *DI, SmallString<128> &Declaration) { | 
|  | // Formatting API expects null terminated input string. | 
|  | StringRef StringDecl(Declaration.c_str(), Declaration.size()); | 
|  |  | 
|  | // Formatter specific code. | 
|  | unsigned Offset = 0; | 
|  | unsigned Length = Declaration.size(); | 
|  |  | 
|  | format::FormatStyle Style = format::getLLVMStyle(); | 
|  | Style.FixNamespaceComments = false; | 
|  | tooling::Replacements Replaces = | 
|  | reformat(Style, StringDecl, tooling::Range(Offset, Length), "xmldecl.xd"); | 
|  | auto FormattedStringDecl = applyAllReplacements(StringDecl, Replaces); | 
|  | if (static_cast<bool>(FormattedStringDecl)) { | 
|  | Declaration = *FormattedStringDecl; | 
|  | } | 
|  | } | 
|  |  | 
|  | } // end unnamed namespace | 
|  |  | 
|  | void CommentASTToXMLConverter::visitTextComment(const TextComment *C) { | 
|  | appendToResultWithXMLEscaping(C->getText()); | 
|  | } | 
|  |  | 
|  | void CommentASTToXMLConverter::visitInlineCommandComment( | 
|  | const InlineCommandComment *C) { | 
|  | // Nothing to render if no arguments supplied. | 
|  | if (C->getNumArgs() == 0) | 
|  | return; | 
|  |  | 
|  | // Nothing to render if argument is empty. | 
|  | StringRef Arg0 = C->getArgText(0); | 
|  | if (Arg0.empty()) | 
|  | return; | 
|  |  | 
|  | switch (C->getRenderKind()) { | 
|  | case InlineCommandComment::RenderNormal: | 
|  | for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) { | 
|  | appendToResultWithXMLEscaping(C->getArgText(i)); | 
|  | Result << " "; | 
|  | } | 
|  | return; | 
|  | case InlineCommandComment::RenderBold: | 
|  | assert(C->getNumArgs() == 1); | 
|  | Result << "<bold>"; | 
|  | appendToResultWithXMLEscaping(Arg0); | 
|  | Result << "</bold>"; | 
|  | return; | 
|  | case InlineCommandComment::RenderMonospaced: | 
|  | assert(C->getNumArgs() == 1); | 
|  | Result << "<monospaced>"; | 
|  | appendToResultWithXMLEscaping(Arg0); | 
|  | Result << "</monospaced>"; | 
|  | return; | 
|  | case InlineCommandComment::RenderEmphasized: | 
|  | assert(C->getNumArgs() == 1); | 
|  | Result << "<emphasized>"; | 
|  | appendToResultWithXMLEscaping(Arg0); | 
|  | Result << "</emphasized>"; | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | void CommentASTToXMLConverter::visitHTMLStartTagComment( | 
|  | const HTMLStartTagComment *C) { | 
|  | Result << "<rawHTML"; | 
|  | if (C->isMalformed()) | 
|  | Result << " isMalformed=\"1\""; | 
|  | Result << ">"; | 
|  | { | 
|  | SmallString<32> Tag; | 
|  | { | 
|  | llvm::raw_svector_ostream TagOS(Tag); | 
|  | printHTMLStartTagComment(C, TagOS); | 
|  | } | 
|  | appendToResultWithCDATAEscaping(Tag); | 
|  | } | 
|  | Result << "</rawHTML>"; | 
|  | } | 
|  |  | 
|  | void | 
|  | CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) { | 
|  | Result << "<rawHTML"; | 
|  | if (C->isMalformed()) | 
|  | Result << " isMalformed=\"1\""; | 
|  | Result << "></" << C->getTagName() << "></rawHTML>"; | 
|  | } | 
|  |  | 
|  | void | 
|  | CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) { | 
|  | appendParagraphCommentWithKind(C, StringRef()); | 
|  | } | 
|  |  | 
|  | void CommentASTToXMLConverter::appendParagraphCommentWithKind( | 
|  | const ParagraphComment *C, | 
|  | StringRef ParagraphKind) { | 
|  | if (C->isWhitespace()) | 
|  | return; | 
|  |  | 
|  | if (ParagraphKind.empty()) | 
|  | Result << "<Para>"; | 
|  | else | 
|  | Result << "<Para kind=\"" << ParagraphKind << "\">"; | 
|  |  | 
|  | for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); | 
|  | I != E; ++I) { | 
|  | visit(*I); | 
|  | } | 
|  | Result << "</Para>"; | 
|  | } | 
|  |  | 
|  | void CommentASTToXMLConverter::visitBlockCommandComment( | 
|  | const BlockCommandComment *C) { | 
|  | StringRef ParagraphKind; | 
|  |  | 
|  | switch (C->getCommandID()) { | 
|  | case CommandTraits::KCI_attention: | 
|  | case CommandTraits::KCI_author: | 
|  | case CommandTraits::KCI_authors: | 
|  | case CommandTraits::KCI_bug: | 
|  | case CommandTraits::KCI_copyright: | 
|  | case CommandTraits::KCI_date: | 
|  | case CommandTraits::KCI_invariant: | 
|  | case CommandTraits::KCI_note: | 
|  | case CommandTraits::KCI_post: | 
|  | case CommandTraits::KCI_pre: | 
|  | case CommandTraits::KCI_remark: | 
|  | case CommandTraits::KCI_remarks: | 
|  | case CommandTraits::KCI_sa: | 
|  | case CommandTraits::KCI_see: | 
|  | case CommandTraits::KCI_since: | 
|  | case CommandTraits::KCI_todo: | 
|  | case CommandTraits::KCI_version: | 
|  | case CommandTraits::KCI_warning: | 
|  | ParagraphKind = C->getCommandName(Traits); | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind); | 
|  | } | 
|  |  | 
|  | void CommentASTToXMLConverter::visitParamCommandComment( | 
|  | const ParamCommandComment *C) { | 
|  | Result << "<Parameter><Name>"; | 
|  | appendToResultWithXMLEscaping(C->isParamIndexValid() | 
|  | ? C->getParamName(FC) | 
|  | : C->getParamNameAsWritten()); | 
|  | Result << "</Name>"; | 
|  |  | 
|  | if (C->isParamIndexValid()) { | 
|  | if (C->isVarArgParam()) | 
|  | Result << "<IsVarArg />"; | 
|  | else | 
|  | Result << "<Index>" << C->getParamIndex() << "</Index>"; | 
|  | } | 
|  |  | 
|  | Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">"; | 
|  | switch (C->getDirection()) { | 
|  | case ParamCommandComment::In: | 
|  | Result << "in"; | 
|  | break; | 
|  | case ParamCommandComment::Out: | 
|  | Result << "out"; | 
|  | break; | 
|  | case ParamCommandComment::InOut: | 
|  | Result << "in,out"; | 
|  | break; | 
|  | } | 
|  | Result << "</Direction><Discussion>"; | 
|  | visit(C->getParagraph()); | 
|  | Result << "</Discussion></Parameter>"; | 
|  | } | 
|  |  | 
|  | void CommentASTToXMLConverter::visitTParamCommandComment( | 
|  | const TParamCommandComment *C) { | 
|  | Result << "<Parameter><Name>"; | 
|  | appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC) | 
|  | : C->getParamNameAsWritten()); | 
|  | Result << "</Name>"; | 
|  |  | 
|  | if (C->isPositionValid() && C->getDepth() == 1) { | 
|  | Result << "<Index>" << C->getIndex(0) << "</Index>"; | 
|  | } | 
|  |  | 
|  | Result << "<Discussion>"; | 
|  | visit(C->getParagraph()); | 
|  | Result << "</Discussion></Parameter>"; | 
|  | } | 
|  |  | 
|  | void CommentASTToXMLConverter::visitVerbatimBlockComment( | 
|  | const VerbatimBlockComment *C) { | 
|  | unsigned NumLines = C->getNumLines(); | 
|  | if (NumLines == 0) | 
|  | return; | 
|  |  | 
|  | switch (C->getCommandID()) { | 
|  | case CommandTraits::KCI_code: | 
|  | Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">"; | 
|  | break; | 
|  | default: | 
|  | Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">"; | 
|  | break; | 
|  | } | 
|  | for (unsigned i = 0; i != NumLines; ++i) { | 
|  | appendToResultWithXMLEscaping(C->getText(i)); | 
|  | if (i + 1 != NumLines) | 
|  | Result << '\n'; | 
|  | } | 
|  | Result << "</Verbatim>"; | 
|  | } | 
|  |  | 
|  | void CommentASTToXMLConverter::visitVerbatimBlockLineComment( | 
|  | const VerbatimBlockLineComment *C) { | 
|  | llvm_unreachable("should not see this AST node"); | 
|  | } | 
|  |  | 
|  | void CommentASTToXMLConverter::visitVerbatimLineComment( | 
|  | const VerbatimLineComment *C) { | 
|  | Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">"; | 
|  | appendToResultWithXMLEscaping(C->getText()); | 
|  | Result << "</Verbatim>"; | 
|  | } | 
|  |  | 
|  | void CommentASTToXMLConverter::visitFullComment(const FullComment *C) { | 
|  | FullCommentParts Parts(C, Traits); | 
|  |  | 
|  | const DeclInfo *DI = C->getDeclInfo(); | 
|  | StringRef RootEndTag; | 
|  | if (DI) { | 
|  | switch (DI->getKind()) { | 
|  | case DeclInfo::OtherKind: | 
|  | RootEndTag = "</Other>"; | 
|  | Result << "<Other"; | 
|  | break; | 
|  | case DeclInfo::FunctionKind: | 
|  | RootEndTag = "</Function>"; | 
|  | Result << "<Function"; | 
|  | switch (DI->TemplateKind) { | 
|  | case DeclInfo::NotTemplate: | 
|  | break; | 
|  | case DeclInfo::Template: | 
|  | Result << " templateKind=\"template\""; | 
|  | break; | 
|  | case DeclInfo::TemplateSpecialization: | 
|  | Result << " templateKind=\"specialization\""; | 
|  | break; | 
|  | case DeclInfo::TemplatePartialSpecialization: | 
|  | llvm_unreachable("partial specializations of functions " | 
|  | "are not allowed in C++"); | 
|  | } | 
|  | if (DI->IsInstanceMethod) | 
|  | Result << " isInstanceMethod=\"1\""; | 
|  | if (DI->IsClassMethod) | 
|  | Result << " isClassMethod=\"1\""; | 
|  | break; | 
|  | case DeclInfo::ClassKind: | 
|  | RootEndTag = "</Class>"; | 
|  | Result << "<Class"; | 
|  | switch (DI->TemplateKind) { | 
|  | case DeclInfo::NotTemplate: | 
|  | break; | 
|  | case DeclInfo::Template: | 
|  | Result << " templateKind=\"template\""; | 
|  | break; | 
|  | case DeclInfo::TemplateSpecialization: | 
|  | Result << " templateKind=\"specialization\""; | 
|  | break; | 
|  | case DeclInfo::TemplatePartialSpecialization: | 
|  | Result << " templateKind=\"partialSpecialization\""; | 
|  | break; | 
|  | } | 
|  | break; | 
|  | case DeclInfo::VariableKind: | 
|  | RootEndTag = "</Variable>"; | 
|  | Result << "<Variable"; | 
|  | break; | 
|  | case DeclInfo::NamespaceKind: | 
|  | RootEndTag = "</Namespace>"; | 
|  | Result << "<Namespace"; | 
|  | break; | 
|  | case DeclInfo::TypedefKind: | 
|  | RootEndTag = "</Typedef>"; | 
|  | Result << "<Typedef"; | 
|  | break; | 
|  | case DeclInfo::EnumKind: | 
|  | RootEndTag = "</Enum>"; | 
|  | Result << "<Enum"; | 
|  | break; | 
|  | } | 
|  |  | 
|  | { | 
|  | // Print line and column number. | 
|  | SourceLocation Loc = DI->CurrentDecl->getLocation(); | 
|  | std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); | 
|  | FileID FID = LocInfo.first; | 
|  | unsigned FileOffset = LocInfo.second; | 
|  |  | 
|  | if (FID.isValid()) { | 
|  | if (const FileEntry *FE = SM.getFileEntryForID(FID)) { | 
|  | Result << " file=\""; | 
|  | appendToResultWithXMLEscaping(FE->getName()); | 
|  | Result << "\""; | 
|  | } | 
|  | Result << " line=\"" << SM.getLineNumber(FID, FileOffset) | 
|  | << "\" column=\"" << SM.getColumnNumber(FID, FileOffset) | 
|  | << "\""; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Finish the root tag. | 
|  | Result << ">"; | 
|  |  | 
|  | bool FoundName = false; | 
|  | if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) { | 
|  | if (DeclarationName DeclName = ND->getDeclName()) { | 
|  | Result << "<Name>"; | 
|  | std::string Name = DeclName.getAsString(); | 
|  | appendToResultWithXMLEscaping(Name); | 
|  | FoundName = true; | 
|  | Result << "</Name>"; | 
|  | } | 
|  | } | 
|  | if (!FoundName) | 
|  | Result << "<Name><anonymous></Name>"; | 
|  |  | 
|  | { | 
|  | // Print USR. | 
|  | SmallString<128> USR; | 
|  | generateUSRForDecl(DI->CommentDecl, USR); | 
|  | if (!USR.empty()) { | 
|  | Result << "<USR>"; | 
|  | appendToResultWithXMLEscaping(USR); | 
|  | Result << "</USR>"; | 
|  | } | 
|  | } | 
|  | } else { | 
|  | // No DeclInfo -- just emit some root tag and name tag. | 
|  | RootEndTag = "</Other>"; | 
|  | Result << "<Other><Name>unknown</Name>"; | 
|  | } | 
|  |  | 
|  | if (Parts.Headerfile) { | 
|  | Result << "<Headerfile>"; | 
|  | visit(Parts.Headerfile); | 
|  | Result << "</Headerfile>"; | 
|  | } | 
|  |  | 
|  | { | 
|  | // Pretty-print the declaration. | 
|  | Result << "<Declaration>"; | 
|  | SmallString<128> Declaration; | 
|  | getSourceTextOfDeclaration(DI, Declaration); | 
|  | formatTextOfDeclaration(DI, Declaration); | 
|  | appendToResultWithXMLEscaping(Declaration); | 
|  | Result << "</Declaration>"; | 
|  | } | 
|  |  | 
|  | bool FirstParagraphIsBrief = false; | 
|  | if (Parts.Brief) { | 
|  | Result << "<Abstract>"; | 
|  | visit(Parts.Brief); | 
|  | Result << "</Abstract>"; | 
|  | } else if (Parts.FirstParagraph) { | 
|  | Result << "<Abstract>"; | 
|  | visit(Parts.FirstParagraph); | 
|  | Result << "</Abstract>"; | 
|  | FirstParagraphIsBrief = true; | 
|  | } | 
|  |  | 
|  | if (Parts.TParams.size() != 0) { | 
|  | Result << "<TemplateParameters>"; | 
|  | for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i) | 
|  | visit(Parts.TParams[i]); | 
|  | Result << "</TemplateParameters>"; | 
|  | } | 
|  |  | 
|  | if (Parts.Params.size() != 0) { | 
|  | Result << "<Parameters>"; | 
|  | for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i) | 
|  | visit(Parts.Params[i]); | 
|  | Result << "</Parameters>"; | 
|  | } | 
|  |  | 
|  | if (Parts.Exceptions.size() != 0) { | 
|  | Result << "<Exceptions>"; | 
|  | for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i) | 
|  | visit(Parts.Exceptions[i]); | 
|  | Result << "</Exceptions>"; | 
|  | } | 
|  |  | 
|  | if (Parts.Returns.size() != 0) { | 
|  | Result << "<ResultDiscussion>"; | 
|  | for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i) | 
|  | visit(Parts.Returns[i]); | 
|  | Result << "</ResultDiscussion>"; | 
|  | } | 
|  |  | 
|  | if (DI->CommentDecl->hasAttrs()) { | 
|  | const AttrVec &Attrs = DI->CommentDecl->getAttrs(); | 
|  | for (unsigned i = 0, e = Attrs.size(); i != e; i++) { | 
|  | const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]); | 
|  | if (!AA) { | 
|  | if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) { | 
|  | if (DA->getMessage().empty()) | 
|  | Result << "<Deprecated/>"; | 
|  | else { | 
|  | Result << "<Deprecated>"; | 
|  | appendToResultWithXMLEscaping(DA->getMessage()); | 
|  | Result << "</Deprecated>"; | 
|  | } | 
|  | } | 
|  | else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) { | 
|  | if (UA->getMessage().empty()) | 
|  | Result << "<Unavailable/>"; | 
|  | else { | 
|  | Result << "<Unavailable>"; | 
|  | appendToResultWithXMLEscaping(UA->getMessage()); | 
|  | Result << "</Unavailable>"; | 
|  | } | 
|  | } | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // 'availability' attribute. | 
|  | Result << "<Availability"; | 
|  | StringRef Distribution; | 
|  | if (AA->getPlatform()) { | 
|  | Distribution = AvailabilityAttr::getPrettyPlatformName( | 
|  | AA->getPlatform()->getName()); | 
|  | if (Distribution.empty()) | 
|  | Distribution = AA->getPlatform()->getName(); | 
|  | } | 
|  | Result << " distribution=\"" << Distribution << "\">"; | 
|  | VersionTuple IntroducedInVersion = AA->getIntroduced(); | 
|  | if (!IntroducedInVersion.empty()) { | 
|  | Result << "<IntroducedInVersion>" | 
|  | << IntroducedInVersion.getAsString() | 
|  | << "</IntroducedInVersion>"; | 
|  | } | 
|  | VersionTuple DeprecatedInVersion = AA->getDeprecated(); | 
|  | if (!DeprecatedInVersion.empty()) { | 
|  | Result << "<DeprecatedInVersion>" | 
|  | << DeprecatedInVersion.getAsString() | 
|  | << "</DeprecatedInVersion>"; | 
|  | } | 
|  | VersionTuple RemovedAfterVersion = AA->getObsoleted(); | 
|  | if (!RemovedAfterVersion.empty()) { | 
|  | Result << "<RemovedAfterVersion>" | 
|  | << RemovedAfterVersion.getAsString() | 
|  | << "</RemovedAfterVersion>"; | 
|  | } | 
|  | StringRef DeprecationSummary = AA->getMessage(); | 
|  | if (!DeprecationSummary.empty()) { | 
|  | Result << "<DeprecationSummary>"; | 
|  | appendToResultWithXMLEscaping(DeprecationSummary); | 
|  | Result << "</DeprecationSummary>"; | 
|  | } | 
|  | if (AA->getUnavailable()) | 
|  | Result << "<Unavailable/>"; | 
|  | Result << "</Availability>"; | 
|  | } | 
|  | } | 
|  |  | 
|  | { | 
|  | bool StartTagEmitted = false; | 
|  | for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) { | 
|  | const Comment *C = Parts.MiscBlocks[i]; | 
|  | if (FirstParagraphIsBrief && C == Parts.FirstParagraph) | 
|  | continue; | 
|  | if (!StartTagEmitted) { | 
|  | Result << "<Discussion>"; | 
|  | StartTagEmitted = true; | 
|  | } | 
|  | visit(C); | 
|  | } | 
|  | if (StartTagEmitted) | 
|  | Result << "</Discussion>"; | 
|  | } | 
|  |  | 
|  | Result << RootEndTag; | 
|  | } | 
|  |  | 
|  | void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) { | 
|  | for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) { | 
|  | const char C = *I; | 
|  | switch (C) { | 
|  | case '&': | 
|  | Result << "&"; | 
|  | break; | 
|  | case '<': | 
|  | Result << "<"; | 
|  | break; | 
|  | case '>': | 
|  | Result << ">"; | 
|  | break; | 
|  | case '"': | 
|  | Result << """; | 
|  | break; | 
|  | case '\'': | 
|  | Result << "'"; | 
|  | break; | 
|  | default: | 
|  | Result << C; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) { | 
|  | if (S.empty()) | 
|  | return; | 
|  |  | 
|  | Result << "<![CDATA["; | 
|  | while (!S.empty()) { | 
|  | size_t Pos = S.find("]]>"); | 
|  | if (Pos == 0) { | 
|  | Result << "]]]]><![CDATA[>"; | 
|  | S = S.drop_front(3); | 
|  | continue; | 
|  | } | 
|  | if (Pos == StringRef::npos) | 
|  | Pos = S.size(); | 
|  |  | 
|  | Result << S.substr(0, Pos); | 
|  |  | 
|  | S = S.drop_front(Pos); | 
|  | } | 
|  | Result << "]]>"; | 
|  | } | 
|  |  | 
|  | CommentToXMLConverter::CommentToXMLConverter() {} | 
|  | CommentToXMLConverter::~CommentToXMLConverter() {} | 
|  |  | 
|  | void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC, | 
|  | SmallVectorImpl<char> &HTML, | 
|  | const ASTContext &Context) { | 
|  | CommentASTToHTMLConverter Converter(FC, HTML, | 
|  | Context.getCommentCommandTraits()); | 
|  | Converter.visit(FC); | 
|  | } | 
|  |  | 
|  | void CommentToXMLConverter::convertHTMLTagNodeToText( | 
|  | const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text, | 
|  | const ASTContext &Context) { | 
|  | CommentASTToHTMLConverter Converter(nullptr, Text, | 
|  | Context.getCommentCommandTraits()); | 
|  | Converter.visit(HTC); | 
|  | } | 
|  |  | 
|  | void CommentToXMLConverter::convertCommentToXML(const FullComment *FC, | 
|  | SmallVectorImpl<char> &XML, | 
|  | const ASTContext &Context) { | 
|  | CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(), | 
|  | Context.getSourceManager()); | 
|  | Converter.visit(FC); | 
|  | } |