Skip to content

Commit e1da6e9

Browse files
Merge pull request #1515 from geoffw0/continuefalseloop
CPP: Improvements to ContinueInFalseLoop.ql
2 parents 1b38208 + bfe5703 commit e1da6e9

File tree

6 files changed

+147
-7
lines changed

6 files changed

+147
-7
lines changed

change-notes/1.22/analysis-cpp.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
| **Query** | **Expected impact** | **Change** |
1313
|----------------------------|------------------------|------------------------------------------------------------------|
14+
| Continue statement that does not continue (`cpp/continue-in-false-loop`) | Fewer false positive results | Analysis is now restricted to `do`-`while` loops. This query is now run and displayed by default on LGTM. |
1415
| Expression has no effect (`cpp/useless-expression`) | Fewer false positive results | Calls to functions with the `weak` attribute are no longer considered to be side effect free, because they could be overridden with a different implementation at link time. |
1516
| No space for zero terminator (`cpp/no-space-for-terminator`) | Fewer false positive results | False positives involving strings that are not null-terminated have been excluded. |
1617
| Suspicious pointer scaling (`cpp/suspicious-pointer-scaling`) | Lower precision | The precision of this query has been reduced to "medium". This coding pattern is used intentionally and safely in a number of real-world projects. Results are no longer displayed on LGTM unless you choose to display them. |
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
7+
<overview>
8+
<p>A <code>continue</code> statement only re-runs the loop if the loop condition is true. Therefore using <code>continue</code> in a loop with a constant false condition will never cause the loop body to be re-run, which is misleading.
9+
</p>
10+
11+
</overview>
12+
<recommendation>
13+
14+
<p>Replace the <code>continue</code> statement with a <code>break</code> statement if the intent is to break from the loop.
15+
</p>
16+
17+
</recommendation>
18+
19+
<references>
20+
<li>
21+
Tutorialspoint - C Programming Language: <a href="https://www.tutorialspoint.com/cprogramming/c_continue_statement.htm">Continue Statement in C</a>.
22+
</li>
23+
</references>
24+
</qhelp>

cpp/ql/src/Likely Bugs/ContinueInFalseLoop.ql

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,43 @@
66
* @kind problem
77
* @id cpp/continue-in-false-loop
88
* @problem.severity warning
9+
* @precision high
10+
* @tags correctness
911
*/
1012

1113
import cpp
1214

13-
Loop getAFalseLoop() {
15+
/**
16+
* Gets a `do` ... `while` loop with a constant false condition.
17+
*/
18+
DoStmt getAFalseLoop() {
1419
result.getControllingExpr().getValue() = "0"
1520
and not result.getControllingExpr().isAffectedByMacro()
1621
}
1722

18-
Loop enclosingLoop(Stmt s) {
23+
/**
24+
* Gets a `do` ... `while` loop surrounding a statement. This is blocked by a
25+
* `switch` statement, since a `continue` inside a `switch` inside a loop may be
26+
* jusitifed (`continue` breaks out of the loop whereas `break` only escapes the
27+
* `switch`).
28+
*/
29+
DoStmt enclosingLoop(Stmt s) {
1930
exists(Stmt parent |
2031
parent = s.getParent() and
21-
if parent instanceof Loop then
22-
result = parent
23-
else
24-
result = enclosingLoop(parent))
32+
(
33+
(
34+
parent instanceof Loop and
35+
result = parent
36+
) or (
37+
not parent instanceof Loop and
38+
not parent instanceof SwitchStmt and
39+
result = enclosingLoop(parent)
40+
)
41+
)
42+
)
2543
}
2644

27-
from Loop loop, ContinueStmt continue
45+
from DoStmt loop, ContinueStmt continue
2846
where loop = getAFalseLoop()
2947
and loop = enclosingLoop(continue)
3048
select continue,
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
| test.cpp:13:4:13:12 | continue; | This 'continue' never re-runs the loop - the $@ is always false. | test.cpp:16:11:16:15 | 0 | loop condition |
2+
| test.cpp:59:5:59:13 | continue; | This 'continue' never re-runs the loop - the $@ is always false. | test.cpp:62:12:62:16 | 0 | loop condition |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Likely Bugs/ContinueInFalseLoop.ql
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
2+
bool cond();
3+
4+
void test1(int x)
5+
{
6+
int i;
7+
8+
// --- do loops ---
9+
10+
do
11+
{
12+
if (cond())
13+
continue; // BAD
14+
if (cond())
15+
break;
16+
} while (false);
17+
18+
do
19+
{
20+
if (cond())
21+
continue;
22+
if (cond())
23+
break;
24+
} while (true);
25+
26+
do
27+
{
28+
if (cond())
29+
continue;
30+
if (cond())
31+
break;
32+
} while (cond());
33+
34+
// --- while, for loops ---
35+
36+
while (false)
37+
{
38+
if (cond())
39+
continue; // GOOD [never reached, if the condition changed so it was then the result would no longer apply]
40+
if (cond())
41+
break;
42+
}
43+
44+
for (i = 0; false; i++)
45+
{
46+
if (cond())
47+
continue; // GOOD [never reached, if the condition changed so it was then the result would no longer apply]
48+
if (cond())
49+
break;
50+
}
51+
52+
// --- nested loops ---
53+
54+
do
55+
{
56+
do
57+
{
58+
if (cond())
59+
continue; // BAD
60+
if (cond())
61+
break;
62+
} while (false);
63+
} while (true);
64+
65+
do
66+
{
67+
do
68+
{
69+
if (cond())
70+
continue; // GOOD
71+
if (cond())
72+
break;
73+
} while (true);
74+
} while (false);
75+
76+
do
77+
{
78+
switch (x)
79+
{
80+
case 1:
81+
// do [1]
82+
83+
break; // break out of the switch
84+
85+
default:
86+
// do [2]
87+
88+
continue; // GOOD; break out of the loop entirely, skipping [3]
89+
};
90+
91+
// do [3]
92+
93+
} while (0);
94+
}

0 commit comments

Comments
 (0)