Skip to content

Commit c99845c

Browse files
authored
Merge pull request github#2035 from geoffw0/comparison
CPP: Unclear comparison precedence template fix
2 parents 7ba0476 + cdf48cf commit c99845c

File tree

6 files changed

+116
-1
lines changed

6 files changed

+116
-1
lines changed

change-notes/1.23/analysis-cpp.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ The following changes in version 1.23 affect C/C++ analysis in all applications.
2020
| Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | More correct results | This query now checks for the beginning date of the Reiwa era (1st May 2019). |
2121
| Too few arguments to formatting function (`cpp/wrong-number-format-arguments`) | Fewer false positive results | Fixed false positives resulting from mistmatching declarations of a formatting function. |
2222
| Too many arguments to formatting function (`cpp/too-many-format-arguments`) | Fewer false positive results | Fixed false positives resulting from mistmatching declarations of a formatting function. |
23+
| Unclear comparison precedence (`cpp/comparison-precedence`) | Fewer false positive results | False positives involving template classes and functions have been fixed. |
2324

2425
## Changes to QL libraries
2526

cpp/ql/src/Likely Bugs/Arithmetic/ComparisonPrecedence.ql

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,8 @@
1414
import cpp
1515

1616
from ComparisonOperation co, ComparisonOperation chco
17-
where co.getAChild() = chco and not chco.isParenthesised()
17+
where
18+
co.getAChild() = chco and
19+
not chco.isParenthesised() and
20+
not co.isFromUninstantiatedTemplate(_)
1821
select co, "Check the comparison operator precedence."
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
| template.cpp:4:7:4:15 | ... < ... | Check the comparison operator precedence. |
2+
| test.cpp:42:6:42:14 | ... < ... | Check the comparison operator precedence. |
3+
| test.cpp:43:6:43:14 | ... > ... | Check the comparison operator precedence. |
4+
| test.cpp:44:6:44:16 | ... <= ... | Check the comparison operator precedence. |
5+
| test.cpp:45:6:45:16 | ... <= ... | Check the comparison operator precedence. |
6+
| test.cpp:46:6:46:14 | ... > ... | Check the comparison operator precedence. |
7+
| test.cpp:50:6:50:32 | ... < ... | Check the comparison operator precedence. |
8+
| test.cpp:51:6:51:18 | ... < ... | Check the comparison operator precedence. |
9+
| test.cpp:54:8:54:16 | ... < ... | Check the comparison operator precedence. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Likely Bugs/Arithmetic/ComparisonPrecedence.ql
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
2+
template <typename T>
3+
void templateFunc1(T x, T y, T z) {
4+
if (x < y < z) {} // BAD (though dubious as we can imagine other instantiations using an overloaded `operator<`)
5+
if (x < y && y < z) {} // GOOD
6+
};
7+
8+
template <typename T>
9+
void templateFunc2(T x, T y, T z) {
10+
if (x < y < z) {} // GOOD (used with an overloaded `operator<`)
11+
if (x < y && y < z) {} // GOOD
12+
};
13+
14+
struct myStruct {
15+
operator bool() {
16+
return true;
17+
}
18+
19+
myStruct operator<(myStruct &other) {
20+
return other; // non-standard `operator<` behaviour
21+
}
22+
};
23+
24+
int main() {
25+
int x = 3;
26+
myStruct y;
27+
28+
templateFunc1(x, x, x);
29+
templateFunc2(y, y, y);
30+
31+
return 0;
32+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
2+
/**
3+
* MyClass1 contains an `int` and has well behaved `operator<`
4+
*/
5+
class MyClass1 {
6+
public:
7+
MyClass1() : v(0) {};
8+
MyClass1(int _v) : v(_v) {};
9+
10+
bool operator<(const MyClass1 &other) {
11+
return v < other.v;
12+
}
13+
14+
operator bool() {
15+
return true;
16+
}
17+
18+
int v;
19+
};
20+
21+
/**
22+
* MyClass2 contains an `int` but has an unusual `operator<`
23+
*/
24+
class MyClass2 {
25+
public:
26+
MyClass2() : v(0) {};
27+
MyClass2(int _v) : v(_v) {};
28+
29+
MyClass2 operator<(const MyClass2 &other) {
30+
return MyClass2(other.v);
31+
}
32+
33+
operator bool() {
34+
return true;
35+
}
36+
37+
int v;
38+
};
39+
40+
void test1(int x, int y, int z) {
41+
// built-in comparison
42+
if (x < y < z) {} // BAD
43+
if (x > y > z) {} // BAD
44+
if (x <= y <= z) {} // BAD
45+
if (x <= y <= z) {} // BAD
46+
if (x < y > z) {} // BAD
47+
if ((x < y) && (y < z)) {} // GOOD
48+
if (x < y && y < z) {} // GOOD
49+
50+
if ((x + 1) < (y + 1) < (z + 1)) {} // BAD
51+
if (x < x + y < z) {} // BAD
52+
53+
if ((x < y) < z) {} // GOOD (this is deliberately allowed)
54+
if (!(x < y < z)) {} // BAD
55+
56+
// overloaded comparison
57+
{
58+
MyClass1 a, b, c;
59+
60+
if (a < b < c) {} // BAD (the overloaded `operator<` behaves like `<`) [NOT DETECTED]
61+
}
62+
63+
// overloaded non-comparison
64+
{
65+
MyClass2 a, b, c;
66+
67+
if (a < b < c) {} // GOOD (the overloaded `operator<` does not behave like `<`)
68+
}
69+
}

0 commit comments

Comments
 (0)