| // 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; |
| } |