diff --git a/change_notes/2026-08-25-fix-fp-rule-7-0-2-unknown-and-reference-bool.md b/change_notes/2026-08-25-fix-fp-rule-7-0-2-unknown-and-reference-bool.md new file mode 100644 index 0000000000..938f098d21 --- /dev/null +++ b/change_notes/2026-08-25-fix-fp-rule-7-0-2-unknown-and-reference-bool.md @@ -0,0 +1,13 @@ +- `RULE-7-0-2` - `NoImplicitBoolConversion.ql`: + - Fixed false positives where a conversion's source expression type could + not be resolved to a concrete type (`UnknownType`), which occurs only in + template-dependent contexts that the extractor cannot resolve, e.g. a + `constexpr bool` variable template whose initializer is itself another + dependent variable template such as `std::conjunction_v<...>`. + - Fixed false positives on reference-dereference conversions (`bool&`/ + `bool&&` to `bool`), which occur e.g. via the compiler-synthesized + `std::get(...)` call used to implement structured binding + decomposition (`auto [a, b] = some_pair_or_tuple_expr;` where `b` is + `bool`). Dereferencing a reference to `bool` does not change the type or + representation of the value, so this is not a conversion to `bool` in + the sense intended by the rule. diff --git a/cpp/misra/src/rules/RULE-7-0-2/NoImplicitBoolConversion.ql b/cpp/misra/src/rules/RULE-7-0-2/NoImplicitBoolConversion.ql index ae70b20259..cbf189d447 100644 --- a/cpp/misra/src/rules/RULE-7-0-2/NoImplicitBoolConversion.ql +++ b/cpp/misra/src/rules/RULE-7-0-2/NoImplicitBoolConversion.ql @@ -56,6 +56,30 @@ where e = conv and conv.getType().getUnspecifiedType() instanceof BoolType and not conv.getExpr().getType().getUnspecifiedType() instanceof BoolType and + // Exclude conversions whose source expression type could not be resolved to a concrete + // type (`UnknownType`). This shape occurs only in template-dependent contexts that the + // extractor has not (and, for uninstantiated templates, cannot) resolve to an actual type + // - e.g. a `constexpr bool` variable template / `noexcept(...)` specifier built from + // another variable template such as `std::conjunction_v<...>`. There is no actual, + // concrete conversion to `bool` that a developer wrote or that the compiler ultimately + // performs; flagging it produces a false positive because the "conversion from 'unknown'" + // is an artifact of incomplete extraction of template-dependent/library code, not a + // genuine violation. + not conv.getExpr().getType() instanceof UnknownType and + // Exclude reference-dereference conversions (`T& -> T` / `T&& -> T`) whose referenced type + // is itself `bool` once its reference-ness is stripped, e.g. the compiler-synthesized + // `std::get(...)` call used to implement structured binding decomposition + // (`auto [a, b] = some_pair_or_tuple_expr;` where `b` is `bool`). `getUnspecifiedType()` + // resolves typedefs/specifiers but does not strip reference qualification, so a `bool&`/ + // `bool&&` is not itself recognised as already being `bool` by the check above. But + // dereferencing a reference to `bool` does not represent a conversion from a different + // type to `bool` - the referenced value is already a `bool` - so this is not a violation. + not conv.getExpr() + .getType() + .getUnspecifiedType() + .(ReferenceType) + .getBaseType() + .getUnspecifiedType() instanceof BoolType and // Exception 2: Contextual conversion from pointer not ( isPointerType(conv.getExpr().getType()) and diff --git a/cpp/misra/test/rules/RULE-7-0-2/test.cpp b/cpp/misra/test/rules/RULE-7-0-2/test.cpp index 01e53c15e4..ce57597632 100644 --- a/cpp/misra/test/rules/RULE-7-0-2/test.cpp +++ b/cpp/misra/test/rules/RULE-7-0-2/test.cpp @@ -213,4 +213,59 @@ void test_member_function_pointer_conversion() { bool l3 = l1; // NON_COMPLIANT bool l4 = l2; // NON_COMPLIANT bool l5 = (l1 != nullptr); // COMPLIANT +} + +// Regression test for a false positive where the compiler-synthesized +// `get(...)` call used to implement structured binding decomposition (`auto +// [a, b] = ...;`) was incorrectly flagged as a "conversion to bool", even +// though the decomposed member is already `bool` - dereferencing a +// `bool&`/`bool&&` reference is not a conversion from another type to `bool`. +struct BoolPair { + std::int32_t first; + bool second; +}; + +template auto get(const BoolPair &p) { + if constexpr (I == 0) { + return p.first; + } else { + return p.second; + } +} + +namespace std { +template struct tuple_size; +template struct tuple_element; + +template <> struct tuple_size { + static constexpr std::size_t value = 2; +}; +template <> struct tuple_element<0, BoolPair> { using type = std::int32_t; }; +template <> struct tuple_element<1, BoolPair> { using type = bool; }; +} // namespace std + +BoolPair make_bool_pair(); + +void test_structured_binding_bool_decomposition() { + auto [l1, l2] = + make_bool_pair(); // COMPLIANT - structured binding decomposition, not a + // real conversion to bool + if (l2) { // COMPLIANT + } + bool l3 = l2; // COMPLIANT - l2 is already bool +} + +// Regression test for a false positive where a `constexpr bool` variable +// template, whose initializer is itself a dependent expression built from +// another variable template, was incorrectly flagged as a "conversion from +// 'unknown' to bool". The extractor cannot resolve a concrete type for a +// dependent, uninstantiated template expression, so it should not be treated as +// an actual conversion. +template constexpr bool is_something_v = true; + +template +constexpr bool derived_from_template_v = is_something_v; // COMPLIANT + +bool test_dependent_variable_template_instantiation() { + return derived_from_template_v; // COMPLIANT } \ No newline at end of file