// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.cocoa.ObjCGenerics,alpha.core.DynamicTypeChecker -verify -Wno-objc-method-access %s // 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 // RUN: %normalize_plist <%t.plist | diff -ub %S/Inputs/expected-plists/generics.m.plist - #if !__has_feature(objc_generics) # error Compiler does not support Objective-C generics? #endif #if !__has_feature(objc_generics_variance) # error Compiler does not support co- and contr-variance? #endif #define nil 0 typedef unsigned long NSUInteger; typedef int BOOL; @protocol NSObject + (id)alloc; - (id)init; @end @protocol NSCopying @end __attribute__((objc_root_class)) @interface NSObject @end @interface NSString : NSObject @end @interface NSMutableString : NSString @end @interface NSNumber : NSObject @end @interface NSSet : NSObject @end @interface NSArray<__covariant ObjectType> : NSObject + (instancetype)arrayWithObjects:(const ObjectType [])objects count:(NSUInteger)count; + (instancetype)getEmpty; + (NSArray *)getEmpty2; - (BOOL)contains:(ObjectType)obj; - (BOOL)containsObject:(ObjectType)anObject; - (ObjectType)getObjAtIndex:(NSUInteger)idx; - (ObjectType)objectAtIndexedSubscript:(NSUInteger)idx; - (NSArray *)arrayByAddingObject:(ObjectType)anObject; @property(readonly) ObjectType firstObject; @end @interface NSMutableArray : NSArray - (void)addObject:(ObjectType)anObject; - (instancetype)init; @end @interface MutableArray : NSArray - (void)addObject:(ObjectType)anObject; @end @interface LegacyMutableArray : MutableArray @end @interface LegacySpecialMutableArray : LegacyMutableArray @end @interface BuggyMutableArray : MutableArray @end @interface BuggySpecialMutableArray : BuggyMutableArray @end @interface MyMutableStringArray : MutableArray @end @interface ExceptionalArray : MutableArray - (ExceptionType) getException; @end @interface UnrelatedType : NSObject @end int getUnknown(); NSArray *getStuff(); NSArray *getTypedStuff() { NSArray *c = getStuff(); return c; } void doStuff(NSArray *); void withArrString(NSArray *); void withArrMutableString(NSArray *); void withMutArrString(MutableArray *); void withMutArrMutableString(MutableArray *); void incompatibleTypesErased(NSArray *a, NSMutableArray *b, NSArray *c, NSMutableArray *d) { a = b; c = a; // expected-warning {{Conversion from value of type 'NSMutableArray *' to incompatible type 'NSArray *'}} [a contains: [[NSNumber alloc] init]]; [a contains: [[NSString alloc] init]]; doStuff(a); // expected-warning {{Conversion}} d = b; [d addObject: [[NSNumber alloc] init]]; // expected-warning {{Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'}} } void crossProceduralErasedTypes() { NSArray *a = getTypedStuff(); // expected-warning {{Conversion}} } void incompatibleTypesErasedReverseConversion(NSMutableArray *a, NSMutableArray *b) { b = a; [a contains: [[NSNumber alloc] init]]; [a contains: [[NSString alloc] init]]; doStuff(a); // expected-warning {{Conversion}} [a addObject: [[NSNumber alloc] init]]; // expected-warning {{Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'}} } void idErasedIncompatibleTypesReverseConversion(id a, NSMutableArray *b) { b = a; [a contains: [[NSNumber alloc] init]]; [a contains: [[NSString alloc] init]]; doStuff(a); // expected-warning {{Conversion}} [a addObject:[[NSNumber alloc] init]]; // expected-warning {{Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'}} } void idErasedIncompatibleTypes(id a, NSMutableArray *b, NSArray *c) { a = b; c = a; // expected-warning {{Conversion}} [a contains: [[NSNumber alloc] init]]; [a contains: [[NSString alloc] init]]; doStuff(a); // expected-warning {{Conversion}} [a addObject:[[NSNumber alloc] init]]; // expected-warning {{Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'}} } void pathSensitiveInference(MutableArray *m, MutableArray *a, MutableArray *b) { if (getUnknown() == 5) { m = a; [m contains: [[NSString alloc] init]]; } else { m = b; [m contains: [[NSMutableString alloc] init]]; } [m addObject: [[NSString alloc] init]]; // expected-warning {{Conversion}} [m addObject: [[NSMutableString alloc] init]]; } void verifyAPIusage(id a, MutableArray *b) { b = a; doStuff(a); // expected-warning {{Conversion}} } void dontInferFromExplicitCastsOnUnspecialized(MutableArray *a, MutableArray *b) { b = (MutableArray *)a; [a addObject: [[NSString alloc] init]]; // no-warning } void dontWarnOnExplicitCastsAfterInference(MutableArray *a) { withMutArrString(a); withMutArrMutableString((MutableArray *)a); // no-warning } void dontDiagnoseOnExplicitCrossCasts(MutableArray *a, MutableArray *b) { // Treat an explicit cast to a specialized type as an indication that // Objective-C's type system is not expressive enough to represent a // the invariant the programmer wanted. After an explicit cast, do not // warn about potential generics shenanigans. b = (MutableArray *)a; // no-warning [a addObject: [[NSSet alloc] init]]; // no-warning [b addObject: [[NSMutableString alloc] init]]; //no-warning } void subtypeOfGeneric(id d, MyMutableStringArray *a, MutableArray *b, MutableArray *c) { d = a; b = d; c = d; // expected-warning {{Conversion}} } void genericSubtypeOfGeneric(id d, ExceptionalArray *a, MutableArray *b, MutableArray *c) { d = a; [d contains: [[NSString alloc] init]]; [d contains: [[NSNumber alloc] init]]; b = d; c = d; // expected-warning {{Conversion}} [d addObject: [[NSNumber alloc] init]]; // expected-warning {{Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'}} } void genericSubtypeOfGenericReverse(id d, ExceptionalArray *a, MutableArray *b, MutableArray *c) { a = d; [d contains: [[NSString alloc] init]]; [d contains: [[NSNumber alloc] init]]; b = d; c = d; // expected-warning {{Conversion}} [d addObject: [[NSNumber alloc] init]]; // expected-warning {{Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'}} } void inferenceFromAPI(id a) { // Here the type parameter is invariant. There should be a warning every time // when the type parameter changes during the conversions. withMutArrString(a); withMutArrMutableString(a); // expected-warning {{Conversion}} } void inferenceFromAPI2(id a) { withMutArrMutableString(a); withMutArrString(a); // expected-warning {{Conversion}} } void inferenceFromAPIWithLegacyTypes(LegacyMutableArray *a) { withMutArrMutableString(a); withMutArrString(a); // expected-warning {{Conversion}} } void inferenceFromAPIWithLegacyTypes2(LegacySpecialMutableArray *a) { withMutArrString(a); withMutArrMutableString(a); // expected-warning {{Conversion}} } void inferenceFromAPIWithLegacyTypes3(__kindof NSArray *a) { LegacyMutableArray *b = a; withMutArrString(b); withMutArrMutableString(b); // expected-warning {{Conversion}} } void inferenceFromAPIWithBuggyTypes(BuggyMutableArray *a) { withMutArrString(a); withMutArrMutableString(a); // expected-warning {{Conversion}} } void InferenceFromAPIWithBuggyTypes2(BuggySpecialMutableArray *a) { withMutArrMutableString(a); withMutArrString(a); // expected-warning {{Conversion}} } void InferenceFromAPIWithBuggyTypes3(MutableArray *a) { id b = a; withMutArrMutableString((BuggyMutableArray *)b); withMutArrString(b); // expected-warning {{Conversion}} } void InferenceFromAPIWithBuggyTypes4(__kindof NSArray *a) { BuggyMutableArray *b = a; withMutArrString(b); withMutArrMutableString(b); // expected-warning {{Conversion}} } NSArray *getStrings(); void enforceDynamicRulesInsteadOfStatic(NSArray *a) { NSArray *b = a; // Valid uses of NSArray of NSNumbers. b = getStrings(); // Valid uses of NSArray of NSStrings. } void workWithProperties(NSArray *a) { NSArray *b = a; NSString *str = [b getObjAtIndex: 0]; // expected-warning {{Object has a dynamic type 'NSNumber *' which is incompatible with static type 'NSString *'}} NSNumber *num = [b getObjAtIndex: 0]; str = [b firstObject]; // expected-warning {{Object has a dynamic type 'NSNumber *' which is incompatible with static type 'NSString *'}} num = [b firstObject]; str = b.firstObject; // expected-warning {{Object has a dynamic type 'NSNumber *' which is incompatible with static type 'NSString *'}} num = b.firstObject; str = b[0]; // expected-warning {{Object has a dynamic type 'NSNumber *' which is incompatible with static type 'NSString *'}} num = b[0]; } void findMethodDeclInTrackedType(id m, NSArray *a, MutableArray *b) { a = b; if (getUnknown() == 5) { m = a; [m addObject: [[NSString alloc] init]]; // expected-warning {{Conversion}} } else { m = b; [m addObject: [[NSMutableString alloc] init]]; } } void findMethodDeclInTrackedType2(__kindof NSArray *a, MutableArray *b) { a = b; if (getUnknown() == 5) { [a addObject: [[NSString alloc] init]]; // expected-warning {{Conversion}} } else { [a addObject: [[NSMutableString alloc] init]]; } } void testUnannotatedLiterals() { // ObjCArrayLiterals are not specialized in the AST. NSArray *arr = @[@"A", @"B"]; [arr contains: [[NSNumber alloc] init]]; } void testAnnotatedLiterals() { NSArray *arr = @[@"A", @"B"]; NSArray *arr2 = arr; [arr2 contains: [[NSNumber alloc] init]]; } void nonExistentMethodDoesNotCrash(id a, MutableArray *b) { a = b; [a nonExistentMethod]; } void trackedClassVariables() { Class c = [NSArray class]; NSArray *a = [c getEmpty]; // expected-warning {{Conversion}} a = [c getEmpty2]; // expected-warning {{Conversion}} } void nestedCollections(NSArray *> *mat, NSArray *row) { id temp = row; [mat contains: temp]; // expected-warning {{Conversion}} } void testMistmatchedTypeCast(MutableArray *a) { MutableArray *b = (MutableArray *)a; [b addObject: [[NSNumber alloc] init]]; id c = (UnrelatedType *)a; [c addObject: [[NSNumber alloc] init]]; [c addObject: [[NSString alloc] init]]; } void returnCollectionToIdVariable(NSArray *> *arr) { NSArray *erased = arr; id a = [erased firstObject]; NSArray *res = a; // expected-warning {{Conversion}} } void eraseSpecialization(NSArray *> *arr) { NSArray *erased = arr; NSArray* a = [erased firstObject]; NSArray *res = a; // expected-warning {{Conversion}} } void returnToUnrelatedType(NSArray *> *arr) { NSArray *erased = arr; NSSet* a = [erased firstObject]; // expected-warning {{Object has a dynamic type 'NSArray *' which is incompatible with static type 'NSSet *'}} (void)a; } void returnToIdVariable(NSArray *arr) { NSArray *erased = arr; id a = [erased firstObject]; NSNumber *res = a; // expected-warning {{Object has a dynamic type 'NSString *' which is incompatible with static type 'NSNumber *'}} } @interface UnrelatedTypeGeneric : NSObject - (void)takesType:(T)v; @end void testGetMostInformativeDerivedForId(NSArray *a, UnrelatedTypeGeneric *b) { id idB = b; a = idB; // expected-warning {{Conversion from value of type 'UnrelatedTypeGeneric *' to incompatible type 'NSArray *'}} // rdar://problem/26086914 crash here caused by symbolic type being unrelated // to compile-time source type of cast. id x = a; // Compile-time type is NSArray<>, Symbolic type is UnrelatedTypeGeneric<>. [x takesType:[[NSNumber alloc] init]]; // expected-warning {{Conversion from value of type 'NSNumber *' to incompatible type 'NSString *'}} } void testArgumentAfterUpcastToRootWithCovariantTypeParameter(NSArray *allStrings, NSNumber *number) { NSArray *allObjects = allStrings; // no-warning NSArray *moreObjects = [allObjects arrayByAddingObject:number]; // no-warning } void testArgumentAfterUpcastWithCovariantTypeParameter(NSArray *allMutableStrings, NSNumber *number) { NSArray *allStrings = allMutableStrings; // no-warning id numberAsId = number; NSArray *moreStrings = [allStrings arrayByAddingObject:numberAsId]; // Sema: expected-warning {{Object has a dynamic type 'NSNumber *' which is incompatible with static type 'NSString *'}} } void testArgumentAfterCastToUnspecializedWithCovariantTypeParameter(NSArray *allMutableStrings, NSNumber *number) { NSArray *allStrings = allMutableStrings; // no-warning id numberAsId = number; NSArray *moreStringsUnspecialized = [allStrings arrayByAddingObject:numberAsId]; // no-warning // Ideally the analyzer would warn here. NSArray *moreStringsSpecialized = [allStrings arrayByAddingObject:numberAsId]; } void testCallToMethodWithCovariantParameterOnInstanceOfSubclassWithInvariantParameter(NSMutableArray *mutableArrayOfMutableStrings, NSString *someString) { NSArray *arrayOfStrings = mutableArrayOfMutableStrings; [arrayOfStrings containsObject:someString]; // no-warning }