// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 -analyzer-checker=core,alpha.core.StackAddressAsyncEscape -fblocks -fobjc-arc -verify %s

typedef struct dispatch_queue_s *dispatch_queue_t;
typedef void (^dispatch_block_t)(void);
void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
typedef long dispatch_once_t;
void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);
typedef long dispatch_time_t;
void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block);

extern dispatch_queue_t queue;
extern dispatch_once_t *predicate;
extern dispatch_time_t when;

void test_block_expr_async() {
  int x = 123;
  int *p = &x;

  dispatch_async(queue, ^{
    *p = 321;
  });
  // expected-warning@-3 {{Address of stack memory associated with local variable 'x' \
is captured by an asynchronously-executed block}}
}

void test_block_expr_once_no_leak() {
  int x = 123;
  int *p = &x;
  // synchronous, no warning
  dispatch_once(predicate, ^{
    *p = 321;
  });
}

void test_block_expr_after() {
  int x = 123;
  int *p = &x;
  dispatch_after(when, queue, ^{
    *p = 321;
  });
  // expected-warning@-3 {{Address of stack memory associated with local variable 'x' \
is captured by an asynchronously-executed block}}
}

void test_block_expr_async_no_leak() {
  int x = 123;
  int *p = &x;
  // no leak
  dispatch_async(queue, ^{
    int y = x;
    ++y;
  });
}

void test_block_var_async() {
  int x = 123;
  int *p = &x;
  void (^b)(void) = ^void(void) {
    *p = 1; 
  };
  dispatch_async(queue, b);
  // expected-warning@-1 {{Address of stack memory associated with local variable 'x' \
is captured by an asynchronously-executed block}}
}

void test_block_with_ref_async() {
  int x = 123;
  int &r = x;
  void (^b)(void) = ^void(void) {
    r = 1; 
  };
  dispatch_async(queue, b);
  // expected-warning@-1 {{Address of stack memory associated with local variable 'x' \
is captured by an asynchronously-executed block}}
}

dispatch_block_t get_leaking_block() {
  int leaked_x = 791;
  int *p = &leaked_x;
  return ^void(void) {
    *p = 1; 
  };
  // expected-warning@-3 {{Address of stack memory associated with local variable 'leaked_x' \
is captured by a returned block}}
}

void test_returned_from_func_block_async() {
  dispatch_async(queue, get_leaking_block());
  // expected-warning@-1 {{Address of stack memory associated with local variable 'leaked_x' \
is captured by an asynchronously-executed block}}
}

// synchronous, no leak
void test_block_var_once() {
  int x = 123;
  int *p = &x;
  void (^b)(void) = ^void(void) {
    *p = 1; 
  };
  dispatch_once(predicate, b); // no-warning
}

void test_block_var_after() {
  int x = 123;
  int *p = &x;
  void (^b)(void) = ^void(void) {
    *p = 1; 
  };
  dispatch_after(when, queue, b);
  // expected-warning@-1 {{Address of stack memory associated with local variable 'x' \
is captured by an asynchronously-executed block}}
}

void test_block_var_async_no_leak() {
  int x = 123;
  int *p = &x;
  void (^b)(void) = ^void(void) {
    int y = x;
    ++y; 
  };
  dispatch_async(queue, b); // no-warning
}

void test_block_inside_block_async_no_leak() {
  int x = 123;
  int *p = &x;
  void (^inner)(void) = ^void(void) {
    int y = x;
    ++y; 
  };
  void (^outer)(void) = ^void(void) {
    int z = x;
    ++z;
    inner(); 
  };
  dispatch_async(queue, outer); // no-warning
}

dispatch_block_t accept_and_pass_back_block(dispatch_block_t block) {
  block();
  return block; // no-warning
}

void test_passing_continuation_no_leak() {
  int x = 123;
  int *p = &x;
  void (^cont)(void) = ^void(void) {
    *p = 128;
  };
  accept_and_pass_back_block(cont); // no-warning
}

@interface NSObject
@end
@protocol OS_dispatch_semaphore
@end
typedef NSObject<OS_dispatch_semaphore> *dispatch_semaphore_t;
dispatch_semaphore_t dispatch_semaphore_create(long value);
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
long dispatch_semaphore_signal(dispatch_semaphore_t dsema);

void test_no_leaks_on_semaphore_pattern() {
  int x = 0;
  int *p = &x;
  dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
  dispatch_async(queue, ^{
    *p = 1;
    // Some work.
    dispatch_semaphore_signal(semaphore);
  }); // no-warning

  // Do some other work concurrently with the asynchronous work
  // Wait for the asynchronous work to finish
  dispatch_semaphore_wait(semaphore, 1000);
}

void test_dispatch_barrier_sync() {
  int buf[16];
  for (int n = 0; n < 16; ++n) {
    int *ptr = &buf[n];
    // FIXME: Should not warn. The dispatch_barrier_sync() call ensures
    // that the block does not outlive 'buf'.
    dispatch_async(queue, ^{ // expected-warning{{Address of stack memory associated with local variable 'buf' is captured by an asynchronously-executed block}}
      (void)ptr;
    });
  }
  dispatch_barrier_sync(queue, ^{});
}
