Index: lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp +++ lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp @@ -45,7 +45,7 @@ void RefVal::print(raw_ostream &Out) const { if (!T.isNull()) - Out << "Tracked " << T.getAsString() << '/'; + Out << "Tracked " << T.getAsString() << " | "; switch (getKind()) { default: llvm_unreachable("Invalid RefVal kind"); Index: lib/StaticAnalyzer/Core/RetainSummaryManager.cpp =================================================================== --- lib/StaticAnalyzer/Core/RetainSummaryManager.cpp +++ lib/StaticAnalyzer/Core/RetainSummaryManager.cpp @@ -8,8 +8,8 @@ //===----------------------------------------------------------------------===// // // This file defines summaries implementation for retain counting, which -// implements a reference count checker for Core Foundation and Cocoa -// on (Mac OS X). +// implements a reference count checker for Core Foundation, Cocoa +// and OSObject (on Mac OS X). // //===----------------------------------------------------------------------===// @@ -94,6 +94,22 @@ return FName.contains_lower("MakeCollectable"); } +/// A function is OSObject related if it is declared on a subclass +/// of OSObject, or any of the parameters is a subclass of an OSObject. +static bool isOSObjectRelated(const CXXMethodDecl *MD) { + if (isOSObjectSubclass(MD->getParent())) + return true; + + for (ParmVarDecl *Param : MD->parameters()) { + QualType PT = Param->getType(); + if (CXXRecordDecl *RD = PT->getPointeeType()->getAsCXXRecordDecl()) + if (isOSObjectSubclass(RD)) + return true; + } + + return false; +} + const RetainSummary * RetainSummaryManager::generateSummary(const FunctionDecl *FD, bool &AllowAnnotations) { @@ -322,12 +338,10 @@ } } - if (isa(FD)) { - - // Stop tracking arguments passed to C++ methods, as those might be - // wrapping smart pointers. - return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, StopTracking, - DoNothing); + if (const auto *MD = dyn_cast(FD)) { + if (!(TrackOSObjects && isOSObjectRelated(MD))) + return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, StopTracking, + DoNothing); } return getDefaultSummary(); @@ -642,6 +656,8 @@ if (D->hasAttr()) return RetEffect::MakeNotOwned(RetEffect::CF); + else if (hasRCAnnotation(D, "rc_ownership_returns_not_retained")) + return RetEffect::MakeNotOwned(RetEffect::Generalized); return None; } Index: test/Analysis/osobject-retain-release.cpp =================================================================== --- test/Analysis/osobject-retain-release.cpp +++ test/Analysis/osobject-retain-release.cpp @@ -2,6 +2,11 @@ struct OSMetaClass; +#define TRUSTED __attribute__((annotate("rc_ownership_trusted_implementation"))) +#define OS_CONSUME TRUSTED __attribute__((annotate("rc_ownership_consumed"))) +#define OS_RETURNS_RETAINED TRUSTED __attribute__((annotate("rc_ownership_returns_retained"))) +#define OS_RETURNS_NOT_RETAINED TRUSTED __attribute__((annotate("rc_ownership_returns_not_retained"))) + #define OSTypeID(type) (type::metaClass) #define OSDynamicCast(type, inst) \ @@ -21,14 +26,53 @@ unsigned int getCount(); static OSArray *withCapacity(unsigned int capacity); + static void consumeArray(OS_CONSUME OSArray * array); + + static OSArray* consumeArrayHasCode(OS_CONSUME OSArray * array) { + return nullptr; + } + + static OS_RETURNS_NOT_RETAINED OSArray *MaskedGetter(); + static OS_RETURNS_RETAINED OSArray *getOoopsActuallyCreate(); + static const OSMetaClass * const metaClass; }; +struct OtherStruct { + static void doNothingToArray(OSArray *array); +}; + struct OSMetaClassBase { static OSObject *safeMetaCast(const OSObject *inst, const OSMetaClass *meta); }; +void check_no_invalidation() { + OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to function 'withCapacity' returns an OSObject of type struct OSArray * with a +1 retain count}} + OtherStruct::doNothingToArray(arr); +} // expected-warning{{Potential leak of an object stored into 'arr'}} + // expected-note@-1{{Object leaked}} + +void check_rc_consumed() { + OSArray *arr = OSArray::withCapacity(10); + OSArray::consumeArray(arr); +} + +void check_rc_consume_temporary() { + OSArray::consumeArray(OSArray::withCapacity(10)); +} + +void check_rc_getter() { + OSArray *arr = OSArray::MaskedGetter(); + (void)arr; +} + +void check_rc_create() { + OSArray *arr = OSArray::getOoopsActuallyCreate(); + arr->release(); +} + + void check_dynamic_cast() { OSArray *arr = OSDynamicCast(OSArray, OSObject::generateObject(1)); arr->release(); @@ -80,11 +124,6 @@ OSArray *getArraySourceUnknown(); }; -//unsigned int leak_on_create_no_release(ArrayOwner *owner) { - //OSArray *myArray = - -//} - unsigned int no_warning_on_getter(ArrayOwner *owner) { OSArray *arr = owner->getArray(); return arr->getCount();