diff options
-rw-r--r-- | lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp | 26 | ||||
-rw-r--r-- | test/Analysis/self-init.m | 28 |
2 files changed, 52 insertions, 2 deletions
diff --git a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp index 97b58cf79b..89340cc290 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp @@ -60,7 +60,8 @@ class ObjCSelfInitChecker : public Checker< check::PreObjCMessage, check::PreStmt<ReturnStmt>, check::PreStmt<CallExpr>, check::PostStmt<CallExpr>, - check::Location > { + check::Location, + check::Bind > { public: void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; void checkPostObjCMessage(ObjCMessage msg, CheckerContext &C) const; @@ -70,6 +71,7 @@ public: void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; void checkLocation(SVal location, bool isLoad, const Stmt *S, CheckerContext &C) const; + void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const; void checkPreStmt(const CallOrObjCMessage &CE, CheckerContext &C) const; void checkPostStmt(const CallOrObjCMessage &CE, CheckerContext &C) const; @@ -336,6 +338,28 @@ void ObjCSelfInitChecker::checkLocation(SVal location, bool isLoad, addSelfFlag(state, state->getSVal(cast<Loc>(location)), SelfFlag_Self, C); } + +void ObjCSelfInitChecker::checkBind(SVal loc, SVal val, const Stmt *S, + CheckerContext &C) const { + // Allow assignment of anything to self. Self is a local variable in the + // initializer, so it is legal to assign anything to it, like results of + // static functions/method calls. After self is assigned something we cannot + // reason about, stop enforcing the rules. + // (Only continue checking if the assigned value should be treated as self.) + if ((isSelfVar(loc, C)) && + !hasSelfFlag(val, SelfFlag_InitRes, C) && + !hasSelfFlag(val, SelfFlag_Self, C) && + !isSelfVar(val, C)) { + + // Stop tracking the checker-specific state in the state. + ProgramStateRef State = C.getState(); + State = State->remove<CalledInit>(); + if (SymbolRef sym = loc.getAsSymbol()) + State = State->remove<SelfFlag>(sym); + C.addTransition(State); + } +} + // FIXME: A callback should disable checkers at the start of functions. static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND) { if (!ND) diff --git a/test/Analysis/self-init.m b/test/Analysis/self-init.m index 365096e445..10b0c4da47 100644 --- a/test/Analysis/self-init.m +++ b/test/Analysis/self-init.m @@ -1,7 +1,8 @@ // RUN: %clang_cc1 -analyze -analyzer-checker=osx.cocoa.SelfInit -fobjc-default-synthesize-properties -fno-builtin %s -verify @class NSZone, NSCoder; -@protocol NSObject- (id)self; +@protocol NSObject +- (id)self; @end @protocol NSCopying - (id)copyWithZone:(NSZone *)zone; @end @@ -254,3 +255,28 @@ extern id _commonInit(MyObj *self); return self; } @end + +// Test for radar://11125870: init constructing a special instance. +typedef signed char BOOL; +@interface MyClass : NSObject +@end +@implementation MyClass ++ (id)specialInstance { + return [[MyClass alloc] init]; +} +- (id)initSpecially:(BOOL)handleSpecially { + if ((self = [super init])) { + if (handleSpecially) { + self = [MyClass specialInstance]; + } + } + return self; +} +- (id)initSelfSelf { + if ((self = [super init])) { + self = self; + } + return self; +} +@end + |