/* * 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 #include @interface SomeObject : NSObject @property int x; @property std::shared_ptr ptr; - (int)returnsPOD; - (std::shared_ptr)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)returnsnonPOD { return std::shared_ptr(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; } + (instancetype)sharedInstance { static SomeObject* shared; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ auto obj = [SomeObject unknown]; if (obj) { shared = [SomeObject new]; } }); return shared; } @end int dereferenceNilBad() { int* int_ptr = nil; return *int_ptr; } int testCallMethodReturnsPODOk() { SomeObject* obj = nil; return [obj returnsPOD]; } std::shared_ptr testCallMethodReturnsnonPODBad() { SomeObject* obj = nil; std::shared_ptr d = [obj returnsnonPOD]; // UB return d; } std::shared_ptr testSkippedOK() { SomeObject* obj = [[SomeObject unknown] get]; std::shared_ptr result = [obj returnsnonPOD]; return result; } std::shared_ptr testNonPODTraceBad() { SomeObject* obj = [[SomeObject returnsNil] get]; std::shared_ptr result = [obj returnsnonPOD]; return result; } std::shared_ptr testCallMethodReturnsnonPODLatent(bool b) { SomeObject* obj; if (b) { obj = nil; } else { obj = [SomeObject new]; } std::shared_ptr d = [obj returnsnonPOD]; // UB if obj nil return d; } std::shared_ptr testCallMethodReturnsnonPODLatentBad(bool b) { return testCallMethodReturnsnonPODLatent(true); } std::shared_ptr testCallMethodReturnsnonPODLatentOk(bool b) { return testCallMethodReturnsnonPODLatent(false); } int testAccessPropertyAccessorOk() { SomeObject* obj = nil; return obj.x; // calls property accessor method } std::shared_ptr 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* mDict, NSString* key) { mDict[key] = @"default"; } void accessZeroElementOk(NSMutableDictionary* mDict) { NSArray* 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); } std::shared_ptr unknown_call_twice_FP() { if (![SomeObject sharedInstance]) { return std::make_shared(0); } return [[SomeObject sharedInstance] returnsnonPOD]; }