mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-18 12:46:51 +00:00

We have a new policy in place making links to private resources something we try to avoid in source and test files. Normally, we'd organically switch to the new policy rather than make a sweeping change across a project. However, Clang is in a somewhat special circumstance currently: recently, I've had several new contributors run into rdar links around test code which their patch was changing the behavior of. This turns out to be a surprisingly bad experience, especially for newer folks, for a handful of reasons: not understanding what the link is and feeling intimidated by it, wondering whether their changes are actually breaking something important to a downstream in some way, having to hunt down strangers not involved with the patch to impose on them for help, accidental pressure from asking for potentially private IP to be made public, etc. Because folks run into these links entirely by chance (through fixing bugs or working on new features), there's not really a set of problematic links to focus on -- all of the links have basically the same potential for causing these problems. As a result, this is an omnibus patch to remove all such links. This was not a mechanical change; it was done by manually searching for rdar, radar, radr, and other variants to find all the various problematic links. From there, I tried to retain or reword the surrounding comments so that we would lose as little context as possible. However, because most links were just a plain link with no supporting context, the majority of the changes are simple removals. Differential Review: https://reviews.llvm.org/D158071
1088 lines
29 KiB
Objective-C
1088 lines
29 KiB
Objective-C
// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.cocoa.RetainCount,osx.cocoa.Dealloc,debug.ExprInspection -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 -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
|
|
|
|
@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
|
|
|
|
|
|
@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
|
|
|
|
|
|
@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(void) {
|
|
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)
|
|
// 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.
|
|
@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}}
|
|
}
|
|
}
|
|
}
|
|
|
|
@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}}
|
|
}
|
|
|
|
- (void)establishIvarIsNilDuringLoops {
|
|
extern id getRandomObject(void);
|
|
|
|
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];
|
|
}
|
|
}
|
|
|
|
- (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(void) {
|
|
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
|