1 | // 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 |
2 | |
3 | int clang_analyzer_eval(int); |
4 | |
5 | @interface Super |
6 | - (void)superMethod; |
7 | @end |
8 | |
9 | @interface Sub : Super { |
10 | int _ivar1; |
11 | int _ivar2; |
12 | } |
13 | @end |
14 | |
15 | @implementation Sub |
16 | - (void)callMethodOnSuperInCXXLambda; { |
17 | // Explicit capture. |
18 | [self]() { |
19 | [super superMethod]; |
20 | }(); |
21 | |
22 | // Implicit capture. |
23 | [=]() { |
24 | [super superMethod]; |
25 | }(); |
26 | } |
27 | |
28 | // Make sure to properly handle super-calls when a block captures |
29 | // a local variable named 'self'. |
30 | - (void)callMethodOnSuperInCXXLambdaWithRedefinedSelf; { |
31 | /*__weak*/ Sub *weakSelf = self; |
32 | // Implicit capture. (Sema outlaws explicit capture of a redefined self |
33 | // and a call to super [which uses the original self]). |
34 | [=]() { |
35 | Sub *self = weakSelf; |
36 | [=]() { |
37 | [super superMethod]; |
38 | }(); |
39 | }(); |
40 | } |
41 | |
42 | - (void)swapIvars { |
43 | int tmp = _ivar1; |
44 | _ivar1 = _ivar2; |
45 | _ivar2 = tmp; |
46 | } |
47 | |
48 | - (void)callMethodOnSelfInCXXLambda; { |
49 | _ivar1 = 7; |
50 | _ivar2 = 8; |
51 | [self]() { |
52 | [self swapIvars]; |
53 | }(); |
54 | |
55 | clang_analyzer_eval(_ivar1 == 8); // expected-warning{{TRUE}} |
56 | clang_analyzer_eval(_ivar2 == 7); // expected-warning{{TRUE}} |
57 | } |
58 | |
59 | @end |
60 | |
61 | int getValue(); |
62 | void useValue(int v); |
63 | |
64 | void castToBlockNoDeadStore() { |
65 | int v = getValue(); // no-warning |
66 | |
67 | (void)(void(^)())[v]() { // This capture should count as a use, so no dead store warning above. |
68 | }; |
69 | } |
70 | |
71 | void takesBlock(void(^block)()); |
72 | |
73 | void passToFunctionTakingBlockNoDeadStore() { |
74 | int v = 7; // no-warning |
75 | int x = 8; // no-warning |
76 | takesBlock([&v, x]() { |
77 | (void)v; |
78 | }); |
79 | } |
80 | |
81 | void castToBlockAndInline() { |
82 | int result = ((int(^)(int))[](int p) { |
83 | return p; |
84 | })(7); |
85 | |
86 | clang_analyzer_eval(result == 7); // expected-warning{{TRUE}} |
87 | } |
88 | |
89 | void castToBlockWithCaptureAndInline() { |
90 | int y = 7; |
91 | |
92 | auto lambda = [y]{ return y; }; |
93 | int(^block)() = lambda; |
94 | |
95 | int result = block(); |
96 | clang_analyzer_eval(result == 7); // expected-warning{{TRUE}} |
97 | } |
98 | |
99 | void castMutableLambdaToBlock() { |
100 | int x = 0; |
101 | |
102 | auto lambda = [x]() mutable { |
103 | x = x + 1; |
104 | return x; |
105 | }; |
106 | |
107 | // The block should copy the lambda before capturing. |
108 | int(^block)() = lambda; |
109 | |
110 | int r1 = block(); |
111 | clang_analyzer_eval(r1 == 1); // expected-warning{{TRUE}} |
112 | |
113 | int r2 = block(); |
114 | clang_analyzer_eval(r2 == 2); // expected-warning{{TRUE}} |
115 | |
116 | // Because block copied the lambda, r3 should be 1. |
117 | int r3 = lambda(); |
118 | clang_analyzer_eval(r3 == 1); // expected-warning{{TRUE}} |
119 | |
120 | // Aliasing the block shouldn't copy the lambda. |
121 | int(^blockAlias)() = block; |
122 | |
123 | int r4 = blockAlias(); |
124 | clang_analyzer_eval(r4 == 3); // expected-warning{{TRUE}} |
125 | |
126 | int r5 = block(); |
127 | clang_analyzer_eval(r5 == 4); // expected-warning{{TRUE}} |
128 | |
129 | // Another copy of lambda |
130 | int(^blockSecondCopy)() = lambda; |
131 | int r6 = blockSecondCopy(); |
132 | clang_analyzer_eval(r6 == 2); // expected-warning{{TRUE}} |
133 | } |
134 | |
135 | void castLambdaInLocalBlock() { |
136 | // Make sure we don't emit a spurious diagnostic about the address of a block |
137 | // escaping in the implicit conversion operator method for lambda-to-block |
138 | // conversions. |
139 | auto lambda = []{ }; // no-warning |
140 | |
141 | void(^block)() = lambda; |
142 | (void)block; |
143 | } |
144 | |