Index: clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h =================================================================== --- clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h +++ clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h @@ -263,7 +263,8 @@ mutable std::unique_ptr Summaries; public: - static constexpr const char *DeallocTagDescription = "DeallocSent"; + static constexpr const char *DeallocTagDescription = "DeallocSentRCC"; + static constexpr const char *DynamicCastFailTagDescription = "CastFailRCC"; /// Track Objective-C and CoreFoundation objects. bool TrackObjCAndCFObjects = false; Index: clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp @@ -905,7 +905,9 @@ NullOutputState = NullOutputState->BindExpr( CE, LCtx, C.getSValBuilder().makeNull(), /*Invalidate=*/false); - C.addTransition(NullOutputState); + static CheckerProgramPointTag CastFailTag(this, + DynamicCastFailTagDescription); + C.addTransition(NullOutputState, &CastFailTag); // And on the original branch assume that both input and // output are non-zero. Index: clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp +++ clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp @@ -508,6 +508,12 @@ bool DeallocSent = false; const ProgramPointTag *Tag = N->getLocation().getTag(); + + if (Tag && Tag->getTagDescription().contains( + RetainCountChecker::DynamicCastFailTagDescription)) { + os << "Assuming dynamic cast returns null due to type mismatch"; + } + if (Tag && Tag->getTagDescription().contains( RetainCountChecker::DeallocTagDescription)) { // We only have summaries attached to nodes after evaluating CallExpr and Index: clang/test/Analysis/osobject-retain-release.cpp =================================================================== --- clang/test/Analysis/osobject-retain-release.cpp +++ clang/test/Analysis/osobject-retain-release.cpp @@ -488,7 +488,7 @@ void check_dynamic_cast_null_branch(OSObject *obj) { OSArray *arr1 = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject}} - OSArray *arr = OSDynamicCast(OSArray, obj); + OSArray *arr = OSDynamicCast(OSArray, obj); // expected-note{{Assuming dynamic cast returns null due to type mismatch}} if (!arr) // expected-note{{Taking true branch}} return; // expected-warning{{Potential leak of an object stored into 'arr1'}} // expected-note@-1{{Object leaked}} @@ -499,6 +499,7 @@ OSArray *arr = OSDynamicCast(OSArray, OSObject::generateObject(1)); // expected-note{{Call to method 'OSObject::generateObject' returns an OSObject}} // expected-warning@-1{{Potential leak of an object}} // expected-note@-2{{Object leaked}} + // expected-note@-3{{Assuming dynamic cast returns null due to type mismatch}} if (!arr) return; arr->release();