[analyzer] Warn when a ‘nil’ object is added to NSArray or NSMutableArray.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@177318 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
index fdb6bbb..a6bcb69 100644
--- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
+++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
@@ -127,8 +127,13 @@
   const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
   if (!ID)
     return;
-  
-  if (findKnownClass(ID) == FC_NSString) {
+
+  FoundationClass Class = findKnownClass(ID);
+
+  static const unsigned InvalidArgIndex = UINT_MAX;
+  unsigned Arg = InvalidArgIndex;
+
+  if (Class == FC_NSString) {
     Selector S = msg.getSelector();
     
     if (S.isUnarySelector())
@@ -152,10 +157,34 @@
         Name == "compare:options:range:locale:" ||
         Name == "componentsSeparatedByCharactersInSet:" ||
         Name == "initWithFormat:") {
-      if (isNil(msg.getArgSVal(0)))
-        WarnNilArg(C, msg, 0);
+      Arg = 0;
+    }
+  } else if (Class == FC_NSArray) {
+    Selector S = msg.getSelector();
+
+    if (S.isUnarySelector())
+      return;
+
+    if (S.getNameForSlot(0).equals("addObject")) {
+      Arg = 0;
+    } else if (S.getNameForSlot(0).equals("insertObject") &&
+               S.getNameForSlot(1).equals("atIndex")) {
+      Arg = 0;
+    } else if (S.getNameForSlot(0).equals("replaceObjectAtIndex") &&
+               S.getNameForSlot(1).equals("withObject")) {
+      Arg = 1;
+    } else if (S.getNameForSlot(0).equals("setObject") &&
+               S.getNameForSlot(1).equals("atIndexedSubscript")) {
+      Arg = 0;
+    } else if (S.getNameForSlot(0).equals("arrayByAddingObject")) {
+      Arg = 0;
     }
   }
+
+  // If argument is '0', report a warning.
+  if ((Arg != InvalidArgIndex) && isNil(msg.getArgSVal(Arg)))
+    WarnNilArg(C, msg, Arg);
+
 }
 
 //===----------------------------------------------------------------------===//
diff --git a/test/Analysis/NSContainers.m b/test/Analysis/NSContainers.m
new file mode 100644
index 0000000..7542a02
--- /dev/null
+++ b/test/Analysis/NSContainers.m
@@ -0,0 +1,76 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.NilArg -verify -Wno-objc-root-class %s
+typedef unsigned long NSUInteger;
+typedef signed char BOOL;
+typedef struct _NSZone NSZone;
+@class NSInvocation, NSMethodSignature, NSCoder, NSString, NSEnumerator;
+@protocol NSObject
+@end
+@protocol NSCopying
+- (id)copyWithZone:(NSZone *)zone;
+@end
+@protocol NSMutableCopying
+- (id)mutableCopyWithZone:(NSZone *)zone;
+@end
+@protocol NSCoding
+- (void)encodeWithCoder:(NSCoder *)aCoder;
+@end
+@protocol NSFastEnumeration
+@end
+@protocol NSSecureCoding <NSCoding>
+@required
++ (BOOL)supportsSecureCoding;
+@end
+@interface NSObject <NSObject> {}
+- (id)init;
++ (id)alloc;
+@end
+
+@interface NSArray : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration>
+
+- (NSUInteger)count;
+- (id)objectAtIndex:(NSUInteger)index;
+
+@end
+
+@interface NSArray (NSExtendedArray)
+- (NSArray *)arrayByAddingObject:(id)anObject;
+- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx __attribute__((availability(macosx,introduced=10.8)));
+@end
+
+@interface NSMutableArray : NSArray
+
+- (void)addObject:(id)anObject;
+- (void)insertObject:(id)anObject atIndex:(NSUInteger)index;
+- (void)removeLastObject;
+- (void)removeObjectAtIndex:(NSUInteger)index;
+- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject;
+
+@end
+
+// NSMutableArray API
+void testNilArg1() {
+  NSMutableArray *marray = [[NSMutableArray alloc] init];
+  [marray addObject:0]; // expected-warning {{Argument to 'NSMutableArray' method 'addObject:' cannot be nil}}
+}
+
+void testNilArg2() {
+  NSMutableArray *marray = [[NSMutableArray alloc] init];
+  [marray insertObject:0 atIndex:1]; // expected-warning {{Argument to 'NSMutableArray' method 'insertObject:atIndex:' cannot be nil}}
+}
+
+void testNilArg3() {
+  NSMutableArray *marray = [[NSMutableArray alloc] init];
+  [marray replaceObjectAtIndex:1 withObject:0]; // expected-warning {{Argument to 'NSMutableArray' method 'replaceObjectAtIndex:withObject:' cannot be nil}}
+}
+
+void testNilArg4() {
+  NSMutableArray *marray = [[NSMutableArray alloc] init];
+  [marray setObject:0 atIndexedSubscript:1]; // expected-warning {{Argument to 'NSMutableArray' method 'setObject:atIndexedSubscript:' cannot be nil}}
+}
+
+// NSArray API
+void testNilArg5() {
+  NSArray *array = [[NSArray alloc] init];
+  NSArray *copyArray = [array arrayByAddingObject:0]; // expected-warning {{Argument to 'NSArray' method 'arrayByAddingObject:' cannot be nil}}
+}
+