From 36a5831534de7715ca48235aaa5a991d1ba55f08 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Wed, 12 Oct 2016 23:57:05 +0000 Subject: [PATCH] [analyzer] DeallocChecker: Don't warn about directly-set IBOutlet ivars on macOS On macOS (but not iOS), if an ObjC property has no setter, the nib-loading code for an IBOutlet is documented as directly setting the backing ivar without retaining the value -- even if the property is 'retain'. This resulted in false positives from the DeallocChecker for code that did not release such ivars in -dealloc. To avoid these false positives, treat IBOutlet ivars that back a property without a setter as having an unknown release requirement in macOS. rdar://problem/28507353 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@284084 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../Checkers/CheckObjCDealloc.cpp | 31 ++++++++ test/Analysis/DeallocMissingRelease.m | 71 ++++++++++++++++++- 2 files changed, 101 insertions(+), 1 deletion(-) diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index 4fece1c7ff..1c8ca0f429 100644 --- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -34,6 +34,7 @@ #include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" #include "clang/Basic/LangOptions.h" +#include "clang/Basic/TargetInfo.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" @@ -180,6 +181,7 @@ private: bool classHasSeparateTeardown(const ObjCInterfaceDecl *ID) const; bool isReleasedByCIFilterDealloc(const ObjCPropertyImplDecl *PropImpl) const; + bool isNibLoadedIvarWithoutRetain(const ObjCPropertyImplDecl *PropImpl) const; }; } // End anonymous namespace. @@ -935,6 +937,9 @@ ReleaseRequirement ObjCDeallocChecker::getDeallocReleaseRequirement( if (isReleasedByCIFilterDealloc(PropImpl)) return ReleaseRequirement::MustNotReleaseDirectly; + if (isNibLoadedIvarWithoutRetain(PropImpl)) + return ReleaseRequirement::Unknown; + return ReleaseRequirement::MustRelease; case ObjCPropertyDecl::Weak: @@ -1091,6 +1096,32 @@ bool ObjCDeallocChecker::isReleasedByCIFilterDealloc( return false; } +/// Returns whether the ivar backing the property is an IBOutlet that +/// has its value set by nib loading code without retaining the value. +/// +/// On macOS, if there is no setter, the nib-loading code sets the ivar +/// directly, without retaining the value, +/// +/// On iOS and its derivatives, the nib-loading code will call +/// -setValue:forKey:, which retains the value before directly setting the ivar. +bool ObjCDeallocChecker::isNibLoadedIvarWithoutRetain( + const ObjCPropertyImplDecl *PropImpl) const { + const ObjCIvarDecl *IvarDecl = PropImpl->getPropertyIvarDecl(); + if (!IvarDecl->hasAttr()) + return false; + + const llvm::Triple &Target = + IvarDecl->getASTContext().getTargetInfo().getTriple(); + + if (!Target.isMacOSX()) + return false; + + if (PropImpl->getPropertyDecl()->getSetterMethodDecl()) + return false; + + return true; +} + void ento::registerObjCDeallocChecker(CheckerManager &Mgr) { const LangOptions &LangOpts = Mgr.getLangOpts(); // These checker only makes sense under MRR. diff --git a/test/Analysis/DeallocMissingRelease.m b/test/Analysis/DeallocMissingRelease.m index 224f44c1d1..63489be912 100644 --- a/test/Analysis/DeallocMissingRelease.m +++ b/test/Analysis/DeallocMissingRelease.m @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=osx.cocoa.Dealloc -fblocks -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=osx.cocoa.Dealloc -fblocks -triple x86_64-apple-ios4.0 -DMACOS=0 -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=osx.cocoa.Dealloc -fblocks -triple x86_64-apple-macosx10.6.0 -DMACOS=1 -verify %s // RUN: %clang_cc1 -analyze -analyzer-checker=osx.cocoa.Dealloc -fblocks -triple x86_64-apple-darwin10 -fobjc-arc -fobjc-runtime-has-weak -verify %s #include "Inputs/system-header-simulator-for-objc-dealloc.h" @@ -938,3 +939,71 @@ __attribute__((objc_root_class)) @implementation NotMissingDeallocCIFilter // no-warning @synthesize inputIvar = inputIvar; @end + + +@interface ClassWithRetainPropWithIBOutletIvarButNoSetter : NSObject { + // On macOS, the nib-loading code will set the ivar directly without + // retaining value (unike iOS, where it is retained). This means that + // on macOS we should not warn about a missing release for a property backed + // by an IBOutlet ivar when that property does not have a setter. + IBOutlet NSObject *ivarForOutlet; +} + +@property (readonly, retain) NSObject *ivarForOutlet; +#if NON_ARC && !MACOS +// expected-note@-2 {{Property is declared here}} +#endif +@end + +@implementation ClassWithRetainPropWithIBOutletIvarButNoSetter + +@synthesize ivarForOutlet; +#if NON_ARC && !MACOS +// expected-note@-2 {{Property is synthesized here}} +#endif +- (void)dealloc { + +#if NON_ARC + [super dealloc]; +#if !MACOS +// expected-warning@-2{{The 'ivarForOutlet' ivar in 'ClassWithRetainPropWithIBOutletIvarButNoSetter' was retained by a synthesized property but not released before '[super dealloc]'}} +#endif +#endif +} + +@end + +@interface ClassWithRetainPropWithIBOutletIvarAndShadowingReadWrite : NSObject { + IBOutlet NSObject *ivarForOutlet; +} + +@property (readonly, retain) NSObject *ivarForOutlet; + +@end + +@interface ClassWithRetainPropWithIBOutletIvarAndShadowingReadWrite () + +// Since there is a shadowing readwrite property, there will be a retaining +// setter and so the ivar will be retained by nib-loading code even on +// macOS and therefore must be released. +@property (readwrite, retain) NSObject *ivarForOutlet; +#if NON_ARC +// expected-note@-2 {{Property is declared here}} +#endif +@end + +@implementation ClassWithRetainPropWithIBOutletIvarAndShadowingReadWrite + +@synthesize ivarForOutlet; +#if NON_ARC +// expected-note@-2 {{Property is synthesized here}} +#endif +- (void)dealloc { + +#if NON_ARC + [super dealloc]; +// expected-warning@-1{{The 'ivarForOutlet' ivar in 'ClassWithRetainPropWithIBOutletIvarAndShadowingReadWrite' was retained by a synthesized property but not released before '[super dealloc]'}} +#endif +} + +@end -- 2.40.0