summaryrefslogtreecommitdiff
path: root/test/Analysis/properties.m
diff options
context:
space:
mode:
Diffstat (limited to 'test/Analysis/properties.m')
-rw-r--r--test/Analysis/properties.m285
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) {