[analyzer] Be more consistent about Objective-C methods that free memory.

Previously, MallocChecker's pointer escape check and its post-call state
update for Objective-C method calls had a fair amount duplicated logic
and not-entirely-consistent checks. This commit restructures all this to
be more consistent and possibly allow us to be more aggressive in warning
about double-frees.

New policy (applies to system header methods only):
(1) If this is a method we know about, model it as taking/holding ownership
    of the passed-in buffer.
  (1a) ...unless there's a "freeWhenDone:" parameter with a zero (NO) value.
(2) If there's a "freeWhenDone:" parameter (but it's not a method we know
    about), treat the buffer as escaping if the value is non-zero (YES) and
    non-escaping if it's zero (NO).
(3) If the first selector piece ends with "NoCopy" (but it's not a method we
    know about and there's no "freeWhenDone:" parameter), treat the buffer
    as escaping.

The reason that (2) and (3) don't explicitly model the ownership transfer is
because we can't be sure that they will actually free the memory using free(),
and we wouldn't want to emit a spurious "mismatched allocator" warning
(coming in Anton's upcoming patch). In the future, we may have an idea of a
"generic deallocation", i.e. we assume that the deallocator is correct but
still continue tracking the region so that we can warn about double-frees.

Patch by Anton Yartsev, with modifications from me.

llvm-svn: 176744
diff --git a/clang/test/Analysis/malloc.mm b/clang/test/Analysis/malloc.mm
index f2a195c..2f583b4 100644
--- a/clang/test/Analysis/malloc.mm
+++ b/clang/test/Analysis/malloc.mm
@@ -1,19 +1,6 @@
 // RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc -analyzer-store=region -verify -fblocks %s
-#include "Inputs/system-header-simulator-objc.h"
-
-typedef __typeof(sizeof(int)) size_t;
-void *malloc(size_t);
-void free(void *);
-
-@interface Wrapper : NSData
-- (id)initWithBytesNoCopy:(void *)bytes length:(NSUInteger)len;
-@end
-
-@implementation Wrapper
-- (id)initWithBytesNoCopy:(void *)bytes length:(NSUInteger)len {
-  return [self initWithBytesNoCopy:bytes length:len freeWhenDone:1]; // no-warning
-}
-@end
+#import "Inputs/system-header-simulator-objc.h"
+#import "Inputs/system-header-simulator-for-malloc.h"
 
 // Done with headers. Start testing.
 void testNSDatafFreeWhenDoneNoError(NSUInteger dataLength) {
@@ -79,6 +66,11 @@
   NSString *nsstr = [[NSString alloc] initWithCharactersNoCopy:data length:dataLength freeWhenDone:0]; // expected-warning{{leak}}
 }
 
+void testOffsetFree() {
+  int *p = (int *)malloc(sizeof(int));
+  NSData *nsdata = [NSData dataWithBytesNoCopy:++p length:sizeof(int) freeWhenDone:1]; // expected-warning{{Argument to free() is offset by 4 bytes from the start of memory allocated by malloc()}}
+}
+
 void testRelinquished1() {
   void *data = malloc(42);
   NSData *nsdata = [NSData dataWithBytesNoCopy:data length:42 freeWhenDone:1];
@@ -92,6 +84,31 @@
   [NSData dataWithBytesNoCopy:data length:42]; // expected-warning {{Attempt to free released memory}}
 }
 
+void testNoCopy() {
+  char *p = (char *)calloc(sizeof(int), 1);
+  CustomData *w = [CustomData somethingNoCopy:p]; // no-warning
+}
+
+void testFreeWhenDone() {
+  char *p = (char *)calloc(sizeof(int), 1);
+  CustomData *w = [CustomData something:p freeWhenDone:1]; // no-warning
+}
+
+void testFreeWhenDonePositive() {
+  char *p = (char *)calloc(sizeof(int), 1);
+  CustomData *w = [CustomData something:p freeWhenDone:0]; // expected-warning{{leak}}
+}
+
+void testFreeWhenDoneNoCopy() {
+  int *p = (int *)malloc(sizeof(int));
+  CustomData *w = [CustomData somethingNoCopy:p length:sizeof(int) freeWhenDone:1]; // no-warning
+}
+
+void testFreeWhenDoneNoCopyPositive() {
+  int *p = (int *)malloc(sizeof(int));
+  CustomData *w = [CustomData somethingNoCopy:p length:sizeof(int) freeWhenDone:0]; // expected-warning{{leak}}
+}
+
 // Test CF/NS...NoCopy. PR12100: Pointers can escape when custom deallocators are provided.
 void testNSDatafFreeWhenDone(NSUInteger dataLength) {
   CFStringRef str;