[cfe-commits] r162508 - in /cfe/trunk: include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp lib/StaticAnalyzer/Core/CallEvent.cpp test/Analysis/inlining/RetainCountExamples.m test/Analysis/inlining/retain-count-self-init.m
Anna Zaks
ganna at apple.com
Thu Aug 23 17:06:12 PDT 2012
Author: zaks
Date: Thu Aug 23 19:06:12 2012
New Revision: 162508
URL: http://llvm.org/viewvc/llvm-project?rev=162508&view=rev
Log:
[analyzer] Make analyzer less aggressive when dealing with [self init].
With inlining, retain count checker starts tracking 'self' through the
init methods. The analyser results were too noisy if the developer
did not follow 'self = [super init]' pattern (which is common
especially in older code bases) - we reported self init anti-pattern AND
possible use-after-free. This patch teaches the retain count
checker to assume that [super init] does not fail when it's not consumed
by another expression. This silences the retain count warning that warns
about possibility of use-after-free when init fails, while preserving
all the other checking on 'self'.
Added:
cfe/trunk/test/Analysis/inlining/retain-count-self-init.m
Modified:
cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
cfe/trunk/lib/StaticAnalyzer/Core/CallEvent.cpp
cfe/trunk/test/Analysis/inlining/RetainCountExamples.m
Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h?rev=162508&r1=162507&r2=162508&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h Thu Aug 23 19:06:12 2012
@@ -153,16 +153,6 @@
: State(Original.State), LCtx(Original.LCtx), Origin(Original.Origin),
Data(Original.Data), Location(Original.Location), RefCount(0) {}
-
- ProgramStateRef getState() const {
- return State;
- }
-
- const LocationContext *getLocationContext() const {
- return LCtx;
- }
-
-
/// Copies this CallEvent, with vtable intact, into a new block of memory.
virtual void cloneTo(void *Dest) const = 0;
@@ -192,6 +182,16 @@
return Origin.dyn_cast<const Decl *>();
}
+ /// \brief The state in which the call is being evaluated.
+ ProgramStateRef getState() const {
+ return State;
+ }
+
+ /// \brief The context in which the call is being evaluated.
+ const LocationContext *getLocationContext() const {
+ return LCtx;
+ }
+
/// \brief Returns the definition of the function or method that will be
/// called.
virtual RuntimeDefinition getRuntimeDefinition() const = 0;
@@ -810,6 +810,9 @@
/// \brief Returns the value of the receiver at the time of this call.
SVal getReceiverSVal() const;
+ /// \brief Return the value of 'self' if available.
+ SVal getSelfSVal() const;
+
/// \brief Get the interface for the receiver.
///
/// This works whether this is an instance message or a class message.
@@ -818,6 +821,9 @@
return getOriginExpr()->getReceiverInterface();
}
+ /// \brief Checks if the receiver refers to 'self' or 'super'.
+ bool isReceiverSelfOrSuper() const;
+
/// Returns how the message was written in the source (property access,
/// subscript, or explicit message send).
ObjCMessageKind getMessageKind() const;
Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp?rev=162508&r1=162507&r2=162508&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp Thu Aug 23 19:06:12 2012
@@ -930,6 +930,35 @@
S = getPersistentSummary(RE, RecEffect, DefEffect);
}
+
+ // Special case '[super init];' and '[self init];'
+ //
+ // Even though calling '[super init]' without assigning the result to self
+ // and checking if the parent returns 'nil' is a bad pattern, it is common.
+ // Additionally, our Self Init checker already warns about it. To avoid
+ // overwhelming the user with messages from both checkers, we model the case
+ // of '[super init]' in cases when it is not consumed by another expression
+ // as if the call preserves the value of 'self'; essentially, assuming it can
+ // never fail and return 'nil'.
+ // Note, we don't want to just stop tracking the value since we want the
+ // RetainCount checker to report leaks and use-after-free if SelfInit checker
+ // is turned off.
+ if (const ObjCMethodCall *MC = dyn_cast<ObjCMethodCall>(&Call)) {
+ if (MC->getMethodFamily() == OMF_init && MC->isReceiverSelfOrSuper()) {
+
+ // Check if the message is not consumed, we know it will not be used in
+ // an assignment, ex: "self = [super init]".
+ const Expr *ME = MC->getOriginExpr();
+ const LocationContext *LCtx = MC->getLocationContext();
+ ParentMap &PM = LCtx->getAnalysisDeclContext()->getParentMap();
+ if (!PM.isConsumedExpr(ME)) {
+ RetainSummaryTemplate ModifiableSummaryTemplate(S, *this);
+ ModifiableSummaryTemplate->setReceiverEffect(DoNothing);
+ ModifiableSummaryTemplate->setRetEffect(RetEffect::MakeNoRet());
+ }
+ }
+
+ }
}
const RetainSummary *
Modified: cfe/trunk/lib/StaticAnalyzer/Core/CallEvent.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/CallEvent.cpp?rev=162508&r1=162507&r2=162508&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/CallEvent.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/CallEvent.cpp Thu Aug 23 19:06:12 2012
@@ -574,6 +574,14 @@
return D->getResultType();
}
+SVal ObjCMethodCall::getSelfSVal() const {
+ const LocationContext *LCtx = getLocationContext();
+ const ImplicitParamDecl *SelfDecl = LCtx->getSelfDecl();
+ if (!SelfDecl)
+ return SVal();
+ return getState()->getSVal(getState()->getRegion(SelfDecl, LCtx));
+}
+
SVal ObjCMethodCall::getReceiverSVal() const {
// FIXME: Is this the best way to handle class receivers?
if (!isInstanceMessage())
@@ -584,10 +592,23 @@
// An instance message with no expression means we are sending to super.
// In this case the object reference is the same as 'self'.
- const LocationContext *LCtx = getLocationContext();
- const ImplicitParamDecl *SelfDecl = LCtx->getSelfDecl();
- assert(SelfDecl && "No message receiver Expr, but not in an ObjC method");
- return getState()->getSVal(getState()->getRegion(SelfDecl, LCtx));
+ assert(getOriginExpr()->getReceiverKind() == ObjCMessageExpr::SuperInstance);
+ SVal SelfVal = getSelfSVal();
+ assert(SelfVal.isValid() && "Calling super but not in ObjC method");
+ return SelfVal;
+}
+
+bool ObjCMethodCall::isReceiverSelfOrSuper() const {
+ if (getOriginExpr()->getReceiverKind() == ObjCMessageExpr::SuperInstance ||
+ getOriginExpr()->getReceiverKind() == ObjCMessageExpr::SuperClass)
+ return true;
+
+ if (!isInstanceMessage())
+ return false;
+
+ SVal RecVal = getSVal(getOriginExpr()->getInstanceReceiver());
+
+ return (RecVal == getSelfSVal());
}
SourceRange ObjCMethodCall::getSourceRange() const {
Modified: cfe/trunk/test/Analysis/inlining/RetainCountExamples.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/inlining/RetainCountExamples.m?rev=162508&r1=162507&r2=162508&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/inlining/RetainCountExamples.m (original)
+++ cfe/trunk/test/Analysis/inlining/RetainCountExamples.m Thu Aug 23 19:06:12 2012
@@ -30,4 +30,36 @@
void selfStaysLive() {
SelfStaysLive *foo = [[SelfStaysLive alloc] init];
[foo release];
-}
\ No newline at end of file
+}
+
+// Test that retain release checker warns on leaks and use-after-frees when
+// self init is not enabled.
+// radar://12115830
+ at interface ParentOfCell : NSObject
+- (id)initWithInt: (int)inInt;
+ at end
+ at interface Cell : ParentOfCell{
+ int x;
+}
+- (id)initWithInt: (int)inInt;
++ (void)testOverRelease;
++ (void)testLeak;
+ at property int x;
+ at end
+ at implementation Cell
+ at synthesize x;
+- (id) initWithInt: (int)inInt {
+ [super initWithInt: inInt];
+ self.x = inInt; // no-warning
+ return self; // Self Init checker would produce a warning here.
+}
++ (void) testOverRelease {
+ Cell *sharedCell3 = [[Cell alloc] initWithInt: 3];
+ [sharedCell3 release];
+ [sharedCell3 release]; // expected-warning {{Reference-counted object is used after it is released}}
+}
++ (void) testLeak {
+ Cell *sharedCell4 = [[Cell alloc] initWithInt: 3]; // expected-warning {{leak}}
+}
+ at end
+
Added: cfe/trunk/test/Analysis/inlining/retain-count-self-init.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/inlining/retain-count-self-init.m?rev=162508&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/inlining/retain-count-self-init.m (added)
+++ cfe/trunk/test/Analysis/inlining/retain-count-self-init.m Thu Aug 23 19:06:12 2012
@@ -0,0 +1,68 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,osx.cocoa.SelfInit -analyzer-ipa=dynamic-bifurcate -verify %s
+
+typedef signed char BOOL;
+typedef struct objc_class *Class;
+typedef struct objc_object {
+ Class isa;
+} *id;
+ at protocol NSObject - (BOOL)isEqual:(id)object; @end
+ at interface NSObject <NSObject> {}
++(id)alloc;
++(id)new;
+- (oneway void)release;
+-(id)init;
+-(id)autorelease;
+-(id)copy;
+- (Class)class;
+-(id)retain;
+ at end
+
+// We do not want to overhelm user with error messages in case they forgot to
+// assign to self and check that the result of [super init] is non-nil. So
+// stop tracking the receiver of init with respect to Retain Release checker.
+// radar://12115830
+ at interface ParentOfCell : NSObject
+- (id)initWithInt: (int)inInt;
+ at end
+ at interface Cell : ParentOfCell{
+ int x;
+}
+- (id)init;
++ (void)test;
+ at property int x;
+ at end
+ at implementation Cell
+ at synthesize x;
+- (id) init {
+ [super init];
+ self.x = 3; // no-warning
+ return self; // expected-warning {{Returning 'self' while it is not set to the result of '[(super or self)}}
+}
+- (id) initWithInt: (int)inInt {
+ [super initWithInt: inInt];
+ self.x = inInt; // no-warning
+ return self; // expected-warning {{Returning 'self' while it is not set to the result of '[(super or self)}}
+}
+- (id) init2 {
+ [self init]; // The call [self init] is inlined. We will warn inside the inlined body.
+ self.x = 2; // no-warning
+ return self;
+}
+
+- (id) initWithIntGood: (int)inInt {
+ if (self = [super initWithInt: inInt]) {
+ self.x = inInt;
+ }
+ return self;
+}
++ (void) test {
+ Cell *sharedCell1 = [[Cell alloc] init];
+ [sharedCell1 release];
+ Cell *sharedCell2 = [[Cell alloc] initWithInt: 3];
+ [sharedCell2 release];
+ Cell *sharedCell3 = [[Cell alloc] initWithIntGood: 3];
+ [sharedCell3 release];
+}
+
+ at end
+
More information about the cfe-commits
mailing list