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