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 - 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(void); 85NSArray *getStuff(void); 86NSArray *getTypedStuff(void) { 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(void) { 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(void); 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(void) { 309 // ObjCArrayLiterals are not specialized in the AST. 310 NSArray *arr = @[@"A", @"B"]; 311 [arr contains: [[NSNumber alloc] init]]; 312} 313 314void testAnnotatedLiterals(void) { 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(void) { 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 // crash here caused by symbolic type being unrelated to compile-time source 378 // 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