// RUN: %clang_cc1 -arcmt-check -verify -triple x86_64-apple-darwin10 -fblocks -Werror %s

#if __has_feature(objc_arc)
#define NS_AUTOMATED_REFCOUNT_UNAVAILABLE __attribute__((unavailable("not available in automatic reference counting mode")))
#else
#define NS_AUTOMATED_REFCOUNT_UNAVAILABLE
#endif

typedef const void * CFTypeRef;
CFTypeRef CFBridgingRetain(id X);
id CFBridgingRelease(CFTypeRef);

typedef int BOOL;
typedef unsigned NSUInteger;

@protocol NSObject
- (id)retain NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
- (NSUInteger)retainCount NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
- (oneway void)release NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
- (id)autorelease NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
@end

@interface NSObject <NSObject> {}
- (id)init;

+ (id)new;
+ (id)alloc;
- (void)dealloc;

- (void)finalize;

- (id)copy;
- (id)mutableCopy;
@end

typedef const struct __CFString * CFStringRef;
extern const CFStringRef kUTTypePlainText;
extern const CFStringRef kUTTypeRTF;
@class NSString;
@class A;

struct UnsafeS {
  A *__unsafe_unretained unsafeObj;
};

@interface A : NSObject
- (id)retain; // expected-note {{'retain' has been explicitly marked unavailable here}}
- (id)retainCount; // expected-note {{'retainCount' has been explicitly marked unavailable here}}
- (id)autorelease; // expected-note 2 {{'autorelease' has been explicitly marked unavailable here}}
- (id)init;
- (oneway void)release;
- (void)dealloc;
-(void)test;
-(id)delegate;
@end

@implementation A
-(void)test {
  [super dealloc];
}
-(void)dealloc {
  [super dealloc];
}

- (id)retain { return self; } // expected-error {{ARC forbids implementation}}
- (id)retainCount { return self; } // expected-error {{ARC forbids implementation}}
- (id)autorelease { return self; } // expected-error {{ARC forbids implementation}}
- (oneway void)release { } // expected-error {{ARC forbids implementation}}

-(id)delegate { return self; }
@end

id global_foo;

void test1(A *a, BOOL b, struct UnsafeS *unsafeS) {
  [[a delegate] release]; // expected-error {{it is not safe to remove 'retain' message on the result of a 'delegate' message; the object that was passed to 'setDelegate:' may not be properly retained}} \
                          // expected-error {{ARC forbids explicit message send}}
  [a.delegate release]; // expected-error {{it is not safe to remove 'retain' message on the result of a 'delegate' message; the object that was passed to 'setDelegate:' may not be properly retained}} \
                        // expected-error {{ARC forbids explicit message send}}
  [unsafeS->unsafeObj retain]; // expected-error {{it is not safe to remove 'retain' message on an __unsafe_unretained type}} \
                               // expected-error {{ARC forbids explicit message send}} \
                               // expected-error {{'retain' is unavailable}}
  id foo = [unsafeS->unsafeObj retain]; // no warning.
  [global_foo retain]; // expected-error {{it is not safe to remove 'retain' message on a global variable}} \
                       // expected-error {{ARC forbids explicit message send}}
  [global_foo release]; // expected-error {{it is not safe to remove 'release' message on a global variable}} \
                        // expected-error {{ARC forbids explicit message send}}
  [a dealloc];
  [a retain];
  [a retainCount]; // expected-error {{ARC forbids explicit message send of 'retainCount'}} \
                   // expected-error {{'retainCount' is unavailable}}
  [a release];
  [a autorelease]; // expected-error {{it is not safe to remove an unused 'autorelease' message; its receiver may be destroyed immediately}} \
                   // expected-error {{ARC forbids explicit message send}} \
                   // expected-error {{'autorelease' is unavailable}}
  [a autorelease]; // expected-error {{it is not safe to remove an unused 'autorelease' message; its receiver may be destroyed immediately}} \
                   // expected-error {{ARC forbids explicit message send}} \
                   // expected-error {{'autorelease' is unavailable}}
  a = 0;

  CFStringRef cfstr;
  NSString *str = (NSString *)cfstr; // expected-error {{cast of C pointer type 'CFStringRef' (aka 'const struct __CFString *') to Objective-C pointer type 'NSString *' requires a bridged cast}} \
  // expected-note {{use __bridge to convert directly (no change in ownership)}} \
  // expected-note {{use CFBridgingRelease call to transfer ownership of a +1 'CFStringRef' (aka 'const struct __CFString *') into ARC}} \
  str = (NSString *)kUTTypePlainText;
  str = b ? kUTTypeRTF : kUTTypePlainText;
  str = (NSString *)(b ? kUTTypeRTF : kUTTypePlainText);
  str = (NSString *)a; // no change.

  SEL s = @selector(retain);  // expected-error {{ARC forbids use of 'retain' in a @selector}}
  s = @selector(release); // expected-error {{ARC forbids use of 'release' in a @selector}}
  s = @selector(autorelease); // expected-error {{ARC forbids use of 'autorelease' in a @selector}}
  s = @selector(dealloc); // expected-error {{ARC forbids use of 'dealloc' in a @selector}}

  static id __autoreleasing X1; // expected-error {{global variables cannot have __autoreleasing ownership}}
}

struct S {
  A* a; // expected-error {{ARC forbids Objective-C objects in struct}}
};

@interface B
-(id)alloc;
- (id)initWithInt: (int) i;
@end

void rdar8861761() {
  B *o1 = [[B alloc] initWithInt:0];
  B *o2 = [B alloc];
  [o2 initWithInt:0];
}

@interface Test13
- (id) init0;
- (void) noninit;
@end
@implementation Test13
- (id) init0 {
  self = 0;
}
- (void) noninit {
  self = 0; // expected-error {{cannot assign to 'self' outside of a method in the init family}}

  for (__strong id x in collection) { // expected-error {{use of undeclared identifier 'collection'}}
    x = 0;
  }
}
@end

void * cvt(id arg)
{
  void* voidp_val;
  (void)(int*)arg; // expected-error {{disallowed}}
  (void)(id)arg;
  (void)(__autoreleasing id*)arg; // expected-error {{disallowed}}
  (void)(id*)arg; // expected-error {{disallowed}}

  (void)(__autoreleasing id**)voidp_val;
  (void)(void*)voidp_val;
  (void)(void**)arg; // expected-error {{disallowed}}
  cvt((void*)arg); // expected-error 2 {{requires a bridged cast}} \
                   // expected-note 2 {{use __bridge to}} expected-note {{use CFBridgingRelease call}} expected-note {{use CFBridgingRetain call}}
  cvt(0);
  (void)(__strong id**)(0);
  return arg; // expected-error {{requires a bridged cast}} expected-note {{use __bridge}} expected-note {{use CFBridgingRetain call}}
}


void test12(id collection) {
  for (id x in collection) {
    x = 0;
  }

  for (__strong id x in collection) {
    x = 0;
  }
}

void test6(unsigned cond) {
  switch (cond) {
  case 0:
    ;
    id x; // expected-note {{jump bypasses initialization of retaining variable}}

  case 1: // expected-error {{cannot jump}}
    x = 0;
    break;
  }
}

@class Test8_incomplete;
@interface Test8_complete @end;
@interface Test8_super @end;
@interface Test8 : Test8_super
- (id) init00;
- (id) init01; // expected-note {{declaration in interface}}
- (id) init02;
- (id) init03; // covariance
- (id) init04; // covariance
- (id) init05;

- (void) init10; // expected-note {{declaration in interface is not in the 'init' family because its result type is not an object pointer}}
- (void) init11;
- (void) init12;
- (void) init13; // expected-note {{declaration in interface is not in the 'init' family because its result type is not an object pointer}}
- (void) init14; // expected-note {{declaration in interface is not in the 'init' family because its result type is not an object pointer}}
- (void) init15;

// These should be invalid to actually call.
- (Test8_incomplete*) init20;
- (Test8_incomplete*) init21; // expected-note {{declaration in interface}}
- (Test8_incomplete*) init22;
- (Test8_incomplete*) init23;
- (Test8_incomplete*) init24;
- (Test8_incomplete*) init25;

- (Test8_super*) init30; // id exception to covariance
- (Test8_super*) init31; // expected-note {{declaration in interface}}
- (Test8_super*) init32;
- (Test8_super*) init33;
- (Test8_super*) init34; // covariance
- (Test8_super*) init35;

- (Test8*) init40; // id exception to covariance
- (Test8*) init41; // expected-note {{declaration in interface}}
- (Test8*) init42;
- (Test8*) init43; // this should be a warning, but that's a general language thing, not an ARC thing
- (Test8*) init44;
- (Test8*) init45;

- (Test8_complete*) init50; // expected-error {{init methods must return a type related to the receiver type}}
- (Test8_complete*) init51; // expected-error {{init methods must return a type related to the receiver type}}
- (Test8_complete*) init52; // expected-error {{init methods must return a type related to the receiver type}}
- (Test8_complete*) init53; // expected-error {{init methods must return a type related to the receiver type}}
- (Test8_complete*) init54; // expected-error {{init methods must return a type related to the receiver type}}
- (Test8_complete*) init55; // expected-error {{init methods must return a type related to the receiver type}}
@end
@implementation Test8
- (id) init00 { return 0; }
- (id) init10 { return 0; } // expected-error {{method implementation does not match its declaration}}
- (id) init20 { return 0; }
- (id) init30 { return 0; }
- (id) init40 { return 0; }
- (id) init50 { return 0; }

- (void) init01 {} // expected-error {{method was declared as an 'init' method, but its implementation doesn't match because its result type is not an object pointer}}
- (void) init11 {}
- (void) init21 {} // expected-error {{method was declared as an 'init' method, but its implementation doesn't match because its result type is not an object pointer}}
- (void) init31 {} // expected-error {{method was declared as an 'init' method, but its implementation doesn't match because its result type is not an object pointer}}
- (void) init41 {} // expected-error {{method was declared as an 'init' method, but its implementation doesn't match because its result type is not an object pointer}}
- (void) init51 {}

- (Test8_incomplete*) init02 { return 0; } // expected-error {{init methods must return a type related to the receiver type}}
- (Test8_incomplete*) init12 { return 0; } // expected-error {{init methods must return a type related to the receiver type}}
- (Test8_incomplete*) init22 { return 0; } // expected-error {{init methods must return a type related to the receiver type}}
- (Test8_incomplete*) init32 { return 0; } // expected-error {{init methods must return a type related to the receiver type}}
- (Test8_incomplete*) init42 { return 0; } // expected-error {{init methods must return a type related to the receiver type}}
- (Test8_incomplete*) init52 { return 0; } // expected-error {{init methods must return a type related to the receiver type}}

- (Test8_super*) init03 { return 0; }
- (Test8_super*) init13 { return 0; } // expected-error {{method implementation does not match its declaration}}
- (Test8_super*) init23 { return 0; }
- (Test8_super*) init33 { return 0; }
- (Test8_super*) init43 { return 0; }
- (Test8_super*) init53 { return 0; }

- (Test8*) init04 { return 0; }
- (Test8*) init14 { return 0; } // expected-error {{method implementation does not match its declaration}}
- (Test8*) init24 { return 0; }
- (Test8*) init34 { return 0; }
- (Test8*) init44 { return 0; }
- (Test8*) init54 { return 0; }

- (Test8_complete*) init05 { return 0; } // expected-error {{init methods must return a type related to the receiver type}}
- (Test8_complete*) init15 { return 0; } // expected-error {{init methods must return a type related to the receiver type}}
- (Test8_complete*) init25 { return 0; } // expected-error {{init methods must return a type related to the receiver type}}
- (Test8_complete*) init35 { return 0; } // expected-error {{init methods must return a type related to the receiver type}}
- (Test8_complete*) init45 { return 0; } // expected-error {{init methods must return a type related to the receiver type}}
- (Test8_complete*) init55 { return 0; } // expected-error {{init methods must return a type related to the receiver type}}
@end

@class Test9_incomplete;
@interface Test9
- (Test9_incomplete*) init1; // expected-error {{init methods must return a type related to the receiver type}}
- (Test9_incomplete*) init2;
@end
id test9(Test9 *v) {
  return [v init1];
}

// rdar://9491791
void rdar9491791(int p) {
  switch (p) {
  case 3:;
    NSObject *o = [[NSObject alloc] init];
    [o release];
    break;
  default:
    break;
  }
}

#define RELEASE_MACRO(x) do { [x release]; } while(1)

// rdar://9504750
void rdar9504750(id p) {
  RELEASE_MACRO(p); // expected-error {{ARC forbids explicit message send of 'release'}} 
}

// rdar://8939557
@interface TestReadonlyProperty : NSObject
@property(assign,readonly) NSObject *value;
@end

@implementation TestReadonlyProperty
@synthesize value;
- (void)viewDidLoad {
  value = [NSObject new]; // expected-error {{assigning retained object}}
}
@end

// rdar://9601437
@interface I9601437 {
  __unsafe_unretained id x;
}
-(void)Meth;
@end

@implementation I9601437
-(void)Meth {
  self->x = [NSObject new]; // expected-error {{assigning retained object}}
}
@end

@interface Test10 : NSObject {
  CFStringRef cfstr;
}
@property (retain) id prop;
-(void)foo;
@end

void test(Test10 *x) {
  x.prop = ^{ [x foo]; }; // expected-warning {{likely to lead to a retain cycle}} \
                          // expected-note {{retained by the captured object}}
}

@implementation Test10
-(void)foo {
  ^{
    NSString *str = (NSString *)cfstr; // expected-error {{cast of C pointer type 'CFStringRef' (aka 'const struct __CFString *') to Objective-C pointer type 'NSString *' requires a bridged cast}} \
    // expected-note {{use __bridge to convert directly (no change in ownership)}} \
    // expected-note {{use CFBridgingRelease call to transfer ownership of a +1 'CFStringRef' (aka 'const struct __CFString *') into ARC}}
  };
}
@end