llvm/clang/test/Analysis/lambdas.mm

// RUN: %clang_analyze_cc1 -std=c++11 -fblocks -Wno-objc-root-class -analyzer-checker=core,deadcode,debug.ExprInspection -analyzer-config inline-lambdas=true -verify %s

int clang_analyzer_eval(int);

@interface Super
- (void)superMethod;
@end

@interface Sub : Super {
  int _ivar1;
  int _ivar2;
}
@end

@implementation Sub
- (void)callMethodOnSuperInCXXLambda; {
  // Explicit capture.
  [self]() {
    [super superMethod];
  }();

  // Implicit capture.
  [=]() {
    [super superMethod];
  }();
}

// Make sure to properly handle super-calls when a block captures
// a local variable named 'self'.
- (void)callMethodOnSuperInCXXLambdaWithRedefinedSelf; {
  /*__weak*/ Sub *weakSelf = self;
  // Implicit capture. (Sema outlaws explicit capture of a redefined self
  // and a call to super [which uses the original self]).
  [=]() {
    Sub *self = weakSelf;
    [=]() {
      [super superMethod];
    }();
  }();
}

- (void)swapIvars {
  int tmp = _ivar1;
  _ivar1 = _ivar2;
  _ivar2 = tmp;
}

- (void)callMethodOnSelfInCXXLambda; {
  _ivar1 = 7;
  _ivar2 = 8;
  [self]() {
    [self swapIvars];
  }();

  clang_analyzer_eval(_ivar1 == 8); // expected-warning{{TRUE}}
  clang_analyzer_eval(_ivar2 == 7); // expected-warning{{TRUE}}
}

@end

int getValue();
void useValue(int v);

void castToBlockNoDeadStore() {
  int v = getValue(); // no-warning

  (void)(void(^)())[v]() { // This capture should count as a use, so no dead store warning above.
  };
}

void takesBlock(void(^block)());

void passToFunctionTakingBlockNoDeadStore() {
  int v = 7; // no-warning
  int x = 8; // no-warning
  takesBlock([&v, x]() {
    (void)v;
  });
}

void castToBlockAndInline() {
  int result = ((int(^)(int))[](int p) {
    return p;
  })(7);

  clang_analyzer_eval(result == 7); // expected-warning{{TRUE}}
}

void castToBlockWithCaptureAndInline() {
  int y = 7;

  auto lambda = [y]{ return y; };
  int(^block)() = lambda;

  int result = block();
  clang_analyzer_eval(result == 7); // expected-warning{{TRUE}}
}

void castMutableLambdaToBlock() {
  int x = 0;

  auto lambda = [x]() mutable {
    x = x + 1;
    return x;
   };

  // The block should copy the lambda before capturing.
  int(^block)() = lambda;

  int r1 = block();
  clang_analyzer_eval(r1 == 1); // expected-warning{{TRUE}}

  int r2 = block();
  clang_analyzer_eval(r2 == 2); // expected-warning{{TRUE}}

  // Because block copied the lambda, r3 should be 1.
  int r3 = lambda();
  clang_analyzer_eval(r3 == 1); // expected-warning{{TRUE}}

  // Aliasing the block shouldn't copy the lambda.
  int(^blockAlias)() = block;

  int r4 = blockAlias();
  clang_analyzer_eval(r4 == 3); // expected-warning{{TRUE}}

  int r5 = block();
  clang_analyzer_eval(r5 == 4); // expected-warning{{TRUE}}

  // Another copy of lambda
  int(^blockSecondCopy)() = lambda;
  int r6 = blockSecondCopy();
  clang_analyzer_eval(r6 == 2); // expected-warning{{TRUE}}
}

void castLambdaInLocalBlock() {
  // Make sure we don't emit a spurious diagnostic about the address of a block
  // escaping in the implicit conversion operator method for lambda-to-block
  // conversions.
  auto lambda = []{ }; // no-warning

  void(^block)() = lambda;
  (void)block;
}