// RUN: %clang_analyze_cc1 -Wno-objc-literal-conversion -Wno-objc-root-class -fobjc-arc \ // RUN: -analyzer-checker=core,osx.cocoa,nullability \ // RUN: -analyzer-config eagerly-assume=false \ // RUN: -analyzer-checker=debug.ExprInspection -verify %s void clang_analyzer_eval(int); #define nil ((id)0) 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 NSSecureCoding @required + (BOOL)supportsSecureCoding; @end @interface NSObject {} - (id)init; + (id)alloc; - (id)mutableCopy; @end typedef struct { unsigned long state; id __unsafe_unretained _Nullable * _Nullable itemsPtr; unsigned long * _Nullable mutationsPtr; unsigned long extra[5]; } NSFastEnumerationState; @protocol NSFastEnumeration - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained _Nullable [_Nonnull])buffer count:(NSUInteger)len; @end @interface NSArray : NSObject - (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 NSArray (NSArrayCreation) + (instancetype)arrayWithObjects:(const id [])objects count:(NSUInteger)cnt; @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 @interface NSDictionary : NSObject - (NSUInteger)count; - (id)objectForKey:(id)aKey; - (NSEnumerator *)keyEnumerator; @end @interface NSDictionary (NSDictionaryCreation) + (id)dictionary; + (id)dictionaryWithObject:(id)object forKey:(id )key; + (instancetype)dictionaryWithObjects:(const id [])objects forKeys:(const id [])keys count:(NSUInteger)cnt; @end @interface NSMutableDictionary : NSDictionary - (void)removeObjectForKey:(id)aKey; - (void)setObject:(id)anObject forKey:(id )aKey; @end @interface NSMutableDictionary (NSExtendedMutableDictionary) - (void)addEntriesFromDictionary:(NSDictionary *)otherDictionary; - (void)removeAllObjects; - (void)removeObjectsForKeys:(NSArray *)keyArray; - (void)setDictionary:(NSDictionary *)otherDictionary; - (void)setObject:(id)obj forKeyedSubscript:(id )key __attribute__((availability(macosx,introduced=10.8))); @end @interface NSOrderedSet : NSObject @end @interface NSOrderedSet (NSOrderedSetCreation) - (NSUInteger)count; @end @interface NSString : NSObject @end @interface NSNull : NSObject + (NSNull *)null; @end // NSMutableArray API void testNilArgNSMutableArray1(void) { NSMutableArray *marray = [[NSMutableArray alloc] init]; [marray addObject:0]; // expected-warning {{Argument to 'NSMutableArray' method 'addObject:' cannot be nil}} } void testNilArgNSMutableArray2(void) { NSMutableArray *marray = [[NSMutableArray alloc] init]; [marray insertObject:0 atIndex:1]; // expected-warning {{Argument to 'NSMutableArray' method 'insertObject:atIndex:' cannot be nil}} } void testNilArgNSMutableArray3(void) { NSMutableArray *marray = [[NSMutableArray alloc] init]; [marray replaceObjectAtIndex:1 withObject:0]; // expected-warning {{Argument to 'NSMutableArray' method 'replaceObjectAtIndex:withObject:' cannot be nil}} } void testNilArgNSMutableArray4(void) { NSMutableArray *marray = [[NSMutableArray alloc] init]; [marray setObject:0 atIndexedSubscript:1]; // expected-warning {{Argument to 'NSMutableArray' method 'setObject:atIndexedSubscript:' cannot be nil}} } void testNilArgNSMutableArray5(void) { NSMutableArray *marray = [[NSMutableArray alloc] init]; marray[1] = 0; // expected-warning {{Array element cannot be nil}} } // NSArray API void testNilArgNSArray1(void) { NSArray *array = [[NSArray alloc] init]; NSArray *copyArray = [array arrayByAddingObject:0]; // expected-warning {{Argument to 'NSArray' method 'arrayByAddingObject:' cannot be nil}} } // NSMutableDictionary and NSDictionary APIs. void testNilArgNSMutableDictionary1(NSMutableDictionary *d, NSString* key) { [d setObject:0 forKey:key]; // expected-warning {{Value argument to 'setObject:forKey:' cannot be nil}} } void testNilArgNSMutableDictionary2(NSMutableDictionary *d, NSObject *obj) { [d setObject:obj forKey:0]; // expected-warning {{Key argument to 'setObject:forKey:' cannot be nil}} } void testNilArgNSMutableDictionary3(NSMutableDictionary *d) { [d removeObjectForKey:0]; // expected-warning {{Value argument to 'removeObjectForKey:' cannot be nil}} } void testNilArgNSMutableDictionary5(NSMutableDictionary *d, NSString* key) { d[key] = 0; // no-warning - removing the mapping for the given key } void testNilArgNSMutableDictionary6(NSMutableDictionary *d, NSString *key) { if (key) ; d[key] = 0; // expected-warning {{'NSMutableDictionary' key cannot be nil}} } NSDictionary *testNilArgNSDictionary1(NSString* key) { return [NSDictionary dictionaryWithObject:0 forKey:key]; // expected-warning {{Value argument to 'dictionaryWithObject:forKey:' cannot be nil}} } NSDictionary *testNilArgNSDictionary2(NSObject *obj) { return [NSDictionary dictionaryWithObject:obj forKey:0]; // expected-warning {{Key argument to 'dictionaryWithObject:forKey:' cannot be nil}} } id testCreateDictionaryLiteralKey(id value, id nilKey) { if (nilKey) ; return @{@"abc":value, nilKey:@"abc"}; // expected-warning {{Dictionary key cannot be nil}} } id testCreateDictionaryLiteralValue(id nilValue) { if (nilValue) ; return @{@"abc":nilValue}; // expected-warning {{Dictionary value cannot be nil}} } id testCreateDictionaryLiteral(id nilValue, id nilKey) { if (nilValue) ; if (nilKey) ; return @{@"abc":nilValue, nilKey:@"abc"}; // expected-warning {{Dictionary key cannot be nil}} // expected-warning@-1 {{Dictionary value cannot be nil}} } id testCreateArrayLiteral(id myNil) { if (myNil) ; return @[ @"a", myNil, @"c" ]; // expected-warning {{Array element cannot be nil}} } // Test inline defensive checks suppression. void idc(id x) { if (x) ; } void testIDC(NSMutableDictionary *d, NSString *key) { idc(key); d[key] = @"abc"; // no-warning } @interface Foo { @public int x; } - (int *)getPtr; - (int)getInt; - (NSMutableDictionary *)getDictPtr; @property (retain, readonly, nonatomic) Foo* data; - (NSString*) stringForKeyFE: (id)key; @end void idc2(id x) { if (!x) return; } Foo *retNil(void) { return 0; } void testIDC2(Foo *obj) { idc2(obj); *[obj getPtr] = 1; // no-warning } int testIDC3(Foo *obj) { idc2(obj); return 1/[obj getInt]; } void testNilReceiverIDC(Foo *obj, NSString *key) { NSMutableDictionary *D = [obj getDictPtr]; idc(D); D[key] = @"abc"; // no-warning } void testNilReceiverRetNil2(NSMutableDictionary *D, Foo *FooPtrIn, id value) { NSString* const kKeyIdentifier = @"key"; Foo *FooPtr = retNil(); NSString *key = [[FooPtr data] stringForKeyFE: kKeyIdentifier]; // key is nil because FooPtr is nil. However, FooPtr is set to nil inside an // inlined function, so this error report should be suppressed. [D setObject: value forKey: key]; // no-warning } void testAssumeNSNullNullReturnsNonNil(NSMutableDictionary *Table, id Object, id InValue) { id Value = Object ? [Table objectForKey:Object] : [NSNull null]; if (!Value) { Value = InValue; [Table setObject:Value forKey:Object]; // no warning } } void testCollectionIsNotEmptyWhenCountIsGreaterThanZero(NSMutableDictionary *D){ if ([D count] > 0) { // Count is greater than zero. NSString *s = 0; for (NSString *key in D) { s = key; // Loop is always entered. } [D removeObjectForKey:s]; // no warning } } void testCountAwareNSOrderedSet(NSOrderedSet *containers, int *validptr) { int *x = 0; NSUInteger containerCount = [containers count]; if (containerCount > 0) x = validptr; for (id c in containers) { *x = 1; // no warning } } void testLiteralsNonNil(void) { clang_analyzer_eval(!!@[]); // expected-warning{{TRUE}} clang_analyzer_eval(!!@{}); // expected-warning{{TRUE}} } @interface NSMutableArray (MySafeAdd) - (void)addObject:(id)obj safe:(BOOL)safe; @end void testArrayCategory(NSMutableArray *arr) { [arr addObject:0 safe:1]; // no-warning } @interface MyView : NSObject -(NSArray *)subviews; @end void testNoReportWhenReceiverNil(NSMutableArray *array, int b) { // Don't warn about adding nil to a container when the receiver is also // definitely nil. if (array == 0) { [array addObject:0]; // no-warning } MyView *view = b ? [[MyView alloc] init] : 0; NSMutableArray *subviews = [[view subviews] mutableCopy]; // When view is nil, subviews is also nil so there should be no warning // here either. [subviews addObject:view]; // no-warning } NSString *getStringFromString(NSString *string) { if (!string) return nil; return @"New String"; } void testInlinedDefensiveCheck(NSMutableDictionary *dict, id obj) { // The check in getStringFromString() is not a good indication // that 'obj' can be nil in this context. dict[obj] = getStringFromString(obj); // no-warning } Foo * getMightBeNullFoo(); Foo * _Nonnull getNonnullFoo(); Foo * _Nullable getNullableFoo(); void testCreateDictionaryLiteralWithNullableArg() { Foo *p1 = getMightBeNullFoo(); Foo *p2 = getNonnullFoo(); Foo *p3 = getNullableFoo(); clang_analyzer_eval(p1 == nil); // expected-warning {{UNKNOWN}} clang_analyzer_eval(p2 == nil); // expected-warning {{UNKNOWN}} clang_analyzer_eval(p3 == nil); // expected-warning {{UNKNOWN}} (void)@{@"abc" : p1}; // no-warning (void)@{@"abc" : p2}; // no-warning (void)@{@"abc" : p3}; // expected-warning {{Nullable pointer is passed to a callee that requires a non-null}} clang_analyzer_eval(p1 == nil); // expected-warning {{FALSE}} clang_analyzer_eval(p2 == nil); // expected-warning {{FALSE}} clang_analyzer_eval(p3 == nil); // expected-warning {{FALSE}} } void testCreateArrayLiteralWithNullableArg() { Foo *p1 = getMightBeNullFoo(); Foo *p2 = getNonnullFoo(); Foo *p3 = getNullableFoo(); clang_analyzer_eval(p1 == nil); // expected-warning {{UNKNOWN}} clang_analyzer_eval(p2 == nil); // expected-warning {{UNKNOWN}} clang_analyzer_eval(p3 == nil); // expected-warning {{UNKNOWN}} (void)@[p1]; // no-warning (void)@[p2]; // no-warning (void)@[p3]; // expected-warning {{Nullable pointer is passed to a callee that requires a non-null}} clang_analyzer_eval(p1 == nil); // expected-warning {{FALSE}} clang_analyzer_eval(p2 == nil); // expected-warning {{FALSE}} clang_analyzer_eval(p3 == nil); // expected-warning {{FALSE}} }