objc_invalidation.m revision 26db7dbf67b1532b2d617b3a85428699a1ffc997
1// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.osx.cocoa.InstanceVariableInvalidation -fobjc-default-synthesize-properties -verify %s
2extern void __assert_fail (__const char *__assertion, __const char *__file,
3    unsigned int __line, __const char *__function)
4     __attribute__ ((__noreturn__));
5
6#define assert(expr) \
7  ((expr)  ? (void)(0)  : __assert_fail (#expr, __FILE__, __LINE__, __func__))
8
9@protocol NSObject
10@end
11@interface NSObject <NSObject> {}
12+(id)alloc;
13+(id)new;
14-(id)init;
15-(id)autorelease;
16-(id)copy;
17- (Class)class;
18-(id)retain;
19-(id)description;
20@end
21@class NSString;
22
23extern void NSLog(NSString *format, ...) __attribute__((format(__NSString__, 1, 2)));
24
25@protocol Invalidation1 <NSObject> 
26- (void) invalidate __attribute__((annotate("objc_instance_variable_invalidator")));
27@end 
28
29@protocol Invalidation2 <NSObject> 
30- (void) invalidate __attribute__((annotate("objc_instance_variable_invalidator")));
31@end 
32
33@protocol Invalidation3 <NSObject>
34- (void) invalidate __attribute__((annotate("objc_instance_variable_invalidator")));
35- (void) invalidate2 __attribute__((annotate("objc_instance_variable_invalidator")));
36@end
37
38@protocol Invalidation3;
39@protocol Invalidation2;
40
41@interface Invalidation2Class <Invalidation2>
42@end
43
44@interface Invalidation1Class <Invalidation1>
45@end
46
47@interface ClassWithInvalidationMethodInCategory <NSObject>
48@end
49
50@interface ClassWithInvalidationMethodInCategory ()
51- (void) invalidate __attribute__((annotate("objc_instance_variable_invalidator")));
52@end
53
54@interface SomeInvalidationImplementingObject: NSObject <Invalidation3, Invalidation2> {
55  SomeInvalidationImplementingObject *ObjA; // invalidation in the parent
56}
57@end
58
59@implementation SomeInvalidationImplementingObject
60- (void)invalidate{
61  ObjA = 0;
62}
63- (void)invalidate2 {
64  [self invalidate];
65}
66@end
67
68@interface SomeSubclassInvalidatableObject : SomeInvalidationImplementingObject {
69  SomeInvalidationImplementingObject *Ivar1; // regular ivar
70  SomeInvalidationImplementingObject *Ivar2; // regular ivar, sending invalidate message
71  SomeInvalidationImplementingObject *_Ivar3; // no property, call -description
72  SomeInvalidationImplementingObject *_Ivar4; // no property, provide as argument to NSLog()
73
74  SomeInvalidationImplementingObject *_Prop1; // partially implemented property, set to 0 with dot syntax
75  SomeInvalidationImplementingObject *_Prop2; // fully implemented prop, set to 0 with dot syntax
76  SomeInvalidationImplementingObject *_propIvar; // property with custom named ivar, set to 0 via setter
77  Invalidation1Class *MultipleProtocols; // regular ivar belonging to a different class
78  Invalidation2Class *MultInheritance; // regular ivar belonging to a different class
79  SomeInvalidationImplementingObject *_Prop3; // property, invalidate via sending a message to a getter method
80  SomeInvalidationImplementingObject *_Prop4; // property with @synthesize, invalidate via property
81  SomeInvalidationImplementingObject *_Prop5; // property with @synthesize, invalidate via getter method
82  SomeInvalidationImplementingObject *_Prop8;
83  
84  // Ivars invalidated by the partial invalidator. 
85  SomeInvalidationImplementingObject *Ivar9;
86  SomeInvalidationImplementingObject *_Prop10;
87  SomeInvalidationImplementingObject *Ivar11;
88
89  // No warnings on these as they are not invalidatable.
90  NSObject *NIvar1;
91  NSObject *NObj2;
92  NSObject *_NProp1;
93  NSObject *_NpropIvar;
94}
95
96@property (assign) SomeInvalidationImplementingObject* Prop0;
97@property (nonatomic, assign) SomeInvalidationImplementingObject* Prop1;
98@property (assign) SomeInvalidationImplementingObject* Prop2;
99@property (assign) SomeInvalidationImplementingObject* Prop3;
100@property (assign) SomeInvalidationImplementingObject *Prop5;
101@property (assign) SomeInvalidationImplementingObject *Prop4;
102
103@property (assign) SomeInvalidationImplementingObject* Prop6; // automatically synthesized prop
104@property (assign) SomeInvalidationImplementingObject* Prop7; // automatically synthesized prop
105@property (assign) SomeInvalidationImplementingObject *SynthIvarProp;
106
107@property (assign) NSObject* NProp0;
108@property (nonatomic, assign) NSObject* NProp1;
109@property (assign) NSObject* NProp2;
110
111-(void)setProp1: (SomeInvalidationImplementingObject*) InO;
112-(void)setNProp1: (NSObject*) InO;
113
114-(void)invalidate;
115
116// Partial invalidators invalidate only some ivars. They are guaranteed to be 
117// called before the invalidation methods.
118-(void)partialInvalidator1 __attribute__((annotate("objc_instance_variable_invalidator_partial")));
119-(void)partialInvalidator2 __attribute__((annotate("objc_instance_variable_invalidator_partial")));
120@end
121
122@interface SomeSubclassInvalidatableObject()
123@property (assign) SomeInvalidationImplementingObject* Prop8;
124@property (assign) SomeInvalidationImplementingObject* Prop10;
125@end
126
127@implementation SomeSubclassInvalidatableObject{
128  @private
129  SomeInvalidationImplementingObject *Ivar5;
130  ClassWithInvalidationMethodInCategory *Ivar13;
131}
132
133@synthesize Prop7 = _propIvar;
134@synthesize Prop3 = _Prop3;
135@synthesize Prop5 = _Prop5;
136@synthesize Prop4 = _Prop4;
137@synthesize Prop8 = _Prop8;
138@synthesize Prop10 = _Prop10;
139
140
141- (void) setProp1: (SomeInvalidationImplementingObject*) InObj {
142  _Prop1 = InObj;
143}
144
145- (void) setProp2: (SomeInvalidationImplementingObject*) InObj {
146  _Prop2 = InObj;
147}
148- (SomeInvalidationImplementingObject*) Prop2 {
149  return _Prop2;
150}
151
152@synthesize NProp2 = _NpropIvar;
153
154- (void) setNProp1: (NSObject*) InObj {
155  _NProp1 = InObj;
156}
157
158- (void) invalidate {
159   [Ivar2 invalidate];
160   self.Prop0 = 0;
161   self.Prop1 = 0;
162   [self setProp2:0];
163   [self setProp3:0];
164   [[self Prop5] invalidate2];
165   [self.Prop4 invalidate];
166   [self.Prop8 invalidate];
167   self.Prop6 = 0;
168   [[self Prop7] invalidate];
169
170   [_Ivar3 description]; 
171   NSLog(@"%@", _Ivar4);
172   [super invalidate];
173}
174// expected-warning@-1 {{Instance variable Ivar1 needs to be invalidated}}
175// expected-warning@-2 {{Instance variable MultipleProtocols needs to be invalidated}}
176// expected-warning@-3 {{Instance variable MultInheritance needs to be invalidated}}
177// expected-warning@-4 {{Property SynthIvarProp needs to be invalidated or set to nil}}
178// expected-warning@-5 {{Instance variable _Ivar3 needs to be invalidated}}
179// expected-warning@-6 {{Instance variable _Ivar4 needs to be invalidated}}
180// expected-warning@-7 {{Instance variable Ivar5 needs to be invalidated or set to nil}}
181// expected-warning@-8 {{Instance variable Ivar13 needs to be invalidated or set to nil}}
182
183
184-(void)partialInvalidator1 {
185  [Ivar9 invalidate];
186  [_Prop10 invalidate];
187}
188
189-(void)partialInvalidator2 {
190  [Ivar11 invalidate];
191}
192
193@end
194
195// Example, where the same property is inherited through 
196// the parent and directly through a protocol. If a property backing ivar is 
197// synthesized in the parent, let the parent invalidate it.
198
199@protocol IDEBuildable <NSObject>
200@property (readonly, strong) id <Invalidation2> ObjB;
201@end
202
203@interface Parent : NSObject <IDEBuildable, Invalidation2> {
204  Invalidation2Class *_ObjB; // Invalidation of ObjB happens in the parent.
205}
206@end
207
208@interface Child: Parent <Invalidation2, IDEBuildable> 
209@end
210
211@implementation Parent{
212  @private
213  Invalidation2Class *Ivar10;
214  Invalidation2Class *Ivar11;
215  Invalidation2Class *Ivar12;
216}
217
218@synthesize ObjB = _ObjB;
219- (void)invalidate{
220  _ObjB = ((void*)0);
221  
222  assert(Ivar10 == 0);
223
224  if (__builtin_expect(!(Ivar11 == ((void*)0)), 0))
225    assert(0);
226
227  assert(0 == Ivar12);
228
229}
230@end
231
232@implementation Child
233- (void)invalidate{ 
234  // no-warning
235} 
236@end
237
238@protocol Invalidation <NSObject>
239- (void)invalidate __attribute__((annotate("objc_instance_variable_invalidator")));
240@end
241
242@interface Foo : NSObject <Invalidation>
243@end
244
245@class FooBar;
246@protocol FooBar_Protocol <NSObject>
247@end
248
249@interface MissingInvalidationMethod : Foo <FooBar_Protocol>
250@property (assign) MissingInvalidationMethod *foobar15_warn; // expected-warning {{Property foobar15_warn needs to be invalidated; no invalidation method is defined in the @implementation for MissingInvalidationMethod}}
251@end
252@implementation MissingInvalidationMethod
253@end
254
255@interface MissingInvalidationMethod2 : Foo <FooBar_Protocol> {
256  Foo *Ivar1;// expected-warning {{Instance variable Ivar1 needs to be invalidated; no invalidation method is defined in the @implementation for MissingInvalidationMethod2}}
257}
258@end
259@implementation MissingInvalidationMethod2
260@end
261
262@interface MissingInvalidationMethodDecl : NSObject {
263  Foo *Ivar1;// expected-warning {{Instance variable Ivar1 needs to be invalidated; no invalidation method is declared for MissingInvalidationMethodDecl}}
264}
265@end
266@implementation MissingInvalidationMethodDecl
267@end
268
269@interface MissingInvalidationMethodDecl2 : NSObject {
270@private
271    Foo *_foo1; // expected-warning {{Instance variable _foo1 needs to be invalidated; no invalidation method is declared for MissingInvalidationMethodDecl2}} 
272}
273@property (strong) Foo *bar1; 
274@end
275@implementation MissingInvalidationMethodDecl2
276@end
277
278@interface InvalidatedInPartial : SomeInvalidationImplementingObject {
279  SomeInvalidationImplementingObject *Ivar1; 
280  SomeInvalidationImplementingObject *Ivar2; 
281}
282-(void)partialInvalidator __attribute__((annotate("objc_instance_variable_invalidator_partial")));
283@end
284@implementation InvalidatedInPartial
285-(void)partialInvalidator {
286  [Ivar1 invalidate];
287  Ivar2 = 0;
288}
289@end
290
291@interface NotInvalidatedInPartial : SomeInvalidationImplementingObject {
292  SomeInvalidationImplementingObject *Ivar1; 
293}
294-(void)partialInvalidator __attribute__((annotate("objc_instance_variable_invalidator_partial")));
295-(void)partialInvalidatorCallsPartial __attribute__((annotate("objc_instance_variable_invalidator_partial")));
296@end
297@implementation NotInvalidatedInPartial
298-(void)partialInvalidator {
299}
300-(void)partialInvalidatorCallsPartial {
301  [self partialInvalidator];
302}
303
304-(void)invalidate {
305} // expected-warning {{Instance variable Ivar1 needs to be invalidated or set to nil}}
306
307@end
308
309// False negative.
310@interface PartialCallsFull : SomeInvalidationImplementingObject {
311  SomeInvalidationImplementingObject *Ivar1;
312}
313-(void)partialInvalidator __attribute__((annotate("objc_instance_variable_invalidator_partial")));
314@end
315@implementation PartialCallsFull
316-(void)partialInvalidator {
317 [self invalidate];
318} // TODO: It would be nice to check that the full invalidation method actually invalidates the ivar. 
319@end
320
321