llvm/clang/test/SemaObjC/warn-called-once.m

// RUN: %clang_cc1 -verify -fsyntax-only -fblocks -fobjc-exceptions -Wcompletion-handler -Wno-pointer-to-int-cast %s

#define NULL (void *)0
#define nil (id)0
#define CALLED_ONCE __attribute__((called_once))
#define NORETURN __attribute__((noreturn))
#define LIKELY(X) __builtin_expect(!!(X), 1)
#define UNLIKELY(X) __builtin_expect(!!(X), 0)
#define LIKELY_WITH_PROBA(X, P) __builtin_expect_with_probability(!!(X), 1, P)
#define UNLIKELY_WITH_PROBA(X, P) __builtin_expect_with_probability(!!(X), 0, P)
#define UNPRED(X) __builtin_unpredictable((long)(X))

@protocol NSObject
@end
@interface NSObject <NSObject>
- (instancetype)init;
- (id)copy;
- (id)class;
- autorelease;
@end

typedef unsigned int NSUInteger;
typedef struct {
} NSFastEnumerationState;

@interface NSArray <__covariant NSFastEnumeration>
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len;
@end
@interface NSMutableArray<ObjectType> : NSArray <ObjectType>
- addObject:anObject;
@end
@class NSString, Protocol;
extern void NSLog(NSString *format, ...);

typedef int group_t;
typedef struct dispatch_queue_s *dispatch_queue_t;
typedef void (^dispatch_block_t)(void);
extern dispatch_queue_t queue;

void dispatch_group_async(dispatch_queue_t queue,
                          group_t group,
                          dispatch_block_t block);
void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

void escape(void (^callback)(void));
void escape_void(void *);
void indirect_call(void (^callback)(void) CALLED_ONCE);
void indirect_conv(void (^completionHandler)(void));
void filler(void);
void exit(int) NORETURN;

void double_call_one_block(void (^callback)(void) CALLED_ONCE) {
  callback(); // expected-note{{previous call is here}}
  callback(); // expected-warning{{'callback' parameter marked 'called_once' is called twice}}
}

void double_call_one_block_parens(void (^callback)(void) CALLED_ONCE) {
  (callback)(); // expected-note{{previous call is here}}
  (callback)(); // expected-warning{{'callback' parameter marked 'called_once' is called twice}}
}

void double_call_one_block_ptr(void (*callback)(void) CALLED_ONCE) {
  callback(); // expected-note{{previous call is here}}
  callback(); // expected-warning{{'callback' parameter marked 'called_once' is called twice}}
}

void double_call_one_block_ptr_deref(void (*callback)(void) CALLED_ONCE) {
  (*callback)(); // expected-note{{previous call is here}}
  (*callback)(); // expected-warning{{'callback' parameter marked 'called_once' is called twice}}
}

void multiple_call_one_block(void (^callback)(void) CALLED_ONCE) {
  // We don't really need to repeat the same warning for the same parameter.
  callback(); // no-warning
  callback(); // no-warning
  callback(); // no-warning
  callback(); // expected-note{{previous call is here}}
  callback(); // expected-warning{{'callback' parameter marked 'called_once' is called twice}}
}

void double_call_branching_1(int cond, void (^callback)(void) CALLED_ONCE) {
  if (cond) {
    callback(); // expected-note{{previous call is here}}
  } else {
    cond += 42;
  }
  callback(); // expected-warning{{'callback' parameter marked 'called_once' is called twice}}
}

void double_call_branching_2(int cond, void (^callback)(void) CALLED_ONCE) {
  callback();
  // expected-note@-1{{previous call is here; set to nil to indicate it cannot be called afterwards}}

  if (cond) {
    callback(); // expected-warning{{'callback' parameter marked 'called_once' is called twice}}
  } else {
    cond += 42;
  }
}

void double_call_branching_3(int cond, void (^callback)(void) CALLED_ONCE) {
  if (cond) {
    callback();
  } else {
    callback();
  }
  // no-warning
}

void double_call_branching_4(int cond1, int cond2, void (^callback)(void) CALLED_ONCE) {
  if (cond1) {
    cond2 = !cond2;
  } else {
    callback();
    // expected-note@-1{{previous call is here; set to nil to indicate it cannot be called afterwards}}
  }

  if (cond2) {
    callback(); // expected-warning{{'callback' parameter marked 'called_once' is called twice}}
  }
}

void double_call_loop(int counter, void (^callback)(void) CALLED_ONCE) {
  while (counter > 0) {
    counter--;
    // Both note and warning are on the same line, which is a common situation
    // in loops.
    callback(); // expected-note{{previous call is here}}
    // expected-warning@-1{{'callback' parameter marked 'called_once' is called twice}}
  }
}

void never_called_trivial(void (^callback)(void) CALLED_ONCE) {
  // expected-warning@-1{{'callback' parameter marked 'called_once' is never called}}
}

int never_called_branching(int x, void (^callback)(void) CALLED_ONCE) {
  // expected-warning@-1{{'callback' parameter marked 'called_once' is never called}}
  x -= 42;

  if (x == 10) {
    return 0;
  }

  return x + 15;
}

void escaped_one_block_1(void (^callback)(void) CALLED_ONCE) {
  escape(callback); // no-warning
}

void escaped_one_block_2(void (^callback)(void) CALLED_ONCE) {
  escape(callback); // no-warning
  callback();
}

void escaped_one_path_1(int cond, void (^callback)(void) CALLED_ONCE) {
  if (cond) {
    escape(callback); // no-warning
  } else {
    callback();
  }
}

void escaped_one_path_2(int cond, void (^callback)(void) CALLED_ONCE) {
  if (cond) {
    escape(callback); // no-warning
  }

  callback();
}

void escaped_one_path_3(int cond, void (^callback)(void) CALLED_ONCE) {
  if (cond) {
    // expected-warning@-1{{'callback' parameter marked 'called_once' is never used when taking false branch}}
    escape(callback);
  }
}

void escape_in_between_1(void (^callback)(void) CALLED_ONCE) {
  callback(); // expected-note{{previous call is here}}
  escape(callback);
  callback(); // expected-warning{{'callback' parameter marked 'called_once' is called twice}}
}

void escape_in_between_2(int cond, void (^callback)(void) CALLED_ONCE) {
  callback(); // expected-note{{previous call is here}}
  if (cond) {
    escape(callback);
  }
  callback(); // expected-warning{{'callback' parameter marked 'called_once' is called twice}}
}

void escape_in_between_3(int cond, void (^callback)(void) CALLED_ONCE) {
  callback(); // expected-note{{previous call is here}}

  if (cond) {
    escape(callback);
  } else {
    escape_void((__bridge void *)callback);
  }

  callback(); // expected-warning{{'callback' parameter marked 'called_once' is called twice}}
}

void escaped_as_void_ptr(void (^callback)(void) CALLED_ONCE) {
  escape_void((__bridge void *)callback); // no-warning
}

void indirect_call_no_warning_1(void (^callback)(void) CALLED_ONCE) {
  indirect_call(callback); // no-warning
}

void indirect_call_no_warning_2(int cond, void (^callback)(void) CALLED_ONCE) {
  if (cond) {
    indirect_call(callback);
  } else {
    callback();
  }
  // no-warning
}

void indirect_call_double_call(void (^callback)(void) CALLED_ONCE) {
  indirect_call(callback); // expected-note{{previous call is here}}
  callback();              // expected-warning{{'callback' parameter marked 'called_once' is called twice}}
}

void indirect_call_within_direct_call(void (^callback)(void) CALLED_ONCE,
                                      void (^meta)(void (^param)(void) CALLED_ONCE) CALLED_ONCE) {
  // TODO: Report warning for 'callback'.
  //       At the moment, it is not possible to access 'called_once' attribute from the type
  //       alone when there is no actual declaration of the marked parameter.
  meta(callback);
  callback();
  // no-warning
}

void block_call_1(void (^callback)(void) CALLED_ONCE) {
  indirect_call( // expected-note{{previous call is here}}
      ^{
        callback();
      });
  callback(); // expected-warning{{'callback' parameter marked 'called_once' is called twice}}
}

void block_call_2(void (^callback)(void) CALLED_ONCE) {
  escape(^{
    callback();
  });
  callback();
  // no-warning
}

void block_call_3(int cond, void (^callback)(void) CALLED_ONCE) {
  ^{
    if (cond) {
      callback(); // expected-note{{previous call is here}}
    }
    callback(); // expected-warning{{'callback' parameter marked 'called_once' is called twice}}
  }();          // no-warning
}

void block_call_4(int cond, void (^callback)(void) CALLED_ONCE) {
  ^{
    if (cond) {
      // expected-warning@-1{{'callback' parameter marked 'called_once' is never used when taking false branch}}
      escape(callback);
    }
  }(); // no-warning
}

void block_call_5(void (^outer)(void) CALLED_ONCE) {
  ^(void (^inner)(void) CALLED_ONCE) {
    // expected-warning@-1{{'inner' parameter marked 'called_once' is never called}}
  }(outer);
}

void block_with_called_once(void (^outer)(void) CALLED_ONCE) {
  escape_void((__bridge void *)^(void (^inner)(void) CALLED_ONCE) {
    inner(); // expected-note{{previous call is here}}
    inner(); // expected-warning{{'inner' parameter marked 'called_once' is called twice}}
  });
  outer(); // expected-note{{previous call is here}}
  outer(); // expected-warning{{'outer' parameter marked 'called_once' is called twice}}
}

void block_dispatch_call(int cond, void (^callback)(void) CALLED_ONCE) {
  dispatch_async(queue, ^{
    if (cond) // expected-warning{{'callback' parameter marked 'called_once' is never called when taking false branch}}
      callback();
  });
}

void block_escape_call_1(int cond, void (^callback)(void) CALLED_ONCE) {
  escape_void((__bridge void *)^{
    if (cond) {
      // no-warning
      callback();
    }
  });
}

void block_escape_call_2(int cond, void (^callback)(void) CALLED_ONCE) {
  escape_void((__bridge void *)^{
    if (cond) {
      callback(); // expected-note{{previous call is here}}
    }
    // Double call can still be reported.
    callback(); // expected-warning{{'callback' parameter marked 'called_once' is called twice}}
  });
}

void never_called_one_exit(int cond, void (^callback)(void) CALLED_ONCE) {
  if (!cond) // expected-warning{{'callback' parameter marked 'called_once' is never called when taking true branch}}
    return;

  callback();
}

void never_called_if_then_1(int cond, void (^callback)(void) CALLED_ONCE) {
  if (cond) { // expected-warning{{'callback' parameter marked 'called_once' is never called when taking true branch}}
  } else {
    callback();
  }
}

void never_called_if_then_2(int cond, void (^callback)(void) CALLED_ONCE) {
  if (cond) { // expected-warning{{'callback' parameter marked 'called_once' is never called when taking true branch}}
    // This way the first statement in the basic block is different from
    // the first statement in the compound statement
    (void)cond;
  } else {
    callback();
  }
}

void never_called_if_else_1(int cond, void (^callback)(void) CALLED_ONCE) {
  if (cond) { // expected-warning{{'callback' parameter marked 'called_once' is never called when taking false branch}}
    callback();
  } else {
  }
}

void never_called_if_else_2(int cond, void (^callback)(void) CALLED_ONCE) {
  if (cond) { // expected-warning{{'callback' parameter marked 'called_once' is never called when taking false branch}}
    callback();
  }
}

void never_called_two_ifs(int cond1, int cond2, void (^callback)(void) CALLED_ONCE) {
  if (cond1) {   // expected-warning{{'callback' parameter marked 'called_once' is never called when taking false branch}}
    if (cond2) { // expected-warning{{'callback' parameter marked 'called_once' is never called when taking true branch}}
      return;
    }
    callback();
  }
}

void never_called_ternary_then(int cond, void (^other)(void), void (^callback)(void) CALLED_ONCE) {
  return cond ? // expected-warning{{'callback' parameter marked 'called_once' is never called when taking true branch}}
             other()
              : callback();
}

void never_called_for_false(int size, void (^callback)(void) CALLED_ONCE) {
  for (int i = 0; i < size; ++i) {
    // expected-warning@-1{{'callback' parameter marked 'called_once' is never called when skipping the loop}}
    callback();
    break;
  }
}

void never_called_for_true(int size, void (^callback)(void) CALLED_ONCE) {
  for (int i = 0; i < size; ++i) {
    // expected-warning@-1{{'callback' parameter marked 'called_once' is never called when entering the loop}}
    return;
  }
  callback();
}

void never_called_while_false(int cond, void (^callback)(void) CALLED_ONCE) {
  while (cond) { // expected-warning{{'callback' parameter marked 'called_once' is never called when skipping the loop}}
    callback();
    break;
  }
}

void never_called_while_true(int cond, void (^callback)(void) CALLED_ONCE) {
  while (cond) { // expected-warning{{'callback' parameter marked 'called_once' is never called when entering the loop}}
    return;
  }
  callback();
}

void never_called_switch_case(int cond, void (^callback)(void) CALLED_ONCE) {
  switch (cond) {
  case 1:
    callback();
    break;
  case 2:
    callback();
    break;
  case 3: // expected-warning{{'callback' parameter marked 'called_once' is never called when handling this case}}
    break;
  default:
    callback();
    break;
  }
}

void never_called_switch_default(int cond, void (^callback)(void) CALLED_ONCE) {
  switch (cond) {
  case 1:
    callback();
    break;
  case 2:
    callback();
    break;
  default: // expected-warning{{'callback' parameter marked 'called_once' is never called when handling this case}}
    break;
  }
}

void never_called_switch_two_cases(int cond, void (^callback)(void) CALLED_ONCE) {
  switch (cond) {
  case 1: // expected-warning{{'callback' parameter marked 'called_once' is never called when handling this case}}
    break;
  case 2: // expected-warning{{'callback' parameter marked 'called_once' is never called when handling this case}}
    break;
  default:
    callback();
    break;
  }
}

void never_called_switch_none(int cond, void (^callback)(void) CALLED_ONCE) {
  switch (cond) { // expected-warning{{'callback' parameter marked 'called_once' is never called when none of the cases applies}}
  case 1:
    callback();
    break;
  case 2:
    callback();
    break;
  }
}

enum YesNoOrMaybe {
  YES,
  NO,
  MAYBE
};

void exhaustive_switch(enum YesNoOrMaybe cond, void (^callback)(void) CALLED_ONCE) {
  switch (cond) {
  case YES:
    callback();
    break;
  case NO:
    callback();
    break;
  case MAYBE:
    callback();
    break;
  }
  // no-warning
}

void called_twice_exceptions(void (^callback)(void) CALLED_ONCE) {
  // TODO: Obj-C exceptions are not supported in CFG,
  //       we should report warnings in these as well.
  @try {
    callback();
    callback();
  }
  @finally {
    callback();
  }
}

void noreturn_1(int cond, void (^callback)(void) CALLED_ONCE) {
  if (cond) {
    exit(1);
  } else {
    callback();
  }
  // no-warning
}

void noreturn_2(int cond, void (^callback)(void) CALLED_ONCE) {
  if (cond) {
    callback();
    exit(1);
  } else {
    callback();
  }
  // no-warning
}

void noreturn_3(int cond, void (^callback)(void) CALLED_ONCE) {
  if (cond) {
    exit(1);
  }

  callback();
  // no-warning
}

void noreturn_4(void (^callback)(void) CALLED_ONCE) {
  exit(1);
  // no-warning
}

void noreturn_5(int cond, void (^callback)(void) CALLED_ONCE) {
  if (cond) {
    // NOTE: This is an ambiguous case caused by the fact that we do a backward
    //       analysis.  We can probably report it here, but for the sake of
    //       the simplicity of our analysis, we don't.
    if (cond == 42) {
      callback();
    }
    exit(1);
  }
  callback();
  // no-warning
}

void never_called_noreturn_1(int cond, void (^callback)(void) CALLED_ONCE) {
  // expected-warning@-1{{'callback' parameter marked 'called_once' is never called}}
  if (cond) {
    exit(1);
  }
}

void double_call_noreturn(int cond, void (^callback)(void) CALLED_ONCE) {
  callback(); // expected-note{{previous call is here}}

  if (cond) {
    if (cond == 42) {
      callback(); // expected-warning{{'callback' parameter marked 'called_once' is called twice}}
    }
    exit(1);
  }
}

void call_with_check_1(void (^callback)(void) CALLED_ONCE) {
  if (callback)
    callback();
  // no-warning
}

void call_with_check_2(void (^callback)(void) CALLED_ONCE) {
  if (!callback) {
  } else {
    callback();
  }
  // no-warning
}

void call_with_check_3(void (^callback)(void) CALLED_ONCE) {
  if (callback != NULL)
    callback();
  // no-warning
}

void call_with_check_4(void (^callback)(void) CALLED_ONCE) {
  if (NULL != callback)
    callback();
  // no-warning
}

void call_with_check_5(void (^callback)(void) CALLED_ONCE) {
  if (callback == NULL) {
  } else {
    callback();
  }
  // no-warning
}

void call_with_check_6(void (^callback)(void) CALLED_ONCE) {
  if (NULL == callback) {
  } else {
    callback();
  }
  // no-warning
}

int call_with_check_7(int (^callback)(void) CALLED_ONCE) {
  return callback ? callback() : 0;
  // no-warning
}

void call_with_builtin_check_1(int (^callback)(void) CALLED_ONCE) {
  if (LIKELY(callback))
    callback();
  // no-warning
}

void call_with_builtin_check_2(int (^callback)(void) CALLED_ONCE) {
  if (!UNLIKELY(callback)) {
  } else {
    callback();
  }
  // no-warning
}

void call_with_builtin_check_3(int (^callback)(void) CALLED_ONCE) {
  if (__builtin_expect((long)callback, 0L)) {
  } else {
    callback();
  }
  // no-warning
}

void call_with_builtin_check_4(int (^callback)(void) CALLED_ONCE) {
  if (__builtin_expect(0L, (long)callback)) {
  } else {
    callback();
  }
  // no-warning
}

void call_with_builtin_check_5(int (^callback)(void) CALLED_ONCE) {
  if (LIKELY_WITH_PROBA(callback, 0.9))
    callback();
  // no-warning
}

void call_with_builtin_check_6(int (^callback)(void) CALLED_ONCE) {
  if (!UNLIKELY_WITH_PROBA(callback, 0.9)) {
  } else {
    callback();
  }
  // no-warning
}

void call_with_builtin_check_7(int (^callback)(void) CALLED_ONCE) {
  if (UNPRED(callback)) {
  } else {
    callback();
  }
  // no-warning
}

void call_with_builtin_check_8(int (^callback)(void) CALLED_ONCE) {
  if (LIKELY(callback != nil))
    callback();
  // no-warning
}

void call_with_builtin_check_9(int (^callback)(void) CALLED_ONCE) {
  if (!UNLIKELY(callback == NULL))
    callback();
  // no-warning
}

void unreachable_true_branch(void (^callback)(void) CALLED_ONCE) {
  if (0) {

  } else {
    callback();
  }
  // no-warning
}

void unreachable_false_branch(void (^callback)(void) CALLED_ONCE) {
  if (1) {
    callback();
  }
  // no-warning
}

void never_called_conv_1(void (^completionHandler)(void)) {
  // expected-warning@-1{{completion handler is never called}}
}

void never_called_conv_2(void (^completion)(void)) {
  // expected-warning@-1{{completion handler is never called}}
}

void never_called_conv_WithCompletion(void (^callback)(void)) {
  // expected-warning@-1{{completion handler is never called}}
}

void indirectly_called_conv(void (^completionHandler)(void)) {
  indirect_conv(completionHandler);
  // no-warning
}

void escape_through_assignment_1(void (^callback)(void) CALLED_ONCE) {
  id escapee;
  escapee = callback;
  escape(escapee);
  // no-warning
}

void escape_through_assignment_2(void (^callback)(void) CALLED_ONCE) {
  id escapee = callback;
  escape(escapee);
  // no-warning
}

void escape_through_assignment_3(void (^callback1)(void) CALLED_ONCE,
                                 void (^callback2)(void) CALLED_ONCE) {
  id escapee1 = callback1, escapee2 = callback2;
  escape(escapee1);
  escape(escapee2);
  // no-warning
}

void not_called_in_throw_branch_1(id exception, void (^callback)(void) CALLED_ONCE) {
  if (exception) {
    @throw exception;
  }

  callback();
}

void not_called_in_throw_branch_2(id exception, void (^callback)(void) CALLED_ONCE) {
  // expected-warning@-1{{'callback' parameter marked 'called_once' is never called}}
  if (exception) {
    @throw exception;
  }
}

void conventional_error_path_1(int error, void (^completionHandler)(void)) {
  if (error) {
    // expected-warning@-1{{completion handler is never called when taking true branch}}
    // This behavior might be tweaked in the future
    return;
  }

  completionHandler();
}

void conventional_error_path_2(int error, void (^callback)(void) CALLED_ONCE) {
  // Conventions do not apply to explicitly marked parameters.
  if (error) {
    // expected-warning@-1{{'callback' parameter marked 'called_once' is never called when taking true branch}}
    return;
  }

  callback();
}

void suppression_1(void (^callback)(void) CALLED_ONCE) {
  // This is a way to tell the analysis that we know about this path,
  // and we do not want to call the callback here.
  (void)callback; // no-warning
}

void suppression_2(int cond, void (^callback)(void) CALLED_ONCE) {
  if (cond) {
    (void)callback; // no-warning
  } else {
    callback();
  }
}

void suppression_3(int cond, void (^callback)(void) CALLED_ONCE) {
  // Even if we do this on one of the paths, it doesn't mean we should
  // forget about other paths.
  if (cond) {
    // expected-warning@-1{{'callback' parameter marked 'called_once' is never used when taking false branch}}
    (void)callback;
  }
}

@interface TestBase : NSObject
- (void)escape:(void (^)(void))callback;
- (void)indirect_call:(void (^)(void))CALLED_ONCE callback;
- (void)indirect_call_conv_1:(int)cond
           completionHandler:(void (^)(void))completionHandler;
- (void)indirect_call_conv_2:(int)cond
           completionHandler:(void (^)(void))handler;
- (void)indirect_call_conv_3WithCompletion:(void (^)(void))handler;
- (void)indirect_call_conv_4:(void (^)(void))handler
    __attribute__((swift_async(swift_private, 1)));
- (void)exit:(int)code NORETURN;
- (int)condition;
@end

@interface TestClass : TestBase
@property(strong) NSMutableArray *handlers;
@property(strong) id storedHandler;
@property int wasCanceled;
@property(getter=hasErrors) int error;
@end

@implementation TestClass

- (void)double_indirect_call_1:(void (^)(void))CALLED_ONCE callback {
  [self indirect_call:callback]; // expected-note{{previous call is here}}
  [self indirect_call:callback]; // expected-warning{{'callback' parameter marked 'called_once' is called twice}}
}

- (void)double_indirect_call_2:(void (^)(void))CALLED_ONCE callback {
  [self indirect_call_conv_1:0 // expected-note{{previous call is here}}
           completionHandler:callback];
  [self indirect_call_conv_1:1 // expected-warning{{'callback' parameter marked 'called_once' is called twice}}
           completionHandler:callback];
}

- (void)double_indirect_call_3:(void (^)(void))completionHandler {
  [self indirect_call_conv_2:0 // expected-note{{previous call is here}}
           completionHandler:completionHandler];
  [self indirect_call_conv_2:1 // expected-warning{{completion handler is called twice}}
           completionHandler:completionHandler];
}

- (void)double_indirect_call_4:(void (^)(void))completion {
  [self indirect_call_conv_2:0 // expected-note{{previous call is here}}
           completionHandler:completion];
  [self indirect_call_conv_2:1 // expected-warning{{completion handler is called twice}}
           completionHandler:completion];
}

- (void)double_indirect_call_5:(void (^)(void))withCompletionHandler {
  [self indirect_call_conv_2:0 // expected-note{{previous call is here}}
           completionHandler:withCompletionHandler];
  [self indirect_call_conv_2:1 // expected-warning{{completion handler is called twice}}
           completionHandler:withCompletionHandler];
}

- (void)double_indirect_call_6:(void (^)(void))completionHandler {
  [self indirect_call_conv_3WithCompletion: // expected-note{{previous call is here}}
            completionHandler];
  [self indirect_call_conv_3WithCompletion: // expected-warning{{completion handler is called twice}}
            completionHandler];
}

- (void)double_indirect_call_7:(void (^)(void))completionHandler {
  [self indirect_call_conv_4: // expected-note{{previous call is here}}
            completionHandler];
  [self indirect_call_conv_4: // expected-warning{{completion handler is called twice}}
            completionHandler];
}

- (void)never_called_trivial:(void (^)(void))CALLED_ONCE callback {
  // expected-warning@-1{{'callback' parameter marked 'called_once' is never called}}
  filler();
}

- (void)noreturn:(int)cond callback:(void (^)(void))CALLED_ONCE callback {
  if (cond) {
    [self exit:1];
  }

  callback();
  // no-warning
}

- (void)escaped_one_path:(int)cond callback:(void (^)(void))CALLED_ONCE callback {
  if (cond) {
    [self escape:callback]; // no-warning
  } else {
    callback();
  }
}

- (void)block_call_1:(void (^)(void))CALLED_ONCE callback {
  // We consider captures by blocks as escapes
  [self indirect_call:(^{ // expected-note{{previous call is here}}
          callback();
        })];
  callback(); // expected-warning{{'callback' parameter marked 'called_once' is called twice}}
}

- (void)block_call_2:(int)cond callback:(void (^)(void))CALLED_ONCE callback {
  [self indirect_call:
            ^{
              if (cond) {
                // expected-warning@-1{{'callback' parameter marked 'called_once' is never used when taking false branch}}
                [self escape:callback];
              }
            }];
}

- (void)block_call_3:(int)cond
    completionHandler:(void (^)(void))callback {
  [self indirect_call:
            ^{
              if (cond) {
                // expected-warning@-1{{completion handler is never used when taking false branch}}
                [self escape:callback];
              }
            }];
}

- (void)block_call_4WithCompletion:(void (^)(void))callback {
  [self indirect_call:
            ^{
              if ([self condition]) {
                // expected-warning@-1{{completion handler is never used when taking false branch}}
                [self escape:callback];
              }
            }];
}

- (void)never_called_conv:(void (^)(void))completionHandler {
  // expected-warning@-1{{completion handler is never called}}
  filler();
}

- (void)indirectly_called_conv:(void (^)(void))completionHandler {
  indirect_conv(completionHandler);
  // no-warning
}

- (void)never_called_one_exit_conv:(int)cond completionHandler:(void (^)(void))handler {
  if (!cond) // expected-warning{{completion handler is never called when taking true branch}}
    return;

  handler();
}

- (void)escape_through_assignment:(void (^)(void))completionHandler {
  _storedHandler = completionHandler;
  // no-warning
}

- (void)escape_through_copy:(void (^)(void))completionHandler {
  _storedHandler = [completionHandler copy];
  // no-warning
}

- (void)escape_through_copy_and_autorelease:(void (^)(void))completionHandler {
  _storedHandler = [[completionHandler copy] autorelease];
  // no-warning
}

- (void)complex_escape:(void (^)(void))completionHandler {
  if (completionHandler) {
    [_handlers addObject:[[completionHandler copy] autorelease]];
  }
  // no-warning
}

- (void)test_crash:(void (^)(void))completionHandler cond:(int)cond {
  if (cond) {
    // expected-warning@-1{{completion handler is never used when taking false branch}}
    for (id _ in _handlers) {
    }

    [_handlers addObject:completionHandler];
  }
}

- (void)conventional_error_path_1:(void (^)(void))completionHandler {
  if (self.wasCanceled)
    // expected-warning@-1{{completion handler is never called when taking true branch}}
    // This behavior might be tweaked in the future
    return;

  completionHandler();
}

- (void)conventional_error_path_2:(void (^)(void))completionHandler {
  if (self.wasCanceled)
    // expected-warning@-1{{completion handler is never used when taking true branch}}
    // This behavior might be tweaked in the future
    return;

  [_handlers addObject:completionHandler];
}

- (void)conventional_error_path_3:(void (^)(void))completionHandler {
  if (self.hasErrors)
    // expected-warning@-1{{completion handler is never called when taking true branch}}
    // This behavior might be tweaked in the future
    return;

  completionHandler();
}

- (void)conventional_error_path_3:(int)cond completionHandler:(void (^)(void))handler {
  if (self.wasCanceled)
    // expected-warning@-1{{completion handler is never called when taking true branch}}
    // TODO: When we have an error on some other path, in order not to prevent it from
    //       being reported, we report this one as well.
    //       Probably, we should address this at some point.
    return;

  if (cond) {
    // expected-warning@-1{{completion handler is never called when taking false branch}}
    handler();
  }
}

#define NSAssert(condition, desc, ...) NSLog(desc, ##__VA_ARGS__);

- (void)empty_base_1:(void (^)(void))completionHandler {
  NSAssert(0, @"Subclass must implement");
  // no-warning
}

- (void)empty_base_2:(void (^)(void))completionHandler {
  // no-warning
}

- (int)empty_base_3:(void (^)(void))completionHandler {
  return 1;
  // no-warning
}

- (int)empty_base_4:(void (^)(void))completionHandler {
  NSAssert(0, @"Subclass must implement");
  return 1;
  // no-warning
}

- (int)empty_base_5:(void (^)(void))completionHandler {
  NSAssert(0, @"%@ doesn't support", [self class]);
  return 1;
  // no-warning
}

#undef NSAssert
#define NSAssert(condition, desc, ...) \
  if (!(condition)) {                  \
    NSLog(desc, ##__VA_ARGS__);        \
  }

- (int)empty_base_6:(void (^)(void))completionHandler {
  NSAssert(0, @"%@ doesn't support", [self class]);
  return 1;
  // no-warning
}

#undef NSAssert
#define NSAssert(condition, desc, ...) \
  do {                                 \
    NSLog(desc, ##__VA_ARGS__);        \
  } while (0)

- (int)empty_base_7:(void (^)(void))completionHandler {
  NSAssert(0, @"%@ doesn't support", [self class]);
  return 1;
  // no-warning
}

- (void)two_conditions_1:(int)first
                  second:(int)second
       completionHandler:(void (^)(void))completionHandler {
  if (first && second) {
    // expected-warning@-1{{completion handler is never called when taking false branch}}
    completionHandler();
  }
}

- (void)two_conditions_2:(int)first
                  second:(int)second
       completionHandler:(void (^)(void))completionHandler {
  if (first || second) {
    // expected-warning@-1{{completion handler is never called when taking true branch}}
    return;
  }

  completionHandler();
}

- (void)testWithCompletionHandler:(void (^)(void))callback {
  if ([self condition]) {
    // expected-warning@-1{{completion handler is never called when taking false branch}}
    callback();
  }
}

- (void)testWithCompletion:(void (^)(void))callback {
  if ([self condition]) {
    // expected-warning@-1{{completion handler is never called when taking false branch}}
    callback();
  }
}

- (void)test:(int)cond fooWithReplyTo:(void (^)(void))handler {
  if (cond) {
    // expected-warning@-1{{completion handler is never called when taking false branch}}
    handler();
  }
}

- (void)test:(int)cond with:(void (^)(void))fooWithCompletionBlock {
  if (cond) {
    // expected-warning@-1{{completion handler is never called when taking false branch}}
    fooWithCompletionBlock();
  }
}

- (void)completion_handler_wrong_type:(int (^)(void))completionHandler {
  // We don't want to consider completion handlers with non-void return types.
  if ([self condition]) {
    // no-warning
    completionHandler();
  }
}

- (void)test_swift_async_none:(int)cond
            completionHandler:(void (^)(void))handler __attribute__((swift_async(none))) {
  if (cond) {
    // no-warning
    handler();
  }
}

- (void)test_swift_async_param:(int)cond
                      callback:(void (^)(void))callback
    __attribute__((swift_async(swift_private, 2))) {
  if (cond) {
    // expected-warning@-1{{completion handler is never called when taking false branch}}
    callback();
  }
}

- (void)test_nil_suggestion:(int)cond1
                     second:(int)cond2
                 completion:(void (^)(void))handler {
  if (cond1) {
    handler();
    // expected-note@-1{{previous call is here; set to nil to indicate it cannot be called afterwards}}
  }

  if (cond2) {
    handler(); // expected-warning{{completion handler is called twice}}
  }
}

- (void)test_nil_suppression_1:(int)cond1
                        second:(int)cond2
                    completion:(void (^)(void))handler {
  if (cond1) {
    handler();
    handler = nil;
    // no-warning
  }

  if (cond2) {
    handler();
  }
}

- (void)test_nil_suppression_2:(int)cond1
                        second:(int)cond2
                    completion:(void (^)(void))handler {
  if (cond1) {
    handler();
    handler = NULL;
    // no-warning
  }

  if (cond2) {
    handler();
  }
}

- (void)test_nil_suppression_3:(int)cond1
                        second:(int)cond2
                    completion:(void (^)(void))handler {
  if (cond1) {
    handler();
    handler = 0;
    // no-warning
  }

  if (cond2) {
    handler();
  }
}

- (void)test_escape_before_branch:(int)cond
                   withCompletion:(void (^)(void))handler {
  if (cond) {
    filler();
  }

  void (^copiedHandler)(void) = ^{
    handler();
  };

  if (cond) {
    // no-warning
    handler();
  } else {
    copiedHandler();
  }
}

- (void)test_escape_after_branch:(int)cond
                  withCompletion:(void (^)(void))handler {
  if (cond) {
    // no-warning
    handler();
  }

  escape(handler);
}

- (void)test_termination:(int)cond
                  withCompletion:(void (^)(void))handler {
  // The code below was able to cause non-termination but should be
  // fixed now:
  do {
    escape(handler);    
    handler();    // expected-warning{{completion handler is called twice}} expected-note{{previous call is here; set to nil to indicate it cannot be called afterwards}}
  } while (cond);
}

typedef void (^DeferredBlock)(void);
static inline void DefferedCallback(DeferredBlock *inBlock) { (*inBlock)(); }
#define _DEFERCONCAT(a, b) a##b
#define _DEFERNAME(a) _DEFERCONCAT(__DeferredVar_, a)
#define DEFER __extension__ __attribute__((cleanup(DefferedCallback), unused)) \
                  DeferredBlock _DEFERNAME(__COUNTER__) = ^

- (void)test_cleanup_1:(int)cond
        withCompletion:(void (^)(void))handler {
  int error = 0;
  DEFER {
    if (error)
      handler();
  };

  if (cond) {
    error = 1;
  } else {
    // no-warning
    handler();
  }
}

- (void)test_cleanup_2:(int)cond
        withCompletion:(void (^)(void))handler {
  int error = 0;
  DEFER {
    if (error)
      handler();
  };

  if (cond) {
    error = 1;
  } else {
    handler(); // expected-note{{previous call is here}}
  }

  // We still can warn about double call even in this case.
  handler(); // expected-warning{{completion handler is called twice}}
}

- (void)initWithAdditions:(int)cond
           withCompletion:(void (^)(void))handler {
  self = [self init];
  if (self) {
    escape(handler);
  }
  // no-warning
}

@end