187 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Objective-C
		
	
	
	
	
	
			
		
		
	
	
			187 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Objective-C
		
	
	
	
	
	
// 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
 | 
						|
  })];
 | 
						|
}
 | 
						|
 |