Index: clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp +++ clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp @@ -306,9 +306,8 @@ }; } // end anonymous namespace -static AllocationInfo -GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N, - SymbolRef Sym) { +static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr, + const ExplodedNode *N, SymbolRef Sym) { const ExplodedNode *AllocationNode = N; const ExplodedNode *AllocationNodeInCurrentOrParentContext = N; const MemRegion *FirstBinding = nullptr; @@ -353,9 +352,9 @@ // Find the last init that was called on the given symbol and store the // init method's location context. if (!InitMethodContext) - if (Optional CEP = N->getLocation().getAs()) { + if (auto CEP = N->getLocation().getAs()) { const Stmt *CE = CEP->getCallExpr(); - if (const ObjCMessageExpr *ME = dyn_cast_or_null(CE)) { + if (const auto *ME = dyn_cast_or_null(CE)) { const Stmt *RecExpr = ME->getInstanceReceiver(); if (RecExpr) { SVal RecV = St->getSVal(RecExpr, NContext); @@ -365,7 +364,7 @@ } } - N = N->pred_empty() ? nullptr : *(N->pred_begin()); + N = N->getFirstPred(); } // If we are reporting a leak of the object that was allocated with alloc, @@ -382,9 +381,11 @@ // If allocation happened in a function different from the leak node context, // do not report the binding. assert(N && "Could not find allocation node"); - if (N->getLocationContext() != LeakContext) { + + if (AllocationNodeInCurrentOrParentContext && + AllocationNodeInCurrentOrParentContext->getLocationContext() != + LeakContext) FirstBinding = nullptr; - } return AllocationInfo(AllocationNodeInCurrentOrParentContext, FirstBinding, @@ -409,8 +410,7 @@ // We are reporting a leak. Walk up the graph to get to the first node where // the symbol appeared, and also get the first VarDecl that tracked object // is stored to. - AllocationInfo AllocI = - GetAllocationSite(BRC.getStateManager(), EndN, Sym); + AllocationInfo AllocI = GetAllocationSite(BRC.getStateManager(), EndN, Sym); const MemRegion* FirstBinding = AllocI.R; BR.markInteresting(AllocI.InterestingMethodContext); @@ -448,11 +448,12 @@ os << (isa(D) ? " is returned from a method " : " is returned from a function "); - if (D->hasAttr()) + if (D->hasAttr()) { os << "that is annotated as CF_RETURNS_NOT_RETAINED"; - else if (D->hasAttr()) + } else if (D->hasAttr()) { os << "that is annotated as NS_RETURNS_NOT_RETAINED"; - else { + // TODO: once the patch is ready, insert a case for OS_RETURNS_NOT_RETAINED + } else { if (const ObjCMethodDecl *MD = dyn_cast(D)) { if (BRC.getASTContext().getLangOpts().ObjCAutoRefCount) { os << "managed by Automatic Reference Counting"; @@ -497,7 +498,8 @@ } } -void CFRefLeakReport::deriveAllocLocation(CheckerContext &Ctx,SymbolRef sym) { +void CFRefLeakReport::deriveAllocLocation(CheckerContext &Ctx, + SymbolRef sym) { // Most bug reports are cached at the location where they occurred. // With leaks, we want to unique them by the location where they were // allocated, and only report a single path. To do this, we need to find @@ -511,7 +513,7 @@ const SourceManager& SMgr = Ctx.getSourceManager(); AllocationInfo AllocI = - GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym); + GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym); AllocNode = AllocI.N; AllocBinding = AllocI.R; Index: clang/test/Analysis/Inputs/expected-plists/retain-release-path-notes.m.plist =================================================================== --- clang/test/Analysis/Inputs/expected-plists/retain-release-path-notes.m.plist +++ clang/test/Analysis/Inputs/expected-plists/retain-release-path-notes.m.plist @@ -4233,12 +4233,12 @@ depth0 extended_message - Object leaked: allocated object is not referenced later in this execution path and has a retain count of +1 + Object leaked: object allocated and stored into 'y' is not referenced later in this execution path and has a retain count of +1 message - Object leaked: allocated object is not referenced later in this execution path and has a retain count of +1 + Object leaked: object allocated and stored into 'y' is not referenced later in this execution path and has a retain count of +1 - descriptionPotential leak of an object + descriptionPotential leak of an object stored into 'y' categoryMemory (Core Foundation/Objective-C) typeLeak check_nameosx.cocoa.RetainCount Index: clang/test/Analysis/osobject-retain-release.cpp =================================================================== --- clang/test/Analysis/osobject-retain-release.cpp +++ clang/test/Analysis/osobject-retain-release.cpp @@ -24,6 +24,8 @@ }; struct OSIterator : public OSObject { + + static const OSMetaClass * const metaClass; }; struct OSArray : public OSObject { @@ -41,7 +43,6 @@ static OS_RETURNS_NOT_RETAINED OSArray *MaskedGetter(); static OS_RETURNS_RETAINED OSArray *getOoopsActuallyCreate(); - static const OSMetaClass * const metaClass; }; @@ -92,6 +93,25 @@ OSArray *getArraySourceUnknown(); }; +OSArray *generateArray() { + return OSArray::withCapacity(10); // expected-note{{Call to function 'withCapacity' returns an OSObject of type struct OSArray * with a +1 retain count}} +} + +unsigned int check_leak_good_error_message() { + unsigned int out; + { + OSArray *leaked = generateArray(); // expected-note{{Calling 'generateArray'}} + // expected-note@-1{{Returning from 'generateArray'}} + out = leaked->getCount(); // expected-warning{{Potential leak of an object stored into 'leaked'}} + // expected-note@-1{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}} + } + return out; +} + +unsigned int check_leak_msg_temporary() { + return generateArray()->getCount(); +} + void check_confusing_getters() { OSArray *arr = OSArray::withCapacity(10); @@ -143,7 +163,7 @@ OSArray *arr1 = OSArray::withCapacity(10); // expected-note{{Call to function 'withCapacity' returns an OSObject}} OSArray *arr = OSDynamicCast(OSArray, obj); if (!arr) // expected-note{{Taking true branch}} - return; // expected-warning{{Potential leak}} + return; // expected-warning{{Potential leak of an object stored into 'arr1'}} // expected-note@-1{{Object leaked}} arr1->release(); } Index: clang/test/Analysis/retain-release-path-notes.m =================================================================== --- clang/test/Analysis/retain-release-path-notes.m +++ clang/test/Analysis/retain-release-path-notes.m @@ -235,7 +235,7 @@ // initZ is not inlined id z = [[MyObj alloc] initZ]; // expected-warning {{Potential leak of an object}} - // expected-note@-1 {{Object leaked: allocated object is not referenced later in this execution path and has a retain count of +1}} + // expected-note@-1 {{Object leaked: object allocated and stored into 'y' is not referenced later in this execution path and has a retain count of +1}} [x release]; [z release];