2014-01-11 04:06:06 +08:00
|
|
|
// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class %s
|
2014-01-23 11:59:10 +08:00
|
|
|
// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class -fobjc-arc %s
|
2014-01-11 04:06:06 +08:00
|
|
|
|
|
|
|
void clang_analyzer_eval(int);
|
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;
|
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;
|
|
|
|
@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}}
|
|
|
|
}
|
|
|
|
|
|
|
|
#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);
|
|
|
|
[other.name release]; // expected-warning{{not owned}}
|
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];
|
|
|
|
[_name release]; // expected-warning{{not owned}}
|
|
|
|
}
|
|
|
|
|
|
|
|
@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}}
|
|
|
|
}
|
|
|
|
|