2018-08-30 04:29:17 +08:00
|
|
|
// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.cocoa.RetainCount,osx.cocoa.Dealloc,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class -analyzer-config eagerly-assume=false %s
|
|
|
|
// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.cocoa.RetainCount,osx.cocoa.Dealloc,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class -fobjc-arc -analyzer-config eagerly-assume=false %s
|
2014-01-11 04:06:06 +08:00
|
|
|
|
|
|
|
void clang_analyzer_eval(int);
|
2011-01-25 08:04:03 +08:00
|
|
|
|
2015-03-07 13:47:24 +08:00
|
|
|
typedef const void * CFTypeRef;
|
|
|
|
extern CFTypeRef CFRetain(CFTypeRef cf);
|
|
|
|
void CFRelease(CFTypeRef cf);
|
|
|
|
|
2011-01-25 08:04:03 +08:00
|
|
|
typedef signed char BOOL;
|
|
|
|
typedef unsigned int NSUInteger;
|
|
|
|
typedef struct _NSZone NSZone;
|
|
|
|
@class NSInvocation, NSMethodSignature, NSCoder, NSString, NSEnumerator;
|
|
|
|
@protocol NSObject - (BOOL)isEqual:(id)object; @end
|
|
|
|
@protocol NSCopying - (id)copyWithZone:(NSZone *)zone; @end
|
|
|
|
@protocol NSCoding - (void)encodeWithCoder:(NSCoder *)aCoder; @end
|
|
|
|
@protocol NSMutableCopying - (id)mutableCopyWithZone:(NSZone *)zone; @end
|
|
|
|
@interface NSObject <NSObject> {}
|
|
|
|
+(id)alloc;
|
|
|
|
-(id)init;
|
|
|
|
-(id)autorelease;
|
|
|
|
-(id)copy;
|
|
|
|
-(id)retain;
|
2014-01-11 04:06:06 +08:00
|
|
|
-(oneway void)release;
|
2016-02-26 05:15:16 +08:00
|
|
|
-(void)dealloc;
|
2011-01-25 08:04:03 +08:00
|
|
|
@end
|
|
|
|
@interface NSString : NSObject <NSCopying, NSMutableCopying, NSCoding>
|
|
|
|
- (NSUInteger)length;
|
|
|
|
-(id)initWithFormat:(NSString *)f,...;
|
|
|
|
-(BOOL)isEqualToString:(NSString *)s;
|
|
|
|
+ (id)string;
|
|
|
|
@end
|
|
|
|
@interface NSNumber : NSObject {}
|
|
|
|
+(id)alloc;
|
|
|
|
-(id)initWithInteger:(int)i;
|
|
|
|
@end
|
|
|
|
|
|
|
|
// rdar://6946338
|
|
|
|
|
|
|
|
@interface Test1 : NSObject {
|
|
|
|
NSString *text;
|
|
|
|
}
|
|
|
|
-(id)myMethod;
|
|
|
|
@property (nonatomic, assign) NSString *text;
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
2014-01-23 11:59:10 +08:00
|
|
|
#if !__has_feature(objc_arc)
|
|
|
|
|
2011-01-25 08:04:03 +08:00
|
|
|
@implementation Test1
|
|
|
|
|
|
|
|
@synthesize text;
|
|
|
|
|
|
|
|
-(id)myMethod {
|
|
|
|
Test1 *cell = [[[Test1 alloc] init] autorelease];
|
|
|
|
|
|
|
|
NSString *string1 = [[NSString alloc] initWithFormat:@"test %f", 0.0]; // expected-warning {{Potential leak}}
|
|
|
|
cell.text = string1;
|
|
|
|
|
|
|
|
return cell;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
// rdar://8824416
|
|
|
|
|
|
|
|
@interface MyNumber : NSObject
|
|
|
|
{
|
|
|
|
NSNumber* _myNumber;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id)initWithNumber:(NSNumber *)number;
|
|
|
|
|
|
|
|
@property (nonatomic, readonly) NSNumber* myNumber;
|
|
|
|
@property (nonatomic, readonly) NSNumber* newMyNumber;
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation MyNumber
|
|
|
|
@synthesize myNumber=_myNumber;
|
|
|
|
|
|
|
|
- (id)initWithNumber:(NSNumber *)number
|
|
|
|
{
|
|
|
|
self = [super init];
|
|
|
|
|
|
|
|
if ( self )
|
|
|
|
{
|
|
|
|
_myNumber = [number copy];
|
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSNumber*)newMyNumber
|
|
|
|
{
|
|
|
|
if ( _myNumber )
|
|
|
|
return [_myNumber retain];
|
|
|
|
|
|
|
|
return [[NSNumber alloc] initWithInteger:1];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id)valueForUndefinedKey:(NSString*)key
|
|
|
|
{
|
|
|
|
id value = 0;
|
|
|
|
|
|
|
|
if ([key isEqualToString:@"MyIvarNumberAsPropertyOverReleased"])
|
|
|
|
value = self.myNumber; // _myNumber will be over released, since the value returned from self.myNumber is not retained.
|
|
|
|
else if ([key isEqualToString:@"MyIvarNumberAsPropertyOk"])
|
|
|
|
value = [self.myNumber retain]; // this line fixes the over release
|
|
|
|
else if ([key isEqualToString:@"MyIvarNumberAsNewMyNumber"])
|
|
|
|
value = self.newMyNumber; // this one is ok, since value is returned retained
|
|
|
|
else
|
|
|
|
value = [[NSNumber alloc] initWithInteger:0];
|
|
|
|
|
2013-04-23 09:42:25 +08:00
|
|
|
return [value autorelease]; // expected-warning {{Object autoreleased too many times}}
|
2011-01-25 08:04:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
NSNumber* numberFromMyNumberProperty(MyNumber* aMyNumber)
|
|
|
|
{
|
|
|
|
NSNumber* result = aMyNumber.myNumber;
|
|
|
|
|
2013-04-23 09:42:25 +08:00
|
|
|
return [result autorelease]; // expected-warning {{Object autoreleased too many times}}
|
2011-01-25 08:04:03 +08:00
|
|
|
}
|
|
|
|
|
2014-01-23 11:59:10 +08:00
|
|
|
#endif
|
|
|
|
|
2011-01-25 08:04:03 +08:00
|
|
|
|
|
|
|
// rdar://6611873
|
|
|
|
|
|
|
|
@interface Person : NSObject {
|
|
|
|
NSString *_name;
|
|
|
|
}
|
|
|
|
@property (retain) NSString * name;
|
2014-01-23 11:59:10 +08:00
|
|
|
@property (assign) id friend;
|
2011-01-25 08:04:03 +08:00
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation Person
|
|
|
|
@synthesize name = _name;
|
2016-02-26 05:15:16 +08:00
|
|
|
|
|
|
|
-(void)dealloc {
|
|
|
|
#if !__has_feature(objc_arc)
|
|
|
|
self.name = [[NSString alloc] init]; // expected-warning {{leak}}
|
|
|
|
|
|
|
|
[super dealloc]; // expected-warning {{The '_name' ivar in 'Person' was retained by a synthesized property but not released before '[super dealloc]}}
|
|
|
|
#endif
|
|
|
|
}
|
2011-01-25 08:04:03 +08:00
|
|
|
@end
|
|
|
|
|
2014-01-23 11:59:10 +08:00
|
|
|
#if !__has_feature(objc_arc)
|
2011-01-25 08:04:03 +08:00
|
|
|
void rdar6611873() {
|
|
|
|
Person *p = [[[Person alloc] init] autorelease];
|
|
|
|
|
|
|
|
p.name = [[NSString string] retain]; // expected-warning {{leak}}
|
|
|
|
p.name = [[NSString alloc] init]; // expected-warning {{leak}}
|
2014-01-23 11:59:10 +08:00
|
|
|
|
|
|
|
p.friend = [[Person alloc] init]; // expected-warning {{leak}}
|
2011-01-25 08:04:03 +08:00
|
|
|
}
|
2014-01-23 11:59:10 +08:00
|
|
|
#endif
|
2011-01-28 00:17:11 +08:00
|
|
|
|
|
|
|
@interface SubPerson : Person
|
|
|
|
-(NSString *)foo;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation SubPerson
|
|
|
|
-(NSString *)foo {
|
|
|
|
return super.name;
|
|
|
|
}
|
|
|
|
@end
|
2012-02-19 04:53:30 +08:00
|
|
|
|
2014-01-23 11:59:10 +08:00
|
|
|
|
|
|
|
#if !__has_feature(objc_arc)
|
2012-02-19 04:53:30 +08:00
|
|
|
// <rdar://problem/9241180> Static analyzer doesn't detect uninitialized variable issues for property accesses
|
|
|
|
@interface RDar9241180
|
|
|
|
@property (readwrite,assign) id x;
|
|
|
|
-(id)testAnalyzer1:(int) y;
|
|
|
|
-(void)testAnalyzer2;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation RDar9241180
|
|
|
|
@synthesize x;
|
|
|
|
-(id)testAnalyzer1:(int)y {
|
|
|
|
RDar9241180 *o;
|
|
|
|
if (y && o.x) // expected-warning {{Property access on an uninitialized object pointer}}
|
|
|
|
return o;
|
|
|
|
return o; // expected-warning {{Undefined or garbage value returned to caller}}
|
|
|
|
}
|
|
|
|
-(void)testAnalyzer2 {
|
|
|
|
id y;
|
|
|
|
self.x = y; // expected-warning {{Argument for property setter is an uninitialized value}}
|
|
|
|
}
|
|
|
|
@end
|
2014-01-23 11:59:10 +08:00
|
|
|
#endif
|
2012-02-19 04:53:30 +08:00
|
|
|
|
|
|
|
|
2014-01-11 04:06:06 +08:00
|
|
|
//------
|
|
|
|
// Property accessor synthesis
|
|
|
|
//------
|
|
|
|
|
[analyzer] Don't track retain counts of objects directly accessed through ivars.
A refinement of r198953 to handle cases where an object is accessed both through
a property getter and through direct ivar access. An object accessed through a
property should always be treated as +0, i.e. not owned by the caller. However,
an object accessed through an ivar may be at +0 or at +1, depending on whether
the ivar is a strong reference. Outside of ARC, we don't have that information,
so we just don't track objects accessed through ivars.
With this change, accessing an ivar directly will deliberately override the +0
provided by a getter, but only if the +0 hasn't participated in other retain
counting yet. That isn't perfect, but it's already unusual for people to be
mixing property access with direct ivar access. (The primary use case for this
is in setters, init methods, and -dealloc.)
Thanks to Ted for spotting a few mistakes in private review.
<rdar://problem/16333368>
llvm-svn: 204730
2014-03-26 01:10:58 +08:00
|
|
|
extern void doSomethingWithPerson(Person *p);
|
|
|
|
extern void doSomethingWithName(NSString *name);
|
|
|
|
|
2014-01-23 11:59:10 +08:00
|
|
|
void testConsistencyRetain(Person *p) {
|
2014-01-11 04:06:06 +08:00
|
|
|
clang_analyzer_eval(p.name == p.name); // expected-warning{{TRUE}}
|
|
|
|
|
|
|
|
id origName = p.name;
|
|
|
|
clang_analyzer_eval(p.name == origName); // expected-warning{{TRUE}}
|
|
|
|
doSomethingWithPerson(p);
|
|
|
|
clang_analyzer_eval(p.name == origName); // expected-warning{{UNKNOWN}}
|
|
|
|
}
|
|
|
|
|
2014-01-23 11:59:10 +08:00
|
|
|
void testConsistencyAssign(Person *p) {
|
|
|
|
clang_analyzer_eval(p.friend == p.friend); // expected-warning{{TRUE}}
|
|
|
|
|
|
|
|
id origFriend = p.friend;
|
|
|
|
clang_analyzer_eval(p.friend == origFriend); // expected-warning{{TRUE}}
|
|
|
|
doSomethingWithPerson(p);
|
|
|
|
clang_analyzer_eval(p.friend == origFriend); // expected-warning{{UNKNOWN}}
|
|
|
|
}
|
|
|
|
|
2016-01-27 07:58:48 +08:00
|
|
|
@interface ClassWithShadowedReadWriteProperty {
|
|
|
|
int _f;
|
|
|
|
}
|
|
|
|
@property (readonly) int someProp;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface ClassWithShadowedReadWriteProperty ()
|
|
|
|
@property (readwrite) int someProp;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation ClassWithShadowedReadWriteProperty
|
|
|
|
- (void)testSynthesisForShadowedReadWriteProperties; {
|
|
|
|
clang_analyzer_eval(self.someProp == self.someProp); // expected-warning{{TRUE}}
|
|
|
|
|
|
|
|
_f = 1;
|
|
|
|
|
|
|
|
// Read of shadowed property should not invalidate receiver.
|
|
|
|
(void)self.someProp;
|
|
|
|
clang_analyzer_eval(_f == 1); // expected-warning{{TRUE}}
|
|
|
|
|
|
|
|
_f = 2;
|
|
|
|
// Call to getter of shadowed property should not invalidate receiver.
|
|
|
|
(void)[self someProp];
|
|
|
|
clang_analyzer_eval(_f == 2); // expected-warning{{TRUE}}
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
2016-04-01 11:24:13 +08:00
|
|
|
// Tests for the analyzer fix that works around a Sema bug
|
|
|
|
// where multiple methods are created for properties in class extensions that
|
|
|
|
// are redeclared in a category method.
|
|
|
|
// The Sema bug is tracked as <rdar://problem/25481164>.
|
|
|
|
@interface ClassWithRedeclaredPropertyInExtensionFollowedByCategory
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface ClassWithRedeclaredPropertyInExtensionFollowedByCategory ()
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface ClassWithRedeclaredPropertyInExtensionFollowedByCategory ()
|
|
|
|
@property (readwrite) int someProp;
|
|
|
|
@property (readonly) int otherProp;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface ClassWithRedeclaredPropertyInExtensionFollowedByCategory (MyCat)
|
|
|
|
@property (readonly) int someProp;
|
|
|
|
@property (readonly) int otherProp;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation ClassWithRedeclaredPropertyInExtensionFollowedByCategory
|
|
|
|
- (void)testSynthesisForRedeclaredProperties; {
|
|
|
|
clang_analyzer_eval(self.someProp == self.someProp); // expected-warning{{TRUE}}
|
|
|
|
clang_analyzer_eval([self someProp] == self.someProp); // expected-warning{{TRUE}}
|
|
|
|
|
|
|
|
clang_analyzer_eval(self.otherProp == self.otherProp); // expected-warning{{TRUE}}
|
|
|
|
clang_analyzer_eval([self otherProp] == self.otherProp); // expected-warning{{TRUE}}
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
// The relative order of the extension and the category matter, so test both.
|
|
|
|
@interface ClassWithRedeclaredPropertyInCategoryFollowedByExtension
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface ClassWithRedeclaredPropertyInCategoryFollowedByExtension ()
|
|
|
|
@property (readwrite) int someProp;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface ClassWithRedeclaredPropertyInCategoryFollowedByExtension (MyCat)
|
|
|
|
@property (readonly) int someProp;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation ClassWithRedeclaredPropertyInCategoryFollowedByExtension
|
|
|
|
- (void)testSynthesisForRedeclaredProperties; {
|
|
|
|
clang_analyzer_eval(self.someProp == self.someProp); // expected-warning{{TRUE}}
|
|
|
|
clang_analyzer_eval([self someProp] == self.someProp); // expected-warning{{TRUE}}
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
2016-03-29 07:55:58 +08:00
|
|
|
@interface ClassWithSynthesizedPropertyAndGetter
|
|
|
|
@property (readonly) int someProp;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation ClassWithSynthesizedPropertyAndGetter
|
|
|
|
@synthesize someProp;
|
|
|
|
|
|
|
|
// Make sure that the actual getter is inlined and not a getter created
|
|
|
|
// by BodyFarm
|
|
|
|
- (void)testBodyFarmGetterNotUsed {
|
|
|
|
int i = self.someProp;
|
|
|
|
clang_analyzer_eval(i == 22); // expected-warning {{TRUE}}
|
|
|
|
}
|
|
|
|
|
|
|
|
-(int)someProp {
|
|
|
|
return 22;
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
2017-01-11 09:02:34 +08:00
|
|
|
__attribute__((objc_root_class))
|
|
|
|
@interface ClassWithPrivatePropertyInClassExtensionWithProtocolShadowingCategory
|
|
|
|
@end
|
|
|
|
|
|
|
|
@protocol HasStuff
|
|
|
|
@property (nonatomic, readonly) int stuffProperty;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface ClassWithPrivatePropertyInClassExtensionWithProtocolShadowingCategory (Private)
|
|
|
|
@property (nonatomic, readonly) int stuffProperty;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface ClassWithPrivatePropertyInClassExtensionWithProtocolShadowingCategory (Internal) <HasStuff>
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface ClassWithPrivatePropertyInClassExtensionWithProtocolShadowingCategory() <HasStuff>
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation ClassWithPrivatePropertyInClassExtensionWithProtocolShadowingCategory
|
|
|
|
@synthesize stuffProperty = _stuffProperty;
|
|
|
|
|
|
|
|
-(void)foo {
|
|
|
|
(void)self.stuffProperty;
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
2016-02-19 03:13:30 +08:00
|
|
|
//------
|
|
|
|
// Setter ivar invalidation.
|
|
|
|
//------
|
|
|
|
|
|
|
|
@interface ClassWithSetters
|
|
|
|
// Note: These properties have implicit @synthesize implementations to be
|
|
|
|
// backed with ivars.
|
|
|
|
@property (assign) int propWithIvar1;
|
|
|
|
@property (assign) int propWithIvar2;
|
|
|
|
|
|
|
|
@property (retain) NSNumber *retainedProperty;
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface ClassWithSetters (InOtherTranslationUnit)
|
|
|
|
// The implementation of this property is in another translation unit.
|
|
|
|
// We don't know whether it is backed by an ivar or not.
|
|
|
|
@property (assign) int propInOther;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation ClassWithSetters
|
|
|
|
- (void) testSettingPropWithIvarInvalidatesExactlyThatIvar; {
|
|
|
|
_propWithIvar1 = 1;
|
|
|
|
_propWithIvar2 = 2;
|
|
|
|
self.propWithIvar1 = 66;
|
|
|
|
|
|
|
|
// Calling the setter of a property backed by the instance variable
|
|
|
|
// should invalidate the storage for the instance variable but not
|
|
|
|
// the rest of the receiver. Ideally we would model the setter completely
|
|
|
|
// but doing so would cause the new value to escape when it is bound
|
|
|
|
// to the ivar. This would cause bad false negatives in the retain count
|
|
|
|
// checker. (There is a test for this scenario in
|
|
|
|
// testWriteRetainedValueToRetainedProperty below).
|
|
|
|
clang_analyzer_eval(_propWithIvar1 == 66); // expected-warning{{UNKNOWN}}
|
|
|
|
clang_analyzer_eval(_propWithIvar2 == 2); // expected-warning{{TRUE}}
|
|
|
|
|
|
|
|
_propWithIvar1 = 1;
|
|
|
|
[self setPropWithIvar1:66];
|
|
|
|
|
|
|
|
clang_analyzer_eval(_propWithIvar1 == 66); // expected-warning{{UNKNOWN}}
|
|
|
|
clang_analyzer_eval(_propWithIvar2 == 2); // expected-warning{{TRUE}}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) testSettingPropWithoutIvarInvalidatesEntireInstance; {
|
|
|
|
_propWithIvar1 = 1;
|
|
|
|
_propWithIvar2 = 2;
|
|
|
|
self.propInOther = 66;
|
|
|
|
|
|
|
|
clang_analyzer_eval(_propWithIvar1 == 66); // expected-warning{{UNKNOWN}}
|
|
|
|
clang_analyzer_eval(_propWithIvar2 == 2); // expected-warning{{UNKNOWN}}
|
|
|
|
|
|
|
|
_propWithIvar1 = 1;
|
|
|
|
_propWithIvar2 = 2;
|
|
|
|
[self setPropInOther:66];
|
|
|
|
|
|
|
|
clang_analyzer_eval(_propWithIvar1 == 66); // expected-warning{{UNKNOWN}}
|
|
|
|
clang_analyzer_eval(_propWithIvar2 == 2); // expected-warning{{UNKNOWN}}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if !__has_feature(objc_arc)
|
|
|
|
- (void) testWriteRetainedValueToRetainedProperty; {
|
|
|
|
NSNumber *number = [[NSNumber alloc] initWithInteger:5]; // expected-warning {{Potential leak of an object stored into 'number'}}
|
|
|
|
|
|
|
|
// Make sure we catch this leak.
|
|
|
|
self.retainedProperty = number;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
@end
|
|
|
|
|
2016-05-07 02:24:50 +08:00
|
|
|
//------
|
|
|
|
// class properties
|
|
|
|
//------
|
|
|
|
|
|
|
|
int gBackingForReadWriteClassProp = 0;
|
|
|
|
|
|
|
|
@interface ClassWithClassProperties
|
|
|
|
@property(class, readonly) int readOnlyClassProp;
|
|
|
|
|
|
|
|
@property(class) int readWriteClassProp;
|
|
|
|
|
|
|
|
// Make sure we handle when a class and instance property have the same
|
|
|
|
// name. Test both when instance comes first and when class comes first.
|
|
|
|
@property(readonly) int classAndInstancePropWithSameNameOrderInstanceFirst;
|
|
|
|
@property(class, readonly) int classAndInstancePropWithSameNameOrderInstanceFirst;
|
|
|
|
|
|
|
|
@property(class, readonly) int classAndInstancePropWithSameNameOrderClassFirst;
|
|
|
|
@property(readonly) int classAndInstancePropWithSameNameOrderClassFirst;
|
|
|
|
|
|
|
|
|
|
|
|
@property(class, readonly) int dynamicClassProp;
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface ClassWithClassProperties (OtherTranslationUnit)
|
|
|
|
@property(class, assign) id propInOtherTranslationUnit;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation ClassWithClassProperties
|
|
|
|
|
|
|
|
@dynamic dynamicClassProp;
|
|
|
|
|
|
|
|
+ (int)readOnlyClassProp {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (int)readWriteClassProp {
|
|
|
|
return gBackingForReadWriteClassProp;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (void)setReadWriteClassProp:(int)val {
|
|
|
|
gBackingForReadWriteClassProp = val;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (int)classAndInstancePropWithSameNameOrderInstanceFirst {
|
|
|
|
return 12;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (int)classAndInstancePropWithSameNameOrderInstanceFirst {
|
|
|
|
return 13;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (int)classAndInstancePropWithSameNameOrderClassFirst {
|
|
|
|
return 14;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (int)classAndInstancePropWithSameNameOrderClassFirst {
|
|
|
|
return 15;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testInlineClassProp {
|
|
|
|
clang_analyzer_eval(ClassWithClassProperties.readOnlyClassProp == 1); // expected-warning{{TRUE}}
|
|
|
|
|
|
|
|
ClassWithClassProperties.readWriteClassProp = 7;
|
|
|
|
clang_analyzer_eval(ClassWithClassProperties.readWriteClassProp == 7); // expected-warning{{TRUE}}
|
|
|
|
ClassWithClassProperties.readWriteClassProp = 8;
|
|
|
|
clang_analyzer_eval(ClassWithClassProperties.readWriteClassProp == 8); // expected-warning{{TRUE}}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testUnknownClassProp {
|
|
|
|
clang_analyzer_eval(ClassWithClassProperties.propInOtherTranslationUnit == ClassWithClassProperties.propInOtherTranslationUnit); // expected-warning{{UNKNOWN}}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testEscapeGlobalOnUnknownProp {
|
|
|
|
gBackingForReadWriteClassProp = 33;
|
|
|
|
ClassWithClassProperties.propInOtherTranslationUnit = 0;
|
|
|
|
clang_analyzer_eval(gBackingForReadWriteClassProp == 33); // expected-warning{{UNKNOWN}}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testClassAndInstancePropertyWithSameName {
|
|
|
|
clang_analyzer_eval(self.classAndInstancePropWithSameNameOrderInstanceFirst == 12); // expected-warning{{TRUE}}
|
|
|
|
clang_analyzer_eval(ClassWithClassProperties.classAndInstancePropWithSameNameOrderInstanceFirst == 13); // expected-warning{{TRUE}}
|
|
|
|
|
|
|
|
clang_analyzer_eval(ClassWithClassProperties.classAndInstancePropWithSameNameOrderClassFirst == 14); // expected-warning{{TRUE}}
|
|
|
|
clang_analyzer_eval(self.classAndInstancePropWithSameNameOrderClassFirst == 15); // expected-warning{{TRUE}}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testDynamicClassProp {
|
|
|
|
clang_analyzer_eval(ClassWithClassProperties.dynamicClassProp == 16); // expected-warning{{UNKNOWN}}
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface SubclassOfClassWithClassProperties : ClassWithClassProperties
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation SubclassOfClassWithClassProperties
|
|
|
|
+ (int)dynamicClassProp; {
|
|
|
|
return 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testDynamicClassProp {
|
|
|
|
clang_analyzer_eval(SubclassOfClassWithClassProperties.dynamicClassProp == 16); // expected-warning{{TRUE}}
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
2014-01-23 11:59:10 +08:00
|
|
|
#if !__has_feature(objc_arc)
|
|
|
|
void testOverrelease(Person *p, int coin) {
|
[analyzer] Don't track retain counts of objects directly accessed through ivars.
A refinement of r198953 to handle cases where an object is accessed both through
a property getter and through direct ivar access. An object accessed through a
property should always be treated as +0, i.e. not owned by the caller. However,
an object accessed through an ivar may be at +0 or at +1, depending on whether
the ivar is a strong reference. Outside of ARC, we don't have that information,
so we just don't track objects accessed through ivars.
With this change, accessing an ivar directly will deliberately override the +0
provided by a getter, but only if the +0 hasn't participated in other retain
counting yet. That isn't perfect, but it's already unusual for people to be
mixing property access with direct ivar access. (The primary use case for this
is in setters, init methods, and -dealloc.)
Thanks to Ted for spotting a few mistakes in private review.
<rdar://problem/16333368>
llvm-svn: 204730
2014-03-26 01:10:58 +08:00
|
|
|
switch (coin) {
|
|
|
|
case 0:
|
2014-01-23 11:59:10 +08:00
|
|
|
[p.name release]; // expected-warning{{not owned}}
|
[analyzer] Don't track retain counts of objects directly accessed through ivars.
A refinement of r198953 to handle cases where an object is accessed both through
a property getter and through direct ivar access. An object accessed through a
property should always be treated as +0, i.e. not owned by the caller. However,
an object accessed through an ivar may be at +0 or at +1, depending on whether
the ivar is a strong reference. Outside of ARC, we don't have that information,
so we just don't track objects accessed through ivars.
With this change, accessing an ivar directly will deliberately override the +0
provided by a getter, but only if the +0 hasn't participated in other retain
counting yet. That isn't perfect, but it's already unusual for people to be
mixing property access with direct ivar access. (The primary use case for this
is in setters, init methods, and -dealloc.)
Thanks to Ted for spotting a few mistakes in private review.
<rdar://problem/16333368>
llvm-svn: 204730
2014-03-26 01:10:58 +08:00
|
|
|
break;
|
|
|
|
case 1:
|
2014-01-23 11:59:10 +08:00
|
|
|
[p.friend release]; // expected-warning{{not owned}}
|
[analyzer] Don't track retain counts of objects directly accessed through ivars.
A refinement of r198953 to handle cases where an object is accessed both through
a property getter and through direct ivar access. An object accessed through a
property should always be treated as +0, i.e. not owned by the caller. However,
an object accessed through an ivar may be at +0 or at +1, depending on whether
the ivar is a strong reference. Outside of ARC, we don't have that information,
so we just don't track objects accessed through ivars.
With this change, accessing an ivar directly will deliberately override the +0
provided by a getter, but only if the +0 hasn't participated in other retain
counting yet. That isn't perfect, but it's already unusual for people to be
mixing property access with direct ivar access. (The primary use case for this
is in setters, init methods, and -dealloc.)
Thanks to Ted for spotting a few mistakes in private review.
<rdar://problem/16333368>
llvm-svn: 204730
2014-03-26 01:10:58 +08:00
|
|
|
break;
|
|
|
|
case 2: {
|
|
|
|
id friend = p.friend;
|
|
|
|
doSomethingWithPerson(p);
|
|
|
|
[friend release]; // expected-warning{{not owned}}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// <rdar://problem/16333368>
|
|
|
|
@implementation Person (Rdar16333368)
|
|
|
|
|
|
|
|
- (void)testDeliberateRelease:(Person *)other {
|
|
|
|
doSomethingWithName(self.name);
|
|
|
|
[_name release]; // no-warning
|
|
|
|
self->_name = 0;
|
|
|
|
|
|
|
|
doSomethingWithName(other->_name);
|
[analyzer] RetainCountChecker: be forgiving when ivars are accessed directly.
A refinement of r204730, itself a refinement of r198953, to better handle
cases where an object is accessed both through a property getter and
through direct ivar access. An object accessed through a property should
always be treated as +0, i.e. not owned by the caller. However, an object
accessed through an ivar may be at +0 or at +1, depending on whether the
ivar is a strong reference. Outside of ARC, we don't always have that
information.
The previous attempt would clear out the +0 provided by a getter, but only
if that +0 hadn't already participated in other retain counting operations.
(That is, "self.foo" is okay, but "[[self.foo retain] autorelease]" is
problematic.) This turned out to not be good enough when our synthesized
getters get involved.
This commit drops the notion of "overridable" reference counting and instead
just tracks whether a value ever came from a (strong) ivar. If it has, we
allow one more release than we otherwise would. This has the added benefit
of being able to catch /some/ overreleases of instance variables, though
it's not likely to come up in practice.
We do still get some false negatives because we currently throw away
refcount state upon assigning a value into an ivar. We should probably
improve on that in the future, especially once we synthesize setters as
well as getters.
rdar://problem/18075108
llvm-svn: 228174
2015-02-05 03:24:52 +08:00
|
|
|
[other.name release]; // no-warning
|
2014-01-11 04:06:06 +08:00
|
|
|
}
|
[analyzer] Don't track retain counts of objects directly accessed through ivars.
A refinement of r198953 to handle cases where an object is accessed both through
a property getter and through direct ivar access. An object accessed through a
property should always be treated as +0, i.e. not owned by the caller. However,
an object accessed through an ivar may be at +0 or at +1, depending on whether
the ivar is a strong reference. Outside of ARC, we don't have that information,
so we just don't track objects accessed through ivars.
With this change, accessing an ivar directly will deliberately override the +0
provided by a getter, but only if the +0 hasn't participated in other retain
counting yet. That isn't perfect, but it's already unusual for people to be
mixing property access with direct ivar access. (The primary use case for this
is in setters, init methods, and -dealloc.)
Thanks to Ted for spotting a few mistakes in private review.
<rdar://problem/16333368>
llvm-svn: 204730
2014-03-26 01:10:58 +08:00
|
|
|
|
|
|
|
- (void)deliberateReleaseFalseNegative {
|
|
|
|
// This is arguably a false negative because the result of p.friend shouldn't
|
|
|
|
// be released, even though we are manipulating the ivar in between the two
|
|
|
|
// actions.
|
|
|
|
id name = self.name;
|
|
|
|
_name = 0;
|
|
|
|
[name release];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testRetainAndRelease {
|
|
|
|
[self.name retain];
|
|
|
|
[self.name release];
|
|
|
|
[self.name release]; // expected-warning{{not owned}}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testRetainAndReleaseIVar {
|
|
|
|
[self.name retain];
|
|
|
|
[_name release];
|
[analyzer] RetainCountChecker: be forgiving when ivars are accessed directly.
A refinement of r204730, itself a refinement of r198953, to better handle
cases where an object is accessed both through a property getter and
through direct ivar access. An object accessed through a property should
always be treated as +0, i.e. not owned by the caller. However, an object
accessed through an ivar may be at +0 or at +1, depending on whether the
ivar is a strong reference. Outside of ARC, we don't always have that
information.
The previous attempt would clear out the +0 provided by a getter, but only
if that +0 hadn't already participated in other retain counting operations.
(That is, "self.foo" is okay, but "[[self.foo retain] autorelease]" is
problematic.) This turned out to not be good enough when our synthesized
getters get involved.
This commit drops the notion of "overridable" reference counting and instead
just tracks whether a value ever came from a (strong) ivar. If it has, we
allow one more release than we otherwise would. This has the added benefit
of being able to catch /some/ overreleases of instance variables, though
it's not likely to come up in practice.
We do still get some false negatives because we currently throw away
refcount state upon assigning a value into an ivar. We should probably
improve on that in the future, especially once we synthesize setters as
well as getters.
rdar://problem/18075108
llvm-svn: 228174
2015-02-05 03:24:52 +08:00
|
|
|
[_name release];
|
[analyzer] Don't track retain counts of objects directly accessed through ivars.
A refinement of r198953 to handle cases where an object is accessed both through
a property getter and through direct ivar access. An object accessed through a
property should always be treated as +0, i.e. not owned by the caller. However,
an object accessed through an ivar may be at +0 or at +1, depending on whether
the ivar is a strong reference. Outside of ARC, we don't have that information,
so we just don't track objects accessed through ivars.
With this change, accessing an ivar directly will deliberately override the +0
provided by a getter, but only if the +0 hasn't participated in other retain
counting yet. That isn't perfect, but it's already unusual for people to be
mixing property access with direct ivar access. (The primary use case for this
is in setters, init methods, and -dealloc.)
Thanks to Ted for spotting a few mistakes in private review.
<rdar://problem/16333368>
llvm-svn: 204730
2014-03-26 01:10:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
2014-01-23 11:59:10 +08:00
|
|
|
#endif
|
2014-01-11 04:06:06 +08:00
|
|
|
|
|
|
|
@interface IntWrapper
|
|
|
|
@property int value;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation IntWrapper
|
|
|
|
@synthesize value;
|
|
|
|
@end
|
|
|
|
|
|
|
|
void testConsistencyInt(IntWrapper *w) {
|
|
|
|
clang_analyzer_eval(w.value == w.value); // expected-warning{{TRUE}}
|
|
|
|
|
|
|
|
int origValue = w.value;
|
|
|
|
if (origValue != 42)
|
|
|
|
return;
|
|
|
|
|
|
|
|
clang_analyzer_eval(w.value == 42); // expected-warning{{TRUE}}
|
|
|
|
}
|
|
|
|
|
|
|
|
void testConsistencyInt2(IntWrapper *w) {
|
|
|
|
if (w.value != 42)
|
|
|
|
return;
|
|
|
|
|
|
|
|
clang_analyzer_eval(w.value == 42); // expected-warning{{TRUE}}
|
|
|
|
}
|
|
|
|
|
2014-01-23 11:59:10 +08:00
|
|
|
|
|
|
|
@interface IntWrapperAuto
|
|
|
|
@property int value;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation IntWrapperAuto
|
|
|
|
@end
|
|
|
|
|
|
|
|
void testConsistencyIntAuto(IntWrapperAuto *w) {
|
|
|
|
clang_analyzer_eval(w.value == w.value); // expected-warning{{TRUE}}
|
|
|
|
|
|
|
|
int origValue = w.value;
|
|
|
|
if (origValue != 42)
|
|
|
|
return;
|
|
|
|
|
|
|
|
clang_analyzer_eval(w.value == 42); // expected-warning{{TRUE}}
|
|
|
|
}
|
|
|
|
|
|
|
|
void testConsistencyIntAuto2(IntWrapperAuto *w) {
|
|
|
|
if (w.value != 42)
|
|
|
|
return;
|
|
|
|
|
|
|
|
clang_analyzer_eval(w.value == 42); // expected-warning{{TRUE}}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-01-11 04:06:06 +08:00
|
|
|
typedef struct {
|
|
|
|
int value;
|
|
|
|
} IntWrapperStruct;
|
|
|
|
|
|
|
|
@interface StructWrapper
|
|
|
|
@property IntWrapperStruct inner;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation StructWrapper
|
|
|
|
@synthesize inner;
|
|
|
|
@end
|
|
|
|
|
|
|
|
void testConsistencyStruct(StructWrapper *w) {
|
|
|
|
clang_analyzer_eval(w.inner.value == w.inner.value); // expected-warning{{TRUE}}
|
|
|
|
|
|
|
|
int origValue = w.inner.value;
|
|
|
|
if (origValue != 42)
|
|
|
|
return;
|
|
|
|
|
|
|
|
clang_analyzer_eval(w.inner.value == 42); // expected-warning{{TRUE}}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@interface OpaqueIntWrapper
|
|
|
|
@property int value;
|
|
|
|
@end
|
|
|
|
|
|
|
|
// For now, don't assume a property is implemented using an ivar unless we can
|
|
|
|
// actually see that it is.
|
|
|
|
void testOpaqueConsistency(OpaqueIntWrapper *w) {
|
|
|
|
clang_analyzer_eval(w.value == w.value); // expected-warning{{UNKNOWN}}
|
|
|
|
}
|
|
|
|
|
[analyzer] RetainCountChecker: be forgiving when ivars are accessed directly.
A refinement of r204730, itself a refinement of r198953, to better handle
cases where an object is accessed both through a property getter and
through direct ivar access. An object accessed through a property should
always be treated as +0, i.e. not owned by the caller. However, an object
accessed through an ivar may be at +0 or at +1, depending on whether the
ivar is a strong reference. Outside of ARC, we don't always have that
information.
The previous attempt would clear out the +0 provided by a getter, but only
if that +0 hadn't already participated in other retain counting operations.
(That is, "self.foo" is okay, but "[[self.foo retain] autorelease]" is
problematic.) This turned out to not be good enough when our synthesized
getters get involved.
This commit drops the notion of "overridable" reference counting and instead
just tracks whether a value ever came from a (strong) ivar. If it has, we
allow one more release than we otherwise would. This has the added benefit
of being able to catch /some/ overreleases of instance variables, though
it's not likely to come up in practice.
We do still get some false negatives because we currently throw away
refcount state upon assigning a value into an ivar. We should probably
improve on that in the future, especially once we synthesize setters as
well as getters.
rdar://problem/18075108
llvm-svn: 228174
2015-02-05 03:24:52 +08:00
|
|
|
|
|
|
|
#if !__has_feature(objc_arc)
|
|
|
|
// Test quite a few cases of retain/release issues.
|
|
|
|
|
|
|
|
@interface RetainCountTesting
|
|
|
|
@property (strong) id ownedProp;
|
|
|
|
@property (unsafe_unretained) id unownedProp;
|
|
|
|
@property (nonatomic, strong) id manualProp;
|
2015-03-21 05:12:27 +08:00
|
|
|
@property (readonly) id readonlyProp;
|
|
|
|
@property (nonatomic, readwrite/*, assign */) id implicitManualProp; // expected-warning {{'assign' is assumed}} expected-warning {{'assign' not appropriate}}
|
|
|
|
@property (nonatomic, readwrite/*, assign */) id implicitSynthProp; // expected-warning {{'assign' is assumed}} expected-warning {{'assign' not appropriate}}
|
2015-03-07 13:47:24 +08:00
|
|
|
@property CFTypeRef cfProp;
|
[analyzer] RetainCountChecker: be forgiving when ivars are accessed directly.
A refinement of r204730, itself a refinement of r198953, to better handle
cases where an object is accessed both through a property getter and
through direct ivar access. An object accessed through a property should
always be treated as +0, i.e. not owned by the caller. However, an object
accessed through an ivar may be at +0 or at +1, depending on whether the
ivar is a strong reference. Outside of ARC, we don't always have that
information.
The previous attempt would clear out the +0 provided by a getter, but only
if that +0 hadn't already participated in other retain counting operations.
(That is, "self.foo" is okay, but "[[self.foo retain] autorelease]" is
problematic.) This turned out to not be good enough when our synthesized
getters get involved.
This commit drops the notion of "overridable" reference counting and instead
just tracks whether a value ever came from a (strong) ivar. If it has, we
allow one more release than we otherwise would. This has the added benefit
of being able to catch /some/ overreleases of instance variables, though
it's not likely to come up in practice.
We do still get some false negatives because we currently throw away
refcount state upon assigning a value into an ivar. We should probably
improve on that in the future, especially once we synthesize setters as
well as getters.
rdar://problem/18075108
llvm-svn: 228174
2015-02-05 03:24:52 +08:00
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation RetainCountTesting {
|
|
|
|
id _ivarOnly;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id)manualProp {
|
|
|
|
return _manualProp;
|
|
|
|
}
|
|
|
|
|
2015-03-21 05:12:27 +08:00
|
|
|
- (void)setImplicitManualProp:(id)newValue {}
|
|
|
|
|
[analyzer] RetainCountChecker: be forgiving when ivars are accessed directly.
A refinement of r204730, itself a refinement of r198953, to better handle
cases where an object is accessed both through a property getter and
through direct ivar access. An object accessed through a property should
always be treated as +0, i.e. not owned by the caller. However, an object
accessed through an ivar may be at +0 or at +1, depending on whether the
ivar is a strong reference. Outside of ARC, we don't always have that
information.
The previous attempt would clear out the +0 provided by a getter, but only
if that +0 hadn't already participated in other retain counting operations.
(That is, "self.foo" is okay, but "[[self.foo retain] autorelease]" is
problematic.) This turned out to not be good enough when our synthesized
getters get involved.
This commit drops the notion of "overridable" reference counting and instead
just tracks whether a value ever came from a (strong) ivar. If it has, we
allow one more release than we otherwise would. This has the added benefit
of being able to catch /some/ overreleases of instance variables, though
it's not likely to come up in practice.
We do still get some false negatives because we currently throw away
refcount state upon assigning a value into an ivar. We should probably
improve on that in the future, especially once we synthesize setters as
well as getters.
rdar://problem/18075108
llvm-svn: 228174
2015-02-05 03:24:52 +08:00
|
|
|
- (void)testOverreleaseOwnedIvar {
|
|
|
|
[_ownedProp retain];
|
|
|
|
[_ownedProp release];
|
|
|
|
[_ownedProp release];
|
2015-03-31 04:18:00 +08:00
|
|
|
[_ownedProp release]; // FIXME-warning{{used after it is released}}
|
[analyzer] RetainCountChecker: be forgiving when ivars are accessed directly.
A refinement of r204730, itself a refinement of r198953, to better handle
cases where an object is accessed both through a property getter and
through direct ivar access. An object accessed through a property should
always be treated as +0, i.e. not owned by the caller. However, an object
accessed through an ivar may be at +0 or at +1, depending on whether the
ivar is a strong reference. Outside of ARC, we don't always have that
information.
The previous attempt would clear out the +0 provided by a getter, but only
if that +0 hadn't already participated in other retain counting operations.
(That is, "self.foo" is okay, but "[[self.foo retain] autorelease]" is
problematic.) This turned out to not be good enough when our synthesized
getters get involved.
This commit drops the notion of "overridable" reference counting and instead
just tracks whether a value ever came from a (strong) ivar. If it has, we
allow one more release than we otherwise would. This has the added benefit
of being able to catch /some/ overreleases of instance variables, though
it's not likely to come up in practice.
We do still get some false negatives because we currently throw away
refcount state upon assigning a value into an ivar. We should probably
improve on that in the future, especially once we synthesize setters as
well as getters.
rdar://problem/18075108
llvm-svn: 228174
2015-02-05 03:24:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testOverreleaseUnownedIvar {
|
|
|
|
[_unownedProp retain];
|
|
|
|
[_unownedProp release];
|
2015-03-31 04:17:47 +08:00
|
|
|
[_unownedProp release]; // FIXME-warning{{not owned at this point by the caller}}
|
[analyzer] RetainCountChecker: be forgiving when ivars are accessed directly.
A refinement of r204730, itself a refinement of r198953, to better handle
cases where an object is accessed both through a property getter and
through direct ivar access. An object accessed through a property should
always be treated as +0, i.e. not owned by the caller. However, an object
accessed through an ivar may be at +0 or at +1, depending on whether the
ivar is a strong reference. Outside of ARC, we don't always have that
information.
The previous attempt would clear out the +0 provided by a getter, but only
if that +0 hadn't already participated in other retain counting operations.
(That is, "self.foo" is okay, but "[[self.foo retain] autorelease]" is
problematic.) This turned out to not be good enough when our synthesized
getters get involved.
This commit drops the notion of "overridable" reference counting and instead
just tracks whether a value ever came from a (strong) ivar. If it has, we
allow one more release than we otherwise would. This has the added benefit
of being able to catch /some/ overreleases of instance variables, though
it's not likely to come up in practice.
We do still get some false negatives because we currently throw away
refcount state upon assigning a value into an ivar. We should probably
improve on that in the future, especially once we synthesize setters as
well as getters.
rdar://problem/18075108
llvm-svn: 228174
2015-02-05 03:24:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testOverreleaseIvarOnly {
|
|
|
|
[_ivarOnly retain];
|
|
|
|
[_ivarOnly release];
|
|
|
|
[_ivarOnly release];
|
2015-03-31 04:18:00 +08:00
|
|
|
[_ivarOnly release]; // FIXME-warning{{used after it is released}}
|
[analyzer] RetainCountChecker: be forgiving when ivars are accessed directly.
A refinement of r204730, itself a refinement of r198953, to better handle
cases where an object is accessed both through a property getter and
through direct ivar access. An object accessed through a property should
always be treated as +0, i.e. not owned by the caller. However, an object
accessed through an ivar may be at +0 or at +1, depending on whether the
ivar is a strong reference. Outside of ARC, we don't always have that
information.
The previous attempt would clear out the +0 provided by a getter, but only
if that +0 hadn't already participated in other retain counting operations.
(That is, "self.foo" is okay, but "[[self.foo retain] autorelease]" is
problematic.) This turned out to not be good enough when our synthesized
getters get involved.
This commit drops the notion of "overridable" reference counting and instead
just tracks whether a value ever came from a (strong) ivar. If it has, we
allow one more release than we otherwise would. This has the added benefit
of being able to catch /some/ overreleases of instance variables, though
it's not likely to come up in practice.
We do still get some false negatives because we currently throw away
refcount state upon assigning a value into an ivar. We should probably
improve on that in the future, especially once we synthesize setters as
well as getters.
rdar://problem/18075108
llvm-svn: 228174
2015-02-05 03:24:52 +08:00
|
|
|
}
|
|
|
|
|
2015-03-21 05:12:27 +08:00
|
|
|
- (void)testOverreleaseReadonlyIvar {
|
|
|
|
[_readonlyProp retain];
|
|
|
|
[_readonlyProp release];
|
|
|
|
[_readonlyProp release];
|
2015-03-31 04:18:00 +08:00
|
|
|
[_readonlyProp release]; // FIXME-warning{{used after it is released}}
|
2015-03-21 05:12:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testOverreleaseImplicitManualIvar {
|
|
|
|
[_implicitManualProp retain];
|
|
|
|
[_implicitManualProp release];
|
|
|
|
[_implicitManualProp release];
|
2015-03-31 04:18:00 +08:00
|
|
|
[_implicitManualProp release]; // FIXME-warning{{used after it is released}}
|
2015-03-21 05:12:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testOverreleaseImplicitSynthIvar {
|
|
|
|
[_implicitSynthProp retain];
|
|
|
|
[_implicitSynthProp release];
|
2015-03-31 04:17:47 +08:00
|
|
|
[_implicitSynthProp release]; // FIXME-warning{{not owned at this point by the caller}}
|
2015-03-21 05:12:27 +08:00
|
|
|
}
|
|
|
|
|
2015-03-07 13:47:24 +08:00
|
|
|
- (void)testOverreleaseCF {
|
|
|
|
CFRetain(_cfProp);
|
|
|
|
CFRelease(_cfProp);
|
|
|
|
CFRelease(_cfProp);
|
2015-03-31 04:18:00 +08:00
|
|
|
CFRelease(_cfProp); // FIXME-warning{{used after it is released}}
|
2015-03-07 13:47:24 +08:00
|
|
|
}
|
|
|
|
|
[analyzer] RetainCountChecker: be forgiving when ivars are accessed directly.
A refinement of r204730, itself a refinement of r198953, to better handle
cases where an object is accessed both through a property getter and
through direct ivar access. An object accessed through a property should
always be treated as +0, i.e. not owned by the caller. However, an object
accessed through an ivar may be at +0 or at +1, depending on whether the
ivar is a strong reference. Outside of ARC, we don't always have that
information.
The previous attempt would clear out the +0 provided by a getter, but only
if that +0 hadn't already participated in other retain counting operations.
(That is, "self.foo" is okay, but "[[self.foo retain] autorelease]" is
problematic.) This turned out to not be good enough when our synthesized
getters get involved.
This commit drops the notion of "overridable" reference counting and instead
just tracks whether a value ever came from a (strong) ivar. If it has, we
allow one more release than we otherwise would. This has the added benefit
of being able to catch /some/ overreleases of instance variables, though
it's not likely to come up in practice.
We do still get some false negatives because we currently throw away
refcount state upon assigning a value into an ivar. We should probably
improve on that in the future, especially once we synthesize setters as
well as getters.
rdar://problem/18075108
llvm-svn: 228174
2015-02-05 03:24:52 +08:00
|
|
|
- (void)testOverreleaseOwnedIvarUse {
|
|
|
|
[_ownedProp retain];
|
|
|
|
[_ownedProp release];
|
|
|
|
[_ownedProp release];
|
2015-03-31 04:18:00 +08:00
|
|
|
[_ownedProp myMethod]; // FIXME-warning{{used after it is released}}
|
[analyzer] RetainCountChecker: be forgiving when ivars are accessed directly.
A refinement of r204730, itself a refinement of r198953, to better handle
cases where an object is accessed both through a property getter and
through direct ivar access. An object accessed through a property should
always be treated as +0, i.e. not owned by the caller. However, an object
accessed through an ivar may be at +0 or at +1, depending on whether the
ivar is a strong reference. Outside of ARC, we don't always have that
information.
The previous attempt would clear out the +0 provided by a getter, but only
if that +0 hadn't already participated in other retain counting operations.
(That is, "self.foo" is okay, but "[[self.foo retain] autorelease]" is
problematic.) This turned out to not be good enough when our synthesized
getters get involved.
This commit drops the notion of "overridable" reference counting and instead
just tracks whether a value ever came from a (strong) ivar. If it has, we
allow one more release than we otherwise would. This has the added benefit
of being able to catch /some/ overreleases of instance variables, though
it's not likely to come up in practice.
We do still get some false negatives because we currently throw away
refcount state upon assigning a value into an ivar. We should probably
improve on that in the future, especially once we synthesize setters as
well as getters.
rdar://problem/18075108
llvm-svn: 228174
2015-02-05 03:24:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testOverreleaseIvarOnlyUse {
|
|
|
|
[_ivarOnly retain];
|
|
|
|
[_ivarOnly release];
|
|
|
|
[_ivarOnly release];
|
2015-03-31 04:18:00 +08:00
|
|
|
[_ivarOnly myMethod]; // FIXME-warning{{used after it is released}}
|
[analyzer] RetainCountChecker: be forgiving when ivars are accessed directly.
A refinement of r204730, itself a refinement of r198953, to better handle
cases where an object is accessed both through a property getter and
through direct ivar access. An object accessed through a property should
always be treated as +0, i.e. not owned by the caller. However, an object
accessed through an ivar may be at +0 or at +1, depending on whether the
ivar is a strong reference. Outside of ARC, we don't always have that
information.
The previous attempt would clear out the +0 provided by a getter, but only
if that +0 hadn't already participated in other retain counting operations.
(That is, "self.foo" is okay, but "[[self.foo retain] autorelease]" is
problematic.) This turned out to not be good enough when our synthesized
getters get involved.
This commit drops the notion of "overridable" reference counting and instead
just tracks whether a value ever came from a (strong) ivar. If it has, we
allow one more release than we otherwise would. This has the added benefit
of being able to catch /some/ overreleases of instance variables, though
it's not likely to come up in practice.
We do still get some false negatives because we currently throw away
refcount state upon assigning a value into an ivar. We should probably
improve on that in the future, especially once we synthesize setters as
well as getters.
rdar://problem/18075108
llvm-svn: 228174
2015-02-05 03:24:52 +08:00
|
|
|
}
|
|
|
|
|
2015-03-07 13:47:24 +08:00
|
|
|
- (void)testOverreleaseCFUse {
|
|
|
|
CFRetain(_cfProp);
|
|
|
|
CFRelease(_cfProp);
|
|
|
|
CFRelease(_cfProp);
|
|
|
|
|
|
|
|
extern void CFUse(CFTypeRef);
|
2015-03-31 04:18:00 +08:00
|
|
|
CFUse(_cfProp); // FIXME-warning{{used after it is released}}
|
2015-03-07 13:47:24 +08:00
|
|
|
}
|
|
|
|
|
[analyzer] RetainCountChecker: be forgiving when ivars are accessed directly.
A refinement of r204730, itself a refinement of r198953, to better handle
cases where an object is accessed both through a property getter and
through direct ivar access. An object accessed through a property should
always be treated as +0, i.e. not owned by the caller. However, an object
accessed through an ivar may be at +0 or at +1, depending on whether the
ivar is a strong reference. Outside of ARC, we don't always have that
information.
The previous attempt would clear out the +0 provided by a getter, but only
if that +0 hadn't already participated in other retain counting operations.
(That is, "self.foo" is okay, but "[[self.foo retain] autorelease]" is
problematic.) This turned out to not be good enough when our synthesized
getters get involved.
This commit drops the notion of "overridable" reference counting and instead
just tracks whether a value ever came from a (strong) ivar. If it has, we
allow one more release than we otherwise would. This has the added benefit
of being able to catch /some/ overreleases of instance variables, though
it's not likely to come up in practice.
We do still get some false negatives because we currently throw away
refcount state upon assigning a value into an ivar. We should probably
improve on that in the future, especially once we synthesize setters as
well as getters.
rdar://problem/18075108
llvm-svn: 228174
2015-02-05 03:24:52 +08:00
|
|
|
- (void)testOverreleaseOwnedIvarAutoreleaseOkay {
|
|
|
|
[_ownedProp retain];
|
|
|
|
[_ownedProp release];
|
|
|
|
[_ownedProp autorelease];
|
|
|
|
} // no-warning
|
|
|
|
|
|
|
|
- (void)testOverreleaseIvarOnlyAutoreleaseOkay {
|
|
|
|
[_ivarOnly retain];
|
|
|
|
[_ivarOnly release];
|
|
|
|
[_ivarOnly autorelease];
|
|
|
|
} // no-warning
|
|
|
|
|
|
|
|
- (void)testOverreleaseOwnedIvarAutorelease {
|
|
|
|
[_ownedProp retain];
|
|
|
|
[_ownedProp release];
|
|
|
|
[_ownedProp autorelease];
|
|
|
|
[_ownedProp autorelease];
|
2015-03-31 04:18:00 +08:00
|
|
|
} // FIXME-warning{{Object autoreleased too many times}}
|
[analyzer] RetainCountChecker: be forgiving when ivars are accessed directly.
A refinement of r204730, itself a refinement of r198953, to better handle
cases where an object is accessed both through a property getter and
through direct ivar access. An object accessed through a property should
always be treated as +0, i.e. not owned by the caller. However, an object
accessed through an ivar may be at +0 or at +1, depending on whether the
ivar is a strong reference. Outside of ARC, we don't always have that
information.
The previous attempt would clear out the +0 provided by a getter, but only
if that +0 hadn't already participated in other retain counting operations.
(That is, "self.foo" is okay, but "[[self.foo retain] autorelease]" is
problematic.) This turned out to not be good enough when our synthesized
getters get involved.
This commit drops the notion of "overridable" reference counting and instead
just tracks whether a value ever came from a (strong) ivar. If it has, we
allow one more release than we otherwise would. This has the added benefit
of being able to catch /some/ overreleases of instance variables, though
it's not likely to come up in practice.
We do still get some false negatives because we currently throw away
refcount state upon assigning a value into an ivar. We should probably
improve on that in the future, especially once we synthesize setters as
well as getters.
rdar://problem/18075108
llvm-svn: 228174
2015-02-05 03:24:52 +08:00
|
|
|
|
|
|
|
- (void)testOverreleaseIvarOnlyAutorelease {
|
|
|
|
[_ivarOnly retain];
|
|
|
|
[_ivarOnly release];
|
|
|
|
[_ivarOnly autorelease];
|
|
|
|
[_ivarOnly autorelease];
|
2015-03-31 04:18:00 +08:00
|
|
|
} // FIXME-warning{{Object autoreleased too many times}}
|
[analyzer] RetainCountChecker: be forgiving when ivars are accessed directly.
A refinement of r204730, itself a refinement of r198953, to better handle
cases where an object is accessed both through a property getter and
through direct ivar access. An object accessed through a property should
always be treated as +0, i.e. not owned by the caller. However, an object
accessed through an ivar may be at +0 or at +1, depending on whether the
ivar is a strong reference. Outside of ARC, we don't always have that
information.
The previous attempt would clear out the +0 provided by a getter, but only
if that +0 hadn't already participated in other retain counting operations.
(That is, "self.foo" is okay, but "[[self.foo retain] autorelease]" is
problematic.) This turned out to not be good enough when our synthesized
getters get involved.
This commit drops the notion of "overridable" reference counting and instead
just tracks whether a value ever came from a (strong) ivar. If it has, we
allow one more release than we otherwise would. This has the added benefit
of being able to catch /some/ overreleases of instance variables, though
it's not likely to come up in practice.
We do still get some false negatives because we currently throw away
refcount state upon assigning a value into an ivar. We should probably
improve on that in the future, especially once we synthesize setters as
well as getters.
rdar://problem/18075108
llvm-svn: 228174
2015-02-05 03:24:52 +08:00
|
|
|
|
|
|
|
- (void)testPropertyAccessThenReleaseOwned {
|
|
|
|
id owned = [self.ownedProp retain];
|
|
|
|
[owned release];
|
|
|
|
[_ownedProp release];
|
|
|
|
clang_analyzer_eval(owned == _ownedProp); // expected-warning{{TRUE}}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testPropertyAccessThenReleaseOwned2 {
|
|
|
|
id fromIvar = _ownedProp;
|
|
|
|
id owned = [self.ownedProp retain];
|
|
|
|
[owned release];
|
|
|
|
[fromIvar release];
|
|
|
|
clang_analyzer_eval(owned == fromIvar); // expected-warning{{TRUE}}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testPropertyAccessThenReleaseUnowned {
|
|
|
|
id unowned = [self.unownedProp retain];
|
|
|
|
[unowned release];
|
2015-03-31 04:17:47 +08:00
|
|
|
[_unownedProp release]; // FIXME-warning{{not owned}}
|
[analyzer] RetainCountChecker: be forgiving when ivars are accessed directly.
A refinement of r204730, itself a refinement of r198953, to better handle
cases where an object is accessed both through a property getter and
through direct ivar access. An object accessed through a property should
always be treated as +0, i.e. not owned by the caller. However, an object
accessed through an ivar may be at +0 or at +1, depending on whether the
ivar is a strong reference. Outside of ARC, we don't always have that
information.
The previous attempt would clear out the +0 provided by a getter, but only
if that +0 hadn't already participated in other retain counting operations.
(That is, "self.foo" is okay, but "[[self.foo retain] autorelease]" is
problematic.) This turned out to not be good enough when our synthesized
getters get involved.
This commit drops the notion of "overridable" reference counting and instead
just tracks whether a value ever came from a (strong) ivar. If it has, we
allow one more release than we otherwise would. This has the added benefit
of being able to catch /some/ overreleases of instance variables, though
it's not likely to come up in practice.
We do still get some false negatives because we currently throw away
refcount state upon assigning a value into an ivar. We should probably
improve on that in the future, especially once we synthesize setters as
well as getters.
rdar://problem/18075108
llvm-svn: 228174
2015-02-05 03:24:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testPropertyAccessThenReleaseUnowned2 {
|
|
|
|
id fromIvar = _unownedProp;
|
|
|
|
id unowned = [self.unownedProp retain];
|
|
|
|
[unowned release];
|
|
|
|
clang_analyzer_eval(unowned == fromIvar); // expected-warning{{TRUE}}
|
2015-03-31 04:17:47 +08:00
|
|
|
[fromIvar release]; // FIXME-warning{{not owned}}
|
[analyzer] RetainCountChecker: be forgiving when ivars are accessed directly.
A refinement of r204730, itself a refinement of r198953, to better handle
cases where an object is accessed both through a property getter and
through direct ivar access. An object accessed through a property should
always be treated as +0, i.e. not owned by the caller. However, an object
accessed through an ivar may be at +0 or at +1, depending on whether the
ivar is a strong reference. Outside of ARC, we don't always have that
information.
The previous attempt would clear out the +0 provided by a getter, but only
if that +0 hadn't already participated in other retain counting operations.
(That is, "self.foo" is okay, but "[[self.foo retain] autorelease]" is
problematic.) This turned out to not be good enough when our synthesized
getters get involved.
This commit drops the notion of "overridable" reference counting and instead
just tracks whether a value ever came from a (strong) ivar. If it has, we
allow one more release than we otherwise would. This has the added benefit
of being able to catch /some/ overreleases of instance variables, though
it's not likely to come up in practice.
We do still get some false negatives because we currently throw away
refcount state upon assigning a value into an ivar. We should probably
improve on that in the future, especially once we synthesize setters as
well as getters.
rdar://problem/18075108
llvm-svn: 228174
2015-02-05 03:24:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testPropertyAccessThenReleaseManual {
|
|
|
|
id prop = [self.manualProp retain];
|
|
|
|
[prop release];
|
|
|
|
[_manualProp release]; // no-warning
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testPropertyAccessThenReleaseManual2 {
|
|
|
|
id fromIvar = _manualProp;
|
|
|
|
id prop = [self.manualProp retain];
|
|
|
|
[prop release];
|
|
|
|
clang_analyzer_eval(prop == fromIvar); // expected-warning{{TRUE}}
|
|
|
|
[fromIvar release]; // no-warning
|
|
|
|
}
|
|
|
|
|
2015-03-07 13:47:24 +08:00
|
|
|
- (void)testPropertyAccessThenReleaseCF {
|
|
|
|
CFTypeRef owned = CFRetain(self.cfProp);
|
|
|
|
CFRelease(owned);
|
|
|
|
CFRelease(_cfProp); // no-warning
|
|
|
|
clang_analyzer_eval(owned == _cfProp); // expected-warning{{TRUE}}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testPropertyAccessThenReleaseCF2 {
|
|
|
|
CFTypeRef fromIvar = _cfProp;
|
|
|
|
CFTypeRef owned = CFRetain(self.cfProp);
|
|
|
|
CFRelease(owned);
|
|
|
|
CFRelease(fromIvar);
|
|
|
|
clang_analyzer_eval(owned == fromIvar); // expected-warning{{TRUE}}
|
|
|
|
}
|
|
|
|
|
2015-03-21 05:12:27 +08:00
|
|
|
- (void)testPropertyAccessThenReleaseReadonly {
|
|
|
|
id prop = [self.readonlyProp retain];
|
|
|
|
[prop release];
|
|
|
|
[_readonlyProp release]; // no-warning
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testPropertyAccessThenReleaseReadonly2 {
|
|
|
|
id fromIvar = _readonlyProp;
|
|
|
|
id prop = [self.readonlyProp retain];
|
|
|
|
[prop release];
|
|
|
|
clang_analyzer_eval(prop == fromIvar); // expected-warning{{TRUE}}
|
|
|
|
[fromIvar release]; // no-warning
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testPropertyAccessThenReleaseImplicitManual {
|
|
|
|
id prop = [self.implicitManualProp retain];
|
|
|
|
[prop release];
|
|
|
|
[_implicitManualProp release]; // no-warning
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testPropertyAccessThenReleaseImplicitManual2 {
|
|
|
|
id fromIvar = _implicitManualProp;
|
|
|
|
id prop = [self.implicitManualProp retain];
|
|
|
|
[prop release];
|
|
|
|
clang_analyzer_eval(prop == fromIvar); // expected-warning{{TRUE}}
|
|
|
|
[fromIvar release]; // no-warning
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testPropertyAccessThenReleaseImplicitSynth {
|
|
|
|
id prop = [self.implicitSynthProp retain];
|
|
|
|
[prop release];
|
2015-03-31 04:17:47 +08:00
|
|
|
[_implicitSynthProp release]; // FIXME-warning{{not owned}}
|
2015-03-21 05:12:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testPropertyAccessThenReleaseImplicitSynth2 {
|
|
|
|
id fromIvar = _implicitSynthProp;
|
|
|
|
id prop = [self.implicitSynthProp retain];
|
|
|
|
[prop release];
|
|
|
|
clang_analyzer_eval(prop == fromIvar); // expected-warning{{TRUE}}
|
2015-03-31 04:17:47 +08:00
|
|
|
[fromIvar release]; // FIXME-warning{{not owned}}
|
2015-03-21 05:12:27 +08:00
|
|
|
}
|
|
|
|
|
[analyzer] RetainCountChecker: be forgiving when ivars are accessed directly.
A refinement of r204730, itself a refinement of r198953, to better handle
cases where an object is accessed both through a property getter and
through direct ivar access. An object accessed through a property should
always be treated as +0, i.e. not owned by the caller. However, an object
accessed through an ivar may be at +0 or at +1, depending on whether the
ivar is a strong reference. Outside of ARC, we don't always have that
information.
The previous attempt would clear out the +0 provided by a getter, but only
if that +0 hadn't already participated in other retain counting operations.
(That is, "self.foo" is okay, but "[[self.foo retain] autorelease]" is
problematic.) This turned out to not be good enough when our synthesized
getters get involved.
This commit drops the notion of "overridable" reference counting and instead
just tracks whether a value ever came from a (strong) ivar. If it has, we
allow one more release than we otherwise would. This has the added benefit
of being able to catch /some/ overreleases of instance variables, though
it's not likely to come up in practice.
We do still get some false negatives because we currently throw away
refcount state upon assigning a value into an ivar. We should probably
improve on that in the future, especially once we synthesize setters as
well as getters.
rdar://problem/18075108
llvm-svn: 228174
2015-02-05 03:24:52 +08:00
|
|
|
- (id)getUnownedFromProperty {
|
|
|
|
[_ownedProp retain];
|
|
|
|
[_ownedProp autorelease];
|
|
|
|
return _ownedProp; // no-warning
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id)transferUnownedFromProperty {
|
|
|
|
[_ownedProp retain];
|
|
|
|
[_ownedProp autorelease];
|
|
|
|
return [_ownedProp autorelease]; // no-warning
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id)transferOwnedFromProperty __attribute__((ns_returns_retained)) {
|
|
|
|
[_ownedProp retain];
|
|
|
|
[_ownedProp autorelease];
|
|
|
|
return _ownedProp; // no-warning
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testAssignOwned:(id)newValue {
|
|
|
|
_ownedProp = newValue;
|
|
|
|
[_ownedProp release]; // FIXME: no-warning{{not owned}}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testAssignUnowned:(id)newValue {
|
|
|
|
_unownedProp = newValue;
|
|
|
|
[_unownedProp release]; // FIXME: no-warning{{not owned}}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testAssignIvarOnly:(id)newValue {
|
|
|
|
_ivarOnly = newValue;
|
|
|
|
[_ivarOnly release]; // FIXME: no-warning{{not owned}}
|
|
|
|
}
|
|
|
|
|
2015-03-07 13:47:24 +08:00
|
|
|
- (void)testAssignCF:(CFTypeRef)newValue {
|
|
|
|
_cfProp = newValue;
|
|
|
|
CFRelease(_cfProp); // FIXME: no-warning{{not owned}}
|
|
|
|
}
|
|
|
|
|
2015-03-21 05:12:27 +08:00
|
|
|
- (void)testAssignReadonly:(id)newValue {
|
|
|
|
_readonlyProp = newValue;
|
|
|
|
[_readonlyProp release]; // FIXME: no-warning{{not owned}}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testAssignImplicitManual:(id)newValue {
|
|
|
|
_implicitManualProp = newValue;
|
|
|
|
[_implicitManualProp release]; // FIXME: no-warning{{not owned}}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testAssignImplicitSynth:(id)newValue {
|
|
|
|
_implicitSynthProp = newValue;
|
|
|
|
[_implicitSynthProp release]; // FIXME: no-warning{{not owned}}
|
|
|
|
}
|
|
|
|
|
[analyzer] RetainCountChecker: be forgiving when ivars are accessed directly.
A refinement of r204730, itself a refinement of r198953, to better handle
cases where an object is accessed both through a property getter and
through direct ivar access. An object accessed through a property should
always be treated as +0, i.e. not owned by the caller. However, an object
accessed through an ivar may be at +0 or at +1, depending on whether the
ivar is a strong reference. Outside of ARC, we don't always have that
information.
The previous attempt would clear out the +0 provided by a getter, but only
if that +0 hadn't already participated in other retain counting operations.
(That is, "self.foo" is okay, but "[[self.foo retain] autorelease]" is
problematic.) This turned out to not be good enough when our synthesized
getters get involved.
This commit drops the notion of "overridable" reference counting and instead
just tracks whether a value ever came from a (strong) ivar. If it has, we
allow one more release than we otherwise would. This has the added benefit
of being able to catch /some/ overreleases of instance variables, though
it's not likely to come up in practice.
We do still get some false negatives because we currently throw away
refcount state upon assigning a value into an ivar. We should probably
improve on that in the future, especially once we synthesize setters as
well as getters.
rdar://problem/18075108
llvm-svn: 228174
2015-02-05 03:24:52 +08:00
|
|
|
- (void)testAssignOwnedOkay:(id)newValue {
|
|
|
|
_ownedProp = [newValue retain];
|
|
|
|
[_ownedProp release]; // no-warning
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testAssignUnownedOkay:(id)newValue {
|
|
|
|
_unownedProp = [newValue retain];
|
|
|
|
[_unownedProp release]; // no-warning
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testAssignIvarOnlyOkay:(id)newValue {
|
|
|
|
_ivarOnly = [newValue retain];
|
|
|
|
[_ivarOnly release]; // no-warning
|
|
|
|
}
|
|
|
|
|
2015-03-07 13:47:24 +08:00
|
|
|
- (void)testAssignCFOkay:(CFTypeRef)newValue {
|
|
|
|
_cfProp = CFRetain(newValue);
|
|
|
|
CFRelease(_cfProp); // no-warning
|
|
|
|
}
|
|
|
|
|
2015-03-21 05:12:27 +08:00
|
|
|
- (void)testAssignReadonlyOkay:(id)newValue {
|
|
|
|
_readonlyProp = [newValue retain];
|
|
|
|
[_readonlyProp release]; // FIXME: no-warning{{not owned}}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testAssignImplicitManualOkay:(id)newValue {
|
|
|
|
_implicitManualProp = [newValue retain];
|
|
|
|
[_implicitManualProp release]; // FIXME: no-warning{{not owned}}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testAssignImplicitSynthOkay:(id)newValue {
|
|
|
|
_implicitSynthProp = [newValue retain];
|
|
|
|
[_implicitSynthProp release]; // FIXME: no-warning{{not owned}}
|
|
|
|
}
|
|
|
|
|
2015-02-20 07:57:04 +08:00
|
|
|
// rdar://problem/19862648
|
|
|
|
- (void)establishIvarIsNilDuringLoops {
|
|
|
|
extern id getRandomObject();
|
|
|
|
|
|
|
|
int i = 4; // Must be at least 4 to trigger the bug.
|
|
|
|
while (--i) {
|
|
|
|
id x = 0;
|
|
|
|
if (getRandomObject())
|
|
|
|
x = _ivarOnly;
|
|
|
|
if (!x)
|
|
|
|
x = getRandomObject();
|
|
|
|
[x myMethod];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-31 04:18:04 +08:00
|
|
|
// rdar://problem/20335433
|
|
|
|
- (void)retainIvarAndInvalidateSelf {
|
|
|
|
extern void invalidate(id);
|
|
|
|
[_unownedProp retain];
|
|
|
|
invalidate(self);
|
|
|
|
[_unownedProp release]; // no-warning
|
|
|
|
}
|
|
|
|
|
[analyzer] RetainCountChecker: be forgiving when ivars are accessed directly.
A refinement of r204730, itself a refinement of r198953, to better handle
cases where an object is accessed both through a property getter and
through direct ivar access. An object accessed through a property should
always be treated as +0, i.e. not owned by the caller. However, an object
accessed through an ivar may be at +0 or at +1, depending on whether the
ivar is a strong reference. Outside of ARC, we don't always have that
information.
The previous attempt would clear out the +0 provided by a getter, but only
if that +0 hadn't already participated in other retain counting operations.
(That is, "self.foo" is okay, but "[[self.foo retain] autorelease]" is
problematic.) This turned out to not be good enough when our synthesized
getters get involved.
This commit drops the notion of "overridable" reference counting and instead
just tracks whether a value ever came from a (strong) ivar. If it has, we
allow one more release than we otherwise would. This has the added benefit
of being able to catch /some/ overreleases of instance variables, though
it's not likely to come up in practice.
We do still get some false negatives because we currently throw away
refcount state upon assigning a value into an ivar. We should probably
improve on that in the future, especially once we synthesize setters as
well as getters.
rdar://problem/18075108
llvm-svn: 228174
2015-02-05 03:24:52 +08:00
|
|
|
@end
|
2017-04-13 06:00:13 +08:00
|
|
|
|
|
|
|
@interface Wrapper
|
|
|
|
@property(nonatomic, readonly) int value;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation Wrapper
|
|
|
|
@synthesize value;
|
|
|
|
@end
|
|
|
|
|
|
|
|
void testNoCrashWhenAccessPropertyAndThereAreNoDirectBindingsAtAll() {
|
|
|
|
union {
|
|
|
|
Wrapper *wrapper;
|
|
|
|
} u = { 0 };
|
|
|
|
[u.wrapper value];
|
|
|
|
}
|
|
|
|
|
[analyzer] RetainCountChecker: be forgiving when ivars are accessed directly.
A refinement of r204730, itself a refinement of r198953, to better handle
cases where an object is accessed both through a property getter and
through direct ivar access. An object accessed through a property should
always be treated as +0, i.e. not owned by the caller. However, an object
accessed through an ivar may be at +0 or at +1, depending on whether the
ivar is a strong reference. Outside of ARC, we don't always have that
information.
The previous attempt would clear out the +0 provided by a getter, but only
if that +0 hadn't already participated in other retain counting operations.
(That is, "self.foo" is okay, but "[[self.foo retain] autorelease]" is
problematic.) This turned out to not be good enough when our synthesized
getters get involved.
This commit drops the notion of "overridable" reference counting and instead
just tracks whether a value ever came from a (strong) ivar. If it has, we
allow one more release than we otherwise would. This has the added benefit
of being able to catch /some/ overreleases of instance variables, though
it's not likely to come up in practice.
We do still get some false negatives because we currently throw away
refcount state upon assigning a value into an ivar. We should probably
improve on that in the future, especially once we synthesize setters as
well as getters.
rdar://problem/18075108
llvm-svn: 228174
2015-02-05 03:24:52 +08:00
|
|
|
#endif // non-ARC
|
|
|
|
|
2019-01-19 06:52:13 +08:00
|
|
|
@interface ExplicitAccessorInCategory : NSObject
|
|
|
|
@property(readonly) int normal;
|
|
|
|
- (int)normal;
|
|
|
|
@property(readonly) int no_custom_accessor;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface ExplicitAccessorInCategory ()
|
|
|
|
@property(readonly) int in_category;
|
|
|
|
|
|
|
|
@property(readonly) int still_no_custom_accessor;
|
|
|
|
// This is an ordinary method, not a getter.
|
|
|
|
- (int)still_no_custom_accessor;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface ExplicitAccessorInCategory ()
|
|
|
|
- (int)in_category;
|
|
|
|
|
|
|
|
// This is an ordinary method, not a getter.
|
|
|
|
- (int)no_custom_accessor;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation ExplicitAccessorInCategory
|
|
|
|
- (void)foo {
|
|
|
|
// Make sure we don't farm bodies for explicit accessors: in particular,
|
|
|
|
// we're not sure that the accessor always returns the same value.
|
|
|
|
clang_analyzer_eval(self.normal == self.normal); // expected-warning{{UNKNOWN}}
|
|
|
|
// Also this used to crash.
|
|
|
|
clang_analyzer_eval(self.in_category == self.in_category); // expected-warning{{UNKNOWN}}
|
|
|
|
|
|
|
|
// When there is no explicit accessor defined (even if it looks like there is),
|
|
|
|
// farm the getter body and see if it does actually always yield the same value.
|
|
|
|
clang_analyzer_eval(self.no_custom_accessor == self.no_custom_accessor); // expected-warning{{TRUE}}
|
|
|
|
clang_analyzer_eval(self.still_no_custom_accessor == self.still_no_custom_accessor); // expected-warning{{TRUE}}
|
|
|
|
}
|
|
|
|
@end
|