From 093594938bf5c46a5fb4b7b3ea157eb269d8f885 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Mon, 29 Feb 2016 23:57:10 +0000 Subject: [PATCH] [analyzer] Teach CheckObjCDealloc about Block_release(). It now treats Block_release(b) as a release in addition to [b release]. llvm-svn: 262272 --- .../StaticAnalyzer/Checkers/CheckObjCDealloc.cpp | 48 +++++++++++++++++----- clang/test/Analysis/DeallocMissingRelease.m | 6 +++ .../system-header-simulator-for-objc-dealloc.h | 3 ++ 3 files changed, 47 insertions(+), 10 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index f72a1ae..ea64d9b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -92,12 +92,13 @@ namespace { class ObjCDeallocChecker : public Checker, check::PreObjCMessage, check::PostObjCMessage, + check::PreCall, check::BeginFunction, check::EndFunction, eval::Assume, check::PointerEscape, check::PreStmt> { - mutable IdentifierInfo *NSObjectII, *SenTestCaseII; + mutable IdentifierInfo *NSObjectII, *SenTestCaseII, *Block_releaseII; mutable Selector DeallocSel, ReleaseSel; std::unique_ptr MissingReleaseBugType; @@ -110,6 +111,7 @@ public: BugReporter &BR) const; void checkBeginFunction(CheckerContext &Ctx) const; void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond, @@ -152,6 +154,7 @@ private: const ObjCPropertyDecl * findShadowedPropertyDecl(const ObjCPropertyImplDecl *PropImpl) const; + void transitionToReleaseValue(CheckerContext &C, SymbolRef Value) const; ProgramStateRef removeValueRequiringRelease(ProgramStateRef State, SymbolRef InstanceSym, SymbolRef ValueSym) const; @@ -341,19 +344,26 @@ void ObjCDeallocChecker::checkPreObjCMessage( if (!ReleasedValue) return; - SymbolRef InstanceSym = getInstanceSymbolFromIvarSymbol(ReleasedValue); - if (!InstanceSym) + transitionToReleaseValue(C, ReleasedValue); +} + +/// If we are in -dealloc or -dealloc is on the stack, handle the call if it is +/// call to Block_release(). +void ObjCDeallocChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + const IdentifierInfo *II = Call.getCalleeIdentifier(); + if (II != Block_releaseII) return; - ProgramStateRef InitialState = C.getState(); - ProgramStateRef ReleasedState = - removeValueRequiringRelease(InitialState, InstanceSym, ReleasedValue); + if (Call.getNumArgs() != 1) + return; - if (ReleasedState != InitialState) { - C.addTransition(ReleasedState); - } -} + SymbolRef ReleasedValue = Call.getArgSVal(0).getAsSymbol(); + if (!ReleasedValue) + return; + transitionToReleaseValue(C, ReleasedValue); +} /// If the message was a call to '[super dealloc]', diagnose any missing /// releases. void ObjCDeallocChecker::checkPostObjCMessage( @@ -684,6 +694,7 @@ void ObjCDeallocChecker::initIdentifierInfoAndSelectors( NSObjectII = &Ctx.Idents.get("NSObject"); SenTestCaseII = &Ctx.Idents.get("SenTestCase"); + Block_releaseII = &Ctx.Idents.get("_Block_release"); IdentifierInfo *DeallocII = &Ctx.Idents.get("dealloc"); IdentifierInfo *ReleaseII = &Ctx.Idents.get("release"); @@ -739,6 +750,23 @@ const ObjCPropertyDecl *ObjCDeallocChecker::findShadowedPropertyDecl( return nullptr; } +/// Add a transition noting the release of the given value. +void ObjCDeallocChecker::transitionToReleaseValue(CheckerContext &C, + SymbolRef Value) const { + assert(Value); + SymbolRef InstanceSym = getInstanceSymbolFromIvarSymbol(Value); + if (!InstanceSym) + return; + ProgramStateRef InitialState = C.getState(); + + ProgramStateRef ReleasedState = + removeValueRequiringRelease(InitialState, InstanceSym, Value); + + if (ReleasedState != InitialState) { + C.addTransition(ReleasedState); + } +} + /// Remove the Value requiring a release from the tracked set for /// Instance and return the resultant state. ProgramStateRef ObjCDeallocChecker::removeValueRequiringRelease( diff --git a/clang/test/Analysis/DeallocMissingRelease.m b/clang/test/Analysis/DeallocMissingRelease.m index bb67e436..ff7c4e5 100644 --- a/clang/test/Analysis/DeallocMissingRelease.m +++ b/clang/test/Analysis/DeallocMissingRelease.m @@ -125,6 +125,9 @@ void (^_blockPropertyIvar)(void); } @property (copy) void (^blockProperty)(void); +@property (copy) void (^blockProperty2)(void); +@property (copy) void (^blockProperty3)(void); + @end @implementation MyPropertyClass4 @@ -132,6 +135,9 @@ - (void)dealloc { #if NON_ARC + [_blockProperty2 release]; + Block_release(_blockProperty3); + [super dealloc]; // expected-warning {{The '_blockPropertyIvar' ivar in 'MyPropertyClass4' was copied by a synthesized property but not released before '[super dealloc]'}} #endif } diff --git a/clang/test/Analysis/Inputs/system-header-simulator-for-objc-dealloc.h b/clang/test/Analysis/Inputs/system-header-simulator-for-objc-dealloc.h index 0b9888a..9850aec 100644 --- a/clang/test/Analysis/Inputs/system-header-simulator-for-objc-dealloc.h +++ b/clang/test/Analysis/Inputs/system-header-simulator-for-objc-dealloc.h @@ -27,3 +27,6 @@ typedef signed char BOOL; @end typedef struct objc_selector *SEL; + +void _Block_release(const void *aBlock); +#define Block_release(...) _Block_release((const void *)(__VA_ARGS__)) -- 2.7.4