Index: lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp +++ lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -46,6 +46,77 @@ REGISTER_MAP_WITH_PROGRAMSTATE(MostSpecializedTypeArgsMap, SymbolRef, const ObjCObjectPointerType *) +// In code that uses Objective-C lightweight generics, we need to track __kindof +// specifiers across chains of casts. The easiest way to track them is to store +// a pointer type that wears all of the __kindof specifiers that historically +// appeared during these casts. For example, if we see Foo being casted to +// Foo<__kindof X, Y> and in another place to Foo, then we'd +// store Foo<__kindof X, __kindof Y> in our map. This is because we don't +// require developers to add __kindof specifiers in all the places where +// they would ideally be necessary. An additional benefit of storing auxiliary +// kindofs directly in the type, instead of a separate structure, would be that +// we can put that type object directly into ASTContext's +// canAssignObjCInterfaces() method, without needing to teach it how to +// deal with our custom data structures. +// This function accumulates __kindof specifiers from OtherPtrTy into +// CurrentPtrTy and returns the resulting type. +static const ObjCObjectPointerType * +relaxWithKindOfs(const ObjCObjectPointerType *CurrentPtrTy, + const ObjCObjectPointerType *OtherPtrTy, ASTContext &C) { + // Don't try to merge __kindofs from a completely incompatible type. + if (CurrentPtrTy->isUnspecialized() || OtherPtrTy->isUnspecialized() || + (!C.canAssignObjCInterfaces(CurrentPtrTy, OtherPtrTy) && + !C.canAssignObjCInterfaces(OtherPtrTy, CurrentPtrTy))) + return CurrentPtrTy; + + const ObjCObjectType *CurrentObjTy = CurrentPtrTy->getObjectType(); + const ObjCObjectType *OtherObjTy = OtherPtrTy->getObjectType(); + ArrayRef CurrentArgs = CurrentObjTy->getTypeArgs(); + ArrayRef OtherArgs = OtherObjTy->getTypeArgs(); + + size_t E = CurrentArgs.size(); + assert(E == OtherArgs.size() && "Types were supposed to be compatible!"); + + llvm::SmallVector RelaxedTypeArgs; + + for (size_t I = 0; I < E; ++I) { + + const auto *CurrentArgPtrTy = + CurrentArgs[I]->getAs(); + const auto *OtherArgPtrTy = + OtherArgs[I]->getAs(); + + if (!CurrentArgPtrTy || !OtherArgPtrTy) { + RelaxedTypeArgs.push_back(CurrentArgs[I]); + continue; + } + + // Merge __kindofs recursively on type arguments of type arguments. + const auto *RelaxedOnArgsArgPtrTy = + relaxWithKindOfs(CurrentArgPtrTy, OtherArgPtrTy, C); + + bool RelaxedShouldBeKindOf = + RelaxedOnArgsArgPtrTy->isKindOfType() || OtherArgPtrTy->isKindOfType(); + + const ObjCObjectType *RelaxedOnArgsArgObjTy = + RelaxedOnArgsArgPtrTy->getObjectType(); + + QualType RelaxedArgObjTy = C.getObjCObjectType( + RelaxedOnArgsArgObjTy->getBaseType(), + RelaxedOnArgsArgObjTy->getTypeArgs(), + RelaxedOnArgsArgObjTy->getProtocols(), RelaxedShouldBeKindOf); + + RelaxedTypeArgs.push_back(C.getObjCObjectPointerType(RelaxedArgObjTy)); + } + + QualType RelaxedObjTy = C.getObjCObjectType( + CurrentObjTy->getBaseType(), RelaxedTypeArgs, + CurrentObjTy->getProtocols(), CurrentObjTy->isKindOfType()); + + return C.getObjCObjectPointerType(RelaxedObjTy) + ->castAs(); +} + namespace { class DynamicTypePropagation: public Checker< check::PreCall, @@ -423,7 +494,11 @@ static const ObjCObjectPointerType * getMostInformativeDerivedClass(const ObjCObjectPointerType *From, const ObjCObjectPointerType *To, ASTContext &C) { - return getMostInformativeDerivedClassImpl(From, To, To, C); + const ObjCObjectPointerType *Ret = + getMostInformativeDerivedClassImpl(From, To, To, C); + Ret = relaxWithKindOfs(Ret, From, C); + Ret = relaxWithKindOfs(Ret, To, C); + return Ret; } /// Inputs: @@ -489,6 +564,13 @@ // Case (3) if (C.canAssignObjCInterfaces(StaticLowerBound, *Current)) { + const ObjCObjectPointerType *Relaxed = *Current; + Relaxed = relaxWithKindOfs(Relaxed, StaticLowerBound, C); + Relaxed = relaxWithKindOfs(Relaxed, StaticUpperBound, C); + if (Relaxed != *Current) { + State = State->set(Sym, Relaxed); + return true; + } return false; } Index: test/Analysis/generics.m =================================================================== --- test/Analysis/generics.m +++ test/Analysis/generics.m @@ -390,6 +390,39 @@ [arrayOfStrings containsObject:someString]; // no-warning } +NSArray *getStringMutableArray() { + NSMutableArray *arr = [[NSMutableArray alloc] init]; + return arr; +} + +NSArray *getStringMutableArraySpecialized() { + NSMutableArray *arr = + [[NSMutableArray alloc] init]; + return arr; +} + +NSArray<__kindof NSString *> *getKindofStringMutableArray() { + NSMutableArray *arr = [[NSMutableArray alloc] init]; + return arr; +} + +NSArray<__kindof NSString *> *getKindofStringMutableArraySpecialized() { + NSMutableArray *arr = + [[NSMutableArray alloc] init]; + return arr; +} + +void testKindofPropagation() { + NSArray *arr1 = + (NSArray *)getKindofStringMutableArray(); // no-warning + NSArray *arr2 = + (NSArray *)getKindofStringMutableArraySpecialized(); // no-warning + NSArray *arr3 = + (NSArray *)getStringMutableArray(); // expected-warning{{Conversion from value of type 'NSMutableArray *' to incompatible type 'NSArray *'}} + NSArray *arr4 = + (NSArray *)getStringMutableArraySpecialized(); // expected-warning{{Conversion from value of type 'NSMutableArray *' to incompatible type 'NSArray *'}} +} + // CHECK: diagnostics // CHECK-NEXT: // CHECK-NEXT: @@ -7140,4 +7173,644 @@ // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: path +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line416 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line416 +// CHECK-NEXT: col9 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line420 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line420 +// CHECK-NEXT: col9 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line420 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line420 +// CHECK-NEXT: col9 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line421 +// CHECK-NEXT: col37 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line421 +// CHECK-NEXT: col57 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line421 +// CHECK-NEXT: col37 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line421 +// CHECK-NEXT: col37 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line421 +// CHECK-NEXT: col59 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Calling 'getStringMutableArray' +// CHECK-NEXT: message +// CHECK-NEXT: Calling 'getStringMutableArray' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line393 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: depth1 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Entered call from 'testKindofPropagation' +// CHECK-NEXT: message +// CHECK-NEXT: Entered call from 'testKindofPropagation' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line393 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line393 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line394 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line394 +// CHECK-NEXT: col16 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line394 +// CHECK-NEXT: col37 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line394 +// CHECK-NEXT: col37 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line394 +// CHECK-NEXT: col65 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth1 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Type 'NSMutableArray<NSString *> *' is inferred from implicit cast (from 'NSMutableArray *' to 'NSMutableArray<NSString *> *') +// CHECK-NEXT: message +// CHECK-NEXT: Type 'NSMutableArray<NSString *> *' is inferred from implicit cast (from 'NSMutableArray *' to 'NSMutableArray<NSString *> *') +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line394 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line394 +// CHECK-NEXT: col16 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line395 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line395 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line421 +// CHECK-NEXT: col37 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line421 +// CHECK-NEXT: col37 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line421 +// CHECK-NEXT: col59 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Returning from 'getStringMutableArray' +// CHECK-NEXT: message +// CHECK-NEXT: Returning from 'getStringMutableArray' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line421 +// CHECK-NEXT: col37 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line421 +// CHECK-NEXT: col57 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line421 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line421 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line421 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line421 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line421 +// CHECK-NEXT: col59 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Conversion from value of type 'NSMutableArray<NSString *> *' to incompatible type 'NSArray<NSMutableString *> *' +// CHECK-NEXT: message +// CHECK-NEXT: Conversion from value of type 'NSMutableArray<NSString *> *' to incompatible type 'NSArray<NSMutableString *> *' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: descriptionConversion from value of type 'NSMutableArray<NSString *> *' to incompatible type 'NSArray<NSMutableString *> *' +// CHECK-NEXT: categoryCore Foundation/Objective-C +// CHECK-NEXT: typeGenerics +// CHECK-NEXT: check_namecore.DynamicTypePropagation +// CHECK-NEXT: +// CHECK-NEXT: issue_hash_content_of_line_in_context555936f27ea22a9caa1046e8c3b7aff2 +// CHECK-NEXT: issue_context_kindfunction +// CHECK-NEXT: issue_contexttestKindofPropagation +// CHECK-NEXT: issue_hash_function_offset6 +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line421 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: path +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line416 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line416 +// CHECK-NEXT: col9 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line422 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line422 +// CHECK-NEXT: col9 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line422 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line422 +// CHECK-NEXT: col9 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line423 +// CHECK-NEXT: col37 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line423 +// CHECK-NEXT: col68 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line423 +// CHECK-NEXT: col37 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line423 +// CHECK-NEXT: col37 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line423 +// CHECK-NEXT: col70 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Calling 'getStringMutableArraySpecialized' +// CHECK-NEXT: message +// CHECK-NEXT: Calling 'getStringMutableArraySpecialized' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line398 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: depth1 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Entered call from 'testKindofPropagation' +// CHECK-NEXT: message +// CHECK-NEXT: Entered call from 'testKindofPropagation' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line398 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line398 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line399 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line399 +// CHECK-NEXT: col16 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line399 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line399 +// CHECK-NEXT: col16 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line401 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line401 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line401 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line401 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line401 +// CHECK-NEXT: col12 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth1 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Type 'NSMutableArray<NSString *> *' is inferred from implicit cast (from 'NSMutableArray<NSString *> *' to 'NSArray<NSString *> *') +// CHECK-NEXT: message +// CHECK-NEXT: Type 'NSMutableArray<NSString *> *' is inferred from implicit cast (from 'NSMutableArray<NSString *> *' to 'NSArray<NSString *> *') +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line423 +// CHECK-NEXT: col37 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line423 +// CHECK-NEXT: col37 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line423 +// CHECK-NEXT: col70 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Returning from 'getStringMutableArraySpecialized' +// CHECK-NEXT: message +// CHECK-NEXT: Returning from 'getStringMutableArraySpecialized' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line423 +// CHECK-NEXT: col37 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line423 +// CHECK-NEXT: col68 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line423 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line423 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line423 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line423 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line423 +// CHECK-NEXT: col70 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Conversion from value of type 'NSMutableArray<NSString *> *' to incompatible type 'NSArray<NSMutableString *> *' +// CHECK-NEXT: message +// CHECK-NEXT: Conversion from value of type 'NSMutableArray<NSString *> *' to incompatible type 'NSArray<NSMutableString *> *' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: descriptionConversion from value of type 'NSMutableArray<NSString *> *' to incompatible type 'NSArray<NSMutableString *> *' +// CHECK-NEXT: categoryCore Foundation/Objective-C +// CHECK-NEXT: typeGenerics +// CHECK-NEXT: check_namecore.DynamicTypePropagation +// CHECK-NEXT: +// CHECK-NEXT: issue_hash_content_of_line_in_context841209dc237ad76f5a39b45facdd1ff9 +// CHECK-NEXT: issue_context_kindfunction +// CHECK-NEXT: issue_contexttestKindofPropagation +// CHECK-NEXT: issue_hash_function_offset8 +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line423 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: