From 297e2e5bf603fed639d03fd7104fa1ff602c1e21 Mon Sep 17 00:00:00 2001
From: Ted Kremenek <kremenek@apple.com>
Date: Mon, 2 Aug 2010 21:59:12 +0000
Subject: [PATCH] Fix idempotent operations false positive caused by ivars not
 being invalidated in function calls when the enclosing object had
 retain/release state.  Fixes <rdar://problem/8261992>.

llvm-svn: 110068
---
 clang/lib/Checker/CFRefCount.cpp              | 13 +++++++--
 .../Analysis/retain-release-region-store.m    | 27 +++++++++++++++++++
 2 files changed, 38 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Checker/CFRefCount.cpp b/clang/lib/Checker/CFRefCount.cpp
index 0cc8905cb0c8..e4a2a39fbe7f 100644
--- a/clang/lib/Checker/CFRefCount.cpp
+++ b/clang/lib/Checker/CFRefCount.cpp
@@ -2623,19 +2623,25 @@ void CFRefCount::EvalSummary(ExplodedNodeSet& Dst,
 
   llvm::SmallVector<const MemRegion*, 10> RegionsToInvalidate;
 
+  // HACK: Symbols that have ref-count state that are referenced directly
+  //  (not as structure or array elements, or via bindings) by an argument
+  //  should not have their ref-count state stripped after we have
+  //  done an invalidation pass.
+  llvm::DenseSet<SymbolRef> WhitelistedSymbols;
+
   for (ConstExprIterator I = arg_beg; I != arg_end; ++I, ++idx) {
     SVal V = state->getSValAsScalarOrLoc(*I);
     SymbolRef Sym = V.getAsLocSymbol();
 
     if (Sym)
       if (RefBindings::data_type* T = state->get<RefBindings>(Sym)) {
+        WhitelistedSymbols.insert(Sym);
         state = Update(state, Sym, *T, Summ.getArg(idx), hasErr);
         if (hasErr) {
           ErrorRange = (*I)->getSourceRange();
           ErrorSym = Sym;
           break;
         }
-        continue;
       }
 
   tryAgain:
@@ -2721,7 +2727,10 @@ void CFRefCount::EvalSummary(ExplodedNodeSet& Dst,
   state = state->makeWithStore(store);
   for (StoreManager::InvalidatedSymbols::iterator I = IS.begin(),
        E = IS.end(); I!=E; ++I) {
-      // Remove any existing reference-count binding.
+    SymbolRef sym = *I;
+    if (WhitelistedSymbols.count(sym))
+      continue;
+    // Remove any existing reference-count binding.
     state = state->remove<RefBindings>(*I);
   }
 
diff --git a/clang/test/Analysis/retain-release-region-store.m b/clang/test/Analysis/retain-release-region-store.m
index db49b91c27f5..7b9855473dce 100644
--- a/clang/test/Analysis/retain-release-region-store.m
+++ b/clang/test/Analysis/retain-release-region-store.m
@@ -50,6 +50,7 @@ typedef struct _NSZone NSZone;
 @end  @protocol NSCoding  - (void)encodeWithCoder:(NSCoder *)aCoder;
 @end
 @interface NSObject <NSObject> {}
+- (id)init;
 + (id)allocWithZone:(NSZone *)zone;
 + (id)alloc;
 - (void)dealloc;
@@ -223,3 +224,29 @@ void pr6699(int x) {
   }
 }
 
+// <rdar://problem/8261992> Idempotent operation checker false positive with ObjC ivars
+@interface R8261992 : NSObject {
+  @package int myIvar;
+}
+@end
+
+static void R8261992_ChangeMyIvar(R8261992 *tc) {
+    tc->myIvar = 5;
+}
+
+void R8261992_test(R8261992 *tc) {
+  int temp = tc->myIvar;
+  // The ivar binding for tc->myIvar gets invalidated.
+  R8261992_ChangeMyIvar(tc);
+  tc->myIvar = temp; // no-warning
+  tc = [[R8261992 alloc] init];
+  temp = tc->myIvar; // no-warning
+  // The ivar binding for tc->myIvar gets invalidated.
+  R8261992_ChangeMyIvar(tc);
+  tc->myIvar = temp;
+  [tc release]; // no-warning
+  // did we analyze this?
+  int *p = 0x0;
+  *p = 0xDEADBEEF; // expected-warning{{null}}
+}
+