Index: cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h +++ cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h @@ -260,9 +260,11 @@ RefCountBug leakWithinFunction{this, RefCountBug::LeakWithinFunction}; RefCountBug leakAtReturn{this, RefCountBug::LeakAtReturn}; + CheckerProgramPointTag DeallocSentTag{this, "DeallocSent"}; + CheckerProgramPointTag CastFailTag{this, "DynamicCastFail"}; + mutable std::unique_ptr Summaries; public: - static constexpr const char *DeallocTagDescription = "DeallocSent"; /// Track Objective-C and CoreFoundation objects. bool TrackObjCAndCFObjects = false; @@ -361,6 +363,14 @@ CheckerContext &Ctx, ExplodedNode *Pred = nullptr) const; + const CheckerProgramPointTag &getDeallocSentTag() const { + return DeallocSentTag; + } + + const CheckerProgramPointTag &getCastFailTag() const { + return CastFailTag; + } + private: /// Perform the necessary checks and state adjustments at the end of the /// function. Index: cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp @@ -575,7 +575,6 @@ // Helper tag for providing diagnostics: indicate whether dealloc was sent // at this location. - static CheckerProgramPointTag DeallocSentTag(this, DeallocTagDescription); bool DeallocSent = false; for (unsigned idx = 0, e = CallOrMsg.getNumArgs(); idx != e; ++idx) { @@ -903,8 +902,7 @@ // Assume that output is zero on the other branch. NullOutputState = NullOutputState->BindExpr( CE, LCtx, C.getSValBuilder().makeNull(), /*Invalidate=*/false); - - C.addTransition(NullOutputState); + C.addTransition(NullOutputState, &CastFailTag); // And on the original branch assume that both input and // output are non-zero. Index: cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h +++ cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h @@ -37,12 +37,18 @@ }; RefCountBug(const CheckerBase *checker, RefCountBugType BT); StringRef getDescription() const; + RefCountBugType getBugType() const { return BT; } + const CheckerBase *getChecker() const { + return Checker; + } + private: RefCountBugType BT; + const CheckerBase *Checker; static StringRef bugTypeToName(RefCountBugType BT); }; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp @@ -66,7 +66,7 @@ RefCountBug::RefCountBug(const CheckerBase *Checker, RefCountBugType BT) : BugType(Checker, bugTypeToName(BT), categories::MemoryRefCount, /*SupressOnSink=*/BT == LeakWithinFunction || BT == LeakAtReturn), - BT(BT) {} + BT(BT), Checker(Checker) {} static bool isNumericLiteralExpression(const Expr *E) { // FIXME: This set of cases was copied from SemaExprObjC. @@ -423,6 +423,8 @@ BugReporterContext &BRC, BugReport &BR) { const auto &BT = static_cast(BR.getBugType()); + const auto *Checker = + static_cast(BT.getChecker()); bool IsFreeUnowned = BT.getBugType() == RefCountBug::FreeNotOwned || BT.getBugType() == RefCountBug::DeallocNotOwned; @@ -509,8 +511,12 @@ bool DeallocSent = false; const ProgramPointTag *Tag = N->getLocation().getTag(); - if (Tag && Tag->getTagDescription().contains( - RetainCountChecker::DeallocTagDescription)) { + + if (Tag == &Checker->getCastFailTag()) { + os << "Assuming dynamic cast returns null due to type mismatch"; + } + + if (Tag == &Checker->getDeallocSentTag()) { // We only have summaries attached to nodes after evaluating CallExpr and // ObjCMessageExprs. const Stmt *S = N->getLocation().castAs().getStmt(); Index: cfe/trunk/test/Analysis/osobject-retain-release.cpp =================================================================== --- cfe/trunk/test/Analysis/osobject-retain-release.cpp +++ cfe/trunk/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();