#include "XRefs.h"
#include "AST.h"
#include "FindSymbols.h"
#include "FindTarget.h"
#include "Headers.h"
#include "HeuristicResolver.h"
#include "IncludeCleaner.h"
#include "ParsedAST.h"
#include "Protocol.h"
#include "Quality.h"
#include "Selection.h"
#include "SourceCode.h"
#include "URI.h"
#include "clang-include-cleaner/Analysis.h"
#include "clang-include-cleaner/Types.h"
#include "index/Index.h"
#include "index/Merge.h"
#include "index/Relation.h"
#include "index/SymbolCollector.h"
#include "index/SymbolID.h"
#include "index/SymbolLocation.h"
#include "support/Logger.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTTypeTraits.h"
#include "clang/AST/Attr.h"
#include "clang/AST/Attrs.inc"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/DeclVisitor.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/StmtCXX.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/Type.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TokenKinds.h"
#include "clang/Index/IndexDataConsumer.h"
#include "clang/Index/IndexSymbol.h"
#include "clang/Index/IndexingAction.h"
#include "clang/Index/IndexingOptions.h"
#include "clang/Index/USRGeneration.h"
#include "clang/Lex/Lexer.h"
#include "clang/Tooling/Syntax/Tokens.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include <optional>
#include <string>
#include <vector>
namespace clang {
namespace clangd {
namespace {
const NamedDecl *getDefinition(const NamedDecl *D) {
assert(D);
if (const auto *TD = dyn_cast<TagDecl>(D))
return TD->getDefinition();
if (const auto *VD = dyn_cast<VarDecl>(D))
return VD->getDefinition();
if (const auto *FD = dyn_cast<FunctionDecl>(D))
return FD->getDefinition();
if (const auto *CTD = dyn_cast<ClassTemplateDecl>(D))
if (const auto *RD = CTD->getTemplatedDecl())
return RD->getDefinition();
if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
if (MD->isThisDeclarationADefinition())
return MD;
auto *DeclCtx = cast<Decl>(MD->getDeclContext());
if (DeclCtx->isInvalidDecl())
return nullptr;
if (const auto *CD = dyn_cast<ObjCContainerDecl>(DeclCtx))
if (const auto *Impl = getCorrespondingObjCImpl(CD))
return Impl->getMethod(MD->getSelector(), MD->isInstanceMethod());
}
if (const auto *CD = dyn_cast<ObjCContainerDecl>(D))
return getCorrespondingObjCImpl(CD);
if (isa<ValueDecl>(D) || isa<TemplateTypeParmDecl>(D) ||
isa<TemplateTemplateParmDecl>(D))
return D;
return nullptr;
}
void logIfOverflow(const SymbolLocation &Loc) {
if (Loc.Start.hasOverflow() || Loc.End.hasOverflow())
log("Possible overflow in symbol location: {0}", Loc);
}
std::optional<Location> toLSPLocation(const SymbolLocation &Loc,
llvm::StringRef TUPath) {
if (!Loc)
return std::nullopt;
auto Uri = URI::parse(Loc.FileURI);
if (!Uri) {
elog("Could not parse URI {0}: {1}", Loc.FileURI, Uri.takeError());
return std::nullopt;
}
auto U = URIForFile::fromURI(*Uri, TUPath);
if (!U) {
elog("Could not resolve URI {0}: {1}", Loc.FileURI, U.takeError());
return std::nullopt;
}
Location LSPLoc;
LSPLoc.uri = std::move(*U);
LSPLoc.range.start.line = Loc.Start.line();
LSPLoc.range.start.character = Loc.Start.column();
LSPLoc.range.end.line = Loc.End.line();
LSPLoc.range.end.character = Loc.End.column();
logIfOverflow(Loc);
return LSPLoc;
}
SymbolLocation toIndexLocation(const Location &Loc, std::string &URIStorage) {
SymbolLocation SymLoc;
URIStorage = Loc.uri.uri();
SymLoc.FileURI = URIStorage.c_str();
SymLoc.Start.setLine(Loc.range.start.line);
SymLoc.Start.setColumn(Loc.range.start.character);
SymLoc.End.setLine(Loc.range.end.line);
SymLoc.End.setColumn(Loc.range.end.character);
return SymLoc;
}
SymbolLocation getPreferredLocation(const Location &ASTLoc,
const SymbolLocation &IdxLoc,
std::string &Scratch) {
Symbol ASTSym, IdxSym;
ASTSym.ID = IdxSym.ID = SymbolID("mock_symbol_id");
ASTSym.CanonicalDeclaration = toIndexLocation(ASTLoc, Scratch);
IdxSym.CanonicalDeclaration = IdxLoc;
auto Merged = mergeSymbol(ASTSym, IdxSym);
return Merged.CanonicalDeclaration;
}
std::vector<std::pair<const NamedDecl *, DeclRelationSet>>
getDeclAtPositionWithRelations(ParsedAST &AST, SourceLocation Pos,
DeclRelationSet Relations,
ASTNodeKind *NodeKind = nullptr) {
unsigned Offset = AST.getSourceManager().getDecomposedSpellingLoc(Pos).second;
std::vector<std::pair<const NamedDecl *, DeclRelationSet>> Result;
auto ResultFromTree = [&](SelectionTree ST) {
if (const SelectionTree::Node *N = ST.commonAncestor()) {
if (NodeKind)
*NodeKind = N->ASTNode.getNodeKind();
if (N->ASTNode.get<Attr>() && N->Parent)
N = N->Parent;
llvm::copy_if(allTargetDecls(N->ASTNode, AST.getHeuristicResolver()),
std::back_inserter(Result),
[&](auto &Entry) { return !(Entry.second & ~Relations); });
}
return !Result.empty();
};
SelectionTree::createEach(AST.getASTContext(), AST.getTokens(), Offset,
Offset, ResultFromTree);
return Result;
}
std::vector<const NamedDecl *>
getDeclAtPosition(ParsedAST &AST, SourceLocation Pos, DeclRelationSet Relations,
ASTNodeKind *NodeKind = nullptr) {
std::vector<const NamedDecl *> Result;
for (auto &Entry :
getDeclAtPositionWithRelations(AST, Pos, Relations, NodeKind))
Result.push_back(Entry.first);
return Result;
}
std::optional<Location> makeLocation(const ASTContext &AST, SourceLocation Loc,
llvm::StringRef TUPath) {
const auto &SM = AST.getSourceManager();
const auto F = SM.getFileEntryRefForID(SM.getFileID(Loc));
if (!F)
return std::nullopt;
auto FilePath = getCanonicalPath(*F, SM.getFileManager());
if (!FilePath) {
log("failed to get path!");
return std::nullopt;
}
Location L;
L.uri = URIForFile::canonicalize(*FilePath, TUPath);
auto TokLen = Lexer::MeasureTokenLength(Loc, SM, AST.getLangOpts());
L.range = halfOpenToRange(
SM, CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(TokLen)));
return L;
}
std::optional<LocatedSymbol> locateFileReferent(const Position &Pos,
ParsedAST &AST,
llvm::StringRef MainFilePath) {
for (auto &Inc : AST.getIncludeStructure().MainFileIncludes) {
if (!Inc.Resolved.empty() && Inc.HashLine == Pos.line) {
LocatedSymbol File;
File.Name = std::string(llvm::sys::path::filename(Inc.Resolved));
File.PreferredDeclaration = {
URIForFile::canonicalize(Inc.Resolved, MainFilePath), Range{}};
File.Definition = File.PreferredDeclaration;
return File;
}
}
return std::nullopt;
}
std::optional<LocatedSymbol>
locateMacroReferent(const syntax::Token &TouchedIdentifier, ParsedAST &AST,
llvm::StringRef MainFilePath) {
if (auto M = locateMacroAt(TouchedIdentifier, AST.getPreprocessor())) {
if (auto Loc =
makeLocation(AST.getASTContext(), M->NameLoc, MainFilePath)) {
LocatedSymbol Macro;
Macro.Name = std::string(M->Name);
Macro.PreferredDeclaration = *Loc;
Macro.Definition = Loc;
Macro.ID = getSymbolID(M->Name, M->Info, AST.getSourceManager());
return Macro;
}
}
return std::nullopt;
}
const NamedDecl *getPreferredDecl(const NamedDecl *D) {
D = llvm::cast<NamedDecl>(D->getCanonicalDecl());
if (const auto *ID = dyn_cast<ObjCInterfaceDecl>(D))
if (const auto *DefinitionID = ID->getDefinition())
return DefinitionID;
if (const auto *PD = dyn_cast<ObjCProtocolDecl>(D))
if (const auto *DefinitionID = PD->getDefinition())
return DefinitionID;
return D;
}
std::vector<LocatedSymbol> findImplementors(llvm::DenseSet<SymbolID> IDs,
RelationKind Predicate,
const SymbolIndex *Index,
llvm::StringRef MainFilePath) {
if (IDs.empty() || !Index)
return {};
static constexpr trace::Metric FindImplementorsMetric(
"find_implementors", trace::Metric::Counter, "case");
switch (Predicate) {
case RelationKind::BaseOf:
FindImplementorsMetric.record(1, "find-base");
break;
case RelationKind::OverriddenBy:
FindImplementorsMetric.record(1, "find-override");
break;
}
RelationsRequest Req;
Req.Predicate = Predicate;
Req.Subjects = std::move(IDs);
std::vector<LocatedSymbol> Results;
Index->relations(Req, [&](const SymbolID &Subject, const Symbol &Object) {
auto DeclLoc =
indexToLSPLocation(Object.CanonicalDeclaration, MainFilePath);
if (!DeclLoc) {
elog("Find overrides: {0}", DeclLoc.takeError());
return;
}
Results.emplace_back();
Results.back().Name = Object.Name.str();
Results.back().PreferredDeclaration = *DeclLoc;
auto DefLoc = indexToLSPLocation(Object.Definition, MainFilePath);
if (!DefLoc) {
elog("Failed to convert location: {0}", DefLoc.takeError());
return;
}
Results.back().Definition = *DefLoc;
});
return Results;
}
void enhanceLocatedSymbolsFromIndex(llvm::MutableArrayRef<LocatedSymbol> Result,
const SymbolIndex *Index,
llvm::StringRef MainFilePath) {
LookupRequest QueryRequest;
llvm::DenseMap<SymbolID, unsigned> ResultIndex;
for (unsigned I = 0; I < Result.size(); ++I) {
if (auto ID = Result[I].ID) {
ResultIndex.try_emplace(ID, I);
QueryRequest.IDs.insert(ID);
}
}
if (!Index || QueryRequest.IDs.empty())
return;
std::string Scratch;
Index->lookup(QueryRequest, [&](const Symbol &Sym) {
auto &R = Result[ResultIndex.lookup(Sym.ID)];
if (R.Definition) {
if (auto Loc = toLSPLocation(Sym.CanonicalDeclaration, MainFilePath))
R.PreferredDeclaration = *Loc;
if (auto Loc = toLSPLocation(
getPreferredLocation(*R.Definition, Sym.Definition, Scratch),
MainFilePath))
R.Definition = *Loc;
} else {
R.Definition = toLSPLocation(Sym.Definition, MainFilePath);
if (auto Loc = toLSPLocation(
getPreferredLocation(R.PreferredDeclaration,
Sym.CanonicalDeclaration, Scratch),
MainFilePath))
R.PreferredDeclaration = *Loc;
}
});
}
std::vector<LocatedSymbol>
locateASTReferent(SourceLocation CurLoc, const syntax::Token *TouchedIdentifier,
ParsedAST &AST, llvm::StringRef MainFilePath,
const SymbolIndex *Index, ASTNodeKind &NodeKind) {
const SourceManager &SM = AST.getSourceManager();
std::vector<LocatedSymbol> Result;
static constexpr trace::Metric LocateASTReferentMetric(
"locate_ast_referent", trace::Metric::Counter, "case");
auto AddResultDecl = [&](const NamedDecl *D) {
D = getPreferredDecl(D);
auto Loc =
makeLocation(AST.getASTContext(), nameLocation(*D, SM), MainFilePath);
if (!Loc)
return;
Result.emplace_back();
Result.back().Name = printName(AST.getASTContext(), *D);
Result.back().PreferredDeclaration = *Loc;
Result.back().ID = getSymbolID(D);
if (const NamedDecl *Def = getDefinition(D))
Result.back().Definition = makeLocation(
AST.getASTContext(), nameLocation(*Def, SM), MainFilePath);
};
DeclRelationSet Relations =
DeclRelation::TemplatePattern | DeclRelation::Alias;
auto Candidates =
getDeclAtPositionWithRelations(AST, CurLoc, Relations, &NodeKind);
llvm::DenseSet<SymbolID> VirtualMethods;
for (const auto &E : Candidates) {
const NamedDecl *D = E.first;
if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D)) {
if (CMD->isPureVirtual()) {
if (TouchedIdentifier && SM.getSpellingLoc(CMD->getLocation()) ==
TouchedIdentifier->location()) {
VirtualMethods.insert(getSymbolID(CMD));
LocateASTReferentMetric.record(1, "method-to-override");
}
}
if (NodeKind.isSame(ASTNodeKind::getFromNodeKind<OverrideAttr>()) ||
NodeKind.isSame(ASTNodeKind::getFromNodeKind<FinalAttr>())) {
for (const NamedDecl *ND : CMD->overridden_methods())
AddResultDecl(ND);
continue;
}
}
if (E.second & DeclRelation::Alias && Candidates.size() > 1 &&
SM.isPointWithin(TouchedIdentifier ? TouchedIdentifier->location()
: CurLoc,
D->getBeginLoc(), D->getEndLoc()))
continue;
if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
if (TouchedIdentifier &&
D->getLocation() == TouchedIdentifier->location()) {
LocateASTReferentMetric.record(1, "template-specialization-to-primary");
AddResultDecl(CTSD->getSpecializedTemplate());
continue;
}
}
if (const auto *CD = dyn_cast<ObjCCategoryDecl>(D))
if (const auto *ID = CD->getClassInterface())
if (TouchedIdentifier &&
(CD->getLocation() == TouchedIdentifier->location() ||
ID->getName() == TouchedIdentifier->text(SM))) {
LocateASTReferentMetric.record(1, "objc-category-to-class");
AddResultDecl(ID);
}
LocateASTReferentMetric.record(1, "regular");
AddResultDecl(D);
}
enhanceLocatedSymbolsFromIndex(Result, Index, MainFilePath);
auto Overrides = findImplementors(VirtualMethods, RelationKind::OverriddenBy,
Index, MainFilePath);
Result.insert(Result.end(), Overrides.begin(), Overrides.end());
return Result;
}
std::vector<LocatedSymbol> locateSymbolForType(const ParsedAST &AST,
const QualType &Type,
const SymbolIndex *Index) {
const auto &SM = AST.getSourceManager();
auto MainFilePath = AST.tuPath();
auto Decls = targetDecl(DynTypedNode::create(Type.getNonReferenceType()),
DeclRelation::TemplatePattern | DeclRelation::Alias,
AST.getHeuristicResolver());
if (Decls.empty())
return {};
std::vector<LocatedSymbol> Results;
const auto &ASTContext = AST.getASTContext();
for (const NamedDecl *D : Decls) {
D = getPreferredDecl(D);
auto Loc = makeLocation(ASTContext, nameLocation(*D, SM), MainFilePath);
if (!Loc)
continue;
Results.emplace_back();
Results.back().Name = printName(ASTContext, *D);
Results.back().PreferredDeclaration = *Loc;
Results.back().ID = getSymbolID(D);
if (const NamedDecl *Def = getDefinition(D))
Results.back().Definition =
makeLocation(ASTContext, nameLocation(*Def, SM), MainFilePath);
}
enhanceLocatedSymbolsFromIndex(Results, Index, MainFilePath);
return Results;
}
bool tokenSpelledAt(SourceLocation SpellingLoc, const syntax::TokenBuffer &TB) {
auto ExpandedTokens = TB.expandedTokens(
TB.sourceManager().getMacroArgExpandedLocation(SpellingLoc));
return !ExpandedTokens.empty();
}
llvm::StringRef sourcePrefix(SourceLocation Loc, const SourceManager &SM) {
auto D = SM.getDecomposedLoc(Loc);
bool Invalid = false;
llvm::StringRef Buf = SM.getBufferData(D.first, &Invalid);
if (Invalid || D.second > Buf.size())
return "";
return Buf.substr(0, D.second);
}
bool isDependentName(ASTNodeKind NodeKind) {
return NodeKind.isSame(ASTNodeKind::getFromNodeKind<OverloadExpr>()) ||
NodeKind.isSame(
ASTNodeKind::getFromNodeKind<CXXDependentScopeMemberExpr>()) ||
NodeKind.isSame(
ASTNodeKind::getFromNodeKind<DependentScopeDeclRefExpr>());
}
}
std::vector<LocatedSymbol> locateSymbolTextually(const SpelledWord &Word,
ParsedAST &AST,
const SymbolIndex *Index,
llvm::StringRef MainFilePath,
ASTNodeKind NodeKind) {
if ((Word.ExpandedToken && !isDependentName(NodeKind)) ||
!Word.LikelyIdentifier || !Index)
return {};
if (Word.PartOfSpelledToken &&
isStringLiteral(Word.PartOfSpelledToken->kind()))
return {};
const auto &SM = AST.getSourceManager();
FuzzyFindRequest Req;
Req.Query = Word.Text.str();
Req.ProximityPaths = {MainFilePath.str()};
Req.Scopes =
visibleNamespaces(sourcePrefix(Word.Location, SM), AST.getLangOpts());
Req.AnyScope = true;
Req.Limit = 10;
bool TooMany = false;
using ScoredLocatedSymbol = std::pair<float, LocatedSymbol>;
std::vector<ScoredLocatedSymbol> ScoredResults;
Index->fuzzyFind(Req, [&](const Symbol &Sym) {
if (Sym.Name != Word.Text)
return;
if (Sym.SymInfo.Kind == index::SymbolKind::Constructor)
return;
auto MaybeDeclLoc =
indexToLSPLocation(Sym.CanonicalDeclaration, MainFilePath);
if (!MaybeDeclLoc) {
log("locateSymbolNamedTextuallyAt: {0}", MaybeDeclLoc.takeError());
return;
}
LocatedSymbol Located;
Located.PreferredDeclaration = *MaybeDeclLoc;
Located.Name = (Sym.Name + Sym.TemplateSpecializationArgs).str();
Located.ID = Sym.ID;
if (Sym.Definition) {
auto MaybeDefLoc = indexToLSPLocation(Sym.Definition, MainFilePath);
if (!MaybeDefLoc) {
log("locateSymbolNamedTextuallyAt: {0}", MaybeDefLoc.takeError());
return;
}
Located.PreferredDeclaration = *MaybeDefLoc;
Located.Definition = *MaybeDefLoc;
}
if (ScoredResults.size() >= 5) {
TooMany = true;
return;
}
SymbolQualitySignals Quality;
Quality.merge(Sym);
SymbolRelevanceSignals Relevance;
Relevance.Name = Sym.Name;
Relevance.Query = SymbolRelevanceSignals::Generic;
Relevance.merge(Sym);
auto Score = evaluateSymbolAndRelevance(Quality.evaluateHeuristics(),
Relevance.evaluateHeuristics());
dlog("locateSymbolNamedTextuallyAt: {0}{1} = {2}\n{3}{4}\n", Sym.Scope,
Sym.Name, Score, Quality, Relevance);
ScoredResults.push_back({Score, std::move(Located)});
});
if (TooMany) {
vlog("Heuristic index lookup for {0} returned too many candidates, ignored",
Word.Text);
return {};
}
llvm::sort(ScoredResults,
[](const ScoredLocatedSymbol &A, const ScoredLocatedSymbol &B) {
return A.first > B.first;
});
std::vector<LocatedSymbol> Results;
for (auto &Res : std::move(ScoredResults))
Results.push_back(std::move(Res.second));
if (Results.empty())
vlog("No heuristic index definition for {0}", Word.Text);
else
log("Found definition heuristically in index for {0}", Word.Text);
return Results;
}
const syntax::Token *findNearbyIdentifier(const SpelledWord &Word,
const syntax::TokenBuffer &TB) {
if (Word.ExpandedToken)
return nullptr;
if (Word.PartOfSpelledToken &&
isStringLiteral(Word.PartOfSpelledToken->kind()))
return {};
const SourceManager &SM = TB.sourceManager();
auto File = SM.getFileID(Word.Location);
unsigned WordLine = SM.getSpellingLineNumber(Word.Location);
auto Cost = [&](SourceLocation Loc) -> unsigned {
assert(SM.getFileID(Loc) == File && "spelled token in wrong file?");
unsigned Line = SM.getSpellingLineNumber(Loc);
return Line >= WordLine ? Line - WordLine : 2 * (WordLine - Line);
};
const syntax::Token *BestTok = nullptr;
unsigned BestCost = -1;
unsigned MaxDistance =
1U << std::min<unsigned>(Word.Text.size(),
std::numeric_limits<unsigned>::digits - 1);
unsigned LineMin =
WordLine + 1 <= MaxDistance / 2 ? 1 : WordLine + 1 - MaxDistance / 2;
unsigned LineMax = WordLine + 1 + MaxDistance;
SourceLocation LocMin = SM.translateLineCol(File, LineMin, 1);
assert(LocMin.isValid());
SourceLocation LocMax = SM.translateLineCol(File, LineMax, 1);
assert(LocMax.isValid());
auto Consider = [&](const syntax::Token &Tok) {
if (Tok.location() < LocMin || Tok.location() > LocMax)
return true;
if (!(Tok.kind() == tok::identifier && Tok.text(SM) == Word.Text))
return false;
if (Tok.location() == Word.Location)
return false;
unsigned TokCost = Cost(Tok.location());
if (TokCost >= BestCost)
return true;
if (!(tokenSpelledAt(Tok.location(), TB) || TB.expansionStartingAt(&Tok)))
return false;
BestCost = TokCost;
BestTok = &Tok;
return false;
};
auto SpelledTokens = TB.spelledTokens(File);
auto *I = llvm::partition_point(SpelledTokens, [&](const syntax::Token &T) {
assert(SM.getFileID(T.location()) == SM.getFileID(Word.Location));
return T.location() < Word.Location;
});
for (const syntax::Token &Tok : llvm::ArrayRef(I, SpelledTokens.end()))
if (Consider(Tok))
break;
for (const syntax::Token &Tok :
llvm::reverse(llvm::ArrayRef(SpelledTokens.begin(), I)))
if (Consider(Tok))
break;
if (BestTok)
vlog(
"Word {0} under cursor {1} isn't a token (after PP), trying nearby {2}",
Word.Text, Word.Location.printToString(SM),
BestTok->location().printToString(SM));
return BestTok;
}
std::vector<LocatedSymbol> locateSymbolAt(ParsedAST &AST, Position Pos,
const SymbolIndex *Index) {
const auto &SM = AST.getSourceManager();
auto MainFilePath = AST.tuPath();
if (auto File = locateFileReferent(Pos, AST, MainFilePath))
return {std::move(*File)};
auto CurLoc = sourceLocationInMainFile(SM, Pos);
if (!CurLoc) {
elog("locateSymbolAt failed to convert position to source location: {0}",
CurLoc.takeError());
return {};
}
const syntax::Token *TouchedIdentifier = nullptr;
auto TokensTouchingCursor =
syntax::spelledTokensTouching(*CurLoc, AST.getTokens());
for (const syntax::Token &Tok : TokensTouchingCursor) {
if (Tok.kind() == tok::identifier) {
if (auto Macro = locateMacroReferent(Tok, AST, MainFilePath))
return {*std::move(Macro)};
TouchedIdentifier = &Tok;
break;
}
if (Tok.kind() == tok::kw_auto || Tok.kind() == tok::kw_decltype) {
if (auto Deduced = getDeducedType(AST.getASTContext(), Tok.location())) {
auto LocSym = locateSymbolForType(AST, *Deduced, Index);
if (!LocSym.empty())
return LocSym;
}
}
}
ASTNodeKind NodeKind;
auto ASTResults = locateASTReferent(*CurLoc, TouchedIdentifier, AST,
MainFilePath, Index, NodeKind);
if (!ASTResults.empty())
return ASTResults;
auto Word =
SpelledWord::touching(*CurLoc, AST.getTokens(), AST.getLangOpts());
if (Word) {
if (const syntax::Token *NearbyIdent =
findNearbyIdentifier(*Word, AST.getTokens())) {
if (auto Macro = locateMacroReferent(*NearbyIdent, AST, MainFilePath)) {
log("Found macro definition heuristically using nearby identifier {0}",
Word->Text);
return {*std::move(Macro)};
}
ASTResults = locateASTReferent(NearbyIdent->location(), NearbyIdent, AST,
MainFilePath, Index, NodeKind);
if (!ASTResults.empty()) {
log("Found definition heuristically using nearby identifier {0}",
NearbyIdent->text(SM));
return ASTResults;
}
vlog("No definition found using nearby identifier {0} at {1}", Word->Text,
Word->Location.printToString(SM));
}
auto TextualResults =
locateSymbolTextually(*Word, AST, Index, MainFilePath, NodeKind);
if (!TextualResults.empty())
return TextualResults;
}
return {};
}
std::vector<DocumentLink> getDocumentLinks(ParsedAST &AST) {
const auto &SM = AST.getSourceManager();
std::vector<DocumentLink> Result;
for (auto &Inc : AST.getIncludeStructure().MainFileIncludes) {
if (Inc.Resolved.empty())
continue;
auto HashLoc = SM.getComposedLoc(SM.getMainFileID(), Inc.HashOffset);
const auto *HashTok = AST.getTokens().spelledTokenContaining(HashLoc);
assert(HashTok && "got inclusion at wrong offset");
const auto *IncludeTok = std::next(HashTok);
const auto *FileTok = std::next(IncludeTok);
auto FileRange =
syntax::FileRange(SM, FileTok->location(), Inc.Written.length())
.toCharRange(SM);
Result.push_back(
DocumentLink({halfOpenToRange(SM, FileRange),
URIForFile::canonicalize(Inc.Resolved, AST.tuPath())}));
}
return Result;
}
namespace {
class ReferenceFinder : public index::IndexDataConsumer {
public:
struct Reference {
syntax::Token SpelledTok;
index::SymbolRoleSet Role;
const Decl *Container;
Range range(const SourceManager &SM) const {
return halfOpenToRange(SM, SpelledTok.range(SM).toCharRange(SM));
}
};
ReferenceFinder(const ParsedAST &AST,
const llvm::ArrayRef<const NamedDecl *> Targets,
bool PerToken)
: PerToken(PerToken), AST(AST) {
for (const NamedDecl *ND : Targets)
TargetDecls.insert(ND->getCanonicalDecl());
}
std::vector<Reference> take() && {
llvm::sort(References, [](const Reference &L, const Reference &R) {
auto LTok = L.SpelledTok.location();
auto RTok = R.SpelledTok.location();
return std::tie(LTok, L.Role) < std::tie(RTok, R.Role);
});
References.erase(std::unique(References.begin(), References.end(),
[](const Reference &L, const Reference &R) {
auto LTok = L.SpelledTok.location();
auto RTok = R.SpelledTok.location();
return std::tie(LTok, L.Role) ==
std::tie(RTok, R.Role);
}),
References.end());
return std::move(References);
}
bool
handleDeclOccurrence(const Decl *D, index::SymbolRoleSet Roles,
llvm::ArrayRef<index::SymbolRelation> Relations,
SourceLocation Loc,
index::IndexDataConsumer::ASTNodeInfo ASTNode) override {
if (!TargetDecls.contains(D->getCanonicalDecl()))
return true;
const SourceManager &SM = AST.getSourceManager();
if (!isInsideMainFile(Loc, SM))
return true;
const auto &TB = AST.getTokens();
llvm::SmallVector<SourceLocation, 1> Locs;
if (PerToken) {
if (auto *OME = llvm::dyn_cast_or_null<ObjCMessageExpr>(ASTNode.OrigE)) {
OME->getSelectorLocs(Locs);
} else if (auto *OMD =
llvm::dyn_cast_or_null<ObjCMethodDecl>(ASTNode.OrigD)) {
OMD->getSelectorLocs(Locs);
}
if (!Locs.empty() && Locs.front() != Loc)
Locs.clear();
}
if (Locs.empty())
Locs.push_back(Loc);
SymbolCollector::Options CollectorOpts;
CollectorOpts.CollectMainFileSymbols = true;
for (SourceLocation L : Locs) {
L = SM.getFileLoc(L);
if (const auto *Tok = TB.spelledTokenContaining(L))
References.push_back(
{*Tok, Roles,
SymbolCollector::getRefContainer(ASTNode.Parent, CollectorOpts)});
}
return true;
}
private:
bool PerToken;
std::vector<Reference> References;
const ParsedAST &AST;
llvm::DenseSet<const Decl *> TargetDecls;
};
std::vector<ReferenceFinder::Reference>
findRefs(const llvm::ArrayRef<const NamedDecl *> TargetDecls, ParsedAST &AST,
bool PerToken) {
ReferenceFinder RefFinder(AST, TargetDecls, PerToken);
index::IndexingOptions IndexOpts;
IndexOpts.SystemSymbolFilter =
index::IndexingOptions::SystemSymbolFilterKind::All;
IndexOpts.IndexFunctionLocals = true;
IndexOpts.IndexParametersInDeclarations = true;
IndexOpts.IndexTemplateParameters = true;
indexTopLevelDecls(AST.getASTContext(), AST.getPreprocessor(),
AST.getLocalTopLevelDecls(), RefFinder, IndexOpts);
return std::move(RefFinder).take();
}
const Stmt *getFunctionBody(DynTypedNode N) {
if (const auto *FD = N.get<FunctionDecl>())
return FD->getBody();
if (const auto *FD = N.get<BlockDecl>())
return FD->getBody();
if (const auto *FD = N.get<LambdaExpr>())
return FD->getBody();
if (const auto *FD = N.get<ObjCMethodDecl>())
return FD->getBody();
return nullptr;
}
const Stmt *getLoopBody(DynTypedNode N) {
if (const auto *LS = N.get<ForStmt>())
return LS->getBody();
if (const auto *LS = N.get<CXXForRangeStmt>())
return LS->getBody();
if (const auto *LS = N.get<WhileStmt>())
return LS->getBody();
if (const auto *LS = N.get<DoStmt>())
return LS->getBody();
return nullptr;
}
class FindControlFlow : public RecursiveASTVisitor<FindControlFlow> {
enum Target {
Break = 1,
Continue = 2,
Return = 4,
Case = 8,
Throw = 16,
Goto = 32,
All = Break | Continue | Return | Case | Throw | Goto,
};
int Ignore = 0;
SourceRange Bounds;
std::vector<SourceLocation> &Result;
const SourceManager &SM;
template <typename Func>
bool filterAndTraverse(DynTypedNode D, const Func &Delegate) {
auto RestoreIgnore = llvm::make_scope_exit(
[OldIgnore(Ignore), this] { Ignore = OldIgnore; });
if (getFunctionBody(D))
Ignore = All;
else if (getLoopBody(D))
Ignore |= Continue | Break;
else if (D.get<SwitchStmt>())
Ignore |= Break | Case;
return (Ignore == All) ? true : Delegate();
}
void found(Target T, SourceLocation Loc) {
if (T & Ignore)
return;
if (SM.isBeforeInTranslationUnit(Loc, Bounds.getBegin()) ||
SM.isBeforeInTranslationUnit(Bounds.getEnd(), Loc))
return;
Result.push_back(Loc);
}
public:
FindControlFlow(SourceRange Bounds, std::vector<SourceLocation> &Result,
const SourceManager &SM)
: Bounds(Bounds), Result(Result), SM(SM) {}
bool TraverseDecl(Decl *D) {
return !D || filterAndTraverse(DynTypedNode::create(*D), [&] {
return RecursiveASTVisitor::TraverseDecl(D);
});
}
bool TraverseStmt(Stmt *S) {
return !S || filterAndTraverse(DynTypedNode::create(*S), [&] {
return RecursiveASTVisitor::TraverseStmt(S);
});
}
bool VisitReturnStmt(ReturnStmt *R) {
found(Return, R->getReturnLoc());
return true;
}
bool VisitBreakStmt(BreakStmt *B) {
found(Break, B->getBreakLoc());
return true;
}
bool VisitContinueStmt(ContinueStmt *C) {
found(Continue, C->getContinueLoc());
return true;
}
bool VisitSwitchCase(SwitchCase *C) {
found(Case, C->getKeywordLoc());
return true;
}
bool VisitCXXThrowExpr(CXXThrowExpr *T) {
found(Throw, T->getThrowLoc());
return true;
}
bool VisitGotoStmt(GotoStmt *G) {
if (const auto *LD = G->getLabel()) {
if (SM.isBeforeInTranslationUnit(LD->getLocation(), Bounds.getBegin()) ||
SM.isBeforeInTranslationUnit(Bounds.getEnd(), LD->getLocation()))
found(Goto, G->getGotoLoc());
}
return true;
}
};
SourceRange findCaseBounds(const SwitchStmt &Switch, SourceLocation Loc,
const SourceManager &SM) {
std::vector<const SwitchCase *> Cases;
for (const SwitchCase *Case = Switch.getSwitchCaseList(); Case;
Case = Case->getNextSwitchCase())
Cases.push_back(Case);
llvm::sort(Cases, [&](const SwitchCase *L, const SwitchCase *R) {
return SM.isBeforeInTranslationUnit(L->getKeywordLoc(), R->getKeywordLoc());
});
auto CaseAfter = llvm::partition_point(Cases, [&](const SwitchCase *C) {
return !SM.isBeforeInTranslationUnit(Loc, C->getKeywordLoc());
});
SourceLocation End = CaseAfter == Cases.end() ? Switch.getEndLoc()
: (*CaseAfter)->getKeywordLoc();
if (CaseAfter == Cases.begin())
return SourceRange(Switch.getBeginLoc(), End);
auto CaseBefore = std::prev(CaseAfter);
while (CaseBefore != Cases.begin() &&
(*std::prev(CaseBefore))->getSubStmt() == *CaseBefore)
--CaseBefore;
return SourceRange((*CaseBefore)->getKeywordLoc(), End);
}
std::vector<SourceLocation> relatedControlFlow(const SelectionTree::Node &N) {
const SourceManager &SM =
N.getDeclContext().getParentASTContext().getSourceManager();
std::vector<SourceLocation> Result;
enum class Cur { None, Break, Continue, Return, Case, Throw } Cursor;
if (N.ASTNode.get<BreakStmt>()) {
Cursor = Cur::Break;
} else if (N.ASTNode.get<ContinueStmt>()) {
Cursor = Cur::Continue;
} else if (N.ASTNode.get<ReturnStmt>()) {
Cursor = Cur::Return;
} else if (N.ASTNode.get<CXXThrowExpr>()) {
Cursor = Cur::Throw;
} else if (N.ASTNode.get<SwitchCase>()) {
Cursor = Cur::Case;
} else if (const GotoStmt *GS = N.ASTNode.get<GotoStmt>()) {
Result.push_back(GS->getGotoLoc());
if (const auto *LD = GS->getLabel())
Result.push_back(LD->getLocation());
Cursor = Cur::None;
} else {
Cursor = Cur::None;
}
const Stmt *Root = nullptr;
SourceRange Bounds;
for (const auto *P = &N; P; P = P->Parent) {
if (const Stmt *FunctionBody = getFunctionBody(P->ASTNode)) {
if (Cursor == Cur::Return || Cursor == Cur::Throw) {
Root = FunctionBody;
}
break;
}
if (const Stmt *LoopBody = getLoopBody(P->ASTNode)) {
if (Cursor == Cur::None || Cursor == Cur::Break ||
Cursor == Cur::Continue) {
Root = LoopBody;
Result.push_back(P->ASTNode.getSourceRange().getBegin());
break;
}
}
if (const auto *SS = P->ASTNode.get<SwitchStmt>()) {
if (Cursor == Cur::Break || Cursor == Cur::Case) {
Result.push_back(SS->getSwitchLoc());
Root = SS->getBody();
Bounds = findCaseBounds(*SS, N.ASTNode.getSourceRange().getBegin(), SM);
break;
}
}
if (Cursor == Cur::None)
break;
}
if (Root) {
if (!Bounds.isValid())
Bounds = Root->getSourceRange();
FindControlFlow(Bounds, Result, SM).TraverseStmt(const_cast<Stmt *>(Root));
}
return Result;
}
DocumentHighlight toHighlight(const ReferenceFinder::Reference &Ref,
const SourceManager &SM) {
DocumentHighlight DH;
DH.range = Ref.range(SM);
if (Ref.Role & index::SymbolRoleSet(index::SymbolRole::Write))
DH.kind = DocumentHighlightKind::Write;
else if (Ref.Role & index::SymbolRoleSet(index::SymbolRole::Read))
DH.kind = DocumentHighlightKind::Read;
else
DH.kind = DocumentHighlightKind::Text;
return DH;
}
std::optional<DocumentHighlight> toHighlight(SourceLocation Loc,
const syntax::TokenBuffer &TB) {
Loc = TB.sourceManager().getFileLoc(Loc);
if (const auto *Tok = TB.spelledTokenContaining(Loc)) {
DocumentHighlight Result;
Result.range = halfOpenToRange(
TB.sourceManager(),
CharSourceRange::getCharRange(Tok->location(), Tok->endLocation()));
return Result;
}
return std::nullopt;
}
}
std::vector<DocumentHighlight> findDocumentHighlights(ParsedAST &AST,
Position Pos) {
const SourceManager &SM = AST.getSourceManager();
auto CurLoc = sourceLocationInMainFile(SM, Pos);
if (!CurLoc) {
llvm::consumeError(CurLoc.takeError());
return {};
}
std::vector<DocumentHighlight> Result;
auto TryTree = [&](SelectionTree ST) {
if (const SelectionTree::Node *N = ST.commonAncestor()) {
DeclRelationSet Relations =
DeclRelation::TemplatePattern | DeclRelation::Alias;
auto TargetDecls =
targetDecl(N->ASTNode, Relations, AST.getHeuristicResolver());
if (!TargetDecls.empty()) {
for (const auto &Ref : findRefs(TargetDecls, AST, true))
Result.push_back(toHighlight(Ref, SM));
return true;
}
auto ControlFlow = relatedControlFlow(*N);
if (!ControlFlow.empty()) {
for (SourceLocation Loc : ControlFlow)
if (auto Highlight = toHighlight(Loc, AST.getTokens()))
Result.push_back(std::move(*Highlight));
return true;
}
}
return false;
};
unsigned Offset =
AST.getSourceManager().getDecomposedSpellingLoc(*CurLoc).second;
SelectionTree::createEach(AST.getASTContext(), AST.getTokens(), Offset,
Offset, TryTree);
return Result;
}
std::vector<LocatedSymbol> findImplementations(ParsedAST &AST, Position Pos,
const SymbolIndex *Index) {
if (!Index)
return {};
const SourceManager &SM = AST.getSourceManager();
auto CurLoc = sourceLocationInMainFile(SM, Pos);
if (!CurLoc) {
elog("Failed to convert position to source location: {0}",
CurLoc.takeError());
return {};
}
DeclRelationSet Relations =
DeclRelation::TemplatePattern | DeclRelation::Alias;
llvm::DenseSet<SymbolID> IDs;
RelationKind QueryKind = RelationKind::OverriddenBy;
for (const NamedDecl *ND : getDeclAtPosition(AST, *CurLoc, Relations)) {
if (const auto *CXXMD = llvm::dyn_cast<CXXMethodDecl>(ND)) {
if (CXXMD->isVirtual()) {
IDs.insert(getSymbolID(ND));
QueryKind = RelationKind::OverriddenBy;
}
} else if (const auto *RD = dyn_cast<CXXRecordDecl>(ND)) {
IDs.insert(getSymbolID(RD));
QueryKind = RelationKind::BaseOf;
}
}
return findImplementors(std::move(IDs), QueryKind, Index, AST.tuPath());
}
namespace {
void getOverriddenMethods(const CXXMethodDecl *CMD,
llvm::DenseSet<SymbolID> &OverriddenMethods) {
if (!CMD)
return;
for (const CXXMethodDecl *Base : CMD->overridden_methods()) {
if (auto ID = getSymbolID(Base))
OverriddenMethods.insert(ID);
getOverriddenMethods(Base, OverriddenMethods);
}
}
std::optional<std::string>
stringifyContainerForMainFileRef(const Decl *Container) {
if (auto *ND = llvm::dyn_cast_if_present<NamedDecl>(Container))
return printQualifiedName(*ND);
return {};
}
std::optional<ReferencesResult>
maybeFindIncludeReferences(ParsedAST &AST, Position Pos,
URIForFile URIMainFile) {
const auto &Includes = AST.getIncludeStructure().MainFileIncludes;
auto IncludeOnLine = llvm::find_if(Includes, [&Pos](const Inclusion &Inc) {
return Inc.HashLine == Pos.line;
});
if (IncludeOnLine == Includes.end())
return std::nullopt;
const SourceManager &SM = AST.getSourceManager();
ReferencesResult Results;
auto Converted = convertIncludes(AST);
include_cleaner::walkUsed(
AST.getLocalTopLevelDecls(), collectMacroReferences(AST),
&AST.getPragmaIncludes(), AST.getPreprocessor(),
[&](const include_cleaner::SymbolReference &Ref,
llvm::ArrayRef<include_cleaner::Header> Providers) {
if (Ref.RT != include_cleaner::RefType::Explicit ||
!isPreferredProvider(*IncludeOnLine, Converted, Providers))
return;
auto Loc = SM.getFileLoc(Ref.RefLocation);
while (SM.getFileID(Loc) != SM.getMainFileID())
Loc = SM.getIncludeLoc(SM.getFileID(Loc));
ReferencesResult::Reference Result;
const auto *Token = AST.getTokens().spelledTokenContaining(Loc);
assert(Token && "references expected token here");
Result.Loc.range = Range{sourceLocToPosition(SM, Token->location()),
sourceLocToPosition(SM, Token->endLocation())};
Result.Loc.uri = URIMainFile;
Results.References.push_back(std::move(Result));
});
if (Results.References.empty())
return std::nullopt;
ReferencesResult::Reference Result;
Result.Loc.range = rangeTillEOL(SM.getBufferData(SM.getMainFileID()),
IncludeOnLine->HashOffset);
Result.Loc.uri = URIMainFile;
Results.References.push_back(std::move(Result));
return Results;
}
}
ReferencesResult findReferences(ParsedAST &AST, Position Pos, uint32_t Limit,
const SymbolIndex *Index, bool AddContext) {
ReferencesResult Results;
const SourceManager &SM = AST.getSourceManager();
auto MainFilePath = AST.tuPath();
auto URIMainFile = URIForFile::canonicalize(MainFilePath, MainFilePath);
auto CurLoc = sourceLocationInMainFile(SM, Pos);
if (!CurLoc) {
llvm::consumeError(CurLoc.takeError());
return {};
}
const auto IncludeReferences =
maybeFindIncludeReferences(AST, Pos, URIMainFile);
if (IncludeReferences)
return *IncludeReferences;
llvm::DenseSet<SymbolID> IDsToQuery, OverriddenMethods;
const auto *IdentifierAtCursor =
syntax::spelledIdentifierTouching(*CurLoc, AST.getTokens());
std::optional<DefinedMacro> Macro;
if (IdentifierAtCursor)
Macro = locateMacroAt(*IdentifierAtCursor, AST.getPreprocessor());
if (Macro) {
if (auto MacroSID = getSymbolID(Macro->Name, Macro->Info, SM)) {
const auto &IDToRefs = AST.getMacros().MacroRefs;
auto Refs = IDToRefs.find(MacroSID);
if (Refs != IDToRefs.end()) {
for (const auto &Ref : Refs->second) {
ReferencesResult::Reference Result;
Result.Loc.range = Ref.toRange(SM);
Result.Loc.uri = URIMainFile;
if (Ref.IsDefinition) {
Result.Attributes |= ReferencesResult::Declaration;
Result.Attributes |= ReferencesResult::Definition;
}
Results.References.push_back(std::move(Result));
}
}
IDsToQuery.insert(MacroSID);
}
} else {
DeclRelationSet Relations =
DeclRelation::TemplatePattern | DeclRelation::Alias;
std::vector<const NamedDecl *> Decls =
getDeclAtPosition(AST, *CurLoc, Relations);
llvm::SmallVector<const NamedDecl *> TargetsInMainFile;
for (const NamedDecl *D : Decls) {
auto ID = getSymbolID(D);
if (!ID)
continue;
TargetsInMainFile.push_back(D);
if (D->getParentFunctionOrMethod())
continue;
IDsToQuery.insert(ID);
}
RelationsRequest OverriddenBy;
if (Index) {
OverriddenBy.Predicate = RelationKind::OverriddenBy;
for (const NamedDecl *ND : Decls) {
if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(ND)) {
if (CMD->isVirtual()) {
if (auto ID = getSymbolID(CMD))
OverriddenBy.Subjects.insert(ID);
getOverriddenMethods(CMD, OverriddenMethods);
}
}
}
}
auto MainFileRefs = findRefs(TargetsInMainFile, AST, false);
MainFileRefs.erase(std::unique(MainFileRefs.begin(), MainFileRefs.end(),
[](const ReferenceFinder::Reference &L,
const ReferenceFinder::Reference &R) {
return L.SpelledTok.location() ==
R.SpelledTok.location();
}),
MainFileRefs.end());
for (const auto &Ref : MainFileRefs) {
ReferencesResult::Reference Result;
Result.Loc.range = Ref.range(SM);
Result.Loc.uri = URIMainFile;
if (AddContext)
Result.Loc.containerName =
stringifyContainerForMainFileRef(Ref.Container);
if (Ref.Role & static_cast<unsigned>(index::SymbolRole::Declaration))
Result.Attributes |= ReferencesResult::Declaration;
if (Ref.Role & static_cast<unsigned>(index::SymbolRole::Definition))
Result.Attributes |=
ReferencesResult::Definition | ReferencesResult::Declaration;
Results.References.push_back(std::move(Result));
}
if (Index && !OverriddenBy.Subjects.empty()) {
LookupRequest ContainerLookup;
llvm::DenseMap<SymbolID, size_t> RefIndexForContainer;
Index->relations(OverriddenBy, [&](const SymbolID &Subject,
const Symbol &Object) {
if (Limit && Results.References.size() >= Limit) {
Results.HasMore = true;
return;
}
const auto LSPLocDecl =
toLSPLocation(Object.CanonicalDeclaration, MainFilePath);
const auto LSPLocDef = toLSPLocation(Object.Definition, MainFilePath);
if (LSPLocDecl && LSPLocDecl != LSPLocDef) {
ReferencesResult::Reference Result;
Result.Loc = {std::move(*LSPLocDecl), std::nullopt};
Result.Attributes =
ReferencesResult::Declaration | ReferencesResult::Override;
RefIndexForContainer.insert({Object.ID, Results.References.size()});
ContainerLookup.IDs.insert(Object.ID);
Results.References.push_back(std::move(Result));
}
if (LSPLocDef) {
ReferencesResult::Reference Result;
Result.Loc = {std::move(*LSPLocDef), std::nullopt};
Result.Attributes = ReferencesResult::Declaration |
ReferencesResult::Definition |
ReferencesResult::Override;
RefIndexForContainer.insert({Object.ID, Results.References.size()});
ContainerLookup.IDs.insert(Object.ID);
Results.References.push_back(std::move(Result));
}
});
if (!ContainerLookup.IDs.empty() && AddContext)
Index->lookup(ContainerLookup, [&](const Symbol &Container) {
auto Ref = RefIndexForContainer.find(Container.ID);
assert(Ref != RefIndexForContainer.end());
Results.References[Ref->getSecond()].Loc.containerName =
Container.Scope.str() + Container.Name.str();
});
}
}
auto QueryIndex = [&](llvm::DenseSet<SymbolID> IDs, bool AllowAttributes,
bool AllowMainFileSymbols) {
if (IDs.empty() || !Index || Results.HasMore)
return;
RefsRequest Req;
Req.IDs = std::move(IDs);
if (Limit) {
if (Limit < Results.References.size()) {
Req.Limit = 0;
} else {
Req.Limit = Limit - Results.References.size();
}
}
LookupRequest ContainerLookup;
llvm::DenseMap<SymbolID, std::vector<size_t>> RefIndicesForContainer;
Results.HasMore |= Index->refs(Req, [&](const Ref &R) {
auto LSPLoc = toLSPLocation(R.Location, MainFilePath);
if (!LSPLoc ||
(!AllowMainFileSymbols && LSPLoc->uri.file() == MainFilePath))
return;
ReferencesResult::Reference Result;
Result.Loc = {std::move(*LSPLoc), std::nullopt};
if (AllowAttributes) {
if ((R.Kind & RefKind::Declaration) == RefKind::Declaration)
Result.Attributes |= ReferencesResult::Declaration;
if ((R.Kind & RefKind::Definition) == RefKind::Definition)
Result.Attributes |=
ReferencesResult::Declaration | ReferencesResult::Definition;
}
if (AddContext) {
SymbolID Container = R.Container;
ContainerLookup.IDs.insert(Container);
RefIndicesForContainer[Container].push_back(Results.References.size());
}
Results.References.push_back(std::move(Result));
});
if (!ContainerLookup.IDs.empty() && AddContext)
Index->lookup(ContainerLookup, [&](const Symbol &Container) {
auto Ref = RefIndicesForContainer.find(Container.ID);
assert(Ref != RefIndicesForContainer.end());
auto ContainerName = Container.Scope.str() + Container.Name.str();
for (auto I : Ref->getSecond()) {
Results.References[I].Loc.containerName = ContainerName;
}
});
};
QueryIndex(std::move(IDsToQuery), true,
false);
QueryIndex(std::move(OverriddenMethods), false,
true);
return Results;
}
std::vector<SymbolDetails> getSymbolInfo(ParsedAST &AST, Position Pos) {
const SourceManager &SM = AST.getSourceManager();
auto CurLoc = sourceLocationInMainFile(SM, Pos);
if (!CurLoc) {
llvm::consumeError(CurLoc.takeError());
return {};
}
auto MainFilePath = AST.tuPath();
std::vector<SymbolDetails> Results;
DeclRelationSet Relations = DeclRelation::TemplatePattern |
DeclRelation::Alias | DeclRelation::Underlying;
for (const NamedDecl *D : getDeclAtPosition(AST, *CurLoc, Relations)) {
D = getPreferredDecl(D);
SymbolDetails NewSymbol;
std::string QName = printQualifiedName(*D);
auto SplitQName = splitQualifiedName(QName);
NewSymbol.containerName = std::string(SplitQName.first);
NewSymbol.name = std::string(SplitQName.second);
if (NewSymbol.containerName.empty()) {
if (const auto *ParentND =
dyn_cast_or_null<NamedDecl>(D->getDeclContext()))
NewSymbol.containerName = printQualifiedName(*ParentND);
}
llvm::SmallString<32> USR;
if (!index::generateUSRForDecl(D, USR)) {
NewSymbol.USR = std::string(USR);
NewSymbol.ID = SymbolID(NewSymbol.USR);
}
if (const NamedDecl *Def = getDefinition(D))
NewSymbol.definitionRange = makeLocation(
AST.getASTContext(), nameLocation(*Def, SM), MainFilePath);
NewSymbol.declarationRange =
makeLocation(AST.getASTContext(), nameLocation(*D, SM), MainFilePath);
Results.push_back(std::move(NewSymbol));
}
const auto *IdentifierAtCursor =
syntax::spelledIdentifierTouching(*CurLoc, AST.getTokens());
if (!IdentifierAtCursor)
return Results;
if (auto M = locateMacroAt(*IdentifierAtCursor, AST.getPreprocessor())) {
SymbolDetails NewMacro;
NewMacro.name = std::string(M->Name);
llvm::SmallString<32> USR;
if (!index::generateUSRForMacro(NewMacro.name, M->Info->getDefinitionLoc(),
SM, USR)) {
NewMacro.USR = std::string(USR);
NewMacro.ID = SymbolID(NewMacro.USR);
}
Results.push_back(std::move(NewMacro));
}
return Results;
}
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const LocatedSymbol &S) {
OS << S.Name << ": " << S.PreferredDeclaration;
if (S.Definition)
OS << " def=" << *S.Definition;
return OS;
}
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
const ReferencesResult::Reference &R) {
OS << R.Loc;
if (R.Attributes & ReferencesResult::Declaration)
OS << " [decl]";
if (R.Attributes & ReferencesResult::Definition)
OS << " [def]";
if (R.Attributes & ReferencesResult::Override)
OS << " [override]";
return OS;
}
template <typename HierarchyItem>
static std::optional<HierarchyItem>
declToHierarchyItem(const NamedDecl &ND, llvm::StringRef TUPath) {
ASTContext &Ctx = ND.getASTContext();
auto &SM = Ctx.getSourceManager();
SourceLocation NameLoc = nameLocation(ND, Ctx.getSourceManager());
SourceLocation BeginLoc = SM.getFileLoc(ND.getBeginLoc());
SourceLocation EndLoc = SM.getFileLoc(ND.getEndLoc());
const auto DeclRange =
toHalfOpenFileRange(SM, Ctx.getLangOpts(), {BeginLoc, EndLoc});
if (!DeclRange)
return std::nullopt;
const auto FE = SM.getFileEntryRefForID(SM.getFileID(NameLoc));
if (!FE)
return std::nullopt;
auto FilePath = getCanonicalPath(*FE, SM.getFileManager());
if (!FilePath)
return std::nullopt;
Position NameBegin = sourceLocToPosition(SM, NameLoc);
Position NameEnd = sourceLocToPosition(
SM, Lexer::getLocForEndOfToken(NameLoc, 0, SM, Ctx.getLangOpts()));
index::SymbolInfo SymInfo = index::getSymbolInfo(&ND);
SymbolKind SK = indexSymbolKindToSymbolKind(SymInfo.Kind);
HierarchyItem HI;
HI.name = printName(Ctx, ND);
HI.kind = SK;
HI.range = Range{sourceLocToPosition(SM, DeclRange->getBegin()),
sourceLocToPosition(SM, DeclRange->getEnd())};
HI.selectionRange = Range{NameBegin, NameEnd};
if (!HI.range.contains(HI.selectionRange)) {
HI.range = HI.selectionRange;
}
HI.uri = URIForFile::canonicalize(*FilePath, TUPath);
return HI;
}
static std::optional<TypeHierarchyItem>
declToTypeHierarchyItem(const NamedDecl &ND, llvm::StringRef TUPath) {
auto Result = declToHierarchyItem<TypeHierarchyItem>(ND, TUPath);
if (Result) {
Result->deprecated = ND.isDeprecated();
Result->data.symbolID = getSymbolID(&ND);
}
return Result;
}
static std::optional<CallHierarchyItem>
declToCallHierarchyItem(const NamedDecl &ND, llvm::StringRef TUPath) {
auto Result = declToHierarchyItem<CallHierarchyItem>(ND, TUPath);
if (!Result)
return Result;
if (ND.isDeprecated())
Result->tags.push_back(SymbolTag::Deprecated);
if (auto ID = getSymbolID(&ND))
Result->data = ID.str();
return Result;
}
template <typename HierarchyItem>
static std::optional<HierarchyItem> symbolToHierarchyItem(const Symbol &S,
PathRef TUPath) {
auto Loc = symbolToLocation(S, TUPath);
if (!Loc) {
elog("Failed to convert symbol to hierarchy item: {0}", Loc.takeError());
return std::nullopt;
}
HierarchyItem HI;
HI.name = std::string(S.Name);
HI.kind = indexSymbolKindToSymbolKind(S.SymInfo.Kind);
HI.selectionRange = Loc->range;
HI.range = HI.selectionRange;
HI.uri = Loc->uri;
return HI;
}
static std::optional<TypeHierarchyItem>
symbolToTypeHierarchyItem(const Symbol &S, PathRef TUPath) {
auto Result = symbolToHierarchyItem<TypeHierarchyItem>(S, TUPath);
if (Result) {
Result->deprecated = (S.Flags & Symbol::Deprecated);
Result->data.symbolID = S.ID;
}
return Result;
}
static std::optional<CallHierarchyItem>
symbolToCallHierarchyItem(const Symbol &S, PathRef TUPath) {
auto Result = symbolToHierarchyItem<CallHierarchyItem>(S, TUPath);
if (!Result)
return Result;
Result->data = S.ID.str();
if (S.Flags & Symbol::Deprecated)
Result->tags.push_back(SymbolTag::Deprecated);
return Result;
}
static void fillSubTypes(const SymbolID &ID,
std::vector<TypeHierarchyItem> &SubTypes,
const SymbolIndex *Index, int Levels, PathRef TUPath) {
RelationsRequest Req;
Req.Subjects.insert(ID);
Req.Predicate = RelationKind::BaseOf;
Index->relations(Req, [&](const SymbolID &Subject, const Symbol &Object) {
if (std::optional<TypeHierarchyItem> ChildSym =
symbolToTypeHierarchyItem(Object, TUPath)) {
if (Levels > 1) {
ChildSym->children.emplace();
fillSubTypes(Object.ID, *ChildSym->children, Index, Levels - 1, TUPath);
}
SubTypes.emplace_back(std::move(*ChildSym));
}
});
}
using RecursionProtectionSet = llvm::SmallSet<const CXXRecordDecl *, 4>;
static void fillSuperTypes(const CXXRecordDecl &CXXRD, llvm::StringRef TUPath,
TypeHierarchyItem &Item,
RecursionProtectionSet &RPSet) {
Item.parents.emplace();
Item.data.parents.emplace();
auto *Pattern = CXXRD.getDescribedTemplate() ? &CXXRD : nullptr;
if (Pattern) {
if (!RPSet.insert(Pattern).second) {
return;
}
}
for (const CXXRecordDecl *ParentDecl : typeParents(&CXXRD)) {
if (std::optional<TypeHierarchyItem> ParentSym =
declToTypeHierarchyItem(*ParentDecl, TUPath)) {
fillSuperTypes(*ParentDecl, TUPath, *ParentSym, RPSet);
Item.data.parents->emplace_back(ParentSym->data);
Item.parents->emplace_back(std::move(*ParentSym));
}
}
if (Pattern) {
RPSet.erase(Pattern);
}
}
std::vector<const CXXRecordDecl *> findRecordTypeAt(ParsedAST &AST,
Position Pos) {
auto RecordFromNode = [&AST](const SelectionTree::Node *N) {
std::vector<const CXXRecordDecl *> Records;
if (!N)
return Records;
auto Decls = explicitReferenceTargets(N->ASTNode, DeclRelation::Underlying,
AST.getHeuristicResolver());
for (const NamedDecl *D : Decls) {
if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
if (const auto *RD = VD->getType().getTypePtr()->getAsCXXRecordDecl())
Records.push_back(RD);
continue;
}
if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) {
Records.push_back(Method->getParent());
continue;
}
if (auto *RD = dyn_cast<CXXRecordDecl>(D))
Records.push_back(RD);
}
return Records;
};
const SourceManager &SM = AST.getSourceManager();
std::vector<const CXXRecordDecl *> Result;
auto Offset = positionToOffset(SM.getBufferData(SM.getMainFileID()), Pos);
if (!Offset) {
llvm::consumeError(Offset.takeError());
return Result;
}
SelectionTree::createEach(AST.getASTContext(), AST.getTokens(), *Offset,
*Offset, [&](SelectionTree ST) {
Result = RecordFromNode(ST.commonAncestor());
return !Result.empty();
});
return Result;
}
static QualType typeForNode(const SelectionTree::Node *N) {
while (N && N->ASTNode.get<NestedNameSpecifierLoc>())
N = N->Parent;
if (!N)
return QualType();
if (const TypeLoc *TL = N->ASTNode.get<TypeLoc>()) {
if (llvm::isa<DeducedType>(TL->getTypePtr()))
if (auto Deduced = getDeducedType(
N->getDeclContext().getParentASTContext(), TL->getBeginLoc()))
return *Deduced;
if (llvm::isa<TypedefType>(TL->getTypePtr()))
return TL->getTypePtr()->getLocallyUnqualifiedSingleStepDesugaredType();
return TL->getType();
}
if (const auto *CCI = N->ASTNode.get<CXXCtorInitializer>()) {
if (const FieldDecl *FD = CCI->getAnyMember())
return FD->getType();
if (const Type *Base = CCI->getBaseClass())
return QualType(Base, 0);
}
if (const auto *CBS = N->ASTNode.get<CXXBaseSpecifier>())
return CBS->getType();
if (const Decl *D = N->ASTNode.get<Decl>()) {
struct Visitor : ConstDeclVisitor<Visitor, QualType> {
QualType VisitValueDecl(const ValueDecl *D) { return D->getType(); }
QualType VisitTypeDecl(const TypeDecl *D) {
return QualType(D->getTypeForDecl(), 0);
}
QualType VisitTypedefNameDecl(const TypedefNameDecl *D) {
return D->getUnderlyingType();
}
QualType VisitTemplateDecl(const TemplateDecl *D) {
return Visit(D->getTemplatedDecl());
}
} V;
return V.Visit(D);
}
if (const Stmt *S = N->ASTNode.get<Stmt>()) {
struct Visitor : ConstStmtVisitor<Visitor, QualType> {
QualType type(const Stmt *S) { return S ? Visit(S) : QualType(); }
QualType VisitExpr(const Expr *S) {
return S->IgnoreImplicitAsWritten()->getType();
}
QualType VisitMemberExpr(const MemberExpr *S) {
if (S->getType()->isSpecificBuiltinType(BuiltinType::BoundMember))
return Expr::findBoundMemberType(S);
return VisitExpr(S);
}
QualType VisitCXXDeleteExpr(const CXXDeleteExpr *S) {
return S->getDestroyedType();
}
QualType VisitCXXPseudoDestructorExpr(const CXXPseudoDestructorExpr *S) {
return S->getDestroyedType();
}
QualType VisitCXXThrowExpr(const CXXThrowExpr *S) {
return S->getSubExpr()->getType();
}
QualType VisitCoyieldExpr(const CoyieldExpr *S) {
return type(S->getOperand());
}
QualType VisitDesignatedInitExpr(const DesignatedInitExpr *S) {
for (auto &D : llvm::reverse(S->designators()))
if (D.isFieldDesignator())
if (const auto *FD = D.getFieldDecl())
return FD->getType();
return QualType();
}
QualType VisitSwitchStmt(const SwitchStmt *S) {
return type(S->getCond());
}
QualType VisitWhileStmt(const WhileStmt *S) { return type(S->getCond()); }
QualType VisitDoStmt(const DoStmt *S) { return type(S->getCond()); }
QualType VisitIfStmt(const IfStmt *S) { return type(S->getCond()); }
QualType VisitCaseStmt(const CaseStmt *S) { return type(S->getLHS()); }
QualType VisitCXXForRangeStmt(const CXXForRangeStmt *S) {
return S->getLoopVariable()->getType();
}
QualType VisitReturnStmt(const ReturnStmt *S) {
return type(S->getRetValue());
}
QualType VisitCoreturnStmt(const CoreturnStmt *S) {
return type(S->getOperand());
}
QualType VisitCXXCatchStmt(const CXXCatchStmt *S) {
return S->getCaughtType();
}
QualType VisitObjCAtThrowStmt(const ObjCAtThrowStmt *S) {
return type(S->getThrowExpr());
}
QualType VisitObjCAtCatchStmt(const ObjCAtCatchStmt *S) {
return S->getCatchParamDecl() ? S->getCatchParamDecl()->getType()
: QualType();
}
} V;
return V.Visit(S);
}
return QualType();
}
static void unwrapFindType(
QualType T, const HeuristicResolver* H, llvm::SmallVector<QualType>& Out) {
if (T.isNull())
return;
if (const auto* TDT = T->getAs<TypedefType>())
return Out.push_back(QualType(TDT, 0));
if (const auto *PT = T->getAs<PointerType>())
return unwrapFindType(PT->getPointeeType(), H, Out);
if (const auto *RT = T->getAs<ReferenceType>())
return unwrapFindType(RT->getPointeeType(), H, Out);
if (const auto *AT = T->getAsArrayTypeUnsafe())
return unwrapFindType(AT->getElementType(), H, Out);
if (auto *FT = T->getAs<FunctionType>())
return unwrapFindType(FT->getReturnType(), H, Out);
if (auto *CRD = T->getAsCXXRecordDecl()) {
if (CRD->isLambda())
return unwrapFindType(CRD->getLambdaCallOperator()->getReturnType(), H,
Out);
}
if (H)
if (const auto* PointeeType = H->getPointeeType(T.getNonReferenceType().getTypePtr())) {
unwrapFindType(QualType(PointeeType, 0), H, Out);
return Out.push_back(T);
}
return Out.push_back(T);
}
static llvm::SmallVector<QualType> unwrapFindType(
QualType T, const HeuristicResolver* H) {
llvm::SmallVector<QualType> Result;
unwrapFindType(T, H, Result);
return Result;
}
std::vector<LocatedSymbol> findType(ParsedAST &AST, Position Pos,
const SymbolIndex *Index) {
const SourceManager &SM = AST.getSourceManager();
auto Offset = positionToOffset(SM.getBufferData(SM.getMainFileID()), Pos);
std::vector<LocatedSymbol> Result;
if (!Offset) {
elog("failed to convert position {0} for findTypes: {1}", Pos,
Offset.takeError());
return Result;
}
auto SymbolsFromNode =
[&](const SelectionTree::Node *N) -> std::vector<LocatedSymbol> {
std::vector<LocatedSymbol> LocatedSymbols;
for (const QualType& Type : unwrapFindType(typeForNode(N), AST.getHeuristicResolver()))
llvm::copy(locateSymbolForType(AST, Type, Index),
std::back_inserter(LocatedSymbols));
return LocatedSymbols;
};
SelectionTree::createEach(AST.getASTContext(), AST.getTokens(), *Offset,
*Offset, [&](SelectionTree ST) {
Result = SymbolsFromNode(ST.commonAncestor());
return !Result.empty();
});
return Result;
}
std::vector<const CXXRecordDecl *> typeParents(const CXXRecordDecl *CXXRD) {
std::vector<const CXXRecordDecl *> Result;
if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(CXXRD)) {
if (CTSD->isInvalidDecl())
CXXRD = CTSD->getSpecializedTemplate()->getTemplatedDecl();
}
if (!CXXRD->hasDefinition())
return Result;
for (auto Base : CXXRD->bases()) {
const CXXRecordDecl *ParentDecl = nullptr;
const Type *Type = Base.getType().getTypePtr();
if (const RecordType *RT = Type->getAs<RecordType>()) {
ParentDecl = RT->getAsCXXRecordDecl();
}
if (!ParentDecl) {
if (const TemplateSpecializationType *TS =
Type->getAs<TemplateSpecializationType>()) {
TemplateName TN = TS->getTemplateName();
if (TemplateDecl *TD = TN.getAsTemplateDecl()) {
ParentDecl = dyn_cast<CXXRecordDecl>(TD->getTemplatedDecl());
}
}
}
if (ParentDecl)
Result.push_back(ParentDecl);
}
return Result;
}
std::vector<TypeHierarchyItem>
getTypeHierarchy(ParsedAST &AST, Position Pos, int ResolveLevels,
TypeHierarchyDirection Direction, const SymbolIndex *Index,
PathRef TUPath) {
std::vector<TypeHierarchyItem> Results;
for (const auto *CXXRD : findRecordTypeAt(AST, Pos)) {
bool WantChildren = Direction == TypeHierarchyDirection::Children ||
Direction == TypeHierarchyDirection::Both;
if (WantChildren) {
if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(CXXRD))
CXXRD = CTSD->getTemplateInstantiationPattern();
}
std::optional<TypeHierarchyItem> Result =
declToTypeHierarchyItem(*CXXRD, AST.tuPath());
if (!Result)
continue;
RecursionProtectionSet RPSet;
fillSuperTypes(*CXXRD, AST.tuPath(), *Result, RPSet);
if (WantChildren && ResolveLevels > 0) {
Result->children.emplace();
if (Index) {
if (auto ID = getSymbolID(CXXRD))
fillSubTypes(ID, *Result->children, Index, ResolveLevels, TUPath);
}
}
Results.emplace_back(std::move(*Result));
}
return Results;
}
std::optional<std::vector<TypeHierarchyItem>>
superTypes(const TypeHierarchyItem &Item, const SymbolIndex *Index) {
std::vector<TypeHierarchyItem> Results;
if (!Item.data.parents)
return std::nullopt;
if (Item.data.parents->empty())
return Results;
LookupRequest Req;
llvm::DenseMap<SymbolID, const TypeHierarchyItem::ResolveParams *> IDToData;
for (const auto &Parent : *Item.data.parents) {
Req.IDs.insert(Parent.symbolID);
IDToData[Parent.symbolID] = &Parent;
}
Index->lookup(Req, [&Item, &Results, &IDToData](const Symbol &S) {
if (auto THI = symbolToTypeHierarchyItem(S, Item.uri.file())) {
THI->data = *IDToData.lookup(S.ID);
Results.emplace_back(std::move(*THI));
}
});
return Results;
}
std::vector<TypeHierarchyItem> subTypes(const TypeHierarchyItem &Item,
const SymbolIndex *Index) {
std::vector<TypeHierarchyItem> Results;
fillSubTypes(Item.data.symbolID, Results, Index, 1, Item.uri.file());
for (auto &ChildSym : Results)
ChildSym.data.parents = {Item.data};
return Results;
}
void resolveTypeHierarchy(TypeHierarchyItem &Item, int ResolveLevels,
TypeHierarchyDirection Direction,
const SymbolIndex *Index) {
if (!Index || Direction == TypeHierarchyDirection::Parents ||
ResolveLevels == 0)
return;
Item.children.emplace();
fillSubTypes(Item.data.symbolID, *Item.children, Index, ResolveLevels,
Item.uri.file());
}
std::vector<CallHierarchyItem>
prepareCallHierarchy(ParsedAST &AST, Position Pos, PathRef TUPath) {
std::vector<CallHierarchyItem> Result;
const auto &SM = AST.getSourceManager();
auto Loc = sourceLocationInMainFile(SM, Pos);
if (!Loc) {
elog("prepareCallHierarchy failed to convert position to source location: "
"{0}",
Loc.takeError());
return Result;
}
for (const NamedDecl *Decl : getDeclAtPosition(AST, *Loc, {})) {
if (!(isa<DeclContext>(Decl) &&
cast<DeclContext>(Decl)->isFunctionOrMethod()) &&
Decl->getKind() != Decl::Kind::FunctionTemplate)
continue;
if (auto CHI = declToCallHierarchyItem(*Decl, AST.tuPath()))
Result.emplace_back(std::move(*CHI));
}
return Result;
}
std::vector<CallHierarchyIncomingCall>
incomingCalls(const CallHierarchyItem &Item, const SymbolIndex *Index) {
std::vector<CallHierarchyIncomingCall> Results;
if (!Index || Item.data.empty())
return Results;
auto ID = SymbolID::fromStr(Item.data);
if (!ID) {
elog("incomingCalls failed to find symbol: {0}", ID.takeError());
return Results;
}
RefsRequest Request;
Request.IDs.insert(*ID);
Request.WantContainer = true;
Request.Filter = RefKind::Reference;
llvm::DenseMap<SymbolID, std::vector<Range>> CallsIn;
LookupRequest ContainerLookup;
Index->refs(Request, [&](const Ref &R) {
auto Loc = indexToLSPLocation(R.Location, Item.uri.file());
if (!Loc) {
elog("incomingCalls failed to convert location: {0}", Loc.takeError());
return;
}
auto It = CallsIn.try_emplace(R.Container, std::vector<Range>{}).first;
It->second.push_back(Loc->range);
ContainerLookup.IDs.insert(R.Container);
});
Index->lookup(ContainerLookup, [&](const Symbol &Caller) {
auto It = CallsIn.find(Caller.ID);
assert(It != CallsIn.end());
if (auto CHI = symbolToCallHierarchyItem(Caller, Item.uri.file()))
Results.push_back(
CallHierarchyIncomingCall{std::move(*CHI), std::move(It->second)});
});
llvm::sort(Results, [](const CallHierarchyIncomingCall &A,
const CallHierarchyIncomingCall &B) {
return A.from.name < B.from.name;
});
return Results;
}
llvm::DenseSet<const Decl *> getNonLocalDeclRefs(ParsedAST &AST,
const FunctionDecl *FD) {
if (!FD->hasBody())
return {};
llvm::DenseSet<const Decl *> DeclRefs;
findExplicitReferences(
FD,
[&](ReferenceLoc Ref) {
for (const Decl *D : Ref.Targets) {
if (!index::isFunctionLocalSymbol(D) && !D->isTemplateParameter() &&
!Ref.IsDecl)
DeclRefs.insert(D);
}
},
AST.getHeuristicResolver());
return DeclRefs;
}
}
}