f4a2713ac8
Change-Id: Ia40e9ffdf29b5dab2f122f673ff6802a58bc690f
377 lines
12 KiB
Objective-C
377 lines
12 KiB
Objective-C
// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.osx.cocoa.InstanceVariableInvalidation -DRUN_IVAR_INVALIDATION -verify %s
|
|
// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.osx.cocoa.MissingInvalidationMethod -DRUN_MISSING_INVALIDATION_METHOD -verify %s
|
|
extern void __assert_fail (__const char *__assertion, __const char *__file,
|
|
unsigned int __line, __const char *__function)
|
|
__attribute__ ((__noreturn__));
|
|
|
|
#define assert(expr) \
|
|
((expr) ? (void)(0) : __assert_fail (#expr, __FILE__, __LINE__, __func__))
|
|
|
|
@protocol NSObject
|
|
@end
|
|
@interface NSObject <NSObject> {}
|
|
+(id)alloc;
|
|
+(id)new;
|
|
-(id)init;
|
|
-(id)autorelease;
|
|
-(id)copy;
|
|
- (Class)class;
|
|
-(id)retain;
|
|
-(id)description;
|
|
@end
|
|
@class NSString;
|
|
|
|
extern void NSLog(NSString *format, ...) __attribute__((format(__NSString__, 1, 2)));
|
|
|
|
@protocol Invalidation1 <NSObject>
|
|
- (void) invalidate __attribute__((annotate("objc_instance_variable_invalidator")));
|
|
@end
|
|
|
|
@protocol Invalidation2 <NSObject>
|
|
- (void) invalidate __attribute__((annotate("objc_instance_variable_invalidator")));
|
|
@end
|
|
|
|
@protocol Invalidation3 <NSObject>
|
|
- (void) invalidate __attribute__((annotate("objc_instance_variable_invalidator")));
|
|
- (void) invalidate2 __attribute__((annotate("objc_instance_variable_invalidator")));
|
|
@end
|
|
|
|
@protocol Invalidation3;
|
|
@protocol Invalidation2;
|
|
|
|
@interface Invalidation2Class <Invalidation2>
|
|
@end
|
|
|
|
@interface Invalidation1Class <Invalidation1>
|
|
@end
|
|
|
|
@interface ClassWithInvalidationMethodInCategory <NSObject>
|
|
@end
|
|
|
|
@interface ClassWithInvalidationMethodInCategory ()
|
|
- (void) invalidate __attribute__((annotate("objc_instance_variable_invalidator")));
|
|
@end
|
|
|
|
@interface SomeInvalidationImplementingObject: NSObject <Invalidation3, Invalidation2> {
|
|
SomeInvalidationImplementingObject *ObjA; // invalidation in the parent
|
|
}
|
|
@end
|
|
|
|
@implementation SomeInvalidationImplementingObject
|
|
- (void)invalidate{
|
|
ObjA = 0;
|
|
}
|
|
- (void)invalidate2 {
|
|
[self invalidate];
|
|
}
|
|
@end
|
|
|
|
@interface SomeSubclassInvalidatableObject : SomeInvalidationImplementingObject {
|
|
SomeInvalidationImplementingObject *Ivar1; // regular ivar
|
|
SomeInvalidationImplementingObject *Ivar2; // regular ivar, sending invalidate message
|
|
SomeInvalidationImplementingObject *_Ivar3; // no property, call -description
|
|
SomeInvalidationImplementingObject *_Ivar4; // no property, provide as argument to NSLog()
|
|
|
|
SomeInvalidationImplementingObject *_Prop1; // partially implemented property, set to 0 with dot syntax
|
|
SomeInvalidationImplementingObject *_Prop2; // fully implemented prop, set to 0 with dot syntax
|
|
SomeInvalidationImplementingObject *_propIvar; // property with custom named ivar, set to 0 via setter
|
|
Invalidation1Class *MultipleProtocols; // regular ivar belonging to a different class
|
|
Invalidation2Class *MultInheritance; // regular ivar belonging to a different class
|
|
SomeInvalidationImplementingObject *_Prop3; // property, invalidate via sending a message to a getter method
|
|
SomeInvalidationImplementingObject *_Prop4; // property with @synthesize, invalidate via property
|
|
SomeInvalidationImplementingObject *_Prop5; // property with @synthesize, invalidate via getter method
|
|
SomeInvalidationImplementingObject *_Prop8;
|
|
|
|
// Ivars invalidated by the partial invalidator.
|
|
SomeInvalidationImplementingObject *Ivar9;
|
|
SomeInvalidationImplementingObject *_Prop10;
|
|
SomeInvalidationImplementingObject *Ivar11;
|
|
|
|
// No warnings on these as they are not invalidatable.
|
|
NSObject *NIvar1;
|
|
NSObject *NObj2;
|
|
NSObject *_NProp1;
|
|
NSObject *_NpropIvar;
|
|
}
|
|
|
|
@property (assign) SomeInvalidationImplementingObject* Prop0;
|
|
@property (nonatomic, assign) SomeInvalidationImplementingObject* Prop1;
|
|
@property (assign) SomeInvalidationImplementingObject* Prop2;
|
|
@property (assign) SomeInvalidationImplementingObject* Prop3;
|
|
@property (assign) SomeInvalidationImplementingObject *Prop5;
|
|
@property (assign) SomeInvalidationImplementingObject *Prop4;
|
|
|
|
@property (assign) SomeInvalidationImplementingObject* Prop6; // automatically synthesized prop
|
|
@property (assign) SomeInvalidationImplementingObject* Prop7; // automatically synthesized prop
|
|
@property (assign) SomeInvalidationImplementingObject *SynthIvarProp;
|
|
|
|
@property (assign) NSObject* NProp0;
|
|
@property (nonatomic, assign) NSObject* NProp1;
|
|
@property (assign) NSObject* NProp2;
|
|
|
|
-(void)setProp1: (SomeInvalidationImplementingObject*) InO;
|
|
-(void)setNProp1: (NSObject*) InO;
|
|
|
|
-(void)invalidate;
|
|
|
|
// Partial invalidators invalidate only some ivars. They are guaranteed to be
|
|
// called before the invalidation methods.
|
|
-(void)partialInvalidator1 __attribute__((annotate("objc_instance_variable_invalidator_partial")));
|
|
-(void)partialInvalidator2 __attribute__((annotate("objc_instance_variable_invalidator_partial")));
|
|
@end
|
|
|
|
@interface SomeSubclassInvalidatableObject()
|
|
@property (assign) SomeInvalidationImplementingObject* Prop8;
|
|
@property (assign) SomeInvalidationImplementingObject* Prop10;
|
|
@end
|
|
|
|
@implementation SomeSubclassInvalidatableObject{
|
|
@private
|
|
SomeInvalidationImplementingObject *Ivar5;
|
|
ClassWithInvalidationMethodInCategory *Ivar13;
|
|
}
|
|
|
|
@synthesize Prop7 = _propIvar;
|
|
@synthesize Prop3 = _Prop3;
|
|
@synthesize Prop5 = _Prop5;
|
|
@synthesize Prop4 = _Prop4;
|
|
@synthesize Prop8 = _Prop8;
|
|
@synthesize Prop10 = _Prop10;
|
|
|
|
|
|
- (void) setProp1: (SomeInvalidationImplementingObject*) InObj {
|
|
_Prop1 = InObj;
|
|
}
|
|
|
|
- (void) setProp2: (SomeInvalidationImplementingObject*) InObj {
|
|
_Prop2 = InObj;
|
|
}
|
|
- (SomeInvalidationImplementingObject*) Prop2 {
|
|
return _Prop2;
|
|
}
|
|
|
|
@synthesize NProp2 = _NpropIvar;
|
|
|
|
- (void) setNProp1: (NSObject*) InObj {
|
|
_NProp1 = InObj;
|
|
}
|
|
|
|
- (void) invalidate {
|
|
[Ivar2 invalidate];
|
|
self.Prop0 = 0;
|
|
self.Prop1 = 0;
|
|
[self setProp2:0];
|
|
[self setProp3:0];
|
|
[[self Prop5] invalidate2];
|
|
[self.Prop4 invalidate];
|
|
[self.Prop8 invalidate];
|
|
self.Prop6 = 0;
|
|
[[self Prop7] invalidate];
|
|
|
|
[_Ivar3 description];
|
|
NSLog(@"%@", _Ivar4);
|
|
[super invalidate];
|
|
}
|
|
#if RUN_IVAR_INVALIDATION
|
|
// expected-warning@-2 {{Instance variable Ivar1 needs to be invalidated}}
|
|
// expected-warning@-3 {{Instance variable MultipleProtocols needs to be invalidated}}
|
|
// expected-warning@-4 {{Instance variable MultInheritance needs to be invalidated}}
|
|
// expected-warning@-5 {{Property SynthIvarProp needs to be invalidated or set to nil}}
|
|
// expected-warning@-6 {{Instance variable _Ivar3 needs to be invalidated}}
|
|
// expected-warning@-7 {{Instance variable _Ivar4 needs to be invalidated}}
|
|
// expected-warning@-8 {{Instance variable Ivar5 needs to be invalidated or set to nil}}
|
|
// expected-warning@-9 {{Instance variable Ivar13 needs to be invalidated or set to nil}}
|
|
#endif
|
|
|
|
-(void)partialInvalidator1 {
|
|
[Ivar9 invalidate];
|
|
[_Prop10 invalidate];
|
|
}
|
|
|
|
-(void)partialInvalidator2 {
|
|
[Ivar11 invalidate];
|
|
}
|
|
|
|
@end
|
|
|
|
// Example, where the same property is inherited through
|
|
// the parent and directly through a protocol. If a property backing ivar is
|
|
// synthesized in the parent, let the parent invalidate it.
|
|
|
|
@protocol IDEBuildable <NSObject>
|
|
@property (readonly, strong) id <Invalidation2> ObjB;
|
|
@end
|
|
|
|
@interface Parent : NSObject <IDEBuildable, Invalidation2> {
|
|
Invalidation2Class *_ObjB; // Invalidation of ObjB happens in the parent.
|
|
}
|
|
@end
|
|
|
|
@interface Child: Parent <Invalidation2, IDEBuildable>
|
|
@end
|
|
|
|
@implementation Parent{
|
|
@private
|
|
Invalidation2Class *Ivar10;
|
|
Invalidation2Class *Ivar11;
|
|
Invalidation2Class *Ivar12;
|
|
}
|
|
|
|
@synthesize ObjB = _ObjB;
|
|
- (void)invalidate{
|
|
_ObjB = ((void*)0);
|
|
|
|
assert(Ivar10 == 0);
|
|
|
|
if (__builtin_expect(!(Ivar11 == ((void*)0)), 0))
|
|
assert(0);
|
|
|
|
assert(0 == Ivar12);
|
|
|
|
}
|
|
@end
|
|
|
|
@implementation Child
|
|
- (void)invalidate{
|
|
// no-warning
|
|
}
|
|
@end
|
|
|
|
@protocol Invalidation <NSObject>
|
|
- (void)invalidate __attribute__((annotate("objc_instance_variable_invalidator")));
|
|
@end
|
|
|
|
@interface Foo : NSObject <Invalidation>
|
|
@end
|
|
|
|
@class FooBar;
|
|
@protocol FooBar_Protocol <NSObject>
|
|
@end
|
|
|
|
@interface MissingInvalidationMethod : Foo <FooBar_Protocol>
|
|
@property (assign) MissingInvalidationMethod *foobar15_warn;
|
|
#if RUN_IVAR_INVALIDATION
|
|
// expected-warning@-2 {{Property foobar15_warn needs to be invalidated; no invalidation method is defined in the @implementation for MissingInvalidationMethod}}
|
|
#endif
|
|
@end
|
|
@implementation MissingInvalidationMethod
|
|
@end
|
|
|
|
@interface MissingInvalidationMethod2 : Foo <FooBar_Protocol> {
|
|
Foo *Ivar1;
|
|
#if RUN_IVAR_INVALIDATION
|
|
// expected-warning@-2 {{Instance variable Ivar1 needs to be invalidated; no invalidation method is defined in the @implementation for MissingInvalidationMethod2}}
|
|
#endif
|
|
}
|
|
@end
|
|
@implementation MissingInvalidationMethod2
|
|
@end
|
|
|
|
@interface MissingInvalidationMethodDecl : NSObject {
|
|
Foo *Ivar1;
|
|
#if RUN_MISSING_INVALIDATION_METHOD
|
|
// expected-warning@-2 {{Instance variable Ivar1 needs to be invalidated; no invalidation method is declared for MissingInvalidationMethodDecl}}
|
|
#endif
|
|
}
|
|
@end
|
|
@implementation MissingInvalidationMethodDecl
|
|
@end
|
|
|
|
@interface MissingInvalidationMethodDecl2 : NSObject {
|
|
@private
|
|
Foo *_foo1;
|
|
#if RUN_MISSING_INVALIDATION_METHOD
|
|
// expected-warning@-2 {{Instance variable _foo1 needs to be invalidated; no invalidation method is declared for MissingInvalidationMethodDecl2}}
|
|
#endif
|
|
}
|
|
@property (strong) Foo *bar1;
|
|
@end
|
|
@implementation MissingInvalidationMethodDecl2
|
|
@end
|
|
|
|
@interface InvalidatedInPartial : SomeInvalidationImplementingObject {
|
|
SomeInvalidationImplementingObject *Ivar1;
|
|
SomeInvalidationImplementingObject *Ivar2;
|
|
}
|
|
-(void)partialInvalidator __attribute__((annotate("objc_instance_variable_invalidator_partial")));
|
|
@end
|
|
@implementation InvalidatedInPartial
|
|
-(void)partialInvalidator {
|
|
[Ivar1 invalidate];
|
|
Ivar2 = 0;
|
|
}
|
|
@end
|
|
|
|
@interface NotInvalidatedInPartial : SomeInvalidationImplementingObject {
|
|
SomeInvalidationImplementingObject *Ivar1;
|
|
}
|
|
-(void)partialInvalidator __attribute__((annotate("objc_instance_variable_invalidator_partial")));
|
|
-(void)partialInvalidatorCallsPartial __attribute__((annotate("objc_instance_variable_invalidator_partial")));
|
|
@end
|
|
@implementation NotInvalidatedInPartial
|
|
-(void)partialInvalidator {
|
|
}
|
|
-(void)partialInvalidatorCallsPartial {
|
|
[self partialInvalidator];
|
|
}
|
|
|
|
-(void)invalidate {
|
|
}
|
|
#if RUN_IVAR_INVALIDATION
|
|
// expected-warning@-2 {{Instance variable Ivar1 needs to be invalidated or set to nil}}
|
|
#endif
|
|
@end
|
|
|
|
@interface SomeNotInvalidatedInPartial : SomeInvalidationImplementingObject {
|
|
SomeInvalidationImplementingObject *Ivar1;
|
|
SomeInvalidationImplementingObject *Ivar2;
|
|
#if RUN_IVAR_INVALIDATION
|
|
// expected-warning@-2 {{Instance variable Ivar2 needs to be invalidated or set to nil}}
|
|
#endif
|
|
}
|
|
-(void)partialInvalidator __attribute__((annotate("objc_instance_variable_invalidator_partial")));
|
|
-(void)partialInvalidatorCallsPartial __attribute__((annotate("objc_instance_variable_invalidator_partial")));
|
|
@end
|
|
@implementation SomeNotInvalidatedInPartial {
|
|
SomeInvalidationImplementingObject *Ivar3;
|
|
#if RUN_IVAR_INVALIDATION
|
|
// expected-warning@-2 {{Instance variable Ivar3 needs to be invalidated or set to nil}}
|
|
#endif
|
|
}
|
|
-(void)partialInvalidator {
|
|
Ivar1 = 0;
|
|
}
|
|
-(void)partialInvalidatorCallsPartial {
|
|
[self partialInvalidator];
|
|
}
|
|
@end
|
|
|
|
@interface OnlyPartialDeclsBase : NSObject
|
|
-(void)partialInvalidator __attribute__((annotate("objc_instance_variable_invalidator_partial")));
|
|
@end
|
|
@implementation OnlyPartialDeclsBase
|
|
-(void)partialInvalidator {}
|
|
@end
|
|
|
|
@interface OnlyPartialDecls : OnlyPartialDeclsBase {
|
|
SomeInvalidationImplementingObject *Ivar1;
|
|
#if RUN_IVAR_INVALIDATION
|
|
// expected-warning@-2 {{Instance variable Ivar1 needs to be invalidated; no invalidation method is defined in the @implementation for OnlyPartialDecls}}
|
|
#endif
|
|
}
|
|
@end
|
|
@implementation OnlyPartialDecls
|
|
@end
|
|
|
|
// False negative.
|
|
@interface PartialCallsFull : SomeInvalidationImplementingObject {
|
|
SomeInvalidationImplementingObject *Ivar1;
|
|
}
|
|
-(void)partialInvalidator __attribute__((annotate("objc_instance_variable_invalidator_partial")));
|
|
@end
|
|
@implementation PartialCallsFull
|
|
-(void)partialInvalidator {
|
|
[self invalidate];
|
|
} // TODO: It would be nice to check that the full invalidation method actually invalidates the ivar.
|
|
@end
|
|
|