|
|
|
/*
|
|
|
|
* Copyright (c) 2017-present, Facebook, Inc.
|
|
|
|
*
|
|
|
|
* 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>
|
|
|
|
#import "Library.h"
|
|
|
|
|
|
|
|
int* __nullable returnsNull();
|
|
|
|
|
|
|
|
typedef struct s_ {
|
|
|
|
int x;
|
|
|
|
} S;
|
|
|
|
|
|
|
|
@interface T : NSObject
|
|
|
|
- (NSObject* _Nullable)nullableMethod;
|
|
|
|
- (T* _Nullable)nullableT;
|
|
|
|
@property(nonatomic) S structProperty;
|
|
|
|
@property(nonatomic) S* pointerProperty;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation T {
|
|
|
|
int* unnanotatedField;
|
|
|
|
int* __nullable nullableField;
|
|
|
|
int* nonnullField;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)assignNullableFieldToNullOkay {
|
|
|
|
nullableField = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)assignUnnanotatedFieldToNullBad {
|
|
|
|
unnanotatedField = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)assignNonnullFieldToNullBad {
|
|
|
|
nonnullField = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testNullableFieldForNullOkay {
|
|
|
|
if (nullableField == nil) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testUnnanotatedFieldForNullBad {
|
|
|
|
if (unnanotatedField == nil) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (int)DeadStoreFP_testUnnanotatedFieldInClosureBad {
|
|
|
|
int (^testField)(int defaultValue);
|
|
|
|
testField = ^(int defaultValue) {
|
|
|
|
if (unnanotatedField != nil) {
|
|
|
|
return *unnanotatedField;
|
|
|
|
} else {
|
|
|
|
return defaultValue;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return testField(42);
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testNonnullFieldForNullBad {
|
|
|
|
if (nonnullField == nil) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)dereferenceUnnanotatedFieldOkay {
|
|
|
|
*unnanotatedField = 42;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)dereferenceNonnullFieldOkay {
|
|
|
|
*nonnullField = 42;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)dereferenceNullableFieldBad {
|
|
|
|
*nullableField = 42;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)dereferenceUnnanotatedFieldAfterTestForNullBad {
|
|
|
|
if (unnanotatedField == nil) {
|
|
|
|
*unnanotatedField = 42;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)FP_dereferenceNonnullFieldAfterTestForNullOkay {
|
|
|
|
if (nonnullField == nil) {
|
|
|
|
*nonnullField = 42;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)dereferenceNullableFunctionBad {
|
|
|
|
int* p = returnsNull();
|
|
|
|
*p = 42;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)dereferenceNullableFunction1Ok {
|
|
|
|
int* p = returnsNull();
|
|
|
|
if (p) {
|
|
|
|
*p = 42;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)dereferenceNullableFunction2Ok {
|
|
|
|
int* p = returnsNull();
|
|
|
|
if (p != nil) {
|
|
|
|
*p = 42;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSObject* _Nullable)nullableMethod {
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*)dereferenceNullableMethodOkay {
|
|
|
|
NSObject* nullableObject = [self nullableMethod];
|
|
|
|
return [nullableObject description]; // does not report here
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)reassigningNullableObjectOkay {
|
|
|
|
NSObject* nullableObject = [self nullableMethod];
|
|
|
|
nullableObject = nil; // does not report here
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray*)nullableObjectInNSArrayBad {
|
|
|
|
NSObject* nullableObject = [self nullableMethod];
|
|
|
|
NSArray* array = @[ nullableObject ]; // reports here
|
|
|
|
return array;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray*)nullableMethodCheckedForNullAndReturnOkay {
|
|
|
|
NSObject* nullableObject = [self nullableMethod];
|
|
|
|
NSArray* array;
|
|
|
|
if (nullableObject == nil) {
|
|
|
|
return array;
|
|
|
|
}
|
|
|
|
array = @[ nullableObject ]; // does not report here
|
|
|
|
return array;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray*)secondElementNullableObjectInNSArrayBad {
|
|
|
|
NSObject* allocatedObject = [NSObject alloc];
|
|
|
|
NSObject* nullableObject = [self nullableMethod];
|
|
|
|
NSArray* array = @[ allocatedObject, nullableObject ]; // reports here
|
|
|
|
return array;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray*)nullableObjectInNSArrayOkay {
|
|
|
|
NSObject* nullableObject = [self nullableMethod];
|
|
|
|
NSArray* array;
|
|
|
|
if (nullableObject) {
|
|
|
|
array = @[ nullableObject ]; // reports here
|
|
|
|
} else {
|
|
|
|
array = @[ @"String" ];
|
|
|
|
}
|
|
|
|
return array;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray*)URLWithStringOkay {
|
|
|
|
NSURL* url = [NSURL URLWithString:@"some/url/string"];
|
|
|
|
NSArray* array = @[ url ]; // reports here
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSDictionary*)nullableValueInNSDictionaryBad {
|
|
|
|
NSObject* nullableValue = [self nullableMethod];
|
|
|
|
NSMutableDictionary* dict = [NSMutableDictionary
|
|
|
|
dictionaryWithObjectsAndKeys:@"key", nullableValue, nil]; // reports here
|
|
|
|
return dict;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSDictionary*)nullableKeyInNSDictionaryBad {
|
|
|
|
NSObject* nullableKey = [self nullableMethod];
|
|
|
|
NSMutableDictionary* dict = [NSMutableDictionary
|
|
|
|
dictionaryWithObjectsAndKeys:nullableKey, @"value", nil]; // reports here
|
|
|
|
return dict;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSDictionary*)nullableKeyInNSDictionaryInitBad {
|
|
|
|
NSObject* nullableKey = [self nullableMethod];
|
|
|
|
NSDictionary* dict = [[NSDictionary alloc]
|
|
|
|
initWithObjectsAndKeys:nullableKey, @"value", nil]; // reports here
|
|
|
|
return dict;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSDictionary*)nullableValueInNSDictionaryInitBad {
|
|
|
|
NSObject* nullableValue = [self nullableMethod];
|
|
|
|
NSDictionary* dict = [[NSDictionary alloc]
|
|
|
|
initWithObjectsAndKeys:@"key", nullableValue, nil]; // reports here
|
|
|
|
return dict;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSDictionary*)nullableKeyInNSDictionaryInitLiteralBad {
|
|
|
|
NSObject* nullableKey = [self nullableMethod];
|
|
|
|
NSDictionary* dict = @{nullableKey : @"value"}; // reports here
|
|
|
|
return dict;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSDictionary*)nullableValueInNSDictionaryInitLiteralBad {
|
|
|
|
NSObject* nullableValue = [self nullableMethod];
|
|
|
|
NSDictionary* dict = @{@"key" : nullableValue}; // reports here
|
|
|
|
return dict;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSDictionary*)indirectNullableKeyInNSDictionaryBad {
|
|
|
|
NSObject* nullableKey = [self nullableMethod];
|
|
|
|
NSString* nullableKeyString = [nullableKey description];
|
|
|
|
NSDictionary* dict = [[NSDictionary alloc]
|
|
|
|
initWithObjectsAndKeys:nullableKeyString, @"value", nil]; // reports here
|
|
|
|
return dict;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray*)createArrayByAddingNilBad {
|
|
|
|
NSArray* array = @[ [NSObject alloc] ];
|
|
|
|
return [array arrayByAddingObject:[self nullableMethod]];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSDictionary*)setNullableObjectInDictionaryBad {
|
|
|
|
NSMutableDictionary* mutableDict = [NSMutableDictionary dictionary];
|
|
|
|
[mutableDict setObject:[self nullableMethod] forKey:@"key"]; // reports here
|
|
|
|
return mutableDict;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray*)addNullableObjectInMutableArrayBad {
|
|
|
|
NSMutableArray* mutableArray = [[NSMutableArray alloc] init];
|
|
|
|
[mutableArray addObject:[self nullableMethod]]; // reports here
|
|
|
|
return mutableArray;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray*)insertNullableObjectInMutableArrayBad {
|
|
|
|
NSMutableArray* mutableArray = [[NSMutableArray alloc] init];
|
|
|
|
[mutableArray insertObject:[self nullableMethod] atIndex:0]; // reports here
|
|
|
|
return mutableArray;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray*)propagateNullabilityOnMethodCallBad {
|
|
|
|
NSObject* nullableObject = [self nullableMethod];
|
|
|
|
NSString* nullableString =
|
|
|
|
[nullableObject description]; // returns nil if nullableObject is nil
|
|
|
|
return @[ nullableString ]; // reports here
|
|
|
|
}
|
|
|
|
|
|
|
|
- (S*)shouldPropagateNullabilityOnPointerTypeBad {
|
|
|
|
T* nullableT = [self nullableT];
|
|
|
|
S* s = nullableT.pointerProperty; // returns nil when nullableT is nil
|
|
|
|
s->x = 42; // reports here
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (S)shouldNotPropagateNullabilityOnNonPointerTypeGood {
|
|
|
|
T* nullableT = [self nullableT];
|
|
|
|
S s =
|
|
|
|
nullableT.structProperty; // returns an empty struct when nullabeT is nil
|
|
|
|
s.x = 42; // does not report here
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray*)pointerAssignmentWithSubtypeOkay:(NSString*)string {
|
|
|
|
NSObject* nullableObject = [self nullableMethod];
|
|
|
|
nullableObject = string;
|
|
|
|
NSArray* array = @[ nullableObject ]; // does not report here
|
|
|
|
return array;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray*)dereferenceLibraryMethodOk:(L*)object {
|
|
|
|
NSObject* nullableObject = [object libraryMethod];
|
|
|
|
NSArray* array = @[ nullableObject ]; // does not report here
|
|
|
|
return array;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray*)dereferenceNullableLibraryMethodBad:(L*)object {
|
|
|
|
NSObject* nullableObject = [object nullableLibraryMethod];
|
|
|
|
NSArray* array = @[ nullableObject ]; // reports here
|
|
|
|
return array;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@protocol P
|
|
|
|
- (NSObject* _Nullable)nullableMethod;
|
|
|
|
@end
|
|
|
|
|
|
|
|
NSDictionary* callNullableMethodFromProtocolBad(id<P> pObject) {
|
|
|
|
NSObject* nullableObject = [pObject nullableMethod];
|
|
|
|
return @{@"key" : nullableObject};
|
|
|
|
}
|