From: Ted Kremenek Date: Fri, 24 Apr 2009 21:56:17 +0000 (+0000) Subject: Fix the same false positive reported in PR 2542 and X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8ee885bae5e8b187a73f3d4671b1619969e5e080;p=clang Fix the same false positive reported in PR 2542 and involving an NSAnimation object delegating its release to a delegate method. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@69992 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Analysis/CFRefCount.cpp b/lib/Analysis/CFRefCount.cpp index 7b16007a7a..4961ef47fc 100644 --- a/lib/Analysis/CFRefCount.cpp +++ b/lib/Analysis/CFRefCount.cpp @@ -1074,8 +1074,8 @@ RetainSummaryManager::getInitMethodSummary(ObjCMessageExpr* ME) { RetainSummary* -RetainSummaryManager::getCommonMethodSummary(ObjCMessageExpr* ME, Selector S) -{ +RetainSummaryManager::getCommonMethodSummary(ObjCMessageExpr* ME, Selector S) { + if (ObjCMethodDecl *MD = ME->getMethodDecl()) { // Scan the method decl for 'void*' arguments. These should be treated // as 'StopTracking' because they are often used with delegates. @@ -1091,12 +1091,26 @@ RetainSummaryManager::getCommonMethodSummary(ObjCMessageExpr* ME, Selector S) } } + // Any special effect for the receiver? + ArgEffect ReceiverEff = DoNothing; + + // If one of the arguments in the selector has the keyword 'delegate' we + // should stop tracking the reference count for the receiver. This is + // because the reference count is quite possibly handled by a delegate + // method. + if (S.isKeywordSelector()) { + const std::string &str = S.getAsString(); + assert(!str.empty()); + if (CStrInCStrNoCase(&str[0], "delegate:")) ReceiverEff = StopTracking; + } + // Look for methods that return an owned object. if (!isTrackedObjectType(ME->getType())) { - if (ScratchArgs.empty()) + if (ScratchArgs.empty() && ReceiverEff == DoNothing) return 0; - return getPersistentSummary(RetEffect::MakeNoRet()); + return getPersistentSummary(RetEffect::MakeNoRet(), ReceiverEff, + MayEscape); } // EXPERIMENTAL: Assume the Cocoa conventions for all objects returned @@ -1108,7 +1122,7 @@ RetainSummaryManager::getCommonMethodSummary(ObjCMessageExpr* ME, Selector S) : RetEffect::MakeOwned(RetEffect::ObjC, true)) : RetEffect::MakeNotOwned(RetEffect::ObjC); - return getPersistentSummary(E); + return getPersistentSummary(E, ReceiverEff, MayEscape); } RetainSummary* diff --git a/test/Analysis/pr_2542_rdar_6793404.m b/test/Analysis/pr_2542_rdar_6793404.m new file mode 100644 index 0000000000..82a028d652 --- /dev/null +++ b/test/Analysis/pr_2542_rdar_6793404.m @@ -0,0 +1,68 @@ +// RUN: clang-cc -analyze -checker-cfref -pedantic -analyzer-store=basic -verify %s && +// RUN: clang-cc -analyze -checker-cfref -pedantic -analyzer-store=region -verify %s + +// BEGIN delta-debugging reduced header stuff + +typedef signed char BOOL; +typedef unsigned int NSUInteger; +typedef struct _NSZone NSZone; +@class NSCoder; +@protocol NSObject +- (BOOL)isEqual:(id)object; +- (id)retain; +- (oneway void)release; +@end +@protocol NSCopying +- (id)copyWithZone:(NSZone *)zone; +@end +@protocol NSCoding +- (void)encodeWithCoder:(NSCoder *)aCoder; +@end +@interface NSObject {} +- (id)init; ++ (id)alloc; +@end +typedef double NSTimeInterval; +enum { NSAnimationEaseInOut, NSAnimationEaseIn, NSAnimationEaseOut, NSAnimationLinear }; +typedef NSUInteger NSAnimationCurve; +@interface NSAnimation : NSObject {} +- (id)initWithDuration:(NSTimeInterval)duration animationCurve:(NSAnimationCurve)animationCurve; +- (void)startAnimation; +- (void)setDelegate:(id)delegate; +@end + +// END delta-debugging reduced header stuff + +// From NSAnimation Class Reference +// -(void)startAnimation +// The receiver retains itself and is then autoreleased at the end +// of the animation or when it receives stopAnimation. + +@interface MyClass { } +- (void)animationDidEnd:(NSAnimation *)animation; +@end + +@implementation MyClass +- (void)f1 { + // NOTE: The analyzer doesn't really handle this; it just stops tracking + // 'animation' when it is sent the message 'setDelegate:'. + NSAnimation *animation = [[NSAnimation alloc] // no-warning + initWithDuration:1.0 + animationCurve:NSAnimationEaseInOut]; + + [animation setDelegate:self]; + [animation startAnimation]; +} + +- (void)f2 { + NSAnimation *animation = [[NSAnimation alloc] // expected-warning{{leak}} + initWithDuration:1.0 + animationCurve:NSAnimationEaseInOut]; + + [animation startAnimation]; +} + +- (void)animationDidEnd:(NSAnimation *)animation { + [animation release]; +} +@end