llvm-project/clang/lib/Index/IndexBody.cpp

480 lines
17 KiB
C++
Raw Normal View History

//===- 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));
}