diff options
-rw-r--r-- | clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h | 15 | ||||
-rw-r--r-- | clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 60 | ||||
-rw-r--r-- | clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 19 | ||||
-rw-r--r-- | clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | 8 | ||||
-rw-r--r-- | clang/test/Analysis/dtor.cpp | 18 | ||||
-rw-r--r-- | clang/test/Analysis/initializer.cpp | 35 |
6 files changed, 127 insertions, 28 deletions
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h index fd5f4373cd2..b49e8e3bf8f 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h @@ -238,6 +238,9 @@ public: /// Get the lvalue for a field reference. SVal getLValue(const FieldDecl *decl, SVal Base) const; + /// Get the lvalue for an indirect field reference. + SVal getLValue(const IndirectFieldDecl *decl, SVal Base) const; + /// Get the lvalue for an array index. SVal getLValue(QualType ElementType, SVal Idx, SVal Base) const; @@ -649,6 +652,18 @@ inline SVal ProgramState::getLValue(const FieldDecl *D, SVal Base) const { return getStateManager().StoreMgr->getLValueField(D, Base); } +inline SVal ProgramState::getLValue(const IndirectFieldDecl *D, + SVal Base) const { + StoreManager &SM = *getStateManager().StoreMgr; + for (IndirectFieldDecl::chain_iterator I = D->chain_begin(), + E = D->chain_end(); + I != E; ++I) { + Base = SM.getLValueField(cast<FieldDecl>(*I), Base); + } + + return Base; +} + inline SVal ProgramState::getLValue(QualType ElementType, SVal Idx, SVal Base) const{ if (NonLoc *N = dyn_cast<NonLoc>(&Idx)) return getStateManager().StoreMgr->getLValueElement(ElementType, *N, Base); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 09cd02701ed..cc244a56a35 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -358,43 +358,42 @@ void ExprEngine::ProcessInitializer(const CFGInitializer Init, ExplodedNodeSet Dst; NodeBuilder Bldr(Pred, Dst, *currentBuilderContext); + ProgramStateRef State = Pred->getState(); + // We don't set EntryNode and currentStmt. And we don't clean up state. const CXXCtorInitializer *BMI = Init.getInitializer(); const StackFrameContext *stackFrame = cast<StackFrameContext>(Pred->getLocationContext()); const CXXConstructorDecl *decl = cast<CXXConstructorDecl>(stackFrame->getDecl()); - SVal thisVal = Pred->getState()->getSVal(svalBuilder.getCXXThis(decl, - stackFrame)); + SVal thisVal = State->getSVal(svalBuilder.getCXXThis(decl, stackFrame)); + // Evaluate the initializer, if necessary if (BMI->isAnyMemberInitializer()) { - // Evaluate the initializer. - ProgramStateRef state = Pred->getState(); - - const FieldDecl *FD = BMI->getAnyMember(); - - // FIXME: This does not work for initializers that call constructors. - SVal FieldLoc = state->getLValue(FD, thisVal); - SVal InitVal = state->getSVal(BMI->getInit(), Pred->getLocationContext()); - state = state->bindLoc(FieldLoc, InitVal); + // Constructors build the object directly in the field, + // but non-objects must be copied in from the initializer. + if (!isa<CXXConstructExpr>(BMI->getInit())) { + SVal FieldLoc; + if (BMI->isIndirectMemberInitializer()) + FieldLoc = State->getLValue(BMI->getIndirectMember(), thisVal); + else + FieldLoc = State->getLValue(BMI->getMember(), thisVal); - // Use a custom node building process. - PostInitializer PP(BMI, stackFrame); - // Builder automatically add the generated node to the deferred set, - // which are processed in the builder's dtor. - Bldr.generateNode(PP, state, Pred); + SVal InitVal = State->getSVal(BMI->getInit(), stackFrame); + State = State->bindLoc(FieldLoc, InitVal); + } } else { assert(BMI->isBaseInitializer()); - // We already did all the work when visiting the CXXConstructExpr. - // Just construct a PostInitializer node so that the diagnostics don't get - // confused. - PostInitializer PP(BMI, stackFrame); - // Builder automatically add the generated node to the deferred set, - // which are processed in the builder's dtor. - Bldr.generateNode(PP, Pred->getState(), Pred); } + // Construct a PostInitializer node whether the state changed or not, + // so that the diagnostics don't get confused. + PostInitializer PP(BMI, stackFrame); + // Builder automatically add the generated node to the deferred set, + // which are processed in the builder's dtor. + Bldr.generateNode(PP, State, Pred); + // Enqueue the new nodes onto the work list. Engine.enqueue(Dst, currentBuilderContext->getBlock(), currentStmtIdx); } @@ -459,7 +458,20 @@ void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D, } void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, - ExplodedNode *Pred, ExplodedNodeSet &Dst) {} + ExplodedNode *Pred, ExplodedNodeSet &Dst) { + const FieldDecl *Member = D.getFieldDecl(); + ProgramStateRef State = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + + const CXXDestructorDecl *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl()); + Loc ThisVal = getSValBuilder().getCXXThis(CurDtor, + LCtx->getCurrentStackFrame()); + SVal FieldVal = State->getLValue(Member, cast<Loc>(State->getSVal(ThisVal))); + + VisitCXXDestructor(Member->getType(), + cast<loc::MemRegionVal>(FieldVal).getRegion(), + CurDtor->getBody(), Pred, Dst); +} void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D, ExplodedNode *Pred, diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index b21928866c9..88fba2919e8 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -63,6 +63,25 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, if (Var->getInit() == CE) Target = State->getLValue(Var, LCtx).getAsRegion(); + // Is this a constructor for a member? + if (const CFGInitializer *InitElem = dyn_cast<CFGInitializer>(&Next)) { + const CXXCtorInitializer *Init = InitElem->getInitializer(); + assert(Init->isAnyMemberInitializer()); + + const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl()); + Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor, + LCtx->getCurrentStackFrame()); + SVal ThisVal = State->getSVal(ThisPtr); + + if (Init->isIndirectMemberInitializer()) { + SVal Field = State->getLValue(Init->getIndirectMember(), ThisVal); + Target = cast<loc::MemRegionVal>(Field).getRegion(); + } else { + SVal Field = State->getLValue(Init->getMember(), ThisVal); + Target = cast<loc::MemRegionVal>(Field).getRegion(); + } + } + // FIXME: This will eventually need to handle new-expressions as well. } diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 29481c65126..bc6aeaa0368 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -305,13 +305,15 @@ bool ExprEngine::inlineCall(const CallEvent &Call, if (!ADC->getCFGBuildOptions().AddImplicitDtors || !ADC->getCFGBuildOptions().AddInitializers) return false; - // FIXME: This is a hack. We don't handle member or temporary constructors + + // FIXME: This is a hack. We don't handle temporary destructors // right now, so we shouldn't inline their constructors. if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) { const CXXConstructExpr *CtorExpr = Ctor->getOriginExpr(); if (CtorExpr->getConstructionKind() == CXXConstructExpr::CK_Complete) - if (!isa<VarRegion>(Ctor->getCXXThisVal().getAsRegion())) - return false; + if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion()) + if (!isa<DeclRegion>(Target)) + return false; } break; } diff --git a/clang/test/Analysis/dtor.cpp b/clang/test/Analysis/dtor.cpp index 8d67f78a4a7..f5837539cb7 100644 --- a/clang/test/Analysis/dtor.cpp +++ b/clang/test/Analysis/dtor.cpp @@ -103,3 +103,21 @@ void testMultipleInheritance3() { // expected-warning@25 {{Attempt to free released memory}} } } + + +class SmartPointerMember { + SmartPointer P; +public: + SmartPointerMember(void *x) : P(x) {} +}; + +void testSmartPointerMember() { + char *mem = (char*)malloc(4); + { + SmartPointerMember Deleter(mem); + // Remove dead bindings... + doSomething(); + // destructor called here + } + *mem = 0; // expected-warning{{Use of memory after it is freed}} +} diff --git a/clang/test/Analysis/initializer.cpp b/clang/test/Analysis/initializer.cpp index 6640e1fc495..0580503a44b 100644 --- a/clang/test/Analysis/initializer.cpp +++ b/clang/test/Analysis/initializer.cpp @@ -1,4 +1,7 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-store region -cfg-add-initializers -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-store region -cfg-add-initializers -cfg-add-implicit-dtors -verify %s + +// We don't inline constructors unless we have both initializers and +// implicit destructors turned on. void clang_analyzer_eval(bool); @@ -11,3 +14,33 @@ public: A::A() : x(0) { clang_analyzer_eval(x == 0); // expected-warning{{TRUE}} } + + +class DirectMember { + int x; +public: + DirectMember(int value) : x(value) {} + + int getX() { return x; } +}; + +void testDirectMember() { + DirectMember obj(3); + clang_analyzer_eval(obj.getX() == 3); // expected-warning{{TRUE}} +} + + +class IndirectMember { + struct { + int x; + }; +public: + IndirectMember(int value) : x(value) {} + + int getX() { return x; } +}; + +void testIndirectMember() { + IndirectMember obj(3); + clang_analyzer_eval(obj.getX() == 3); // expected-warning{{TRUE}} +} |