// 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 <NSCoding>
@required
+ (BOOL)supportsSecureCoding;
@end
@interface NSObject <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 <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 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 <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration>

- (NSUInteger)count;
- (id)objectForKey:(id)aKey;
- (NSEnumerator *)keyEnumerator;

@end

@interface NSDictionary (NSDictionaryCreation)

+ (id)dictionary;
+ (id)dictionaryWithObject:(id)object forKey:(id <NSCopying>)key;
+ (instancetype)dictionaryWithObjects:(const id [])objects forKeys:(const id <NSCopying> [])keys count:(NSUInteger)cnt;

@end

@interface NSMutableDictionary : NSDictionary

- (void)removeObjectForKey:(id)aKey;
- (void)setObject:(id)anObject forKey:(id <NSCopying>)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 <NSCopying>)key __attribute__((availability(macosx,introduced=10.8)));

@end

@interface NSOrderedSet : NSObject <NSFastEnumeration>
@end
@interface NSOrderedSet (NSOrderedSetCreation)
- (NSUInteger)count;
@end

@interface NSString : NSObject <NSCopying, NSMutableCopying, NSSecureCoding>

@end

@interface NSNull : NSObject <NSCopying, NSSecureCoding>
+ (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<NSCopying>)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}}
}
