diff options
Diffstat (limited to 'lib/StaticAnalyzer/Core/ExprEngine.cpp')
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngine.cpp | 227 |
1 files changed, 118 insertions, 109 deletions
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 405aecdee032..5b2119aeda27 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -27,10 +27,9 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h" -#include "llvm/ADT/ImmutableList.h" #include "llvm/ADT/Statistic.h" -#include "llvm/Support/raw_ostream.h" #include "llvm/Support/SaveAndRestore.h" +#include "llvm/Support/raw_ostream.h" #ifndef NDEBUG #include "llvm/Support/GraphWriter.h" @@ -203,25 +202,32 @@ ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State, MemRegionManager &MRMgr = StateMgr.getRegionManager(); StoreManager &StoreMgr = StateMgr.getStoreManager(); - // We need to be careful about treating a derived type's value as - // bindings for a base type. Unless we're creating a temporary pointer region, - // start by stripping and recording base casts. - SmallVector<const CastExpr *, 4> Casts; - const Expr *Inner = Ex->IgnoreParens(); - if (!Loc::isLocType(Result->getType())) { - while (const CastExpr *CE = dyn_cast<CastExpr>(Inner)) { - if (CE->getCastKind() == CK_DerivedToBase || - CE->getCastKind() == CK_UncheckedDerivedToBase) - Casts.push_back(CE); - else if (CE->getCastKind() != CK_NoOp) - break; + // MaterializeTemporaryExpr may appear out of place, after a few field and + // base-class accesses have been made to the object, even though semantically + // it is the whole object that gets materialized and lifetime-extended. + // + // For example: + // + // `-MaterializeTemporaryExpr + // `-MemberExpr + // `-CXXTemporaryObjectExpr + // + // instead of the more natural + // + // `-MemberExpr + // `-MaterializeTemporaryExpr + // `-CXXTemporaryObjectExpr + // + // Use the usual methods for obtaining the expression of the base object, + // and record the adjustments that we need to make to obtain the sub-object + // that the whole expression 'Ex' refers to. This trick is usual, + // in the sense that CodeGen takes a similar route. - Inner = CE->getSubExpr()->IgnoreParens(); - } - } + SmallVector<const Expr *, 2> CommaLHSs; + SmallVector<SubobjectAdjustment, 2> Adjustments; + + const Expr *Init = Ex->skipRValueSubobjectAdjustments(CommaLHSs, Adjustments); - // Create a temporary object region for the inner expression (which may have - // a more derived type) and bind the value into it. const TypedValueRegion *TR = nullptr; if (const MaterializeTemporaryExpr *MT = dyn_cast<MaterializeTemporaryExpr>(Result)) { @@ -229,25 +235,37 @@ ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State, // If this object is bound to a reference with static storage duration, we // put it in a different region to prevent "address leakage" warnings. if (SD == SD_Static || SD == SD_Thread) - TR = MRMgr.getCXXStaticTempObjectRegion(Inner); + TR = MRMgr.getCXXStaticTempObjectRegion(Init); } if (!TR) - TR = MRMgr.getCXXTempObjectRegion(Inner, LC); + TR = MRMgr.getCXXTempObjectRegion(Init, LC); SVal Reg = loc::MemRegionVal(TR); + // Make the necessary adjustments to obtain the sub-object. + for (auto I = Adjustments.rbegin(), E = Adjustments.rend(); I != E; ++I) { + const SubobjectAdjustment &Adj = *I; + switch (Adj.Kind) { + case SubobjectAdjustment::DerivedToBaseAdjustment: + Reg = StoreMgr.evalDerivedToBase(Reg, Adj.DerivedToBase.BasePath); + break; + case SubobjectAdjustment::FieldAdjustment: + Reg = StoreMgr.getLValueField(Adj.Field, Reg); + break; + case SubobjectAdjustment::MemberPointerAdjustment: + // FIXME: Unimplemented. + State->bindDefault(Reg, UnknownVal()); + return State; + } + } + + // Try to recover some path sensitivity in case we couldn't compute the value. if (V.isUnknown()) V = getSValBuilder().conjureSymbolVal(Result, LC, TR->getValueType(), currBldrCtx->blockCount()); + // Bind the value of the expression to the sub-object region, and then bind + // the sub-object region to our expression. State = State->bindLoc(Reg, V); - - // Re-apply the casts (from innermost to outermost) for type sanity. - for (SmallVectorImpl<const CastExpr *>::reverse_iterator I = Casts.rbegin(), - E = Casts.rend(); - I != E; ++I) { - Reg = StoreMgr.evalDerivedToBase(Reg, *I); - } - State = State->BindExpr(Result, LC, Reg); return State; } @@ -263,10 +281,6 @@ ProgramStateRef ExprEngine::processAssume(ProgramStateRef state, return getCheckerManager().runCheckersForEvalAssume(state, cond, assumption); } -bool ExprEngine::wantsRegionChangeUpdate(ProgramStateRef state) { - return getCheckerManager().wantsRegionChangeUpdate(state); -} - ProgramStateRef ExprEngine::processRegionChanges(ProgramStateRef state, const InvalidatedSymbols *invalidated, @@ -493,7 +507,7 @@ void ExprEngine::ProcessInitializer(const CFGInitializer Init, } SVal InitVal; - if (BMI->getNumArrayIndices() > 0) { + if (Init->getType()->isArrayType()) { // Handle arrays of trivial type. We can represent this with a // primitive load/copy from the base array region. const ArraySubscriptExpr *ASE; @@ -597,9 +611,9 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor, SVal dest = state->getLValue(varDecl, Pred->getLocationContext()); const MemRegion *Region = dest.castAs<loc::MemRegionVal>().getRegion(); - if (const ReferenceType *refType = varType->getAs<ReferenceType>()) { - varType = refType->getPointeeType(); - Region = state->getSVal(Region).getAsRegion(); + if (varType->isReferenceType()) { + Region = state->getSVal(Region).getAsRegion()->getBaseRegion(); + varType = cast<TypedValueRegion>(Region)->getValueType(); } VisitCXXDestructor(varType, Region, Dtor.getTriggerStmt(), /*IsBase=*/ false, @@ -847,6 +861,14 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::OMPDistributeParallelForSimdDirectiveClass: case Stmt::OMPDistributeSimdDirectiveClass: case Stmt::OMPTargetParallelForSimdDirectiveClass: + case Stmt::OMPTargetSimdDirectiveClass: + case Stmt::OMPTeamsDistributeDirectiveClass: + case Stmt::OMPTeamsDistributeSimdDirectiveClass: + case Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass: + case Stmt::OMPTeamsDistributeParallelForDirectiveClass: + case Stmt::OMPTargetTeamsDirectiveClass: + case Stmt::OMPTargetTeamsDistributeDirectiveClass: + case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass: llvm_unreachable("Stmt should not be in analyzer evaluation loop"); case Stmt::ObjCSubscriptRefExprClass: @@ -886,6 +908,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, // 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: case Stmt::ObjCAtCatchStmtClass: @@ -1211,16 +1235,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::ObjCBridgedCastExprClass: { Bldr.takeNodes(Pred); const CastExpr *C = cast<CastExpr>(S); - // Handle the previsit checks. - ExplodedNodeSet dstPrevisit; - getCheckerManager().runCheckersForPreStmt(dstPrevisit, Pred, C, *this); - - // Handle the expression itself. ExplodedNodeSet dstExpr; - for (ExplodedNodeSet::iterator i = dstPrevisit.begin(), - e = dstPrevisit.end(); i != e ; ++i) { - VisitCast(C, C->getSubExpr(), *i, dstExpr); - } + VisitCast(C, C->getSubExpr(), Pred, dstExpr); // Handle the postvisit checks. getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, C, *this); @@ -1773,7 +1789,8 @@ void ExprEngine::processBeginOfFunction(NodeBuilderContext &BC, /// ProcessEndPath - Called by CoreEngine. Used to generate end-of-path /// nodes when the control reaches the end of a function. void ExprEngine::processEndOfFunction(NodeBuilderContext& BC, - ExplodedNode *Pred) { + ExplodedNode *Pred, + const ReturnStmt *RS) { // FIXME: Assert that stackFrameDoesNotContainInitializedTemporaries(*Pred)). // We currently cannot enable this assert, as lifetime extended temporaries // are not modelled correctly. @@ -1795,7 +1812,7 @@ void ExprEngine::processEndOfFunction(NodeBuilderContext& BC, getCheckerManager().runCheckersForEndFunction(BC, Dst, Pred, *this); } - Engine.enqueueEndOfFunction(Dst); + Engine.enqueueEndOfFunction(Dst, RS); } /// ProcessSwitch - Called by CoreEngine. Used to generate successor @@ -1841,7 +1858,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { ProgramStateRef StateCase; if (Optional<NonLoc> NL = CondV.getAs<NonLoc>()) std::tie(StateCase, DefaultSt) = - DefaultSt->assumeWithinInclusiveRange(*NL, V1, V2); + DefaultSt->assumeInclusiveRange(*NL, V1, V2); else // UnknownVal StateCase = DefaultSt; @@ -1975,24 +1992,26 @@ void ExprEngine::VisitLvalArraySubscriptExpr(const ArraySubscriptExpr *A, const Expr *Base = A->getBase()->IgnoreParens(); const Expr *Idx = A->getIdx()->IgnoreParens(); - ExplodedNodeSet checkerPreStmt; - getCheckerManager().runCheckersForPreStmt(checkerPreStmt, Pred, A, *this); + ExplodedNodeSet CheckerPreStmt; + getCheckerManager().runCheckersForPreStmt(CheckerPreStmt, Pred, A, *this); - StmtNodeBuilder Bldr(checkerPreStmt, Dst, *currBldrCtx); + ExplodedNodeSet EvalSet; + StmtNodeBuilder Bldr(CheckerPreStmt, EvalSet, *currBldrCtx); assert(A->isGLValue() || (!AMgr.getLangOpts().CPlusPlus && A->getType().isCForbiddenLValueType())); - for (ExplodedNodeSet::iterator it = checkerPreStmt.begin(), - ei = checkerPreStmt.end(); it != ei; ++it) { - const LocationContext *LCtx = (*it)->getLocationContext(); - ProgramStateRef state = (*it)->getState(); + for (auto *Node : CheckerPreStmt) { + const LocationContext *LCtx = Node->getLocationContext(); + ProgramStateRef state = Node->getState(); SVal V = state->getLValue(A->getType(), state->getSVal(Idx, LCtx), state->getSVal(Base, LCtx)); - Bldr.generateNode(A, *it, state->BindExpr(A, LCtx, V), nullptr, + Bldr.generateNode(A, Node, state->BindExpr(A, LCtx, V), nullptr, ProgramPoint::PostLValueKind); } + + getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, A, *this); } /// VisitMemberExpr - Transfer function for member expressions. @@ -2051,7 +2070,7 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, if (!M->isGLValue()) { assert(M->getType()->isArrayType()); const ImplicitCastExpr *PE = - dyn_cast<ImplicitCastExpr>((*I)->getParentMap().getParent(M)); + dyn_cast<ImplicitCastExpr>((*I)->getParentMap().getParentIgnoreParens(M)); if (!PE || PE->getCastKind() != CK_ArrayToPointerDecay) { llvm_unreachable("should always be wrapped in ArrayToPointerDecay"); } @@ -2521,26 +2540,10 @@ struct DOTGraphTraits<ExplodedNode*> : // FIXME: Since we do not cache error nodes in ExprEngine now, this does not // work. static std::string getNodeAttributes(const ExplodedNode *N, void*) { - -#if 0 - // FIXME: Replace with a general scheme to tell if the node is - // an error node. - if (GraphPrintCheckerState->isImplicitNullDeref(N) || - GraphPrintCheckerState->isExplicitNullDeref(N) || - GraphPrintCheckerState->isUndefDeref(N) || - GraphPrintCheckerState->isUndefStore(N) || - GraphPrintCheckerState->isUndefControlFlow(N) || - GraphPrintCheckerState->isUndefResult(N) || - GraphPrintCheckerState->isBadCall(N) || - GraphPrintCheckerState->isUndefArg(N)) - return "color=\"red\",style=\"filled\""; - - if (GraphPrintCheckerState->isNoReturnCall(N)) - return "color=\"blue\",style=\"filled\""; -#endif return ""; } + // De-duplicate some source location pretty-printing. static void printLocation(raw_ostream &Out, SourceLocation SLoc) { if (SLoc.isFileID()) { Out << "\\lline=" @@ -2550,6 +2553,12 @@ struct DOTGraphTraits<ExplodedNode*> : << "\\l"; } } + static void printLocation2(raw_ostream &Out, SourceLocation SLoc) { + if (SLoc.isFileID() && GraphPrintSourceManager->isInMainFile(SLoc)) + Out << "line " << GraphPrintSourceManager->getExpansionLineNumber(SLoc); + else + SLoc.print(Out, *GraphPrintSourceManager); + } static std::string getNodeLabel(const ExplodedNode *N, void*){ @@ -2563,12 +2572,6 @@ struct DOTGraphTraits<ExplodedNode*> : case ProgramPoint::BlockEntranceKind: { Out << "Block Entrance: B" << Loc.castAs<BlockEntrance>().getBlock()->getBlockID(); - if (const NamedDecl *ND = - dyn_cast<NamedDecl>(Loc.getLocationContext()->getDecl())) { - Out << " ("; - ND->printName(Out); - Out << ")"; - } break; } @@ -2693,13 +2696,6 @@ struct DOTGraphTraits<ExplodedNode*> : Out << "\\l"; } -#if 0 - // FIXME: Replace with a general scheme to determine - // the name of the check. - if (GraphPrintCheckerState->isUndefControlFlow(N)) { - Out << "\\|Control-flow based on\\lUndefined value.\\l"; - } -#endif break; } @@ -2721,27 +2717,6 @@ struct DOTGraphTraits<ExplodedNode*> : else if (Loc.getAs<PostLValue>()) Out << "\\lPostLValue\\l"; -#if 0 - // FIXME: Replace with a general scheme to determine - // the name of the check. - if (GraphPrintCheckerState->isImplicitNullDeref(N)) - Out << "\\|Implicit-Null Dereference.\\l"; - else if (GraphPrintCheckerState->isExplicitNullDeref(N)) - Out << "\\|Explicit-Null Dereference.\\l"; - else if (GraphPrintCheckerState->isUndefDeref(N)) - Out << "\\|Dereference of undefialied value.\\l"; - else if (GraphPrintCheckerState->isUndefStore(N)) - Out << "\\|Store to Undefined Loc."; - else if (GraphPrintCheckerState->isUndefResult(N)) - Out << "\\|Result of operation is undefined."; - else if (GraphPrintCheckerState->isNoReturnCall(N)) - Out << "\\|Call to function marked \"noreturn\"."; - else if (GraphPrintCheckerState->isBadCall(N)) - Out << "\\|Call to NULL/Undefined."; - else if (GraphPrintCheckerState->isUndefArg(N)) - Out << "\\|Argument in call is undefined"; -#endif - break; } } @@ -2749,6 +2724,40 @@ struct DOTGraphTraits<ExplodedNode*> : ProgramStateRef state = N->getState(); Out << "\\|StateID: " << (const void*) state.get() << " NodeID: " << (const void*) N << "\\|"; + + // Analysis stack backtrace. + Out << "Location context stack (from current to outer):\\l"; + const LocationContext *LC = Loc.getLocationContext(); + unsigned Idx = 0; + for (; LC; LC = LC->getParent(), ++Idx) { + Out << Idx << ". (" << (const void *)LC << ") "; + switch (LC->getKind()) { + case LocationContext::StackFrame: + if (const NamedDecl *D = dyn_cast<NamedDecl>(LC->getDecl())) + Out << "Calling " << D->getQualifiedNameAsString(); + else + Out << "Calling anonymous code"; + if (const Stmt *S = cast<StackFrameContext>(LC)->getCallSite()) { + Out << " at "; + printLocation2(Out, S->getLocStart()); + } + break; + case LocationContext::Block: + Out << "Invoking block"; + if (const Decl *D = cast<BlockInvocationContext>(LC)->getBlockDecl()) { + Out << " defined at "; + printLocation2(Out, D->getLocStart()); + } + break; + case LocationContext::Scope: + Out << "Entering scope"; + // FIXME: Add more info once ScopeContext is activated. + break; + } + Out << "\\l"; + } + Out << "\\l"; + state->printDOT(Out); Out << "\\l"; |