diff options
Diffstat (limited to 'clang/test/SemaObjC/warn-retain-cycle.m')
| -rw-r--r-- | clang/test/SemaObjC/warn-retain-cycle.m | 125 | 
1 files changed, 125 insertions, 0 deletions
diff --git a/clang/test/SemaObjC/warn-retain-cycle.m b/clang/test/SemaObjC/warn-retain-cycle.m new file mode 100644 index 0000000..00fd234 --- /dev/null +++ b/clang/test/SemaObjC/warn-retain-cycle.m @@ -0,0 +1,125 @@ +// RUN: %clang_cc1 -fsyntax-only -fobjc-runtime-has-weak -fobjc-arc -fblocks -verify -Wno-objc-root-class %s + +@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]; }; +} + +@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 +  | 
