diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2022-07-03 14:10:23 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2022-07-03 14:10:23 +0000 |
| commit | 145449b1e420787bb99721a429341fa6be3adfb6 (patch) | |
| tree | 1d56ae694a6de602e348dd80165cf881a36600ed /clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | |
| parent | ecbca9f5fb7d7613d2b94982c4825eb0d33d6842 (diff) | |
Diffstat (limited to 'clang/lib/StaticAnalyzer/Core/ExprEngine.cpp')
| -rw-r--r-- | clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 251 |
1 files changed, 180 insertions, 71 deletions
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index a170ef3885b2..45af22de50ae 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -7,7 +7,7 @@ //===----------------------------------------------------------------------===// // // This file defines a meta-engine for path-sensitive dataflow analysis that -// is built on GREngine, but provides the boilerplate to execute transfer +// is built on CoreEngine, but provides the boilerplate to execute transfer // functions and build the ExplodedGraph at the expression level. // //===----------------------------------------------------------------------===// @@ -118,18 +118,10 @@ namespace { /// the construction context was present and contained references to these /// AST nodes. class ConstructedObjectKey { - typedef std::pair<ConstructionContextItem, const LocationContext *> - ConstructedObjectKeyImpl; - + using ConstructedObjectKeyImpl = + std::pair<ConstructionContextItem, const LocationContext *>; const ConstructedObjectKeyImpl Impl; - const void *getAnyASTNodePtr() const { - if (const Stmt *S = getItem().getStmtOrNull()) - return S; - else - return getItem().getCXXCtorInitializer(); - } - public: explicit ConstructedObjectKey(const ConstructionContextItem &Item, const LocationContext *LC) @@ -200,24 +192,17 @@ REGISTER_TRAIT_WITH_PROGRAMSTATE(ObjectsUnderConstruction, static const char* TagProviderName = "ExprEngine"; ExprEngine::ExprEngine(cross_tu::CrossTranslationUnitContext &CTU, - AnalysisManager &mgr, - SetOfConstDecls *VisitedCalleesIn, - FunctionSummariesTy *FS, - InliningModes HowToInlineIn) - : CTU(CTU), AMgr(mgr), - AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()), + AnalysisManager &mgr, SetOfConstDecls *VisitedCalleesIn, + FunctionSummariesTy *FS, InliningModes HowToInlineIn) + : CTU(CTU), IsCTUEnabled(mgr.getAnalyzerOptions().IsNaiveCTUEnabled), + AMgr(mgr), AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()), Engine(*this, FS, mgr.getAnalyzerOptions()), G(Engine.getGraph()), StateMgr(getContext(), mgr.getStoreManagerCreator(), - mgr.getConstraintManagerCreator(), G.getAllocator(), - this), - SymMgr(StateMgr.getSymbolManager()), - MRMgr(StateMgr.getRegionManager()), - svalBuilder(StateMgr.getSValBuilder()), - ObjCNoRet(mgr.getASTContext()), - BR(mgr, *this), - VisitedCallees(VisitedCalleesIn), - HowToInline(HowToInlineIn) - { + mgr.getConstraintManagerCreator(), G.getAllocator(), this), + SymMgr(StateMgr.getSymbolManager()), MRMgr(StateMgr.getRegionManager()), + svalBuilder(StateMgr.getSValBuilder()), ObjCNoRet(mgr.getASTContext()), + BR(mgr, *this), VisitedCallees(VisitedCalleesIn), + HowToInline(HowToInlineIn) { unsigned TrimInterval = mgr.options.GraphTrimInterval; if (TrimInterval != 0) { // Enable eager node reclamation when constructing the ExplodedGraph. @@ -319,7 +304,7 @@ ProgramStateRef ExprEngine::createTemporaryRegionIfNeeded( if (!Result) { // If we don't have an explicit result expression, we're in "if needed" // mode. Only create a region if the current value is a NonLoc. - if (!InitValWithAdjustments.getAs<NonLoc>()) { + if (!isa<NonLoc>(InitValWithAdjustments)) { if (OutRegionWithAdjustments) *OutRegionWithAdjustments = nullptr; return State; @@ -328,7 +313,7 @@ ProgramStateRef ExprEngine::createTemporaryRegionIfNeeded( } else { // We need to create a region no matter what. Make sure we don't try to // stuff a Loc into a non-pointer temporary region. - assert(!InitValWithAdjustments.getAs<Loc>() || + assert(!isa<Loc>(InitValWithAdjustments) || Loc::isLocType(Result->getType()) || Result->getType()->isMemberPointerType()); } @@ -620,6 +605,8 @@ void ExprEngine::printJson(raw_ostream &Out, ProgramStateRef State, } void ExprEngine::processEndWorklist() { + // This prints the name of the top-level function if we crash. + PrettyStackTraceLocationContext CrashInfo(getRootLocationContext()); getCheckerManager().runCheckersForEndAnalysis(G, BR, *this); } @@ -1251,6 +1238,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::OMPParallelForSimdDirectiveClass: case Stmt::OMPParallelSectionsDirectiveClass: case Stmt::OMPParallelMasterDirectiveClass: + case Stmt::OMPParallelMaskedDirectiveClass: case Stmt::OMPTaskDirectiveClass: case Stmt::OMPTaskyieldDirectiveClass: case Stmt::OMPBarrierDirectiveClass: @@ -1274,9 +1262,13 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::OMPTaskLoopDirectiveClass: case Stmt::OMPTaskLoopSimdDirectiveClass: case Stmt::OMPMasterTaskLoopDirectiveClass: + case Stmt::OMPMaskedTaskLoopDirectiveClass: case Stmt::OMPMasterTaskLoopSimdDirectiveClass: + case Stmt::OMPMaskedTaskLoopSimdDirectiveClass: case Stmt::OMPParallelMasterTaskLoopDirectiveClass: + case Stmt::OMPParallelMaskedTaskLoopDirectiveClass: case Stmt::OMPParallelMasterTaskLoopSimdDirectiveClass: + case Stmt::OMPParallelMaskedTaskLoopSimdDirectiveClass: case Stmt::OMPDistributeDirectiveClass: case Stmt::OMPDistributeParallelForDirectiveClass: case Stmt::OMPDistributeParallelForSimdDirectiveClass: @@ -1297,6 +1289,10 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::OMPDispatchDirectiveClass: case Stmt::OMPMaskedDirectiveClass: case Stmt::OMPGenericLoopDirectiveClass: + case Stmt::OMPTeamsGenericLoopDirectiveClass: + case Stmt::OMPTargetTeamsGenericLoopDirectiveClass: + case Stmt::OMPParallelGenericLoopDirectiveClass: + case Stmt::OMPTargetParallelGenericLoopDirectiveClass: case Stmt::CapturedStmtClass: case Stmt::OMPUnrollDirectiveClass: case Stmt::OMPMetaDirectiveClass: { @@ -1342,8 +1338,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::GNUNullExprClass: { // GNU __null is a pointer-width integer, not an actual pointer. ProgramStateRef state = Pred->getState(); - state = state->BindExpr(S, Pred->getLocationContext(), - svalBuilder.makeIntValWithPtrWidth(0, false)); + state = state->BindExpr( + S, Pred->getLocationContext(), + svalBuilder.makeIntValWithWidth(getContext().VoidPtrTy, 0)); Bldr.generateNode(S, Pred, state); break; } @@ -1370,10 +1367,14 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, break; } + case Stmt::ArrayInitLoopExprClass: + Bldr.takeNodes(Pred); + VisitArrayInitLoopExpr(cast<ArrayInitLoopExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); + break; // Cases not handled yet; but will handle some day. case Stmt::DesignatedInitExprClass: case Stmt::DesignatedInitUpdateExprClass: - case Stmt::ArrayInitLoopExprClass: case Stmt::ArrayInitIndexExprClass: case Stmt::ExtVectorElementExprClass: case Stmt::ImaginaryLiteralClass: @@ -2343,7 +2344,7 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { llvm_unreachable("No block with label."); } - if (V.getAs<loc::ConcreteInt>() || V.getAs<UndefinedVal>()) { + if (isa<UndefinedVal, loc::ConcreteInt>(V)) { // Dispatch to the first target and mark it as a sink. //ExplodedNode* N = builder.generateNode(builder.begin(), state, true); // FIXME: add checker visit. @@ -2598,15 +2599,144 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, // operator&. return; } - if (isa<BindingDecl>(D)) { - // FIXME: proper support for bound declarations. - // For now, let's just prevent crashing. + if (const auto *BD = dyn_cast<BindingDecl>(D)) { + const auto *DD = cast<DecompositionDecl>(BD->getDecomposedDecl()); + + SVal Base = state->getLValue(DD, LCtx); + if (DD->getType()->isReferenceType()) { + Base = state->getSVal(Base.getAsRegion()); + } + + SVal V = UnknownVal(); + + // Handle binding to data members + if (const auto *ME = dyn_cast<MemberExpr>(BD->getBinding())) { + const auto *Field = cast<FieldDecl>(ME->getMemberDecl()); + V = state->getLValue(Field, Base); + } + // Handle binding to arrays + else if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(BD->getBinding())) { + SVal Idx = state->getSVal(ASE->getIdx(), LCtx); + + // Note: the index of an element in a structured binding is automatically + // created and it is a unique identifier of the specific element. Thus it + // cannot be a value that varies at runtime. + assert(Idx.isConstant() && "BindingDecl array index is not a constant!"); + + V = state->getLValue(BD->getType(), Idx, Base); + } + // Handle binding to tuple-like strcutures + else if (BD->getHoldingVar()) { + // FIXME: handle tuples + return; + } else + llvm_unreachable("An unknown case of structured binding encountered!"); + + if (BD->getType()->isReferenceType()) + V = state->getSVal(V.getAsRegion()); + + Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr, + ProgramPoint::PostLValueKind); + return; } llvm_unreachable("Support for this Decl not implemented."); } +/// VisitArrayInitLoopExpr - Transfer function for array init loop. +void ExprEngine::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *Ex, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + ExplodedNodeSet CheckerPreStmt; + getCheckerManager().runCheckersForPreStmt(CheckerPreStmt, Pred, Ex, *this); + + ExplodedNodeSet EvalSet; + StmtNodeBuilder Bldr(CheckerPreStmt, EvalSet, *currBldrCtx); + + const Expr *Arr = Ex->getCommonExpr()->getSourceExpr(); + + for (auto *Node : CheckerPreStmt) { + const LocationContext *LCtx = Node->getLocationContext(); + ProgramStateRef state = Node->getState(); + + SVal Base = UnknownVal(); + + // As in case of this expression the sub-expressions are not visited by any + // other transfer functions, they are handled by matching their AST. + + // Case of implicit copy or move ctor of object with array member + // + // Note: ExprEngine::VisitMemberExpr is not able to bind the array to the + // environment. + // + // struct S { + // int arr[2]; + // }; + // + // + // S a; + // S b = a; + // + // The AST in case of a *copy constructor* looks like this: + // ArrayInitLoopExpr + // |-OpaqueValueExpr + // | `-MemberExpr <-- match this + // | `-DeclRefExpr + // ` ... + // + // + // S c; + // S d = std::move(d); + // + // In case of a *move constructor* the resulting AST looks like: + // ArrayInitLoopExpr + // |-OpaqueValueExpr + // | `-MemberExpr <-- match this first + // | `-CXXStaticCastExpr <-- match this after + // | `-DeclRefExpr + // ` ... + if (const auto *ME = dyn_cast<MemberExpr>(Arr)) { + Expr *MEBase = ME->getBase(); + + // Move ctor + if (auto CXXSCE = dyn_cast<CXXStaticCastExpr>(MEBase)) { + MEBase = CXXSCE->getSubExpr(); + } + + auto ObjDeclExpr = cast<DeclRefExpr>(MEBase); + SVal Obj = state->getLValue(cast<VarDecl>(ObjDeclExpr->getDecl()), LCtx); + + Base = state->getLValue(cast<FieldDecl>(ME->getMemberDecl()), Obj); + } + + // Case of lambda capture and decomposition declaration + // + // int arr[2]; + // + // [arr]{ int a = arr[0]; }(); + // auto[a, b] = arr; + // + // In both of these cases the AST looks like the following: + // ArrayInitLoopExpr + // |-OpaqueValueExpr + // | `-DeclRefExpr <-- match this + // ` ... + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Arr)) + Base = state->getLValue(cast<VarDecl>(DRE->getDecl()), LCtx); + + // Create a lazy compound value to the original array + if (const MemRegion *R = Base.getAsRegion()) + Base = state->getSVal(R); + else + Base = UnknownVal(); + + Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, Base)); + } + + getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, Ex, *this); +} + /// VisitArraySubscriptExpr - Transfer function for array accesses void ExprEngine::VisitArraySubscriptExpr(const ArraySubscriptExpr *A, ExplodedNode *Pred, @@ -2894,7 +3024,7 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, // If the location is not a 'Loc', it will already be handled by // the checkers. There is nothing left to do. - if (!location.getAs<Loc>()) { + if (!isa<Loc>(location)) { const ProgramPoint L = PostStore(StoreE, LC, /*Loc*/nullptr, /*tag*/nullptr); ProgramStateRef state = Pred->getState(); @@ -2964,7 +3094,7 @@ void ExprEngine::evalLoad(ExplodedNodeSet &Dst, SVal location, const ProgramPointTag *tag, QualType LoadTy) { - assert(!location.getAs<NonLoc>() && "location cannot be a NonLoc."); + assert(!isa<NonLoc>(location) && "location cannot be a NonLoc."); assert(NodeEx); assert(BoundEx); // Evaluate the location (checks for bad dereferences). @@ -3095,7 +3225,7 @@ void ExprEngine::VisitGCCAsmStmt(const GCCAsmStmt *A, ExplodedNode *Pred, for (const Expr *O : A->outputs()) { SVal X = state->getSVal(O, Pred->getLocationContext()); - assert(!X.getAs<NonLoc>()); // Should be an Lval, or unknown, undef. + assert(!isa<NonLoc>(X)); // Should be an Lval, or unknown, undef. if (Optional<Loc> LV = X.getAs<Loc>()) state = state->bindLoc(*LV, UnknownVal(), Pred->getLocationContext()); @@ -3114,7 +3244,6 @@ void ExprEngine::VisitMSAsmStmt(const MSAsmStmt *A, ExplodedNode *Pred, // Visualization. //===----------------------------------------------------------------------===// -#ifndef NDEBUG namespace llvm { template<> @@ -3212,29 +3341,18 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits { }; } // namespace llvm -#endif void ExprEngine::ViewGraph(bool trim) { -#ifndef NDEBUG std::string Filename = DumpGraph(trim); llvm::DisplayGraph(Filename, false, llvm::GraphProgram::DOT); -#else - llvm::errs() << "Warning: viewing graph requires assertions" << "\n"; -#endif } - -void ExprEngine::ViewGraph(ArrayRef<const ExplodedNode*> Nodes) { -#ifndef NDEBUG +void ExprEngine::ViewGraph(ArrayRef<const ExplodedNode *> Nodes) { std::string Filename = DumpGraph(Nodes); llvm::DisplayGraph(Filename, false, llvm::GraphProgram::DOT); -#else - llvm::errs() << "Warning: viewing graph requires assertions" << "\n"; -#endif } std::string ExprEngine::DumpGraph(bool trim, StringRef Filename) { -#ifndef NDEBUG if (trim) { std::vector<const ExplodedNode *> Src; @@ -3249,35 +3367,26 @@ std::string ExprEngine::DumpGraph(bool trim, StringRef Filename) { Src.push_back(N); } return DumpGraph(Src, Filename); - } else { - return llvm::WriteGraph(&G, "ExprEngine", /*ShortNames=*/false, - /*Title=*/"Exploded Graph", - /*Filename=*/std::string(Filename)); } -#else - llvm::errs() << "Warning: dumping graph requires assertions" << "\n"; - return ""; -#endif + + return llvm::WriteGraph(&G, "ExprEngine", /*ShortNames=*/false, + /*Title=*/"Exploded Graph", + /*Filename=*/std::string(Filename)); } -std::string ExprEngine::DumpGraph(ArrayRef<const ExplodedNode*> Nodes, +std::string ExprEngine::DumpGraph(ArrayRef<const ExplodedNode *> Nodes, StringRef Filename) { -#ifndef NDEBUG std::unique_ptr<ExplodedGraph> TrimmedG(G.trim(Nodes)); if (!TrimmedG.get()) { llvm::errs() << "warning: Trimmed ExplodedGraph is empty.\n"; return ""; - } else { - return llvm::WriteGraph(TrimmedG.get(), "TrimmedExprEngine", - /*ShortNames=*/false, - /*Title=*/"Trimmed Exploded Graph", - /*Filename=*/std::string(Filename)); } -#else - llvm::errs() << "Warning: dumping graph requires assertions" << "\n"; - return ""; -#endif + + return llvm::WriteGraph(TrimmedG.get(), "TrimmedExprEngine", + /*ShortNames=*/false, + /*Title=*/"Trimmed Exploded Graph", + /*Filename=*/std::string(Filename)); } void *ProgramStateTrait<ReplayWithoutInlining>::GDMIndex() { |
