diff options
-rw-r--r-- | lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp | 66 | ||||
-rw-r--r-- | test/Analysis/inlining/ObjCDynTypePopagation.m | 26 |
2 files changed, 86 insertions, 6 deletions
diff --git a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp index 56610b5818..fec6c43dbd 100644 --- a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp +++ b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -24,11 +24,18 @@ using namespace clang; using namespace ento; namespace { -class DynamicTypePropagation : public Checker< check::PostCall > { +class DynamicTypePropagation: + public Checker< check::PostCall, + check::PostStmt<ImplicitCastExpr> > { const ObjCObjectType *getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE, CheckerContext &C) const; + + /// \brief Return a better dynamic type if one can be derived from the cast. + const ObjCObjectPointerType *getBetterObjCType(const Expr *CastE, + CheckerContext &C) const; public: - void checkPostCall(const CallEvent &CE, CheckerContext &C) const; + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + void checkPostStmt(const ImplicitCastExpr *CastE, CheckerContext &C) const; }; } @@ -77,6 +84,25 @@ void DynamicTypePropagation::checkPostCall(const CallEvent &Call, } } +void DynamicTypePropagation::checkPostStmt(const ImplicitCastExpr *CastE, + CheckerContext &C) const { + // We only track dynamic type info for regions. + const MemRegion *ToR = C.getSVal(CastE).getAsRegion(); + if (!ToR) + return; + + switch (CastE->getCastKind()) { + default: + break; + case CK_BitCast: + // Only handle ObjCObjects for now. + if (const Type *NewTy = getBetterObjCType(CastE, C)) + C.addTransition(C.getState()->addDynamicTypeInfo(ToR, QualType(NewTy,0))); + break; + } + return; +} + const ObjCObjectType * DynamicTypePropagation::getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE, CheckerContext &C) const { @@ -111,6 +137,42 @@ DynamicTypePropagation::getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE, return 0; } +// Return a better dynamic type if one can be derived from the cast. +// Compare the current dynamic type of the region and the new type to which we +// are casting. If the new type is lower in the inheritance hierarchy, pick it. +const ObjCObjectPointerType * +DynamicTypePropagation::getBetterObjCType(const Expr *CastE, + CheckerContext &C) const { + const MemRegion *ToR = C.getSVal(CastE).getAsRegion(); + assert(ToR); + + // Get the old and new types. + const ObjCObjectPointerType *NewTy = + CastE->getType()->getAs<ObjCObjectPointerType>(); + if (!NewTy) + return 0; + QualType OldDTy = C.getState()->getDynamicTypeInfo(ToR).getType(); + if (OldDTy.isNull()) { + return NewTy; + } + const ObjCObjectPointerType *OldTy = + OldDTy->getAs<ObjCObjectPointerType>(); + if (!OldTy) + return 0; + + // Id the old type is 'id', the new one is more precise. + if (OldTy->isObjCIdType() && !NewTy->isObjCIdType()) + return NewTy; + + // Return new if it's a subclass of old. + const ObjCInterfaceDecl *ToI = NewTy->getInterfaceDecl(); + const ObjCInterfaceDecl *FromI = OldTy->getInterfaceDecl(); + if (ToI && FromI && FromI->isSuperClassOf(ToI)) + return NewTy; + + return 0; +} + void ento::registerDynamicTypePropagation(CheckerManager &mgr) { mgr.registerChecker<DynamicTypePropagation>(); } diff --git a/test/Analysis/inlining/ObjCDynTypePopagation.m b/test/Analysis/inlining/ObjCDynTypePopagation.m index 89c05c9c56..425903242d 100644 --- a/test/Analysis/inlining/ObjCDynTypePopagation.m +++ b/test/Analysis/inlining/ObjCDynTypePopagation.m @@ -41,8 +41,8 @@ MyClass *getObj(); /* Test that we get the right type from call to alloc. */ + (void) testAllocSelf { - id a = [self alloc]; - clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}} + id a = [self alloc]; + clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}} } @@ -68,8 +68,26 @@ MyClass *getObj(); } + (void) testNewSelf { - id a = [self new]; - clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}} + id a = [self new]; + clang_analyzer_eval([a getZeroOverridden] == 0); // expected-warning{{TRUE}} +} + +// Casting to parent should not pessimize the dynamic type. ++ (void) testCastToParent { + id a = [[self alloc] init]; + MyParent *p = a; + clang_analyzer_eval([p getZeroOverridden] == 0); // expected-warning{{TRUE}} +} + +// The type of parameter gets used. ++ (void)testTypeFromParam:(MyParent*) p { + clang_analyzer_eval([p getZero] == 0); // expected-warning{{TRUE}} +} + +// Test implisit cast. ++ (void) testCastFromId:(id) a { + MyParent *p = a; + clang_analyzer_eval([p getZero] == 0); // expected-warning{{TRUE}} } @end
\ No newline at end of file |