diff options
-rw-r--r-- | clang/include/clang/Index/CommentToXML.h | 50 | ||||
-rw-r--r-- | clang/lib/Index/CMakeLists.txt | 2 | ||||
-rw-r--r-- | clang/lib/Index/CommentToXML.cpp | 1136 | ||||
-rw-r--r-- | clang/lib/Index/SimpleFormatContext.h (renamed from clang/tools/libclang/SimpleFormatContext.h) | 8 | ||||
-rw-r--r-- | clang/tools/libclang/CIndex.cpp | 7 | ||||
-rw-r--r-- | clang/tools/libclang/CMakeLists.txt | 1 | ||||
-rw-r--r-- | clang/tools/libclang/CXComment.cpp | 1131 | ||||
-rw-r--r-- | clang/tools/libclang/CXTranslationUnit.h | 7 |
8 files changed, 1221 insertions, 1121 deletions
diff --git a/clang/include/clang/Index/CommentToXML.h b/clang/include/clang/Index/CommentToXML.h new file mode 100644 index 00000000000..8444b145343 --- /dev/null +++ b/clang/include/clang/Index/CommentToXML.h @@ -0,0 +1,50 @@ +//===--- CommentToXML.h - 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. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_COMMENTTOXML_H +#define LLVM_CLANG_INDEX_COMMENTTOXML_H + +#include "clang/Basic/LLVM.h" + +namespace clang { +class ASTContext; + +namespace comments { +class FullComment; +class HTMLTagComment; +} + +namespace index { +class SimpleFormatContext; + +class CommentToXMLConverter { + SimpleFormatContext *FormatContext; + unsigned FormatInMemoryUniqueId; + +public: + CommentToXMLConverter() : FormatContext(0), FormatInMemoryUniqueId(0) {} + + void convertCommentToHTML(const comments::FullComment *FC, + SmallVectorImpl<char> &HTML, + const ASTContext &Context); + + void convertHTMLTagNodeToText(const comments::HTMLTagComment *HTC, + SmallVectorImpl<char> &Text, + const ASTContext &Context); + + void convertCommentToXML(const comments::FullComment *FC, + SmallVectorImpl<char> &XML, + const ASTContext &Context); +}; + +} // namespace index +} // namespace clang + +#endif // LLVM_CLANG_INDEX_COMMENTTOXML_H + diff --git a/clang/lib/Index/CMakeLists.txt b/clang/lib/Index/CMakeLists.txt index 97d43031e25..7194b9b2236 100644 --- a/clang/lib/Index/CMakeLists.txt +++ b/clang/lib/Index/CMakeLists.txt @@ -1,4 +1,6 @@ add_clang_library(clangIndex + CommentToXML.cpp + SimpleFormatContext.h USRGeneration.cpp ) diff --git a/clang/lib/Index/CommentToXML.cpp b/clang/lib/Index/CommentToXML.cpp new file mode 100644 index 00000000000..0a9619dfe08 --- /dev/null +++ b/clang/lib/Index/CommentToXML.cpp @@ -0,0 +1,1136 @@ +//===--- 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 "SimpleFormatContext.h" +#include "clang/AST/Attr.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Comment.h" +#include "clang/AST/CommentVisitor.h" +#include "clang/Format/Format.h" +#include "clang/Index/USRGeneration.h" +#include "clang/Lex/Lexer.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(NULL), Headerfile(NULL), FirstParagraph(NULL) { + 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>"; + } + + Result.flush(); +} + +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, + SimpleFormatContext &SFC, + unsigned FUID) : + FC(FC), Result(Str), Traits(Traits), SM(SM), + FormatRewriterContext(SFC), + FormatInMemoryUniqueId(FUID) { } + + // 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 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; + SimpleFormatContext &FormatRewriterContext; + unsigned FormatInMemoryUniqueId; +}; + +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; + ThisDecl->CurrentDecl->print(OS, PPolicy, + /*Indentation*/0, /*PrintInstantiation*/false); +} + +void CommentASTToXMLConverter::formatTextOfDeclaration( + const DeclInfo *DI, SmallString<128> &Declaration) { + // FIXME. formatting API expects null terminated input string. + // There might be more efficient way of doing this. + std::string StringDecl = Declaration.str(); + + // Formatter specific code. + // Form a unique in memory buffer name. + SmallString<128> filename; + filename += "xmldecl"; + filename += llvm::utostr(FormatInMemoryUniqueId); + filename += ".xd"; + FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl); + SourceLocation Start = FormatRewriterContext.Sources.getLocForStartOfFile(ID) + .getLocWithOffset(0); + unsigned Length = Declaration.size(); + + std::vector<CharSourceRange> Ranges( + 1, CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length))); + ASTContext &Context = DI->CurrentDecl->getASTContext(); + const LangOptions &LangOpts = Context.getLangOpts(); + Lexer Lex(ID, FormatRewriterContext.Sources.getBuffer(ID), + FormatRewriterContext.Sources, LangOpts); + tooling::Replacements Replace = reformat( + format::getLLVMStyle(), Lex, FormatRewriterContext.Sources, Ranges); + applyAllReplacements(Replace, FormatRewriterContext.Rewrite); + Declaration = FormatRewriterContext.getRewrittenText(ID); +} + +} // 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><![CDATA["; + printHTMLStartTagComment(C, Result); + Result << "]]></rawHTML>"; +} + +void +CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) { + Result << "<rawHTML></" << 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.isInvalid()) { + 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; + + Result.flush(); +} + +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 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(0, Text, + Context.getCommentCommandTraits()); + Converter.visit(HTC); +} + +void CommentToXMLConverter::convertCommentToXML(const FullComment *FC, + SmallVectorImpl<char> &XML, + const ASTContext &Context) { + if (!FormatContext) { + FormatContext = new SimpleFormatContext(Context.getLangOpts()); + } else if ((FormatInMemoryUniqueId % 1000) == 0) { + // Delete after some number of iterations, so the buffers don't grow + // too large. + delete FormatContext; + FormatContext = new SimpleFormatContext(Context.getLangOpts()); + } + + CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(), + Context.getSourceManager(), *FormatContext, + FormatInMemoryUniqueId++); + Converter.visit(FC); +} + diff --git a/clang/tools/libclang/SimpleFormatContext.h b/clang/lib/Index/SimpleFormatContext.h index 016d0b67d44..fde43aed211 100644 --- a/clang/tools/libclang/SimpleFormatContext.h +++ b/clang/lib/Index/SimpleFormatContext.h @@ -27,6 +27,7 @@ #include "llvm/Support/raw_ostream.h" namespace clang { +namespace index { /// \brief A small class to be used by libclang clients to format /// a declaration string in memory. This object is instantiated once @@ -35,7 +36,7 @@ class SimpleFormatContext { public: SimpleFormatContext(LangOptions Options) : DiagOpts(new DiagnosticOptions()), - Diagnostics(new DiagnosticsEngine(new DiagnosticIDs, + Diagnostics(new DiagnosticsEngine(new DiagnosticIDs, DiagOpts.getPtr())), Files((FileSystemOptions())), Sources(*Diagnostics, Files), @@ -47,9 +48,9 @@ public: FileID createInMemoryFile(StringRef Name, StringRef Content) { const llvm::MemoryBuffer *Source = - llvm::MemoryBuffer::getMemBuffer(Content); + llvm::MemoryBuffer::getMemBuffer(Content); const FileEntry *Entry = - Files.getVirtualFile(Name, Source->getBufferSize(), 0); + Files.getVirtualFile(Name, Source->getBufferSize(), 0); Sources.overrideFileContents(Entry, Source, true); assert(Entry != NULL); return Sources.createFileID(Entry, SourceLocation(), SrcMgr::C_User); @@ -70,6 +71,7 @@ public: Rewriter Rewrite; }; +} // end namespace index } // end namespace clang #endif diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 3e741281beb..f53e5c1c495 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -22,7 +22,6 @@ #include "CXTranslationUnit.h" #include "CXType.h" #include "CursorVisitor.h" -#include "SimpleFormatContext.h" #include "clang/AST/Attr.h" #include "clang/AST/StmtVisitor.h" #include "clang/Basic/Diagnostic.h" @@ -30,6 +29,7 @@ #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Index/CommentToXML.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Lexer.h" #include "clang/Lex/PreprocessingRecord.h" @@ -68,8 +68,7 @@ CXTranslationUnit cxtu::MakeCXTranslationUnit(CIndexer *CIdx, ASTUnit *AU) { D->StringPool = new cxstring::CXStringPool(); D->Diagnostics = 0; D->OverridenCursorsPool = createOverridenCXCursorsPool(); - D->FormatContext = 0; - D->FormatInMemoryUniqueId = 0; + D->CommentToXML = 0; return D; } @@ -2904,7 +2903,7 @@ void clang_disposeTranslationUnit(CXTranslationUnit CTUnit) { delete CTUnit->StringPool; delete static_cast<CXDiagnosticSetImpl *>(CTUnit->Diagnostics); disposeOverridenCXCursorsPool(CTUnit->OverridenCursorsPool); - delete CTUnit->FormatContext; + delete CTUnit->CommentToXML; delete CTUnit; } } diff --git a/clang/tools/libclang/CMakeLists.txt b/clang/tools/libclang/CMakeLists.txt index a593dfccd00..f84780e47db 100644 --- a/clang/tools/libclang/CMakeLists.txt +++ b/clang/tools/libclang/CMakeLists.txt @@ -39,7 +39,6 @@ set(SOURCES Indexing.cpp IndexingContext.cpp IndexingContext.h - SimpleFormatContext.h ../../include/clang-c/Index.h ) diff --git a/clang/tools/libclang/CXComment.cpp b/clang/tools/libclang/CXComment.cpp index 1df6e240d9e..21057cb66ea 100644 --- a/clang/tools/libclang/CXComment.cpp +++ b/clang/tools/libclang/CXComment.cpp @@ -15,19 +15,11 @@ #include "CXComment.h" #include "CXCursor.h" #include "CXString.h" -#include "SimpleFormatContext.h" -#include "clang/AST/Attr.h" -#include "clang/AST/CommentCommandTraits.h" -#include "clang/AST/CommentVisitor.h" #include "clang/AST/Decl.h" -#include "clang/AST/PrettyPrinter.h" -#include "clang/Format/Format.h" -#include "clang/Lex/Lexer.h" +#include "clang/Index/CommentToXML.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" -#include "llvm/ADT/TinyPtrVector.h" #include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/raw_ostream.h" #include <climits> using namespace clang; @@ -352,524 +344,23 @@ CXString clang_VerbatimLineComment_getText(CXComment CXC) { return cxstring::createRef(VLC->getText()); } -} // end extern "C" - //===----------------------------------------------------------------------===// -// Helpers for converting comment AST to HTML. +// Converting comments to XML. //===----------------------------------------------------------------------===// -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(NULL), Headerfile(NULL), FirstParagraph(NULL) { - 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>"; - } - - Result.flush(); -} - -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; - } - } -} - -extern "C" { - CXString clang_HTMLTagComment_getAsString(CXComment CXC) { const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC); if (!HTC) return cxstring::createNull(); - SmallString<128> HTML; - CommentASTToHTMLConverter Converter(0, HTML, getCommandTraits(CXC)); - Converter.visit(HTC); - return cxstring::createDup(HTML.str()); + CXTranslationUnit TU = CXC.TranslationUnit; + if (!TU->CommentToXML) + TU->CommentToXML = new index::CommentToXMLConverter(); + + SmallString<128> Text; + TU->CommentToXML->convertHTMLTagNodeToText( + HTC, Text, cxtu::getASTUnit(TU)->getASTContext()); + return cxstring::createDup(Text.str()); } CXString clang_FullComment_getAsHTML(CXComment CXC) { @@ -877,608 +368,28 @@ CXString clang_FullComment_getAsHTML(CXComment CXC) { if (!FC) return cxstring::createNull(); + CXTranslationUnit TU = CXC.TranslationUnit; + if (!TU->CommentToXML) + TU->CommentToXML = new index::CommentToXMLConverter(); + SmallString<1024> HTML; - CommentASTToHTMLConverter Converter(FC, HTML, getCommandTraits(CXC)); - Converter.visit(FC); + TU->CommentToXML + ->convertCommentToHTML(FC, HTML, cxtu::getASTUnit(TU)->getASTContext()); return cxstring::createDup(HTML.str()); } -} // end extern "C" - -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, - SimpleFormatContext &SFC, - unsigned FUID) : - FC(FC), Result(Str), Traits(Traits), SM(SM), - FormatRewriterContext(SFC), - FormatInMemoryUniqueId(FUID) { } - - // 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 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; - SimpleFormatContext &FormatRewriterContext; - unsigned FormatInMemoryUniqueId; -}; - -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; - ThisDecl->CurrentDecl->print(OS, PPolicy, - /*Indentation*/0, /*PrintInstantiation*/false); -} - -void CommentASTToXMLConverter::formatTextOfDeclaration( - const DeclInfo *DI, - SmallString<128> &Declaration) { - // FIXME. formatting API expects null terminated input string. - // There might be more efficient way of doing this. - std::string StringDecl = Declaration.str(); - - // Formatter specific code. - // Form a unique in memory buffer name. - SmallString<128> filename; - filename += "xmldecl"; - filename += llvm::utostr(FormatInMemoryUniqueId); - filename += ".xd"; - FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl); - SourceLocation Start = - FormatRewriterContext.Sources.getLocForStartOfFile(ID).getLocWithOffset(0); - unsigned Length = Declaration.size(); - - std::vector<CharSourceRange> - Ranges(1, CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length))); - ASTContext &Context = DI->CurrentDecl->getASTContext(); - const LangOptions &LangOpts = Context.getLangOpts(); - Lexer Lex(ID, FormatRewriterContext.Sources.getBuffer(ID), - FormatRewriterContext.Sources, LangOpts); - tooling::Replacements Replace = - reformat(format::getLLVMStyle(), Lex, FormatRewriterContext.Sources, Ranges); - applyAllReplacements(Replace, FormatRewriterContext.Rewrite); - Declaration = FormatRewriterContext.getRewrittenText(ID); -} - -} // 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><![CDATA["; - PrintHTMLStartTagComment(C, Result); - Result << "]]></rawHTML>"; -} - -void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) { - Result << "<rawHTML></" << 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.isInvalid()) { - 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; - cxcursor::getDeclCursorUSR(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; - - Result.flush(); -} - -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; - } - } -} - -extern "C" { - CXString clang_FullComment_getAsXML(CXComment CXC) { const FullComment *FC = getASTNodeAs<FullComment>(CXC); if (!FC) return cxstring::createNull(); - ASTContext &Context = FC->getDeclInfo()->CurrentDecl->getASTContext(); + CXTranslationUnit TU = CXC.TranslationUnit; - SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager(); - - if (!TU->FormatContext) { - TU->FormatContext = new SimpleFormatContext(Context.getLangOpts()); - } else if ((TU->FormatInMemoryUniqueId % 1000) == 0) { - // Delete after some number of iterators, so the buffers don't grow - // too large. - delete TU->FormatContext; - TU->FormatContext = new SimpleFormatContext(Context.getLangOpts()); - } + if (!TU->CommentToXML) + TU->CommentToXML = new index::CommentToXMLConverter(); SmallString<1024> XML; - CommentASTToXMLConverter Converter(FC, XML, getCommandTraits(CXC), SM, - *TU->FormatContext, - TU->FormatInMemoryUniqueId++); - Converter.visit(FC); + TU->CommentToXML + ->convertCommentToXML(FC, XML, cxtu::getASTUnit(TU)->getASTContext()); return cxstring::createDup(XML.str()); } diff --git a/clang/tools/libclang/CXTranslationUnit.h b/clang/tools/libclang/CXTranslationUnit.h index bdc171cff66..c0014c0c8f1 100644 --- a/clang/tools/libclang/CXTranslationUnit.h +++ b/clang/tools/libclang/CXTranslationUnit.h @@ -20,7 +20,9 @@ namespace clang { class ASTUnit; class CIndexer; - class SimpleFormatContext; +namespace index { +class CommentToXMLConverter; +} // namespace index } // namespace clang struct CXTranslationUnitImpl { @@ -29,8 +31,7 @@ struct CXTranslationUnitImpl { clang::cxstring::CXStringPool *StringPool; void *Diagnostics; void *OverridenCursorsPool; - clang::SimpleFormatContext *FormatContext; - unsigned FormatInMemoryUniqueId; + clang::index::CommentToXMLConverter *CommentToXML; }; namespace clang { |