// RUN: %clang_cc1 -fblocks -fsyntax-only -Wnullable-to-nonnull-conversion %s -verify // // Test the substitution of type arguments for type parameters when // using parameterized classes in Objective-C. @protocol NSObject @end __attribute__((objc_root_class)) @interface NSObject + (instancetype)alloc; - (instancetype)init; @end @protocol NSCopying @end @interface NSString : NSObject @end @interface NSMutableString : NSString @end @interface NSNumber : NSObject @end @interface NSArray : NSObject { @public T *data; // don't try this at home } - (T)objectAtIndexedSubscript:(int)index; + (NSArray *)array; + (void)setArray:(NSArray *)array; @property (copy,nonatomic) T lastObject; @end @interface NSMutableArray : NSArray -(instancetype)initWithArray:(NSArray *)array; // expected-note{{passing argument}} - (void)setObject:(T)object atIndexedSubscript:(int)index; // expected-note 2{{passing argument to parameter 'object' here}} @end @interface NSStringArray : NSArray @end @interface NSSet : NSObject - (T)firstObject; @property (nonatomic, copy) NSArray *allObjects; @end // Parameterized inheritance (simple case) @interface NSMutableSet> : NSSet - (void)addObject:(U)object; // expected-note 7{{passing argument to parameter 'object' here}} @end @interface Widget : NSObject @end // Non-parameterized class inheriting from a specialization of a // parameterized class. @interface WidgetSet : NSMutableSet @end // Parameterized inheritance with a more interesting transformation in // the specialization. @interface MutableSetOfArrays : NSMutableSet*> @end // Inheriting from an unspecialized form of a parameterized type. @interface UntypedMutableSet : NSMutableSet @end @interface Window : NSObject @end @interface NSDictionary : NSObject - (V)objectForKeyedSubscript:(K)key; // expected-note 2{{parameter 'key'}} @end @interface NSMutableDictionary, V> : NSDictionary - (void)setObject:(V)object forKeyedSubscript:(K)key; // expected-note@-1 {{parameter 'object' here}} // expected-note@-2 {{parameter 'object' here}} // expected-note@-3 {{parameter 'key' here}} // expected-note@-4 {{parameter 'key' here}} @property (strong) K someRandomKey; @end @interface WindowArray : NSArray @end @interface NSSet (Searching) - (T)findObject:(T)object; @end @interface NSView : NSObject @end @interface NSControl : NSView - (void)toggle; @end @interface NSViewController : NSObject @property (nonatomic,retain) ViewType view; @end @interface TypedefTypeParam : NSObject typedef T AliasT; - (void)test:(AliasT)object; // expected-note@-1 {{parameter 'object' here}} @end // -------------------------------------------------------------------------- // Nullability // -------------------------------------------------------------------------- typedef NSControl * _Nonnull Nonnull_NSControl; @interface NSNullableTest : NSObject - (ViewType)view; - (nullable ViewType)maybeView; @end @interface NSNullableTest2 : NSObject // expected-error{{type parameter 'ViewType' bound 'NSView * _Nullable' cannot explicitly specify nullability}} @end void test_nullability(void) { NSControl * _Nonnull nonnull_NSControl; // Nullability introduced by substitution. NSNullableTest *unspecifiedControl; nonnull_NSControl = [unspecifiedControl view]; nonnull_NSControl = [unspecifiedControl maybeView]; // expected-warning{{from nullable pointer 'NSControl * _Nullable' to non-nullable pointer type 'NSControl * _Nonnull'}} // Nullability overridden by substitution. NSNullableTest *nonnullControl; nonnull_NSControl = [nonnullControl view]; nonnull_NSControl = [nonnullControl maybeView]; // expected-warning{{from nullable pointer 'Nonnull_NSControl _Nullable' (aka 'NSControl *') to non-nullable pointer type 'NSControl * _Nonnull'}} // Nullability cannot be specified directly on a type argument. NSNullableTest *nonnullControl2; // expected-error{{type argument 'NSControl *' cannot explicitly specify nullability}} } // -------------------------------------------------------------------------- // Message sends. // -------------------------------------------------------------------------- void test_message_send_result( NSSet *stringSet, NSMutableSet *mutStringSet, WidgetSet *widgetSet, UntypedMutableSet *untypedMutSet, MutableSetOfArrays *mutStringArraySet, NSSet *set, NSMutableSet *mutSet, MutableSetOfArrays *mutArraySet, NSArray *stringArray, NSArray<__kindof NSString *> *kindofStringArray, void (^block)(void)) { int *ip; ip = [stringSet firstObject]; // expected-warning{{from 'NSString *'}} ip = [mutStringSet firstObject]; // expected-warning{{from 'NSString *'}} ip = [widgetSet firstObject]; // expected-warning{{from 'Widget *'}} ip = [untypedMutSet firstObject]; // expected-warning{{from 'id'}} ip = [mutStringArraySet firstObject]; // expected-warning{{from 'NSArray *'}} ip = [set firstObject]; // expected-warning{{from 'id'}} ip = [mutSet firstObject]; // expected-warning{{from 'id'}} ip = [mutArraySet firstObject]; // expected-warning{{from 'id'}} ip = [block firstObject]; // expected-warning{{from 'id'}} ip = [stringSet findObject:@"blah"]; // expected-warning{{from 'NSString *'}} // Class messages. ip = [NSSet alloc]; // expected-warning{{from 'NSSet *'}} ip = [NSSet alloc]; // expected-warning{{from 'NSSet *'}} ip = [MutableSetOfArrays alloc]; // expected-warning{{from 'MutableSetOfArrays *'}} ip = [MutableSetOfArrays alloc]; // expected-warning{{from 'MutableSetOfArrays *'}} ip = [NSArray array]; // expected-warning{{from 'NSArray *'}} ip = [NSArray array]; // expected-warning{{from 'NSArray *'}} ip = [[NSMutableArray alloc] init]; // expected-warning{{from 'NSMutableArray *'}} [[NSMutableArray alloc] initWithArray: stringArray]; // okay [[NSMutableArray alloc] initWithArray: stringArray]; // okay [[NSMutableArray alloc] initWithArray: stringArray]; // expected-warning{{sending 'NSArray *' to parameter of type 'NSArray *'}} ip = [[[NSViewController alloc] init] view]; // expected-warning{{from '__kindof NSView *'}} [[[[NSViewController alloc] init] view] toggle]; NSMutableString *mutStr = kindofStringArray[0]; NSNumber *number = kindofStringArray[0]; // expected-warning{{of type '__kindof NSString *'}} } void test_message_send_param( NSMutableSet *mutStringSet, WidgetSet *widgetSet, UntypedMutableSet *untypedMutSet, MutableSetOfArrays *mutStringArraySet, NSMutableSet *mutSet, MutableSetOfArrays *mutArraySet, TypedefTypeParam *typedefTypeParam, void (^block)(void)) { Window *window; [mutStringSet addObject: window]; // expected-warning{{parameter of type 'NSString *'}} [widgetSet addObject: window]; // expected-warning{{parameter of type 'Widget *'}} [untypedMutSet addObject: window]; // expected-warning{{parameter of incompatible type 'id'}} [mutStringArraySet addObject: window]; // expected-warning{{parameter of type 'NSArray *'}} [mutSet addObject: window]; // expected-warning{{parameter of incompatible type 'id'}} [mutArraySet addObject: window]; // expected-warning{{parameter of incompatible type 'id'}} [typedefTypeParam test: window]; // expected-warning{{parameter of type 'NSString *'}} [block addObject: window]; // expected-warning{{parameter of incompatible type 'id'}} } // -------------------------------------------------------------------------- // Property accesses. // -------------------------------------------------------------------------- void test_property_read( NSSet *stringSet, NSMutableSet *mutStringSet, WidgetSet *widgetSet, UntypedMutableSet *untypedMutSet, MutableSetOfArrays *mutStringArraySet, NSSet *set, NSMutableSet *mutSet, MutableSetOfArrays *mutArraySet, NSMutableDictionary *mutDict) { int *ip; ip = stringSet.allObjects; // expected-warning{{from 'NSArray *'}} ip = mutStringSet.allObjects; // expected-warning{{from 'NSArray *'}} ip = widgetSet.allObjects; // expected-warning{{from 'NSArray *'}} ip = untypedMutSet.allObjects; // expected-warning{{from 'NSArray *'}} ip = mutStringArraySet.allObjects; // expected-warning{{from 'NSArray *> *'}} ip = set.allObjects; // expected-warning{{from 'NSArray *'}} ip = mutSet.allObjects; // expected-warning{{from 'NSArray *'}} ip = mutArraySet.allObjects; // expected-warning{{from 'NSArray *'}} ip = mutDict.someRandomKey; // expected-warning{{from '__kindof id'}} ip = [[NSViewController alloc] init].view; // expected-warning{{from '__kindof NSView *'}} } void test_property_write( NSMutableSet *mutStringSet, WidgetSet *widgetSet, UntypedMutableSet *untypedMutSet, MutableSetOfArrays *mutStringArraySet, NSMutableSet *mutSet, MutableSetOfArrays *mutArraySet, NSMutableDictionary *mutDict) { int *ip; mutStringSet.allObjects = ip; // expected-warning{{to 'NSArray *'}} widgetSet.allObjects = ip; // expected-warning{{to 'NSArray *'}} untypedMutSet.allObjects = ip; // expected-warning{{to 'NSArray *'}} mutStringArraySet.allObjects = ip; // expected-warning{{to 'NSArray *> *'}} mutSet.allObjects = ip; // expected-warning{{to 'NSArray *'}} mutArraySet.allObjects = ip; // expected-warning{{to 'NSArray *'}} mutDict.someRandomKey = ip; // expected-warning{{to 'id'}} } // -------------------------------------------------------------------------- // Subscripting // -------------------------------------------------------------------------- void test_subscripting( NSArray *stringArray, NSMutableArray *mutStringArray, NSArray *array, NSMutableArray *mutArray, NSDictionary *stringWidgetDict, NSMutableDictionary *mutStringWidgetDict, NSDictionary *dict, NSMutableDictionary *mutDict) { int *ip; NSString *string; Widget *widget; Window *window; ip = stringArray[0]; // expected-warning{{from 'NSString *'}} ip = mutStringArray[0]; // expected-warning{{from 'NSString *'}} mutStringArray[0] = ip; // expected-warning{{parameter of type 'NSString *'}} ip = array[0]; // expected-warning{{from 'id'}} ip = mutArray[0]; // expected-warning{{from 'id'}} mutArray[0] = ip; // expected-warning{{parameter of type 'id'}} ip = stringWidgetDict[string]; // expected-warning{{from 'Widget *'}} widget = stringWidgetDict[widget]; // expected-warning{{to parameter of type 'NSString *'}} ip = mutStringWidgetDict[string]; // expected-warning{{from 'Widget *'}} widget = mutStringWidgetDict[widget]; // expected-warning{{to parameter of type 'NSString *'}} mutStringWidgetDict[string] = ip; // expected-warning{{to parameter of type 'Widget *'}} mutStringWidgetDict[widget] = widget; // expected-warning{{to parameter of type 'NSString *'}} ip = dict[string]; // expected-warning{{from 'id'}} ip = mutDict[string]; // expected-warning{{from 'id'}} mutDict[string] = ip; // expected-warning{{to parameter of type 'id'}} widget = mutDict[window]; mutDict[window] = widget; // expected-warning{{parameter of incompatible type 'id'}} } // -------------------------------------------------------------------------- // Instance variable access. // -------------------------------------------------------------------------- void test_instance_variable(NSArray *stringArray, NSArray *array) { int *ip; ip = stringArray->data; // expected-warning{{from 'NSString **'}} ip = array->data; // expected-warning{{from 'id *'}} } @implementation WindowArray - (void)testInstanceVariable { int *ip; ip = data; // expected-warning{{from 'Window **'}} } @end // -------------------------------------------------------------------------- // Implicit conversions. // -------------------------------------------------------------------------- void test_implicit_conversions(NSArray *stringArray, NSArray *numberArray, NSMutableArray *mutStringArray, NSArray *array, NSMutableArray *mutArray) { // Specialized -> unspecialized (same level) array = stringArray; // Unspecialized -> specialized (same level) stringArray = array; // Specialized -> specialized failure (same level). stringArray = numberArray; // expected-warning{{incompatible pointer types assigning to 'NSArray *' from 'NSArray *'}} // Specialized -> specialized (different levels). stringArray = mutStringArray; // Specialized -> specialized failure (different levels). numberArray = mutStringArray; // expected-warning{{incompatible pointer types assigning to 'NSArray *' from 'NSMutableArray *'}} // Unspecialized -> specialized (different levels). stringArray = mutArray; // Specialized -> unspecialized (different levels). array = mutStringArray; } @interface NSCovariant1<__covariant T> @end @interface NSContravariant1<__contravariant T> @end void test_variance(NSCovariant1 *covariant1, NSCovariant1 *covariant2, NSCovariant1 *covariant3, NSCovariant1 *covariant4, NSCovariant1 *covariant5, NSCovariant1> *covariant6, NSContravariant1 *contravariant1, NSContravariant1 *contravariant2) { covariant1 = covariant2; // okay covariant2 = covariant1; // expected-warning{{incompatible pointer types assigning to 'NSCovariant1 *' from 'NSCovariant1 *'}} covariant3 = covariant4; // okay covariant4 = covariant3; // expected-warning{{incompatible pointer types assigning to 'NSCovariant1 *' from 'NSCovariant1 *'}} covariant5 = covariant1; // okay covariant1 = covariant5; // okay: id is promiscuous covariant5 = covariant3; // okay covariant3 = covariant5; // okay contravariant1 = contravariant2; // expected-warning{{incompatible pointer types assigning to 'NSContravariant1 *' from 'NSContravariant1 *'}} contravariant2 = contravariant1; // okay } // -------------------------------------------------------------------------- // Ternary operator // -------------------------------------------------------------------------- void test_ternary_operator(NSArray *stringArray, NSArray *numberArray, NSMutableArray *mutStringArray, NSStringArray *stringArray2, NSArray *array, NSMutableArray *mutArray, int cond) { int *ip; id object; ip = cond ? stringArray : mutStringArray; // expected-warning{{from 'NSArray *'}} ip = cond ? mutStringArray : stringArray; // expected-warning{{from 'NSArray *'}} ip = cond ? stringArray2 : mutStringArray; // expected-warning{{from 'NSArray *'}} ip = cond ? mutStringArray : stringArray2; // expected-warning{{from 'NSArray *'}} ip = cond ? stringArray : mutArray; // expected-warning{{from 'NSArray *'}} ip = cond ? stringArray2 : mutArray; // expected-warning{{from 'NSArray *'}} ip = cond ? mutArray : stringArray; // expected-warning{{from 'NSArray *'}} ip = cond ? mutArray : stringArray2; // expected-warning{{from 'NSArray *'}} object = cond ? stringArray : numberArray; // expected-warning{{incompatible operand types ('NSArray *' and 'NSArray *')}} } // -------------------------------------------------------------------------- // super // -------------------------------------------------------------------------- @implementation NSStringArray - (void)useSuperMethod { int *ip; ip = super.lastObject; // expected-warning{{from 'NSString *'}} super.lastObject = ip; // expected-warning{{to 'NSString *'}} ip = [super objectAtIndexedSubscript:0]; // expected-warning{{from 'NSString *'}} } + (void)useSuperMethod { int *ip; ip = super.array; // expected-warning{{from 'NSArray *'}} super.array = ip; // expected-warning{{to 'NSArray *'}} ip = [super array]; // expected-warning{{from 'NSArray *'}} } @end // -------------------------------------------------------------------------- // warning about likely protocol/class name typos. // -------------------------------------------------------------------------- typedef NSArray ArrayOfNSObjectWarning; // expected-warning{{parameterized class 'NSArray' already conforms to the protocols listed; did you forget a '*'?}} // rdar://25060179 @interface MyMutableDictionary : NSObject - (void)setObject:(ObjectType)obj forKeyedSubscript:(KeyType )key; // expected-note{{passing argument to parameter 'obj' here}} \ // expected-note{{passing argument to parameter 'key' here}} @end void bar(MyMutableDictionary *stringsByString, NSNumber *n1, NSNumber *n2) { // We warn here when the key types do not match. stringsByString[n1] = n2; // expected-warning{{incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString *'}} \ // expected-warning{{incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString *'}} } @interface MyTest : NSObject - (V)test:(K)key; - (V)test2:(K)key; // expected-note{{previous definition is here}} - (void)mapUsingBlock:(id (^)(V))block; - (void)mapUsingBlock2:(id (^)(V))block; // expected-note{{previous definition is here}} @end @implementation MyTest - (id)test:(id)key { return key; } - (int)test2:(id)key{ // expected-warning{{conflicting return type in implementation}} return 0; } - (void)mapUsingBlock:(id (^)(id))block { } - (void)mapUsingBlock2:(id)block { // expected-warning{{conflicting parameter types in implementation}} } @end // -------------------------------------------------------------------------- // Use a type parameter as a type argument. // -------------------------------------------------------------------------- // Type bounds in a category/extension are omitted. rdar://problem/54329242 @interface ParameterizedContainer> - (ParameterizedContainer *)inInterface; @end @interface ParameterizedContainer (Cat) - (ParameterizedContainer *)inCategory; @end @interface ParameterizedContainer () - (ParameterizedContainer *)inExtension; @end