[analyzer] Assume pointer escapes when a callback is passed inside
a struct.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@156135 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/StaticAnalyzer/Core/ObjCMessage.cpp b/lib/StaticAnalyzer/Core/ObjCMessage.cpp
index dc24e81..0c6d8a8 100644
--- a/lib/StaticAnalyzer/Core/ObjCMessage.cpp
+++ b/lib/StaticAnalyzer/Core/ObjCMessage.cpp
@@ -89,10 +89,28 @@
 }
 
 bool CallOrObjCMessage::isCallbackArg(unsigned Idx, const Type *T) const {
-  // Should we dig into struct fields, arrays ect?
+  // If the parameter is 0, it's harmless.
+  if (getArgSVal(Idx).isZeroConstant())
+    return false;
+    
+  // If a parameter is a block or a callback, assume it can modify pointer.
   if (T->isBlockPointerType() || T->isFunctionPointerType())
-    if (!getArgSVal(Idx).isZeroConstant())
-      return true;
+    return true;
+
+  // Check if a callback is passed inside a struct (for both, struct passed by
+  // reference and by value). Dig just one level into the struct for now.
+  if (const PointerType *PT = dyn_cast<PointerType>(T))
+    T = PT->getPointeeType().getTypePtr();
+
+  if (const RecordType *RT = T->getAsStructureType()) {
+    const RecordDecl *RD = RT->getDecl();
+    for (RecordDecl::field_iterator I = RD->field_begin(),
+                                    E = RD->field_end(); I != E; ++I ) {
+      const Type *FieldT = I->getType().getTypePtr();
+      if (FieldT->isBlockPointerType() || FieldT->isFunctionPointerType())
+        return true;
+    }
+  }
   return false;
 }
 
diff --git a/test/Analysis/malloc.c b/test/Analysis/malloc.c
index f5bff4f..a7d5b0b 100644
--- a/test/Analysis/malloc.c
+++ b/test/Analysis/malloc.c
@@ -819,6 +819,16 @@
   sqlite3_bind_text_my(0, x, 12, free); // no - warning
 }
 
+// Passing callbacks in a struct.
+void r11160612_5(StWithCallback St) {
+  void *x = malloc(12);
+  dealocateMemWhenDoneByVal(x, St);
+}
+void r11160612_6(StWithCallback St) {
+  void *x = malloc(12);
+  dealocateMemWhenDoneByRef(&St, x);
+}
+
 // ----------------------------------------------------------------------------
 // Below are the known false positives.
 
diff --git a/test/Analysis/system-header-simulator.h b/test/Analysis/system-header-simulator.h
index c910ad9..a8cba9c 100644
--- a/test/Analysis/system-header-simulator.h
+++ b/test/Analysis/system-header-simulator.h
@@ -37,3 +37,12 @@
                  int (*)(void *));
 
 int sqlite3_bind_text_my(int, const char*, int n, void(*)(void*));
+
+typedef void (*freeCallback) (void*);
+typedef struct {
+  int i;
+  freeCallback fc;
+} StWithCallback;
+
+int dealocateMemWhenDoneByVal(void*, StWithCallback);
+int dealocateMemWhenDoneByRef(StWithCallback*, const void*);