Index: lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp +++ lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -381,6 +381,37 @@ // Checking if from and to are the same classes modulo specialization. if (From->getInterfaceDecl()->getCanonicalDecl() == To->getInterfaceDecl()->getCanonicalDecl()) { + // If from has more __kindof specs, we drop all the info and trust from + // instead. This is because __kindof specs are usually at API borders, + // which is the only place where __kindof specs are required to be present. + // We do not enforce __kindof specs within API implementations because + // that'd break existing code, so putting __kindof at the border is enough + // for people to express their intent. + if (From->isSpecialized()) { + ArrayRef FromArgs = From->getObjectType()->getTypeArgs(); + size_t N = FromArgs.size(); + assert(N > 0); + if (MostInformativeCandidate->isUnspecialized()) { + for (size_t I = 0; I < N; ++I) + if (const auto *FromOT = + FromArgs[I]->getAs()) + if (FromOT->getObjectType()->isKindOfType()) + return From; + } else { + ArrayRef MostArgs = + MostInformativeCandidate->getObjectType()->getTypeArgs(); + assert(N == MostArgs.size() && + "Objective-C has no partial specializations!"); + for (size_t I = 0; I < N; ++I) + if (const auto *FromOT = + FromArgs[I]->getAs()) + if (const auto *MostOT = + MostArgs[I]->getAs()) + if (FromOT->getObjectType()->isKindOfType()) + if (!MostOT->getObjectType()->isKindOfType()) + return From; + } + } if (To->isSpecialized()) { assert(MostInformativeCandidate->isSpecialized()); return MostInformativeCandidate; Index: test/Analysis/generics.m =================================================================== --- test/Analysis/generics.m +++ test/Analysis/generics.m @@ -390,6 +390,25 @@ [arrayOfStrings containsObject:someString]; // no-warning } +NSArray *getStringMutableArray() { + NSMutableArray *arr = + [[NSMutableArray alloc] init]; + return arr; +} + +NSArray<__kindof NSString *> *getKindofStringMutableArray() { + NSMutableArray *arr = + [[NSMutableArray alloc] init]; + return arr; +} + +void testKindofPropagation() { + NSArray *arr1 = + (NSArray *)getKindofStringMutableArray(); // no-warning + NSArray *arr2 = + (NSArray *)getStringMutableArray(); // expected-warning{{Conversion from value of type 'NSMutableArray *' to incompatible type 'NSArray *'}} +} + // CHECK: diagnostics // CHECK-NEXT: // CHECK-NEXT: @@ -7140,4 +7159,324 @@ // 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: line406 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line406 +// CHECK-NEXT: col9 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line408 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line408 +// 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: line408 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line408 +// CHECK-NEXT: col9 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line409 +// CHECK-NEXT: col37 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line409 +// 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: line409 +// CHECK-NEXT: col37 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line409 +// CHECK-NEXT: col37 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line409 +// 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: 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: line396 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line396 +// 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: line396 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line396 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line396 +// 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: line409 +// CHECK-NEXT: col37 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line409 +// CHECK-NEXT: col37 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line409 +// 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: line409 +// CHECK-NEXT: col37 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line409 +// CHECK-NEXT: col57 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line409 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line409 +// 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: line409 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line409 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line409 +// 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_offset4 +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line409 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: