diff options
-rw-r--r-- | include/clang/AST/DeclCXX.h | 13 | ||||
-rw-r--r-- | lib/AST/DeclCXX.cpp | 13 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp | 70 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/CallEvent.cpp | 2 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | 23 | ||||
-rw-r--r-- | test/Analysis/ctor-inlining.mm | 48 |
6 files changed, 141 insertions, 28 deletions
diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 851e3105bc..2d95f038df 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -1646,14 +1646,17 @@ public: /// \brief Find the method in RD that corresponds to this one. /// /// Find if RD or one of the classes it inherits from override this method. - /// If so, return it. RD is assumed to be a base class of the class defining - /// this method (or be the class itself). + /// If so, return it. RD is assumed to be a subclass of the class defining + /// this method (or be the class itself), unless MayBeBase is set to true. CXXMethodDecl * - getCorrespondingMethodInClass(const CXXRecordDecl *RD); + getCorrespondingMethodInClass(const CXXRecordDecl *RD, + bool MayBeBase = false); const CXXMethodDecl * - getCorrespondingMethodInClass(const CXXRecordDecl *RD) const { - return const_cast<CXXMethodDecl*>(this)->getCorrespondingMethodInClass(RD); + getCorrespondingMethodInClass(const CXXRecordDecl *RD, + bool MayBeBase = false) const { + return const_cast<CXXMethodDecl *>(this) + ->getCorrespondingMethodInClass(RD, MayBeBase); } // Implement isa/cast/dyncast/etc. diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index eec2e9d3cf..2f21e4cbd6 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -1294,15 +1294,20 @@ static bool recursivelyOverrides(const CXXMethodDecl *DerivedMD, } CXXMethodDecl * -CXXMethodDecl::getCorrespondingMethodInClass(const CXXRecordDecl *RD) { +CXXMethodDecl::getCorrespondingMethodInClass(const CXXRecordDecl *RD, + bool MayBeBase) { if (this->getParent()->getCanonicalDecl() == RD->getCanonicalDecl()) return this; // Lookup doesn't work for destructors, so handle them separately. if (isa<CXXDestructorDecl>(this)) { CXXMethodDecl *MD = RD->getDestructor(); - if (MD && recursivelyOverrides(MD, this)) - return MD; + if (MD) { + if (recursivelyOverrides(MD, this)) + return MD; + if (MayBeBase && recursivelyOverrides(this, MD)) + return MD; + } return NULL; } @@ -1313,6 +1318,8 @@ CXXMethodDecl::getCorrespondingMethodInClass(const CXXRecordDecl *RD) { continue; if (recursivelyOverrides(MD, this)) return MD; + if (MayBeBase && recursivelyOverrides(this, MD)) + return MD; } for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(), 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); } diff --git a/test/Analysis/ctor-inlining.mm b/test/Analysis/ctor-inlining.mm index 54d51b46dc..fe4781c16e 100644 --- a/test/Analysis/ctor-inlining.mm +++ b/test/Analysis/ctor-inlining.mm @@ -39,3 +39,51 @@ void testNonPODCopyConstructor() { clang_analyzer_eval(b.x == 42); // expected-warning{{TRUE}} } + +namespace ConstructorVirtualCalls { + class A { + public: + int *out1, *out2, *out3; + + virtual int get() { return 1; } + + A(int *out1) { + *out1 = get(); + } + }; + + class B : public A { + public: + virtual int get() { return 2; } + + B(int *out1, int *out2) : A(out1) { + *out2 = get(); + } + }; + + class C : public B { + public: + virtual int get() { return 3; } + + C(int *out1, int *out2, int *out3) : B(out1, out2) { + *out3 = get(); + } + }; + + void test() { + int a, b, c; + + C obj(&a, &b, &c); + clang_analyzer_eval(a == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(b == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(c == 3); // expected-warning{{TRUE}} + + clang_analyzer_eval(obj.get() == 3); // expected-warning{{TRUE}} + + // Sanity check for devirtualization. + A *base = &obj; + clang_analyzer_eval(base->get() == 3); // expected-warning{{TRUE}} + } +} + + |