11// RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection \
2- // RUN: -verify=expected,eagerlyassume %s
2+ // RUN: -verify=expected,noassumeone, eagerlyassume,combo %s
33// RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection \
44// 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-one-iteration=true \
8+ // RUN: -verify=expected,eagerlyassume,combo %s
9+ // RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection \
10+ // RUN: -analyzer-config assume-one-iteration=true,eagerly-assume=false \
511// RUN: -verify=expected,noeagerlyassume %s
612
13+ // The verify tag "combo" is used for one unique warning which is produced in three
14+ // of the four RUN combinations.
15+
716// These tests validate the logic within `ExprEngine::processBranch` which
817// ensures that in loops with opaque conditions we don't assume execution paths
918// 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-one-iteration` is enabled, then we don't assume that
22+ // a loop can be skipped completely.
1023
1124void clang_analyzer_numTimesReached (void );
12- void clang_analyzer_warnIfReached (void );
1325void clang_analyzer_dump (int );
1426
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 ,
1729 // then this corrective logic doesn't activate and the engine executes
1830 // `-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 ++ )
2033 clang_analyzer_numTimesReached (); // expected-warning {{4}}
2134
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 (obviously) skipped, even in `assume-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}}
2346}
2447
2548void opaqueCondition (int arg ) {
@@ -28,10 +51,13 @@ void opaqueCondition(int arg) {
2851 // that more than two iterations are possible. (It _does_ imply that two
2952 // iterations may be possible at least in some cases, because otherwise an
3053 // `if` would've been enough.)
31- for (int i = 0 ; i < arg ; i ++ )
54+ // Moreover, if `assume-one-iteration` is enabled, then assume at least one
55+ // iteration.
56+ int i ;
57+ for (i = 0 ; i < arg ; i ++ )
3258 clang_analyzer_numTimesReached (); // expected-warning {{2}}
3359
34- clang_analyzer_warnIfReached ( ); // expected-warning {{REACHABLE }}
60+ clang_analyzer_dump ( i ); // noassumeone-warning {{0}} expected-warning {{1}} expected-warning {{2 }}
3561}
3662
3763int check (void );
@@ -42,22 +68,26 @@ void opaqueConditionCall(int arg) {
4268 // insert an assertion to guide the analyzer and rule out more than two
4369 // iterations (so the analyzer needs to proactively avoid those unjustified
4470 // branches).
45- while (check ())
71+ int i = 0 ; // Helper to distinguish the the branches after the loop.
72+ while (check ()) {
4673 clang_analyzer_numTimesReached (); // expected-warning {{2}}
74+ i ++ ;
75+ }
4776
48- clang_analyzer_warnIfReached ( ); // expected-warning {{REACHABLE }}
77+ clang_analyzer_dump ( i ); // noassumeone-warning {{0}} expected-warning {{1}} expected-warning {{2 }}
4978}
5079
5180void opaqueConditionDoWhile (int arg ) {
5281 // Same situation as `opaqueCondition()` but with a `do {} while ()` loop.
5382 // This is tested separately because this loop type is a special case in the
5483 // iteration count calculation.
84+ // Obviously, this loop guarantees that at least one iteration will happen.
5585 int i = 0 ;
5686 do {
5787 clang_analyzer_numTimesReached (); // expected-warning {{2}}
5888 } while (i ++ < arg );
5989
60- clang_analyzer_warnIfReached ( ); // expected-warning {{REACHABLE }}
90+ clang_analyzer_dump ( i ); // expected-warning {{1}} expected-warning {{2 }}
6191}
6292
6393void dontRememberOldBifurcation (int arg ) {
@@ -69,7 +99,7 @@ void dontRememberOldBifurcation(int arg) {
6999 // by default), because the code remembered that there was a bifurcation on
70100 // the first iteration of the loop and didn't realize that this is obsolete.
71101
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
73103 // loop change the state -- otherwise the analyzer stops iterating because it
74104 // returns to the same `ExplodedNode`.
75105 int i = 0 ;
@@ -78,21 +108,23 @@ void dontRememberOldBifurcation(int arg) {
78108 i ++ ;
79109 }
80110
81- clang_analyzer_warnIfReached ( ); // expected -warning {{REACHABLE }}
111+ clang_analyzer_dump ( i ); // noassumeone -warning {{0 }}
82112}
83113
84114void dontAssumeFourthIterartion (int arg ) {
115+ int i ;
116+
85117 if (arg == 2 )
86118 return ;
87119
88120 // In this function the analyzer cannot leave the loop after exactly two
89121 // iterations (because it knows that `arg != 2` at that point), so it
90122 // performs a third iteration, but it does not assume that a fourth iteration
91123 // is also possible.
92- for (int i = 0 ; i < arg ; i ++ )
124+ for (i = 0 ; i < arg ; i ++ )
93125 clang_analyzer_numTimesReached (); // expected-warning {{3}}
94126
95- clang_analyzer_warnIfReached ( ); // expected-warning {{REACHABLE }}
127+ clang_analyzer_dump ( i ); // noassumeone-warning {{0}} expected-warning {{1}} expected-warning {{3 }}
96128}
97129
98130#define TRUE 1
@@ -108,20 +140,24 @@ void shortCircuitInLoopCondition(int arg) {
108140 // false positive on the ffmpeg codebase. Eventually we should properly
109141 // recognize the full syntactical loop condition expression as "the loop
110142 // 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 ++ ) {
112145 clang_analyzer_numTimesReached (); // expected-warning {{4}}
113146 }
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}}
115149}
116150
117151void shortCircuitInLoopConditionRHS (int arg ) {
118152 // Unlike `shortCircuitInLoopCondition()`, this case is handled properly
119153 // because the analyzer thinks that the right hand side of the `&&` is the
120154 // loop condition.
121- for (int i = 0 ; TRUE && i < arg ; i ++ ) {
155+ int i ;
156+ for (i = 0 ; TRUE && i < arg ; i ++ ) {
122157 clang_analyzer_numTimesReached (); // expected-warning {{2}}
123158 }
124- clang_analyzer_warnIfReached (); // expected-warning {{REACHABLE}}
159+
160+ clang_analyzer_dump (i ); // noassumeone-warning {{0}} expected-warning {{1}} expected-warning {{2}}
125161}
126162
127163void eagerlyAssumeInSubexpression (int arg ) {
@@ -134,16 +170,22 @@ void eagerlyAssumeInSubexpression(int arg) {
134170 // sub-expression of the loop condition (as in this contrived test case).
135171 // FIXME: I don't know a real-world example for this inconsistency, but it
136172 // would be good to eliminate it eventually.
137- for (int i = 0 ; (i >= arg ) - 1 ; i ++ ) {
173+ int i ;
174+ for (i = 0 ; (i >= arg ) - 1 ; i ++ ) {
138175 clang_analyzer_numTimesReached (); // eagerlyassume-warning {{4}} noeagerlyassume-warning {{2}}
139176 }
140- clang_analyzer_warnIfReached (); // expected-warning {{REACHABLE}}
177+
178+ // The 'combo' warning intentionally appears when `assume-one-iteration` is
179+ // disabled, but also appears as a bug (or at least inaccuracy) when
180+ // `assume-one-iteration` is true but `EagerlyAssume` is also enabled.
181+ clang_analyzer_dump (i ); // combo-warning {{0}} expected-warning {{1}} expected-warning {{2}} eagerlyassume-warning {{3}}
141182}
142183
143184void calledTwice (int arg , int isFirstCall ) {
144185 // This function is called twice (with two different unknown 'arg' values) to
145186 // check the iteration count handling in this situation.
146- for (int i = 0 ; i < arg ; i ++ ) {
187+ int i ;
188+ for (i = 0 ; i < arg ; i ++ ) {
147189 if (isFirstCall ) {
148190 clang_analyzer_numTimesReached (); // expected-warning {{2}}
149191 } else {
@@ -215,5 +257,5 @@ void onlyLoopConditions(int arg) {
215257 break ;
216258 }
217259
218- clang_analyzer_warnIfReached ( ); // expected-warning {{REACHABLE }}
260+ clang_analyzer_dump ( i ); // expected-warning {{1}} expected-warning {{2}} expected-warning {{3}} expected-warning {{4 }}
219261}
0 commit comments