xref: /llvm-project/clang/test/SemaObjC/warn-retain-cycle.m (revision 0f1c1be1968076d6f96f8a7bcc4a15cf195ecd97)
1// RUN: %clang_cc1 -fsyntax-only -fobjc-runtime-has-weak -fobjc-arc -fblocks -verify -Wno-objc-root-class -Wno-implicit-retain-self %s
2
3void *_Block_copy(const void *block);
4
5@interface Test0
6- (void) setBlock: (void(^)(void)) block;
7- (void) addBlock: (void(^)(void)) block;
8- (void) actNow;
9@end
10void test0(Test0 *x) {
11  [x setBlock: // expected-note {{block will be retained by the captured object}}
12       ^{ [x actNow]; }]; // expected-warning {{capturing 'x' strongly in this block is likely to lead to a retain cycle}}
13  x.block = // expected-note {{block will be retained by the captured object}}
14       ^{ [x actNow]; }; // expected-warning {{capturing 'x' strongly in this block is likely to lead to a retain cycle}}
15
16  [x addBlock: // expected-note {{block will be retained by the captured object}}
17       ^{ [x actNow]; }]; // expected-warning {{capturing 'x' strongly in this block is likely to lead to a retain cycle}}
18
19  // These actually don't cause retain cycles.
20  __weak Test0 *weakx = x;
21  [x addBlock: ^{ [weakx actNow]; }];
22  [x setBlock: ^{ [weakx actNow]; }];
23  x.block = ^{ [weakx actNow]; };
24
25  // These do cause retain cycles, but we're not clever enough to figure that out.
26  [weakx addBlock: ^{ [x actNow]; }];
27  [weakx setBlock: ^{ [x actNow]; }];
28  weakx.block = ^{ [x actNow]; };
29
30  x.block = ^{ (void)x.actNow; };  // expected-warning {{capturing 'x' strongly in this block is likely to lead to a retain cycle}} \
31                                   // expected-note {{block will be retained by the captured object}}
32}
33
34@interface BlockOwner
35@property (retain) void (^strong)(void); // expected-warning {{retain'ed block property does not copy the block - use copy attribute instead}}
36@end
37
38@interface Test1 {
39@public
40  BlockOwner *owner;
41};
42@property (retain) BlockOwner *owner;
43@property (assign) __strong BlockOwner *owner2; // expected-error {{unsafe_unretained property 'owner2' may not also be declared __strong}}
44@property (assign) BlockOwner *owner3;
45@end
46void test1(Test1 *x) {
47  x->owner.strong = ^{ (void) x; }; // expected-warning {{retain cycle}} expected-note {{block will be retained by an object strongly retained by the captured object}}
48  x.owner.strong = ^{ (void) x; }; // expected-warning {{retain cycle}} expected-note {{block will be retained by an object strongly retained by the captured object}}
49  x.owner2.strong = ^{ (void) x; };
50  x.owner3.strong = ^{ (void) x; };
51}
52
53@implementation Test1 {
54  BlockOwner * __unsafe_unretained owner3ivar;
55  __weak BlockOwner *weakowner;
56}
57@dynamic owner;
58@dynamic owner2;
59@synthesize owner3 = owner3ivar;
60
61- (id) init {
62  self.owner.strong = ^{ (void) owner; }; // expected-warning {{retain cycle}} expected-note {{block will be retained by an object strongly retained by the captured object}}
63  self.owner2.strong = ^{ (void) owner; };
64
65  // TODO: should we warn here?  What's the story with this kind of mismatch?
66  self.owner3.strong = ^{ (void) owner; };
67
68  owner.strong = ^{ (void) owner; }; // expected-warning {{retain cycle}} expected-note {{block will be retained by an object strongly retained by the captured object}}
69
70  owner.strong = ^{ ^{ (void) owner; }(); }; // expected-warning {{retain cycle}} expected-note {{block will be retained by an object strongly retained by the captured object}}
71
72  owner.strong = ^{ (void) sizeof(self); // expected-note {{block will be retained by an object strongly retained by the captured object}}
73                    (void) owner; }; // expected-warning {{capturing 'self' strongly in this block is likely to lead to a retain cycle}}
74
75  weakowner.strong = ^{ (void) owner; };
76
77  return self;
78}
79- (void) foo {
80  owner.strong = ^{ (void) owner; }; // expected-warning {{retain cycle}} expected-note {{block will be retained by an object strongly retained by the captured object}}
81}
82@end
83
84void test2_helper(id);
85@interface Test2 {
86  void (^block)(void);
87  id x;
88}
89@end
90@implementation Test2
91- (void) test {
92  block = ^{ // expected-note {{block will be retained by an object strongly retained by the captured object}}
93    test2_helper(x); // expected-warning {{capturing 'self' strongly in this block is likely to lead to a retain cycle}}
94  };
95}
96@end
97
98
99@interface NSOperationQueue {}
100- (void)addOperationWithBlock:(void (^)(void))block;
101- (void)addSomethingElse:(void (^)(void))block;
102
103@end
104
105@interface Test3 {
106  NSOperationQueue *myOperationQueue;
107  unsigned count;
108}
109@end
110void doSomething(unsigned v);
111@implementation Test3
112- (void) test {
113  // 'addOperationWithBlock:' is specifically allowlisted.
114  [myOperationQueue addOperationWithBlock:^() { // no-warning
115    if (count > 20) {
116      doSomething(count);
117    }
118  }];
119}
120- (void) test_positive {
121  // Check that we are really allowlisting 'addOperationWithBlock:' and not doing
122  // something funny.
123  [myOperationQueue addSomethingElse:^() { // expected-note {{block will be retained by an object strongly retained by the captured object}}
124    if (count > 20) {
125      doSomething(count); // expected-warning {{capturing 'self' strongly in this block is likely to lead to a retain cycle}}
126    }
127  }];
128}
129@end
130
131
132void testBlockVariable(void) {
133  typedef void (^block_t)(void);
134
135  // This case will be caught by -Wuninitialized, and does not create a
136  // retain cycle.
137  block_t a1 = ^{
138    a1(); // no-warning
139  };
140
141  // This case will also be caught by -Wuninitialized.
142  block_t a2;
143  a2 = ^{
144    a2(); // no-warning
145  };
146
147  __block block_t b1 = ^{ // expected-note{{block will be retained by the captured object}}
148    b1(); // expected-warning{{capturing 'b1' strongly in this block is likely to lead to a retain cycle}}
149  };
150
151  __block block_t b2;
152  b2 = ^{ // expected-note{{block will be retained by the captured object}}
153    b2(); // expected-warning{{capturing 'b2' strongly in this block is likely to lead to a retain cycle}}
154  };
155}
156
157
158@interface NSObject
159- (id)copy;
160
161- (void (^)(void))someRandomMethodReturningABlock;
162@end
163
164
165void testCopying(Test0 *obj) {
166  typedef void (^block_t)(void);
167
168  [obj setBlock:[^{ // expected-note{{block will be retained by the captured object}}
169    [obj actNow]; // expected-warning{{capturing 'obj' strongly in this block is likely to lead to a retain cycle}}
170  } copy]];
171
172  [obj addBlock:(__bridge_transfer block_t)_Block_copy((__bridge void *)^{ // expected-note{{block will be retained by the captured object}}
173    [obj actNow]; // expected-warning{{capturing 'obj' strongly in this block is likely to lead to a retain cycle}}
174  })];
175
176  [obj addBlock:[^{
177    [obj actNow]; // no-warning
178  } someRandomMethodReturningABlock]];
179
180  extern block_t someRandomFunctionReturningABlock(block_t);
181  [obj setBlock:someRandomFunctionReturningABlock(^{
182    [obj actNow]; // no-warning
183  })];
184}
185
186void func(int someCondition) {
187
188__block void(^myBlock)(void) = ^{
189        if (someCondition) {
190            doSomething(1);
191            myBlock();
192        }
193        else {
194	    myBlock = ((void*)0);
195        }
196   };
197
198}
199
200typedef void (^a_block_t)(void);
201
202@interface HonorNoEscape
203- (void)addStuffUsingBlock:(__attribute__((noescape)) a_block_t)block;
204@end
205
206void testNoEscape(HonorNoEscape *obj) {
207  [obj addStuffUsingBlock:^{
208    (void)obj; // ok.
209  }];
210}
211