1 // RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection \
2 // RUN: -verify=expected,eagerlyassume %s
3 // RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection \
4 // RUN: -analyzer-config eagerly-assume=false \
5 // RUN: -verify=expected,noeagerlyassume %s
7 // These tests validate the logic within `ExprEngine::processBranch` which
8 // ensures that in loops with opaque conditions we don't assume execution paths
9 // if the code does not imply that they are possible.
11 void clang_analyzer_numTimesReached(void);
12 void clang_analyzer_warnIfReached(void);
13 void clang_analyzer_dump(int);
15 void clearCondition(void) {
16 // If the analyzer can definitely determine the value of the loop condition,
17 // then this corrective logic doesn't activate and the engine executes
18 // `-analyzer-max-loop` iterations (by default, 4).
19 for (int i
= 0; i
< 10; i
++)
20 clang_analyzer_numTimesReached(); // expected-warning {{4}}
22 clang_analyzer_warnIfReached(); // unreachable
25 void opaqueCondition(int arg
) {
26 // If the loop condition is opaque, don't assume more than two iterations,
27 // because the presence of a loop does not imply that the programmer thought
28 // that more than two iterations are possible. (It _does_ imply that two
29 // iterations may be possible at least in some cases, because otherwise an
30 // `if` would've been enough.)
31 for (int i
= 0; i
< arg
; i
++)
32 clang_analyzer_numTimesReached(); // expected-warning {{2}}
34 clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
39 void opaqueConditionCall(int arg
) {
40 // Same situation as `opaqueCondition()` but with a `while ()` loop. This
41 // is also an example for a situation where the programmer cannot easily
42 // insert an assertion to guide the analyzer and rule out more than two
43 // iterations (so the analyzer needs to proactively avoid those unjustified
46 clang_analyzer_numTimesReached(); // expected-warning {{2}}
48 clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
51 void opaqueConditionDoWhile(int arg
) {
52 // Same situation as `opaqueCondition()` but with a `do {} while ()` loop.
53 // This is tested separately because this loop type is a special case in the
54 // iteration count calculation.
57 clang_analyzer_numTimesReached(); // expected-warning {{2}}
60 clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
63 void dontRememberOldBifurcation(int arg
) {
64 // In this (slightly contrived) test case the analyzer performs an assumption
65 // at the first iteration of the loop, but does not make any new assumptions
66 // in the subsequent iterations, so the analyzer should continue evaluating
68 // Previously this was mishandled in `eagerly-assume` mode (which is enabled
69 // by default), because the code remembered that there was a bifurcation on
70 // the first iteration of the loop and didn't realize that this is obsolete.
72 // NOTE: The variable `i` is introduced to ensure that the iterations of the
73 // loop change the state -- otherwise the analyzer stops iterating because it
74 // returns to the same `ExplodedNode`.
77 clang_analyzer_numTimesReached(); // expected-warning {{4}}
81 clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
84 void dontAssumeFourthIterartion(int arg
) {
88 // In this function the analyzer cannot leave the loop after exactly two
89 // iterations (because it knows that `arg != 2` at that point), so it
90 // performs a third iteration, but it does not assume that a fourth iteration
92 for (int i
= 0; i
< arg
; i
++)
93 clang_analyzer_numTimesReached(); // expected-warning {{3}}
95 clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
99 void shortCircuitInLoopCondition(int arg
) {
100 // When the loop condition expression contains short-circuiting operators, it
101 // performs "inner" bifurcations for those operators and only considers the
102 // last (rightmost) operand as the branch condition that is associated with
103 // the loop itself (as its loop condition).
104 // This means that assumptions taken in the left-hand side of a short-circuiting
105 // operator are not recognized as "opaque" loop condition, so the loop in
106 // this test case is allowed to finish four iterations.
107 // FIXME: This corner case is responsible for at least one out-of-bounds
108 // false positive on the ffmpeg codebase. Eventually we should properly
109 // recognize the full syntactical loop condition expression as "the loop
110 // condition", but this will be complicated to implement.
111 for (int i
= 0; i
< arg
&& TRUE
; i
++) {
112 clang_analyzer_numTimesReached(); // expected-warning {{4}}
114 clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
117 void shortCircuitInLoopConditionRHS(int arg
) {
118 // Unlike `shortCircuitInLoopCondition()`, this case is handled properly
119 // because the analyzer thinks that the right hand side of the `&&` is the
121 for (int i
= 0; TRUE
&& i
< arg
; i
++) {
122 clang_analyzer_numTimesReached(); // expected-warning {{2}}
124 clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
127 void eagerlyAssumeInSubexpression(int arg
) {
128 // The `EagerlyAssume` logic is another complication that can "split the
129 // 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
132 // cases where the loop condition is targeted by `EagerlyAssume`, but does
133 // not handle the (fortunately rare) case when `EagerlyAssume` hits a
134 // 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
++) {
138 clang_analyzer_numTimesReached(); // eagerlyassume-warning {{4}} noeagerlyassume-warning {{2}}
140 clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
143 void calledTwice(int arg
, int isFirstCall
) {
144 // This function is called twice (with two different unknown 'arg' values) to
145 // check the iteration count handling in this situation.
146 for (int i
= 0; i
< arg
; i
++) {
148 clang_analyzer_numTimesReached(); // expected-warning {{2}}
150 clang_analyzer_numTimesReached(); // expected-warning {{2}}
155 void caller(int arg
, int arg2
) {
156 // Entry point for `calledTwice()`.
158 calledTwice(arg2
, 0);
161 void innerLoopClearCondition(void) {
162 // A "control group" test case for the behavior of an inner loop. Notice that
163 // although the (default) value of `-analyzer-max-loop` is 4, we only see 3 iterations
164 // of the inner loop, because `-analyzer-max-loop` limits the number of
165 // evaluations of _the loop condition of the inner loop_ and in addition to
166 // the 3 evaluations before the 3 iterations, there is also a step where it
167 // evaluates to false (in the first iteration of the outer loop).
168 for (int outer
= 0; outer
< 2; outer
++) {
172 clang_analyzer_dump(limit
); // expected-warning {{0}} expected-warning {{10}}
173 for (int i
= 0; i
< limit
; i
++) {
174 clang_analyzer_numTimesReached(); // expected-warning {{3}}
179 void innerLoopOpaqueCondition(int arg
) {
180 // In this test case the engine doesn't assume a second iteration within the
181 // inner loop (in the second iteration of the outer loop, when the limit is
182 // opaque) because `CoreEngine::getCompletedIterationCount()` is based on the
183 // `BlockCount` values queried from the `BlockCounter` which count _all_
184 // evaluations of a given `CFGBlock` (in our case, the loop condition) and
185 // not just the evaluations within the current iteration of the outer loop.
186 // FIXME: This inaccurate iteration count could in theory cause some false
187 // negatives, although I think this would be unusual in practice, as the
188 // small default value of `-analyzer-max-loop` means that this is only
189 // relevant if the analyzer can deduce that the inner loop performs 0 or 1
190 // iterations within the first iteration of the outer loop (and then the
191 // condition of the inner loop is opaque within the second iteration of the
193 for (int outer
= 0; outer
< 2; outer
++) {
197 clang_analyzer_dump(limit
); // expected-warning {{0}} expected-warning {{reg_$}}
198 for (int i
= 0; i
< limit
; i
++) {
199 clang_analyzer_numTimesReached(); // expected-warning {{1}}
204 void onlyLoopConditions(int arg
) {
205 // This "don't assume third iteration" logic only examines the conditions of
206 // loop statements and does not affect the analysis of code that implements
207 // similar behavior with different language features like if + break, goto,
208 // recursive functions, ...
211 clang_analyzer_numTimesReached(); // expected-warning {{4}}
213 // This is not a loop condition.
218 clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}