| //===- 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() && D.getField()) |
| 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)); |
| } |