// RUN: %clang_cc1 -fsyntax-only -fobjc-runtime-has-weak -fobjc-arc -fblocks -verify -Wno-objc-root-class -Wno-implicit-retain-self %s void *_Block_copy(const void *block); @interface Test0 - (void) setBlock: (void(^)(void)) block; - (void) addBlock: (void(^)(void)) block; - (void) actNow; @end void test0(Test0 *x) { [x setBlock: // expected-note {{block will be retained by the captured object}} ^{ [x actNow]; }]; // expected-warning {{capturing 'x' strongly in this block is likely to lead to a retain cycle}} x.block = // expected-note {{block will be retained by the captured object}} ^{ [x actNow]; }; // expected-warning {{capturing 'x' strongly in this block is likely to lead to a retain cycle}} [x addBlock: // expected-note {{block will be retained by the captured object}} ^{ [x actNow]; }]; // expected-warning {{capturing 'x' strongly in this block is likely to lead to a retain cycle}} // These actually don't cause retain cycles. __weak Test0 *weakx = x; [x addBlock: ^{ [weakx actNow]; }]; [x setBlock: ^{ [weakx actNow]; }]; x.block = ^{ [weakx actNow]; }; // These do cause retain cycles, but we're not clever enough to figure that out. [weakx addBlock: ^{ [x actNow]; }]; [weakx setBlock: ^{ [x actNow]; }]; weakx.block = ^{ [x actNow]; }; // rdar://11702054 x.block = ^{ (void)x.actNow; }; // expected-warning {{capturing 'x' strongly in this block is likely to lead to a retain cycle}} \ // expected-note {{block will be retained by the captured object}} } @interface BlockOwner @property (retain) void (^strong)(void); // expected-warning {{retain'ed block property does not copy the block - use copy attribute instead}} @end @interface Test1 { @public BlockOwner *owner; }; @property (retain) BlockOwner *owner; @property (assign) __strong BlockOwner *owner2; // expected-error {{unsafe_unretained property 'owner2' may not also be declared __strong}} @property (assign) BlockOwner *owner3; @end void test1(Test1 *x) { x->owner.strong = ^{ (void) x; }; // expected-warning {{retain cycle}} expected-note {{block will be retained by an object strongly retained by the captured object}} x.owner.strong = ^{ (void) x; }; // expected-warning {{retain cycle}} expected-note {{block will be retained by an object strongly retained by the captured object}} x.owner2.strong = ^{ (void) x; }; x.owner3.strong = ^{ (void) x; }; } @implementation Test1 { BlockOwner * __unsafe_unretained owner3ivar; __weak BlockOwner *weakowner; } @dynamic owner; @dynamic owner2; @synthesize owner3 = owner3ivar; - (id) init { self.owner.strong = ^{ (void) owner; }; // expected-warning {{retain cycle}} expected-note {{block will be retained by an object strongly retained by the captured object}} self.owner2.strong = ^{ (void) owner; }; // TODO: should we warn here? What's the story with this kind of mismatch? self.owner3.strong = ^{ (void) owner; }; owner.strong = ^{ (void) owner; }; // expected-warning {{retain cycle}} expected-note {{block will be retained by an object strongly retained by the captured object}} owner.strong = ^{ ^{ (void) owner; }(); }; // expected-warning {{retain cycle}} expected-note {{block will be retained by an object strongly retained by the captured object}} owner.strong = ^{ (void) sizeof(self); // expected-note {{block will be retained by an object strongly retained by the captured object}} (void) owner; }; // expected-warning {{capturing 'self' strongly in this block is likely to lead to a retain cycle}} weakowner.strong = ^{ (void) owner; }; return self; } - (void) foo { owner.strong = ^{ (void) owner; }; // expected-warning {{retain cycle}} expected-note {{block will be retained by an object strongly retained by the captured object}} } @end void test2_helper(id); @interface Test2 { void (^block)(void); id x; } @end @implementation Test2 - (void) test { block = ^{ // expected-note {{block will be retained by an object strongly retained by the captured object}} test2_helper(x); // expected-warning {{capturing 'self' strongly in this block is likely to lead to a retain cycle}} }; } @end @interface NSOperationQueue {} - (void)addOperationWithBlock:(void (^)(void))block; - (void)addSomethingElse:(void (^)(void))block; @end @interface Test3 { NSOperationQueue *myOperationQueue; unsigned count; } @end void doSomething(unsigned v); @implementation Test3 - (void) test { // 'addOperationWithBlock:' is specifically whitelisted. [myOperationQueue addOperationWithBlock:^() { // no-warning if (count > 20) { doSomething(count); } }]; } - (void) test_positive { // Sanity check that we are really whitelisting 'addOperationWithBlock:' and not doing // something funny. [myOperationQueue addSomethingElse:^() { // expected-note {{block will be retained by an object strongly retained by the captured object}} if (count > 20) { // expected-warning {{capturing 'self' strongly in this block is likely to lead to a retain cycle}} doSomething(count); } }]; } @end void testBlockVariable() { typedef void (^block_t)(void); // This case will be caught by -Wuninitialized, and does not create a // retain cycle. block_t a1 = ^{ a1(); // no-warning }; // This case will also be caught by -Wuninitialized. block_t a2; a2 = ^{ a2(); // no-warning }; __block block_t b1 = ^{ // expected-note{{block will be retained by the captured object}} b1(); // expected-warning{{capturing 'b1' strongly in this block is likely to lead to a retain cycle}} }; __block block_t b2; b2 = ^{ // expected-note{{block will be retained by the captured object}} b2(); // expected-warning{{capturing 'b2' strongly in this block is likely to lead to a retain cycle}} }; } @interface NSObject - (id)copy; - (void (^)(void))someRandomMethodReturningABlock; @end void testCopying(Test0 *obj) { typedef void (^block_t)(void); [obj setBlock:[^{ // expected-note{{block will be retained by the captured object}} [obj actNow]; // expected-warning{{capturing 'obj' strongly in this block is likely to lead to a retain cycle}} } copy]]; [obj addBlock:(__bridge_transfer block_t)_Block_copy((__bridge void *)^{ // expected-note{{block will be retained by the captured object}} [obj actNow]; // expected-warning{{capturing 'obj' strongly in this block is likely to lead to a retain cycle}} })]; [obj addBlock:[^{ [obj actNow]; // no-warning } someRandomMethodReturningABlock]]; extern block_t someRandomFunctionReturningABlock(block_t); [obj setBlock:someRandomFunctionReturningABlock(^{ [obj actNow]; // no-warning })]; }