Index: clang/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h +++ clang/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h @@ -636,9 +636,44 @@ InitializeMethodSummaries(); } - bool canEval(const CallExpr *CE, - const FunctionDecl *FD, - bool &hasTrustedImplementationAnnotation); + class BehaviorSummary { + bool IsIdentity; + bool IsNoop; + bool IsNonZero; + + public: + BehaviorSummary(bool IsIdentity, + bool IsNoop, + bool IsNonZero) + : IsIdentity(IsIdentity), + IsNoop(IsNoop), + IsNonZero(IsNonZero) {} + + bool isIdentity() { + return IsIdentity; + } + + bool isNonZero() { + return IsNonZero; + } + + bool isNoop() { + return IsNoop; + } + + // Function does not do anything. + static const BehaviorSummary NoOp; + + // Function returns the first argument. + static const BehaviorSummary Identity; + + // Function is an identity, and both input and output are + // guaranteed to be non zero. + static const BehaviorSummary IdentityNonZero; + }; + + Optional canEval(const CallExpr *CE, const FunctionDecl *FD, + bool &hasTrustedImplementationAnnotation); bool isTrustedReferenceCountImplementation(const FunctionDecl *FD); Index: clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp @@ -774,14 +774,17 @@ const LocationContext *LCtx = C.getLocationContext(); + Optional BSmr = + SmrMgr.canEval(CE, FD, hasTrustedImplementationAnnotation); + // See if it's one of the specific functions we know how to eval. - if (!SmrMgr.canEval(CE, FD, hasTrustedImplementationAnnotation)) + if (!BSmr) return false; // Bind the return value. // For now, all the functions which we can evaluate and which take // at least one argument are identities. - if (CE->getNumArgs() >= 1) { + if (BSmr->isIdentity()) { SVal RetVal = state->getSVal(CE->getArg(0), LCtx); // If the receiver is unknown or the function has @@ -794,6 +797,12 @@ SVB.conjureSymbolVal(nullptr, CE, LCtx, ResultTy, C.blockCount()); } state = state->BindExpr(CE, LCtx, RetVal, false); + + if (BSmr->isNonZero()) { + if (auto L = RetVal.getAs()) + // Assume that both input and output has to be non-zero. + state = state->assume(*L, /*Assumption=*/true); + } } C.addTransition(state); Index: clang/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp +++ clang/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp @@ -515,20 +515,21 @@ return hasRCAnnotation(FD, "rc_ownership_trusted_implementation"); } -bool RetainSummaryManager::canEval(const CallExpr *CE, - const FunctionDecl *FD, - bool &hasTrustedImplementationAnnotation) { +Optional +RetainSummaryManager::canEval(const CallExpr *CE, const FunctionDecl *FD, + bool &hasTrustedImplementationAnnotation) { IdentifierInfo *II = FD->getIdentifier(); if (!II) - return false; + return None; StringRef FName = II->getName(); FName = FName.substr(FName.find_first_not_of('_')); QualType ResultTy = CE->getCallReturnType(Ctx); if (ResultTy->isObjCIdType()) { - return II->isStr("NSMakeCollectable"); + if (II->isStr("NSMakeCollectable")) + return BehaviorSummary::Identity; } else if (ResultTy->isPointerType()) { // Handle: (CF|CG|CV)Retain // CFAutorelease @@ -536,31 +537,32 @@ if (cocoa::isRefType(ResultTy, "CF", FName) || cocoa::isRefType(ResultTy, "CG", FName) || cocoa::isRefType(ResultTy, "CV", FName)) - return isRetain(FD, FName) || isAutorelease(FD, FName) || - isMakeCollectable(FName); + if (isRetain(FD, FName) || isAutorelease(FD, FName) || + isMakeCollectable(FName)) + return BehaviorSummary::Identity; // Process OSDynamicCast: should just return the first argument. // For now, treating the cast as a no-op, and disregarding the case where // the output becomes null due to the type mismatch. if (TrackOSObjects && FName == "safeMetaCast") { - return true; + return BehaviorSummary::IdentityNonZero; } const FunctionDecl* FDD = FD->getDefinition(); if (FDD && isTrustedReferenceCountImplementation(FDD)) { hasTrustedImplementationAnnotation = true; - return true; + return BehaviorSummary::Identity; } } if (const auto *MD = dyn_cast(FD)) { const CXXRecordDecl *Parent = MD->getParent(); if (TrackOSObjects && Parent && isOSObjectSubclass(Parent)) - return FName == "release" || FName == "retain"; + if (FName == "release" || FName == "retain") + return BehaviorSummary::NoOp; } - return false; - + return None; } const RetainSummary * @@ -1002,3 +1004,20 @@ } return CE; } + +const RetainSummaryManager::BehaviorSummary + RetainSummaryManager::BehaviorSummary::NoOp = {/*Identity=*/false, + /*IsNoop=*/true, + /*IsNonZero=*/false}; + +const RetainSummaryManager::BehaviorSummary + RetainSummaryManager::BehaviorSummary::Identity = {/*Identity=*/true, + /*IsNoop=*/false, + /*IsNonZero=*/false}; + +const RetainSummaryManager::BehaviorSummary + RetainSummaryManager::BehaviorSummary::IdentityNonZero = { + /*Identity=*/true, + /*IsNoop=*/false, + /*IsNonZero=*/true}; + Index: clang/test/Analysis/osobject-retain-release.cpp =================================================================== --- clang/test/Analysis/osobject-retain-release.cpp +++ clang/test/Analysis/osobject-retain-release.cpp @@ -17,6 +17,8 @@ virtual void release() {}; virtual ~OSObject(){} + unsigned int foo() { return 42; } + static OSObject *generateObject(int); static const OSMetaClass * const metaClass; @@ -44,7 +46,7 @@ }; struct OSMetaClassBase { - static OSObject *safeMetaCast(const OSObject *inst, const OSMetaClass *meta); + static OS_RETURNS_NOT_RETAINED OSObject *safeMetaCast(const OSObject *inst, const OSMetaClass *meta); }; void check_no_invalidation() { @@ -78,6 +80,18 @@ arr->release(); } +unsigned int check_dynamic_cast_no_null(OSObject *obj) { + OSArray *arr = OSDynamicCast(OSArray, obj); + if (arr) { + return arr->getCount(); + } else { + + // The fact that dynamic cast has failed should not imply that + // the input object was null. + return obj->foo(); // no-warning + } +} + void check_dynamic_cast_null_check() { OSArray *arr = OSDynamicCast(OSArray, OSObject::generateObject(1)); if (!arr)