llvm-project/clang/test/Analysis/lambdas.mm

144 lines
3.1 KiB
Plaintext

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