diff options
Diffstat (limited to 'lib/Index')
-rw-r--r-- | lib/Index/CMakeLists.txt | 11 | ||||
-rw-r--r-- | lib/Index/CommentToXML.cpp | 1136 | ||||
-rw-r--r-- | lib/Index/Makefile | 13 | ||||
-rw-r--r-- | lib/Index/SimpleFormatContext.h | 77 | ||||
-rw-r--r-- | lib/Index/USRGeneration.cpp | 803 |
5 files changed, 2040 insertions, 0 deletions
diff --git a/lib/Index/CMakeLists.txt b/lib/Index/CMakeLists.txt new file mode 100644 index 0000000..c4ff5a0 --- /dev/null +++ b/lib/Index/CMakeLists.txt @@ -0,0 +1,11 @@ +add_clang_library(clangIndex + CommentToXML.cpp + SimpleFormatContext.h + USRGeneration.cpp + ) + +target_link_libraries(clangIndex + clangBasic + clangAST + clangFormat + ) diff --git a/lib/Index/CommentToXML.cpp b/lib/Index/CommentToXML.cpp new file mode 100644 index 0000000..0a9619d --- /dev/null +++ b/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/lib/Index/Makefile b/lib/Index/Makefile new file mode 100644 index 0000000..c53fccd --- /dev/null +++ b/lib/Index/Makefile @@ -0,0 +1,13 @@ +##===- clang/lib/Index/Makefile ----------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL := ../.. +LIBRARYNAME := clangIndex + +include $(CLANG_LEVEL)/Makefile diff --git a/lib/Index/SimpleFormatContext.h b/lib/Index/SimpleFormatContext.h new file mode 100644 index 0000000..fde43ae --- /dev/null +++ b/lib/Index/SimpleFormatContext.h @@ -0,0 +1,77 @@ +//===--- SimpleFormatContext.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +/// \file +/// +/// \brief Defines a utility class for use of clang-format in libclang +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SIMPLE_FORM_CONTEXT_H +#define LLVM_CLANG_SIMPLE_FORM_CONTEXT_H + +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#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 +/// and used each time a formatting is needed. +class SimpleFormatContext { +public: + SimpleFormatContext(LangOptions Options) + : DiagOpts(new DiagnosticOptions()), + Diagnostics(new DiagnosticsEngine(new DiagnosticIDs, + DiagOpts.getPtr())), + Files((FileSystemOptions())), + Sources(*Diagnostics, Files), + Rewrite(Sources, Options) { + Diagnostics->setClient(new IgnoringDiagConsumer, true); + } + + ~SimpleFormatContext() { } + + FileID createInMemoryFile(StringRef Name, StringRef Content) { + const llvm::MemoryBuffer *Source = + llvm::MemoryBuffer::getMemBuffer(Content); + const FileEntry *Entry = + Files.getVirtualFile(Name, Source->getBufferSize(), 0); + Sources.overrideFileContents(Entry, Source, true); + assert(Entry != NULL); + return Sources.createFileID(Entry, SourceLocation(), SrcMgr::C_User); + } + + std::string getRewrittenText(FileID ID) { + std::string Result; + llvm::raw_string_ostream OS(Result); + Rewrite.getEditBuffer(ID).write(OS); + OS.flush(); + return Result; + } + + IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts; + IntrusiveRefCntPtr<DiagnosticsEngine> Diagnostics; + FileManager Files; + SourceManager Sources; + Rewriter Rewrite; +}; + +} // end namespace index +} // end namespace clang + +#endif diff --git a/lib/Index/USRGeneration.cpp b/lib/Index/USRGeneration.cpp new file mode 100644 index 0000000..16d89f8 --- /dev/null +++ b/lib/Index/USRGeneration.cpp @@ -0,0 +1,803 @@ +//===- USRGeneration.cpp - Routines for USR generation --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/USRGeneration.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/DeclVisitor.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::index; + +//===----------------------------------------------------------------------===// +// USR generation. +//===----------------------------------------------------------------------===// + +namespace { +class USRGenerator : public ConstDeclVisitor<USRGenerator> { + SmallVectorImpl<char> &Buf; + llvm::raw_svector_ostream Out; + bool IgnoreResults; + ASTContext *Context; + bool generatedLoc; + + llvm::DenseMap<const Type *, unsigned> TypeSubstitutions; + +public: + explicit USRGenerator(ASTContext *Ctx, SmallVectorImpl<char> &Buf) + : Buf(Buf), + Out(Buf), + IgnoreResults(false), + Context(Ctx), + generatedLoc(false) + { + // Add the USR space prefix. + Out << getUSRSpacePrefix(); + } + + bool ignoreResults() const { return IgnoreResults; } + + // Visitation methods from generating USRs from AST elements. + void VisitDeclContext(const DeclContext *D); + void VisitFieldDecl(const FieldDecl *D); + void VisitFunctionDecl(const FunctionDecl *D); + void VisitNamedDecl(const NamedDecl *D); + void VisitNamespaceDecl(const NamespaceDecl *D); + void VisitNamespaceAliasDecl(const NamespaceAliasDecl *D); + void VisitFunctionTemplateDecl(const FunctionTemplateDecl *D); + void VisitClassTemplateDecl(const ClassTemplateDecl *D); + void VisitObjCContainerDecl(const ObjCContainerDecl *CD); + void VisitObjCMethodDecl(const ObjCMethodDecl *MD); + void VisitObjCPropertyDecl(const ObjCPropertyDecl *D); + void VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *D); + void VisitTagDecl(const TagDecl *D); + void VisitTypedefDecl(const TypedefDecl *D); + void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D); + void VisitVarDecl(const VarDecl *D); + void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *D); + void VisitTemplateTemplateParmDecl(const TemplateTemplateParmDecl *D); + void VisitLinkageSpecDecl(const LinkageSpecDecl *D) { + IgnoreResults = true; + } + void VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) { + IgnoreResults = true; + } + void VisitUsingDecl(const UsingDecl *D) { + IgnoreResults = true; + } + void VisitUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl *D) { + IgnoreResults = true; + } + void VisitUnresolvedUsingTypenameDecl(const UnresolvedUsingTypenameDecl *D) { + IgnoreResults = true; + } + + /// Generate the string component containing the location of the + /// declaration. + bool GenLoc(const Decl *D); + + /// String generation methods used both by the visitation methods + /// and from other clients that want to directly generate USRs. These + /// methods do not construct complete USRs (which incorporate the parents + /// of an AST element), but only the fragments concerning the AST element + /// itself. + + /// Generate a USR for an Objective-C class. + void GenObjCClass(StringRef cls) { + generateUSRForObjCClass(cls, Out); + } + /// Generate a USR for an Objective-C class category. + void GenObjCCategory(StringRef cls, StringRef cat) { + generateUSRForObjCCategory(cls, cat, Out); + } + /// Generate a USR fragment for an Objective-C instance variable. The + /// complete USR can be created by concatenating the USR for the + /// encompassing class with this USR fragment. + void GenObjCIvar(StringRef ivar) { + generateUSRForObjCIvar(ivar, Out); + } + /// Generate a USR fragment for an Objective-C method. + void GenObjCMethod(StringRef sel, bool isInstanceMethod) { + generateUSRForObjCMethod(sel, isInstanceMethod, Out); + } + /// Generate a USR fragment for an Objective-C property. + void GenObjCProperty(StringRef prop) { + generateUSRForObjCProperty(prop, Out); + } + /// Generate a USR for an Objective-C protocol. + void GenObjCProtocol(StringRef prot) { + generateUSRForObjCProtocol(prot, Out); + } + + void VisitType(QualType T); + void VisitTemplateParameterList(const TemplateParameterList *Params); + void VisitTemplateName(TemplateName Name); + void VisitTemplateArgument(const TemplateArgument &Arg); + + /// Emit a Decl's name using NamedDecl::printName() and return true if + /// the decl had no name. + bool EmitDeclName(const NamedDecl *D); +}; + +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Generating USRs from ASTS. +//===----------------------------------------------------------------------===// + +bool USRGenerator::EmitDeclName(const NamedDecl *D) { + Out.flush(); + const unsigned startSize = Buf.size(); + D->printName(Out); + Out.flush(); + const unsigned endSize = Buf.size(); + return startSize == endSize; +} + +static inline bool ShouldGenerateLocation(const NamedDecl *D) { + return !D->isExternallyVisible(); +} + +void USRGenerator::VisitDeclContext(const DeclContext *DC) { + if (const NamedDecl *D = dyn_cast<NamedDecl>(DC)) + Visit(D); +} + +void USRGenerator::VisitFieldDecl(const FieldDecl *D) { + // The USR for an ivar declared in a class extension is based on the + // ObjCInterfaceDecl, not the ObjCCategoryDecl. + if (const ObjCInterfaceDecl *ID = Context->getObjContainingInterface(D)) + Visit(ID); + else + VisitDeclContext(D->getDeclContext()); + Out << (isa<ObjCIvarDecl>(D) ? "@" : "@FI@"); + if (EmitDeclName(D)) { + // Bit fields can be anonymous. + IgnoreResults = true; + return; + } +} + +void USRGenerator::VisitFunctionDecl(const FunctionDecl *D) { + if (ShouldGenerateLocation(D) && GenLoc(D)) + return; + + VisitDeclContext(D->getDeclContext()); + if (FunctionTemplateDecl *FunTmpl = D->getDescribedFunctionTemplate()) { + Out << "@FT@"; + VisitTemplateParameterList(FunTmpl->getTemplateParameters()); + } else + Out << "@F@"; + D->printName(Out); + + ASTContext &Ctx = *Context; + if (!Ctx.getLangOpts().CPlusPlus || D->isExternC()) + return; + + if (const TemplateArgumentList * + SpecArgs = D->getTemplateSpecializationArgs()) { + Out << '<'; + for (unsigned I = 0, N = SpecArgs->size(); I != N; ++I) { + Out << '#'; + VisitTemplateArgument(SpecArgs->get(I)); + } + Out << '>'; + } + + // Mangle in type information for the arguments. + for (FunctionDecl::param_const_iterator I = D->param_begin(), + E = D->param_end(); + I != E; ++I) { + Out << '#'; + if (ParmVarDecl *PD = *I) + VisitType(PD->getType()); + } + if (D->isVariadic()) + Out << '.'; + Out << '#'; + if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) { + if (MD->isStatic()) + Out << 'S'; + if (unsigned quals = MD->getTypeQualifiers()) + Out << (char)('0' + quals); + } +} + +void USRGenerator::VisitNamedDecl(const NamedDecl *D) { + VisitDeclContext(D->getDeclContext()); + Out << "@"; + + if (EmitDeclName(D)) { + // The string can be empty if the declaration has no name; e.g., it is + // the ParmDecl with no name for declaration of a function pointer type, + // e.g.: void (*f)(void *); + // In this case, don't generate a USR. + IgnoreResults = true; + } +} + +void USRGenerator::VisitVarDecl(const VarDecl *D) { + // VarDecls can be declared 'extern' within a function or method body, + // but their enclosing DeclContext is the function, not the TU. We need + // to check the storage class to correctly generate the USR. + if (ShouldGenerateLocation(D) && GenLoc(D)) + return; + + VisitDeclContext(D->getDeclContext()); + + // Variables always have simple names. + StringRef s = D->getName(); + + // The string can be empty if the declaration has no name; e.g., it is + // the ParmDecl with no name for declaration of a function pointer type, e.g.: + // void (*f)(void *); + // In this case, don't generate a USR. + if (s.empty()) + IgnoreResults = true; + else + Out << '@' << s; +} + +void USRGenerator::VisitNonTypeTemplateParmDecl( + const NonTypeTemplateParmDecl *D) { + GenLoc(D); + return; +} + +void USRGenerator::VisitTemplateTemplateParmDecl( + const TemplateTemplateParmDecl *D) { + GenLoc(D); + return; +} + +void USRGenerator::VisitNamespaceDecl(const NamespaceDecl *D) { + if (D->isAnonymousNamespace()) { + Out << "@aN"; + return; + } + + VisitDeclContext(D->getDeclContext()); + if (!IgnoreResults) + Out << "@N@" << D->getName(); +} + +void USRGenerator::VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) { + VisitFunctionDecl(D->getTemplatedDecl()); +} + +void USRGenerator::VisitClassTemplateDecl(const ClassTemplateDecl *D) { + VisitTagDecl(D->getTemplatedDecl()); +} + +void USRGenerator::VisitNamespaceAliasDecl(const NamespaceAliasDecl *D) { + VisitDeclContext(D->getDeclContext()); + if (!IgnoreResults) + Out << "@NA@" << D->getName(); +} + +void USRGenerator::VisitObjCMethodDecl(const ObjCMethodDecl *D) { + const DeclContext *container = D->getDeclContext(); + if (const ObjCProtocolDecl *pd = dyn_cast<ObjCProtocolDecl>(container)) { + Visit(pd); + } + else { + // The USR for a method declared in a class extension or category is based on + // the ObjCInterfaceDecl, not the ObjCCategoryDecl. + const ObjCInterfaceDecl *ID = D->getClassInterface(); + if (!ID) { + IgnoreResults = true; + return; + } + Visit(ID); + } + // Ideally we would use 'GenObjCMethod', but this is such a hot path + // for Objective-C code that we don't want to use + // DeclarationName::getAsString(). + Out << (D->isInstanceMethod() ? "(im)" : "(cm)") + << DeclarationName(D->getSelector()); +} + +void USRGenerator::VisitObjCContainerDecl(const ObjCContainerDecl *D) { + switch (D->getKind()) { + default: + llvm_unreachable("Invalid ObjC container."); + case Decl::ObjCInterface: + case Decl::ObjCImplementation: + GenObjCClass(D->getName()); + break; + case Decl::ObjCCategory: { + const ObjCCategoryDecl *CD = cast<ObjCCategoryDecl>(D); + const ObjCInterfaceDecl *ID = CD->getClassInterface(); + if (!ID) { + // Handle invalid code where the @interface might not + // have been specified. + // FIXME: We should be able to generate this USR even if the + // @interface isn't available. + IgnoreResults = true; + return; + } + // Specially handle class extensions, which are anonymous categories. + // We want to mangle in the location to uniquely distinguish them. + if (CD->IsClassExtension()) { + Out << "objc(ext)" << ID->getName() << '@'; + GenLoc(CD); + } + else + GenObjCCategory(ID->getName(), CD->getName()); + + break; + } + case Decl::ObjCCategoryImpl: { + const ObjCCategoryImplDecl *CD = cast<ObjCCategoryImplDecl>(D); + const ObjCInterfaceDecl *ID = CD->getClassInterface(); + if (!ID) { + // Handle invalid code where the @interface might not + // have been specified. + // FIXME: We should be able to generate this USR even if the + // @interface isn't available. + IgnoreResults = true; + return; + } + GenObjCCategory(ID->getName(), CD->getName()); + break; + } + case Decl::ObjCProtocol: + GenObjCProtocol(cast<ObjCProtocolDecl>(D)->getName()); + break; + } +} + +void USRGenerator::VisitObjCPropertyDecl(const ObjCPropertyDecl *D) { + // The USR for a property declared in a class extension or category is based + // on the ObjCInterfaceDecl, not the ObjCCategoryDecl. + if (const ObjCInterfaceDecl *ID = Context->getObjContainingInterface(D)) + Visit(ID); + else + Visit(cast<Decl>(D->getDeclContext())); + GenObjCProperty(D->getName()); +} + +void USRGenerator::VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *D) { + if (ObjCPropertyDecl *PD = D->getPropertyDecl()) { + VisitObjCPropertyDecl(PD); + return; + } + + IgnoreResults = true; +} + +void USRGenerator::VisitTagDecl(const TagDecl *D) { + // Add the location of the tag decl to handle resolution across + // translation units. + if (ShouldGenerateLocation(D) && GenLoc(D)) + return; + + D = D->getCanonicalDecl(); + VisitDeclContext(D->getDeclContext()); + + bool AlreadyStarted = false; + if (const CXXRecordDecl *CXXRecord = dyn_cast<CXXRecordDecl>(D)) { + if (ClassTemplateDecl *ClassTmpl = CXXRecord->getDescribedClassTemplate()) { + AlreadyStarted = true; + + switch (D->getTagKind()) { + case TTK_Interface: + case TTK_Struct: Out << "@ST"; break; + case TTK_Class: Out << "@CT"; break; + case TTK_Union: Out << "@UT"; break; + case TTK_Enum: llvm_unreachable("enum template"); + } + VisitTemplateParameterList(ClassTmpl->getTemplateParameters()); + } else if (const ClassTemplatePartialSpecializationDecl *PartialSpec + = dyn_cast<ClassTemplatePartialSpecializationDecl>(CXXRecord)) { + AlreadyStarted = true; + + switch (D->getTagKind()) { + case TTK_Interface: + case TTK_Struct: Out << "@SP"; break; + case TTK_Class: Out << "@CP"; break; + case TTK_Union: Out << "@UP"; break; + case TTK_Enum: llvm_unreachable("enum partial specialization"); + } + VisitTemplateParameterList(PartialSpec->getTemplateParameters()); + } + } + + if (!AlreadyStarted) { + switch (D->getTagKind()) { + case TTK_Interface: + case TTK_Struct: Out << "@S"; break; + case TTK_Class: Out << "@C"; break; + case TTK_Union: Out << "@U"; break; + case TTK_Enum: Out << "@E"; break; + } + } + + Out << '@'; + Out.flush(); + assert(Buf.size() > 0); + const unsigned off = Buf.size() - 1; + + if (EmitDeclName(D)) { + if (const TypedefNameDecl *TD = D->getTypedefNameForAnonDecl()) { + Buf[off] = 'A'; + Out << '@' << *TD; + } + else + Buf[off] = 'a'; + } + + // For a class template specialization, mangle the template arguments. + if (const ClassTemplateSpecializationDecl *Spec + = dyn_cast<ClassTemplateSpecializationDecl>(D)) { + const TemplateArgumentList &Args = Spec->getTemplateInstantiationArgs(); + Out << '>'; + for (unsigned I = 0, N = Args.size(); I != N; ++I) { + Out << '#'; + VisitTemplateArgument(Args.get(I)); + } + } +} + +void USRGenerator::VisitTypedefDecl(const TypedefDecl *D) { + if (ShouldGenerateLocation(D) && GenLoc(D)) + return; + const DeclContext *DC = D->getDeclContext(); + if (const NamedDecl *DCN = dyn_cast<NamedDecl>(DC)) + Visit(DCN); + Out << "@T@"; + Out << D->getName(); +} + +void USRGenerator::VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) { + GenLoc(D); + return; +} + +bool USRGenerator::GenLoc(const Decl *D) { + if (generatedLoc) + return IgnoreResults; + generatedLoc = true; + + // Guard against null declarations in invalid code. + if (!D) { + IgnoreResults = true; + return true; + } + + // Use the location of canonical decl. + D = D->getCanonicalDecl(); + + const SourceManager &SM = Context->getSourceManager(); + SourceLocation L = D->getLocStart(); + if (L.isInvalid()) { + IgnoreResults = true; + return true; + } + L = SM.getExpansionLoc(L); + const std::pair<FileID, unsigned> &Decomposed = SM.getDecomposedLoc(L); + const FileEntry *FE = SM.getFileEntryForID(Decomposed.first); + if (FE) { + Out << llvm::sys::path::filename(FE->getName()); + } + else { + // This case really isn't interesting. + IgnoreResults = true; + return true; + } + // Use the offest into the FileID to represent the location. Using + // a line/column can cause us to look back at the original source file, + // which is expensive. + Out << '@' << Decomposed.second; + return IgnoreResults; +} + +void USRGenerator::VisitType(QualType T) { + // This method mangles in USR information for types. It can possibly + // just reuse the naming-mangling logic used by codegen, although the + // requirements for USRs might not be the same. + ASTContext &Ctx = *Context; + + do { + T = Ctx.getCanonicalType(T); + Qualifiers Q = T.getQualifiers(); + unsigned qVal = 0; + if (Q.hasConst()) + qVal |= 0x1; + if (Q.hasVolatile()) + qVal |= 0x2; + if (Q.hasRestrict()) + qVal |= 0x4; + if(qVal) + Out << ((char) ('0' + qVal)); + + // Mangle in ObjC GC qualifiers? + + if (const PackExpansionType *Expansion = T->getAs<PackExpansionType>()) { + Out << 'P'; + T = Expansion->getPattern(); + } + + if (const BuiltinType *BT = T->getAs<BuiltinType>()) { + unsigned char c = '\0'; + switch (BT->getKind()) { + case BuiltinType::Void: + c = 'v'; break; + case BuiltinType::Bool: + c = 'b'; break; + case BuiltinType::Char_U: + case BuiltinType::UChar: + c = 'c'; break; + case BuiltinType::Char16: + c = 'q'; break; + case BuiltinType::Char32: + c = 'w'; break; + case BuiltinType::UShort: + c = 's'; break; + case BuiltinType::UInt: + c = 'i'; break; + case BuiltinType::ULong: + c = 'l'; break; + case BuiltinType::ULongLong: + c = 'k'; break; + case BuiltinType::UInt128: + c = 'j'; break; + case BuiltinType::Char_S: + case BuiltinType::SChar: + c = 'C'; break; + case BuiltinType::WChar_S: + case BuiltinType::WChar_U: + c = 'W'; break; + case BuiltinType::Short: + c = 'S'; break; + case BuiltinType::Int: + c = 'I'; break; + case BuiltinType::Long: + c = 'L'; break; + case BuiltinType::LongLong: + c = 'K'; break; + case BuiltinType::Int128: + c = 'J'; break; + case BuiltinType::Half: + c = 'h'; break; + case BuiltinType::Float: + c = 'f'; break; + case BuiltinType::Double: + c = 'd'; break; + case BuiltinType::LongDouble: + c = 'D'; break; + case BuiltinType::NullPtr: + c = 'n'; break; +#define BUILTIN_TYPE(Id, SingletonId) +#define PLACEHOLDER_TYPE(Id, SingletonId) case BuiltinType::Id: +#include "clang/AST/BuiltinTypes.def" + case BuiltinType::Dependent: + case BuiltinType::OCLImage1d: + case BuiltinType::OCLImage1dArray: + case BuiltinType::OCLImage1dBuffer: + case BuiltinType::OCLImage2d: + case BuiltinType::OCLImage2dArray: + case BuiltinType::OCLImage3d: + case BuiltinType::OCLEvent: + case BuiltinType::OCLSampler: + IgnoreResults = true; + return; + case BuiltinType::ObjCId: + c = 'o'; break; + case BuiltinType::ObjCClass: + c = 'O'; break; + case BuiltinType::ObjCSel: + c = 'e'; break; + } + Out << c; + return; + } + + // If we have already seen this (non-built-in) type, use a substitution + // encoding. + llvm::DenseMap<const Type *, unsigned>::iterator Substitution + = TypeSubstitutions.find(T.getTypePtr()); + if (Substitution != TypeSubstitutions.end()) { + Out << 'S' << Substitution->second << '_'; + return; + } else { + // Record this as a substitution. + unsigned Number = TypeSubstitutions.size(); + TypeSubstitutions[T.getTypePtr()] = Number; + } + + if (const PointerType *PT = T->getAs<PointerType>()) { + Out << '*'; + T = PT->getPointeeType(); + continue; + } + if (const ReferenceType *RT = T->getAs<ReferenceType>()) { + Out << '&'; + T = RT->getPointeeType(); + continue; + } + if (const FunctionProtoType *FT = T->getAs<FunctionProtoType>()) { + Out << 'F'; + VisitType(FT->getResultType()); + for (FunctionProtoType::arg_type_iterator + I = FT->arg_type_begin(), E = FT->arg_type_end(); I!=E; ++I) { + VisitType(*I); + } + if (FT->isVariadic()) + Out << '.'; + return; + } + if (const BlockPointerType *BT = T->getAs<BlockPointerType>()) { + Out << 'B'; + T = BT->getPointeeType(); + continue; + } + if (const ComplexType *CT = T->getAs<ComplexType>()) { + Out << '<'; + T = CT->getElementType(); + continue; + } + if (const TagType *TT = T->getAs<TagType>()) { + Out << '$'; + VisitTagDecl(TT->getDecl()); + return; + } + if (const TemplateTypeParmType *TTP = T->getAs<TemplateTypeParmType>()) { + Out << 't' << TTP->getDepth() << '.' << TTP->getIndex(); + return; + } + if (const TemplateSpecializationType *Spec + = T->getAs<TemplateSpecializationType>()) { + Out << '>'; + VisitTemplateName(Spec->getTemplateName()); + Out << Spec->getNumArgs(); + for (unsigned I = 0, N = Spec->getNumArgs(); I != N; ++I) + VisitTemplateArgument(Spec->getArg(I)); + return; + } + + // Unhandled type. + Out << ' '; + break; + } while (true); +} + +void USRGenerator::VisitTemplateParameterList( + const TemplateParameterList *Params) { + if (!Params) + return; + Out << '>' << Params->size(); + for (TemplateParameterList::const_iterator P = Params->begin(), + PEnd = Params->end(); + P != PEnd; ++P) { + Out << '#'; + if (isa<TemplateTypeParmDecl>(*P)) { + if (cast<TemplateTypeParmDecl>(*P)->isParameterPack()) + Out<< 'p'; + Out << 'T'; + continue; + } + + if (NonTypeTemplateParmDecl *NTTP = dyn_cast<NonTypeTemplateParmDecl>(*P)) { + if (NTTP->isParameterPack()) + Out << 'p'; + Out << 'N'; + VisitType(NTTP->getType()); + continue; + } + + TemplateTemplateParmDecl *TTP = cast<TemplateTemplateParmDecl>(*P); + if (TTP->isParameterPack()) + Out << 'p'; + Out << 't'; + VisitTemplateParameterList(TTP->getTemplateParameters()); + } +} + +void USRGenerator::VisitTemplateName(TemplateName Name) { + if (TemplateDecl *Template = Name.getAsTemplateDecl()) { + if (TemplateTemplateParmDecl *TTP + = dyn_cast<TemplateTemplateParmDecl>(Template)) { + Out << 't' << TTP->getDepth() << '.' << TTP->getIndex(); + return; + } + + Visit(Template); + return; + } + + // FIXME: Visit dependent template names. +} + +void USRGenerator::VisitTemplateArgument(const TemplateArgument &Arg) { + switch (Arg.getKind()) { + case TemplateArgument::Null: + break; + + case TemplateArgument::Declaration: + Visit(Arg.getAsDecl()); + break; + + case TemplateArgument::NullPtr: + break; + + case TemplateArgument::TemplateExpansion: + Out << 'P'; // pack expansion of... + // Fall through + case TemplateArgument::Template: + VisitTemplateName(Arg.getAsTemplateOrTemplatePattern()); + break; + + case TemplateArgument::Expression: + // FIXME: Visit expressions. + break; + + case TemplateArgument::Pack: + Out << 'p' << Arg.pack_size(); + for (TemplateArgument::pack_iterator P = Arg.pack_begin(), PEnd = Arg.pack_end(); + P != PEnd; ++P) + VisitTemplateArgument(*P); + break; + + case TemplateArgument::Type: + VisitType(Arg.getAsType()); + break; + + case TemplateArgument::Integral: + Out << 'V'; + VisitType(Arg.getIntegralType()); + Out << Arg.getAsIntegral(); + break; + } +} + +//===----------------------------------------------------------------------===// +// USR generation functions. +//===----------------------------------------------------------------------===// + +void clang::index::generateUSRForObjCClass(StringRef Cls, raw_ostream &OS) { + OS << "objc(cs)" << Cls; +} + +void clang::index::generateUSRForObjCCategory(StringRef Cls, StringRef Cat, + raw_ostream &OS) { + OS << "objc(cy)" << Cls << '@' << Cat; +} + +void clang::index::generateUSRForObjCIvar(StringRef Ivar, raw_ostream &OS) { + OS << '@' << Ivar; +} + +void clang::index::generateUSRForObjCMethod(StringRef Sel, + bool IsInstanceMethod, + raw_ostream &OS) { + OS << (IsInstanceMethod ? "(im)" : "(cm)") << Sel; +} + +void clang::index::generateUSRForObjCProperty(StringRef Prop, raw_ostream &OS) { + OS << "(py)" << Prop; +} + +void clang::index::generateUSRForObjCProtocol(StringRef Prot, raw_ostream &OS) { + OS << "objc(pl)" << Prot; +} + +bool clang::index::generateUSRForDecl(const Decl *D, + SmallVectorImpl<char> &Buf) { + // Don't generate USRs for things with invalid locations. + if (!D || D->getLocStart().isInvalid()) + return true; + + USRGenerator UG(&D->getASTContext(), Buf); + UG.Visit(D); + return UG.ignoreResults(); +} |