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