Index: lib/Analysis/RetainSummaryManager.cpp =================================================================== --- lib/Analysis/RetainSummaryManager.cpp +++ lib/Analysis/RetainSummaryManager.cpp @@ -228,29 +228,36 @@ const RetainSummary * RetainSummaryManager::getSummaryForOSObject(const FunctionDecl *FD, StringRef FName, QualType RetTy) { + assert(TrackOSObjects && + "Requesting a summary for an OSObject but OSObjects are not tracked"); + if (RetTy->isPointerType()) { const CXXRecordDecl *PD = RetTy->getPointeeType()->getAsCXXRecordDecl(); if (PD && isOSObjectSubclass(PD)) { - if (const IdentifierInfo *II = FD->getIdentifier()) { - StringRef FuncName = II->getName(); - if (isOSObjectDynamicCast(FuncName) || isOSObjectThisCast(FuncName)) - return getDefaultSummary(); - - // All objects returned with functions *not* starting with - // get, or iterators, are returned at +1. - if ((!FuncName.startswith("get") && !FuncName.startswith("Get")) || - isOSIteratorSubclass(PD)) { - return getOSSummaryCreateRule(FD); - } else { - return getOSSummaryGetRule(FD); - } + if (isOSObjectDynamicCast(FName) || isOSObjectThisCast(FName)) + return getDefaultSummary(); + + // TODO: Add support for the slightly common *Matching(table) idiom. + // Cf. IOService::nameMatching() etc. - these function have an unusual + // contract of returning at +0 or +1 depending on their last argument. + if (FName.endswith("Matching")) { + return getPersistentStopSummary(); + } + + // All objects returned with functions *not* starting with 'get', + // or iterators, are returned at +1. + if ((!FName.startswith("get") && !FName.startswith("Get")) || + isOSIteratorSubclass(PD)) { + return getOSSummaryCreateRule(FD); + } else { + return getOSSummaryGetRule(FD); } } } if (const auto *MD = dyn_cast(FD)) { const CXXRecordDecl *Parent = MD->getParent(); - if (TrackOSObjects && Parent && isOSObjectSubclass(Parent)) { + if (Parent && isOSObjectSubclass(Parent)) { if (FName == "release" || FName == "taggedRelease") return getOSSummaryReleaseRule(FD); Index: test/Analysis/osobject-retain-release.cpp =================================================================== --- test/Analysis/osobject-retain-release.cpp +++ test/Analysis/osobject-retain-release.cpp @@ -679,3 +679,26 @@ obj->release(); obj->release(); } + +class IOService { +public: + OSObject *somethingMatching(OSObject *table = 0); +}; + +OSObject *testSuppressionForMethodsEndingWithMatching(IOService *svc, + OSObject *table = 0) { + // This probably just passes table through. We should probably not make + // ptr1 definitely equal to table, but we should not warn about leaks. + OSObject *ptr1 = svc->somethingMatching(table); // no-warning + + // FIXME: This, however, should follow the Create Rule regardless. + // We should warn about the leak here. + OSObject *ptr2 = svc->somethingMatching(); // no-warning + + if (!table) + table = OSTypeAlloc(OSArray); + + // This function itself ends with "Matching"! Do not warn when we're + // returning from it at +0. + return table; // no-warning +}