[analyzer] SelfInit: Stop tracking self if it's assigned a value we
don't reason about.

Self is just like a local variable in init methods, so it can be
assigned anything like result of static functions, other methods ... So
to suppress false positives that result in such cases, stop tracking the
checker-specific state after self is being assigned to (unless the
value is't being assigned to is either self or conforms to our rules).

This change does not invalidate any existing regression tests.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@156420 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
index 97b58cf..89340cc 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
@@ -60,7 +60,8 @@
                                              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 @@
   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 @@
     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 365096e..10b0c4d 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 @@
    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
+