Skip to content

Commit 62d5dcc

Browse files
committed
RULE-7-0-2 - NoImplicitBoolConversion
Detects implicit conversions to bool type from fundamental types, unscoped enumerations, and pointers that may lead to unintended behavior in conditional expressions.
1 parent 5b810c3 commit 62d5dcc

File tree

4 files changed

+253
-0
lines changed

4 files changed

+253
-0
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/**
2+
* @id cpp/misra/no-implicit-bool-conversion
3+
* @name RULE-7-0-2: There shall be no conversion to type bool
4+
* @description Implicit and contextual conversions to bool from fundamental types, unscoped enums,
5+
* or pointers may lead to unintended behavior, except for specific cases like pointer
6+
* checks and explicit operator bool conversions.
7+
* @kind problem
8+
* @precision very-high
9+
* @problem.severity error
10+
* @tags external/misra/id/rule-7-0-2
11+
* scope/single-translation-unit
12+
* external/misra/enforcement/decidable
13+
* external/misra/obligation/required
14+
*/
15+
16+
import cpp
17+
import codingstandards.cpp.misra
18+
19+
predicate isInContextualBoolContext(Expr expr) {
20+
exists(IfStmt ifStmt | ifStmt.getCondition() = expr) or
21+
exists(WhileStmt whileStmt | whileStmt.getCondition() = expr) or
22+
exists(ForStmt forStmt | forStmt.getCondition() = expr) or
23+
exists(DoStmt doStmt | doStmt.getCondition() = expr) or
24+
exists(ConditionalExpr condExpr | condExpr.getCondition() = expr) or
25+
exists(LogicalAndExpr logicalAnd | logicalAnd.getAnOperand() = expr) or
26+
exists(LogicalOrExpr logicalOr | logicalOr.getAnOperand() = expr) or
27+
exists(NotExpr notExpr | notExpr.getOperand() = expr)
28+
}
29+
30+
predicate isInWhileConditionDeclaration(Expr expr) {
31+
exists(WhileStmt whileStmt, ConditionDeclExpr condDecl |
32+
whileStmt.getCondition() = condDecl and
33+
condDecl.getExpr() = expr
34+
)
35+
}
36+
37+
predicate isBitFieldOfSizeOne(Expr expr) {
38+
exists(BitField bf |
39+
expr = bf.getAnAccess() and
40+
bf.getNumBits() = 1
41+
)
42+
}
43+
44+
from Element e, string reason
45+
where
46+
not isExcluded(e, ConversionsPackage::noImplicitBoolConversionQuery()) and
47+
(
48+
// Conversions to bool
49+
exists(Conversion conv |
50+
e = conv and
51+
conv.getType().getUnspecifiedType() instanceof BoolType and
52+
not conv.getExpr().getType().getUnspecifiedType() instanceof BoolType and
53+
// Exception 2: Contextual conversion from pointer
54+
not (
55+
conv.getExpr().getType().getUnspecifiedType() instanceof PointerType and
56+
isInContextualBoolContext(conv.getExpr())
57+
) and
58+
// Exception 3: Bit-field of size 1
59+
not isBitFieldOfSizeOne(conv.getExpr()) and
60+
// Exception 4: While condition declaration
61+
not isInWhileConditionDeclaration(conv.getExpr()) and
62+
reason = "Conversion from '" + conv.getExpr().getType().toString() + "' to 'bool'"
63+
)
64+
or
65+
// Calls to conversion operators to bool
66+
//
67+
// Note: we flag these separately because:
68+
// 1. If the conversion via the operator is implicit, there is no `Conversion` - only a call to
69+
// the `ConversionOperator`.
70+
// 2. If the conversion is explicit, the `Conversion` is from `bool` to `bool`, which is not
71+
// flagged in the previous `Conversion` case above.
72+
exists(Call conversionCall, ConversionOperator op |
73+
e = conversionCall and
74+
conversionCall.getTarget() = op and
75+
op.getType().getUnspecifiedType() instanceof BoolType and
76+
// Exception 1: Static cast to bool from class with explicit operator bool
77+
not exists(StaticCast conv |
78+
op.isExplicit() and
79+
conv.getExpr() = conversionCall and
80+
conv.getType().getUnspecifiedType() instanceof BoolType
81+
) and
82+
// Exception 2: Contextual conversion from class with explicit operator bool is allowed
83+
not (
84+
op.isExplicit() and
85+
isInContextualBoolContext(conversionCall)
86+
) and
87+
reason =
88+
"Conversion operator call from '" + conversionCall.getQualifier().getType().toString() +
89+
"' to 'bool'"
90+
)
91+
)
92+
select e, reason + "."
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
| test.cpp:46:20:46:28 | (bool)... | Conversion from 'int' to 'bool'. |
2+
| test.cpp:50:7:50:7 | (bool)... | Conversion from 'int' to 'bool'. |
3+
| test.cpp:52:7:52:8 | (bool)... | Conversion from 'uint8_t' to 'bool'. |
4+
| test.cpp:54:8:54:8 | (bool)... | Conversion from 'int' to 'bool'. |
5+
| test.cpp:58:7:58:8 | (bool)... | Conversion from 'uint8_t' to 'bool'. |
6+
| test.cpp:69:8:69:9 | (bool)... | Conversion from 'int16_t' to 'bool'. |
7+
| test.cpp:78:20:78:21 | (bool)... | Conversion from 'int32_t' to 'bool'. |
8+
| test.cpp:84:31:84:32 | (bool)... | Conversion from 'int32_t' to 'bool'. |
9+
| test.cpp:92:30:92:31 | (bool)... | Conversion from 'int32_t' to 'bool'. |
10+
| test.cpp:103:13:103:16 | (bool)... | Conversion from 'int32_t *' to 'bool'. |
11+
| test.cpp:108:13:108:32 | static_cast<bool>... | Conversion from 'int' to 'bool'. |
12+
| test.cpp:109:13:109:14 | (bool)... | Conversion from 'uint8_t' to 'bool'. |
13+
| test.cpp:121:13:121:13 | call to operator bool | Conversion operator call from 'TestClassImplicit' to 'bool'. |
14+
| test.cpp:134:13:134:14 | (bool)... | Conversion from 'Color' to 'bool'. |
15+
| test.cpp:135:7:135:8 | (bool)... | Conversion from 'Color' to 'bool'. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/RULE-7-0-2/NoImplicitBoolConversion.ql
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
#include <cstdint>
2+
#include <iostream>
3+
4+
// Global variables for testing
5+
std::uint8_t g1 = 5;
6+
std::uint8_t g2 = 10;
7+
std::uint8_t g3 = 15;
8+
std::uint8_t g4 = 20;
9+
std::int16_t g5 = 100;
10+
std::int32_t g6 = 200;
11+
bool g7 = true;
12+
13+
// Function declarations
14+
std::int32_t f1();
15+
bool f2();
16+
std::int32_t *f3();
17+
18+
// Class with explicit operator bool
19+
class TestClassExplicit {
20+
std::int32_t m1;
21+
22+
public:
23+
explicit operator bool() const { return m1 < 0; }
24+
};
25+
26+
class TestClassImplicit {
27+
std::int32_t m1;
28+
29+
public:
30+
operator bool() const { return m1 < 0; } // Implicit conversion
31+
};
32+
33+
// Bit-field struct for exception #3
34+
struct BitFieldStruct {
35+
unsigned int m1 : 1;
36+
};
37+
38+
void test_logical_operators() {
39+
std::uint8_t l1 = 5;
40+
std::uint8_t l2 = 10;
41+
std::uint8_t l3 = 15;
42+
std::uint8_t l4 = 20;
43+
44+
if ((l1 < l2) && (l3 < l4)) { // COMPLIANT
45+
}
46+
if ((l1 < l2) && (l3 + l4)) { // NON_COMPLIANT
47+
}
48+
if (true && (l3 < l4)) { // COMPLIANT
49+
}
50+
if (1 && (l3 < l4)) { // NON_COMPLIANT
51+
}
52+
if (l1 && (l3 < l4)) { // NON_COMPLIANT
53+
}
54+
if (!0) { // NON_COMPLIANT
55+
}
56+
if (!false) { // COMPLIANT
57+
}
58+
if (l1) { // NON_COMPLIANT
59+
}
60+
}
61+
62+
void test_conditional_operator() {
63+
std::int32_t l1 = 100;
64+
std::int32_t l2 = 200;
65+
std::int32_t l3 = 300;
66+
std::int16_t l4 = 50;
67+
bool l5 = true;
68+
69+
l1 = l4 ? l2 : l3; // NON_COMPLIANT
70+
l1 = l5 ? l2 : l3; // COMPLIANT
71+
l1 = (l4 < 5) ? l2 : l3; // COMPLIANT
72+
}
73+
74+
void test_if_statements() {
75+
std::int32_t l1;
76+
bool l2 = f2();
77+
78+
if (std::int32_t l3 = f1()) { // NON_COMPLIANT
79+
}
80+
if (std::int32_t l4 = f1(); l4 != 0) { // COMPLIANT
81+
}
82+
if (bool l5 = f2()) { // COMPLIANT
83+
}
84+
if (std::int32_t l6 = f1(); l6) { // NON_COMPLIANT
85+
}
86+
}
87+
88+
void test_while_loops() {
89+
while (std::int32_t l1 = f1()) { // COMPLIANT - exception #4
90+
}
91+
92+
for (std::int32_t l2 = 10; l2; --l2) { // NON_COMPLIANT
93+
}
94+
95+
while (std::cin) { // COMPLIANT - exception #2
96+
}
97+
}
98+
99+
void test_pointer_conversions() {
100+
if (f3()) { // COMPLIANT - exception #2
101+
}
102+
103+
bool l1 = f3(); // NON_COMPLIANT
104+
bool l2 = f3() != nullptr; // COMPLIANT
105+
}
106+
107+
void test_assignment_to_bool() {
108+
bool l1 = static_cast<bool>(4); // NON_COMPLIANT
109+
bool l2 = g1; // NON_COMPLIANT
110+
bool l3 = (g1 < g2); // COMPLIANT
111+
bool l4 = g7; // COMPLIANT
112+
}
113+
114+
void test_class_with_explicit_bool_operator() {
115+
TestClassExplicit l1;
116+
117+
bool l2 = static_cast<bool>(l1); // COMPLIANT - exception #1
118+
if (l1) { // COMPLIANT - exception #2
119+
}
120+
TestClassImplicit l3;
121+
bool l4 = l3; // NON_COMPLIANT
122+
}
123+
124+
void test_bitfield_conversion() {
125+
BitFieldStruct l1;
126+
127+
bool l2 = l1.m1; // COMPLIANT - exception #3
128+
}
129+
130+
void test_unscoped_enum_conversion() {
131+
enum Color { RED, GREEN, BLUE };
132+
Color l1 = RED;
133+
134+
bool l2 = l1; // NON_COMPLIANT
135+
if (l1) { // NON_COMPLIANT
136+
}
137+
bool l3 = (l1 == RED); // COMPLIANT
138+
}
139+
140+
void test_scoped_enum_conversion() {
141+
enum class Status { ACTIVE, INACTIVE };
142+
Status l1 = Status::ACTIVE;
143+
144+
bool l2 = (l1 == Status::ACTIVE); // COMPLIANT
145+
}

0 commit comments

Comments
 (0)