diff options
Diffstat (limited to 'test/Analysis/properties.m')
-rw-r--r-- | test/Analysis/properties.m | 285 |
1 files changed, 283 insertions, 2 deletions
diff --git a/test/Analysis/properties.m b/test/Analysis/properties.m index bf9424c8c2068..b1305341e5d4b 100644 --- a/test/Analysis/properties.m +++ b/test/Analysis/properties.m @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class %s -// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class -fobjc-arc %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,osx.cocoa.Dealloc,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,osx.cocoa.Dealloc,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class -fobjc-arc %s void clang_analyzer_eval(int); @@ -22,6 +22,7 @@ typedef struct _NSZone NSZone; -(id)copy; -(id)retain; -(oneway void)release; +-(void)dealloc; @end @interface NSString : NSObject <NSCopying, NSMutableCopying, NSCoding> - (NSUInteger)length; @@ -138,6 +139,14 @@ NSNumber* numberFromMyNumberProperty(MyNumber* aMyNumber) @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) @@ -211,6 +220,278 @@ void testConsistencyAssign(Person *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 + +//------ +// 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) { |