aboutsummaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer
diff options
context:
space:
mode:
authorJordan Rose <jordan_rose@apple.com>2012-08-15 20:07:17 +0000
committerJordan Rose <jordan_rose@apple.com>2012-08-15 20:07:17 +0000
commit4e79fdfe22db1c982e8fdf8397fee426a8c57821 (patch)
tree3284c97b4f7dcd239c8f202246cd997c5bc14f39 /lib/StaticAnalyzer
parent99570a58b09fca5d0b328733ab8b6717a1a04f4a (diff)
[analyzer] Correctly devirtualize virtual method calls in constructors.
This is the other half of C++11 [class.cdtor]p4 (the destructor side was added in r161915). This also fixes an issue with post-call checks where the 'this' value was already being cleaned out of the state, thus being omitted from a reconstructed CXXConstructorCall. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@161981 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/StaticAnalyzer')
-rw-r--r--lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp70
-rw-r--r--lib/StaticAnalyzer/Core/CallEvent.cpp2
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp23
3 files changed, 75 insertions, 20 deletions
diff --git a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
index 036686e964..b636efbe35 100644
--- a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
+++ b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
@@ -41,9 +41,23 @@ public:
};
}
+static void recordFixedType(const MemRegion *Region, const CXXMethodDecl *MD,
+ CheckerContext &C) {
+ assert(Region);
+ assert(MD);
+
+ ASTContext &Ctx = C.getASTContext();
+ QualType Ty = Ctx.getPointerType(Ctx.getRecordType(MD->getParent()));
+
+ ProgramStateRef State = C.getState();
+ State = State->setDynamicTypeInfo(Region, Ty, /*CanBeSubclass=*/false);
+ C.addTransition(State);
+ return;
+}
+
void DynamicTypePropagation::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
- if (const CXXDestructorCall *Dtor = dyn_cast<CXXDestructorCall>(&Call)) {
+ if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
// C++11 [class.cdtor]p4: When a virtual function is called directly or
// indirectly from a constructor or from a destructor, including during
// the construction or destruction of the class’s non-static data members,
@@ -51,7 +65,24 @@ void DynamicTypePropagation::checkPreCall(const CallEvent &Call,
// construction or destruction, the function called is the final overrider
// in the constructor's or destructor's class and not one overriding it in
// a more-derived class.
- // FIXME: We don't support this behavior yet for constructors.
+
+ switch (Ctor->getOriginExpr()->getConstructionKind()) {
+ case CXXConstructExpr::CK_Complete:
+ case CXXConstructExpr::CK_Delegating:
+ // No additional type info necessary.
+ return;
+ case CXXConstructExpr::CK_NonVirtualBase:
+ case CXXConstructExpr::CK_VirtualBase:
+ if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion())
+ recordFixedType(Target, Ctor->getDecl(), C);
+ return;
+ }
+
+ return;
+ }
+
+ if (const CXXDestructorCall *Dtor = dyn_cast<CXXDestructorCall>(&Call)) {
+ // C++11 [class.cdtor]p4 (see above)
const MemRegion *Target = Dtor->getCXXThisVal().getAsRegion();
if (!Target)
@@ -63,14 +94,8 @@ void DynamicTypePropagation::checkPreCall(const CallEvent &Call,
if (!D)
return;
- const CXXRecordDecl *Class = cast<CXXDestructorDecl>(D)->getParent();
-
- ASTContext &Ctx = C.getASTContext();
- QualType Ty = Ctx.getPointerType(Ctx.getRecordType(Class));
-
- ProgramStateRef State = C.getState();
- State = State->setDynamicTypeInfo(Target, Ty, /*CanBeSubclass=*/false);
- C.addTransition(State);
+ recordFixedType(Target, cast<CXXDestructorDecl>(D), C);
+ return;
}
}
@@ -117,6 +142,31 @@ void DynamicTypePropagation::checkPostCall(const CallEvent &Call,
break;
}
}
+
+ return;
+ }
+
+ if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
+ // We may need to undo the effects of our pre-call check.
+ switch (Ctor->getOriginExpr()->getConstructionKind()) {
+ case CXXConstructExpr::CK_Complete:
+ case CXXConstructExpr::CK_Delegating:
+ // No additional work necessary.
+ // Note: This will leave behind the actual type of the object for
+ // complete constructors, but arguably that's a good thing, since it
+ // means the dynamic type info will be correct even for objects
+ // constructed with operator new.
+ return;
+ case CXXConstructExpr::CK_NonVirtualBase:
+ case CXXConstructExpr::CK_VirtualBase:
+ if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion()) {
+ // We just finished a base constructor. Now we can use the subclass's
+ // type when resolving virtual calls.
+ const Decl *D = C.getLocationContext()->getDecl();
+ recordFixedType(Target, cast<CXXConstructorDecl>(D), C);
+ }
+ return;
+ }
}
}
diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp
index 8481cd9c33..1817ebb276 100644
--- a/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -405,7 +405,7 @@ RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const {
return RuntimeDefinition();
// Find the decl for this method in that class.
- const CXXMethodDecl *Result = MD->getCorrespondingMethodInClass(RD);
+ const CXXMethodDecl *Result = MD->getCorrespondingMethodInClass(RD, true);
assert(Result && "At the very least the static decl should show up.");
// Does the decl that we found have an implementation?
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index 5b93dfdb83..4e3071f73d 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -154,6 +154,11 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
}
}
+ // Generate a CallEvent /before/ cleaning the state, so that we can get the
+ // correct value for 'this' (if necessary).
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<> Call = CEMgr.getCaller(calleeCtx, state);
+
// Step 3: BindedRetNode -> CleanedNodes
// If we can find a statement and a block in the inlined function, run remove
// dead bindings before returning from the call. This is important to ensure
@@ -203,21 +208,21 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
&Ctx);
SaveAndRestore<unsigned> CBISave(currentStmtIdx, calleeCtx->getIndex());
- CallEventManager &CEMgr = getStateManager().getCallEventManager();
- CallEventRef<> Call = CEMgr.getCaller(calleeCtx, CEEState);
+ CallEventRef<> UpdatedCall = Call.cloneWithState(CEEState);
ExplodedNodeSet DstPostCall;
- getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode, *Call,
- *this, true);
+ getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode,
+ *UpdatedCall, *this,
+ /*WasInlined=*/true);
ExplodedNodeSet Dst;
- if (isa<ObjCMethodCall>(Call)) {
- getCheckerManager().runCheckersForPostObjCMessage(Dst, DstPostCall,
- cast<ObjCMethodCall>(*Call),
- *this, true);
+ if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) {
+ getCheckerManager().runCheckersForPostObjCMessage(Dst, DstPostCall, *Msg,
+ *this,
+ /*WasInlined=*/true);
} else if (CE) {
getCheckerManager().runCheckersForPostStmt(Dst, DstPostCall, CE,
- *this, true);
+ *this, /*WasInlined=*/true);
} else {
Dst.insert(DstPostCall);
}