Skip to content

Commit c88993f

Browse files
committed
Fix RULE-7-0-2 false positives for contextual bool conversions
NoImplicitBoolConversion reported contextual conversions that the rule permits: - Condition declarations (if (T* p = f()), while (auto sp = w.lock()), for (...; T* p = f(); ...)): the initializer is contextually converted to bool. Treat the initializer of a condition declaration as a contextual bool context, so pointers and classes with an explicit operator bool are no longer flagged. - static_cast<bool>(x) where x is a class (or class template instantiation) with an explicit operator bool: the extractor leaves members of uninstantiated templates unresolved, so look the operator up on the instantiated template as well.
1 parent bc8a0eb commit c88993f

4 files changed

Lines changed: 70 additions & 2 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
- `RULE-7-0-2` - `NoImplicitBoolConversion.ql`:
2+
- Fixed false positives on the initializer of an `if`/`for` condition
3+
declaration (e.g. `if (T* p = f())` or `if (auto sp = weak.lock())`) when
4+
the initialized type is a pointer or a class with an explicit
5+
`operator bool`. These are contextual conversions, which are permitted.
6+
- Fixed false positives on `static_cast<bool>` of a class type with an
7+
explicit `operator bool` inside a class template.

‎cpp/misra/src/rules/RULE-7-0-2/NoImplicitBoolConversion.ql‎

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,21 @@
1616
import cpp
1717
import codingstandards.cpp.misra
1818

19+
/**
20+
* The initializer of a condition declaration is contextually converted to bool, e.g.
21+
* `if (T* p = f())` or `if (auto sp = weak.lock())`.
22+
*/
23+
predicate isInConditionDeclarationInitializer(Expr expr) {
24+
exists(ConditionDeclExpr condDecl |
25+
condDecl.getExpr() = expr and
26+
(
27+
exists(IfStmt ifStmt | ifStmt.getCondition() = condDecl) or
28+
exists(WhileStmt whileStmt | whileStmt.getCondition() = condDecl) or
29+
exists(ForStmt forStmt | forStmt.getCondition() = condDecl)
30+
)
31+
)
32+
}
33+
1934
predicate isInContextualBoolContext(Expr expr) {
2035
exists(IfStmt ifStmt | ifStmt.getCondition() = expr) or
2136
exists(WhileStmt whileStmt | whileStmt.getCondition() = expr) or
@@ -24,7 +39,8 @@ predicate isInContextualBoolContext(Expr expr) {
2439
exists(ConditionalExpr condExpr | condExpr.getCondition() = expr) or
2540
exists(LogicalAndExpr logicalAnd | logicalAnd.getAnOperand() = expr) or
2641
exists(LogicalOrExpr logicalOr | logicalOr.getAnOperand() = expr) or
27-
exists(NotExpr notExpr | notExpr.getOperand() = expr)
42+
exists(NotExpr notExpr | notExpr.getOperand() = expr) or
43+
isInConditionDeclarationInitializer(expr)
2844
}
2945

3046
predicate isInWhileConditionDeclaration(Expr expr) {
@@ -88,6 +104,20 @@ where
88104
// an array to a pointer, then the pointer to bool
89105
isInContextualBoolContext(conv.getUnconverted())
90106
) and
107+
// Exception 1: Static cast to bool from a class with an explicit operator bool. In an
108+
// uninstantiated template the cast has no resolved call to the conversion operator, so it
109+
// appears only as a `Conversion` from the class type.
110+
not (
111+
conv instanceof StaticCast and
112+
exists(ConversionOperator op |
113+
op.isExplicit() and
114+
op.getType().getUnspecifiedType() instanceof BoolType and
115+
exists(Class source | source = conv.getExpr().getType().getUnspecifiedType() |
116+
op = source.getAMember() or
117+
op = source.(ClassTemplateInstantiation).getTemplate().getAMember()
118+
)
119+
)
120+
) and
91121
// Exception 3: Unconverted expression is a bit-field of size 1
92122
not isBitFieldOfSizeOne(conv.getUnconverted()) and
93123
// Exception 4: Unconverted expression is in a while condition declaration

‎cpp/misra/test/rules/RULE-7-0-2/NoImplicitBoolConversion.expected‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@
2525
| test.cpp:201:13:201:14 | (bool)... | Conversion from 'int32_t *' to 'bool'. |
2626
| test.cpp:213:13:213:14 | (bool)... | Conversion from '..:: *' to 'bool'. |
2727
| test.cpp:214:13:214:14 | (bool)... | Conversion from '..:: *' to 'bool'. |
28+
| test.cpp:283:20:283:21 | (bool)... | Conversion from 'int32_t' to 'bool'. |

‎cpp/misra/test/rules/RULE-7-0-2/test.cpp‎

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,4 +268,34 @@ constexpr bool derived_from_template_v = is_something_v<T>; // COMPLIANT
268268

269269
bool test_dependent_variable_template_instantiation() {
270270
return derived_from_template_v<std::int32_t>; // COMPLIANT
271-
}
271+
}
272+
void test_condition_declaration() {
273+
if (std::int32_t *l1 = f3()) { // COMPLIANT - exception #2
274+
}
275+
if (auto *const l2 = f3()) { // COMPLIANT - exception #2
276+
}
277+
while (std::int32_t *l3 = f3()) { // COMPLIANT - exception #2
278+
}
279+
if (TestClassExplicit l4 = TestClassExplicit{}) { // COMPLIANT
280+
}
281+
if (std::int32_t *l5 = f3(); l5) { // COMPLIANT - exception #2
282+
}
283+
if (std::int32_t l6 = f1()) { // NON_COMPLIANT
284+
}
285+
}
286+
287+
template <typename T> class TestTemplateExplicit {
288+
public:
289+
explicit operator bool() const { return true; }
290+
};
291+
292+
template <typename T>
293+
bool test_static_cast_template(const TestTemplateExplicit<T> &l1) {
294+
return !static_cast<bool>(l1); // COMPLIANT - exception #1
295+
}
296+
297+
bool test_static_cast_template_use(
298+
const TestTemplateExplicit<std::int32_t> &l1) {
299+
bool l2 = static_cast<bool>(l1); // COMPLIANT - exception #1
300+
return l2 && test_static_cast_template(l1);
301+
}

0 commit comments

Comments
 (0)