1 | // RUN: %clang_cc1 -fblocks -fsyntax-only -Wnullable-to-nonnull-conversion %s -verify |
2 | // |
3 | // Test the substitution of type arguments for type parameters when |
4 | // using parameterized classes in Objective-C. |
5 | |
6 | @protocol NSObject |
7 | @end |
8 | |
9 | __attribute__((objc_root_class)) |
10 | @interface NSObject <NSObject> |
11 | + (instancetype)alloc; |
12 | - (instancetype)init; |
13 | @end |
14 | |
15 | @protocol NSCopying |
16 | @end |
17 | |
18 | @interface NSString : NSObject <NSCopying> |
19 | @end |
20 | |
21 | @interface NSMutableString : NSString |
22 | @end |
23 | |
24 | @interface NSNumber : NSObject <NSCopying> |
25 | @end |
26 | |
27 | @interface NSArray<T> : NSObject <NSCopying> { |
28 | @public |
29 | T *data; // don't try this at home |
30 | } |
31 | - (T)objectAtIndexedSubscript:(int)index; |
32 | + (NSArray<T> *)array; |
33 | + (void)setArray:(NSArray <T> *)array; |
34 | @property (copy,nonatomic) T lastObject; |
35 | @end |
36 | |
37 | @interface NSMutableArray<T> : NSArray<T> |
38 | -(instancetype)initWithArray:(NSArray<T> *)array; // expected-note{{passing argument}} |
39 | - (void)setObject:(T)object atIndexedSubscript:(int)index; // expected-note 2{{passing argument to parameter 'object' here}} |
40 | @end |
41 | |
42 | @interface NSStringArray : NSArray<NSString *> |
43 | @end |
44 | |
45 | @interface NSSet<T> : NSObject <NSCopying> |
46 | - (T)firstObject; |
47 | @property (nonatomic, copy) NSArray<T> *allObjects; |
48 | @end |
49 | |
50 | // Parameterized inheritance (simple case) |
51 | @interface NSMutableSet<U : id<NSCopying>> : NSSet<U> |
52 | - (void)addObject:(U)object; // expected-note 7{{passing argument to parameter 'object' here}} |
53 | @end |
54 | |
55 | @interface Widget : NSObject <NSCopying> |
56 | @end |
57 | |
58 | // Non-parameterized class inheriting from a specialization of a |
59 | // parameterized class. |
60 | @interface WidgetSet : NSMutableSet<Widget *> |
61 | @end |
62 | |
63 | // Parameterized inheritance with a more interesting transformation in |
64 | // the specialization. |
65 | @interface MutableSetOfArrays<T> : NSMutableSet<NSArray<T>*> |
66 | @end |
67 | |
68 | // Inheriting from an unspecialized form of a parameterized type. |
69 | @interface UntypedMutableSet : NSMutableSet |
70 | @end |
71 | |
72 | @interface Window : NSObject |
73 | @end |
74 | |
75 | @interface NSDictionary<K, V> : NSObject <NSCopying> |
76 | - (V)objectForKeyedSubscript:(K)key; // expected-note 2{{parameter 'key'}} |
77 | @end |
78 | |
79 | @interface NSMutableDictionary<K : id<NSCopying>, V> : NSDictionary<K, V> |
80 | - (void)setObject:(V)object forKeyedSubscript:(K)key; |
81 | // expected-note@-1 {{parameter 'object' here}} |
82 | // expected-note@-2 {{parameter 'object' here}} |
83 | // expected-note@-3 {{parameter 'key' here}} |
84 | // expected-note@-4 {{parameter 'key' here}} |
85 | |
86 | @property (strong) K someRandomKey; |
87 | @end |
88 | |
89 | @interface WindowArray : NSArray<Window *> |
90 | @end |
91 | |
92 | @interface NSSet<T> (Searching) |
93 | - (T)findObject:(T)object; |
94 | @end |
95 | |
96 | @interface NSView : NSObject |
97 | @end |
98 | |
99 | @interface NSControl : NSView |
100 | - (void)toggle; |
101 | @end |
102 | |
103 | @interface NSViewController<ViewType : NSView *> : NSObject |
104 | @property (nonatomic,retain) ViewType view; |
105 | @end |
106 | |
107 | @interface TypedefTypeParam<T> : NSObject |
108 | typedef T AliasT; |
109 | - (void)test:(AliasT)object; |
110 | // expected-note@-1 {{parameter 'object' here}} |
111 | @end |
112 | |
113 | // -------------------------------------------------------------------------- |
114 | // Nullability |
115 | // -------------------------------------------------------------------------- |
116 | typedef NSControl * _Nonnull Nonnull_NSControl; |
117 | |
118 | @interface NSNullableTest<ViewType : NSView *> : NSObject |
119 | - (ViewType)view; |
120 | - (nullable ViewType)maybeView; |
121 | @end |
122 | |
123 | @interface NSNullableTest2<ViewType : NSView * _Nullable> : NSObject // expected-error{{type parameter 'ViewType' bound 'NSView * _Nullable' cannot explicitly specify nullability}} |
124 | @end |
125 | |
126 | void test_nullability(void) { |
127 | NSControl * _Nonnull nonnull_NSControl; |
128 | |
129 | // Nullability introduced by substitution. |
130 | NSNullableTest<NSControl *> *unspecifiedControl; |
131 | nonnull_NSControl = [unspecifiedControl view]; |
132 | nonnull_NSControl = [unspecifiedControl maybeView]; // expected-warning{{from nullable pointer 'NSControl * _Nullable' to non-nullable pointer type 'NSControl * _Nonnull'}} |
133 | |
134 | // Nullability overridden by substitution. |
135 | NSNullableTest<Nonnull_NSControl> *nonnullControl; |
136 | nonnull_NSControl = [nonnullControl view]; |
137 | nonnull_NSControl = [nonnullControl maybeView]; // expected-warning{{from nullable pointer 'Nonnull_NSControl _Nullable' (aka 'NSControl *') to non-nullable pointer type 'NSControl * _Nonnull'}} |
138 | |
139 | // Nullability cannot be specified directly on a type argument. |
140 | NSNullableTest<NSControl * _Nonnull> *nonnullControl2; // expected-error{{type argument 'NSControl *' cannot explicitly specify nullability}} |
141 | } |
142 | |
143 | // -------------------------------------------------------------------------- |
144 | // Message sends. |
145 | // -------------------------------------------------------------------------- |
146 | void test_message_send_result( |
147 | NSSet<NSString *> *stringSet, |
148 | NSMutableSet<NSString *> *mutStringSet, |
149 | WidgetSet *widgetSet, |
150 | UntypedMutableSet *untypedMutSet, |
151 | MutableSetOfArrays<NSString *> *mutStringArraySet, |
152 | NSSet *set, |
153 | NSMutableSet *mutSet, |
154 | MutableSetOfArrays *mutArraySet, |
155 | NSArray<NSString *> *stringArray, |
156 | NSArray<__kindof NSString *> *kindofStringArray, |
157 | void (^block)(void)) { |
158 | int *ip; |
159 | ip = [stringSet firstObject]; // expected-warning{{from 'NSString *'}} |
160 | ip = [mutStringSet firstObject]; // expected-warning{{from 'NSString *'}} |
161 | ip = [widgetSet firstObject]; // expected-warning{{from 'Widget *'}} |
162 | ip = [untypedMutSet firstObject]; // expected-warning{{from 'id'}} |
163 | ip = [mutStringArraySet firstObject]; // expected-warning{{from 'NSArray<NSString *> *'}} |
164 | ip = [set firstObject]; // expected-warning{{from 'id'}} |
165 | ip = [mutSet firstObject]; // expected-warning{{from 'id'}} |
166 | ip = [mutArraySet firstObject]; // expected-warning{{from 'id'}} |
167 | ip = [block firstObject]; // expected-warning{{from 'id'}} |
168 | |
169 | ip = [stringSet findObject:@"blah"]; // expected-warning{{from 'NSString *'}} |
170 | |
171 | // Class messages. |
172 | ip = [NSSet<NSString *> alloc]; // expected-warning{{from 'NSSet<NSString *> *'}} |
173 | ip = [NSSet alloc]; // expected-warning{{from 'NSSet *'}} |
174 | ip = [MutableSetOfArrays<NSString *> alloc]; // expected-warning{{from 'MutableSetOfArrays<NSString *> *'}} |
175 | ip = [MutableSetOfArrays alloc]; // expected-warning{{from 'MutableSetOfArrays *'}} |
176 | ip = [NSArray<NSString *> array]; // expected-warning{{from 'NSArray<NSString *> *'}} |
177 | ip = [NSArray<NSString *><NSCopying> array]; // expected-warning{{from 'NSArray<NSString *> *'}} |
178 | |
179 | ip = [[NSMutableArray<NSString *> alloc] init]; // expected-warning{{from 'NSMutableArray<NSString *> *'}} |
180 | |
181 | [[NSMutableArray alloc] initWithArray: stringArray]; // okay |
182 | [[NSMutableArray<NSString *> alloc] initWithArray: stringArray]; // okay |
183 | [[NSMutableArray<NSNumber *> alloc] initWithArray: stringArray]; // expected-warning{{sending 'NSArray<NSString *> *' to parameter of type 'NSArray<NSNumber *> *'}} |
184 | |
185 | ip = [[[NSViewController alloc] init] view]; // expected-warning{{from '__kindof NSView *'}} |
186 | [[[[NSViewController alloc] init] view] toggle]; |
187 | |
188 | NSMutableString *mutStr = kindofStringArray[0]; |
189 | NSNumber *number = kindofStringArray[0]; // expected-warning{{of type '__kindof NSString *'}} |
190 | } |
191 | |
192 | void test_message_send_param( |
193 | NSMutableSet<NSString *> *mutStringSet, |
194 | WidgetSet *widgetSet, |
195 | UntypedMutableSet *untypedMutSet, |
196 | MutableSetOfArrays<NSString *> *mutStringArraySet, |
197 | NSMutableSet *mutSet, |
198 | MutableSetOfArrays *mutArraySet, |
199 | TypedefTypeParam<NSString *> *typedefTypeParam, |
200 | void (^block)(void)) { |
201 | Window *window; |
202 | |
203 | [mutStringSet addObject: window]; // expected-warning{{parameter of type 'NSString *'}} |
204 | [widgetSet addObject: window]; // expected-warning{{parameter of type 'Widget *'}} |
205 | [untypedMutSet addObject: window]; // expected-warning{{parameter of incompatible type 'id<NSCopying>'}} |
206 | [mutStringArraySet addObject: window]; // expected-warning{{parameter of type 'NSArray<NSString *> *'}} |
207 | [mutSet addObject: window]; // expected-warning{{parameter of incompatible type 'id<NSCopying>'}} |
208 | [mutArraySet addObject: window]; // expected-warning{{parameter of incompatible type 'id<NSCopying>'}} |
209 | [typedefTypeParam test: window]; // expected-warning{{parameter of type 'NSString *'}} |
210 | [block addObject: window]; // expected-warning{{parameter of incompatible type 'id<NSCopying>'}} |
211 | } |
212 | |
213 | // -------------------------------------------------------------------------- |
214 | // Property accesses. |
215 | // -------------------------------------------------------------------------- |
216 | void test_property_read( |
217 | NSSet<NSString *> *stringSet, |
218 | NSMutableSet<NSString *> *mutStringSet, |
219 | WidgetSet *widgetSet, |
220 | UntypedMutableSet *untypedMutSet, |
221 | MutableSetOfArrays<NSString *> *mutStringArraySet, |
222 | NSSet *set, |
223 | NSMutableSet *mutSet, |
224 | MutableSetOfArrays *mutArraySet, |
225 | NSMutableDictionary *mutDict) { |
226 | int *ip; |
227 | ip = stringSet.allObjects; // expected-warning{{from 'NSArray<NSString *> *'}} |
228 | ip = mutStringSet.allObjects; // expected-warning{{from 'NSArray<NSString *> *'}} |
229 | ip = widgetSet.allObjects; // expected-warning{{from 'NSArray<Widget *> *'}} |
230 | ip = untypedMutSet.allObjects; // expected-warning{{from 'NSArray *'}} |
231 | ip = mutStringArraySet.allObjects; // expected-warning{{from 'NSArray<NSArray<NSString *> *> *'}} |
232 | ip = set.allObjects; // expected-warning{{from 'NSArray *'}} |
233 | ip = mutSet.allObjects; // expected-warning{{from 'NSArray *'}} |
234 | ip = mutArraySet.allObjects; // expected-warning{{from 'NSArray *'}} |
235 | |
236 | ip = mutDict.someRandomKey; // expected-warning{{from '__kindof id<NSCopying>'}} |
237 | |
238 | ip = [[NSViewController alloc] init].view; // expected-warning{{from '__kindof NSView *'}} |
239 | } |
240 | |
241 | void test_property_write( |
242 | NSMutableSet<NSString *> *mutStringSet, |
243 | WidgetSet *widgetSet, |
244 | UntypedMutableSet *untypedMutSet, |
245 | MutableSetOfArrays<NSString *> *mutStringArraySet, |
246 | NSMutableSet *mutSet, |
247 | MutableSetOfArrays *mutArraySet, |
248 | NSMutableDictionary *mutDict) { |
249 | int *ip; |
250 | |
251 | mutStringSet.allObjects = ip; // expected-warning{{to 'NSArray<NSString *> *'}} |
252 | widgetSet.allObjects = ip; // expected-warning{{to 'NSArray<Widget *> *'}} |
253 | untypedMutSet.allObjects = ip; // expected-warning{{to 'NSArray *'}} |
254 | mutStringArraySet.allObjects = ip; // expected-warning{{to 'NSArray<NSArray<NSString *> *> *'}} |
255 | mutSet.allObjects = ip; // expected-warning{{to 'NSArray *'}} |
256 | mutArraySet.allObjects = ip; // expected-warning{{to 'NSArray *'}} |
257 | |
258 | mutDict.someRandomKey = ip; // expected-warning{{to 'id<NSCopying>'}} |
259 | } |
260 | |
261 | // -------------------------------------------------------------------------- |
262 | // Subscripting |
263 | // -------------------------------------------------------------------------- |
264 | void test_subscripting( |
265 | NSArray<NSString *> *stringArray, |
266 | NSMutableArray<NSString *> *mutStringArray, |
267 | NSArray *array, |
268 | NSMutableArray *mutArray, |
269 | NSDictionary<NSString *, Widget *> *stringWidgetDict, |
270 | NSMutableDictionary<NSString *, Widget *> *mutStringWidgetDict, |
271 | NSDictionary *dict, |
272 | NSMutableDictionary *mutDict) { |
273 | int *ip; |
274 | NSString *string; |
275 | Widget *widget; |
276 | Window *window; |
277 | |
278 | ip = stringArray[0]; // expected-warning{{from 'NSString *'}} |
279 | |
280 | ip = mutStringArray[0]; // expected-warning{{from 'NSString *'}} |
281 | mutStringArray[0] = ip; // expected-warning{{parameter of type 'NSString *'}} |
282 | |
283 | ip = array[0]; // expected-warning{{from 'id'}} |
284 | |
285 | ip = mutArray[0]; // expected-warning{{from 'id'}} |
286 | mutArray[0] = ip; // expected-warning{{parameter of type 'id'}} |
287 | |
288 | ip = stringWidgetDict[string]; // expected-warning{{from 'Widget *'}} |
289 | widget = stringWidgetDict[widget]; // expected-warning{{to parameter of type 'NSString *'}} |
290 | |
291 | ip = mutStringWidgetDict[string]; // expected-warning{{from 'Widget *'}} |
292 | widget = mutStringWidgetDict[widget]; // expected-warning{{to parameter of type 'NSString *'}} |
293 | mutStringWidgetDict[string] = ip; // expected-warning{{to parameter of type 'Widget *'}} |
294 | mutStringWidgetDict[widget] = widget; // expected-warning{{to parameter of type 'NSString *'}} |
295 | |
296 | ip = dict[string]; // expected-warning{{from 'id'}} |
297 | |
298 | ip = mutDict[string]; // expected-warning{{from 'id'}} |
299 | mutDict[string] = ip; // expected-warning{{to parameter of type 'id'}} |
300 | |
301 | widget = mutDict[window]; |
302 | mutDict[window] = widget; // expected-warning{{parameter of incompatible type 'id<NSCopying>'}} |
303 | } |
304 | |
305 | // -------------------------------------------------------------------------- |
306 | // Instance variable access. |
307 | // -------------------------------------------------------------------------- |
308 | void test_instance_variable(NSArray<NSString *> *stringArray, |
309 | NSArray *array) { |
310 | int *ip; |
311 | |
312 | ip = stringArray->data; // expected-warning{{from 'NSString **'}} |
313 | ip = array->data; // expected-warning{{from 'id *'}} |
314 | } |
315 | |
316 | @implementation WindowArray |
317 | - (void)testInstanceVariable { |
318 | int *ip; |
319 | |
320 | ip = data; // expected-warning{{from 'Window **'}} |
321 | } |
322 | @end |
323 | |
324 | // -------------------------------------------------------------------------- |
325 | // Implicit conversions. |
326 | // -------------------------------------------------------------------------- |
327 | void test_implicit_conversions(NSArray<NSString *> *stringArray, |
328 | NSArray<NSNumber *> *numberArray, |
329 | NSMutableArray<NSString *> *mutStringArray, |
330 | NSArray *array, |
331 | NSMutableArray *mutArray) { |
332 | // Specialized -> unspecialized (same level) |
333 | array = stringArray; |
334 | |
335 | // Unspecialized -> specialized (same level) |
336 | stringArray = array; |
337 | |
338 | // Specialized -> specialized failure (same level). |
339 | stringArray = numberArray; // expected-warning{{incompatible pointer types assigning to 'NSArray<NSString *> *' from 'NSArray<NSNumber *> *'}} |
340 | |
341 | // Specialized -> specialized (different levels). |
342 | stringArray = mutStringArray; |
343 | |
344 | // Specialized -> specialized failure (different levels). |
345 | numberArray = mutStringArray; // expected-warning{{incompatible pointer types assigning to 'NSArray<NSNumber *> *' from 'NSMutableArray<NSString *> *'}} |
346 | |
347 | // Unspecialized -> specialized (different levels). |
348 | stringArray = mutArray; |
349 | |
350 | // Specialized -> unspecialized (different levels). |
351 | array = mutStringArray; |
352 | } |
353 | |
354 | @interface NSCovariant1<__covariant T> |
355 | @end |
356 | |
357 | @interface NSContravariant1<__contravariant T> |
358 | @end |
359 | |
360 | void test_variance(NSCovariant1<NSString *> *covariant1, |
361 | NSCovariant1<NSMutableString *> *covariant2, |
362 | NSCovariant1<NSString *(^)(void)> *covariant3, |
363 | NSCovariant1<NSMutableString *(^)(void)> *covariant4, |
364 | NSCovariant1<id> *covariant5, |
365 | NSCovariant1<id<NSCopying>> *covariant6, |
366 | NSContravariant1<NSString *> *contravariant1, |
367 | NSContravariant1<NSMutableString *> *contravariant2) { |
368 | covariant1 = covariant2; // okay |
369 | covariant2 = covariant1; // expected-warning{{incompatible pointer types assigning to 'NSCovariant1<NSMutableString *> *' from 'NSCovariant1<NSString *> *'}} |
370 | |
371 | covariant3 = covariant4; // okay |
372 | covariant4 = covariant3; // expected-warning{{incompatible pointer types assigning to 'NSCovariant1<NSMutableString *(^)(void)> *' from 'NSCovariant1<NSString *(^)(void)> *'}} |
373 | |
374 | covariant5 = covariant1; // okay |
375 | covariant1 = covariant5; // okay: id is promiscuous |
376 | |
377 | covariant5 = covariant3; // okay |
378 | covariant3 = covariant5; // okay |
379 | |
380 | contravariant1 = contravariant2; // expected-warning{{incompatible pointer types assigning to 'NSContravariant1<NSString *> *' from 'NSContravariant1<NSMutableString *> *'}} |
381 | contravariant2 = contravariant1; // okay |
382 | } |
383 | |
384 | // -------------------------------------------------------------------------- |
385 | // Ternary operator |
386 | // -------------------------------------------------------------------------- |
387 | void test_ternary_operator(NSArray<NSString *> *stringArray, |
388 | NSArray<NSNumber *> *numberArray, |
389 | NSMutableArray<NSString *> *mutStringArray, |
390 | NSStringArray *stringArray2, |
391 | NSArray *array, |
392 | NSMutableArray *mutArray, |
393 | int cond) { |
394 | int *ip; |
395 | id object; |
396 | |
397 | ip = cond ? stringArray : mutStringArray; // expected-warning{{from 'NSArray<NSString *> *'}} |
398 | ip = cond ? mutStringArray : stringArray; // expected-warning{{from 'NSArray<NSString *> *'}} |
399 | |
400 | ip = cond ? stringArray2 : mutStringArray; // expected-warning{{from 'NSArray<NSString *> *'}} |
401 | ip = cond ? mutStringArray : stringArray2; // expected-warning{{from 'NSArray<NSString *> *'}} |
402 | |
403 | ip = cond ? stringArray : mutArray; // expected-warning{{from 'NSArray *'}} |
404 | |
405 | ip = cond ? stringArray2 : mutArray; // expected-warning{{from 'NSArray *'}} |
406 | |
407 | ip = cond ? mutArray : stringArray; // expected-warning{{from 'NSArray *'}} |
408 | |
409 | ip = cond ? mutArray : stringArray2; // expected-warning{{from 'NSArray *'}} |
410 | |
411 | object = cond ? stringArray : numberArray; // expected-warning{{incompatible operand types ('NSArray<NSString *> *' and 'NSArray<NSNumber *> *')}} |
412 | } |
413 | |
414 | // -------------------------------------------------------------------------- |
415 | // super |
416 | // -------------------------------------------------------------------------- |
417 | @implementation NSStringArray |
418 | - (void)useSuperMethod { |
419 | int *ip; |
420 | ip = super.lastObject; // expected-warning{{from 'NSString *'}} |
421 | super.lastObject = ip; // expected-warning{{to 'NSString *'}} |
422 | ip = [super objectAtIndexedSubscript:0]; // expected-warning{{from 'NSString *'}} |
423 | } |
424 | |
425 | + (void)useSuperMethod { |
426 | int *ip; |
427 | ip = super.array; // expected-warning{{from 'NSArray<NSString *> *'}} |
428 | super.array = ip; // expected-warning{{to 'NSArray<NSString *> *'}} |
429 | ip = [super array]; // expected-warning{{from 'NSArray<NSString *> *'}} |
430 | } |
431 | @end |
432 | |
433 | // -------------------------------------------------------------------------- |
434 | // warning about likely protocol/class name typos. |
435 | // -------------------------------------------------------------------------- |
436 | typedef NSArray<NSObject> ArrayOfNSObjectWarning; // expected-warning{{parameterized class 'NSArray' already conforms to the protocols listed; did you forget a '*'?}} |
437 | |
438 | // rdar://25060179 |
439 | @interface MyMutableDictionary<KeyType, ObjectType> : NSObject |
440 | - (void)setObject:(ObjectType)obj forKeyedSubscript:(KeyType <NSCopying>)key; // expected-note{{passing argument to parameter 'obj' here}} \ |
441 | // expected-note{{passing argument to parameter 'key' here}} |
442 | @end |
443 | |
444 | void bar(MyMutableDictionary<NSString *, NSString *> *stringsByString, |
445 | NSNumber *n1, NSNumber *n2) { |
446 | // We warn here when the key types do not match. |
447 | stringsByString[n1] = n2; // expected-warning{{incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString *'}} \ |
448 | // expected-warning{{incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString<NSCopying> *'}} |
449 | } |
450 | |
451 | @interface MyTest<K, V> : NSObject <NSCopying> |
452 | - (V)test:(K)key; |
453 | - (V)test2:(K)key; // expected-note{{previous definition is here}} |
454 | - (void)mapUsingBlock:(id (^)(V))block; |
455 | - (void)mapUsingBlock2:(id (^)(V))block; // expected-note{{previous definition is here}} |
456 | @end |
457 | |
458 | @implementation MyTest |
459 | - (id)test:(id)key { |
460 | return key; |
461 | } |
462 | - (int)test2:(id)key{ // expected-warning{{conflicting return type in implementation}} |
463 | return 0; |
464 | } |
465 | - (void)mapUsingBlock:(id (^)(id))block { |
466 | } |
467 | - (void)mapUsingBlock2:(id)block { // expected-warning{{conflicting parameter types in implementation}} |
468 | } |
469 | @end |
470 | |