summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp31
-rw-r--r--clang/test/Analysis/DeallocMissingRelease.m71
2 files changed, 101 insertions, 1 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
index 4fece1c7fff..1c8ca0f4295 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
+++ b/clang/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<IBOutletAttr>())
+ 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/clang/test/Analysis/DeallocMissingRelease.m b/clang/test/Analysis/DeallocMissingRelease.m
index 224f44c1d1e..63489be9126 100644
--- a/clang/test/Analysis/DeallocMissingRelease.m
+++ b/clang/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
OpenPOWER on IntegriCloud