|
|
|
/*
|
|
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#import <Foundation/Foundation.h>
|
|
|
|
#include <memory>
|
|
|
|
|
|
|
|
@interface SomeObject : NSObject
|
|
|
|
|
|
|
|
@property int x;
|
|
|
|
@property std::shared_ptr<int> ptr;
|
|
|
|
|
|
|
|
- (int)returnsPOD;
|
|
|
|
|
|
|
|
- (std::shared_ptr<int>)returnsnonPOD;
|
|
|
|
|
|
|
|
- (int)add:(int)param1 andParam2:(int)param2;
|
|
|
|
|
|
|
|
- (int*)getXPtr;
|
|
|
|
|
|
|
|
+ (SomeObject*)unknown;
|
|
|
|
|
|
|
|
+ (SomeObject*)returnsNil;
|
|
|
|
|
|
|
|
- (SomeObject*)get;
|
|
|
|
|
|
|
|
+ (NSString*)returnsStringNil;
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation SomeObject
|
|
|
|
|
|
|
|
- (int)returnsPOD {
|
|
|
|
return _x;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (std::shared_ptr<int>)returnsnonPOD {
|
|
|
|
return std::shared_ptr<int>(new int(_x));
|
|
|
|
}
|
|
|
|
|
|
|
|
- (int)add:(int)param1 andParam2:(int)param2 {
|
|
|
|
return _x + param1 + param2;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (int*)getXPtr {
|
|
|
|
return &_x;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (SomeObject*)returnsNil {
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (SomeObject*)get {
|
|
|
|
SomeObject* o = [SomeObject new];
|
|
|
|
return o;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (NSString*)returnsStringNil {
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
int dereferenceNilBad() {
|
|
|
|
int* int_ptr = nil;
|
|
|
|
return *int_ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
int testCallMethodReturnsPODOk() {
|
|
|
|
SomeObject* obj = nil;
|
|
|
|
return [obj returnsPOD];
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<int> testCallMethodReturnsnonPODBad() {
|
|
|
|
SomeObject* obj = nil;
|
|
|
|
std::shared_ptr<int> d = [obj returnsnonPOD]; // UB
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<int> testSkippedOK() {
|
|
|
|
SomeObject* obj = [[SomeObject unknown] get];
|
|
|
|
std::shared_ptr<int> result = [obj returnsnonPOD];
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<int> testNonPODTraceBad() {
|
|
|
|
SomeObject* obj = [[SomeObject returnsNil] get];
|
|
|
|
std::shared_ptr<int> result = [obj returnsnonPOD];
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<int> testCallMethodReturnsnonPODLatent(bool b) {
|
|
|
|
SomeObject* obj;
|
|
|
|
if (b) {
|
|
|
|
obj = nil;
|
|
|
|
} else {
|
|
|
|
obj = [SomeObject new];
|
|
|
|
}
|
|
|
|
std::shared_ptr<int> d = [obj returnsnonPOD]; // UB if obj nil
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<int> testCallMethodReturnsnonPODLatentBad(bool b) {
|
|
|
|
return testCallMethodReturnsnonPODLatent(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<int> testCallMethodReturnsnonPODLatentOk(bool b) {
|
|
|
|
return testCallMethodReturnsnonPODLatent(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
int testAccessPropertyAccessorOk() {
|
|
|
|
SomeObject* obj = nil;
|
|
|
|
return obj.x; // calls property accessor method
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<int> testAccessPropertyAccessorBad() {
|
|
|
|
SomeObject* obj = nil;
|
|
|
|
return obj.ptr; // calls property accessor method, but return type is non-POD
|
|
|
|
}
|
|
|
|
|
|
|
|
int methodReturnsPOD(SomeObject* obj) { return [obj returnsPOD]; };
|
|
|
|
|
|
|
|
int methodReturnsPODNilOk() { return methodReturnsPOD(nil); };
|
|
|
|
|
|
|
|
int methodReturnsPODNotNilOK() {
|
|
|
|
SomeObject* o = [SomeObject new];
|
|
|
|
return methodReturnsPOD(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
int testFalsyReturnedValueOk() {
|
|
|
|
int x = testCallMethodReturnsPODOk();
|
|
|
|
|
|
|
|
if (x != 0) {
|
|
|
|
int* int_ptr = nil;
|
|
|
|
return *int_ptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int testParamsRemainTheSameOk() {
|
|
|
|
SomeObject* obj = nil;
|
|
|
|
int x1 = 0;
|
|
|
|
int x2 = 5;
|
|
|
|
int x = [obj add:x1 andParam2:x2];
|
|
|
|
|
|
|
|
if (x1 != 0) {
|
|
|
|
int* int_ptr = nil;
|
|
|
|
return *int_ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (x2 != 5) {
|
|
|
|
int* int_ptr = nil;
|
|
|
|
return *int_ptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int testTraceBad() {
|
|
|
|
SomeObject* obj = nil;
|
|
|
|
int* ptr = [obj getXPtr];
|
|
|
|
return *ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void testUnknownNilSpecOk() {
|
|
|
|
NSString* const str = [SomeObject returnsStringNil];
|
|
|
|
if (str.length == 0) {
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
NSDictionary* dict = @{@"helloString" : str};
|
|
|
|
}
|
|
|
|
|
|
|
|
void addNilInDictBad(NSMutableDictionary* mDict) {
|
|
|
|
id value = nil;
|
|
|
|
[mDict setObject:value forKey:@"somestring"];
|
|
|
|
}
|
|
|
|
|
|
|
|
void addNSNullInDictOk(NSMutableDictionary* mDict) {
|
|
|
|
[mDict setObject:[NSNull null] forKey:@"somestring"];
|
|
|
|
}
|
|
|
|
|
|
|
|
void addObjectInDictOk(NSMutableDictionary* mDict) {
|
|
|
|
SomeObject* o = [SomeObject new];
|
|
|
|
[mDict setObject:o forKey:@"somestring"];
|
|
|
|
}
|
|
|
|
|
|
|
|
void addObjectKeyNilInDictBad(NSMutableDictionary* mDict) {
|
|
|
|
SomeObject* o = [SomeObject new];
|
|
|
|
[mDict setObject:o forKey:nil];
|
|
|
|
}
|
|
|
|
|
|
|
|
void addObjectInDict(NSMutableDictionary* mDict, id value) {
|
|
|
|
[mDict setObject:value forKey:@"somestring"];
|
|
|
|
}
|
|
|
|
|
|
|
|
void addNilInDictBracketsOk(NSMutableDictionary* mDict) {
|
|
|
|
mDict[@"key2"] = nil; // Passing nil will cause any object corresponding
|
|
|
|
// to a key to be removed from the dictionary.
|
|
|
|
}
|
|
|
|
|
|
|
|
void addNilKeyInDictBracketsBad(NSMutableDictionary* mDict) {
|
|
|
|
id key = nil;
|
|
|
|
mDict[key] = @"somestring";
|
|
|
|
}
|
|
|
|
|
|
|
|
void addInDictBracketsOk(NSMutableDictionary* mDict) {
|
|
|
|
mDict[@"key"] = @"somestring";
|
|
|
|
}
|
|
|
|
|
|
|
|
void removeObjectFromDict(NSMutableDictionary* mDict, id key) {
|
|
|
|
[mDict removeObjectForKey:key];
|
|
|
|
}
|
|
|
|
|
|
|
|
void removeObjectFromDictKeyNilBad(NSMutableDictionary* mDict) {
|
|
|
|
removeObjectFromDict(mDict, nil);
|
|
|
|
}
|
|
|
|
|
|
|
|
void removeObjectFromDictKeyNotNilOK(NSMutableDictionary* mDict) {
|
|
|
|
removeObjectFromDict(mDict, @"somestring");
|
|
|
|
}
|
|
|
|
|
|
|
|
void dictionaryWithSharedKeySetOk() {
|
|
|
|
id sharedKeySet = [NSDictionary sharedKeySetForKeys:@[ @"key1", @"key2" ]];
|
|
|
|
NSMutableDictionary* mDict =
|
|
|
|
[NSMutableDictionary dictionaryWithSharedKeySet:sharedKeySet];
|
|
|
|
}
|
|
|
|
|
|
|
|
void dictionaryWithSharedKeySetBad() {
|
|
|
|
NSMutableDictionary* mDict =
|
|
|
|
[NSMutableDictionary dictionaryWithSharedKeySet:nil];
|
|
|
|
}
|
|
|
|
|
|
|
|
void testNilMessagingForModelNilNilOK_FP() { addObjectInDict(nil, nil); }
|
|
|
|
|
|
|
|
void testNilMessagingForModelNilStringOK() {
|
|
|
|
addObjectInDict(nil, @"somestring");
|
|
|
|
}
|
|
|
|
|
|
|
|
void testNilMessagingForModelNotNilDictBad(NSMutableDictionary* mDict) {
|
|
|
|
addObjectInDict(mDict, nil);
|
|
|
|
}
|
|
|
|
|
|
|
|
void addNilInArrayBad(NSMutableArray* mArray) { [mArray addObject:nil]; }
|
|
|
|
|
|
|
|
void addObjectInArrayOk(NSMutableArray* mArray) {
|
|
|
|
[mArray addObject:[SomeObject new]];
|
|
|
|
}
|
|
|
|
|
|
|
|
void insertNilInArrayBad(NSMutableArray* mArray) {
|
|
|
|
[mArray insertObject:nil atIndex:0];
|
|
|
|
}
|
|
|
|
|
|
|
|
void insertObjectInArrayOk(NSMutableArray* mArray) {
|
|
|
|
[mArray insertObject:[SomeObject new] atIndex:0];
|
|
|
|
}
|
|
|
|
|
|
|
|
void replaceNilInArrayBad(NSMutableArray* mArray) {
|
|
|
|
[mArray replaceObjectAtIndex:0 withObject:nil];
|
|
|
|
}
|
|
|
|
|
|
|
|
void replaceObjectInArrayOk(NSMutableArray* mArray) {
|
|
|
|
[mArray replaceObjectAtIndex:0 withObject:[SomeObject new]];
|
|
|
|
}
|
|
|
|
|
|
|
|
void removeObjectsAtIndexesFromArray(NSMutableArray* mArray, id indexset) {
|
|
|
|
[mArray removeObjectsAtIndexes:indexset];
|
|
|
|
}
|
|
|
|
|
|
|
|
void removeObjectsAtIndexesFromArrayOK(NSMutableArray* mArray) {
|
|
|
|
NSIndexSet* indexset = [NSIndexSet indexSetWithIndex:1];
|
|
|
|
removeObjectsAtIndexesFromArray(mArray, indexset);
|
|
|
|
}
|
|
|
|
|
|
|
|
void removeObjectsAtIndexesFromArrayBad(NSMutableArray* mArray) {
|
|
|
|
removeObjectsAtIndexesFromArray(mArray, nil);
|
|
|
|
}
|
|
|
|
|
|
|
|
void replaceObjectsAtIndexesWithObjectsInArray(NSMutableArray* mArray,
|
|
|
|
id indexset,
|
|
|
|
id objects) {
|
|
|
|
[mArray replaceObjectsAtIndexes:indexset withObjects:objects];
|
|
|
|
}
|
|
|
|
|
|
|
|
void replaceObjectsAtIndexesWithObjectsInArrayOk(NSMutableArray* mArray) {
|
|
|
|
NSIndexSet* indexset = [NSIndexSet indexSetWithIndex:0];
|
|
|
|
replaceObjectsAtIndexesWithObjectsInArray(
|
|
|
|
mArray, indexset, @[ [SomeObject new] ]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void replaceObjectsAtNilIndexesWithObjectsInArrayBad(NSMutableArray* mArray) {
|
|
|
|
replaceObjectsAtIndexesWithObjectsInArray(mArray, nil, @[ [SomeObject new] ]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void replaceObjectsAtIndexesWithNilObjectsInArrayBad(NSMutableArray* mArray) {
|
|
|
|
NSIndexSet* indexset = [NSIndexSet indexSetWithIndex:0];
|
|
|
|
replaceObjectsAtIndexesWithObjectsInArray(mArray, indexset, nil);
|
|
|
|
}
|
|
|
|
|
|
|
|
void addInDictBracketsDefault(NSMutableDictionary<NSString*, NSString*>* mDict,
|
|
|
|
NSString* key) {
|
|
|
|
mDict[key] = @"default";
|
|
|
|
}
|
|
|
|
|
|
|
|
void accessZeroElementOk(NSMutableDictionary<NSString*, NSString*>* mDict) {
|
|
|
|
NSArray<NSString*>* array =
|
|
|
|
[[NSUserDefaults standardUserDefaults] arrayForKey:@"key"];
|
|
|
|
NSString* key = array[0];
|
|
|
|
addInDictBracketsDefault(mDict, key);
|
|
|
|
}
|
|
|
|
|
|
|
|
void addObjectInMSet(NSMutableSet* mSet, id object) { [mSet addObject:object]; }
|
|
|
|
|
|
|
|
void addObjectInMSetOk(NSMutableSet* mSet) {
|
|
|
|
addObjectInMSet(mSet, @"somestring");
|
|
|
|
}
|
|
|
|
|
|
|
|
void addObjectInMSetBad(NSMutableSet* mSet) { addObjectInMSet(mSet, nil); }
|
|
|
|
|
|
|
|
void removeObjectFromMSet(NSMutableSet* mSet, id object) {
|
|
|
|
[mSet removeObject:object];
|
|
|
|
}
|
|
|
|
|
|
|
|
void removeObjectFromMSetOk(NSMutableSet* mSet) {
|
|
|
|
removeObjectFromMSet(mSet, @"somestring");
|
|
|
|
}
|
|
|
|
|
|
|
|
void removeObjectFromMSetBad(NSMutableSet* mSet) {
|
|
|
|
removeObjectFromMSet(mSet, nil);
|
|
|
|
}
|
|
|
|
|
|
|
|
void dictionaryLiteral(id key, id object) {
|
|
|
|
NSDictionary* dict = @{key : object};
|
|
|
|
}
|
|
|
|
|
|
|
|
void dictionaryLiteralOk() { dictionaryLiteral(@"key", @"obj"); }
|
|
|
|
|
|
|
|
void dictionaryLiteralKeyNilBad() { dictionaryLiteral(nil, @"obj"); }
|
|
|
|
|
|
|
|
void dictionaryLiteralObjectNilBad() { dictionaryLiteral(@"key", nil); }
|
|
|
|
|
|
|
|
void dictionaryWithObjectsForKeysCount(id key, id object) {
|
|
|
|
NSString* values[1];
|
|
|
|
values[0] = object;
|
|
|
|
|
|
|
|
NSString* keys[1];
|
|
|
|
keys[0] = key;
|
|
|
|
|
|
|
|
NSDictionary* dict = [NSDictionary dictionaryWithObjects:values
|
|
|
|
forKeys:keys
|
|
|
|
count:1];
|
|
|
|
}
|
|
|
|
|
|
|
|
void dictionaryWithObjectsForKeysCountOk() {
|
|
|
|
dictionaryWithObjectsForKeysCount(@"key", @"somestring");
|
|
|
|
}
|
|
|
|
|
|
|
|
void FN_dictionaryWithObjectsForKeysCountKeyNilBad() {
|
|
|
|
dictionaryWithObjectsForKeysCount(nil, @"somestring");
|
|
|
|
}
|
|
|
|
|
|
|
|
void FN_dictionaryWithObjectsForKeysCountObjectNilBad() {
|
|
|
|
dictionaryWithObjectsForKeysCount(@"key", nil);
|
|
|
|
}
|
|
|
|
|
|
|
|
void dictionaryWithObjectForKey(id key, id object) {
|
|
|
|
NSDictionary* dict = [NSDictionary dictionaryWithObject:object forKey:key];
|
|
|
|
}
|
|
|
|
|
|
|
|
void dictionaryWithObjectForKeyOk() {
|
|
|
|
dictionaryWithObjectForKey(@"key", @"somestring");
|
|
|
|
}
|
|
|
|
|
|
|
|
void dictionaryWithObjectForKeyNilBad() {
|
|
|
|
dictionaryWithObjectForKey(nil, @"somestring");
|
|
|
|
}
|
|
|
|
|
|
|
|
void dictionaryWithNilObjectForKeyBad() {
|
|
|
|
dictionaryWithObjectForKey(@"key", nil);
|
|
|
|
}
|
|
|
|
|
|
|
|
void dictionaryWithSharedKeySetForKeys(id keys) {
|
|
|
|
id sharedKeySet = [NSDictionary sharedKeySetForKeys:keys];
|
|
|
|
}
|
|
|
|
|
|
|
|
void dictionaryWithSharedKeySetForKeysOk() {
|
|
|
|
dictionaryWithSharedKeySetForKeys(@[ @"key1", @"key2" ]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void dictionaryWithSharedKeySetForKeysBad() {
|
|
|
|
dictionaryWithSharedKeySetForKeys(nil);
|
|
|
|
}
|
|
|
|
|
|
|
|
void setWithObject(id object) { NSSet* set = [NSSet setWithObject:object]; }
|
|
|
|
|
|
|
|
void setWithObjectOk() { setWithObject(@"obj"); }
|
|
|
|
|
|
|
|
void setWithObjectBad() { setWithObject(nil); }
|
|
|
|
|
|
|
|
void setByAddingObject(id object) {
|
|
|
|
NSSet* set = [NSSet set];
|
|
|
|
set = [set setByAddingObject:object];
|
|
|
|
}
|
|
|
|
|
|
|
|
void setByAddingObjectOk() { setByAddingObject(@"obj"); }
|
|
|
|
|
|
|
|
void setByAddingObjectBad() { setByAddingObject(nil); }
|
|
|
|
|
|
|
|
void setWithObjectsForCount(id object) {
|
|
|
|
NSString* values[1];
|
|
|
|
values[0] = object;
|
|
|
|
|
|
|
|
NSSet* set = [NSSet setWithObjects:values count:1];
|
|
|
|
}
|
|
|
|
|
|
|
|
void setWithObjectsForCountOk() { setWithObjectsForCount(@"obj"); }
|
|
|
|
|
|
|
|
void FN_setWithObjectsForCountBad() { setWithObjectsForCount(nil); }
|
|
|
|
|
|
|
|
void setInitWithObjectsForCount(id object) {
|
|
|
|
NSString* values[1];
|
|
|
|
values[0] = object;
|
|
|
|
|
|
|
|
NSSet* set = [[NSSet alloc] initWithObjects:values count:1];
|
|
|
|
}
|
|
|
|
|
|
|
|
void setInitWithObjectsForCountOk() { setInitWithObjectsForCount(@"obj"); }
|
|
|
|
|
|
|
|
void FN_setInitWithObjectsForCountBad() { setInitWithObjectsForCount(nil); }
|
|
|
|
|
|
|
|
void arrayLiteral(id object) { NSArray* array = @[ object ]; }
|
|
|
|
|
|
|
|
void arrayLiteralOk() { arrayLiteral(@"obj"); }
|
|
|
|
|
|
|
|
void arrayLiteralNilBad() { arrayLiteral(nil); }
|
|
|
|
|
|
|
|
void arrayWithObject(id object) {
|
|
|
|
NSArray* array = [NSArray arrayWithObject:object];
|
|
|
|
}
|
|
|
|
|
|
|
|
void arrayWithObjectOK() { arrayWithObject(@"obj"); }
|
|
|
|
|
|
|
|
void arrayWithObjectNilBad() { arrayWithObject(nil); }
|
|
|
|
|
|
|
|
void arrayWithObjects(id object) {
|
|
|
|
NSString* values[1];
|
|
|
|
values[0] = object;
|
|
|
|
|
|
|
|
NSArray* array = [NSArray arrayWithObjects:values count:1];
|
|
|
|
}
|
|
|
|
|
|
|
|
void arrayWithObjectsOk() { arrayWithObjects(@"obj"); }
|
|
|
|
|
|
|
|
void FN_arrayWithObjectsNilBad() { arrayWithObjects(nil); }
|