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: %normalize_plist <%t.plist | diff -ub %S/Inputs/expected-plists/generics.m.plist -
5 #if !__has_feature(objc_generics)
6 # error Compiler does not support Objective-C generics?
9 #if !__has_feature(objc_generics_variance)
10 # error Compiler does not support co- and contr-variance?
14 typedef unsigned long NSUInteger;
25 __attribute__((objc_root_class))
26 @interface NSObject <NSObject>
29 @interface NSString : NSObject <NSCopying>
32 @interface NSMutableString : NSString
35 @interface NSNumber : NSObject <NSCopying>
38 @interface NSSet : NSObject <NSCopying>
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;
53 @interface NSMutableArray<ObjectType> : NSArray<ObjectType>
54 - (void)addObject:(ObjectType)anObject;
58 @interface MutableArray<ObjectType> : NSArray<ObjectType>
59 - (void)addObject:(ObjectType)anObject;
62 @interface LegacyMutableArray : MutableArray
65 @interface LegacySpecialMutableArray : LegacyMutableArray
68 @interface BuggyMutableArray<T> : MutableArray
71 @interface BuggySpecialMutableArray<T> : BuggyMutableArray<T>
74 @interface MyMutableStringArray : MutableArray<NSString *>
77 @interface ExceptionalArray<ExceptionType> : MutableArray<NSString *>
78 - (ExceptionType) getException;
81 @interface UnrelatedType : NSObject<NSCopying>
85 NSArray *getStuff(void);
86 NSArray *getTypedStuff(void) {
87 NSArray<NSNumber *> *c = getStuff();
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 *> *);
97 void incompatibleTypesErased(NSArray *a, NSMutableArray<NSString *> *b,
98 NSArray<NSNumber *> *c,
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}}
107 [d addObject: [[NSNumber alloc] init]]; // expected-warning {{Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'}}
110 void crossProceduralErasedTypes(void) {
111 NSArray<NSString *> *a = getTypedStuff(); // expected-warning {{Conversion}}
114 void incompatibleTypesErasedReverseConversion(NSMutableArray *a,
115 NSMutableArray<NSString *> *b) {
117 [a contains: [[NSNumber alloc] init]];
118 [a contains: [[NSString alloc] init]];
119 doStuff(a); // expected-warning {{Conversion}}
121 [a addObject: [[NSNumber alloc] init]]; // expected-warning {{Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'}}
124 void idErasedIncompatibleTypesReverseConversion(id a, NSMutableArray<NSString *> *b) {
126 [a contains: [[NSNumber alloc] init]];
127 [a contains: [[NSString alloc] init]];
128 doStuff(a); // expected-warning {{Conversion}}
130 [a addObject:[[NSNumber alloc] init]]; // expected-warning {{Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'}}
133 void idErasedIncompatibleTypes(id a, NSMutableArray<NSString *> *b,
134 NSArray<NSNumber *> *c) {
136 c = a; // expected-warning {{Conversion}}
137 [a contains: [[NSNumber alloc] init]];
138 [a contains: [[NSString alloc] init]];
139 doStuff(a); // expected-warning {{Conversion}}
141 [a addObject:[[NSNumber alloc] init]]; // expected-warning {{Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'}}
144 void pathSensitiveInference(MutableArray *m, MutableArray<NSString *> *a,
145 MutableArray<NSMutableString *> *b) {
146 if (getUnknown() == 5) {
148 [m contains: [[NSString alloc] init]];
151 [m contains: [[NSMutableString alloc] init]];
153 [m addObject: [[NSString alloc] init]]; // expected-warning {{Conversion}}
154 [m addObject: [[NSMutableString alloc] init]];
157 void verifyAPIusage(id a, MutableArray<NSString *> *b) {
159 doStuff(a); // expected-warning {{Conversion}}
162 void dontInferFromExplicitCastsOnUnspecialized(MutableArray *a,
163 MutableArray<NSMutableString *> *b) {
164 b = (MutableArray<NSMutableString *> *)a;
165 [a addObject: [[NSString alloc] init]]; // no-warning
168 void dontWarnOnExplicitCastsAfterInference(MutableArray *a) {
170 withMutArrMutableString((MutableArray<NSMutableString *> *)a); // no-warning
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
184 void subtypeOfGeneric(id d, MyMutableStringArray *a,
185 MutableArray<NSString *> *b,
186 MutableArray<NSNumber *> *c) {
189 c = d; // expected-warning {{Conversion}}
192 void genericSubtypeOfGeneric(id d, ExceptionalArray<NSString *> *a,
193 MutableArray<NSString *> *b,
194 MutableArray<NSNumber *> *c) {
196 [d contains: [[NSString alloc] init]];
197 [d contains: [[NSNumber alloc] init]];
199 c = d; // expected-warning {{Conversion}}
201 [d addObject: [[NSNumber alloc] init]]; // expected-warning {{Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'}}
204 void genericSubtypeOfGenericReverse(id d, ExceptionalArray<NSString *> *a,
205 MutableArray<NSString *> *b,
206 MutableArray<NSNumber *> *c) {
208 [d contains: [[NSString alloc] init]];
209 [d contains: [[NSNumber alloc] init]];
211 c = d; // expected-warning {{Conversion}}
213 [d addObject: [[NSNumber alloc] init]]; // expected-warning {{Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'}}
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.
220 withMutArrMutableString(a); // expected-warning {{Conversion}}
223 void inferenceFromAPI2(id a) {
224 withMutArrMutableString(a);
225 withMutArrString(a); // expected-warning {{Conversion}}
228 void inferenceFromAPIWithLegacyTypes(LegacyMutableArray *a) {
229 withMutArrMutableString(a);
230 withMutArrString(a); // expected-warning {{Conversion}}
233 void inferenceFromAPIWithLegacyTypes2(LegacySpecialMutableArray *a) {
235 withMutArrMutableString(a); // expected-warning {{Conversion}}
238 void inferenceFromAPIWithLegacyTypes3(__kindof NSArray<NSString *> *a) {
239 LegacyMutableArray *b = a;
241 withMutArrMutableString(b); // expected-warning {{Conversion}}
244 void inferenceFromAPIWithBuggyTypes(BuggyMutableArray<NSMutableString *> *a) {
246 withMutArrMutableString(a); // expected-warning {{Conversion}}
249 void InferenceFromAPIWithBuggyTypes2(BuggySpecialMutableArray<NSMutableString *> *a) {
250 withMutArrMutableString(a);
251 withMutArrString(a); // expected-warning {{Conversion}}
254 void InferenceFromAPIWithBuggyTypes3(MutableArray<NSMutableString *> *a) {
256 withMutArrMutableString((BuggyMutableArray<NSMutableString *> *)b);
257 withMutArrString(b); // expected-warning {{Conversion}}
260 void InferenceFromAPIWithBuggyTypes4(__kindof NSArray<NSString *> *a) {
261 BuggyMutableArray<NSMutableString *> *b = a;
263 withMutArrMutableString(b); // expected-warning {{Conversion}}
266 NSArray<NSString *> *getStrings(void);
267 void enforceDynamicRulesInsteadOfStatic(NSArray<NSNumber *> *a) {
269 // Valid uses of NSArray of NSNumbers.
271 // Valid uses of NSArray of NSStrings.
274 void workWithProperties(NSArray<NSNumber *> *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 *'}}
282 str = b[0]; // expected-warning {{Object has a dynamic type 'NSNumber *' which is incompatible with static type 'NSString *'}}
286 void findMethodDeclInTrackedType(id m, NSArray<NSMutableString *> *a,
287 MutableArray<NSMutableString *> *b) {
289 if (getUnknown() == 5) {
291 [m addObject: [[NSString alloc] init]]; // expected-warning {{Conversion}}
294 [m addObject: [[NSMutableString alloc] init]];
298 void findMethodDeclInTrackedType2(__kindof NSArray<NSString *> *a,
299 MutableArray<NSMutableString *> *b) {
301 if (getUnknown() == 5) {
302 [a addObject: [[NSString alloc] init]]; // expected-warning {{Conversion}}
304 [a addObject: [[NSMutableString alloc] init]];
308 void testUnannotatedLiterals(void) {
309 // ObjCArrayLiterals are not specialized in the AST.
310 NSArray *arr = @[@"A", @"B"];
311 [arr contains: [[NSNumber alloc] init]];
314 void testAnnotatedLiterals(void) {
315 NSArray<NSString *> *arr = @[@"A", @"B"];
317 [arr2 contains: [[NSNumber alloc] init]];
320 void nonExistentMethodDoesNotCrash(id a, MutableArray<NSMutableString *> *b) {
322 [a nonExistentMethod];
325 void trackedClassVariables(void) {
326 Class c = [NSArray<NSString *> class];
327 NSArray<NSNumber *> *a = [c getEmpty]; // expected-warning {{Conversion}}
328 a = [c getEmpty2]; // expected-warning {{Conversion}}
331 void nestedCollections(NSArray<NSArray<NSNumber *> *> *mat, NSArray<NSString *> *row) {
333 [mat contains: temp]; // expected-warning {{Conversion}}
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]];
344 void returnCollectionToIdVariable(NSArray<NSArray<NSString *> *> *arr) {
345 NSArray *erased = arr;
346 id a = [erased firstObject];
347 NSArray<NSNumber *> *res = a; // expected-warning {{Conversion}}
350 void eraseSpecialization(NSArray<NSArray<NSString *> *> *arr) {
351 NSArray *erased = arr;
352 NSArray* a = [erased firstObject];
353 NSArray<NSNumber *> *res = a; // expected-warning {{Conversion}}
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 *'}}
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 *'}}
368 @interface UnrelatedTypeGeneric<T> : NSObject<NSCopying>
369 - (void)takesType:(T)v;
372 void testGetMostInformativeDerivedForId(NSArray<NSString *> *a,
373 UnrelatedTypeGeneric<NSString *> *b) {
375 a = idB; // expected-warning {{Conversion from value of type 'UnrelatedTypeGeneric<NSString *> *' to incompatible type 'NSArray<NSString *> *'}}
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 *'}}
383 void testArgumentAfterUpcastToRootWithCovariantTypeParameter(NSArray<NSString *> *allStrings, NSNumber *number) {
384 NSArray<NSObject *> *allObjects = allStrings; // no-warning
385 NSArray<NSObject *> *moreObjects = [allObjects arrayByAddingObject:number]; // no-warning
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 *'}}
394 void testArgumentAfterCastToUnspecializedWithCovariantTypeParameter(NSArray<NSMutableString *> *allMutableStrings, NSNumber *number) {
395 NSArray *allStrings = allMutableStrings; // no-warning
396 id numberAsId = number;
398 NSArray *moreStringsUnspecialized = [allStrings arrayByAddingObject:numberAsId]; // no-warning
400 // Ideally the analyzer would warn here.
401 NSArray<NSString *> *moreStringsSpecialized = [allStrings arrayByAddingObject:numberAsId];
404 void testCallToMethodWithCovariantParameterOnInstanceOfSubclassWithInvariantParameter(NSMutableArray<NSMutableString *> *mutableArrayOfMutableStrings, NSString *someString) {
405 NSArray<NSString *> *arrayOfStrings = mutableArrayOfMutableStrings;
406 [arrayOfStrings containsObject:someString]; // no-warning