diff options
Diffstat (limited to 'clang/lib/Index')
| -rw-r--r-- | clang/lib/Index/CommentToXML.cpp | 1141 | ||||
| -rw-r--r-- | clang/lib/Index/FileIndexRecord.cpp | 60 | ||||
| -rw-r--r-- | clang/lib/Index/FileIndexRecord.h | 57 | ||||
| -rw-r--r-- | clang/lib/Index/IndexBody.cpp | 479 | ||||
| -rw-r--r-- | clang/lib/Index/IndexDecl.cpp | 770 | ||||
| -rw-r--r-- | clang/lib/Index/IndexSymbol.cpp | 584 | ||||
| -rw-r--r-- | clang/lib/Index/IndexTypeSourceInfo.cpp | 286 | ||||
| -rw-r--r-- | clang/lib/Index/IndexingAction.cpp | 211 | ||||
| -rw-r--r-- | clang/lib/Index/IndexingContext.cpp | 467 | ||||
| -rw-r--r-- | clang/lib/Index/IndexingContext.h | 139 | ||||
| -rw-r--r-- | clang/lib/Index/SimpleFormatContext.h | 72 | ||||
| -rw-r--r-- | clang/lib/Index/USRGeneration.cpp | 1151 |
12 files changed, 5417 insertions, 0 deletions
diff --git a/clang/lib/Index/CommentToXML.cpp b/clang/lib/Index/CommentToXML.cpp new file mode 100644 index 000000000000..55923d679fea --- /dev/null +++ b/clang/lib/Index/CommentToXML.cpp @@ -0,0 +1,1141 @@ +//===--- CommentToXML.cpp - Convert comments to XML representation --------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/CommentToXML.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Attr.h" +#include "clang/AST/Comment.h" +#include "clang/AST/CommentVisitor.h" +#include "clang/Format/Format.h" +#include "clang/Index/USRGeneration.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/TinyPtrVector.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::comments; +using namespace clang::index; + +namespace { + +/// This comparison will sort parameters with valid index by index, then vararg +/// parameters, and invalid (unresolved) parameters last. +class ParamCommandCommentCompareIndex { +public: + bool operator()(const ParamCommandComment *LHS, + const ParamCommandComment *RHS) const { + unsigned LHSIndex = UINT_MAX; + unsigned RHSIndex = UINT_MAX; + + if (LHS->isParamIndexValid()) { + if (LHS->isVarArgParam()) + LHSIndex = UINT_MAX - 1; + else + LHSIndex = LHS->getParamIndex(); + } + if (RHS->isParamIndexValid()) { + if (RHS->isVarArgParam()) + RHSIndex = UINT_MAX - 1; + else + RHSIndex = RHS->getParamIndex(); + } + return LHSIndex < RHSIndex; + } +}; + +/// This comparison will sort template parameters in the following order: +/// \li real template parameters (depth = 1) in index order; +/// \li all other names (depth > 1); +/// \li unresolved names. +class TParamCommandCommentComparePosition { +public: + bool operator()(const TParamCommandComment *LHS, + const TParamCommandComment *RHS) const { + // Sort unresolved names last. + if (!LHS->isPositionValid()) + return false; + if (!RHS->isPositionValid()) + return true; + + if (LHS->getDepth() > 1) + return false; + if (RHS->getDepth() > 1) + return true; + + // Sort template parameters in index order. + if (LHS->getDepth() == 1 && RHS->getDepth() == 1) + return LHS->getIndex(0) < RHS->getIndex(0); + + // Leave all other names in source order. + return true; + } +}; + +/// Separate parts of a FullComment. +struct FullCommentParts { + /// Take a full comment apart and initialize members accordingly. + FullCommentParts(const FullComment *C, + const CommandTraits &Traits); + + const BlockContentComment *Brief; + const BlockContentComment *Headerfile; + const ParagraphComment *FirstParagraph; + SmallVector<const BlockCommandComment *, 4> Returns; + SmallVector<const ParamCommandComment *, 8> Params; + SmallVector<const TParamCommandComment *, 4> TParams; + llvm::TinyPtrVector<const BlockCommandComment *> Exceptions; + SmallVector<const BlockContentComment *, 8> MiscBlocks; +}; + +FullCommentParts::FullCommentParts(const FullComment *C, + const CommandTraits &Traits) : + Brief(nullptr), Headerfile(nullptr), FirstParagraph(nullptr) { + for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); + I != E; ++I) { + const Comment *Child = *I; + if (!Child) + continue; + switch (Child->getCommentKind()) { + case Comment::NoCommentKind: + continue; + + case Comment::ParagraphCommentKind: { + const ParagraphComment *PC = cast<ParagraphComment>(Child); + if (PC->isWhitespace()) + break; + if (!FirstParagraph) + FirstParagraph = PC; + + MiscBlocks.push_back(PC); + break; + } + + case Comment::BlockCommandCommentKind: { + const BlockCommandComment *BCC = cast<BlockCommandComment>(Child); + const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID()); + if (!Brief && Info->IsBriefCommand) { + Brief = BCC; + break; + } + if (!Headerfile && Info->IsHeaderfileCommand) { + Headerfile = BCC; + break; + } + if (Info->IsReturnsCommand) { + Returns.push_back(BCC); + break; + } + if (Info->IsThrowsCommand) { + Exceptions.push_back(BCC); + break; + } + MiscBlocks.push_back(BCC); + break; + } + + case Comment::ParamCommandCommentKind: { + const ParamCommandComment *PCC = cast<ParamCommandComment>(Child); + if (!PCC->hasParamName()) + break; + + if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph()) + break; + + Params.push_back(PCC); + break; + } + + case Comment::TParamCommandCommentKind: { + const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child); + if (!TPCC->hasParamName()) + break; + + if (!TPCC->hasNonWhitespaceParagraph()) + break; + + TParams.push_back(TPCC); + break; + } + + case Comment::VerbatimBlockCommentKind: + MiscBlocks.push_back(cast<BlockCommandComment>(Child)); + break; + + case Comment::VerbatimLineCommentKind: { + const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child); + const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID()); + if (!Info->IsDeclarationCommand) + MiscBlocks.push_back(VLC); + break; + } + + case Comment::TextCommentKind: + case Comment::InlineCommandCommentKind: + case Comment::HTMLStartTagCommentKind: + case Comment::HTMLEndTagCommentKind: + case Comment::VerbatimBlockLineCommentKind: + case Comment::FullCommentKind: + llvm_unreachable("AST node of this kind can't be a child of " + "a FullComment"); + } + } + + // Sort params in order they are declared in the function prototype. + // Unresolved parameters are put at the end of the list in the same order + // they were seen in the comment. + llvm::stable_sort(Params, ParamCommandCommentCompareIndex()); + llvm::stable_sort(TParams, TParamCommandCommentComparePosition()); +} + +void printHTMLStartTagComment(const HTMLStartTagComment *C, + llvm::raw_svector_ostream &Result) { + Result << "<" << C->getTagName(); + + if (C->getNumAttrs() != 0) { + for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) { + Result << " "; + const HTMLStartTagComment::Attribute &Attr = C->getAttr(i); + Result << Attr.Name; + if (!Attr.Value.empty()) + Result << "=\"" << Attr.Value << "\""; + } + } + + if (!C->isSelfClosing()) + Result << ">"; + else + Result << "/>"; +} + +class CommentASTToHTMLConverter : + public ConstCommentVisitor<CommentASTToHTMLConverter> { +public: + /// \param Str accumulator for HTML. + CommentASTToHTMLConverter(const FullComment *FC, + SmallVectorImpl<char> &Str, + const CommandTraits &Traits) : + FC(FC), Result(Str), Traits(Traits) + { } + + // Inline content. + void visitTextComment(const TextComment *C); + void visitInlineCommandComment(const InlineCommandComment *C); + void visitHTMLStartTagComment(const HTMLStartTagComment *C); + void visitHTMLEndTagComment(const HTMLEndTagComment *C); + + // Block content. + void visitParagraphComment(const ParagraphComment *C); + void visitBlockCommandComment(const BlockCommandComment *C); + void visitParamCommandComment(const ParamCommandComment *C); + void visitTParamCommandComment(const TParamCommandComment *C); + void visitVerbatimBlockComment(const VerbatimBlockComment *C); + void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); + void visitVerbatimLineComment(const VerbatimLineComment *C); + + void visitFullComment(const FullComment *C); + + // Helpers. + + /// Convert a paragraph that is not a block by itself (an argument to some + /// command). + void visitNonStandaloneParagraphComment(const ParagraphComment *C); + + void appendToResultWithHTMLEscaping(StringRef S); + +private: + const FullComment *FC; + /// Output stream for HTML. + llvm::raw_svector_ostream Result; + + const CommandTraits &Traits; +}; +} // end unnamed namespace + +void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) { + appendToResultWithHTMLEscaping(C->getText()); +} + +void CommentASTToHTMLConverter::visitInlineCommandComment( + const InlineCommandComment *C) { + // Nothing to render if no arguments supplied. + if (C->getNumArgs() == 0) + return; + + // Nothing to render if argument is empty. + StringRef Arg0 = C->getArgText(0); + if (Arg0.empty()) + return; + + switch (C->getRenderKind()) { + case InlineCommandComment::RenderNormal: + for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) { + appendToResultWithHTMLEscaping(C->getArgText(i)); + Result << " "; + } + return; + + case InlineCommandComment::RenderBold: + assert(C->getNumArgs() == 1); + Result << "<b>"; + appendToResultWithHTMLEscaping(Arg0); + Result << "</b>"; + return; + case InlineCommandComment::RenderMonospaced: + assert(C->getNumArgs() == 1); + Result << "<tt>"; + appendToResultWithHTMLEscaping(Arg0); + Result<< "</tt>"; + return; + case InlineCommandComment::RenderEmphasized: + assert(C->getNumArgs() == 1); + Result << "<em>"; + appendToResultWithHTMLEscaping(Arg0); + Result << "</em>"; + return; + } +} + +void CommentASTToHTMLConverter::visitHTMLStartTagComment( + const HTMLStartTagComment *C) { + printHTMLStartTagComment(C, Result); +} + +void CommentASTToHTMLConverter::visitHTMLEndTagComment( + const HTMLEndTagComment *C) { + Result << "</" << C->getTagName() << ">"; +} + +void CommentASTToHTMLConverter::visitParagraphComment( + const ParagraphComment *C) { + if (C->isWhitespace()) + return; + + Result << "<p>"; + for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); + I != E; ++I) { + visit(*I); + } + Result << "</p>"; +} + +void CommentASTToHTMLConverter::visitBlockCommandComment( + const BlockCommandComment *C) { + const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID()); + if (Info->IsBriefCommand) { + Result << "<p class=\"para-brief\">"; + visitNonStandaloneParagraphComment(C->getParagraph()); + Result << "</p>"; + return; + } + if (Info->IsReturnsCommand) { + Result << "<p class=\"para-returns\">" + "<span class=\"word-returns\">Returns</span> "; + visitNonStandaloneParagraphComment(C->getParagraph()); + Result << "</p>"; + return; + } + // We don't know anything about this command. Just render the paragraph. + visit(C->getParagraph()); +} + +void CommentASTToHTMLConverter::visitParamCommandComment( + const ParamCommandComment *C) { + if (C->isParamIndexValid()) { + if (C->isVarArgParam()) { + Result << "<dt class=\"param-name-index-vararg\">"; + appendToResultWithHTMLEscaping(C->getParamNameAsWritten()); + } else { + Result << "<dt class=\"param-name-index-" + << C->getParamIndex() + << "\">"; + appendToResultWithHTMLEscaping(C->getParamName(FC)); + } + } else { + Result << "<dt class=\"param-name-index-invalid\">"; + appendToResultWithHTMLEscaping(C->getParamNameAsWritten()); + } + Result << "</dt>"; + + if (C->isParamIndexValid()) { + if (C->isVarArgParam()) + Result << "<dd class=\"param-descr-index-vararg\">"; + else + Result << "<dd class=\"param-descr-index-" + << C->getParamIndex() + << "\">"; + } else + Result << "<dd class=\"param-descr-index-invalid\">"; + + visitNonStandaloneParagraphComment(C->getParagraph()); + Result << "</dd>"; +} + +void CommentASTToHTMLConverter::visitTParamCommandComment( + const TParamCommandComment *C) { + if (C->isPositionValid()) { + if (C->getDepth() == 1) + Result << "<dt class=\"tparam-name-index-" + << C->getIndex(0) + << "\">"; + else + Result << "<dt class=\"tparam-name-index-other\">"; + appendToResultWithHTMLEscaping(C->getParamName(FC)); + } else { + Result << "<dt class=\"tparam-name-index-invalid\">"; + appendToResultWithHTMLEscaping(C->getParamNameAsWritten()); + } + + Result << "</dt>"; + + if (C->isPositionValid()) { + if (C->getDepth() == 1) + Result << "<dd class=\"tparam-descr-index-" + << C->getIndex(0) + << "\">"; + else + Result << "<dd class=\"tparam-descr-index-other\">"; + } else + Result << "<dd class=\"tparam-descr-index-invalid\">"; + + visitNonStandaloneParagraphComment(C->getParagraph()); + Result << "</dd>"; +} + +void CommentASTToHTMLConverter::visitVerbatimBlockComment( + const VerbatimBlockComment *C) { + unsigned NumLines = C->getNumLines(); + if (NumLines == 0) + return; + + Result << "<pre>"; + for (unsigned i = 0; i != NumLines; ++i) { + appendToResultWithHTMLEscaping(C->getText(i)); + if (i + 1 != NumLines) + Result << '\n'; + } + Result << "</pre>"; +} + +void CommentASTToHTMLConverter::visitVerbatimBlockLineComment( + const VerbatimBlockLineComment *C) { + llvm_unreachable("should not see this AST node"); +} + +void CommentASTToHTMLConverter::visitVerbatimLineComment( + const VerbatimLineComment *C) { + Result << "<pre>"; + appendToResultWithHTMLEscaping(C->getText()); + Result << "</pre>"; +} + +void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) { + FullCommentParts Parts(C, Traits); + + bool FirstParagraphIsBrief = false; + if (Parts.Headerfile) + visit(Parts.Headerfile); + if (Parts.Brief) + visit(Parts.Brief); + else if (Parts.FirstParagraph) { + Result << "<p class=\"para-brief\">"; + visitNonStandaloneParagraphComment(Parts.FirstParagraph); + Result << "</p>"; + FirstParagraphIsBrief = true; + } + + for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) { + const Comment *C = Parts.MiscBlocks[i]; + if (FirstParagraphIsBrief && C == Parts.FirstParagraph) + continue; + visit(C); + } + + if (Parts.TParams.size() != 0) { + Result << "<dl>"; + for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i) + visit(Parts.TParams[i]); + Result << "</dl>"; + } + + if (Parts.Params.size() != 0) { + Result << "<dl>"; + for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i) + visit(Parts.Params[i]); + Result << "</dl>"; + } + + if (Parts.Returns.size() != 0) { + Result << "<div class=\"result-discussion\">"; + for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i) + visit(Parts.Returns[i]); + Result << "</div>"; + } + +} + +void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment( + const ParagraphComment *C) { + if (!C) + return; + + for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); + I != E; ++I) { + visit(*I); + } +} + +void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) { + for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) { + const char C = *I; + switch (C) { + case '&': + Result << "&"; + break; + case '<': + Result << "<"; + break; + case '>': + Result << ">"; + break; + case '"': + Result << """; + break; + case '\'': + Result << "'"; + break; + case '/': + Result << "/"; + break; + default: + Result << C; + break; + } + } +} + +namespace { +class CommentASTToXMLConverter : + public ConstCommentVisitor<CommentASTToXMLConverter> { +public: + /// \param Str accumulator for XML. + CommentASTToXMLConverter(const FullComment *FC, + SmallVectorImpl<char> &Str, + const CommandTraits &Traits, + const SourceManager &SM) : + FC(FC), Result(Str), Traits(Traits), SM(SM) { } + + // Inline content. + void visitTextComment(const TextComment *C); + void visitInlineCommandComment(const InlineCommandComment *C); + void visitHTMLStartTagComment(const HTMLStartTagComment *C); + void visitHTMLEndTagComment(const HTMLEndTagComment *C); + + // Block content. + void visitParagraphComment(const ParagraphComment *C); + + void appendParagraphCommentWithKind(const ParagraphComment *C, + StringRef Kind); + + void visitBlockCommandComment(const BlockCommandComment *C); + void visitParamCommandComment(const ParamCommandComment *C); + void visitTParamCommandComment(const TParamCommandComment *C); + void visitVerbatimBlockComment(const VerbatimBlockComment *C); + void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); + void visitVerbatimLineComment(const VerbatimLineComment *C); + + void visitFullComment(const FullComment *C); + + // Helpers. + void appendToResultWithXMLEscaping(StringRef S); + void appendToResultWithCDATAEscaping(StringRef S); + + void formatTextOfDeclaration(const DeclInfo *DI, + SmallString<128> &Declaration); + +private: + const FullComment *FC; + + /// Output stream for XML. + llvm::raw_svector_ostream Result; + + const CommandTraits &Traits; + const SourceManager &SM; +}; + +void getSourceTextOfDeclaration(const DeclInfo *ThisDecl, + SmallVectorImpl<char> &Str) { + ASTContext &Context = ThisDecl->CurrentDecl->getASTContext(); + const LangOptions &LangOpts = Context.getLangOpts(); + llvm::raw_svector_ostream OS(Str); + PrintingPolicy PPolicy(LangOpts); + PPolicy.PolishForDeclaration = true; + PPolicy.TerseOutput = true; + PPolicy.ConstantsAsWritten = true; + ThisDecl->CurrentDecl->print(OS, PPolicy, + /*Indentation*/0, /*PrintInstantiation*/false); +} + +void CommentASTToXMLConverter::formatTextOfDeclaration( + const DeclInfo *DI, SmallString<128> &Declaration) { + // Formatting API expects null terminated input string. + StringRef StringDecl(Declaration.c_str(), Declaration.size()); + + // Formatter specific code. + unsigned Offset = 0; + unsigned Length = Declaration.size(); + + format::FormatStyle Style = format::getLLVMStyle(); + Style.FixNamespaceComments = false; + tooling::Replacements Replaces = + reformat(Style, StringDecl, tooling::Range(Offset, Length), "xmldecl.xd"); + auto FormattedStringDecl = applyAllReplacements(StringDecl, Replaces); + if (static_cast<bool>(FormattedStringDecl)) { + Declaration = *FormattedStringDecl; + } +} + +} // end unnamed namespace + +void CommentASTToXMLConverter::visitTextComment(const TextComment *C) { + appendToResultWithXMLEscaping(C->getText()); +} + +void CommentASTToXMLConverter::visitInlineCommandComment( + const InlineCommandComment *C) { + // Nothing to render if no arguments supplied. + if (C->getNumArgs() == 0) + return; + + // Nothing to render if argument is empty. + StringRef Arg0 = C->getArgText(0); + if (Arg0.empty()) + return; + + switch (C->getRenderKind()) { + case InlineCommandComment::RenderNormal: + for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) { + appendToResultWithXMLEscaping(C->getArgText(i)); + Result << " "; + } + return; + case InlineCommandComment::RenderBold: + assert(C->getNumArgs() == 1); + Result << "<bold>"; + appendToResultWithXMLEscaping(Arg0); + Result << "</bold>"; + return; + case InlineCommandComment::RenderMonospaced: + assert(C->getNumArgs() == 1); + Result << "<monospaced>"; + appendToResultWithXMLEscaping(Arg0); + Result << "</monospaced>"; + return; + case InlineCommandComment::RenderEmphasized: + assert(C->getNumArgs() == 1); + Result << "<emphasized>"; + appendToResultWithXMLEscaping(Arg0); + Result << "</emphasized>"; + return; + } +} + +void CommentASTToXMLConverter::visitHTMLStartTagComment( + const HTMLStartTagComment *C) { + Result << "<rawHTML"; + if (C->isMalformed()) + Result << " isMalformed=\"1\""; + Result << ">"; + { + SmallString<32> Tag; + { + llvm::raw_svector_ostream TagOS(Tag); + printHTMLStartTagComment(C, TagOS); + } + appendToResultWithCDATAEscaping(Tag); + } + Result << "</rawHTML>"; +} + +void +CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) { + Result << "<rawHTML"; + if (C->isMalformed()) + Result << " isMalformed=\"1\""; + Result << "></" << C->getTagName() << "></rawHTML>"; +} + +void +CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) { + appendParagraphCommentWithKind(C, StringRef()); +} + +void CommentASTToXMLConverter::appendParagraphCommentWithKind( + const ParagraphComment *C, + StringRef ParagraphKind) { + if (C->isWhitespace()) + return; + + if (ParagraphKind.empty()) + Result << "<Para>"; + else + Result << "<Para kind=\"" << ParagraphKind << "\">"; + + for (Comment::child_iterator I = C->child_begin(), E = C->child_end(); + I != E; ++I) { + visit(*I); + } + Result << "</Para>"; +} + +void CommentASTToXMLConverter::visitBlockCommandComment( + const BlockCommandComment *C) { + StringRef ParagraphKind; + + switch (C->getCommandID()) { + case CommandTraits::KCI_attention: + case CommandTraits::KCI_author: + case CommandTraits::KCI_authors: + case CommandTraits::KCI_bug: + case CommandTraits::KCI_copyright: + case CommandTraits::KCI_date: + case CommandTraits::KCI_invariant: + case CommandTraits::KCI_note: + case CommandTraits::KCI_post: + case CommandTraits::KCI_pre: + case CommandTraits::KCI_remark: + case CommandTraits::KCI_remarks: + case CommandTraits::KCI_sa: + case CommandTraits::KCI_see: + case CommandTraits::KCI_since: + case CommandTraits::KCI_todo: + case CommandTraits::KCI_version: + case CommandTraits::KCI_warning: + ParagraphKind = C->getCommandName(Traits); + break; + default: + break; + } + + appendParagraphCommentWithKind(C->getParagraph(), ParagraphKind); +} + +void CommentASTToXMLConverter::visitParamCommandComment( + const ParamCommandComment *C) { + Result << "<Parameter><Name>"; + appendToResultWithXMLEscaping(C->isParamIndexValid() + ? C->getParamName(FC) + : C->getParamNameAsWritten()); + Result << "</Name>"; + + if (C->isParamIndexValid()) { + if (C->isVarArgParam()) + Result << "<IsVarArg />"; + else + Result << "<Index>" << C->getParamIndex() << "</Index>"; + } + + Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">"; + switch (C->getDirection()) { + case ParamCommandComment::In: + Result << "in"; + break; + case ParamCommandComment::Out: + Result << "out"; + break; + case ParamCommandComment::InOut: + Result << "in,out"; + break; + } + Result << "</Direction><Discussion>"; + visit(C->getParagraph()); + Result << "</Discussion></Parameter>"; +} + +void CommentASTToXMLConverter::visitTParamCommandComment( + const TParamCommandComment *C) { + Result << "<Parameter><Name>"; + appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC) + : C->getParamNameAsWritten()); + Result << "</Name>"; + + if (C->isPositionValid() && C->getDepth() == 1) { + Result << "<Index>" << C->getIndex(0) << "</Index>"; + } + + Result << "<Discussion>"; + visit(C->getParagraph()); + Result << "</Discussion></Parameter>"; +} + +void CommentASTToXMLConverter::visitVerbatimBlockComment( + const VerbatimBlockComment *C) { + unsigned NumLines = C->getNumLines(); + if (NumLines == 0) + return; + + switch (C->getCommandID()) { + case CommandTraits::KCI_code: + Result << "<Verbatim xml:space=\"preserve\" kind=\"code\">"; + break; + default: + Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">"; + break; + } + for (unsigned i = 0; i != NumLines; ++i) { + appendToResultWithXMLEscaping(C->getText(i)); + if (i + 1 != NumLines) + Result << '\n'; + } + Result << "</Verbatim>"; +} + +void CommentASTToXMLConverter::visitVerbatimBlockLineComment( + const VerbatimBlockLineComment *C) { + llvm_unreachable("should not see this AST node"); +} + +void CommentASTToXMLConverter::visitVerbatimLineComment( + const VerbatimLineComment *C) { + Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">"; + appendToResultWithXMLEscaping(C->getText()); + Result << "</Verbatim>"; +} + +void CommentASTToXMLConverter::visitFullComment(const FullComment *C) { + FullCommentParts Parts(C, Traits); + + const DeclInfo *DI = C->getDeclInfo(); + StringRef RootEndTag; + if (DI) { + switch (DI->getKind()) { + case DeclInfo::OtherKind: + RootEndTag = "</Other>"; + Result << "<Other"; + break; + case DeclInfo::FunctionKind: + RootEndTag = "</Function>"; + Result << "<Function"; + switch (DI->TemplateKind) { + case DeclInfo::NotTemplate: + break; + case DeclInfo::Template: + Result << " templateKind=\"template\""; + break; + case DeclInfo::TemplateSpecialization: + Result << " templateKind=\"specialization\""; + break; + case DeclInfo::TemplatePartialSpecialization: + llvm_unreachable("partial specializations of functions " + "are not allowed in C++"); + } + if (DI->IsInstanceMethod) + Result << " isInstanceMethod=\"1\""; + if (DI->IsClassMethod) + Result << " isClassMethod=\"1\""; + break; + case DeclInfo::ClassKind: + RootEndTag = "</Class>"; + Result << "<Class"; + switch (DI->TemplateKind) { + case DeclInfo::NotTemplate: + break; + case DeclInfo::Template: + Result << " templateKind=\"template\""; + break; + case DeclInfo::TemplateSpecialization: + Result << " templateKind=\"specialization\""; + break; + case DeclInfo::TemplatePartialSpecialization: + Result << " templateKind=\"partialSpecialization\""; + break; + } + break; + case DeclInfo::VariableKind: + RootEndTag = "</Variable>"; + Result << "<Variable"; + break; + case DeclInfo::NamespaceKind: + RootEndTag = "</Namespace>"; + Result << "<Namespace"; + break; + case DeclInfo::TypedefKind: + RootEndTag = "</Typedef>"; + Result << "<Typedef"; + break; + case DeclInfo::EnumKind: + RootEndTag = "</Enum>"; + Result << "<Enum"; + break; + } + + { + // Print line and column number. + SourceLocation Loc = DI->CurrentDecl->getLocation(); + std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); + FileID FID = LocInfo.first; + unsigned FileOffset = LocInfo.second; + + if (FID.isValid()) { + if (const FileEntry *FE = SM.getFileEntryForID(FID)) { + Result << " file=\""; + appendToResultWithXMLEscaping(FE->getName()); + Result << "\""; + } + Result << " line=\"" << SM.getLineNumber(FID, FileOffset) + << "\" column=\"" << SM.getColumnNumber(FID, FileOffset) + << "\""; + } + } + + // Finish the root tag. + Result << ">"; + + bool FoundName = false; + if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) { + if (DeclarationName DeclName = ND->getDeclName()) { + Result << "<Name>"; + std::string Name = DeclName.getAsString(); + appendToResultWithXMLEscaping(Name); + FoundName = true; + Result << "</Name>"; + } + } + if (!FoundName) + Result << "<Name><anonymous></Name>"; + + { + // Print USR. + SmallString<128> USR; + generateUSRForDecl(DI->CommentDecl, USR); + if (!USR.empty()) { + Result << "<USR>"; + appendToResultWithXMLEscaping(USR); + Result << "</USR>"; + } + } + } else { + // No DeclInfo -- just emit some root tag and name tag. + RootEndTag = "</Other>"; + Result << "<Other><Name>unknown</Name>"; + } + + if (Parts.Headerfile) { + Result << "<Headerfile>"; + visit(Parts.Headerfile); + Result << "</Headerfile>"; + } + + { + // Pretty-print the declaration. + Result << "<Declaration>"; + SmallString<128> Declaration; + getSourceTextOfDeclaration(DI, Declaration); + formatTextOfDeclaration(DI, Declaration); + appendToResultWithXMLEscaping(Declaration); + Result << "</Declaration>"; + } + + bool FirstParagraphIsBrief = false; + if (Parts.Brief) { + Result << "<Abstract>"; + visit(Parts.Brief); + Result << "</Abstract>"; + } else if (Parts.FirstParagraph) { + Result << "<Abstract>"; + visit(Parts.FirstParagraph); + Result << "</Abstract>"; + FirstParagraphIsBrief = true; + } + + if (Parts.TParams.size() != 0) { + Result << "<TemplateParameters>"; + for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i) + visit(Parts.TParams[i]); + Result << "</TemplateParameters>"; + } + + if (Parts.Params.size() != 0) { + Result << "<Parameters>"; + for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i) + visit(Parts.Params[i]); + Result << "</Parameters>"; + } + + if (Parts.Exceptions.size() != 0) { + Result << "<Exceptions>"; + for (unsigned i = 0, e = Parts.Exceptions.size(); i != e; ++i) + visit(Parts.Exceptions[i]); + Result << "</Exceptions>"; + } + + if (Parts.Returns.size() != 0) { + Result << "<ResultDiscussion>"; + for (unsigned i = 0, e = Parts.Returns.size(); i != e; ++i) + visit(Parts.Returns[i]); + Result << "</ResultDiscussion>"; + } + + if (DI->CommentDecl->hasAttrs()) { + const AttrVec &Attrs = DI->CommentDecl->getAttrs(); + for (unsigned i = 0, e = Attrs.size(); i != e; i++) { + const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]); + if (!AA) { + if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) { + if (DA->getMessage().empty()) + Result << "<Deprecated/>"; + else { + Result << "<Deprecated>"; + appendToResultWithXMLEscaping(DA->getMessage()); + Result << "</Deprecated>"; + } + } + else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) { + if (UA->getMessage().empty()) + Result << "<Unavailable/>"; + else { + Result << "<Unavailable>"; + appendToResultWithXMLEscaping(UA->getMessage()); + Result << "</Unavailable>"; + } + } + continue; + } + + // 'availability' attribute. + Result << "<Availability"; + StringRef Distribution; + if (AA->getPlatform()) { + Distribution = AvailabilityAttr::getPrettyPlatformName( + AA->getPlatform()->getName()); + if (Distribution.empty()) + Distribution = AA->getPlatform()->getName(); + } + Result << " distribution=\"" << Distribution << "\">"; + VersionTuple IntroducedInVersion = AA->getIntroduced(); + if (!IntroducedInVersion.empty()) { + Result << "<IntroducedInVersion>" + << IntroducedInVersion.getAsString() + << "</IntroducedInVersion>"; + } + VersionTuple DeprecatedInVersion = AA->getDeprecated(); + if (!DeprecatedInVersion.empty()) { + Result << "<DeprecatedInVersion>" + << DeprecatedInVersion.getAsString() + << "</DeprecatedInVersion>"; + } + VersionTuple RemovedAfterVersion = AA->getObsoleted(); + if (!RemovedAfterVersion.empty()) { + Result << "<RemovedAfterVersion>" + << RemovedAfterVersion.getAsString() + << "</RemovedAfterVersion>"; + } + StringRef DeprecationSummary = AA->getMessage(); + if (!DeprecationSummary.empty()) { + Result << "<DeprecationSummary>"; + appendToResultWithXMLEscaping(DeprecationSummary); + Result << "</DeprecationSummary>"; + } + if (AA->getUnavailable()) + Result << "<Unavailable/>"; + Result << "</Availability>"; + } + } + + { + bool StartTagEmitted = false; + for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) { + const Comment *C = Parts.MiscBlocks[i]; + if (FirstParagraphIsBrief && C == Parts.FirstParagraph) + continue; + if (!StartTagEmitted) { + Result << "<Discussion>"; + StartTagEmitted = true; + } + visit(C); + } + if (StartTagEmitted) + Result << "</Discussion>"; + } + + Result << RootEndTag; +} + +void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) { + for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) { + const char C = *I; + switch (C) { + case '&': + Result << "&"; + break; + case '<': + Result << "<"; + break; + case '>': + Result << ">"; + break; + case '"': + Result << """; + break; + case '\'': + Result << "'"; + break; + default: + Result << C; + break; + } + } +} + +void CommentASTToXMLConverter::appendToResultWithCDATAEscaping(StringRef S) { + if (S.empty()) + return; + + Result << "<![CDATA["; + while (!S.empty()) { + size_t Pos = S.find("]]>"); + if (Pos == 0) { + Result << "]]]]><![CDATA[>"; + S = S.drop_front(3); + continue; + } + if (Pos == StringRef::npos) + Pos = S.size(); + + Result << S.substr(0, Pos); + + S = S.drop_front(Pos); + } + Result << "]]>"; +} + +CommentToXMLConverter::CommentToXMLConverter() {} +CommentToXMLConverter::~CommentToXMLConverter() {} + +void CommentToXMLConverter::convertCommentToHTML(const FullComment *FC, + SmallVectorImpl<char> &HTML, + const ASTContext &Context) { + CommentASTToHTMLConverter Converter(FC, HTML, + Context.getCommentCommandTraits()); + Converter.visit(FC); +} + +void CommentToXMLConverter::convertHTMLTagNodeToText( + const comments::HTMLTagComment *HTC, SmallVectorImpl<char> &Text, + const ASTContext &Context) { + CommentASTToHTMLConverter Converter(nullptr, Text, + Context.getCommentCommandTraits()); + Converter.visit(HTC); +} + +void CommentToXMLConverter::convertCommentToXML(const FullComment *FC, + SmallVectorImpl<char> &XML, + const ASTContext &Context) { + CommentASTToXMLConverter Converter(FC, XML, Context.getCommentCommandTraits(), + Context.getSourceManager()); + Converter.visit(FC); +} diff --git a/clang/lib/Index/FileIndexRecord.cpp b/clang/lib/Index/FileIndexRecord.cpp new file mode 100644 index 000000000000..c9dcb0f5377d --- /dev/null +++ b/clang/lib/Index/FileIndexRecord.cpp @@ -0,0 +1,60 @@ +//===--- FileIndexRecord.cpp - Index data per file --------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "FileIndexRecord.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclTemplate.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Path.h" + +using namespace clang; +using namespace clang::index; + +void FileIndexRecord::addDeclOccurence(SymbolRoleSet Roles, unsigned Offset, + const Decl *D, + ArrayRef<SymbolRelation> Relations) { + assert(D->isCanonicalDecl() && + "Occurrences should be associated with their canonical decl"); + + auto IsNextOccurence = [&]() -> bool { + if (Decls.empty()) + return true; + auto &Last = Decls.back(); + return Last.Offset < Offset; + }; + + if (IsNextOccurence()) { + Decls.emplace_back(Roles, Offset, D, Relations); + return; + } + + DeclOccurrence NewInfo(Roles, Offset, D, Relations); + // We keep Decls in order as we need to access them in this order in all cases. + auto It = llvm::upper_bound(Decls, NewInfo); + Decls.insert(It, std::move(NewInfo)); +} + +void FileIndexRecord::print(llvm::raw_ostream &OS) const { + OS << "DECLS BEGIN ---\n"; + for (auto &DclInfo : Decls) { + const Decl *D = DclInfo.Dcl; + SourceManager &SM = D->getASTContext().getSourceManager(); + SourceLocation Loc = SM.getFileLoc(D->getLocation()); + PresumedLoc PLoc = SM.getPresumedLoc(Loc); + OS << llvm::sys::path::filename(PLoc.getFilename()) << ':' << PLoc.getLine() + << ':' << PLoc.getColumn(); + + if (auto ND = dyn_cast<NamedDecl>(D)) { + OS << ' ' << ND->getNameAsString(); + } + + OS << '\n'; + } + OS << "DECLS END ---\n"; +} diff --git a/clang/lib/Index/FileIndexRecord.h b/clang/lib/Index/FileIndexRecord.h new file mode 100644 index 000000000000..37bf96a71964 --- /dev/null +++ b/clang/lib/Index/FileIndexRecord.h @@ -0,0 +1,57 @@ +//===--- FileIndexRecord.h - Index data per file ----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_INDEX_FILEINDEXRECORD_H +#define LLVM_CLANG_LIB_INDEX_FILEINDEXRECORD_H + +#include "clang/Basic/SourceLocation.h" +#include "clang/Index/DeclOccurrence.h" +#include "clang/Index/IndexSymbol.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include <vector> + +namespace clang { +class IdentifierInfo; + +namespace index { + +/// Stores the declaration occurrences seen in a particular source or header +/// file of a translation unit +class FileIndexRecord { +private: + FileID FID; + bool IsSystem; + std::vector<DeclOccurrence> Decls; + +public: + FileIndexRecord(FileID FID, bool IsSystem) : FID(FID), IsSystem(IsSystem) {} + + ArrayRef<DeclOccurrence> getDeclOccurrencesSortedByOffset() const { + return Decls; + } + + FileID getFileID() const { return FID; } + bool isSystem() const { return IsSystem; } + + /// Adds an occurrence of the canonical declaration \c D at the supplied + /// \c Offset + /// + /// \param Roles the roles the occurrence fulfills in this position. + /// \param Offset the offset in the file of this occurrence. + /// \param D the canonical declaration this is an occurrence of. + /// \param Relations the set of symbols related to this occurrence. + void addDeclOccurence(SymbolRoleSet Roles, unsigned Offset, const Decl *D, + ArrayRef<SymbolRelation> Relations); + void print(llvm::raw_ostream &OS) const; +}; + +} // end namespace index +} // end namespace clang + +#endif // LLVM_CLANG_LIB_INDEX_FILEINDEXRECORD_H diff --git a/clang/lib/Index/IndexBody.cpp b/clang/lib/Index/IndexBody.cpp new file mode 100644 index 000000000000..07a94f30c883 --- /dev/null +++ b/clang/lib/Index/IndexBody.cpp @@ -0,0 +1,479 @@ +//===- IndexBody.cpp - Indexing statements --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "IndexingContext.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/ASTLambda.h" + +using namespace clang; +using namespace clang::index; + +namespace { + +class BodyIndexer : public RecursiveASTVisitor<BodyIndexer> { + IndexingContext &IndexCtx; + const NamedDecl *Parent; + const DeclContext *ParentDC; + SmallVector<Stmt*, 16> StmtStack; + + typedef RecursiveASTVisitor<BodyIndexer> base; + + Stmt *getParentStmt() const { + return StmtStack.size() < 2 ? nullptr : StmtStack.end()[-2]; + } +public: + BodyIndexer(IndexingContext &indexCtx, + const NamedDecl *Parent, const DeclContext *DC) + : IndexCtx(indexCtx), Parent(Parent), ParentDC(DC) { } + + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool dataTraverseStmtPre(Stmt *S) { + StmtStack.push_back(S); + return true; + } + + bool dataTraverseStmtPost(Stmt *S) { + assert(StmtStack.back() == S); + StmtStack.pop_back(); + return true; + } + + bool TraverseTypeLoc(TypeLoc TL) { + IndexCtx.indexTypeLoc(TL, Parent, ParentDC); + return true; + } + + bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) { + IndexCtx.indexNestedNameSpecifierLoc(NNS, Parent, ParentDC); + return true; + } + + SymbolRoleSet getRolesForRef(const Expr *E, + SmallVectorImpl<SymbolRelation> &Relations) { + SymbolRoleSet Roles{}; + assert(!StmtStack.empty() && E == StmtStack.back()); + if (StmtStack.size() == 1) + return Roles; + auto It = StmtStack.end()-2; + while (isa<CastExpr>(*It) || isa<ParenExpr>(*It)) { + if (auto ICE = dyn_cast<ImplicitCastExpr>(*It)) { + if (ICE->getCastKind() == CK_LValueToRValue) + Roles |= (unsigned)(unsigned)SymbolRole::Read; + } + if (It == StmtStack.begin()) + break; + --It; + } + const Stmt *Parent = *It; + + if (auto BO = dyn_cast<BinaryOperator>(Parent)) { + if (BO->getOpcode() == BO_Assign && BO->getLHS()->IgnoreParenCasts() == E) + Roles |= (unsigned)SymbolRole::Write; + + } else if (auto UO = dyn_cast<UnaryOperator>(Parent)) { + if (UO->isIncrementDecrementOp()) { + Roles |= (unsigned)SymbolRole::Read; + Roles |= (unsigned)SymbolRole::Write; + } else if (UO->getOpcode() == UO_AddrOf) { + Roles |= (unsigned)SymbolRole::AddressOf; + } + + } else if (auto CA = dyn_cast<CompoundAssignOperator>(Parent)) { + if (CA->getLHS()->IgnoreParenCasts() == E) { + Roles |= (unsigned)SymbolRole::Read; + Roles |= (unsigned)SymbolRole::Write; + } + + } else if (auto CE = dyn_cast<CallExpr>(Parent)) { + if (CE->getCallee()->IgnoreParenCasts() == E) { + addCallRole(Roles, Relations); + if (auto *ME = dyn_cast<MemberExpr>(E)) { + if (auto *CXXMD = dyn_cast_or_null<CXXMethodDecl>(ME->getMemberDecl())) + if (CXXMD->isVirtual() && !ME->hasQualifier()) { + Roles |= (unsigned)SymbolRole::Dynamic; + auto BaseTy = ME->getBase()->IgnoreImpCasts()->getType(); + if (!BaseTy.isNull()) + if (auto *CXXRD = BaseTy->getPointeeCXXRecordDecl()) + Relations.emplace_back((unsigned)SymbolRole::RelationReceivedBy, + CXXRD); + } + } + } else if (auto CXXOp = dyn_cast<CXXOperatorCallExpr>(CE)) { + if (CXXOp->getNumArgs() > 0 && CXXOp->getArg(0)->IgnoreParenCasts() == E) { + OverloadedOperatorKind Op = CXXOp->getOperator(); + if (Op == OO_Equal) { + Roles |= (unsigned)SymbolRole::Write; + } else if ((Op >= OO_PlusEqual && Op <= OO_PipeEqual) || + Op == OO_LessLessEqual || Op == OO_GreaterGreaterEqual || + Op == OO_PlusPlus || Op == OO_MinusMinus) { + Roles |= (unsigned)SymbolRole::Read; + Roles |= (unsigned)SymbolRole::Write; + } else if (Op == OO_Amp) { + Roles |= (unsigned)SymbolRole::AddressOf; + } + } + } + } + + return Roles; + } + + void addCallRole(SymbolRoleSet &Roles, + SmallVectorImpl<SymbolRelation> &Relations) { + Roles |= (unsigned)SymbolRole::Call; + if (auto *FD = dyn_cast<FunctionDecl>(ParentDC)) + Relations.emplace_back((unsigned)SymbolRole::RelationCalledBy, FD); + else if (auto *MD = dyn_cast<ObjCMethodDecl>(ParentDC)) + Relations.emplace_back((unsigned)SymbolRole::RelationCalledBy, MD); + } + + bool VisitDeclRefExpr(DeclRefExpr *E) { + SmallVector<SymbolRelation, 4> Relations; + SymbolRoleSet Roles = getRolesForRef(E, Relations); + return IndexCtx.handleReference(E->getDecl(), E->getLocation(), + Parent, ParentDC, Roles, Relations, E); + } + + bool VisitMemberExpr(MemberExpr *E) { + SourceLocation Loc = E->getMemberLoc(); + if (Loc.isInvalid()) + Loc = E->getBeginLoc(); + SmallVector<SymbolRelation, 4> Relations; + SymbolRoleSet Roles = getRolesForRef(E, Relations); + return IndexCtx.handleReference(E->getMemberDecl(), Loc, + Parent, ParentDC, Roles, Relations, E); + } + + bool indexDependentReference( + const Expr *E, const Type *T, const DeclarationNameInfo &NameInfo, + llvm::function_ref<bool(const NamedDecl *ND)> Filter) { + if (!T) + return true; + const TemplateSpecializationType *TST = + T->getAs<TemplateSpecializationType>(); + if (!TST) + return true; + TemplateName TN = TST->getTemplateName(); + const ClassTemplateDecl *TD = + dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl()); + if (!TD) + return true; + CXXRecordDecl *RD = TD->getTemplatedDecl(); + if (!RD->hasDefinition()) + return true; + RD = RD->getDefinition(); + std::vector<const NamedDecl *> Symbols = + RD->lookupDependentName(NameInfo.getName(), Filter); + // FIXME: Improve overload handling. + if (Symbols.size() != 1) + return true; + SourceLocation Loc = NameInfo.getLoc(); + if (Loc.isInvalid()) + Loc = E->getBeginLoc(); + SmallVector<SymbolRelation, 4> Relations; + SymbolRoleSet Roles = getRolesForRef(E, Relations); + return IndexCtx.handleReference(Symbols[0], Loc, Parent, ParentDC, Roles, + Relations, E); + } + + bool VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E) { + const DeclarationNameInfo &Info = E->getMemberNameInfo(); + return indexDependentReference( + E, E->getBaseType().getTypePtrOrNull(), Info, + [](const NamedDecl *D) { return D->isCXXInstanceMember(); }); + } + + bool VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *E) { + const DeclarationNameInfo &Info = E->getNameInfo(); + const NestedNameSpecifier *NNS = E->getQualifier(); + return indexDependentReference( + E, NNS->getAsType(), Info, + [](const NamedDecl *D) { return !D->isCXXInstanceMember(); }); + } + + bool VisitDesignatedInitExpr(DesignatedInitExpr *E) { + for (DesignatedInitExpr::Designator &D : llvm::reverse(E->designators())) { + if (D.isFieldDesignator() && D.getField()) + return IndexCtx.handleReference(D.getField(), D.getFieldLoc(), Parent, + ParentDC, SymbolRoleSet(), {}, E); + } + return true; + } + + bool VisitObjCIvarRefExpr(ObjCIvarRefExpr *E) { + SmallVector<SymbolRelation, 4> Relations; + SymbolRoleSet Roles = getRolesForRef(E, Relations); + return IndexCtx.handleReference(E->getDecl(), E->getLocation(), + Parent, ParentDC, Roles, Relations, E); + } + + bool VisitObjCMessageExpr(ObjCMessageExpr *E) { + auto isDynamic = [](const ObjCMessageExpr *MsgE)->bool { + if (MsgE->getReceiverKind() != ObjCMessageExpr::Instance) + return false; + if (auto *RecE = dyn_cast<ObjCMessageExpr>( + MsgE->getInstanceReceiver()->IgnoreParenCasts())) { + if (RecE->getMethodFamily() == OMF_alloc) + return false; + } + return true; + }; + + if (ObjCMethodDecl *MD = E->getMethodDecl()) { + SymbolRoleSet Roles{}; + SmallVector<SymbolRelation, 2> Relations; + addCallRole(Roles, Relations); + Stmt *Containing = getParentStmt(); + + auto IsImplicitProperty = [](const PseudoObjectExpr *POE) -> bool { + const auto *E = POE->getSyntacticForm(); + if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) + E = BinOp->getLHS(); + const auto *PRE = dyn_cast<ObjCPropertyRefExpr>(E); + if (!PRE) + return false; + if (PRE->isExplicitProperty()) + return false; + if (const ObjCMethodDecl *Getter = PRE->getImplicitPropertyGetter()) { + // Class properties that are explicitly defined using @property + // declarations are represented implicitly as there is no ivar for + // class properties. + if (Getter->isClassMethod() && + Getter->getCanonicalDecl()->findPropertyDecl()) + return false; + } + return true; + }; + bool IsPropCall = Containing && isa<PseudoObjectExpr>(Containing); + // Implicit property message sends are not 'implicit'. + if ((E->isImplicit() || IsPropCall) && + !(IsPropCall && + IsImplicitProperty(cast<PseudoObjectExpr>(Containing)))) + Roles |= (unsigned)SymbolRole::Implicit; + + if (isDynamic(E)) { + Roles |= (unsigned)SymbolRole::Dynamic; + + auto addReceivers = [&](const ObjCObjectType *Ty) { + if (!Ty) + return; + if (const auto *clsD = Ty->getInterface()) { + Relations.emplace_back((unsigned)SymbolRole::RelationReceivedBy, + clsD); + } + for (const auto *protD : Ty->quals()) { + Relations.emplace_back((unsigned)SymbolRole::RelationReceivedBy, + protD); + } + }; + QualType recT = E->getReceiverType(); + if (const auto *Ptr = recT->getAs<ObjCObjectPointerType>()) + addReceivers(Ptr->getObjectType()); + else + addReceivers(recT->getAs<ObjCObjectType>()); + } + + return IndexCtx.handleReference(MD, E->getSelectorStartLoc(), + Parent, ParentDC, Roles, Relations, E); + } + return true; + } + + bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *E) { + if (E->isClassReceiver()) + IndexCtx.handleReference(E->getClassReceiver(), E->getReceiverLocation(), + Parent, ParentDC); + if (E->isExplicitProperty()) { + SmallVector<SymbolRelation, 2> Relations; + SymbolRoleSet Roles = getRolesForRef(E, Relations); + return IndexCtx.handleReference(E->getExplicitProperty(), E->getLocation(), + Parent, ParentDC, Roles, Relations, E); + } else if (const ObjCMethodDecl *Getter = E->getImplicitPropertyGetter()) { + // Class properties that are explicitly defined using @property + // declarations are represented implicitly as there is no ivar for class + // properties. + if (Getter->isClassMethod()) { + if (const auto *PD = Getter->getCanonicalDecl()->findPropertyDecl()) { + SmallVector<SymbolRelation, 2> Relations; + SymbolRoleSet Roles = getRolesForRef(E, Relations); + return IndexCtx.handleReference(PD, E->getLocation(), Parent, + ParentDC, Roles, Relations, E); + } + } + } + + // No need to do a handleReference for the objc method, because there will + // be a message expr as part of PseudoObjectExpr. + return true; + } + + bool VisitMSPropertyRefExpr(MSPropertyRefExpr *E) { + return IndexCtx.handleReference(E->getPropertyDecl(), E->getMemberLoc(), + Parent, ParentDC, SymbolRoleSet(), {}, E); + } + + bool VisitObjCProtocolExpr(ObjCProtocolExpr *E) { + return IndexCtx.handleReference(E->getProtocol(), E->getProtocolIdLoc(), + Parent, ParentDC, SymbolRoleSet(), {}, E); + } + + bool passObjCLiteralMethodCall(const ObjCMethodDecl *MD, const Expr *E) { + SymbolRoleSet Roles{}; + SmallVector<SymbolRelation, 2> Relations; + addCallRole(Roles, Relations); + Roles |= (unsigned)SymbolRole::Implicit; + return IndexCtx.handleReference(MD, E->getBeginLoc(), Parent, ParentDC, + Roles, Relations, E); + } + + bool VisitObjCBoxedExpr(ObjCBoxedExpr *E) { + if (ObjCMethodDecl *MD = E->getBoxingMethod()) { + return passObjCLiteralMethodCall(MD, E); + } + return true; + } + + bool VisitObjCDictionaryLiteral(ObjCDictionaryLiteral *E) { + if (ObjCMethodDecl *MD = E->getDictWithObjectsMethod()) { + return passObjCLiteralMethodCall(MD, E); + } + return true; + } + + bool VisitObjCArrayLiteral(ObjCArrayLiteral *E) { + if (ObjCMethodDecl *MD = E->getArrayWithObjectsMethod()) { + return passObjCLiteralMethodCall(MD, E); + } + return true; + } + + bool VisitCXXConstructExpr(CXXConstructExpr *E) { + SymbolRoleSet Roles{}; + SmallVector<SymbolRelation, 2> Relations; + addCallRole(Roles, Relations); + return IndexCtx.handleReference(E->getConstructor(), E->getLocation(), + Parent, ParentDC, Roles, Relations, E); + } + + bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr *E, + DataRecursionQueue *Q = nullptr) { + if (E->getOperatorLoc().isInvalid()) + return true; // implicit. + return base::TraverseCXXOperatorCallExpr(E, Q); + } + + bool VisitDeclStmt(DeclStmt *S) { + if (IndexCtx.shouldIndexFunctionLocalSymbols()) { + IndexCtx.indexDeclGroupRef(S->getDeclGroup()); + return true; + } + + DeclGroupRef DG = S->getDeclGroup(); + for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) { + const Decl *D = *I; + if (!D) + continue; + if (!isFunctionLocalSymbol(D)) + IndexCtx.indexTopLevelDecl(D); + } + + return true; + } + + bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C, + Expr *Init) { + if (C->capturesThis() || C->capturesVLAType()) + return true; + + if (C->capturesVariable() && IndexCtx.shouldIndexFunctionLocalSymbols()) + return IndexCtx.handleReference(C->getCapturedVar(), C->getLocation(), + Parent, ParentDC, SymbolRoleSet()); + + // FIXME: Lambda init-captures. + return true; + } + + // RecursiveASTVisitor visits both syntactic and semantic forms, duplicating + // the things that we visit. Make sure to only visit the semantic form. + // Also visit things that are in the syntactic form but not the semantic one, + // for example the indices in DesignatedInitExprs. + bool TraverseInitListExpr(InitListExpr *S, DataRecursionQueue *Q = nullptr) { + auto visitForm = [&](InitListExpr *Form) { + for (Stmt *SubStmt : Form->children()) { + if (!TraverseStmt(SubStmt, Q)) + return false; + } + return true; + }; + + auto visitSyntacticDesignatedInitExpr = [&](DesignatedInitExpr *E) -> bool { + for (DesignatedInitExpr::Designator &D : llvm::reverse(E->designators())) { + if (D.isFieldDesignator()) + return IndexCtx.handleReference(D.getField(), D.getFieldLoc(), + Parent, ParentDC, SymbolRoleSet(), + {}, E); + } + return true; + }; + + InitListExpr *SemaForm = S->isSemanticForm() ? S : S->getSemanticForm(); + InitListExpr *SyntaxForm = S->isSemanticForm() ? S->getSyntacticForm() : S; + + if (SemaForm) { + // Visit things present in syntactic form but not the semantic form. + if (SyntaxForm) { + for (Expr *init : SyntaxForm->inits()) { + if (auto *DIE = dyn_cast<DesignatedInitExpr>(init)) + visitSyntacticDesignatedInitExpr(DIE); + } + } + return visitForm(SemaForm); + } + + // No semantic, try the syntactic. + if (SyntaxForm) { + return visitForm(SyntaxForm); + } + + return true; + } + + bool VisitOffsetOfExpr(OffsetOfExpr *S) { + for (unsigned I = 0, E = S->getNumComponents(); I != E; ++I) { + const OffsetOfNode &Component = S->getComponent(I); + if (Component.getKind() == OffsetOfNode::Field) + IndexCtx.handleReference(Component.getField(), Component.getEndLoc(), + Parent, ParentDC, SymbolRoleSet(), {}); + // FIXME: Try to resolve dependent field references. + } + return true; + } + + bool VisitParmVarDecl(ParmVarDecl* D) { + // Index the parameters of lambda expression. + if (IndexCtx.shouldIndexFunctionLocalSymbols()) { + const auto *DC = D->getDeclContext(); + if (DC && isLambdaCallOperator(DC)) + IndexCtx.handleDecl(D); + } + return true; + } +}; + +} // anonymous namespace + +void IndexingContext::indexBody(const Stmt *S, const NamedDecl *Parent, + const DeclContext *DC) { + if (!S) + return; + + if (!DC) + DC = Parent->getLexicalDeclContext(); + BodyIndexer(*this, Parent, DC).TraverseStmt(const_cast<Stmt*>(S)); +} diff --git a/clang/lib/Index/IndexDecl.cpp b/clang/lib/Index/IndexDecl.cpp new file mode 100644 index 000000000000..5bbbb0d32bf4 --- /dev/null +++ b/clang/lib/Index/IndexDecl.cpp @@ -0,0 +1,770 @@ +//===- IndexDecl.cpp - Indexing declarations ------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "IndexingContext.h" +#include "clang/Index/IndexDataConsumer.h" +#include "clang/AST/DeclVisitor.h" + +using namespace clang; +using namespace index; + +#define TRY_DECL(D,CALL_EXPR) \ + do { \ + if (!IndexCtx.shouldIndex(D)) return true; \ + if (!CALL_EXPR) \ + return false; \ + } while (0) + +#define TRY_TO(CALL_EXPR) \ + do { \ + if (!CALL_EXPR) \ + return false; \ + } while (0) + +namespace { + +class IndexingDeclVisitor : public ConstDeclVisitor<IndexingDeclVisitor, bool> { + IndexingContext &IndexCtx; + +public: + explicit IndexingDeclVisitor(IndexingContext &indexCtx) + : IndexCtx(indexCtx) { } + + bool Handled = true; + + bool VisitDecl(const Decl *D) { + Handled = false; + return true; + } + + /// Returns true if the given method has been defined explicitly by the + /// user. + static bool hasUserDefined(const ObjCMethodDecl *D, + const ObjCImplDecl *Container) { + const ObjCMethodDecl *MD = Container->getMethod(D->getSelector(), + D->isInstanceMethod()); + return MD && !MD->isImplicit() && MD->isThisDeclarationADefinition(); + } + + void handleTemplateArgumentLoc(const TemplateArgumentLoc &TALoc, + const NamedDecl *Parent, + const DeclContext *DC) { + const TemplateArgumentLocInfo &LocInfo = TALoc.getLocInfo(); + switch (TALoc.getArgument().getKind()) { + case TemplateArgument::Expression: + IndexCtx.indexBody(LocInfo.getAsExpr(), Parent, DC); + break; + case TemplateArgument::Type: + IndexCtx.indexTypeSourceInfo(LocInfo.getAsTypeSourceInfo(), Parent, DC); + break; + case TemplateArgument::Template: + case TemplateArgument::TemplateExpansion: + IndexCtx.indexNestedNameSpecifierLoc(TALoc.getTemplateQualifierLoc(), + Parent, DC); + if (const TemplateDecl *TD = TALoc.getArgument() + .getAsTemplateOrTemplatePattern() + .getAsTemplateDecl()) { + if (const NamedDecl *TTD = TD->getTemplatedDecl()) + IndexCtx.handleReference(TTD, TALoc.getTemplateNameLoc(), Parent, DC); + } + break; + default: + break; + } + } + + void handleDeclarator(const DeclaratorDecl *D, + const NamedDecl *Parent = nullptr, + bool isIBType = false) { + if (!Parent) Parent = D; + + IndexCtx.indexTypeSourceInfo(D->getTypeSourceInfo(), Parent, + Parent->getLexicalDeclContext(), + /*isBase=*/false, isIBType); + IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), Parent); + if (IndexCtx.shouldIndexFunctionLocalSymbols()) { + if (const ParmVarDecl *Parm = dyn_cast<ParmVarDecl>(D)) { + auto *DC = Parm->getDeclContext(); + if (auto *FD = dyn_cast<FunctionDecl>(DC)) { + if (IndexCtx.shouldIndexParametersInDeclarations() || + FD->isThisDeclarationADefinition()) + IndexCtx.handleDecl(Parm); + } else if (auto *MD = dyn_cast<ObjCMethodDecl>(DC)) { + if (MD->isThisDeclarationADefinition()) + IndexCtx.handleDecl(Parm); + } else { + IndexCtx.handleDecl(Parm); + } + } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + if (IndexCtx.shouldIndexParametersInDeclarations() || + FD->isThisDeclarationADefinition()) { + for (auto PI : FD->parameters()) { + IndexCtx.handleDecl(PI); + } + } + } + } else { + // Index the default parameter value for function definitions. + if (const auto *FD = dyn_cast<FunctionDecl>(D)) { + if (FD->isThisDeclarationADefinition()) { + for (const auto *PV : FD->parameters()) { + if (PV->hasDefaultArg() && !PV->hasUninstantiatedDefaultArg() && + !PV->hasUnparsedDefaultArg()) + IndexCtx.indexBody(PV->getDefaultArg(), D); + } + } + } + } + } + + bool handleObjCMethod(const ObjCMethodDecl *D, + const ObjCPropertyDecl *AssociatedProp = nullptr) { + SmallVector<SymbolRelation, 4> Relations; + SmallVector<const ObjCMethodDecl*, 4> Overriden; + + D->getOverriddenMethods(Overriden); + for(auto overridden: Overriden) { + Relations.emplace_back((unsigned) SymbolRole::RelationOverrideOf, + overridden); + } + if (AssociatedProp) + Relations.emplace_back((unsigned)SymbolRole::RelationAccessorOf, + AssociatedProp); + + // getLocation() returns beginning token of a method declaration, but for + // indexing purposes we want to point to the base name. + SourceLocation MethodLoc = D->getSelectorStartLoc(); + if (MethodLoc.isInvalid()) + MethodLoc = D->getLocation(); + + SourceLocation AttrLoc; + + // check for (getter=/setter=) + if (AssociatedProp) { + bool isGetter = !D->param_size(); + AttrLoc = isGetter ? + AssociatedProp->getGetterNameLoc(): + AssociatedProp->getSetterNameLoc(); + } + + SymbolRoleSet Roles = (SymbolRoleSet)SymbolRole::Dynamic; + if (D->isImplicit()) { + if (AttrLoc.isValid()) { + MethodLoc = AttrLoc; + } else { + Roles |= (SymbolRoleSet)SymbolRole::Implicit; + } + } else if (AttrLoc.isValid()) { + IndexCtx.handleReference(D, AttrLoc, cast<NamedDecl>(D->getDeclContext()), + D->getDeclContext(), 0); + } + + TRY_DECL(D, IndexCtx.handleDecl(D, MethodLoc, Roles, Relations)); + IndexCtx.indexTypeSourceInfo(D->getReturnTypeSourceInfo(), D); + bool hasIBActionAndFirst = D->hasAttr<IBActionAttr>(); + for (const auto *I : D->parameters()) { + handleDeclarator(I, D, /*isIBType=*/hasIBActionAndFirst); + hasIBActionAndFirst = false; + } + + if (D->isThisDeclarationADefinition()) { + const Stmt *Body = D->getBody(); + if (Body) { + IndexCtx.indexBody(Body, D, D); + } + } + return true; + } + + /// Gather the declarations which the given declaration \D overrides in a + /// pseudo-override manner. + /// + /// Pseudo-overrides occur when a class template specialization declares + /// a declaration that has the same name as a similar declaration in the + /// non-specialized template. + void + gatherTemplatePseudoOverrides(const NamedDecl *D, + SmallVectorImpl<SymbolRelation> &Relations) { + if (!IndexCtx.getLangOpts().CPlusPlus) + return; + const auto *CTSD = + dyn_cast<ClassTemplateSpecializationDecl>(D->getLexicalDeclContext()); + if (!CTSD) + return; + llvm::PointerUnion<ClassTemplateDecl *, + ClassTemplatePartialSpecializationDecl *> + Template = CTSD->getSpecializedTemplateOrPartial(); + if (const auto *CTD = Template.dyn_cast<ClassTemplateDecl *>()) { + const CXXRecordDecl *Pattern = CTD->getTemplatedDecl(); + bool TypeOverride = isa<TypeDecl>(D); + for (const NamedDecl *ND : Pattern->lookup(D->getDeclName())) { + if (const auto *CTD = dyn_cast<ClassTemplateDecl>(ND)) + ND = CTD->getTemplatedDecl(); + if (ND->isImplicit()) + continue; + // Types can override other types. + if (!TypeOverride) { + if (ND->getKind() != D->getKind()) + continue; + } else if (!isa<TypeDecl>(ND)) + continue; + if (const auto *FD = dyn_cast<FunctionDecl>(ND)) { + const auto *DFD = cast<FunctionDecl>(D); + // Function overrides are approximated using the number of parameters. + if (FD->getStorageClass() != DFD->getStorageClass() || + FD->getNumParams() != DFD->getNumParams()) + continue; + } + Relations.emplace_back( + SymbolRoleSet(SymbolRole::RelationSpecializationOf), ND); + } + } + } + + bool VisitFunctionDecl(const FunctionDecl *D) { + SymbolRoleSet Roles{}; + SmallVector<SymbolRelation, 4> Relations; + if (auto *CXXMD = dyn_cast<CXXMethodDecl>(D)) { + if (CXXMD->isVirtual()) + Roles |= (unsigned)SymbolRole::Dynamic; + for (const CXXMethodDecl *O : CXXMD->overridden_methods()) { + Relations.emplace_back((unsigned)SymbolRole::RelationOverrideOf, O); + } + } + gatherTemplatePseudoOverrides(D, Relations); + if (const auto *Base = D->getPrimaryTemplate()) + Relations.push_back( + SymbolRelation(SymbolRoleSet(SymbolRole::RelationSpecializationOf), + Base->getTemplatedDecl())); + + TRY_DECL(D, IndexCtx.handleDecl(D, Roles, Relations)); + handleDeclarator(D); + + if (const CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(D)) { + IndexCtx.handleReference(Ctor->getParent(), Ctor->getLocation(), + Ctor->getParent(), Ctor->getDeclContext(), + (unsigned)SymbolRole::NameReference); + + // Constructor initializers. + for (const auto *Init : Ctor->inits()) { + if (Init->isWritten()) { + IndexCtx.indexTypeSourceInfo(Init->getTypeSourceInfo(), D); + if (const FieldDecl *Member = Init->getAnyMember()) + IndexCtx.handleReference(Member, Init->getMemberLocation(), D, D, + (unsigned)SymbolRole::Write); + IndexCtx.indexBody(Init->getInit(), D, D); + } + } + } else if (const CXXDestructorDecl *Dtor = dyn_cast<CXXDestructorDecl>(D)) { + if (auto TypeNameInfo = Dtor->getNameInfo().getNamedTypeInfo()) { + IndexCtx.handleReference(Dtor->getParent(), + TypeNameInfo->getTypeLoc().getBeginLoc(), + Dtor->getParent(), Dtor->getDeclContext(), + (unsigned)SymbolRole::NameReference); + } + } else if (const auto *Guide = dyn_cast<CXXDeductionGuideDecl>(D)) { + IndexCtx.handleReference(Guide->getDeducedTemplate()->getTemplatedDecl(), + Guide->getLocation(), Guide, + Guide->getDeclContext()); + } + // Template specialization arguments. + if (const ASTTemplateArgumentListInfo *TemplateArgInfo = + D->getTemplateSpecializationArgsAsWritten()) { + for (const auto &Arg : TemplateArgInfo->arguments()) + handleTemplateArgumentLoc(Arg, D, D->getLexicalDeclContext()); + } + + if (D->isThisDeclarationADefinition()) { + const Stmt *Body = D->getBody(); + if (Body) { + IndexCtx.indexBody(Body, D, D); + } + } + return true; + } + + bool VisitVarDecl(const VarDecl *D) { + SmallVector<SymbolRelation, 4> Relations; + gatherTemplatePseudoOverrides(D, Relations); + TRY_DECL(D, IndexCtx.handleDecl(D, SymbolRoleSet(), Relations)); + handleDeclarator(D); + IndexCtx.indexBody(D->getInit(), D); + return true; + } + + bool VisitDecompositionDecl(const DecompositionDecl *D) { + for (const auto *Binding : D->bindings()) + TRY_DECL(Binding, IndexCtx.handleDecl(Binding)); + return Base::VisitDecompositionDecl(D); + } + + bool VisitFieldDecl(const FieldDecl *D) { + SmallVector<SymbolRelation, 4> Relations; + gatherTemplatePseudoOverrides(D, Relations); + TRY_DECL(D, IndexCtx.handleDecl(D, SymbolRoleSet(), Relations)); + handleDeclarator(D); + if (D->isBitField()) + IndexCtx.indexBody(D->getBitWidth(), D); + else if (D->hasInClassInitializer()) + IndexCtx.indexBody(D->getInClassInitializer(), D); + return true; + } + + bool VisitObjCIvarDecl(const ObjCIvarDecl *D) { + if (D->getSynthesize()) { + // handled in VisitObjCPropertyImplDecl + return true; + } + TRY_DECL(D, IndexCtx.handleDecl(D)); + handleDeclarator(D); + return true; + } + + bool VisitMSPropertyDecl(const MSPropertyDecl *D) { + TRY_DECL(D, IndexCtx.handleDecl(D)); + handleDeclarator(D); + return true; + } + + bool VisitEnumConstantDecl(const EnumConstantDecl *D) { + TRY_DECL(D, IndexCtx.handleDecl(D)); + IndexCtx.indexBody(D->getInitExpr(), D); + return true; + } + + bool VisitTypedefNameDecl(const TypedefNameDecl *D) { + if (!D->isTransparentTag()) { + SmallVector<SymbolRelation, 4> Relations; + gatherTemplatePseudoOverrides(D, Relations); + TRY_DECL(D, IndexCtx.handleDecl(D, SymbolRoleSet(), Relations)); + IndexCtx.indexTypeSourceInfo(D->getTypeSourceInfo(), D); + } + return true; + } + + bool VisitTagDecl(const TagDecl *D) { + // Non-free standing tags are handled in indexTypeSourceInfo. + if (D->isFreeStanding()) { + if (D->isThisDeclarationADefinition()) { + SmallVector<SymbolRelation, 4> Relations; + gatherTemplatePseudoOverrides(D, Relations); + IndexCtx.indexTagDecl(D, Relations); + } else { + SmallVector<SymbolRelation, 1> Relations; + gatherTemplatePseudoOverrides(D, Relations); + return IndexCtx.handleDecl(D, D->getLocation(), SymbolRoleSet(), + Relations, D->getLexicalDeclContext()); + } + } + return true; + } + + bool handleReferencedProtocols(const ObjCProtocolList &ProtList, + const ObjCContainerDecl *ContD, + SourceLocation SuperLoc) { + ObjCInterfaceDecl::protocol_loc_iterator LI = ProtList.loc_begin(); + for (ObjCInterfaceDecl::protocol_iterator + I = ProtList.begin(), E = ProtList.end(); I != E; ++I, ++LI) { + SourceLocation Loc = *LI; + ObjCProtocolDecl *PD = *I; + SymbolRoleSet roles{}; + if (Loc == SuperLoc) + roles |= (SymbolRoleSet)SymbolRole::Implicit; + TRY_TO(IndexCtx.handleReference(PD, Loc, ContD, ContD, roles, + SymbolRelation{(unsigned)SymbolRole::RelationBaseOf, ContD})); + } + return true; + } + + bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *D) { + if (D->isThisDeclarationADefinition()) { + TRY_DECL(D, IndexCtx.handleDecl(D)); + SourceLocation SuperLoc = D->getSuperClassLoc(); + if (auto *SuperD = D->getSuperClass()) { + bool hasSuperTypedef = false; + if (auto *TInfo = D->getSuperClassTInfo()) { + if (auto *TT = TInfo->getType()->getAs<TypedefType>()) { + if (auto *TD = TT->getDecl()) { + hasSuperTypedef = true; + TRY_TO(IndexCtx.handleReference(TD, SuperLoc, D, D, + SymbolRoleSet())); + } + } + } + SymbolRoleSet superRoles{}; + if (hasSuperTypedef) + superRoles |= (SymbolRoleSet)SymbolRole::Implicit; + TRY_TO(IndexCtx.handleReference(SuperD, SuperLoc, D, D, superRoles, + SymbolRelation{(unsigned)SymbolRole::RelationBaseOf, D})); + } + TRY_TO(handleReferencedProtocols(D->getReferencedProtocols(), D, + SuperLoc)); + TRY_TO(IndexCtx.indexDeclContext(D)); + } else { + return IndexCtx.handleReference(D, D->getLocation(), nullptr, + D->getDeclContext(), SymbolRoleSet()); + } + return true; + } + + bool VisitObjCProtocolDecl(const ObjCProtocolDecl *D) { + if (D->isThisDeclarationADefinition()) { + TRY_DECL(D, IndexCtx.handleDecl(D)); + TRY_TO(handleReferencedProtocols(D->getReferencedProtocols(), D, + /*SuperLoc=*/SourceLocation())); + TRY_TO(IndexCtx.indexDeclContext(D)); + } else { + return IndexCtx.handleReference(D, D->getLocation(), nullptr, + D->getDeclContext(), SymbolRoleSet()); + } + return true; + } + + bool VisitObjCImplementationDecl(const ObjCImplementationDecl *D) { + const ObjCInterfaceDecl *Class = D->getClassInterface(); + if (!Class) + return true; + + if (Class->isImplicitInterfaceDecl()) + IndexCtx.handleDecl(Class); + + TRY_DECL(D, IndexCtx.handleDecl(D)); + + // Visit implicit @synthesize property implementations first as their + // location is reported at the name of the @implementation block. This + // serves no purpose other than to simplify the FileCheck-based tests. + for (const auto *I : D->property_impls()) { + if (I->getLocation().isInvalid()) + IndexCtx.indexDecl(I); + } + for (const auto *I : D->decls()) { + if (!isa<ObjCPropertyImplDecl>(I) || + cast<ObjCPropertyImplDecl>(I)->getLocation().isValid()) + IndexCtx.indexDecl(I); + } + + return true; + } + + bool VisitObjCCategoryDecl(const ObjCCategoryDecl *D) { + if (!IndexCtx.shouldIndex(D)) + return true; + const ObjCInterfaceDecl *C = D->getClassInterface(); + if (!C) + return true; + TRY_TO(IndexCtx.handleReference(C, D->getLocation(), D, D, SymbolRoleSet(), + SymbolRelation{ + (unsigned)SymbolRole::RelationExtendedBy, D + })); + SourceLocation CategoryLoc = D->getCategoryNameLoc(); + if (!CategoryLoc.isValid()) + CategoryLoc = D->getLocation(); + TRY_TO(IndexCtx.handleDecl(D, CategoryLoc)); + TRY_TO(handleReferencedProtocols(D->getReferencedProtocols(), D, + /*SuperLoc=*/SourceLocation())); + TRY_TO(IndexCtx.indexDeclContext(D)); + return true; + } + + bool VisitObjCCategoryImplDecl(const ObjCCategoryImplDecl *D) { + const ObjCCategoryDecl *Cat = D->getCategoryDecl(); + if (!Cat) + return true; + const ObjCInterfaceDecl *C = D->getClassInterface(); + if (C) + TRY_TO(IndexCtx.handleReference(C, D->getLocation(), D, D, + SymbolRoleSet())); + SourceLocation CategoryLoc = D->getCategoryNameLoc(); + if (!CategoryLoc.isValid()) + CategoryLoc = D->getLocation(); + TRY_DECL(D, IndexCtx.handleDecl(D, CategoryLoc)); + IndexCtx.indexDeclContext(D); + return true; + } + + bool VisitObjCMethodDecl(const ObjCMethodDecl *D) { + // Methods associated with a property, even user-declared ones, are + // handled when we handle the property. + if (D->isPropertyAccessor()) + return true; + + handleObjCMethod(D); + return true; + } + + bool VisitObjCPropertyDecl(const ObjCPropertyDecl *D) { + if (ObjCMethodDecl *MD = D->getGetterMethodDecl()) + if (MD->getLexicalDeclContext() == D->getLexicalDeclContext()) + handleObjCMethod(MD, D); + if (ObjCMethodDecl *MD = D->getSetterMethodDecl()) + if (MD->getLexicalDeclContext() == D->getLexicalDeclContext()) + handleObjCMethod(MD, D); + TRY_DECL(D, IndexCtx.handleDecl(D)); + if (IBOutletCollectionAttr *attr = D->getAttr<IBOutletCollectionAttr>()) + IndexCtx.indexTypeSourceInfo(attr->getInterfaceLoc(), D, + D->getLexicalDeclContext(), false, true); + IndexCtx.indexTypeSourceInfo(D->getTypeSourceInfo(), D); + return true; + } + + bool VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *D) { + ObjCPropertyDecl *PD = D->getPropertyDecl(); + auto *Container = cast<ObjCImplDecl>(D->getDeclContext()); + SourceLocation Loc = D->getLocation(); + SymbolRoleSet Roles = 0; + SmallVector<SymbolRelation, 1> Relations; + + if (ObjCIvarDecl *ID = D->getPropertyIvarDecl()) + Relations.push_back({(SymbolRoleSet)SymbolRole::RelationAccessorOf, ID}); + if (Loc.isInvalid()) { + Loc = Container->getLocation(); + Roles |= (SymbolRoleSet)SymbolRole::Implicit; + } + TRY_DECL(D, IndexCtx.handleDecl(D, Loc, Roles, Relations)); + + if (D->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) + return true; + + assert(D->getPropertyImplementation() == ObjCPropertyImplDecl::Synthesize); + SymbolRoleSet AccessorMethodRoles = + SymbolRoleSet(SymbolRole::Dynamic) | SymbolRoleSet(SymbolRole::Implicit); + if (ObjCMethodDecl *MD = PD->getGetterMethodDecl()) { + if (MD->isPropertyAccessor() && + !hasUserDefined(MD, Container)) + IndexCtx.handleDecl(MD, Loc, AccessorMethodRoles, {}, Container); + } + if (ObjCMethodDecl *MD = PD->getSetterMethodDecl()) { + if (MD->isPropertyAccessor() && + !hasUserDefined(MD, Container)) + IndexCtx.handleDecl(MD, Loc, AccessorMethodRoles, {}, Container); + } + if (ObjCIvarDecl *IvarD = D->getPropertyIvarDecl()) { + if (IvarD->getSynthesize()) { + // For synthesized ivars, use the location of its name in the + // corresponding @synthesize. If there isn't one, use the containing + // @implementation's location, rather than the property's location, + // otherwise the header file containing the @interface will have different + // indexing contents based on whether the @implementation was present or + // not in the translation unit. + SymbolRoleSet IvarRoles = 0; + SourceLocation IvarLoc = D->getPropertyIvarDeclLoc(); + if (D->getLocation().isInvalid()) { + IvarLoc = Container->getLocation(); + IvarRoles = (SymbolRoleSet)SymbolRole::Implicit; + } else if (D->getLocation() == IvarLoc) { + IvarRoles = (SymbolRoleSet)SymbolRole::Implicit; + } + TRY_DECL(IvarD, IndexCtx.handleDecl(IvarD, IvarLoc, IvarRoles)); + } else { + IndexCtx.handleReference(IvarD, D->getPropertyIvarDeclLoc(), nullptr, + D->getDeclContext(), SymbolRoleSet()); + } + } + return true; + } + + bool VisitNamespaceDecl(const NamespaceDecl *D) { + TRY_DECL(D, IndexCtx.handleDecl(D)); + IndexCtx.indexDeclContext(D); + return true; + } + + bool VisitNamespaceAliasDecl(const NamespaceAliasDecl *D) { + TRY_DECL(D, IndexCtx.handleDecl(D)); + IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), D); + IndexCtx.handleReference(D->getAliasedNamespace(), D->getTargetNameLoc(), D, + D->getLexicalDeclContext()); + return true; + } + + bool VisitUsingDecl(const UsingDecl *D) { + IndexCtx.handleDecl(D); + + const DeclContext *DC = D->getDeclContext()->getRedeclContext(); + const NamedDecl *Parent = dyn_cast<NamedDecl>(DC); + IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), Parent, + D->getLexicalDeclContext()); + for (const auto *I : D->shadows()) + IndexCtx.handleReference(I->getUnderlyingDecl(), D->getLocation(), Parent, + D->getLexicalDeclContext(), SymbolRoleSet()); + return true; + } + + bool VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) { + const DeclContext *DC = D->getDeclContext()->getRedeclContext(); + const NamedDecl *Parent = dyn_cast<NamedDecl>(DC); + + // NNS for the local 'using namespace' directives is visited by the body + // visitor. + if (!D->getParentFunctionOrMethod()) + IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), Parent, + D->getLexicalDeclContext()); + + return IndexCtx.handleReference(D->getNominatedNamespaceAsWritten(), + D->getLocation(), Parent, + D->getLexicalDeclContext(), + SymbolRoleSet()); + } + + bool VisitUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl *D) { + TRY_DECL(D, IndexCtx.handleDecl(D)); + const DeclContext *DC = D->getDeclContext()->getRedeclContext(); + const NamedDecl *Parent = dyn_cast<NamedDecl>(DC); + IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), Parent, + D->getLexicalDeclContext()); + return true; + } + + bool VisitUnresolvedUsingTypenameDecl(const UnresolvedUsingTypenameDecl *D) { + TRY_DECL(D, IndexCtx.handleDecl(D)); + const DeclContext *DC = D->getDeclContext()->getRedeclContext(); + const NamedDecl *Parent = dyn_cast<NamedDecl>(DC); + IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), Parent, + D->getLexicalDeclContext()); + return true; + } + + bool VisitClassTemplateSpecializationDecl(const + ClassTemplateSpecializationDecl *D) { + // FIXME: Notify subsequent callbacks if info comes from implicit + // instantiation. + llvm::PointerUnion<ClassTemplateDecl *, + ClassTemplatePartialSpecializationDecl *> + Template = D->getSpecializedTemplateOrPartial(); + const Decl *SpecializationOf = + Template.is<ClassTemplateDecl *>() + ? (Decl *)Template.get<ClassTemplateDecl *>() + : Template.get<ClassTemplatePartialSpecializationDecl *>(); + if (!D->isThisDeclarationADefinition()) + IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), D); + IndexCtx.indexTagDecl( + D, SymbolRelation(SymbolRoleSet(SymbolRole::RelationSpecializationOf), + SpecializationOf)); + if (TypeSourceInfo *TSI = D->getTypeAsWritten()) + IndexCtx.indexTypeSourceInfo(TSI, /*Parent=*/nullptr, + D->getLexicalDeclContext()); + return true; + } + + static bool shouldIndexTemplateParameterDefaultValue(const NamedDecl *D) { + // We want to index the template parameters only once when indexing the + // canonical declaration. + if (!D) + return false; + if (const auto *FD = dyn_cast<FunctionDecl>(D)) + return FD->getCanonicalDecl() == FD; + else if (const auto *TD = dyn_cast<TagDecl>(D)) + return TD->getCanonicalDecl() == TD; + else if (const auto *VD = dyn_cast<VarDecl>(D)) + return VD->getCanonicalDecl() == VD; + return true; + } + + bool VisitTemplateDecl(const TemplateDecl *D) { + + const NamedDecl *Parent = D->getTemplatedDecl(); + if (!Parent) + return true; + + // Index the default values for the template parameters. + if (D->getTemplateParameters() && + shouldIndexTemplateParameterDefaultValue(Parent)) { + const TemplateParameterList *Params = D->getTemplateParameters(); + for (const NamedDecl *TP : *Params) { + if (IndexCtx.shouldIndexTemplateParameters()) + IndexCtx.handleDecl(TP); + if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(TP)) { + if (TTP->hasDefaultArgument()) + IndexCtx.indexTypeSourceInfo(TTP->getDefaultArgumentInfo(), Parent); + } else if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(TP)) { + if (NTTP->hasDefaultArgument()) + IndexCtx.indexBody(NTTP->getDefaultArgument(), Parent); + } else if (const auto *TTPD = dyn_cast<TemplateTemplateParmDecl>(TP)) { + if (TTPD->hasDefaultArgument()) + handleTemplateArgumentLoc(TTPD->getDefaultArgument(), Parent, + TP->getLexicalDeclContext()); + } + } + } + + return Visit(Parent); + } + + bool VisitFriendDecl(const FriendDecl *D) { + if (auto ND = D->getFriendDecl()) { + // FIXME: Ignore a class template in a dependent context, these are not + // linked properly with their redeclarations, ending up with duplicate + // USRs. + // See comment "Friend templates are visible in fairly strange ways." in + // SemaTemplate.cpp which precedes code that prevents the friend template + // from becoming visible from the enclosing context. + if (isa<ClassTemplateDecl>(ND) && D->getDeclContext()->isDependentContext()) + return true; + return Visit(ND); + } + if (auto Ty = D->getFriendType()) { + IndexCtx.indexTypeSourceInfo(Ty, cast<NamedDecl>(D->getDeclContext())); + } + return true; + } + + bool VisitImportDecl(const ImportDecl *D) { + return IndexCtx.importedModule(D); + } + + bool VisitStaticAssertDecl(const StaticAssertDecl *D) { + IndexCtx.indexBody(D->getAssertExpr(), + dyn_cast<NamedDecl>(D->getDeclContext()), + D->getLexicalDeclContext()); + return true; + } +}; + +} // anonymous namespace + +bool IndexingContext::indexDecl(const Decl *D) { + if (D->isImplicit() && shouldIgnoreIfImplicit(D)) + return true; + + if (isTemplateImplicitInstantiation(D) && !shouldIndexImplicitInstantiation()) + return true; + + IndexingDeclVisitor Visitor(*this); + bool ShouldContinue = Visitor.Visit(D); + if (!ShouldContinue) + return false; + + if (!Visitor.Handled && isa<DeclContext>(D)) + return indexDeclContext(cast<DeclContext>(D)); + + return true; +} + +bool IndexingContext::indexDeclContext(const DeclContext *DC) { + for (const auto *I : DC->decls()) + if (!indexDecl(I)) + return false; + return true; +} + +bool IndexingContext::indexTopLevelDecl(const Decl *D) { + if (D->getLocation().isInvalid()) + return true; + + if (isa<ObjCMethodDecl>(D)) + return true; // Wait for the objc container. + + return indexDecl(D); +} + +bool IndexingContext::indexDeclGroupRef(DeclGroupRef DG) { + for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) + if (!indexTopLevelDecl(*I)) + return false; + return true; +} diff --git a/clang/lib/Index/IndexSymbol.cpp b/clang/lib/Index/IndexSymbol.cpp new file mode 100644 index 000000000000..5165567ff75e --- /dev/null +++ b/clang/lib/Index/IndexSymbol.cpp @@ -0,0 +1,584 @@ +//===--- IndexSymbol.cpp - Types and functions for indexing symbols -------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexSymbol.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/PrettyPrinter.h" +#include "clang/Lex/MacroInfo.h" + +using namespace clang; +using namespace clang::index; + +/// \returns true if \c D is a subclass of 'XCTestCase'. +static bool isUnitTestCase(const ObjCInterfaceDecl *D) { + if (!D) + return false; + while (const ObjCInterfaceDecl *SuperD = D->getSuperClass()) { + if (SuperD->getName() == "XCTestCase") + return true; + D = SuperD; + } + return false; +} + +/// \returns true if \c D is in a subclass of 'XCTestCase', returns void, has +/// no parameters, and its name starts with 'test'. +static bool isUnitTest(const ObjCMethodDecl *D) { + if (!D->parameters().empty()) + return false; + if (!D->getReturnType()->isVoidType()) + return false; + if (!D->getSelector().getNameForSlot(0).startswith("test")) + return false; + return isUnitTestCase(D->getClassInterface()); +} + +static void checkForIBOutlets(const Decl *D, SymbolPropertySet &PropSet) { + if (D->hasAttr<IBOutletAttr>()) { + PropSet |= (SymbolPropertySet)SymbolProperty::IBAnnotated; + } else if (D->hasAttr<IBOutletCollectionAttr>()) { + PropSet |= (SymbolPropertySet)SymbolProperty::IBAnnotated; + PropSet |= (SymbolPropertySet)SymbolProperty::IBOutletCollection; + } +} + +bool index::isFunctionLocalSymbol(const Decl *D) { + assert(D); + + if (isa<ParmVarDecl>(D)) + return true; + + if (isa<ObjCTypeParamDecl>(D)) + return true; + + if (isa<UsingDirectiveDecl>(D)) + return false; + if (!D->getParentFunctionOrMethod()) + return false; + + if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) { + switch (ND->getFormalLinkage()) { + case NoLinkage: + case InternalLinkage: + return true; + case VisibleNoLinkage: + case UniqueExternalLinkage: + case ModuleInternalLinkage: + llvm_unreachable("Not a sema linkage"); + case ModuleLinkage: + case ExternalLinkage: + return false; + } + } + + return true; +} + +SymbolInfo index::getSymbolInfo(const Decl *D) { + assert(D); + SymbolInfo Info; + Info.Kind = SymbolKind::Unknown; + Info.SubKind = SymbolSubKind::None; + Info.Properties = SymbolPropertySet(); + Info.Lang = SymbolLanguage::C; + + if (isFunctionLocalSymbol(D)) { + Info.Properties |= (SymbolPropertySet)SymbolProperty::Local; + } + if (isa<ObjCProtocolDecl>(D->getDeclContext())) { + Info.Properties |= (SymbolPropertySet)SymbolProperty::ProtocolInterface; + } + + if (auto *VT = dyn_cast<VarTemplateDecl>(D)) { + Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; + Info.Lang = SymbolLanguage::CXX; + // All other fields are filled from the templated decl. + D = VT->getTemplatedDecl(); + } + + if (const TagDecl *TD = dyn_cast<TagDecl>(D)) { + switch (TD->getTagKind()) { + case TTK_Struct: + Info.Kind = SymbolKind::Struct; break; + case TTK_Union: + Info.Kind = SymbolKind::Union; break; + case TTK_Class: + Info.Kind = SymbolKind::Class; + Info.Lang = SymbolLanguage::CXX; + break; + case TTK_Interface: + Info.Kind = SymbolKind::Protocol; + Info.Lang = SymbolLanguage::CXX; + break; + case TTK_Enum: + Info.Kind = SymbolKind::Enum; break; + } + + if (const CXXRecordDecl *CXXRec = dyn_cast<CXXRecordDecl>(D)) { + if (!CXXRec->isCLike()) { + Info.Lang = SymbolLanguage::CXX; + if (CXXRec->getDescribedClassTemplate()) { + Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; + } + } + } + + if (isa<ClassTemplatePartialSpecializationDecl>(D)) { + Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; + Info.Properties |= + (SymbolPropertySet)SymbolProperty::TemplatePartialSpecialization; + } else if (isa<ClassTemplateSpecializationDecl>(D)) { + Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; + Info.Properties |= + (SymbolPropertySet)SymbolProperty::TemplateSpecialization; + } + + } else if (auto *VD = dyn_cast<VarDecl>(D)) { + Info.Kind = SymbolKind::Variable; + if (isa<ParmVarDecl>(D)) { + Info.Kind = SymbolKind::Parameter; + } else if (isa<CXXRecordDecl>(D->getDeclContext())) { + Info.Kind = SymbolKind::StaticProperty; + Info.Lang = SymbolLanguage::CXX; + } + + if (isa<VarTemplatePartialSpecializationDecl>(D)) { + Info.Lang = SymbolLanguage::CXX; + Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; + Info.Properties |= + (SymbolPropertySet)SymbolProperty::TemplatePartialSpecialization; + } else if (isa<VarTemplateSpecializationDecl>(D)) { + Info.Lang = SymbolLanguage::CXX; + Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; + Info.Properties |= + (SymbolPropertySet)SymbolProperty::TemplateSpecialization; + } else if (VD->getDescribedVarTemplate()) { + Info.Lang = SymbolLanguage::CXX; + Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; + } + + } else { + switch (D->getKind()) { + case Decl::Import: + Info.Kind = SymbolKind::Module; + break; + case Decl::Typedef: + Info.Kind = SymbolKind::TypeAlias; break; // Lang = C + case Decl::Function: + Info.Kind = SymbolKind::Function; + break; + case Decl::Field: + case Decl::IndirectField: + Info.Kind = SymbolKind::Field; + if (const CXXRecordDecl * + CXXRec = dyn_cast<CXXRecordDecl>(D->getDeclContext())) { + if (!CXXRec->isCLike()) + Info.Lang = SymbolLanguage::CXX; + } + break; + case Decl::EnumConstant: + Info.Kind = SymbolKind::EnumConstant; break; + case Decl::ObjCInterface: + case Decl::ObjCImplementation: { + Info.Kind = SymbolKind::Class; + Info.Lang = SymbolLanguage::ObjC; + const ObjCInterfaceDecl *ClsD = dyn_cast<ObjCInterfaceDecl>(D); + if (!ClsD) + ClsD = cast<ObjCImplementationDecl>(D)->getClassInterface(); + if (isUnitTestCase(ClsD)) + Info.Properties |= (SymbolPropertySet)SymbolProperty::UnitTest; + break; + } + case Decl::ObjCProtocol: + Info.Kind = SymbolKind::Protocol; + Info.Lang = SymbolLanguage::ObjC; + break; + case Decl::ObjCCategory: + case Decl::ObjCCategoryImpl: { + Info.Kind = SymbolKind::Extension; + Info.Lang = SymbolLanguage::ObjC; + const ObjCInterfaceDecl *ClsD = nullptr; + if (auto *CatD = dyn_cast<ObjCCategoryDecl>(D)) + ClsD = CatD->getClassInterface(); + else + ClsD = cast<ObjCCategoryImplDecl>(D)->getClassInterface(); + if (isUnitTestCase(ClsD)) + Info.Properties |= (SymbolPropertySet)SymbolProperty::UnitTest; + break; + } + case Decl::ObjCMethod: { + const ObjCMethodDecl *MD = cast<ObjCMethodDecl>(D); + Info.Kind = MD->isInstanceMethod() ? SymbolKind::InstanceMethod : SymbolKind::ClassMethod; + if (MD->isPropertyAccessor()) { + if (MD->param_size()) + Info.SubKind = SymbolSubKind::AccessorSetter; + else + Info.SubKind = SymbolSubKind::AccessorGetter; + } + Info.Lang = SymbolLanguage::ObjC; + if (isUnitTest(MD)) + Info.Properties |= (SymbolPropertySet)SymbolProperty::UnitTest; + if (D->hasAttr<IBActionAttr>()) + Info.Properties |= (SymbolPropertySet)SymbolProperty::IBAnnotated; + break; + } + case Decl::ObjCProperty: + Info.Kind = SymbolKind::InstanceProperty; + Info.Lang = SymbolLanguage::ObjC; + checkForIBOutlets(D, Info.Properties); + if (auto *Annot = D->getAttr<AnnotateAttr>()) { + if (Annot->getAnnotation() == "gk_inspectable") + Info.Properties |= (SymbolPropertySet)SymbolProperty::GKInspectable; + } + break; + case Decl::ObjCIvar: + Info.Kind = SymbolKind::Field; + Info.Lang = SymbolLanguage::ObjC; + checkForIBOutlets(D, Info.Properties); + break; + case Decl::Namespace: + Info.Kind = SymbolKind::Namespace; + Info.Lang = SymbolLanguage::CXX; + break; + case Decl::NamespaceAlias: + Info.Kind = SymbolKind::NamespaceAlias; + Info.Lang = SymbolLanguage::CXX; + break; + case Decl::CXXConstructor: { + Info.Kind = SymbolKind::Constructor; + Info.Lang = SymbolLanguage::CXX; + auto *CD = cast<CXXConstructorDecl>(D); + if (CD->isCopyConstructor()) + Info.SubKind = SymbolSubKind::CXXCopyConstructor; + else if (CD->isMoveConstructor()) + Info.SubKind = SymbolSubKind::CXXMoveConstructor; + break; + } + case Decl::CXXDestructor: + Info.Kind = SymbolKind::Destructor; + Info.Lang = SymbolLanguage::CXX; + break; + case Decl::CXXConversion: + Info.Kind = SymbolKind::ConversionFunction; + Info.Lang = SymbolLanguage::CXX; + break; + case Decl::CXXMethod: { + const CXXMethodDecl *MD = cast<CXXMethodDecl>(D); + if (MD->isStatic()) + Info.Kind = SymbolKind::StaticMethod; + else + Info.Kind = SymbolKind::InstanceMethod; + Info.Lang = SymbolLanguage::CXX; + break; + } + case Decl::ClassTemplate: + Info.Kind = SymbolKind::Class; + Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; + Info.Lang = SymbolLanguage::CXX; + break; + case Decl::FunctionTemplate: + Info.Kind = SymbolKind::Function; + Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; + Info.Lang = SymbolLanguage::CXX; + if (const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>( + cast<FunctionTemplateDecl>(D)->getTemplatedDecl())) { + if (isa<CXXConstructorDecl>(MD)) + Info.Kind = SymbolKind::Constructor; + else if (isa<CXXDestructorDecl>(MD)) + Info.Kind = SymbolKind::Destructor; + else if (isa<CXXConversionDecl>(MD)) + Info.Kind = SymbolKind::ConversionFunction; + else { + if (MD->isStatic()) + Info.Kind = SymbolKind::StaticMethod; + else + Info.Kind = SymbolKind::InstanceMethod; + } + } + break; + case Decl::TypeAliasTemplate: + Info.Kind = SymbolKind::TypeAlias; + Info.Lang = SymbolLanguage::CXX; + Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; + break; + case Decl::TypeAlias: + Info.Kind = SymbolKind::TypeAlias; + Info.Lang = SymbolLanguage::CXX; + break; + case Decl::UnresolvedUsingTypename: + Info.Kind = SymbolKind::Using; + Info.SubKind = SymbolSubKind::UsingTypename; + Info.Lang = SymbolLanguage::CXX; + Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; + break; + case Decl::UnresolvedUsingValue: + Info.Kind = SymbolKind::Using; + Info.SubKind = SymbolSubKind::UsingValue; + Info.Lang = SymbolLanguage::CXX; + Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; + break; + case Decl::Using: + Info.Kind = SymbolKind::Using; + Info.Lang = SymbolLanguage::CXX; + break; + case Decl::Binding: + Info.Kind = SymbolKind::Variable; + Info.Lang = SymbolLanguage::CXX; + break; + case Decl::MSProperty: + Info.Kind = SymbolKind::InstanceProperty; + if (const CXXRecordDecl *CXXRec = + dyn_cast<CXXRecordDecl>(D->getDeclContext())) { + if (!CXXRec->isCLike()) + Info.Lang = SymbolLanguage::CXX; + } + break; + case Decl::ClassTemplatePartialSpecialization: + case Decl::ClassScopeFunctionSpecialization: + case Decl::ClassTemplateSpecialization: + case Decl::CXXRecord: + case Decl::Enum: + case Decl::Record: + llvm_unreachable("records handled before"); + break; + case Decl::VarTemplateSpecialization: + case Decl::VarTemplatePartialSpecialization: + case Decl::ImplicitParam: + case Decl::ParmVar: + case Decl::Var: + case Decl::VarTemplate: + llvm_unreachable("variables handled before"); + break; + // Other decls get the 'unknown' kind. + default: + break; + } + } + + if (Info.Kind == SymbolKind::Unknown) + return Info; + + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + if (FD->getTemplatedKind() == + FunctionDecl::TK_FunctionTemplateSpecialization) { + Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; + Info.Properties |= + (SymbolPropertySet)SymbolProperty::TemplateSpecialization; + } + } + + if (Info.Properties & (SymbolPropertySet)SymbolProperty::Generic) + Info.Lang = SymbolLanguage::CXX; + + if (auto *attr = D->getExternalSourceSymbolAttr()) { + if (attr->getLanguage() == "Swift") + Info.Lang = SymbolLanguage::Swift; + } + + return Info; +} + +SymbolInfo index::getSymbolInfoForMacro(const MacroInfo &) { + SymbolInfo Info; + Info.Kind = SymbolKind::Macro; + Info.SubKind = SymbolSubKind::None; + Info.Properties = SymbolPropertySet(); + Info.Lang = SymbolLanguage::C; + return Info; +} + +bool index::applyForEachSymbolRoleInterruptible(SymbolRoleSet Roles, + llvm::function_ref<bool(SymbolRole)> Fn) { +#define APPLY_FOR_ROLE(Role) \ + if (Roles & (unsigned)SymbolRole::Role) \ + if (!Fn(SymbolRole::Role)) \ + return false; + + APPLY_FOR_ROLE(Declaration); + APPLY_FOR_ROLE(Definition); + APPLY_FOR_ROLE(Reference); + APPLY_FOR_ROLE(Read); + APPLY_FOR_ROLE(Write); + APPLY_FOR_ROLE(Call); + APPLY_FOR_ROLE(Dynamic); + APPLY_FOR_ROLE(AddressOf); + APPLY_FOR_ROLE(Implicit); + APPLY_FOR_ROLE(Undefinition); + APPLY_FOR_ROLE(RelationChildOf); + APPLY_FOR_ROLE(RelationBaseOf); + APPLY_FOR_ROLE(RelationOverrideOf); + APPLY_FOR_ROLE(RelationReceivedBy); + APPLY_FOR_ROLE(RelationCalledBy); + APPLY_FOR_ROLE(RelationExtendedBy); + APPLY_FOR_ROLE(RelationAccessorOf); + APPLY_FOR_ROLE(RelationContainedBy); + APPLY_FOR_ROLE(RelationIBTypeOf); + APPLY_FOR_ROLE(RelationSpecializationOf); + APPLY_FOR_ROLE(NameReference); + +#undef APPLY_FOR_ROLE + + return true; +} + +void index::applyForEachSymbolRole(SymbolRoleSet Roles, + llvm::function_ref<void(SymbolRole)> Fn) { + applyForEachSymbolRoleInterruptible(Roles, [&](SymbolRole r) -> bool { + Fn(r); + return true; + }); +} + +void index::printSymbolRoles(SymbolRoleSet Roles, raw_ostream &OS) { + bool VisitedOnce = false; + applyForEachSymbolRole(Roles, [&](SymbolRole Role) { + if (VisitedOnce) + OS << ','; + else + VisitedOnce = true; + switch (Role) { + case SymbolRole::Declaration: OS << "Decl"; break; + case SymbolRole::Definition: OS << "Def"; break; + case SymbolRole::Reference: OS << "Ref"; break; + case SymbolRole::Read: OS << "Read"; break; + case SymbolRole::Write: OS << "Writ"; break; + case SymbolRole::Call: OS << "Call"; break; + case SymbolRole::Dynamic: OS << "Dyn"; break; + case SymbolRole::AddressOf: OS << "Addr"; break; + case SymbolRole::Implicit: OS << "Impl"; break; + case SymbolRole::Undefinition: OS << "Undef"; break; + case SymbolRole::RelationChildOf: OS << "RelChild"; break; + case SymbolRole::RelationBaseOf: OS << "RelBase"; break; + case SymbolRole::RelationOverrideOf: OS << "RelOver"; break; + case SymbolRole::RelationReceivedBy: OS << "RelRec"; break; + case SymbolRole::RelationCalledBy: OS << "RelCall"; break; + case SymbolRole::RelationExtendedBy: OS << "RelExt"; break; + case SymbolRole::RelationAccessorOf: OS << "RelAcc"; break; + case SymbolRole::RelationContainedBy: OS << "RelCont"; break; + case SymbolRole::RelationIBTypeOf: OS << "RelIBType"; break; + case SymbolRole::RelationSpecializationOf: OS << "RelSpecialization"; break; + case SymbolRole::NameReference: OS << "NameReference"; break; + } + }); +} + +bool index::printSymbolName(const Decl *D, const LangOptions &LO, + raw_ostream &OS) { + if (auto *ND = dyn_cast<NamedDecl>(D)) { + PrintingPolicy Policy(LO); + // Forward references can have different template argument names. Suppress + // the template argument names in constructors to make their name more + // stable. + Policy.SuppressTemplateArgsInCXXConstructors = true; + DeclarationName DeclName = ND->getDeclName(); + if (DeclName.isEmpty()) + return true; + DeclName.print(OS, Policy); + return false; + } else { + return true; + } +} + +StringRef index::getSymbolKindString(SymbolKind K) { + switch (K) { + case SymbolKind::Unknown: return "<unknown>"; + case SymbolKind::Module: return "module"; + case SymbolKind::Namespace: return "namespace"; + case SymbolKind::NamespaceAlias: return "namespace-alias"; + case SymbolKind::Macro: return "macro"; + case SymbolKind::Enum: return "enum"; + case SymbolKind::Struct: return "struct"; + case SymbolKind::Class: return "class"; + case SymbolKind::Protocol: return "protocol"; + case SymbolKind::Extension: return "extension"; + case SymbolKind::Union: return "union"; + case SymbolKind::TypeAlias: return "type-alias"; + case SymbolKind::Function: return "function"; + case SymbolKind::Variable: return "variable"; + case SymbolKind::Field: return "field"; + case SymbolKind::EnumConstant: return "enumerator"; + case SymbolKind::InstanceMethod: return "instance-method"; + case SymbolKind::ClassMethod: return "class-method"; + case SymbolKind::StaticMethod: return "static-method"; + case SymbolKind::InstanceProperty: return "instance-property"; + case SymbolKind::ClassProperty: return "class-property"; + case SymbolKind::StaticProperty: return "static-property"; + case SymbolKind::Constructor: return "constructor"; + case SymbolKind::Destructor: return "destructor"; + case SymbolKind::ConversionFunction: return "conversion-func"; + case SymbolKind::Parameter: return "param"; + case SymbolKind::Using: return "using"; + } + llvm_unreachable("invalid symbol kind"); +} + +StringRef index::getSymbolSubKindString(SymbolSubKind K) { + switch (K) { + case SymbolSubKind::None: return "<none>"; + case SymbolSubKind::CXXCopyConstructor: return "cxx-copy-ctor"; + case SymbolSubKind::CXXMoveConstructor: return "cxx-move-ctor"; + case SymbolSubKind::AccessorGetter: return "acc-get"; + case SymbolSubKind::AccessorSetter: return "acc-set"; + case SymbolSubKind::UsingTypename: return "using-typename"; + case SymbolSubKind::UsingValue: return "using-value"; + } + llvm_unreachable("invalid symbol subkind"); +} + +StringRef index::getSymbolLanguageString(SymbolLanguage K) { + switch (K) { + case SymbolLanguage::C: return "C"; + case SymbolLanguage::ObjC: return "ObjC"; + case SymbolLanguage::CXX: return "C++"; + case SymbolLanguage::Swift: return "Swift"; + } + llvm_unreachable("invalid symbol language kind"); +} + +void index::applyForEachSymbolProperty(SymbolPropertySet Props, + llvm::function_ref<void(SymbolProperty)> Fn) { +#define APPLY_FOR_PROPERTY(K) \ + if (Props & (SymbolPropertySet)SymbolProperty::K) \ + Fn(SymbolProperty::K) + + APPLY_FOR_PROPERTY(Generic); + APPLY_FOR_PROPERTY(TemplatePartialSpecialization); + APPLY_FOR_PROPERTY(TemplateSpecialization); + APPLY_FOR_PROPERTY(UnitTest); + APPLY_FOR_PROPERTY(IBAnnotated); + APPLY_FOR_PROPERTY(IBOutletCollection); + APPLY_FOR_PROPERTY(GKInspectable); + APPLY_FOR_PROPERTY(Local); + APPLY_FOR_PROPERTY(ProtocolInterface); + +#undef APPLY_FOR_PROPERTY +} + +void index::printSymbolProperties(SymbolPropertySet Props, raw_ostream &OS) { + bool VisitedOnce = false; + applyForEachSymbolProperty(Props, [&](SymbolProperty Prop) { + if (VisitedOnce) + OS << ','; + else + VisitedOnce = true; + switch (Prop) { + case SymbolProperty::Generic: OS << "Gen"; break; + case SymbolProperty::TemplatePartialSpecialization: OS << "TPS"; break; + case SymbolProperty::TemplateSpecialization: OS << "TS"; break; + case SymbolProperty::UnitTest: OS << "test"; break; + case SymbolProperty::IBAnnotated: OS << "IB"; break; + case SymbolProperty::IBOutletCollection: OS << "IBColl"; break; + case SymbolProperty::GKInspectable: OS << "GKI"; break; + case SymbolProperty::Local: OS << "local"; break; + case SymbolProperty::ProtocolInterface: OS << "protocol"; break; + } + }); +} diff --git a/clang/lib/Index/IndexTypeSourceInfo.cpp b/clang/lib/Index/IndexTypeSourceInfo.cpp new file mode 100644 index 000000000000..959d5f1197fe --- /dev/null +++ b/clang/lib/Index/IndexTypeSourceInfo.cpp @@ -0,0 +1,286 @@ +//===- IndexTypeSourceInfo.cpp - Indexing types ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "IndexingContext.h" +#include "clang/AST/RecursiveASTVisitor.h" + +using namespace clang; +using namespace index; + +namespace { + +class TypeIndexer : public RecursiveASTVisitor<TypeIndexer> { + IndexingContext &IndexCtx; + const NamedDecl *Parent; + const DeclContext *ParentDC; + bool IsBase; + SmallVector<SymbolRelation, 3> Relations; + + typedef RecursiveASTVisitor<TypeIndexer> base; + +public: + TypeIndexer(IndexingContext &indexCtx, const NamedDecl *parent, + const DeclContext *DC, bool isBase, bool isIBType) + : IndexCtx(indexCtx), Parent(parent), ParentDC(DC), IsBase(isBase) { + if (IsBase) { + assert(Parent); + Relations.emplace_back((unsigned)SymbolRole::RelationBaseOf, Parent); + } + if (isIBType) { + assert(Parent); + Relations.emplace_back((unsigned)SymbolRole::RelationIBTypeOf, Parent); + } + } + + bool shouldWalkTypesOfTypeLocs() const { return false; } + +#define TRY_TO(CALL_EXPR) \ + do { \ + if (!CALL_EXPR) \ + return false; \ + } while (0) + + bool VisitTemplateTypeParmTypeLoc(TemplateTypeParmTypeLoc TTPL) { + SourceLocation Loc = TTPL.getNameLoc(); + TemplateTypeParmDecl *TTPD = TTPL.getDecl(); + return IndexCtx.handleReference(TTPD, Loc, Parent, ParentDC, + SymbolRoleSet()); + } + + bool VisitTypedefTypeLoc(TypedefTypeLoc TL) { + SourceLocation Loc = TL.getNameLoc(); + TypedefNameDecl *ND = TL.getTypedefNameDecl(); + if (ND->isTransparentTag()) { + TagDecl *Underlying = ND->getUnderlyingType()->getAsTagDecl(); + return IndexCtx.handleReference(Underlying, Loc, Parent, + ParentDC, SymbolRoleSet(), Relations); + } + if (IsBase) { + TRY_TO(IndexCtx.handleReference(ND, Loc, + Parent, ParentDC, SymbolRoleSet())); + if (auto *CD = TL.getType()->getAsCXXRecordDecl()) { + TRY_TO(IndexCtx.handleReference(CD, Loc, Parent, ParentDC, + (unsigned)SymbolRole::Implicit, + Relations)); + } + } else { + TRY_TO(IndexCtx.handleReference(ND, Loc, + Parent, ParentDC, SymbolRoleSet(), + Relations)); + } + return true; + } + + bool traverseParamVarHelper(ParmVarDecl *D) { + TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc())); + if (D->getTypeSourceInfo()) + TRY_TO(TraverseTypeLoc(D->getTypeSourceInfo()->getTypeLoc())); + return true; + } + + bool TraverseParmVarDecl(ParmVarDecl *D) { + // Avoid visiting default arguments from the definition that were already + // visited in the declaration. + // FIXME: A free function definition can have default arguments. + // Avoiding double visitaiton of default arguments should be handled by the + // visitor probably with a bit in the AST to indicate if the attached + // default argument was 'inherited' or written in source. + if (auto FD = dyn_cast<FunctionDecl>(D->getDeclContext())) { + if (FD->isThisDeclarationADefinition()) { + return traverseParamVarHelper(D); + } + } + + return base::TraverseParmVarDecl(D); + } + + bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) { + IndexCtx.indexNestedNameSpecifierLoc(NNS, Parent, ParentDC); + return true; + } + + bool VisitTagTypeLoc(TagTypeLoc TL) { + TagDecl *D = TL.getDecl(); + if (!IndexCtx.shouldIndexFunctionLocalSymbols() && + D->getParentFunctionOrMethod()) + return true; + + if (TL.isDefinition()) { + IndexCtx.indexTagDecl(D); + return true; + } + + return IndexCtx.handleReference(D, TL.getNameLoc(), + Parent, ParentDC, SymbolRoleSet(), + Relations); + } + + bool VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc TL) { + return IndexCtx.handleReference(TL.getIFaceDecl(), TL.getNameLoc(), + Parent, ParentDC, SymbolRoleSet(), Relations); + } + + bool VisitObjCObjectTypeLoc(ObjCObjectTypeLoc TL) { + for (unsigned i = 0, e = TL.getNumProtocols(); i != e; ++i) { + IndexCtx.handleReference(TL.getProtocol(i), TL.getProtocolLoc(i), + Parent, ParentDC, SymbolRoleSet(), Relations); + } + return true; + } + + void HandleTemplateSpecializationTypeLoc(TemplateName TemplName, + SourceLocation TemplNameLoc, + CXXRecordDecl *ResolvedClass, + bool IsTypeAlias) { + // In presence of type aliases, the resolved class was never written in + // the code so don't report it. + if (!IsTypeAlias && ResolvedClass && + (!ResolvedClass->isImplicit() || + IndexCtx.shouldIndexImplicitInstantiation())) { + IndexCtx.handleReference(ResolvedClass, TemplNameLoc, Parent, ParentDC, + SymbolRoleSet(), Relations); + } else if (const TemplateDecl *D = TemplName.getAsTemplateDecl()) { + IndexCtx.handleReference(D, TemplNameLoc, Parent, ParentDC, + SymbolRoleSet(), Relations); + } + } + + bool VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc TL) { + auto *T = TL.getTypePtr(); + if (!T) + return true; + HandleTemplateSpecializationTypeLoc( + T->getTemplateName(), TL.getTemplateNameLoc(), T->getAsCXXRecordDecl(), + T->isTypeAlias()); + return true; + } + + bool VisitDeducedTemplateSpecializationTypeLoc(DeducedTemplateSpecializationTypeLoc TL) { + auto *T = TL.getTypePtr(); + if (!T) + return true; + HandleTemplateSpecializationTypeLoc( + T->getTemplateName(), TL.getTemplateNameLoc(), T->getAsCXXRecordDecl(), + /*IsTypeAlias=*/false); + return true; + } + + bool VisitDependentNameTypeLoc(DependentNameTypeLoc TL) { + const DependentNameType *DNT = TL.getTypePtr(); + const NestedNameSpecifier *NNS = DNT->getQualifier(); + const Type *T = NNS->getAsType(); + if (!T) + return true; + const TemplateSpecializationType *TST = + T->getAs<TemplateSpecializationType>(); + if (!TST) + return true; + TemplateName TN = TST->getTemplateName(); + const ClassTemplateDecl *TD = + dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl()); + if (!TD) + return true; + CXXRecordDecl *RD = TD->getTemplatedDecl(); + if (!RD->hasDefinition()) + return true; + RD = RD->getDefinition(); + DeclarationName Name(DNT->getIdentifier()); + std::vector<const NamedDecl *> Symbols = RD->lookupDependentName( + Name, [](const NamedDecl *ND) { return isa<TypeDecl>(ND); }); + if (Symbols.size() != 1) + return true; + return IndexCtx.handleReference(Symbols[0], TL.getNameLoc(), Parent, + ParentDC, SymbolRoleSet(), Relations); + } + + bool TraverseStmt(Stmt *S) { + IndexCtx.indexBody(S, Parent, ParentDC); + return true; + } +}; + +} // anonymous namespace + +void IndexingContext::indexTypeSourceInfo(TypeSourceInfo *TInfo, + const NamedDecl *Parent, + const DeclContext *DC, + bool isBase, + bool isIBType) { + if (!TInfo || TInfo->getTypeLoc().isNull()) + return; + + indexTypeLoc(TInfo->getTypeLoc(), Parent, DC, isBase, isIBType); +} + +void IndexingContext::indexTypeLoc(TypeLoc TL, + const NamedDecl *Parent, + const DeclContext *DC, + bool isBase, + bool isIBType) { + if (TL.isNull()) + return; + + if (!DC) + DC = Parent->getLexicalDeclContext(); + TypeIndexer(*this, Parent, DC, isBase, isIBType).TraverseTypeLoc(TL); +} + +void IndexingContext::indexNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS, + const NamedDecl *Parent, + const DeclContext *DC) { + if (!NNS) + return; + + if (NestedNameSpecifierLoc Prefix = NNS.getPrefix()) + indexNestedNameSpecifierLoc(Prefix, Parent, DC); + + if (!DC) + DC = Parent->getLexicalDeclContext(); + SourceLocation Loc = NNS.getLocalBeginLoc(); + + switch (NNS.getNestedNameSpecifier()->getKind()) { + case NestedNameSpecifier::Identifier: + case NestedNameSpecifier::Global: + case NestedNameSpecifier::Super: + break; + + case NestedNameSpecifier::Namespace: + handleReference(NNS.getNestedNameSpecifier()->getAsNamespace(), + Loc, Parent, DC, SymbolRoleSet()); + break; + case NestedNameSpecifier::NamespaceAlias: + handleReference(NNS.getNestedNameSpecifier()->getAsNamespaceAlias(), + Loc, Parent, DC, SymbolRoleSet()); + break; + + case NestedNameSpecifier::TypeSpec: + case NestedNameSpecifier::TypeSpecWithTemplate: + indexTypeLoc(NNS.getTypeLoc(), Parent, DC); + break; + } +} + +void IndexingContext::indexTagDecl(const TagDecl *D, + ArrayRef<SymbolRelation> Relations) { + if (!shouldIndex(D)) + return; + if (!shouldIndexFunctionLocalSymbols() && isFunctionLocalSymbol(D)) + return; + + if (handleDecl(D, /*Roles=*/SymbolRoleSet(), Relations)) { + if (D->isThisDeclarationADefinition()) { + indexNestedNameSpecifierLoc(D->getQualifierLoc(), D); + if (auto CXXRD = dyn_cast<CXXRecordDecl>(D)) { + for (const auto &I : CXXRD->bases()) { + indexTypeSourceInfo(I.getTypeSourceInfo(), CXXRD, CXXRD, /*isBase=*/true); + } + } + indexDeclContext(D); + } + } +} diff --git a/clang/lib/Index/IndexingAction.cpp b/clang/lib/Index/IndexingAction.cpp new file mode 100644 index 000000000000..6d6133e89d86 --- /dev/null +++ b/clang/lib/Index/IndexingAction.cpp @@ -0,0 +1,211 @@ +//===- IndexingAction.cpp - Frontend index action -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexingAction.h" +#include "IndexingContext.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Frontend/MultiplexConsumer.h" +#include "clang/Index/IndexDataConsumer.h" +#include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Serialization/ASTReader.h" +#include "llvm/ADT/STLExtras.h" +#include <memory> + +using namespace clang; +using namespace clang::index; + +namespace { + +class IndexPPCallbacks final : public PPCallbacks { + std::shared_ptr<IndexingContext> IndexCtx; + +public: + IndexPPCallbacks(std::shared_ptr<IndexingContext> IndexCtx) + : IndexCtx(std::move(IndexCtx)) {} + + void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD, + SourceRange Range, const MacroArgs *Args) override { + IndexCtx->handleMacroReference(*MacroNameTok.getIdentifierInfo(), + Range.getBegin(), *MD.getMacroInfo()); + } + + void MacroDefined(const Token &MacroNameTok, + const MacroDirective *MD) override { + IndexCtx->handleMacroDefined(*MacroNameTok.getIdentifierInfo(), + MacroNameTok.getLocation(), + *MD->getMacroInfo()); + } + + void MacroUndefined(const Token &MacroNameTok, const MacroDefinition &MD, + const MacroDirective *Undef) override { + if (!MD.getMacroInfo()) // Ignore noop #undef. + return; + IndexCtx->handleMacroUndefined(*MacroNameTok.getIdentifierInfo(), + MacroNameTok.getLocation(), + *MD.getMacroInfo()); + } +}; + +class IndexASTConsumer final : public ASTConsumer { + std::shared_ptr<IndexDataConsumer> DataConsumer; + std::shared_ptr<IndexingContext> IndexCtx; + std::shared_ptr<Preprocessor> PP; + std::function<bool(const Decl *)> ShouldSkipFunctionBody; + +public: + IndexASTConsumer(std::shared_ptr<IndexDataConsumer> DataConsumer, + const IndexingOptions &Opts, + std::shared_ptr<Preprocessor> PP, + std::function<bool(const Decl *)> ShouldSkipFunctionBody) + : DataConsumer(std::move(DataConsumer)), + IndexCtx(new IndexingContext(Opts, *this->DataConsumer)), + PP(std::move(PP)), + ShouldSkipFunctionBody(std::move(ShouldSkipFunctionBody)) { + assert(this->DataConsumer != nullptr); + assert(this->PP != nullptr); + } + +protected: + void Initialize(ASTContext &Context) override { + IndexCtx->setASTContext(Context); + IndexCtx->getDataConsumer().initialize(Context); + IndexCtx->getDataConsumer().setPreprocessor(PP); + PP->addPPCallbacks(std::make_unique<IndexPPCallbacks>(IndexCtx)); + } + + bool HandleTopLevelDecl(DeclGroupRef DG) override { + return IndexCtx->indexDeclGroupRef(DG); + } + + void HandleInterestingDecl(DeclGroupRef DG) override { + // Ignore deserialized decls. + } + + void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override { + IndexCtx->indexDeclGroupRef(DG); + } + + void HandleTranslationUnit(ASTContext &Ctx) override { + DataConsumer->finish(); + } + + bool shouldSkipFunctionBody(Decl *D) override { + return ShouldSkipFunctionBody(D); + } +}; + +class IndexAction final : public ASTFrontendAction { + std::shared_ptr<IndexDataConsumer> DataConsumer; + IndexingOptions Opts; + +public: + IndexAction(std::shared_ptr<IndexDataConsumer> DataConsumer, + const IndexingOptions &Opts) + : DataConsumer(std::move(DataConsumer)), Opts(Opts) { + assert(this->DataConsumer != nullptr); + } + +protected: + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override { + return std::make_unique<IndexASTConsumer>( + DataConsumer, Opts, CI.getPreprocessorPtr(), + /*ShouldSkipFunctionBody=*/[](const Decl *) { return false; }); + } +}; + +} // anonymous namespace + +std::unique_ptr<ASTConsumer> index::createIndexingASTConsumer( + std::shared_ptr<IndexDataConsumer> DataConsumer, + const IndexingOptions &Opts, std::shared_ptr<Preprocessor> PP, + std::function<bool(const Decl *)> ShouldSkipFunctionBody) { + return std::make_unique<IndexASTConsumer>(DataConsumer, Opts, PP, + ShouldSkipFunctionBody); +} + +std::unique_ptr<FrontendAction> +index::createIndexingAction(std::shared_ptr<IndexDataConsumer> DataConsumer, + const IndexingOptions &Opts) { + assert(DataConsumer != nullptr); + return std::make_unique<IndexAction>(std::move(DataConsumer), Opts); +} + +static bool topLevelDeclVisitor(void *context, const Decl *D) { + IndexingContext &IndexCtx = *static_cast<IndexingContext*>(context); + return IndexCtx.indexTopLevelDecl(D); +} + +static void indexTranslationUnit(ASTUnit &Unit, IndexingContext &IndexCtx) { + Unit.visitLocalTopLevelDecls(&IndexCtx, topLevelDeclVisitor); +} + +static void indexPreprocessorMacros(const Preprocessor &PP, + IndexDataConsumer &DataConsumer) { + for (const auto &M : PP.macros()) + if (MacroDirective *MD = M.second.getLatest()) + DataConsumer.handleMacroOccurence( + M.first, MD->getMacroInfo(), + static_cast<unsigned>(index::SymbolRole::Definition), + MD->getLocation()); +} + +void index::indexASTUnit(ASTUnit &Unit, IndexDataConsumer &DataConsumer, + IndexingOptions Opts) { + IndexingContext IndexCtx(Opts, DataConsumer); + IndexCtx.setASTContext(Unit.getASTContext()); + DataConsumer.initialize(Unit.getASTContext()); + DataConsumer.setPreprocessor(Unit.getPreprocessorPtr()); + + if (Opts.IndexMacrosInPreprocessor) + indexPreprocessorMacros(Unit.getPreprocessor(), DataConsumer); + indexTranslationUnit(Unit, IndexCtx); + DataConsumer.finish(); +} + +void index::indexTopLevelDecls(ASTContext &Ctx, Preprocessor &PP, + ArrayRef<const Decl *> Decls, + IndexDataConsumer &DataConsumer, + IndexingOptions Opts) { + IndexingContext IndexCtx(Opts, DataConsumer); + IndexCtx.setASTContext(Ctx); + + DataConsumer.initialize(Ctx); + + if (Opts.IndexMacrosInPreprocessor) + indexPreprocessorMacros(PP, DataConsumer); + + for (const Decl *D : Decls) + IndexCtx.indexTopLevelDecl(D); + DataConsumer.finish(); +} + +std::unique_ptr<PPCallbacks> +index::indexMacrosCallback(IndexDataConsumer &Consumer, IndexingOptions Opts) { + return std::make_unique<IndexPPCallbacks>( + std::make_shared<IndexingContext>(Opts, Consumer)); +} + +void index::indexModuleFile(serialization::ModuleFile &Mod, ASTReader &Reader, + IndexDataConsumer &DataConsumer, + IndexingOptions Opts) { + ASTContext &Ctx = Reader.getContext(); + IndexingContext IndexCtx(Opts, DataConsumer); + IndexCtx.setASTContext(Ctx); + DataConsumer.initialize(Ctx); + + if (Opts.IndexMacrosInPreprocessor) + indexPreprocessorMacros(Reader.getPreprocessor(), DataConsumer); + + for (const Decl *D : Reader.getModuleFileLevelDecls(Mod)) { + IndexCtx.indexTopLevelDecl(D); + } + DataConsumer.finish(); +} diff --git a/clang/lib/Index/IndexingContext.cpp b/clang/lib/Index/IndexingContext.cpp new file mode 100644 index 000000000000..e29856007149 --- /dev/null +++ b/clang/lib/Index/IndexingContext.cpp @@ -0,0 +1,467 @@ +//===- IndexingContext.cpp - Indexing context data ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "IndexingContext.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Index/IndexDataConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/DeclObjC.h" +#include "clang/Basic/SourceManager.h" + +using namespace clang; +using namespace index; + +static bool isGeneratedDecl(const Decl *D) { + if (auto *attr = D->getAttr<ExternalSourceSymbolAttr>()) { + return attr->getGeneratedDeclaration(); + } + return false; +} + +bool IndexingContext::shouldIndex(const Decl *D) { + return !isGeneratedDecl(D); +} + +const LangOptions &IndexingContext::getLangOpts() const { + return Ctx->getLangOpts(); +} + +bool IndexingContext::shouldIndexFunctionLocalSymbols() const { + return IndexOpts.IndexFunctionLocals; +} + +bool IndexingContext::shouldIndexImplicitInstantiation() const { + return IndexOpts.IndexImplicitInstantiation; +} + +bool IndexingContext::shouldIndexParametersInDeclarations() const { + return IndexOpts.IndexParametersInDeclarations; +} + +bool IndexingContext::shouldIndexTemplateParameters() const { + return IndexOpts.IndexTemplateParameters; +} + +bool IndexingContext::handleDecl(const Decl *D, + SymbolRoleSet Roles, + ArrayRef<SymbolRelation> Relations) { + return handleDecl(D, D->getLocation(), Roles, Relations); +} + +bool IndexingContext::handleDecl(const Decl *D, SourceLocation Loc, + SymbolRoleSet Roles, + ArrayRef<SymbolRelation> Relations, + const DeclContext *DC) { + if (!DC) + DC = D->getDeclContext(); + + const Decl *OrigD = D; + if (isa<ObjCPropertyImplDecl>(D)) { + D = cast<ObjCPropertyImplDecl>(D)->getPropertyDecl(); + } + return handleDeclOccurrence(D, Loc, /*IsRef=*/false, cast<Decl>(DC), + Roles, Relations, + nullptr, OrigD, DC); +} + +bool IndexingContext::handleReference(const NamedDecl *D, SourceLocation Loc, + const NamedDecl *Parent, + const DeclContext *DC, + SymbolRoleSet Roles, + ArrayRef<SymbolRelation> Relations, + const Expr *RefE, + const Decl *RefD) { + if (!shouldIndexFunctionLocalSymbols() && isFunctionLocalSymbol(D)) + return true; + + if (!shouldIndexTemplateParameters() && + (isa<NonTypeTemplateParmDecl>(D) || isa<TemplateTypeParmDecl>(D) || + isa<TemplateTemplateParmDecl>(D))) { + return true; + } + + return handleDeclOccurrence(D, Loc, /*IsRef=*/true, Parent, Roles, Relations, + RefE, RefD, DC); +} + +static void reportModuleReferences(const Module *Mod, + ArrayRef<SourceLocation> IdLocs, + const ImportDecl *ImportD, + IndexDataConsumer &DataConsumer) { + if (!Mod) + return; + reportModuleReferences(Mod->Parent, IdLocs.drop_back(), ImportD, + DataConsumer); + DataConsumer.handleModuleOccurence(ImportD, Mod, + (SymbolRoleSet)SymbolRole::Reference, + IdLocs.back()); +} + +bool IndexingContext::importedModule(const ImportDecl *ImportD) { + if (ImportD->isInvalidDecl()) + return true; + + SourceLocation Loc; + auto IdLocs = ImportD->getIdentifierLocs(); + if (!IdLocs.empty()) + Loc = IdLocs.back(); + else + Loc = ImportD->getLocation(); + + SourceManager &SM = Ctx->getSourceManager(); + FileID FID = SM.getFileID(SM.getFileLoc(Loc)); + if (FID.isInvalid()) + return true; + + bool Invalid = false; + const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid); + if (Invalid || !SEntry.isFile()) + return true; + + if (SEntry.getFile().getFileCharacteristic() != SrcMgr::C_User) { + switch (IndexOpts.SystemSymbolFilter) { + case IndexingOptions::SystemSymbolFilterKind::None: + return true; + case IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly: + case IndexingOptions::SystemSymbolFilterKind::All: + break; + } + } + + const Module *Mod = ImportD->getImportedModule(); + if (!ImportD->isImplicit() && Mod->Parent && !IdLocs.empty()) { + reportModuleReferences(Mod->Parent, IdLocs.drop_back(), ImportD, + DataConsumer); + } + + SymbolRoleSet Roles = (unsigned)SymbolRole::Declaration; + if (ImportD->isImplicit()) + Roles |= (unsigned)SymbolRole::Implicit; + + return DataConsumer.handleModuleOccurence(ImportD, Mod, Roles, Loc); +} + +bool IndexingContext::isTemplateImplicitInstantiation(const Decl *D) { + TemplateSpecializationKind TKind = TSK_Undeclared; + if (const ClassTemplateSpecializationDecl * + SD = dyn_cast<ClassTemplateSpecializationDecl>(D)) { + TKind = SD->getSpecializationKind(); + } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + TKind = FD->getTemplateSpecializationKind(); + } else if (auto *VD = dyn_cast<VarDecl>(D)) { + TKind = VD->getTemplateSpecializationKind(); + } else if (const auto *RD = dyn_cast<CXXRecordDecl>(D)) { + if (RD->getInstantiatedFromMemberClass()) + TKind = RD->getTemplateSpecializationKind(); + } else if (const auto *ED = dyn_cast<EnumDecl>(D)) { + if (ED->getInstantiatedFromMemberEnum()) + TKind = ED->getTemplateSpecializationKind(); + } else if (isa<FieldDecl>(D) || isa<TypedefNameDecl>(D) || + isa<EnumConstantDecl>(D)) { + if (const auto *Parent = dyn_cast<Decl>(D->getDeclContext())) + return isTemplateImplicitInstantiation(Parent); + } + switch (TKind) { + case TSK_Undeclared: + case TSK_ExplicitSpecialization: + return false; + case TSK_ImplicitInstantiation: + case TSK_ExplicitInstantiationDeclaration: + case TSK_ExplicitInstantiationDefinition: + return true; + } + llvm_unreachable("invalid TemplateSpecializationKind"); +} + +bool IndexingContext::shouldIgnoreIfImplicit(const Decl *D) { + if (isa<ObjCInterfaceDecl>(D)) + return false; + if (isa<ObjCCategoryDecl>(D)) + return false; + if (isa<ObjCIvarDecl>(D)) + return false; + if (isa<ObjCMethodDecl>(D)) + return false; + if (isa<ImportDecl>(D)) + return false; + return true; +} + +static const CXXRecordDecl * +getDeclContextForTemplateInstationPattern(const Decl *D) { + if (const auto *CTSD = + dyn_cast<ClassTemplateSpecializationDecl>(D->getDeclContext())) + return CTSD->getTemplateInstantiationPattern(); + else if (const auto *RD = dyn_cast<CXXRecordDecl>(D->getDeclContext())) + return RD->getInstantiatedFromMemberClass(); + return nullptr; +} + +static const Decl *adjustTemplateImplicitInstantiation(const Decl *D) { + if (const ClassTemplateSpecializationDecl * + SD = dyn_cast<ClassTemplateSpecializationDecl>(D)) { + return SD->getTemplateInstantiationPattern(); + } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + return FD->getTemplateInstantiationPattern(); + } else if (auto *VD = dyn_cast<VarDecl>(D)) { + return VD->getTemplateInstantiationPattern(); + } else if (const auto *RD = dyn_cast<CXXRecordDecl>(D)) { + return RD->getInstantiatedFromMemberClass(); + } else if (const auto *ED = dyn_cast<EnumDecl>(D)) { + return ED->getInstantiatedFromMemberEnum(); + } else if (isa<FieldDecl>(D) || isa<TypedefNameDecl>(D)) { + const auto *ND = cast<NamedDecl>(D); + if (const CXXRecordDecl *Pattern = + getDeclContextForTemplateInstationPattern(ND)) { + for (const NamedDecl *BaseND : Pattern->lookup(ND->getDeclName())) { + if (BaseND->isImplicit()) + continue; + if (BaseND->getKind() == ND->getKind()) + return BaseND; + } + } + } else if (const auto *ECD = dyn_cast<EnumConstantDecl>(D)) { + if (const auto *ED = dyn_cast<EnumDecl>(ECD->getDeclContext())) { + if (const EnumDecl *Pattern = ED->getInstantiatedFromMemberEnum()) { + for (const NamedDecl *BaseECD : Pattern->lookup(ECD->getDeclName())) + return BaseECD; + } + } + } + return nullptr; +} + +static bool isDeclADefinition(const Decl *D, const DeclContext *ContainerDC, ASTContext &Ctx) { + if (auto VD = dyn_cast<VarDecl>(D)) + return VD->isThisDeclarationADefinition(Ctx); + + if (auto FD = dyn_cast<FunctionDecl>(D)) + return FD->isThisDeclarationADefinition(); + + if (auto TD = dyn_cast<TagDecl>(D)) + return TD->isThisDeclarationADefinition(); + + if (auto MD = dyn_cast<ObjCMethodDecl>(D)) + return MD->isThisDeclarationADefinition() || isa<ObjCImplDecl>(ContainerDC); + + if (isa<TypedefNameDecl>(D) || + isa<EnumConstantDecl>(D) || + isa<FieldDecl>(D) || + isa<MSPropertyDecl>(D) || + isa<ObjCImplDecl>(D) || + isa<ObjCPropertyImplDecl>(D)) + return true; + + return false; +} + +/// Whether the given NamedDecl should be skipped because it has no name. +static bool shouldSkipNamelessDecl(const NamedDecl *ND) { + return (ND->getDeclName().isEmpty() && !isa<TagDecl>(ND) && + !isa<ObjCCategoryDecl>(ND)) || isa<CXXDeductionGuideDecl>(ND); +} + +static const Decl *adjustParent(const Decl *Parent) { + if (!Parent) + return nullptr; + for (;; Parent = cast<Decl>(Parent->getDeclContext())) { + if (isa<TranslationUnitDecl>(Parent)) + return nullptr; + if (isa<LinkageSpecDecl>(Parent) || isa<BlockDecl>(Parent)) + continue; + if (auto NS = dyn_cast<NamespaceDecl>(Parent)) { + if (NS->isAnonymousNamespace()) + continue; + } else if (auto RD = dyn_cast<RecordDecl>(Parent)) { + if (RD->isAnonymousStructOrUnion()) + continue; + } else if (auto ND = dyn_cast<NamedDecl>(Parent)) { + if (shouldSkipNamelessDecl(ND)) + continue; + } + return Parent; + } +} + +static const Decl *getCanonicalDecl(const Decl *D) { + D = D->getCanonicalDecl(); + if (auto TD = dyn_cast<TemplateDecl>(D)) { + if (auto TTD = TD->getTemplatedDecl()) { + D = TTD; + assert(D->isCanonicalDecl()); + } + } + + return D; +} + +static bool shouldReportOccurrenceForSystemDeclOnlyMode( + bool IsRef, SymbolRoleSet Roles, ArrayRef<SymbolRelation> Relations) { + if (!IsRef) + return true; + + auto acceptForRelation = [](SymbolRoleSet roles) -> bool { + bool accept = false; + applyForEachSymbolRoleInterruptible(roles, [&accept](SymbolRole r) -> bool { + switch (r) { + case SymbolRole::RelationChildOf: + case SymbolRole::RelationBaseOf: + case SymbolRole::RelationOverrideOf: + case SymbolRole::RelationExtendedBy: + case SymbolRole::RelationAccessorOf: + case SymbolRole::RelationIBTypeOf: + accept = true; + return false; + case SymbolRole::Declaration: + case SymbolRole::Definition: + case SymbolRole::Reference: + case SymbolRole::Read: + case SymbolRole::Write: + case SymbolRole::Call: + case SymbolRole::Dynamic: + case SymbolRole::AddressOf: + case SymbolRole::Implicit: + case SymbolRole::Undefinition: + case SymbolRole::RelationReceivedBy: + case SymbolRole::RelationCalledBy: + case SymbolRole::RelationContainedBy: + case SymbolRole::RelationSpecializationOf: + case SymbolRole::NameReference: + return true; + } + llvm_unreachable("Unsupported SymbolRole value!"); + }); + return accept; + }; + + for (auto &Rel : Relations) { + if (acceptForRelation(Rel.Roles)) + return true; + } + + return false; +} + +bool IndexingContext::handleDeclOccurrence(const Decl *D, SourceLocation Loc, + bool IsRef, const Decl *Parent, + SymbolRoleSet Roles, + ArrayRef<SymbolRelation> Relations, + const Expr *OrigE, + const Decl *OrigD, + const DeclContext *ContainerDC) { + if (D->isImplicit() && !isa<ObjCMethodDecl>(D)) + return true; + if (!isa<NamedDecl>(D) || shouldSkipNamelessDecl(cast<NamedDecl>(D))) + return true; + + SourceManager &SM = Ctx->getSourceManager(); + FileID FID = SM.getFileID(SM.getFileLoc(Loc)); + if (FID.isInvalid()) + return true; + + bool Invalid = false; + const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid); + if (Invalid || !SEntry.isFile()) + return true; + + if (SEntry.getFile().getFileCharacteristic() != SrcMgr::C_User) { + switch (IndexOpts.SystemSymbolFilter) { + case IndexingOptions::SystemSymbolFilterKind::None: + return true; + case IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly: + if (!shouldReportOccurrenceForSystemDeclOnlyMode(IsRef, Roles, Relations)) + return true; + break; + case IndexingOptions::SystemSymbolFilterKind::All: + break; + } + } + + if (!OrigD) + OrigD = D; + + if (isTemplateImplicitInstantiation(D)) { + if (!IsRef) + return true; + D = adjustTemplateImplicitInstantiation(D); + if (!D) + return true; + assert(!isTemplateImplicitInstantiation(D)); + } + + if (IsRef) + Roles |= (unsigned)SymbolRole::Reference; + else if (isDeclADefinition(OrigD, ContainerDC, *Ctx)) + Roles |= (unsigned)SymbolRole::Definition; + else + Roles |= (unsigned)SymbolRole::Declaration; + + D = getCanonicalDecl(D); + Parent = adjustParent(Parent); + if (Parent) + Parent = getCanonicalDecl(Parent); + + SmallVector<SymbolRelation, 6> FinalRelations; + FinalRelations.reserve(Relations.size()+1); + + auto addRelation = [&](SymbolRelation Rel) { + auto It = llvm::find_if(FinalRelations, [&](SymbolRelation Elem) -> bool { + return Elem.RelatedSymbol == Rel.RelatedSymbol; + }); + if (It != FinalRelations.end()) { + It->Roles |= Rel.Roles; + } else { + FinalRelations.push_back(Rel); + } + Roles |= Rel.Roles; + }; + + if (Parent) { + if (IsRef || (!isa<ParmVarDecl>(D) && isFunctionLocalSymbol(D))) { + addRelation(SymbolRelation{ + (unsigned)SymbolRole::RelationContainedBy, + Parent + }); + } else { + addRelation(SymbolRelation{ + (unsigned)SymbolRole::RelationChildOf, + Parent + }); + } + } + + for (auto &Rel : Relations) { + addRelation(SymbolRelation(Rel.Roles, + Rel.RelatedSymbol->getCanonicalDecl())); + } + + IndexDataConsumer::ASTNodeInfo Node{OrigE, OrigD, Parent, ContainerDC}; + return DataConsumer.handleDeclOccurence(D, Roles, FinalRelations, Loc, Node); +} + +void IndexingContext::handleMacroDefined(const IdentifierInfo &Name, + SourceLocation Loc, + const MacroInfo &MI) { + SymbolRoleSet Roles = (unsigned)SymbolRole::Definition; + DataConsumer.handleMacroOccurence(&Name, &MI, Roles, Loc); +} + +void IndexingContext::handleMacroUndefined(const IdentifierInfo &Name, + SourceLocation Loc, + const MacroInfo &MI) { + SymbolRoleSet Roles = (unsigned)SymbolRole::Undefinition; + DataConsumer.handleMacroOccurence(&Name, &MI, Roles, Loc); +} + +void IndexingContext::handleMacroReference(const IdentifierInfo &Name, + SourceLocation Loc, + const MacroInfo &MI) { + SymbolRoleSet Roles = (unsigned)SymbolRole::Reference; + DataConsumer.handleMacroOccurence(&Name, &MI, Roles, Loc); +} diff --git a/clang/lib/Index/IndexingContext.h b/clang/lib/Index/IndexingContext.h new file mode 100644 index 000000000000..3136878c080c --- /dev/null +++ b/clang/lib/Index/IndexingContext.h @@ -0,0 +1,139 @@ +//===- IndexingContext.h - Indexing context data ----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_INDEX_INDEXINGCONTEXT_H +#define LLVM_CLANG_LIB_INDEX_INDEXINGCONTEXT_H + +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LLVM.h" +#include "clang/Index/IndexSymbol.h" +#include "clang/Index/IndexingAction.h" +#include "clang/Lex/MacroInfo.h" +#include "llvm/ADT/ArrayRef.h" + +namespace clang { + class ASTContext; + class Decl; + class DeclGroupRef; + class ImportDecl; + class TagDecl; + class TypeSourceInfo; + class NamedDecl; + class ObjCMethodDecl; + class DeclContext; + class NestedNameSpecifierLoc; + class Stmt; + class Expr; + class TypeLoc; + class SourceLocation; + +namespace index { + class IndexDataConsumer; + +class IndexingContext { + IndexingOptions IndexOpts; + IndexDataConsumer &DataConsumer; + ASTContext *Ctx = nullptr; + +public: + IndexingContext(IndexingOptions IndexOpts, IndexDataConsumer &DataConsumer) + : IndexOpts(IndexOpts), DataConsumer(DataConsumer) {} + + const IndexingOptions &getIndexOpts() const { return IndexOpts; } + IndexDataConsumer &getDataConsumer() { return DataConsumer; } + + void setASTContext(ASTContext &ctx) { Ctx = &ctx; } + + bool shouldIndex(const Decl *D); + + const LangOptions &getLangOpts() const; + + bool shouldSuppressRefs() const { + return false; + } + + bool shouldIndexFunctionLocalSymbols() const; + + bool shouldIndexImplicitInstantiation() const; + + bool shouldIndexParametersInDeclarations() const; + + bool shouldIndexTemplateParameters() const; + + static bool isTemplateImplicitInstantiation(const Decl *D); + + bool handleDecl(const Decl *D, SymbolRoleSet Roles = SymbolRoleSet(), + ArrayRef<SymbolRelation> Relations = None); + + bool handleDecl(const Decl *D, SourceLocation Loc, + SymbolRoleSet Roles = SymbolRoleSet(), + ArrayRef<SymbolRelation> Relations = None, + const DeclContext *DC = nullptr); + + bool handleReference(const NamedDecl *D, SourceLocation Loc, + const NamedDecl *Parent, + const DeclContext *DC, + SymbolRoleSet Roles = SymbolRoleSet(), + ArrayRef<SymbolRelation> Relations = None, + const Expr *RefE = nullptr, + const Decl *RefD = nullptr); + + void handleMacroDefined(const IdentifierInfo &Name, SourceLocation Loc, + const MacroInfo &MI); + + void handleMacroUndefined(const IdentifierInfo &Name, SourceLocation Loc, + const MacroInfo &MI); + + void handleMacroReference(const IdentifierInfo &Name, SourceLocation Loc, + const MacroInfo &MD); + + bool importedModule(const ImportDecl *ImportD); + + bool indexDecl(const Decl *D); + + void indexTagDecl(const TagDecl *D, + ArrayRef<SymbolRelation> Relations = None); + + void indexTypeSourceInfo(TypeSourceInfo *TInfo, const NamedDecl *Parent, + const DeclContext *DC = nullptr, + bool isBase = false, + bool isIBType = false); + + void indexTypeLoc(TypeLoc TL, const NamedDecl *Parent, + const DeclContext *DC = nullptr, + bool isBase = false, + bool isIBType = false); + + void indexNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS, + const NamedDecl *Parent, + const DeclContext *DC = nullptr); + + bool indexDeclContext(const DeclContext *DC); + + void indexBody(const Stmt *S, const NamedDecl *Parent, + const DeclContext *DC = nullptr); + + bool indexTopLevelDecl(const Decl *D); + bool indexDeclGroupRef(DeclGroupRef DG); + +private: + bool shouldIgnoreIfImplicit(const Decl *D); + + bool handleDeclOccurrence(const Decl *D, SourceLocation Loc, + bool IsRef, const Decl *Parent, + SymbolRoleSet Roles, + ArrayRef<SymbolRelation> Relations, + const Expr *RefE, + const Decl *RefD, + const DeclContext *ContainerDC); +}; + +} // end namespace index +} // end namespace clang + +#endif diff --git a/clang/lib/Index/SimpleFormatContext.h b/clang/lib/Index/SimpleFormatContext.h new file mode 100644 index 000000000000..17793154a3ae --- /dev/null +++ b/clang/lib/Index/SimpleFormatContext.h @@ -0,0 +1,72 @@ +//===--- SimpleFormatContext.h ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +/// \file +/// +/// Defines a utility class for use of clang-format in libclang +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_INDEX_SIMPLEFORMATCONTEXT_H +#define LLVM_CLANG_LIB_INDEX_SIMPLEFORMATCONTEXT_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 { + +/// 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.get())), + InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem), + Files(FileSystemOptions(), InMemoryFileSystem), + Sources(*Diagnostics, Files), Rewrite(Sources, Options) { + Diagnostics->setClient(new IgnoringDiagConsumer, true); + } + + FileID createInMemoryFile(StringRef Name, StringRef Content) { + InMemoryFileSystem->addFile(Name, 0, + llvm::MemoryBuffer::getMemBuffer(Content)); + const FileEntry *Entry = Files.getFile(Name); + assert(Entry != nullptr); + 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; + IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem; + FileManager Files; + SourceManager Sources; + Rewriter Rewrite; +}; + +} // end namespace index +} // end namespace clang + +#endif diff --git a/clang/lib/Index/USRGeneration.cpp b/clang/lib/Index/USRGeneration.cpp new file mode 100644 index 000000000000..f4316fe7d067 --- /dev/null +++ b/clang/lib/Index/USRGeneration.cpp @@ -0,0 +1,1151 @@ +//===- USRGeneration.cpp - Routines for USR generation --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/USRGeneration.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/DeclVisitor.h" +#include "clang/Lex/PreprocessingRecord.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::index; + +//===----------------------------------------------------------------------===// +// USR generation. +//===----------------------------------------------------------------------===// + +/// \returns true on error. +static bool printLoc(llvm::raw_ostream &OS, SourceLocation Loc, + const SourceManager &SM, bool IncludeOffset) { + if (Loc.isInvalid()) { + return true; + } + Loc = SM.getExpansionLoc(Loc); + const std::pair<FileID, unsigned> &Decomposed = SM.getDecomposedLoc(Loc); + const FileEntry *FE = SM.getFileEntryForID(Decomposed.first); + if (FE) { + OS << llvm::sys::path::filename(FE->getName()); + } else { + // This case really isn't interesting. + return true; + } + if (IncludeOffset) { + // 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. + OS << '@' << Decomposed.second; + } + return false; +} + +static StringRef GetExternalSourceContainer(const NamedDecl *D) { + if (!D) + return StringRef(); + if (auto *attr = D->getExternalSourceSymbolAttr()) { + return attr->getDefinedIn(); + } + return StringRef(); +} + +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, + const ObjCCategoryDecl *CatD = nullptr); + 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 VisitBindingDecl(const BindingDecl *D); + void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *D); + void VisitTemplateTemplateParmDecl(const TemplateTemplateParmDecl *D); + void VisitUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl *D); + void VisitUnresolvedUsingTypenameDecl(const UnresolvedUsingTypenameDecl *D); + + void VisitLinkageSpecDecl(const LinkageSpecDecl *D) { + IgnoreResults = true; // No USRs for linkage specs themselves. + } + + void VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) { + IgnoreResults = true; + } + + void VisitUsingDecl(const UsingDecl *D) { + VisitDeclContext(D->getDeclContext()); + Out << "@UD@"; + + bool EmittedDeclName = !EmitDeclName(D); + assert(EmittedDeclName && "EmitDeclName can not fail for UsingDecls"); + (void)EmittedDeclName; + } + + bool ShouldGenerateLocation(const NamedDecl *D); + + bool isLocal(const NamedDecl *D) { + return D->getParentFunctionOrMethod() != nullptr; + } + + void GenExtSymbolContainer(const NamedDecl *D); + + /// Generate the string component containing the location of the + /// declaration. + bool GenLoc(const Decl *D, bool IncludeOffset); + + /// 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, StringRef ExtSymDefinedIn, + StringRef CategoryContextExtSymbolDefinedIn) { + generateUSRForObjCClass(cls, Out, ExtSymDefinedIn, + CategoryContextExtSymbolDefinedIn); + } + + /// Generate a USR for an Objective-C class category. + void GenObjCCategory(StringRef cls, StringRef cat, + StringRef clsExt, StringRef catExt) { + generateUSRForObjCCategory(cls, cat, Out, clsExt, catExt); + } + + /// Generate a USR fragment for an Objective-C property. + void GenObjCProperty(StringRef prop, bool isClassProp) { + generateUSRForObjCProperty(prop, isClassProp, Out); + } + + /// Generate a USR for an Objective-C protocol. + void GenObjCProtocol(StringRef prot, StringRef ext) { + generateUSRForObjCProtocol(prot, Out, ext); + } + + 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) { + const unsigned startSize = Buf.size(); + D->printName(Out); + const unsigned endSize = Buf.size(); + return startSize == endSize; +} + +bool USRGenerator::ShouldGenerateLocation(const NamedDecl *D) { + if (D->isExternallyVisible()) + return false; + if (D->getParentFunctionOrMethod()) + return true; + SourceLocation Loc = D->getLocation(); + if (Loc.isInvalid()) + return false; + const SourceManager &SM = Context->getSourceManager(); + return !SM.isInSystemHeader(Loc); +} + +void USRGenerator::VisitDeclContext(const DeclContext *DC) { + if (const NamedDecl *D = dyn_cast<NamedDecl>(DC)) + Visit(D); + else if (isa<LinkageSpecDecl>(DC)) // Linkage specs are transparent in USRs. + VisitDeclContext(DC->getParent()); +} + +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, /*IncludeOffset=*/isLocal(D))) + return; + + const unsigned StartSize = Buf.size(); + VisitDeclContext(D->getDeclContext()); + if (Buf.size() == StartSize) + GenExtSymbolContainer(D); + + bool IsTemplate = false; + if (FunctionTemplateDecl *FunTmpl = D->getDescribedFunctionTemplate()) { + IsTemplate = true; + Out << "@FT@"; + VisitTemplateParameterList(FunTmpl->getTemplateParameters()); + } else + Out << "@F@"; + + PrintingPolicy Policy(Context->getLangOpts()); + // Forward references can have different template argument names. Suppress the + // template argument names in constructors to make their USR more stable. + Policy.SuppressTemplateArgsInCXXConstructors = true; + D->getDeclName().print(Out, Policy); + + ASTContext &Ctx = *Context; + if ((!Ctx.getLangOpts().CPlusPlus || D->isExternC()) && + !D->hasAttr<OverloadableAttr>()) + 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 (auto PD : D->parameters()) { + Out << '#'; + VisitType(PD->getType()); + } + if (D->isVariadic()) + Out << '.'; + if (IsTemplate) { + // Function templates can be overloaded by return type, for example: + // \code + // template <class T> typename T::A foo() {} + // template <class T> typename T::B foo() {} + // \endcode + Out << '#'; + VisitType(D->getReturnType()); + } + Out << '#'; + if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) { + if (MD->isStatic()) + Out << 'S'; + // FIXME: OpenCL: Need to consider address spaces + if (unsigned quals = MD->getMethodQualifiers().getCVRUQualifiers()) + Out << (char)('0' + quals); + switch (MD->getRefQualifier()) { + case RQ_None: break; + case RQ_LValue: Out << '&'; break; + case RQ_RValue: Out << "&&"; break; + } + } +} + +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, /*IncludeOffset=*/isLocal(D))) + return; + + VisitDeclContext(D->getDeclContext()); + + if (VarTemplateDecl *VarTmpl = D->getDescribedVarTemplate()) { + Out << "@VT"; + VisitTemplateParameterList(VarTmpl->getTemplateParameters()); + } else if (const VarTemplatePartialSpecializationDecl *PartialSpec + = dyn_cast<VarTemplatePartialSpecializationDecl>(D)) { + Out << "@VP"; + VisitTemplateParameterList(PartialSpec->getTemplateParameters()); + } + + // 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; + + // For a template specialization, mangle the template arguments. + if (const VarTemplateSpecializationDecl *Spec + = dyn_cast<VarTemplateSpecializationDecl>(D)) { + const TemplateArgumentList &Args = Spec->getTemplateArgs(); + Out << '>'; + for (unsigned I = 0, N = Args.size(); I != N; ++I) { + Out << '#'; + VisitTemplateArgument(Args.get(I)); + } + } +} + +void USRGenerator::VisitBindingDecl(const BindingDecl *D) { + if (isLocal(D) && GenLoc(D, /*IncludeOffset=*/true)) + return; + VisitNamedDecl(D); +} + +void USRGenerator::VisitNonTypeTemplateParmDecl( + const NonTypeTemplateParmDecl *D) { + GenLoc(D, /*IncludeOffset=*/true); +} + +void USRGenerator::VisitTemplateTemplateParmDecl( + const TemplateTemplateParmDecl *D) { + GenLoc(D, /*IncludeOffset=*/true); +} + +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; + } + auto getCategoryContext = [](const ObjCMethodDecl *D) -> + const ObjCCategoryDecl * { + if (auto *CD = dyn_cast<ObjCCategoryDecl>(D->getDeclContext())) + return CD; + if (auto *ICD = dyn_cast<ObjCCategoryImplDecl>(D->getDeclContext())) + return ICD->getCategoryDecl(); + return nullptr; + }; + auto *CD = getCategoryContext(D); + VisitObjCContainerDecl(ID, CD); + } + // 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, + const ObjCCategoryDecl *CatD) { + switch (D->getKind()) { + default: + llvm_unreachable("Invalid ObjC container."); + case Decl::ObjCInterface: + case Decl::ObjCImplementation: + GenObjCClass(D->getName(), GetExternalSourceContainer(D), + GetExternalSourceContainer(CatD)); + 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, /*IncludeOffset=*/true); + } + else + GenObjCCategory(ID->getName(), CD->getName(), + GetExternalSourceContainer(ID), + GetExternalSourceContainer(CD)); + + 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(), + GetExternalSourceContainer(ID), + GetExternalSourceContainer(CD)); + break; + } + case Decl::ObjCProtocol: { + const ObjCProtocolDecl *PD = cast<ObjCProtocolDecl>(D); + GenObjCProtocol(PD->getName(), GetExternalSourceContainer(PD)); + 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(), D->isClassProperty()); +} + +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 (!isa<EnumDecl>(D) && + ShouldGenerateLocation(D) && GenLoc(D, /*IncludeOffset=*/isLocal(D))) + return; + + GenExtSymbolContainer(D); + + 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_Class: + case TTK_Struct: Out << "@ST"; 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_Class: + case TTK_Struct: Out << "@SP"; 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_Class: + case TTK_Struct: Out << "@S"; break; + case TTK_Union: Out << "@U"; break; + case TTK_Enum: Out << "@E"; break; + } + } + + Out << '@'; + 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 { + if (D->isEmbeddedInDeclarator() && !D->isFreeStanding()) { + printLoc(Out, D->getLocation(), Context->getSourceManager(), true); + } else { + Buf[off] = 'a'; + if (auto *ED = dyn_cast<EnumDecl>(D)) { + // Distinguish USRs of anonymous enums by using their first enumerator. + auto enum_range = ED->enumerators(); + if (enum_range.begin() != enum_range.end()) { + Out << '@' << **enum_range.begin(); + } + } + } + } + } + + // For a class template specialization, mangle the template arguments. + if (const ClassTemplateSpecializationDecl *Spec + = dyn_cast<ClassTemplateSpecializationDecl>(D)) { + const TemplateArgumentList &Args = Spec->getTemplateArgs(); + 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, /*IncludeOffset=*/isLocal(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, /*IncludeOffset=*/true); +} + +void USRGenerator::GenExtSymbolContainer(const NamedDecl *D) { + StringRef Container = GetExternalSourceContainer(D); + if (!Container.empty()) + Out << "@M@" << Container; +} + +bool USRGenerator::GenLoc(const Decl *D, bool IncludeOffset) { + 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(); + + IgnoreResults = + IgnoreResults || printLoc(Out, D->getBeginLoc(), + Context->getSourceManager(), IncludeOffset); + + return IgnoreResults; +} + +static void printQualifier(llvm::raw_ostream &Out, ASTContext &Ctx, NestedNameSpecifier *NNS) { + // FIXME: Encode the qualifier, don't just print it. + PrintingPolicy PO(Ctx.getLangOpts()); + PO.SuppressTagKeyword = true; + PO.SuppressUnwrittenScope = true; + PO.ConstantArraySizeAsWritten = false; + PO.AnonymousTagLocations = false; + NNS->print(Out, PO); +} + +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::UChar: + c = 'c'; break; + case BuiltinType::Char8: + c = 'u'; break; // FIXME: Check this doesn't collide + 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_U: + case BuiltinType::Char_S: + c = 'C'; break; + case BuiltinType::SChar: + c = 'r'; 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::Float16: + 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::Float128: + c = 'Q'; 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: +#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ + case BuiltinType::Id: +#include "clang/Basic/OpenCLImageTypes.def" +#define EXT_OPAQUE_TYPE(ExtType, Id, Ext) \ + case BuiltinType::Id: +#include "clang/Basic/OpenCLExtensionTypes.def" + case BuiltinType::OCLEvent: + case BuiltinType::OCLClkEvent: + case BuiltinType::OCLQueue: + case BuiltinType::OCLReserveID: + case BuiltinType::OCLSampler: +#define SVE_TYPE(Name, Id, SingletonId) \ + case BuiltinType::Id: +#include "clang/Basic/AArch64SVEACLETypes.def" + case BuiltinType::ShortAccum: + case BuiltinType::Accum: + case BuiltinType::LongAccum: + case BuiltinType::UShortAccum: + case BuiltinType::UAccum: + case BuiltinType::ULongAccum: + case BuiltinType::ShortFract: + case BuiltinType::Fract: + case BuiltinType::LongFract: + case BuiltinType::UShortFract: + case BuiltinType::UFract: + case BuiltinType::ULongFract: + case BuiltinType::SatShortAccum: + case BuiltinType::SatAccum: + case BuiltinType::SatLongAccum: + case BuiltinType::SatUShortAccum: + case BuiltinType::SatUAccum: + case BuiltinType::SatULongAccum: + case BuiltinType::SatShortFract: + case BuiltinType::SatFract: + case BuiltinType::SatLongFract: + case BuiltinType::SatUShortFract: + case BuiltinType::SatUFract: + case BuiltinType::SatULongFract: + 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 ObjCObjectPointerType *OPT = T->getAs<ObjCObjectPointerType>()) { + Out << '*'; + T = OPT->getPointeeType(); + continue; + } + if (const RValueReferenceType *RT = T->getAs<RValueReferenceType>()) { + Out << "&&"; + T = RT->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->getReturnType()); + Out << '('; + for (const auto &I : FT->param_types()) { + Out << '#'; + VisitType(I); + } + Out << ')'; + 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 ObjCInterfaceType *OIT = T->getAs<ObjCInterfaceType>()) { + Out << '$'; + VisitObjCInterfaceDecl(OIT->getDecl()); + return; + } + if (const ObjCObjectType *OIT = T->getAs<ObjCObjectType>()) { + Out << 'Q'; + VisitType(OIT->getBaseType()); + for (auto *Prot : OIT->getProtocols()) + VisitObjCProtocolDecl(Prot); + 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; + } + if (const DependentNameType *DNT = T->getAs<DependentNameType>()) { + Out << '^'; + printQualifier(Out, Ctx, DNT->getQualifier()); + Out << ':' << DNT->getIdentifier()->getName(); + return; + } + if (const InjectedClassNameType *InjT = T->getAs<InjectedClassNameType>()) { + T = InjT->getInjectedSpecializationType(); + continue; + } + if (const auto *VT = T->getAs<VectorType>()) { + Out << (T->isExtVectorType() ? ']' : '['); + Out << VT->getNumElements(); + T = VT->getElementType(); + continue; + } + if (const auto *const AT = dyn_cast<ArrayType>(T)) { + Out << '{'; + switch (AT->getSizeModifier()) { + case ArrayType::Static: + Out << 's'; + break; + case ArrayType::Star: + Out << '*'; + break; + case ArrayType::Normal: + Out << 'n'; + break; + } + if (const auto *const CAT = dyn_cast<ConstantArrayType>(T)) + Out << CAT->getSize(); + + T = AT->getElementType(); + continue; + } + + // 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... + LLVM_FALLTHROUGH; + case TemplateArgument::Template: + VisitTemplateName(Arg.getAsTemplateOrTemplatePattern()); + break; + + case TemplateArgument::Expression: + // FIXME: Visit expressions. + break; + + case TemplateArgument::Pack: + Out << 'p' << Arg.pack_size(); + for (const auto &P : Arg.pack_elements()) + VisitTemplateArgument(P); + break; + + case TemplateArgument::Type: + VisitType(Arg.getAsType()); + break; + + case TemplateArgument::Integral: + Out << 'V'; + VisitType(Arg.getIntegralType()); + Out << Arg.getAsIntegral(); + break; + } +} + +void USRGenerator::VisitUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl *D) { + if (ShouldGenerateLocation(D) && GenLoc(D, /*IncludeOffset=*/isLocal(D))) + return; + VisitDeclContext(D->getDeclContext()); + Out << "@UUV@"; + printQualifier(Out, D->getASTContext(), D->getQualifier()); + EmitDeclName(D); +} + +void USRGenerator::VisitUnresolvedUsingTypenameDecl(const UnresolvedUsingTypenameDecl *D) { + if (ShouldGenerateLocation(D) && GenLoc(D, /*IncludeOffset=*/isLocal(D))) + return; + VisitDeclContext(D->getDeclContext()); + Out << "@UUT@"; + printQualifier(Out, D->getASTContext(), D->getQualifier()); + Out << D->getName(); // Simple name. +} + + + +//===----------------------------------------------------------------------===// +// USR generation functions. +//===----------------------------------------------------------------------===// + +static void combineClassAndCategoryExtContainers(StringRef ClsSymDefinedIn, + StringRef CatSymDefinedIn, + raw_ostream &OS) { + if (ClsSymDefinedIn.empty() && CatSymDefinedIn.empty()) + return; + if (CatSymDefinedIn.empty()) { + OS << "@M@" << ClsSymDefinedIn << '@'; + return; + } + OS << "@CM@" << CatSymDefinedIn << '@'; + if (ClsSymDefinedIn != CatSymDefinedIn) { + OS << ClsSymDefinedIn << '@'; + } +} + +void clang::index::generateUSRForObjCClass(StringRef Cls, raw_ostream &OS, + StringRef ExtSymDefinedIn, + StringRef CategoryContextExtSymbolDefinedIn) { + combineClassAndCategoryExtContainers(ExtSymDefinedIn, + CategoryContextExtSymbolDefinedIn, OS); + OS << "objc(cs)" << Cls; +} + +void clang::index::generateUSRForObjCCategory(StringRef Cls, StringRef Cat, + raw_ostream &OS, + StringRef ClsSymDefinedIn, + StringRef CatSymDefinedIn) { + combineClassAndCategoryExtContainers(ClsSymDefinedIn, CatSymDefinedIn, 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, bool isClassProp, + raw_ostream &OS) { + OS << (isClassProp ? "(cpy)" : "(py)") << Prop; +} + +void clang::index::generateUSRForObjCProtocol(StringRef Prot, raw_ostream &OS, + StringRef ExtSymDefinedIn) { + if (!ExtSymDefinedIn.empty()) + OS << "@M@" << ExtSymDefinedIn << '@'; + OS << "objc(pl)" << Prot; +} + +void clang::index::generateUSRForGlobalEnum(StringRef EnumName, raw_ostream &OS, + StringRef ExtSymDefinedIn) { + if (!ExtSymDefinedIn.empty()) + OS << "@M@" << ExtSymDefinedIn; + OS << "@E@" << EnumName; +} + +void clang::index::generateUSRForEnumConstant(StringRef EnumConstantName, + raw_ostream &OS) { + OS << '@' << EnumConstantName; +} + +bool clang::index::generateUSRForDecl(const Decl *D, + SmallVectorImpl<char> &Buf) { + if (!D) + return true; + // We don't ignore decls with invalid source locations. Implicit decls, like + // C++'s operator new function, can have invalid locations but it is fine to + // create USRs that can identify them. + + USRGenerator UG(&D->getASTContext(), Buf); + UG.Visit(D); + return UG.ignoreResults(); +} + +bool clang::index::generateUSRForMacro(const MacroDefinitionRecord *MD, + const SourceManager &SM, + SmallVectorImpl<char> &Buf) { + if (!MD) + return true; + return generateUSRForMacro(MD->getName()->getName(), MD->getLocation(), + SM, Buf); + +} + +bool clang::index::generateUSRForMacro(StringRef MacroName, SourceLocation Loc, + const SourceManager &SM, + SmallVectorImpl<char> &Buf) { + // Don't generate USRs for things with invalid locations. + if (MacroName.empty() || Loc.isInvalid()) + return true; + + llvm::raw_svector_ostream Out(Buf); + + // Assume that system headers are sane. Don't put source location + // information into the USR if the macro comes from a system header. + bool ShouldGenerateLocation = !SM.isInSystemHeader(Loc); + + Out << getUSRSpacePrefix(); + if (ShouldGenerateLocation) + printLoc(Out, Loc, SM, /*IncludeOffset=*/true); + Out << "@macro@"; + Out << MacroName; + return false; +} + +bool clang::index::generateUSRForType(QualType T, ASTContext &Ctx, + SmallVectorImpl<char> &Buf) { + if (T.isNull()) + return true; + T = T.getCanonicalType(); + + USRGenerator UG(&Ctx, Buf); + UG.VisitType(T); + return UG.ignoreResults(); +} + +bool clang::index::generateFullUSRForModule(const Module *Mod, + raw_ostream &OS) { + if (!Mod->Parent) + return generateFullUSRForTopLevelModuleName(Mod->Name, OS); + if (generateFullUSRForModule(Mod->Parent, OS)) + return true; + return generateUSRFragmentForModule(Mod, OS); +} + +bool clang::index::generateFullUSRForTopLevelModuleName(StringRef ModName, + raw_ostream &OS) { + OS << getUSRSpacePrefix(); + return generateUSRFragmentForModuleName(ModName, OS); +} + +bool clang::index::generateUSRFragmentForModule(const Module *Mod, + raw_ostream &OS) { + return generateUSRFragmentForModuleName(Mod->Name, OS); +} + +bool clang::index::generateUSRFragmentForModuleName(StringRef ModName, + raw_ostream &OS) { + OS << "@M@" << ModName; + return false; +} |
