Clang Project

clang_source_code/test/Analysis/generics.m
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
14typedef unsigned long NSUInteger;
15typedef 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
84int getUnknown();
85NSArray *getStuff();
86NSArray *getTypedStuff() {
87  NSArray<NSNumber *> *c = getStuff();
88  return c;
89}
90
91void doStuff(NSArray<NSNumber *> *);
92void withArrString(NSArray<NSString *> *);
93void withArrMutableString(NSArray<NSMutableString *> *);
94void withMutArrString(MutableArray<NSString *> *);
95void withMutArrMutableString(MutableArray<NSMutableString *> *);
96
97void 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
110void crossProceduralErasedTypes() {
111  NSArray<NSString *> *a = getTypedStuff(); // expected-warning {{Conversion}}
112}
113
114void 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
124void 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
133void 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
144void 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
157void verifyAPIusage(id a, MutableArray<NSString *> *b) {
158  b = a;
159  doStuff(a); // expected-warning {{Conversion}}
160}
161
162void dontInferFromExplicitCastsOnUnspecialized(MutableArray *a,
163                        MutableArray<NSMutableString *> *b) {
164  b = (MutableArray<NSMutableString *> *)a;
165  [a addObject: [[NSString alloc] init]]; // no-warning
166}
167
168void dontWarnOnExplicitCastsAfterInference(MutableArray *a) {
169  withMutArrString(a);
170  withMutArrMutableString((MutableArray<NSMutableString *> *)a); // no-warning
171}
172
173void 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
184void 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
192void 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
204void 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
216void 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
223void inferenceFromAPI2(id a) {
224  withMutArrMutableString(a);
225  withMutArrString(a); // expected-warning {{Conversion}}
226}
227
228void inferenceFromAPIWithLegacyTypes(LegacyMutableArray *a) {
229  withMutArrMutableString(a);
230  withMutArrString(a); // expected-warning {{Conversion}}
231}
232
233void inferenceFromAPIWithLegacyTypes2(LegacySpecialMutableArray *a) {
234  withMutArrString(a);
235  withMutArrMutableString(a); // expected-warning {{Conversion}}
236}
237
238void inferenceFromAPIWithLegacyTypes3(__kindof NSArray<NSString *> *a) {
239  LegacyMutableArray *b = a;
240  withMutArrString(b);
241  withMutArrMutableString(b); // expected-warning {{Conversion}}
242}
243
244void inferenceFromAPIWithBuggyTypes(BuggyMutableArray<NSMutableString *> *a) {
245  withMutArrString(a);
246  withMutArrMutableString(a); // expected-warning {{Conversion}}
247}
248
249void InferenceFromAPIWithBuggyTypes2(BuggySpecialMutableArray<NSMutableString *> *a) {
250  withMutArrMutableString(a);
251  withMutArrString(a); // expected-warning {{Conversion}}
252}
253
254void InferenceFromAPIWithBuggyTypes3(MutableArray<NSMutableString *> *a) {
255  id b = a;
256  withMutArrMutableString((BuggyMutableArray<NSMutableString *> *)b);
257  withMutArrString(b); // expected-warning {{Conversion}}
258}
259
260void InferenceFromAPIWithBuggyTypes4(__kindof NSArray<NSString *> *a) {
261  BuggyMutableArray<NSMutableString *> *b = a;
262  withMutArrString(b);
263  withMutArrMutableString(b); // expected-warning {{Conversion}}
264}
265
266NSArray<NSString *> *getStrings();
267void 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
274void 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
286void 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
298void 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
308void testUnannotatedLiterals() {
309  // ObjCArrayLiterals are not specialized in the AST. 
310  NSArray *arr = @[@"A", @"B"];
311  [arr contains: [[NSNumber alloc] init]];
312}
313
314void testAnnotatedLiterals() {
315  NSArray<NSString *> *arr = @[@"A", @"B"];
316  NSArray *arr2 = arr;
317  [arr2 contains: [[NSNumber alloc] init]];
318}
319
320void nonExistentMethodDoesNotCrash(id a, MutableArray<NSMutableString *> *b) {
321  a = b;
322  [a nonExistentMethod];
323}
324
325void 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
331void nestedCollections(NSArray<NSArray<NSNumber *> *> *mat, NSArray<NSString *> *row) {
332  id temp = row;
333  [mat contains: temp]; // expected-warning {{Conversion}}
334}
335
336void 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
344void returnCollectionToIdVariable(NSArray<NSArray<NSString *> *> *arr) {
345  NSArray *erased = arr;
346  id a = [erased firstObject];
347  NSArray<NSNumber *> *res = a; // expected-warning {{Conversion}}
348}
349
350void eraseSpecialization(NSArray<NSArray<NSString *> *> *arr) {
351  NSArray *erased = arr;
352  NSArray* a = [erased firstObject];
353  NSArray<NSNumber *> *res = a; // expected-warning {{Conversion}}
354}
355
356void 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
362void 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
372void 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
383void testArgumentAfterUpcastToRootWithCovariantTypeParameter(NSArray<NSString *> *allStrings, NSNumber *number) {
384  NSArray<NSObject *> *allObjects = allStrings; // no-warning
385  NSArray<NSObject *> *moreObjects = [allObjects arrayByAddingObject:number]; // no-warning
386}
387
388void 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
394void 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
404void testCallToMethodWithCovariantParameterOnInstanceOfSubclassWithInvariantParameter(NSMutableArray<NSMutableString *> *mutableArrayOfMutableStrings, NSString *someString) {
405  NSArray<NSString *> *arrayOfStrings = mutableArrayOfMutableStrings;
406  [arrayOfStrings containsObject:someString]; // no-warning
407}
408
409