1
1
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection \
2
- // RUN: -verify=expected,eagerlyassume %s
2
+ // RUN: -verify=expected,noassumeone, eagerlyassume,combo %s
3
3
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection \
4
4
// RUN: -analyzer-config eagerly-assume=false \
5
+ // RUN: -verify=expected,noassumeone,noeagerlyassume,combo %s
6
+ // RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection \
7
+ // RUN: -analyzer-config assume-at-least-one-iteration=true \
8
+ // RUN: -verify=expected,eagerlyassume,combo %s
9
+ // RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection \
10
+ // RUN: -analyzer-config assume-at-least-one-iteration=true,eagerly-assume=false \
5
11
// RUN: -verify=expected,noeagerlyassume %s
6
12
13
+ // The verify tag "combo" is used for one unique warning which is produced in three
14
+ // of the four RUN combinations.
15
+
7
16
// These tests validate the logic within `ExprEngine::processBranch` which
8
17
// ensures that in loops with opaque conditions we don't assume execution paths
9
18
// if the code does not imply that they are possible.
19
+ // In particular, if two (or more) iterations are already completed in a loop,
20
+ // we don't assume that there can be another iteration. Moreover, if the
21
+ // analyzer option `assume-at-least-one-iteration` is enabled, then we don't
22
+ // assume that a loop can be skipped completely.
10
23
11
24
void clang_analyzer_numTimesReached (void );
12
- void clang_analyzer_warnIfReached (void );
13
25
void clang_analyzer_dump (int );
14
26
15
- void clearCondition (void ) {
16
- // If the analyzer can definitely determine the value of the loop condition,
27
+ void clearTrueCondition (void ) {
28
+ // If the analyzer can definitely determine that the loop condition is true ,
17
29
// then this corrective logic doesn't activate and the engine executes
18
30
// `-analyzer-max-loop` iterations (by default, 4).
19
- for (int i = 0 ; i < 10 ; i ++ )
31
+ int i ;
32
+ for (i = 0 ; i < 10 ; i ++ )
20
33
clang_analyzer_numTimesReached (); // expected-warning {{4}}
21
34
22
- clang_analyzer_warnIfReached (); // unreachable
35
+ clang_analyzer_dump (i ); // Unreachable, no reports.
36
+ }
37
+
38
+ void clearFalseCondition (void ) {
39
+ // If the analyzer can definitely determine that the loop condition is false,
40
+ // then the loop is skipped, even in `assume-at-least-one-iteration` mode.
41
+ int i ;
42
+ for (i = 0 ; i > 10 ; i ++ )
43
+ clang_analyzer_numTimesReached (); // Unreachable, no report.
44
+
45
+ clang_analyzer_dump (i ); // expected-warning {{0}}
23
46
}
24
47
25
48
void opaqueCondition (int arg ) {
@@ -28,10 +51,13 @@ void opaqueCondition(int arg) {
28
51
// that more than two iterations are possible. (It _does_ imply that two
29
52
// iterations may be possible at least in some cases, because otherwise an
30
53
// `if` would've been enough.)
31
- for (int i = 0 ; i < arg ; i ++ )
54
+ // Moreover, if `assume-at-least-one-iteration` is enabled, then assume at
55
+ // least one iteration.
56
+ int i ;
57
+ for (i = 0 ; i < arg ; i ++ )
32
58
clang_analyzer_numTimesReached (); // expected-warning {{2}}
33
59
34
- clang_analyzer_warnIfReached ( ); // expected-warning {{REACHABLE }}
60
+ clang_analyzer_dump ( i ); // noassumeone-warning {{0}} expected-warning {{1}} expected-warning {{2 }}
35
61
}
36
62
37
63
int check (void );
@@ -42,22 +68,26 @@ void opaqueConditionCall(int arg) {
42
68
// insert an assertion to guide the analyzer and rule out more than two
43
69
// iterations (so the analyzer needs to proactively avoid those unjustified
44
70
// branches).
45
- while (check ())
71
+ int i = 0 ; // Helper to distinguish the the branches after the loop.
72
+ while (check ()) {
46
73
clang_analyzer_numTimesReached (); // expected-warning {{2}}
74
+ i ++ ;
75
+ }
47
76
48
- clang_analyzer_warnIfReached ( ); // expected-warning {{REACHABLE }}
77
+ clang_analyzer_dump ( i ); // noassumeone-warning {{0}} expected-warning {{1}} expected-warning {{2 }}
49
78
}
50
79
51
80
void opaqueConditionDoWhile (int arg ) {
52
81
// Same situation as `opaqueCondition()` but with a `do {} while ()` loop.
53
82
// This is tested separately because this loop type is a special case in the
54
83
// iteration count calculation.
84
+ // Obviously, this loop guarantees that at least one iteration will happen.
55
85
int i = 0 ;
56
86
do {
57
87
clang_analyzer_numTimesReached (); // expected-warning {{2}}
58
88
} while (i ++ < arg );
59
89
60
- clang_analyzer_warnIfReached ( ); // expected-warning {{REACHABLE }}
90
+ clang_analyzer_dump ( i ); // expected-warning {{1}} expected-warning {{2 }}
61
91
}
62
92
63
93
void dontRememberOldBifurcation (int arg ) {
@@ -69,7 +99,7 @@ void dontRememberOldBifurcation(int arg) {
69
99
// by default), because the code remembered that there was a bifurcation on
70
100
// the first iteration of the loop and didn't realize that this is obsolete.
71
101
72
- // NOTE: The variable `i` is introduced to ensure that the iterations of the
102
+ // NOTE: The variable `i` is significant to ensure that the iterations of the
73
103
// loop change the state -- otherwise the analyzer stops iterating because it
74
104
// returns to the same `ExplodedNode`.
75
105
int i = 0 ;
@@ -78,21 +108,23 @@ void dontRememberOldBifurcation(int arg) {
78
108
i ++ ;
79
109
}
80
110
81
- clang_analyzer_warnIfReached ( ); // expected -warning {{REACHABLE }}
111
+ clang_analyzer_dump ( i ); // noassumeone -warning {{0 }}
82
112
}
83
113
84
114
void dontAssumeFourthIterartion (int arg ) {
115
+ int i ;
116
+
85
117
if (arg == 2 )
86
118
return ;
87
119
88
120
// In this function the analyzer cannot leave the loop after exactly two
89
121
// iterations (because it knows that `arg != 2` at that point), so it
90
122
// performs a third iteration, but it does not assume that a fourth iteration
91
123
// is also possible.
92
- for (int i = 0 ; i < arg ; i ++ )
124
+ for (i = 0 ; i < arg ; i ++ )
93
125
clang_analyzer_numTimesReached (); // expected-warning {{3}}
94
126
95
- clang_analyzer_warnIfReached ( ); // expected-warning {{REACHABLE }}
127
+ clang_analyzer_dump ( i ); // noassumeone-warning {{0}} expected-warning {{1}} expected-warning {{3 }}
96
128
}
97
129
98
130
#define TRUE 1
@@ -108,42 +140,53 @@ void shortCircuitInLoopCondition(int arg) {
108
140
// false positive on the ffmpeg codebase. Eventually we should properly
109
141
// recognize the full syntactical loop condition expression as "the loop
110
142
// condition", but this will be complicated to implement.
111
- for (int i = 0 ; i < arg && TRUE; i ++ ) {
143
+ int i ;
144
+ for (i = 0 ; i < arg && TRUE; i ++ ) {
112
145
clang_analyzer_numTimesReached (); // expected-warning {{4}}
113
146
}
114
- clang_analyzer_warnIfReached (); // expected-warning {{REACHABLE}}
147
+
148
+ clang_analyzer_dump (i ); // expected-warning {{0}} expected-warning {{1}} expected-warning {{2}} expected-warning {{3}}
115
149
}
116
150
117
151
void shortCircuitInLoopConditionRHS (int arg ) {
118
152
// Unlike `shortCircuitInLoopCondition()`, this case is handled properly
119
153
// because the analyzer thinks that the right hand side of the `&&` is the
120
154
// loop condition.
121
- for (int i = 0 ; TRUE && i < arg ; i ++ ) {
155
+ int i ;
156
+ for (i = 0 ; TRUE && i < arg ; i ++ ) {
122
157
clang_analyzer_numTimesReached (); // expected-warning {{2}}
123
158
}
124
- clang_analyzer_warnIfReached (); // expected-warning {{REACHABLE}}
159
+
160
+ clang_analyzer_dump (i ); // noassumeone-warning {{0}} expected-warning {{1}} expected-warning {{2}}
125
161
}
126
162
127
163
void eagerlyAssumeInSubexpression (int arg ) {
128
164
// The `EagerlyAssume` logic is another complication that can "split the
129
165
// state" within the loop condition, but before the `processBranch()` call
130
- // which is (in theory) responsible for evaluating the loop condition.
131
- // The current implementation partially compensates this by noticing the
166
+ // which would be "naturally" responsible for evaluating the loop condition.
167
+ // The current implementation tries to handle this by noticing the
132
168
// cases where the loop condition is targeted by `EagerlyAssume`, but does
133
169
// not handle the (fortunately rare) case when `EagerlyAssume` hits a
134
170
// sub-expression of the loop condition (as in this contrived test case).
135
- // FIXME: I don't know a real-world example for this inconsistency, but it
136
- // would be good to eliminate it eventually.
137
- for (int i = 0 ; (i >= arg ) - 1 ; i ++ ) {
171
+ // FIXME: It would be good to eventually eliminate this inconsistency, but
172
+ // I don't know a realistic example that could appear in real-world code, so
173
+ // this seems to be a low-priority goal.
174
+ int i ;
175
+ for (i = 0 ; (i >= arg ) - 1 ; i ++ ) {
138
176
clang_analyzer_numTimesReached (); // eagerlyassume-warning {{4}} noeagerlyassume-warning {{2}}
139
177
}
140
- clang_analyzer_warnIfReached (); // expected-warning {{REACHABLE}}
178
+
179
+ // The 'combo' note intentionally appears if `assume-at-least-one-iteration`
180
+ // is disabled, but also appears as a bug when `eagerly-assume` and
181
+ // `assume-at-least-one-iteration` are both enabled.
182
+ clang_analyzer_dump (i ); // combo-warning {{0}} expected-warning {{1}} expected-warning {{2}} eagerlyassume-warning {{3}}
141
183
}
142
184
143
185
void calledTwice (int arg , int isFirstCall ) {
144
186
// This function is called twice (with two different unknown 'arg' values) to
145
187
// check the iteration count handling in this situation.
146
- for (int i = 0 ; i < arg ; i ++ ) {
188
+ int i ;
189
+ for (i = 0 ; i < arg ; i ++ ) {
147
190
if (isFirstCall ) {
148
191
clang_analyzer_numTimesReached (); // expected-warning {{2}}
149
192
} else {
@@ -215,5 +258,5 @@ void onlyLoopConditions(int arg) {
215
258
break ;
216
259
}
217
260
218
- clang_analyzer_warnIfReached ( ); // expected-warning {{REACHABLE }}
261
+ clang_analyzer_dump ( i ); // expected-warning {{1}} expected-warning {{2}} expected-warning {{3}} expected-warning {{4 }}
219
262
}
0 commit comments