diff options
Diffstat (limited to 'lib/StaticAnalyzer/Core/ExprEngineCXX.cpp')
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 188 |
1 files changed, 148 insertions, 40 deletions
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 2a76621..556e223 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -103,49 +103,32 @@ static SVal makeZeroElementRegion(ProgramStateRef State, SVal LValue, } -static const MemRegion *getRegionForConstructedObject( - const CXXConstructExpr *CE, ExplodedNode *Pred, ExprEngine &Eng, - unsigned int CurrStmtIdx) { +const MemRegion * +ExprEngine::getRegionForConstructedObject(const CXXConstructExpr *CE, + ExplodedNode *Pred) { const LocationContext *LCtx = Pred->getLocationContext(); ProgramStateRef State = Pred->getState(); - const NodeBuilderContext &CurrBldrCtx = Eng.getBuilderContext(); // See if we're constructing an existing region by looking at the next // element in the CFG. - const CFGBlock *B = CurrBldrCtx.getBlock(); - unsigned int NextStmtIdx = CurrStmtIdx + 1; - if (NextStmtIdx < B->size()) { - CFGElement Next = (*B)[NextStmtIdx]; - - // Is this a destructor? If so, we might be in the middle of an assignment - // to a local or member: look ahead one more element to see what we find. - while (Next.getAs<CFGImplicitDtor>() && NextStmtIdx + 1 < B->size()) { - ++NextStmtIdx; - Next = (*B)[NextStmtIdx]; - } - // Is this a constructor for a local variable? - if (Optional<CFGStmt> StmtElem = Next.getAs<CFGStmt>()) { - if (const DeclStmt *DS = dyn_cast<DeclStmt>(StmtElem->getStmt())) { - if (const VarDecl *Var = dyn_cast<VarDecl>(DS->getSingleDecl())) { - if (Var->getInit() && Var->getInit()->IgnoreImplicit() == CE) { - SVal LValue = State->getLValue(Var, LCtx); - QualType Ty = Var->getType(); - LValue = makeZeroElementRegion(State, LValue, Ty); - return LValue.getAsRegion(); - } + if (auto Elem = findElementDirectlyInitializedByCurrentConstructor()) { + if (Optional<CFGStmt> StmtElem = Elem->getAs<CFGStmt>()) { + auto *DS = cast<DeclStmt>(StmtElem->getStmt()); + if (const auto *Var = dyn_cast<VarDecl>(DS->getSingleDecl())) { + if (Var->getInit() && Var->getInit()->IgnoreImplicit() == CE) { + SVal LValue = State->getLValue(Var, LCtx); + QualType Ty = Var->getType(); + LValue = makeZeroElementRegion(State, LValue, Ty); + return LValue.getAsRegion(); } } - } - - // Is this a constructor for a member? - if (Optional<CFGInitializer> InitElem = Next.getAs<CFGInitializer>()) { + } else if (Optional<CFGInitializer> InitElem = Elem->getAs<CFGInitializer>()) { const CXXCtorInitializer *Init = InitElem->getInitializer(); assert(Init->isAnyMemberInitializer()); - const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl()); - Loc ThisPtr = Eng.getSValBuilder().getCXXThis(CurCtor, - LCtx->getCurrentStackFrame()); + Loc ThisPtr = + getSValBuilder().getCXXThis(CurCtor, LCtx->getCurrentStackFrame()); SVal ThisVal = State->getSVal(ThisPtr); const ValueDecl *Field; @@ -167,13 +150,86 @@ static const MemRegion *getRegionForConstructedObject( // Don't forget to update the pre-constructor initialization code in // ExprEngine::VisitCXXConstructExpr. } - // If we couldn't find an existing region to construct into, assume we're // constructing a temporary. - MemRegionManager &MRMgr = Eng.getSValBuilder().getRegionManager(); + MemRegionManager &MRMgr = getSValBuilder().getRegionManager(); return MRMgr.getCXXTempObjectRegion(CE, LCtx); } +/// Returns true if the initializer for \Elem can be a direct +/// constructor. +static bool canHaveDirectConstructor(CFGElement Elem){ + // DeclStmts and CXXCtorInitializers for fields can be directly constructed. + + if (Optional<CFGStmt> StmtElem = Elem.getAs<CFGStmt>()) { + if (isa<DeclStmt>(StmtElem->getStmt())) { + return true; + } + } + + if (Elem.getKind() == CFGElement::Initializer) { + return true; + } + + return false; +} + +Optional<CFGElement> +ExprEngine::findElementDirectlyInitializedByCurrentConstructor() { + const NodeBuilderContext &CurrBldrCtx = getBuilderContext(); + // See if we're constructing an existing region by looking at the next + // element in the CFG. + const CFGBlock *B = CurrBldrCtx.getBlock(); + assert(isa<CXXConstructExpr>(((*B)[currStmtIdx]).castAs<CFGStmt>().getStmt())); + unsigned int NextStmtIdx = currStmtIdx + 1; + if (NextStmtIdx >= B->size()) + return None; + + CFGElement Next = (*B)[NextStmtIdx]; + + // Is this a destructor? If so, we might be in the middle of an assignment + // to a local or member: look ahead one more element to see what we find. + while (Next.getAs<CFGImplicitDtor>() && NextStmtIdx + 1 < B->size()) { + ++NextStmtIdx; + Next = (*B)[NextStmtIdx]; + } + + if (canHaveDirectConstructor(Next)) + return Next; + + return None; +} + +const CXXConstructExpr * +ExprEngine::findDirectConstructorForCurrentCFGElement() { + // Go backward in the CFG to see if the previous element (ignoring + // destructors) was a CXXConstructExpr. If so, that constructor + // was constructed directly into an existing region. + // This process is essentially the inverse of that performed in + // findElementDirectlyInitializedByCurrentConstructor(). + if (currStmtIdx == 0) + return nullptr; + + const CFGBlock *B = getBuilderContext().getBlock(); + assert(canHaveDirectConstructor((*B)[currStmtIdx])); + + unsigned int PreviousStmtIdx = currStmtIdx - 1; + CFGElement Previous = (*B)[PreviousStmtIdx]; + + while (Previous.getAs<CFGImplicitDtor>() && PreviousStmtIdx > 0) { + --PreviousStmtIdx; + Previous = (*B)[PreviousStmtIdx]; + } + + if (Optional<CFGStmt> PrevStmtElem = Previous.getAs<CFGStmt>()) { + if (auto *CtorExpr = dyn_cast<CXXConstructExpr>(PrevStmtElem->getStmt())) { + return CtorExpr; + } + } + + return nullptr; +} + void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, ExplodedNode *Pred, ExplodedNodeSet &destNodes) { @@ -188,7 +244,7 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, switch (CE->getConstructionKind()) { case CXXConstructExpr::CK_Complete: { - Target = getRegionForConstructedObject(CE, Pred, *this, currStmtIdx); + Target = getRegionForConstructedObject(CE, Pred); break; } case CXXConstructExpr::CK_VirtualBase: @@ -300,7 +356,7 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, const MemRegion *Dest, const Stmt *S, bool IsBaseDtor, - ExplodedNode *Pred, + ExplodedNode *Pred, ExplodedNodeSet &Dst) { const LocationContext *LCtx = Pred->getLocationContext(); ProgramStateRef State = Pred->getState(); @@ -373,7 +429,7 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, // Also, we need to decide how allocators actually work -- they're not // really part of the CXXNewExpr because they happen BEFORE the // CXXConstructExpr subexpression. See PR12014 for some discussion. - + unsigned blockCount = currBldrCtx->blockCount(); const LocationContext *LCtx = Pred->getLocationContext(); DefinedOrUnknownSVal symVal = UnknownVal(); @@ -392,8 +448,8 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, IsStandardGlobalOpNewFunction = (FD->getNumParams() == 1); } - // We assume all standard global 'operator new' functions allocate memory in - // heap. We realize this is an approximation that might not correctly model + // We assume all standard global 'operator new' functions allocate memory in + // heap. We realize this is an approximation that might not correctly model // a custom global allocator. if (IsStandardGlobalOpNewFunction) symVal = svalBuilder.getConjuredHeapSymbolVal(CNE, LCtx, blockCount); @@ -472,7 +528,7 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, } } -void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE, +void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); ProgramStateRef state = Pred->getState(); @@ -513,3 +569,55 @@ void ExprEngine::VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred, SVal V = state->getSVal(loc::MemRegionVal(R)); Bldr.generateNode(TE, Pred, state->BindExpr(TE, LCtx, V)); } + +void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + const LocationContext *LocCtxt = Pred->getLocationContext(); + + // Get the region of the lambda itself. + const MemRegion *R = svalBuilder.getRegionManager().getCXXTempObjectRegion( + LE, LocCtxt); + SVal V = loc::MemRegionVal(R); + + ProgramStateRef State = Pred->getState(); + + // If we created a new MemRegion for the lambda, we should explicitly bind + // the captures. + CXXRecordDecl::field_iterator CurField = LE->getLambdaClass()->field_begin(); + for (LambdaExpr::const_capture_init_iterator i = LE->capture_init_begin(), + e = LE->capture_init_end(); + i != e; ++i, ++CurField) { + FieldDecl *FieldForCapture = *CurField; + SVal FieldLoc = State->getLValue(FieldForCapture, V); + + SVal InitVal; + if (!FieldForCapture->hasCapturedVLAType()) { + Expr *InitExpr = *i; + assert(InitExpr && "Capture missing initialization expression"); + InitVal = State->getSVal(InitExpr, LocCtxt); + } else { + // The field stores the length of a captured variable-length array. + // These captures don't have initialization expressions; instead we + // get the length from the VLAType size expression. + Expr *SizeExpr = FieldForCapture->getCapturedVLAType()->getSizeExpr(); + InitVal = State->getSVal(SizeExpr, LocCtxt); + } + + State = State->bindLoc(FieldLoc, InitVal); + } + + // Decay the Loc into an RValue, because there might be a + // MaterializeTemporaryExpr node above this one which expects the bound value + // to be an RValue. + SVal LambdaRVal = State->getSVal(R); + + ExplodedNodeSet Tmp; + StmtNodeBuilder Bldr(Pred, Tmp, *currBldrCtx); + // FIXME: is this the right program point kind? + Bldr.generateNode(LE, Pred, + State->BindExpr(LE, LocCtxt, LambdaRVal), + nullptr, ProgramPoint::PostLValueKind); + + // FIXME: Move all post/pre visits to ::Visit(). + getCheckerManager().runCheckersForPostStmt(Dst, Tmp, LE, *this); +} |