| // RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.osx.cocoa.InstanceVariableInvalidation -DRUN_IVAR_INVALIDATION -verify %s |
| // RUN: %clang_analyze_cc1 -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 |
| |