[analyzer] IvarInvalidation: track synthesized ivars and allow escape
through property getters.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@164802 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
index 0bf8073..836860e 100644
--- a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
@@ -41,6 +41,8 @@
                          const ObjCIvarDecl*> MethToIvarMapTy;
   typedef llvm::DenseMap<const ObjCPropertyDecl*,
                          const ObjCIvarDecl*> PropToIvarMapTy;
+  typedef llvm::DenseMap<const ObjCIvarDecl*,
+                         const ObjCPropertyDecl*> IvarToPropMapTy;
 
   /// Statement visitor, which walks the method body and flags the ivars
   /// referenced in it (either directly or via property).
@@ -49,18 +51,19 @@
     /// The set of Ivars which need to be invalidated.
     IvarSet &IVars;
 
-    /// Property setter to ivar mapping.
-    MethToIvarMapTy &PropertySetterToIvarMap;
+    /// Property setter/getter to ivar mapping.
+    MethToIvarMapTy &PropertyAccessorToIvarMap;
 
     // Property to ivar mapping.
     PropToIvarMapTy &PropertyToIvarMap;
 
   public:
     MethodCrawler(const ObjCInterfaceDecl *InID,
-                  IvarSet &InIVars, MethToIvarMapTy &InPropertySetterToIvarMap,
+                  IvarSet &InIVars,
+                  MethToIvarMapTy &InPropertyAccessorToIvarMap,
                   PropToIvarMapTy &InPropertyToIvarMap)
     : IVars(InIVars),
-      PropertySetterToIvarMap(InPropertySetterToIvarMap),
+      PropertyAccessorToIvarMap(InPropertyAccessorToIvarMap),
       PropertyToIvarMap(InPropertyToIvarMap) {}
 
     void VisitStmt(const Stmt *S) { VisitChildren(S); }
@@ -80,7 +83,11 @@
 
   /// Check if the any of the methods inside the interface are annotated with
   /// the invalidation annotation.
-  bool containsInvalidationMethod(const ObjCContainerDecl *D) const;
+  static bool containsInvalidationMethod(const ObjCContainerDecl *D);
+
+  /// Check if ivar should be tracked and add to TrackedIvars if positive.
+  /// Returns true if ivar should be tracked.
+  static bool trackIvar(const ObjCIvarDecl *Iv, IvarSet &TrackedIvars);
 
   /// Given the property declaration, and the list of tracked ivars, finds
   /// the ivar backing the property when possible. Returns '0' when no such
@@ -88,7 +95,7 @@
   static const ObjCIvarDecl *findPropertyBackingIvar(
       const ObjCPropertyDecl *Prop,
       const ObjCInterfaceDecl *InterfaceD,
-      IvarSet TrackedIvars);
+      IvarSet &TrackedIvars);
 
 public:
   void checkASTDecl(const ObjCMethodDecl *D, AnalysisManager& Mgr,
@@ -109,7 +116,7 @@
 }
 
 bool IvarInvalidationChecker::containsInvalidationMethod (
-    const ObjCContainerDecl *D) const {
+    const ObjCContainerDecl *D) {
 
   // TODO: Cache the results.
 
@@ -152,16 +159,36 @@
   llvm_unreachable("One of the casts above should have succeeded.");
 }
 
+bool IvarInvalidationChecker::trackIvar(const ObjCIvarDecl *Iv,
+                                        IvarSet &TrackedIvars) {
+  QualType IvQTy = Iv->getType();
+  const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>();
+  if (!IvTy)
+    return false;
+  const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl();
+  if (containsInvalidationMethod(IvInterf)) {
+    TrackedIvars[cast<ObjCIvarDecl>(Iv->getCanonicalDecl())] = false;
+    return true;
+  }
+  return false;
+}
+
 const ObjCIvarDecl *IvarInvalidationChecker::findPropertyBackingIvar(
                         const ObjCPropertyDecl *Prop,
                         const ObjCInterfaceDecl *InterfaceD,
-                        IvarSet TrackedIvars) {
+                        IvarSet &TrackedIvars) {
   const ObjCIvarDecl *IvarD = 0;
 
   // Lookup for the synthesized case.
   IvarD = Prop->getPropertyIvarDecl();
-  if (IvarD)
-    return IvarD;
+  if (IvarD) {
+    if (TrackedIvars.count(IvarD)) {
+      return IvarD;
+    }
+    // If the ivar is synthesized we still want to track it.
+    if (trackIvar(IvarD, TrackedIvars))
+      return IvarD;
+  }
 
   // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars.
   StringRef PropName = Prop->getIdentifier()->getName();
@@ -202,19 +229,14 @@
       II = InterfaceD->ivar_begin(),
       IE = InterfaceD->ivar_end(); II != IE; ++II) {
     const ObjCIvarDecl *Iv = *II;
-    QualType IvQTy = Iv->getType();
-    const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>();
-    if (!IvTy)
-      continue;
-    const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl();
-    if (containsInvalidationMethod(IvInterf))
-      Ivars[cast<ObjCIvarDecl>(Iv->getCanonicalDecl())] = false;
+    trackIvar(Iv, Ivars);
   }
 
-  // Construct Property/Property Setter to Ivar maps to assist checking if an
+  // Construct Property/Property Accessor to Ivar maps to assist checking if an
   // ivar which is backing a property has been reset.
-  MethToIvarMapTy PropSetterToIvarMap;
+  MethToIvarMapTy PropAccessorToIvarMap;
   PropToIvarMapTy PropertyToIvarMap;
+  IvarToPropMapTy IvarToPopertyMap;
   for (ObjCInterfaceDecl::prop_iterator
       I = InterfaceD->prop_begin(),
       E = InterfaceD->prop_end(); I != E; ++I) {
@@ -224,24 +246,31 @@
     if (!ID) {
       continue;
     }
-    // Find the setter.
-    const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl();
-    // If we don't know the setter, do not track this ivar.
-    if (!SetterD)
-      continue;
 
     // Store the mappings.
     PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
-    SetterD = cast<ObjCMethodDecl>(SetterD->getCanonicalDecl());
     PropertyToIvarMap[PD] = ID;
-    PropSetterToIvarMap[SetterD] = ID;
+    IvarToPopertyMap[ID] = PD;
+
+    // Find the setter and the getter.
+    const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl();
+    if (SetterD) {
+      SetterD = cast<ObjCMethodDecl>(SetterD->getCanonicalDecl());
+      PropAccessorToIvarMap[SetterD] = ID;
+    }
+
+    const ObjCMethodDecl *GetterD = PD->getGetterMethodDecl();
+    if (GetterD) {
+      GetterD = cast<ObjCMethodDecl>(GetterD->getCanonicalDecl());
+      PropAccessorToIvarMap[GetterD] = ID;
+    }
   }
 
 
   // Check which ivars have been accessed by the method.
   // We assume that if ivar was at least accessed, it was not forgotten.
   MethodCrawler(InterfaceD, Ivars,
-                PropSetterToIvarMap, PropertyToIvarMap).VisitStmt(D->getBody());
+                PropAccessorToIvarMap, PropertyToIvarMap).VisitStmt(D->getBody());
 
   // Warn on the ivars that were not accessed by the method.
   for (IvarSet::const_iterator I = Ivars.begin(), E = Ivars.end(); I != E; ++I){
@@ -254,8 +283,17 @@
 
       SmallString<128> sbuf;
       llvm::raw_svector_ostream os(sbuf);
-      os << "Instance variable "<< IvarDecl->getName()
-         << " needs to be invalidated";
+
+      // Construct the warning message.
+      if (IvarDecl->getSynthesize()) {
+        const ObjCPropertyDecl *PD = IvarToPopertyMap[IvarDecl];
+        assert(PD &&
+               "Do we synthesize ivars for something other than properties?");
+        os << "Property "<< PD->getName() << " needs to be invalidated";
+      } else {
+        os << "Instance variable "<< IvarDecl->getName()
+             << " needs to be invalidated";
+      }
 
       BR.EmitBasicReport(D,
           "Incomplete invalidation",
@@ -282,7 +320,7 @@
   const ObjCMethodDecl *MD = ME->getMethodDecl();
   if (MD) {
     MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
-    IVars[PropertySetterToIvarMap[MD]] = true;
+    IVars[PropertyAccessorToIvarMap[MD]] = true;
   }
   VisitStmt(ME);
 }
@@ -305,7 +343,7 @@
     const ObjCMethodDecl *MD = PA->getImplicitPropertySetter();
     if (MD) {
       MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
-      IVars[PropertySetterToIvarMap[MD]] = true;
+      IVars[PropertyAccessorToIvarMap[MD]] = true;
       VisitStmt(PA);
       return;
     }
diff --git a/test/Analysis/objc_invalidation.m b/test/Analysis/objc_invalidation.m
index e0ce28b..ededae6 100644
--- a/test/Analysis/objc_invalidation.m
+++ b/test/Analysis/objc_invalidation.m
@@ -50,6 +50,7 @@
   SomeInvalidationImplementingObject *_propIvar;
   Invalidation1Class *MultipleProtocols;
   Invalidation2Class *MultInheritance; 
+  SomeInvalidationImplementingObject *_Prop5; // invalidate via getter method
   
   // No warnings on these.
   NSObject *NObj1;
@@ -63,6 +64,10 @@
 @property (assign) SomeInvalidationImplementingObject* Prop2;
 @property (assign) SomeInvalidationImplementingObject* Prop3;
 @property (assign) SomeInvalidationImplementingObject* Prop4;
+@property (assign) SomeInvalidationImplementingObject* Prop5;
+@property (assign) SomeInvalidationImplementingObject *SynthIvarProp;
+
+
 @property (assign) NSObject* NProp0;
 @property (nonatomic, assign) NSObject* NProp1;
 @property (assign) NSObject* NProp2;
@@ -98,13 +103,16 @@
 
 - (void) invalidate {
    [Obj3 invalidate];
+   self.Prop0 = 0;
    self.Prop1 = 0;
    [self setProp2:0];
    [self setProp3:0];
    self.Prop4 = 0;
+   [[self Prop5] invalidate];
    [super invalidate];
 }// expected-warning {{Instance variable Obj1 needs to be invalidated}}
  // expected-warning@-1 {{Instance variable Obj2 needs to be invalidated}}
  // expected-warning@-2 {{Instance variable MultipleProtocols needs to be invalidated}}
  // expected-warning@-3 {{Instance variable MultInheritance needs to be invalidated}}
+ // expected-warning@-4 {{Property SynthIvarProp needs to be invalidated}}
 @end