You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1098 lines
29 KiB
1098 lines
29 KiB
// 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
|
|
|
|
void clang_analyzer_eval(int);
|
|
|
|
#define nil ((id)0)
|
|
|
|
typedef const void * CFTypeRef;
|
|
extern CFTypeRef CFRetain(CFTypeRef cf);
|
|
void CFRelease(CFTypeRef cf);
|
|
|
|
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;
|
|
-(oneway void)release;
|
|
-(void)dealloc;
|
|
@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
|
|
|
|
|
|
#if !__has_feature(objc_arc)
|
|
|
|
@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];
|
|
|
|
return [value autorelease]; // expected-warning {{Object autoreleased too many times}}
|
|
}
|
|
|
|
@end
|
|
|
|
NSNumber* numberFromMyNumberProperty(MyNumber* aMyNumber)
|
|
{
|
|
NSNumber* result = aMyNumber.myNumber;
|
|
|
|
return [result autorelease]; // expected-warning {{Object autoreleased too many times}}
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
// rdar://6611873
|
|
|
|
@interface Person : NSObject {
|
|
NSString *_name;
|
|
}
|
|
@property (retain) NSString * name;
|
|
@property (assign) id friend;
|
|
@end
|
|
|
|
@implementation Person
|
|
@synthesize name = _name;
|
|
|
|
-(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
|
|
}
|
|
@end
|
|
|
|
#if !__has_feature(objc_arc)
|
|
void rdar6611873() {
|
|
Person *p = [[[Person alloc] init] autorelease];
|
|
|
|
p.name = [[NSString string] retain]; // expected-warning {{leak}}
|
|
p.name = [[NSString alloc] init]; // expected-warning {{leak}}
|
|
|
|
p.friend = [[Person alloc] init]; // expected-warning {{leak}}
|
|
}
|
|
#endif
|
|
|
|
@interface SubPerson : Person
|
|
-(NSString *)foo;
|
|
@end
|
|
|
|
@implementation SubPerson
|
|
-(NSString *)foo {
|
|
return super.name;
|
|
}
|
|
@end
|
|
|
|
|
|
#if !__has_feature(objc_arc)
|
|
// <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
|
|
#endif
|
|
|
|
|
|
//------
|
|
// Property accessor synthesis
|
|
//------
|
|
|
|
extern void doSomethingWithPerson(Person *p);
|
|
extern void doSomethingWithName(NSString *name);
|
|
|
|
void testConsistencyRetain(Person *p) {
|
|
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}}
|
|
}
|
|
|
|
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}}
|
|
}
|
|
|
|
@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
|
|
|
|
// 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
|
|
|
|
@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
|
|
|
|
__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
|
|
|
|
//------
|
|
// 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
|
|
|
|
//------
|
|
// 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
|
|
|
|
|
|
#if !__has_feature(objc_arc)
|
|
void testOverrelease(Person *p, int coin) {
|
|
switch (coin) {
|
|
case 0:
|
|
[p.name release]; // expected-warning{{not owned}}
|
|
break;
|
|
case 1:
|
|
[p.friend release]; // expected-warning{{not owned}}
|
|
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]; // no-warning
|
|
}
|
|
|
|
- (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];
|
|
}
|
|
|
|
@end
|
|
#endif
|
|
|
|
@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}}
|
|
}
|
|
|
|
|
|
@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}}
|
|
}
|
|
|
|
|
|
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}}
|
|
}
|
|
|
|
|
|
#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;
|
|
@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}}
|
|
@property CFTypeRef cfProp;
|
|
@end
|
|
|
|
@implementation RetainCountTesting {
|
|
id _ivarOnly;
|
|
}
|
|
|
|
- (id)manualProp {
|
|
return _manualProp;
|
|
}
|
|
|
|
- (void)setImplicitManualProp:(id)newValue {}
|
|
|
|
- (void)testOverreleaseOwnedIvar {
|
|
[_ownedProp retain];
|
|
[_ownedProp release];
|
|
[_ownedProp release];
|
|
[_ownedProp release]; // FIXME-warning{{used after it is released}}
|
|
}
|
|
|
|
- (void)testOverreleaseUnownedIvar {
|
|
[_unownedProp retain];
|
|
[_unownedProp release];
|
|
[_unownedProp release]; // FIXME-warning{{not owned at this point by the caller}}
|
|
}
|
|
|
|
- (void)testOverreleaseIvarOnly {
|
|
[_ivarOnly retain];
|
|
[_ivarOnly release];
|
|
[_ivarOnly release];
|
|
[_ivarOnly release]; // FIXME-warning{{used after it is released}}
|
|
}
|
|
|
|
- (void)testOverreleaseReadonlyIvar {
|
|
[_readonlyProp retain];
|
|
[_readonlyProp release];
|
|
[_readonlyProp release];
|
|
[_readonlyProp release]; // FIXME-warning{{used after it is released}}
|
|
}
|
|
|
|
- (void)testOverreleaseImplicitManualIvar {
|
|
[_implicitManualProp retain];
|
|
[_implicitManualProp release];
|
|
[_implicitManualProp release];
|
|
[_implicitManualProp release]; // FIXME-warning{{used after it is released}}
|
|
}
|
|
|
|
- (void)testOverreleaseImplicitSynthIvar {
|
|
[_implicitSynthProp retain];
|
|
[_implicitSynthProp release];
|
|
[_implicitSynthProp release]; // FIXME-warning{{not owned at this point by the caller}}
|
|
}
|
|
|
|
- (void)testOverreleaseCF {
|
|
CFRetain(_cfProp);
|
|
CFRelease(_cfProp);
|
|
CFRelease(_cfProp);
|
|
CFRelease(_cfProp); // FIXME-warning{{used after it is released}}
|
|
}
|
|
|
|
- (void)testOverreleaseOwnedIvarUse {
|
|
[_ownedProp retain];
|
|
[_ownedProp release];
|
|
[_ownedProp release];
|
|
[_ownedProp myMethod]; // FIXME-warning{{used after it is released}}
|
|
}
|
|
|
|
- (void)testOverreleaseIvarOnlyUse {
|
|
[_ivarOnly retain];
|
|
[_ivarOnly release];
|
|
[_ivarOnly release];
|
|
[_ivarOnly myMethod]; // FIXME-warning{{used after it is released}}
|
|
}
|
|
|
|
- (void)testOverreleaseCFUse {
|
|
CFRetain(_cfProp);
|
|
CFRelease(_cfProp);
|
|
CFRelease(_cfProp);
|
|
|
|
extern void CFUse(CFTypeRef);
|
|
CFUse(_cfProp); // FIXME-warning{{used after it is released}}
|
|
}
|
|
|
|
- (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];
|
|
} // FIXME-warning{{Object autoreleased too many times}}
|
|
|
|
- (void)testOverreleaseIvarOnlyAutorelease {
|
|
[_ivarOnly retain];
|
|
[_ivarOnly release];
|
|
[_ivarOnly autorelease];
|
|
[_ivarOnly autorelease];
|
|
} // FIXME-warning{{Object autoreleased too many times}}
|
|
|
|
- (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];
|
|
[_unownedProp release]; // FIXME-warning{{not owned}}
|
|
}
|
|
|
|
- (void)testPropertyAccessThenReleaseUnowned2 {
|
|
id fromIvar = _unownedProp;
|
|
id unowned = [self.unownedProp retain];
|
|
[unowned release];
|
|
clang_analyzer_eval(unowned == fromIvar); // expected-warning{{TRUE}}
|
|
[fromIvar release]; // FIXME-warning{{not owned}}
|
|
}
|
|
|
|
- (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
|
|
}
|
|
|
|
- (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}}
|
|
}
|
|
|
|
- (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];
|
|
[_implicitSynthProp release]; // FIXME-warning{{not owned}}
|
|
}
|
|
|
|
- (void)testPropertyAccessThenReleaseImplicitSynth2 {
|
|
id fromIvar = _implicitSynthProp;
|
|
id prop = [self.implicitSynthProp retain];
|
|
[prop release];
|
|
clang_analyzer_eval(prop == fromIvar); // expected-warning{{TRUE}}
|
|
[fromIvar release]; // FIXME-warning{{not owned}}
|
|
}
|
|
|
|
- (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}}
|
|
}
|
|
|
|
- (void)testAssignCF:(CFTypeRef)newValue {
|
|
_cfProp = newValue;
|
|
CFRelease(_cfProp); // FIXME: no-warning{{not owned}}
|
|
}
|
|
|
|
- (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}}
|
|
}
|
|
|
|
- (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
|
|
}
|
|
|
|
- (void)testAssignCFOkay:(CFTypeRef)newValue {
|
|
_cfProp = CFRetain(newValue);
|
|
CFRelease(_cfProp); // no-warning
|
|
}
|
|
|
|
- (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}}
|
|
}
|
|
|
|
// 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];
|
|
}
|
|
}
|
|
|
|
// rdar://problem/20335433
|
|
- (void)retainIvarAndInvalidateSelf {
|
|
extern void invalidate(id);
|
|
[_unownedProp retain];
|
|
invalidate(self);
|
|
[_unownedProp release]; // no-warning
|
|
}
|
|
|
|
@end
|
|
|
|
@interface Wrapper
|
|
@property(nonatomic, readonly) int value;
|
|
@end
|
|
|
|
@implementation Wrapper
|
|
@synthesize value;
|
|
@end
|
|
|
|
void testNoCrashWhenAccessPropertyAndThereAreNoDirectBindingsAtAll() {
|
|
union {
|
|
Wrapper *wrapper;
|
|
} u = { 0 };
|
|
[u.wrapper value];
|
|
}
|
|
|
|
#endif // non-ARC
|
|
|
|
@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
|
|
|
|
@interface Shadowed
|
|
@property (assign) NSObject *o;
|
|
- (NSObject *)getShadowedIvar;
|
|
- (void)clearShadowedIvar;
|
|
- (NSObject *)getShadowedProp;
|
|
- (void)clearShadowedProp;
|
|
|
|
@property (assign) NSObject *o2;
|
|
@end
|
|
|
|
@implementation Shadowed
|
|
- (NSObject *)getShadowedIvar {
|
|
return self->_o;
|
|
}
|
|
- (void)clearShadowedIvar {
|
|
self->_o = nil;
|
|
}
|
|
- (NSObject *)getShadowedProp {
|
|
return self.o;
|
|
}
|
|
- (void)clearShadowedProp {
|
|
self.o = nil;
|
|
}
|
|
@end
|
|
|
|
@interface Shadowing : Shadowed
|
|
@end
|
|
|
|
@implementation Shadowing
|
|
// Property 'o' is declared in the superclass but synthesized here.
|
|
// This creates a separate ivar that shadows the superclass's ivar,
|
|
// but the old ivar is still accessible from the methods of the superclass.
|
|
// The old property, however, is not accessible with the property syntax
|
|
// even from the superclass methods.
|
|
@synthesize o;
|
|
|
|
-(void)testPropertyShadowing {
|
|
NSObject *oo = self.o; // no-crash
|
|
clang_analyzer_eval(self.o == oo); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval([self getShadowedIvar] == oo); // expected-warning{{UNKNOWN}}
|
|
[self clearShadowedIvar];
|
|
clang_analyzer_eval(self.o == oo); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval([self getShadowedIvar] == oo); // expected-warning{{UNKNOWN}}
|
|
clang_analyzer_eval([self getShadowedIvar] == nil); // expected-warning{{TRUE}}
|
|
}
|
|
|
|
@synthesize o2 = ooo2;
|
|
|
|
-(void)testPropertyShadowingWithExplicitIvar {
|
|
NSObject *oo2 = self.o2; // no-crash
|
|
}
|
|
@end
|