1 | // RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.cocoa.ObjCGenerics,alpha.core.DynamicTypeChecker -verify -Wno-objc-method-access %s |
2 | // RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.cocoa.ObjCGenerics,alpha.core.DynamicTypeChecker -verify -Wno-objc-method-access %s -analyzer-output=plist -o %t.plist |
3 | // RUN: cat %t.plist | %diff_plist %S/Inputs/expected-plists/generics.m.plist - |
4 | |
5 | #if !__has_feature(objc_generics) |
6 | # error Compiler does not support Objective-C generics? |
7 | #endif |
8 | |
9 | #if !__has_feature(objc_generics_variance) |
10 | # error Compiler does not support co- and contr-variance? |
11 | #endif |
12 | |
13 | #define nil 0 |
14 | typedef unsigned long NSUInteger; |
15 | typedef int BOOL; |
16 | |
17 | @protocol NSObject |
18 | + (id)alloc; |
19 | - (id)init; |
20 | @end |
21 | |
22 | @protocol NSCopying |
23 | @end |
24 | |
25 | __attribute__((objc_root_class)) |
26 | @interface NSObject <NSObject> |
27 | @end |
28 | |
29 | @interface NSString : NSObject <NSCopying> |
30 | @end |
31 | |
32 | @interface NSMutableString : NSString |
33 | @end |
34 | |
35 | @interface NSNumber : NSObject <NSCopying> |
36 | @end |
37 | |
38 | @interface NSSet : NSObject <NSCopying> |
39 | @end |
40 | |
41 | @interface NSArray<__covariant ObjectType> : NSObject |
42 | + (instancetype)arrayWithObjects:(const ObjectType [])objects count:(NSUInteger)count; |
43 | + (instancetype)getEmpty; |
44 | + (NSArray<ObjectType> *)getEmpty2; |
45 | - (BOOL)contains:(ObjectType)obj; |
46 | - (BOOL)containsObject:(ObjectType)anObject; |
47 | - (ObjectType)getObjAtIndex:(NSUInteger)idx; |
48 | - (ObjectType)objectAtIndexedSubscript:(NSUInteger)idx; |
49 | - (NSArray<ObjectType> *)arrayByAddingObject:(ObjectType)anObject; |
50 | @property(readonly) ObjectType firstObject; |
51 | @end |
52 | |
53 | @interface NSMutableArray<ObjectType> : NSArray<ObjectType> |
54 | - (void)addObject:(ObjectType)anObject; |
55 | - (instancetype)init; |
56 | @end |
57 | |
58 | @interface MutableArray<ObjectType> : NSArray<ObjectType> |
59 | - (void)addObject:(ObjectType)anObject; |
60 | @end |
61 | |
62 | @interface LegacyMutableArray : MutableArray |
63 | @end |
64 | |
65 | @interface LegacySpecialMutableArray : LegacyMutableArray |
66 | @end |
67 | |
68 | @interface BuggyMutableArray<T> : MutableArray |
69 | @end |
70 | |
71 | @interface BuggySpecialMutableArray<T> : BuggyMutableArray<T> |
72 | @end |
73 | |
74 | @interface MyMutableStringArray : MutableArray<NSString *> |
75 | @end |
76 | |
77 | @interface ExceptionalArray<ExceptionType> : MutableArray<NSString *> |
78 | - (ExceptionType) getException; |
79 | @end |
80 | |
81 | @interface UnrelatedType : NSObject<NSCopying> |
82 | @end |
83 | |
84 | int getUnknown(); |
85 | NSArray *getStuff(); |
86 | NSArray *getTypedStuff() { |
87 | NSArray<NSNumber *> *c = getStuff(); |
88 | return c; |
89 | } |
90 | |
91 | void doStuff(NSArray<NSNumber *> *); |
92 | void withArrString(NSArray<NSString *> *); |
93 | void withArrMutableString(NSArray<NSMutableString *> *); |
94 | void withMutArrString(MutableArray<NSString *> *); |
95 | void withMutArrMutableString(MutableArray<NSMutableString *> *); |
96 | |
97 | void incompatibleTypesErased(NSArray *a, NSMutableArray<NSString *> *b, |
98 | NSArray<NSNumber *> *c, |
99 | NSMutableArray *d) { |
100 | a = b; |
101 | c = a; // expected-warning {{Conversion from value of type 'NSMutableArray<NSString *> *' to incompatible type 'NSArray<NSNumber *> *'}} |
102 | [a contains: [[NSNumber alloc] init]]; |
103 | [a contains: [[NSString alloc] init]]; |
104 | doStuff(a); // expected-warning {{Conversion}} |
105 | |
106 | d = b; |
107 | [d addObject: [[NSNumber alloc] init]]; // expected-warning {{Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'}} |
108 | } |
109 | |
110 | void crossProceduralErasedTypes() { |
111 | NSArray<NSString *> *a = getTypedStuff(); // expected-warning {{Conversion}} |
112 | } |
113 | |
114 | void incompatibleTypesErasedReverseConversion(NSMutableArray *a, |
115 | NSMutableArray<NSString *> *b) { |
116 | b = a; |
117 | [a contains: [[NSNumber alloc] init]]; |
118 | [a contains: [[NSString alloc] init]]; |
119 | doStuff(a); // expected-warning {{Conversion}} |
120 | |
121 | [a addObject: [[NSNumber alloc] init]]; // expected-warning {{Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'}} |
122 | } |
123 | |
124 | void idErasedIncompatibleTypesReverseConversion(id a, NSMutableArray<NSString *> *b) { |
125 | b = a; |
126 | [a contains: [[NSNumber alloc] init]]; |
127 | [a contains: [[NSString alloc] init]]; |
128 | doStuff(a); // expected-warning {{Conversion}} |
129 | |
130 | [a addObject:[[NSNumber alloc] init]]; // expected-warning {{Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'}} |
131 | } |
132 | |
133 | void idErasedIncompatibleTypes(id a, NSMutableArray<NSString *> *b, |
134 | NSArray<NSNumber *> *c) { |
135 | a = b; |
136 | c = a; // expected-warning {{Conversion}} |
137 | [a contains: [[NSNumber alloc] init]]; |
138 | [a contains: [[NSString alloc] init]]; |
139 | doStuff(a); // expected-warning {{Conversion}} |
140 | |
141 | [a addObject:[[NSNumber alloc] init]]; // expected-warning {{Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'}} |
142 | } |
143 | |
144 | void pathSensitiveInference(MutableArray *m, MutableArray<NSString *> *a, |
145 | MutableArray<NSMutableString *> *b) { |
146 | if (getUnknown() == 5) { |
147 | m = a; |
148 | [m contains: [[NSString alloc] init]]; |
149 | } else { |
150 | m = b; |
151 | [m contains: [[NSMutableString alloc] init]]; |
152 | } |
153 | [m addObject: [[NSString alloc] init]]; // expected-warning {{Conversion}} |
154 | [m addObject: [[NSMutableString alloc] init]]; |
155 | } |
156 | |
157 | void verifyAPIusage(id a, MutableArray<NSString *> *b) { |
158 | b = a; |
159 | doStuff(a); // expected-warning {{Conversion}} |
160 | } |
161 | |
162 | void dontInferFromExplicitCastsOnUnspecialized(MutableArray *a, |
163 | MutableArray<NSMutableString *> *b) { |
164 | b = (MutableArray<NSMutableString *> *)a; |
165 | [a addObject: [[NSString alloc] init]]; // no-warning |
166 | } |
167 | |
168 | void dontWarnOnExplicitCastsAfterInference(MutableArray *a) { |
169 | withMutArrString(a); |
170 | withMutArrMutableString((MutableArray<NSMutableString *> *)a); // no-warning |
171 | } |
172 | |
173 | void dontDiagnoseOnExplicitCrossCasts(MutableArray<NSSet *> *a, |
174 | MutableArray<NSMutableString *> *b) { |
175 | // Treat an explicit cast to a specialized type as an indication that |
176 | // Objective-C's type system is not expressive enough to represent a |
177 | // the invariant the programmer wanted. After an explicit cast, do not |
178 | // warn about potential generics shenanigans. |
179 | b = (MutableArray<NSMutableString *> *)a; // no-warning |
180 | [a addObject: [[NSSet alloc] init]]; // no-warning |
181 | [b addObject: [[NSMutableString alloc] init]]; //no-warning |
182 | } |
183 | |
184 | void subtypeOfGeneric(id d, MyMutableStringArray *a, |
185 | MutableArray<NSString *> *b, |
186 | MutableArray<NSNumber *> *c) { |
187 | d = a; |
188 | b = d; |
189 | c = d; // expected-warning {{Conversion}} |
190 | } |
191 | |
192 | void genericSubtypeOfGeneric(id d, ExceptionalArray<NSString *> *a, |
193 | MutableArray<NSString *> *b, |
194 | MutableArray<NSNumber *> *c) { |
195 | d = a; |
196 | [d contains: [[NSString alloc] init]]; |
197 | [d contains: [[NSNumber alloc] init]]; |
198 | b = d; |
199 | c = d; // expected-warning {{Conversion}} |
200 | |
201 | [d addObject: [[NSNumber alloc] init]]; // expected-warning {{Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'}} |
202 | } |
203 | |
204 | void genericSubtypeOfGenericReverse(id d, ExceptionalArray<NSString *> *a, |
205 | MutableArray<NSString *> *b, |
206 | MutableArray<NSNumber *> *c) { |
207 | a = d; |
208 | [d contains: [[NSString alloc] init]]; |
209 | [d contains: [[NSNumber alloc] init]]; |
210 | b = d; |
211 | c = d; // expected-warning {{Conversion}} |
212 | |
213 | [d addObject: [[NSNumber alloc] init]]; // expected-warning {{Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'}} |
214 | } |
215 | |
216 | void inferenceFromAPI(id a) { |
217 | // Here the type parameter is invariant. There should be a warning every time |
218 | // when the type parameter changes during the conversions. |
219 | withMutArrString(a); |
220 | withMutArrMutableString(a); // expected-warning {{Conversion}} |
221 | } |
222 | |
223 | void inferenceFromAPI2(id a) { |
224 | withMutArrMutableString(a); |
225 | withMutArrString(a); // expected-warning {{Conversion}} |
226 | } |
227 | |
228 | void inferenceFromAPIWithLegacyTypes(LegacyMutableArray *a) { |
229 | withMutArrMutableString(a); |
230 | withMutArrString(a); // expected-warning {{Conversion}} |
231 | } |
232 | |
233 | void inferenceFromAPIWithLegacyTypes2(LegacySpecialMutableArray *a) { |
234 | withMutArrString(a); |
235 | withMutArrMutableString(a); // expected-warning {{Conversion}} |
236 | } |
237 | |
238 | void inferenceFromAPIWithLegacyTypes3(__kindof NSArray<NSString *> *a) { |
239 | LegacyMutableArray *b = a; |
240 | withMutArrString(b); |
241 | withMutArrMutableString(b); // expected-warning {{Conversion}} |
242 | } |
243 | |
244 | void inferenceFromAPIWithBuggyTypes(BuggyMutableArray<NSMutableString *> *a) { |
245 | withMutArrString(a); |
246 | withMutArrMutableString(a); // expected-warning {{Conversion}} |
247 | } |
248 | |
249 | void InferenceFromAPIWithBuggyTypes2(BuggySpecialMutableArray<NSMutableString *> *a) { |
250 | withMutArrMutableString(a); |
251 | withMutArrString(a); // expected-warning {{Conversion}} |
252 | } |
253 | |
254 | void InferenceFromAPIWithBuggyTypes3(MutableArray<NSMutableString *> *a) { |
255 | id b = a; |
256 | withMutArrMutableString((BuggyMutableArray<NSMutableString *> *)b); |
257 | withMutArrString(b); // expected-warning {{Conversion}} |
258 | } |
259 | |
260 | void InferenceFromAPIWithBuggyTypes4(__kindof NSArray<NSString *> *a) { |
261 | BuggyMutableArray<NSMutableString *> *b = a; |
262 | withMutArrString(b); |
263 | withMutArrMutableString(b); // expected-warning {{Conversion}} |
264 | } |
265 | |
266 | NSArray<NSString *> *getStrings(); |
267 | void enforceDynamicRulesInsteadOfStatic(NSArray<NSNumber *> *a) { |
268 | NSArray *b = a; |
269 | // Valid uses of NSArray of NSNumbers. |
270 | b = getStrings(); |
271 | // Valid uses of NSArray of NSStrings. |
272 | } |
273 | |
274 | void workWithProperties(NSArray<NSNumber *> *a) { |
275 | NSArray *b = a; |
276 | NSString *str = [b getObjAtIndex: 0]; // expected-warning {{Object has a dynamic type 'NSNumber *' which is incompatible with static type 'NSString *'}} |
277 | NSNumber *num = [b getObjAtIndex: 0]; |
278 | str = [b firstObject]; // expected-warning {{Object has a dynamic type 'NSNumber *' which is incompatible with static type 'NSString *'}} |
279 | num = [b firstObject]; |
280 | str = b.firstObject; // expected-warning {{Object has a dynamic type 'NSNumber *' which is incompatible with static type 'NSString *'}} |
281 | num = b.firstObject; |
282 | str = b[0]; // expected-warning {{Object has a dynamic type 'NSNumber *' which is incompatible with static type 'NSString *'}} |
283 | num = b[0]; |
284 | } |
285 | |
286 | void findMethodDeclInTrackedType(id m, NSArray<NSMutableString *> *a, |
287 | MutableArray<NSMutableString *> *b) { |
288 | a = b; |
289 | if (getUnknown() == 5) { |
290 | m = a; |
291 | [m addObject: [[NSString alloc] init]]; // expected-warning {{Conversion}} |
292 | } else { |
293 | m = b; |
294 | [m addObject: [[NSMutableString alloc] init]]; |
295 | } |
296 | } |
297 | |
298 | void findMethodDeclInTrackedType2(__kindof NSArray<NSString *> *a, |
299 | MutableArray<NSMutableString *> *b) { |
300 | a = b; |
301 | if (getUnknown() == 5) { |
302 | [a addObject: [[NSString alloc] init]]; // expected-warning {{Conversion}} |
303 | } else { |
304 | [a addObject: [[NSMutableString alloc] init]]; |
305 | } |
306 | } |
307 | |
308 | void testUnannotatedLiterals() { |
309 | // ObjCArrayLiterals are not specialized in the AST. |
310 | NSArray *arr = @[@"A", @"B"]; |
311 | [arr contains: [[NSNumber alloc] init]]; |
312 | } |
313 | |
314 | void testAnnotatedLiterals() { |
315 | NSArray<NSString *> *arr = @[@"A", @"B"]; |
316 | NSArray *arr2 = arr; |
317 | [arr2 contains: [[NSNumber alloc] init]]; |
318 | } |
319 | |
320 | void nonExistentMethodDoesNotCrash(id a, MutableArray<NSMutableString *> *b) { |
321 | a = b; |
322 | [a nonExistentMethod]; |
323 | } |
324 | |
325 | void trackedClassVariables() { |
326 | Class c = [NSArray<NSString *> class]; |
327 | NSArray<NSNumber *> *a = [c getEmpty]; // expected-warning {{Conversion}} |
328 | a = [c getEmpty2]; // expected-warning {{Conversion}} |
329 | } |
330 | |
331 | void nestedCollections(NSArray<NSArray<NSNumber *> *> *mat, NSArray<NSString *> *row) { |
332 | id temp = row; |
333 | [mat contains: temp]; // expected-warning {{Conversion}} |
334 | } |
335 | |
336 | void testMistmatchedTypeCast(MutableArray<NSMutableString *> *a) { |
337 | MutableArray *b = (MutableArray<NSNumber *> *)a; |
338 | [b addObject: [[NSNumber alloc] init]]; |
339 | id c = (UnrelatedType *)a; |
340 | [c addObject: [[NSNumber alloc] init]]; |
341 | [c addObject: [[NSString alloc] init]]; |
342 | } |
343 | |
344 | void returnCollectionToIdVariable(NSArray<NSArray<NSString *> *> *arr) { |
345 | NSArray *erased = arr; |
346 | id a = [erased firstObject]; |
347 | NSArray<NSNumber *> *res = a; // expected-warning {{Conversion}} |
348 | } |
349 | |
350 | void eraseSpecialization(NSArray<NSArray<NSString *> *> *arr) { |
351 | NSArray *erased = arr; |
352 | NSArray* a = [erased firstObject]; |
353 | NSArray<NSNumber *> *res = a; // expected-warning {{Conversion}} |
354 | } |
355 | |
356 | void returnToUnrelatedType(NSArray<NSArray<NSString *> *> *arr) { |
357 | NSArray *erased = arr; |
358 | NSSet* a = [erased firstObject]; // expected-warning {{Object has a dynamic type 'NSArray<NSString *> *' which is incompatible with static type 'NSSet *'}} |
359 | (void)a; |
360 | } |
361 | |
362 | void returnToIdVariable(NSArray<NSString *> *arr) { |
363 | NSArray *erased = arr; |
364 | id a = [erased firstObject]; |
365 | NSNumber *res = a; // expected-warning {{Object has a dynamic type 'NSString *' which is incompatible with static type 'NSNumber *'}} |
366 | } |
367 | |
368 | @interface UnrelatedTypeGeneric<T> : NSObject<NSCopying> |
369 | - (void)takesType:(T)v; |
370 | @end |
371 | |
372 | void testGetMostInformativeDerivedForId(NSArray<NSString *> *a, |
373 | UnrelatedTypeGeneric<NSString *> *b) { |
374 | id idB = b; |
375 | a = idB; // expected-warning {{Conversion from value of type 'UnrelatedTypeGeneric<NSString *> *' to incompatible type 'NSArray<NSString *> *'}} |
376 | |
377 | // rdar://problem/26086914 crash here caused by symbolic type being unrelated |
378 | // to compile-time source type of cast. |
379 | id x = a; // Compile-time type is NSArray<>, Symbolic type is UnrelatedTypeGeneric<>. |
380 | [x takesType:[[NSNumber alloc] init]]; // expected-warning {{Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'}} |
381 | } |
382 | |
383 | void testArgumentAfterUpcastToRootWithCovariantTypeParameter(NSArray<NSString *> *allStrings, NSNumber *number) { |
384 | NSArray<NSObject *> *allObjects = allStrings; // no-warning |
385 | NSArray<NSObject *> *moreObjects = [allObjects arrayByAddingObject:number]; // no-warning |
386 | } |
387 | |
388 | void testArgumentAfterUpcastWithCovariantTypeParameter(NSArray<NSMutableString *> *allMutableStrings, NSNumber *number) { |
389 | NSArray<NSString *> *allStrings = allMutableStrings; // no-warning |
390 | id numberAsId = number; |
391 | NSArray<NSString *> *moreStrings = [allStrings arrayByAddingObject:numberAsId]; // Sema: expected-warning {{Object has a dynamic type 'NSNumber *' which is incompatible with static type 'NSString *'}} |
392 | } |
393 | |
394 | void testArgumentAfterCastToUnspecializedWithCovariantTypeParameter(NSArray<NSMutableString *> *allMutableStrings, NSNumber *number) { |
395 | NSArray *allStrings = allMutableStrings; // no-warning |
396 | id numberAsId = number; |
397 | |
398 | NSArray *moreStringsUnspecialized = [allStrings arrayByAddingObject:numberAsId]; // no-warning |
399 | |
400 | // Ideally the analyzer would warn here. |
401 | NSArray<NSString *> *moreStringsSpecialized = [allStrings arrayByAddingObject:numberAsId]; |
402 | } |
403 | |
404 | void testCallToMethodWithCovariantParameterOnInstanceOfSubclassWithInvariantParameter(NSMutableArray<NSMutableString *> *mutableArrayOfMutableStrings, NSString *someString) { |
405 | NSArray<NSString *> *arrayOfStrings = mutableArrayOfMutableStrings; |
406 | [arrayOfStrings containsObject:someString]; // no-warning |
407 | } |
408 | |
409 | |