409 lines · plain
1// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.cocoa.ObjCGenerics,alpha.core.DynamicTypeChecker -verify -Wno-objc-method-access %s2// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.cocoa.ObjCGenerics,alpha.core.DynamicTypeChecker -verify -Wno-objc-method-access %s -analyzer-output=plist -o %t.plist3// 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#endif8 9#if !__has_feature(objc_generics_variance)10# error Compiler does not support co- and contr-variance?11#endif12 13#define nil 014typedef unsigned long NSUInteger;15typedef int BOOL;16 17@protocol NSObject18+ (id)alloc;19- (id)init;20@end21 22@protocol NSCopying23@end24 25__attribute__((objc_root_class))26@interface NSObject <NSObject>27@end28 29@interface NSString : NSObject <NSCopying>30@end31 32@interface NSMutableString : NSString33@end34 35@interface NSNumber : NSObject <NSCopying>36@end37 38@interface NSSet : NSObject <NSCopying>39@end40 41@interface NSArray<__covariant ObjectType> : NSObject42+ (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@end52 53@interface NSMutableArray<ObjectType> : NSArray<ObjectType>54- (void)addObject:(ObjectType)anObject;55- (instancetype)init;56@end57 58@interface MutableArray<ObjectType> : NSArray<ObjectType>59- (void)addObject:(ObjectType)anObject;60@end61 62@interface LegacyMutableArray : MutableArray63@end64 65@interface LegacySpecialMutableArray : LegacyMutableArray66@end67 68@interface BuggyMutableArray<T> : MutableArray69@end70 71@interface BuggySpecialMutableArray<T> : BuggyMutableArray<T>72@end73 74@interface MyMutableStringArray : MutableArray<NSString *>75@end76 77@interface ExceptionalArray<ExceptionType> : MutableArray<NSString *>78- (ExceptionType) getException;79@end80 81@interface UnrelatedType : NSObject<NSCopying>82@end83 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-warning166}167 168void dontWarnOnExplicitCastsAfterInference(MutableArray *a) {169 withMutArrString(a);170 withMutArrMutableString((MutableArray<NSMutableString *> *)a); // no-warning171}172 173void dontDiagnoseOnExplicitCrossCasts(MutableArray<NSSet *> *a,174 MutableArray<NSMutableString *> *b) {175 // Treat an explicit cast to a specialized type as an indication that176 // Objective-C's type system is not expressive enough to represent a177 // the invariant the programmer wanted. After an explicit cast, do not178 // warn about potential generics shenanigans.179 b = (MutableArray<NSMutableString *> *)a; // no-warning180 [a addObject: [[NSSet alloc] init]]; // no-warning181 [b addObject: [[NSMutableString alloc] init]]; //no-warning182}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 time218 // 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@end371 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 source378 // 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-warning385 NSArray<NSObject *> *moreObjects = [allObjects arrayByAddingObject:number]; // no-warning386}387 388void testArgumentAfterUpcastWithCovariantTypeParameter(NSArray<NSMutableString *> *allMutableStrings, NSNumber *number) {389 NSArray<NSString *> *allStrings = allMutableStrings; // no-warning390 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-warning396 id numberAsId = number;397 398 NSArray *moreStringsUnspecialized = [allStrings arrayByAddingObject:numberAsId]; // no-warning399 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-warning407}408 409