From 1d2aa2e2d13524a9c1f4533eefacc46c1d853d2a Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Tue, 10 Jun 2025 12:34:13 +0100 Subject: [PATCH 01/57] RULE-7-0-1 - NoConversionFromBool Detects implicit and explicit conversions from type bool to other types, preventing potential confusion between bitwise and logical operators and ensuring clear type usage. [a] --- .../rules/RULE-7-0-1/NoConversionFromBool.ql | 47 +++++++++ .../RULE-7-0-1/NoConversionFromBool.expected | 29 ++++++ .../RULE-7-0-1/NoConversionFromBool.qlref | 1 + cpp/misra/test/rules/RULE-7-0-1/test.cpp | 97 +++++++++++++++++++ 4 files changed, 174 insertions(+) create mode 100644 cpp/misra/src/rules/RULE-7-0-1/NoConversionFromBool.ql create mode 100644 cpp/misra/test/rules/RULE-7-0-1/NoConversionFromBool.expected create mode 100644 cpp/misra/test/rules/RULE-7-0-1/NoConversionFromBool.qlref create mode 100644 cpp/misra/test/rules/RULE-7-0-1/test.cpp diff --git a/cpp/misra/src/rules/RULE-7-0-1/NoConversionFromBool.ql b/cpp/misra/src/rules/RULE-7-0-1/NoConversionFromBool.ql new file mode 100644 index 000000000..0c2cab56f --- /dev/null +++ b/cpp/misra/src/rules/RULE-7-0-1/NoConversionFromBool.ql @@ -0,0 +1,47 @@ +/** + * @id cpp/misra/no-conversion-from-bool + * @name RULE-7-0-1: There shall be no conversion from type bool + * @description Converting a bool type (implicitly or explicitly) to another type can lead to + * unintended behavior and code obfuscation, particularly when using bitwise operators + * instead of logical operators. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-7-0-1 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra + +from Expr e, Conversion conv +where + not isExcluded(e, ConversionsPackage::noConversionFromBoolQuery()) and + conv = e.getConversion() and + conv.getExpr().getType().stripTopLevelSpecifiers() instanceof BoolType and + not conv.getType().stripTopLevelSpecifiers() instanceof BoolType and + // Exclude cases that are explicitly allowed + not ( + // Exception: equality operators with both bool operands + exists(EQExpr eq | + eq.getAnOperand() = e and + eq.getLeftOperand().getType().stripTopLevelSpecifiers() instanceof BoolType and + eq.getRightOperand().getType().stripTopLevelSpecifiers() instanceof BoolType + ) or + exists(NEExpr ne | + ne.getAnOperand() = e and + ne.getLeftOperand().getType().stripTopLevelSpecifiers() instanceof BoolType and + ne.getRightOperand().getType().stripTopLevelSpecifiers() instanceof BoolType + ) or + // Exception: explicit constructor calls + exists(ConstructorCall cc | cc.getAnArgument() = e) or + // Exception: assignment to bit-field of length 1 + exists(AssignExpr assign | + assign.getRValue() = e and + assign.getLValue().(ValueFieldAccess).getTarget() instanceof BitField and + assign.getLValue().(ValueFieldAccess).getTarget().(BitField).getNumBits() = 1 + ) + ) +select e, "Conversion from 'bool' to '" + conv.getType().toString() + "'." \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-0-1/NoConversionFromBool.expected b/cpp/misra/test/rules/RULE-7-0-1/NoConversionFromBool.expected new file mode 100644 index 000000000..badfe7f04 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-1/NoConversionFromBool.expected @@ -0,0 +1,29 @@ +| test.cpp:23:9:23:10 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:23:14:23:15 | b2 | Conversion from 'bool' to 'int'. | +| test.cpp:24:9:24:10 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:24:14:24:15 | b2 | Conversion from 'bool' to 'int'. | +| test.cpp:25:9:25:10 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:25:14:25:15 | b2 | Conversion from 'bool' to 'int'. | +| test.cpp:26:10:26:11 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:29:9:29:10 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:29:14:29:15 | b2 | Conversion from 'bool' to 'int'. | +| test.cpp:30:9:30:10 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:30:14:30:15 | b2 | Conversion from 'bool' to 'int'. | +| test.cpp:31:9:31:10 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:31:15:31:16 | b2 | Conversion from 'bool' to 'int'. | +| test.cpp:32:9:32:10 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:32:15:32:16 | b2 | Conversion from 'bool' to 'int'. | +| test.cpp:35:9:35:10 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:36:9:36:10 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:37:9:37:10 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:40:22:40:23 | b1 | Conversion from 'bool' to 'double'. | +| test.cpp:41:22:41:23 | b1 | Conversion from 'bool' to 'double'. | +| test.cpp:42:30:42:31 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:45:36:45:37 | b1 | Conversion from 'bool' to 'int8_t'. | +| test.cpp:46:38:46:39 | b1 | Conversion from 'bool' to 'int32_t'. | +| test.cpp:49:8:49:9 | b1 | Conversion from 'bool' to 'int32_t'. | +| test.cpp:50:8:50:9 | b1 | Conversion from 'bool' to 'double'. | +| test.cpp:53:13:53:14 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:59:11:59:12 | b1 | Conversion from 'bool' to 'int8_t'. | +| test.cpp:60:12:60:13 | b1 | Conversion from 'bool' to 'int32_t'. | +| test.cpp:61:10:61:11 | b1 | Conversion from 'bool' to 'double'. | diff --git a/cpp/misra/test/rules/RULE-7-0-1/NoConversionFromBool.qlref b/cpp/misra/test/rules/RULE-7-0-1/NoConversionFromBool.qlref new file mode 100644 index 000000000..6d66f5148 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-1/NoConversionFromBool.qlref @@ -0,0 +1 @@ +rules/RULE-7-0-1/NoConversionFromBool.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-0-1/test.cpp b/cpp/misra/test/rules/RULE-7-0-1/test.cpp new file mode 100644 index 000000000..15a366a28 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-1/test.cpp @@ -0,0 +1,97 @@ +#include + +struct A { + explicit A(bool) {} +}; + +struct BitField { + std::uint8_t bit : 1; +}; + +void f1(std::int32_t n) {} +void f2(double d) {} + +void test_bool_conversion_violations() { + bool b1 = true; + bool b2 = false; + double d1 = 1.0; + std::int8_t s8a = 0; + std::int32_t s32a = 0; + BitField bf; + + // Bitwise operations - non-compliant + if (b1 & b2) {} // NON_COMPLIANT + if (b1 | b2) {} // NON_COMPLIANT + if (b1 ^ b2) {} // NON_COMPLIANT + if (~b1) {} // NON_COMPLIANT + + // Relational operations - non-compliant + if (b1 < b2) {} // NON_COMPLIANT + if (b1 > b2) {} // NON_COMPLIANT + if (b1 <= b2) {} // NON_COMPLIANT + if (b1 >= b2) {} // NON_COMPLIANT + + // Comparison with integer literals - non-compliant + if (b1 == 0) {} // NON_COMPLIANT + if (b1 == 1) {} // NON_COMPLIANT + if (b1 != 0) {} // NON_COMPLIANT + + // Arithmetic operations - non-compliant + double l1 = d1 * b1; // NON_COMPLIANT + double l2 = d1 + b1; // NON_COMPLIANT + std::int32_t l3 = s32a + b1; // NON_COMPLIANT + + // Explicit casts to integral types - non-compliant + s8a = static_cast(b1); // NON_COMPLIANT + s32a = static_cast(b1); // NON_COMPLIANT + + // Function parameter conversion - non-compliant + f1(b1); // NON_COMPLIANT + f2(b1); // NON_COMPLIANT + + // Switch statement - non-compliant + switch (b1) { // NON_COMPLIANT + case 0: break; + case 1: break; + } + + // Assignment to integral types - non-compliant + s8a = b1; // NON_COMPLIANT + s32a = b1; // NON_COMPLIANT + d1 = b1; // NON_COMPLIANT +} + +void test_bool_conversion_compliant() { + bool b1 = true; + bool b2 = false; + std::int8_t s8a = 0; + BitField bf; + + // Boolean equality operations - compliant + if (b1 == false) {} // COMPLIANT + if (b1 == true) {} // COMPLIANT + if (b1 == b2) {} // COMPLIANT + if (b1 != b2) {} // COMPLIANT + + // Logical operations - compliant + if (b1 && b2) {} // COMPLIANT + if (b1 || b2) {} // COMPLIANT + if (!b1) {} // COMPLIANT + + // Conditional operator without conversion - compliant + s8a = b1 ? 3 : 7; // COMPLIANT + + // Function parameter without conversion - compliant + f1(b1 ? 1 : 0); // COMPLIANT + + // Explicit constructor calls - compliant + A l1{true}; // COMPLIANT + A l2(false); // COMPLIANT + A l3 = static_cast(true); // COMPLIANT + + // Assignment to constructor - compliant + A l4 = A{false}; // COMPLIANT + + // Bit-field assignment exception - compliant + bf.bit = b1; // COMPLIANT +} \ No newline at end of file From 5b810c33d8a53e9272bc210e2fd1887fc7df78fc Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Tue, 10 Jun 2025 12:41:38 +0100 Subject: [PATCH 02/57] Rule 7.0.1: Address review issues - Simplify query implementation - Format test code --- .../cpp/exclusions/cpp/RuleMetadata.qll | 3 + .../rules/RULE-7-0-1/NoConversionFromBool.ql | 16 +- .../RULE-7-0-1/NoConversionFromBool.expected | 58 +++--- cpp/misra/test/rules/RULE-7-0-1/test.cpp | 182 ++++++++++-------- 4 files changed, 139 insertions(+), 120 deletions(-) diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll index abd6aeff9..88e4d5535 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll @@ -12,6 +12,7 @@ import Comments import Concurrency import Conditionals import Const +import Conversions import DeadCode import Declarations import ExceptionSafety @@ -67,6 +68,7 @@ newtype TCPPQuery = TConcurrencyPackageQuery(ConcurrencyQuery q) or TConditionalsPackageQuery(ConditionalsQuery q) or TConstPackageQuery(ConstQuery q) or + TConversionsPackageQuery(ConversionsQuery q) or TDeadCodePackageQuery(DeadCodeQuery q) or TDeclarationsPackageQuery(DeclarationsQuery q) or TExceptionSafetyPackageQuery(ExceptionSafetyQuery q) or @@ -122,6 +124,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat isConcurrencyQueryMetadata(query, queryId, ruleId, category) or isConditionalsQueryMetadata(query, queryId, ruleId, category) or isConstQueryMetadata(query, queryId, ruleId, category) or + isConversionsQueryMetadata(query, queryId, ruleId, category) or isDeadCodeQueryMetadata(query, queryId, ruleId, category) or isDeclarationsQueryMetadata(query, queryId, ruleId, category) or isExceptionSafetyQueryMetadata(query, queryId, ruleId, category) or diff --git a/cpp/misra/src/rules/RULE-7-0-1/NoConversionFromBool.ql b/cpp/misra/src/rules/RULE-7-0-1/NoConversionFromBool.ql index 0c2cab56f..600d45486 100644 --- a/cpp/misra/src/rules/RULE-7-0-1/NoConversionFromBool.ql +++ b/cpp/misra/src/rules/RULE-7-0-1/NoConversionFromBool.ql @@ -25,23 +25,19 @@ where // Exclude cases that are explicitly allowed not ( // Exception: equality operators with both bool operands - exists(EQExpr eq | + exists(EqualityOperation eq | eq.getAnOperand() = e and eq.getLeftOperand().getType().stripTopLevelSpecifiers() instanceof BoolType and eq.getRightOperand().getType().stripTopLevelSpecifiers() instanceof BoolType - ) or - exists(NEExpr ne | - ne.getAnOperand() = e and - ne.getLeftOperand().getType().stripTopLevelSpecifiers() instanceof BoolType and - ne.getRightOperand().getType().stripTopLevelSpecifiers() instanceof BoolType - ) or + ) + or // Exception: explicit constructor calls - exists(ConstructorCall cc | cc.getAnArgument() = e) or + exists(ConstructorCall cc | cc.getAnArgument() = e) + or // Exception: assignment to bit-field of length 1 exists(AssignExpr assign | assign.getRValue() = e and - assign.getLValue().(ValueFieldAccess).getTarget() instanceof BitField and assign.getLValue().(ValueFieldAccess).getTarget().(BitField).getNumBits() = 1 ) ) -select e, "Conversion from 'bool' to '" + conv.getType().toString() + "'." \ No newline at end of file +select e, "Conversion from 'bool' to '" + conv.getType().toString() + "'." diff --git a/cpp/misra/test/rules/RULE-7-0-1/NoConversionFromBool.expected b/cpp/misra/test/rules/RULE-7-0-1/NoConversionFromBool.expected index badfe7f04..8e0795fc6 100644 --- a/cpp/misra/test/rules/RULE-7-0-1/NoConversionFromBool.expected +++ b/cpp/misra/test/rules/RULE-7-0-1/NoConversionFromBool.expected @@ -1,29 +1,29 @@ -| test.cpp:23:9:23:10 | b1 | Conversion from 'bool' to 'int'. | -| test.cpp:23:14:23:15 | b2 | Conversion from 'bool' to 'int'. | -| test.cpp:24:9:24:10 | b1 | Conversion from 'bool' to 'int'. | -| test.cpp:24:14:24:15 | b2 | Conversion from 'bool' to 'int'. | -| test.cpp:25:9:25:10 | b1 | Conversion from 'bool' to 'int'. | -| test.cpp:25:14:25:15 | b2 | Conversion from 'bool' to 'int'. | -| test.cpp:26:10:26:11 | b1 | Conversion from 'bool' to 'int'. | -| test.cpp:29:9:29:10 | b1 | Conversion from 'bool' to 'int'. | -| test.cpp:29:14:29:15 | b2 | Conversion from 'bool' to 'int'. | -| test.cpp:30:9:30:10 | b1 | Conversion from 'bool' to 'int'. | -| test.cpp:30:14:30:15 | b2 | Conversion from 'bool' to 'int'. | -| test.cpp:31:9:31:10 | b1 | Conversion from 'bool' to 'int'. | -| test.cpp:31:15:31:16 | b2 | Conversion from 'bool' to 'int'. | -| test.cpp:32:9:32:10 | b1 | Conversion from 'bool' to 'int'. | -| test.cpp:32:15:32:16 | b2 | Conversion from 'bool' to 'int'. | -| test.cpp:35:9:35:10 | b1 | Conversion from 'bool' to 'int'. | -| test.cpp:36:9:36:10 | b1 | Conversion from 'bool' to 'int'. | -| test.cpp:37:9:37:10 | b1 | Conversion from 'bool' to 'int'. | -| test.cpp:40:22:40:23 | b1 | Conversion from 'bool' to 'double'. | -| test.cpp:41:22:41:23 | b1 | Conversion from 'bool' to 'double'. | -| test.cpp:42:30:42:31 | b1 | Conversion from 'bool' to 'int'. | -| test.cpp:45:36:45:37 | b1 | Conversion from 'bool' to 'int8_t'. | -| test.cpp:46:38:46:39 | b1 | Conversion from 'bool' to 'int32_t'. | -| test.cpp:49:8:49:9 | b1 | Conversion from 'bool' to 'int32_t'. | -| test.cpp:50:8:50:9 | b1 | Conversion from 'bool' to 'double'. | -| test.cpp:53:13:53:14 | b1 | Conversion from 'bool' to 'int'. | -| test.cpp:59:11:59:12 | b1 | Conversion from 'bool' to 'int8_t'. | -| test.cpp:60:12:60:13 | b1 | Conversion from 'bool' to 'int32_t'. | -| test.cpp:61:10:61:11 | b1 | Conversion from 'bool' to 'double'. | +| test.cpp:23:7:23:8 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:23:12:23:13 | b2 | Conversion from 'bool' to 'int'. | +| test.cpp:25:7:25:8 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:25:12:25:13 | b2 | Conversion from 'bool' to 'int'. | +| test.cpp:27:7:27:8 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:27:12:27:13 | b2 | Conversion from 'bool' to 'int'. | +| test.cpp:29:8:29:9 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:33:7:33:8 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:33:12:33:13 | b2 | Conversion from 'bool' to 'int'. | +| test.cpp:35:7:35:8 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:35:12:35:13 | b2 | Conversion from 'bool' to 'int'. | +| test.cpp:37:7:37:8 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:37:13:37:14 | b2 | Conversion from 'bool' to 'int'. | +| test.cpp:39:7:39:8 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:39:13:39:14 | b2 | Conversion from 'bool' to 'int'. | +| test.cpp:43:7:43:8 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:45:7:45:8 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:47:7:47:8 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:51:20:51:21 | b1 | Conversion from 'bool' to 'double'. | +| test.cpp:52:20:52:21 | b1 | Conversion from 'bool' to 'double'. | +| test.cpp:53:28:53:29 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:56:34:56:35 | b1 | Conversion from 'bool' to 'int8_t'. | +| test.cpp:57:36:57:37 | b1 | Conversion from 'bool' to 'int32_t'. | +| test.cpp:60:6:60:7 | b1 | Conversion from 'bool' to 'int32_t'. | +| test.cpp:61:6:61:7 | b1 | Conversion from 'bool' to 'double'. | +| test.cpp:64:11:64:12 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:72:9:72:10 | b1 | Conversion from 'bool' to 'int8_t'. | +| test.cpp:73:10:73:11 | b1 | Conversion from 'bool' to 'int32_t'. | +| test.cpp:74:8:74:9 | b1 | Conversion from 'bool' to 'double'. | diff --git a/cpp/misra/test/rules/RULE-7-0-1/test.cpp b/cpp/misra/test/rules/RULE-7-0-1/test.cpp index 15a366a28..57243e1c9 100644 --- a/cpp/misra/test/rules/RULE-7-0-1/test.cpp +++ b/cpp/misra/test/rules/RULE-7-0-1/test.cpp @@ -1,97 +1,117 @@ #include struct A { - explicit A(bool) {} + explicit A(bool) {} }; struct BitField { - std::uint8_t bit : 1; + std::uint8_t bit : 1; }; void f1(std::int32_t n) {} void f2(double d) {} void test_bool_conversion_violations() { - bool b1 = true; - bool b2 = false; - double d1 = 1.0; - std::int8_t s8a = 0; - std::int32_t s32a = 0; - BitField bf; - - // Bitwise operations - non-compliant - if (b1 & b2) {} // NON_COMPLIANT - if (b1 | b2) {} // NON_COMPLIANT - if (b1 ^ b2) {} // NON_COMPLIANT - if (~b1) {} // NON_COMPLIANT - - // Relational operations - non-compliant - if (b1 < b2) {} // NON_COMPLIANT - if (b1 > b2) {} // NON_COMPLIANT - if (b1 <= b2) {} // NON_COMPLIANT - if (b1 >= b2) {} // NON_COMPLIANT - - // Comparison with integer literals - non-compliant - if (b1 == 0) {} // NON_COMPLIANT - if (b1 == 1) {} // NON_COMPLIANT - if (b1 != 0) {} // NON_COMPLIANT - - // Arithmetic operations - non-compliant - double l1 = d1 * b1; // NON_COMPLIANT - double l2 = d1 + b1; // NON_COMPLIANT - std::int32_t l3 = s32a + b1; // NON_COMPLIANT - - // Explicit casts to integral types - non-compliant - s8a = static_cast(b1); // NON_COMPLIANT - s32a = static_cast(b1); // NON_COMPLIANT - - // Function parameter conversion - non-compliant - f1(b1); // NON_COMPLIANT - f2(b1); // NON_COMPLIANT - - // Switch statement - non-compliant - switch (b1) { // NON_COMPLIANT - case 0: break; - case 1: break; - } - - // Assignment to integral types - non-compliant - s8a = b1; // NON_COMPLIANT - s32a = b1; // NON_COMPLIANT - d1 = b1; // NON_COMPLIANT + bool b1 = true; + bool b2 = false; + double d1 = 1.0; + std::int8_t s8a = 0; + std::int32_t s32a = 0; + BitField bf; + + // Bitwise operations - non-compliant + if (b1 & b2) { // NON_COMPLIANT + } + if (b1 | b2) { // NON_COMPLIANT + } + if (b1 ^ b2) { // NON_COMPLIANT + } + if (~b1) { // NON_COMPLIANT + } + + // Relational operations - non-compliant + if (b1 < b2) { // NON_COMPLIANT + } + if (b1 > b2) { // NON_COMPLIANT + } + if (b1 <= b2) { // NON_COMPLIANT + } + if (b1 >= b2) { // NON_COMPLIANT + } + + // Comparison with integer literals - non-compliant + if (b1 == 0) { // NON_COMPLIANT + } + if (b1 == 1) { // NON_COMPLIANT + } + if (b1 != 0) { // NON_COMPLIANT + } + + // Arithmetic operations - non-compliant + double l1 = d1 * b1; // NON_COMPLIANT + double l2 = d1 + b1; // NON_COMPLIANT + std::int32_t l3 = s32a + b1; // NON_COMPLIANT + + // Explicit casts to integral types - non-compliant + s8a = static_cast(b1); // NON_COMPLIANT + s32a = static_cast(b1); // NON_COMPLIANT + + // Function parameter conversion - non-compliant + f1(b1); // NON_COMPLIANT + f2(b1); // NON_COMPLIANT + + // Switch statement - non-compliant + switch (b1) { // NON_COMPLIANT + case 0: + break; + case 1: + break; + } + + // Assignment to integral types - non-compliant + s8a = b1; // NON_COMPLIANT + s32a = b1; // NON_COMPLIANT + d1 = b1; // NON_COMPLIANT } void test_bool_conversion_compliant() { - bool b1 = true; - bool b2 = false; - std::int8_t s8a = 0; - BitField bf; - - // Boolean equality operations - compliant - if (b1 == false) {} // COMPLIANT - if (b1 == true) {} // COMPLIANT - if (b1 == b2) {} // COMPLIANT - if (b1 != b2) {} // COMPLIANT - - // Logical operations - compliant - if (b1 && b2) {} // COMPLIANT - if (b1 || b2) {} // COMPLIANT - if (!b1) {} // COMPLIANT - - // Conditional operator without conversion - compliant - s8a = b1 ? 3 : 7; // COMPLIANT - - // Function parameter without conversion - compliant - f1(b1 ? 1 : 0); // COMPLIANT - - // Explicit constructor calls - compliant - A l1{true}; // COMPLIANT - A l2(false); // COMPLIANT - A l3 = static_cast(true); // COMPLIANT - - // Assignment to constructor - compliant - A l4 = A{false}; // COMPLIANT - - // Bit-field assignment exception - compliant - bf.bit = b1; // COMPLIANT + bool b1 = true; + bool b2 = false; + std::int8_t s8a = 0; + BitField bf; + + // Boolean equality operations - compliant + if (b1 == false) { // COMPLIANT + } + if (b1 == true) { // COMPLIANT + } + if (b1 == b2) { // COMPLIANT + } + if (b1 != b2) { // COMPLIANT + } + + // Logical operations - compliant + if (b1 && b2) { // COMPLIANT + } + if (b1 || b2) { // COMPLIANT + } + if (!b1) { // COMPLIANT + } + + // Conditional operator without conversion - compliant + s8a = b1 ? 3 : 7; // COMPLIANT + + // Function parameter without conversion - compliant + f1(b1 ? 1 : 0); // COMPLIANT + + // Explicit constructor calls - compliant + A l1{true}; // COMPLIANT + A l2(false); // COMPLIANT + A l3 = static_cast(true); // COMPLIANT + + // Assignment to constructor - compliant + A l4 = A{false}; // COMPLIANT + + // Bit-field assignment exception - compliant + bf.bit = b1; // COMPLIANT } \ No newline at end of file From 62d5dccade18b5175b2fb8162147a7c92be69256 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Wed, 11 Jun 2025 10:35:07 +0100 Subject: [PATCH 03/57] 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. --- .../RULE-7-0-2/NoImplicitBoolConversion.ql | 92 +++++++++++ .../NoImplicitBoolConversion.expected | 15 ++ .../RULE-7-0-2/NoImplicitBoolConversion.qlref | 1 + cpp/misra/test/rules/RULE-7-0-2/test.cpp | 145 ++++++++++++++++++ 4 files changed, 253 insertions(+) create mode 100644 cpp/misra/src/rules/RULE-7-0-2/NoImplicitBoolConversion.ql create mode 100644 cpp/misra/test/rules/RULE-7-0-2/NoImplicitBoolConversion.expected create mode 100644 cpp/misra/test/rules/RULE-7-0-2/NoImplicitBoolConversion.qlref create mode 100644 cpp/misra/test/rules/RULE-7-0-2/test.cpp diff --git a/cpp/misra/src/rules/RULE-7-0-2/NoImplicitBoolConversion.ql b/cpp/misra/src/rules/RULE-7-0-2/NoImplicitBoolConversion.ql new file mode 100644 index 000000000..3ee3693a3 --- /dev/null +++ b/cpp/misra/src/rules/RULE-7-0-2/NoImplicitBoolConversion.ql @@ -0,0 +1,92 @@ +/** + * @id cpp/misra/no-implicit-bool-conversion + * @name RULE-7-0-2: There shall be no conversion to type bool + * @description Implicit and contextual conversions to bool from fundamental types, unscoped enums, + * or pointers may lead to unintended behavior, except for specific cases like pointer + * checks and explicit operator bool conversions. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-7-0-2 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra + +predicate isInContextualBoolContext(Expr expr) { + exists(IfStmt ifStmt | ifStmt.getCondition() = expr) or + exists(WhileStmt whileStmt | whileStmt.getCondition() = expr) or + exists(ForStmt forStmt | forStmt.getCondition() = expr) or + exists(DoStmt doStmt | doStmt.getCondition() = expr) or + exists(ConditionalExpr condExpr | condExpr.getCondition() = expr) or + exists(LogicalAndExpr logicalAnd | logicalAnd.getAnOperand() = expr) or + exists(LogicalOrExpr logicalOr | logicalOr.getAnOperand() = expr) or + exists(NotExpr notExpr | notExpr.getOperand() = expr) +} + +predicate isInWhileConditionDeclaration(Expr expr) { + exists(WhileStmt whileStmt, ConditionDeclExpr condDecl | + whileStmt.getCondition() = condDecl and + condDecl.getExpr() = expr + ) +} + +predicate isBitFieldOfSizeOne(Expr expr) { + exists(BitField bf | + expr = bf.getAnAccess() and + bf.getNumBits() = 1 + ) +} + +from Element e, string reason +where + not isExcluded(e, ConversionsPackage::noImplicitBoolConversionQuery()) and + ( + // Conversions to bool + exists(Conversion conv | + e = conv and + conv.getType().getUnspecifiedType() instanceof BoolType and + not conv.getExpr().getType().getUnspecifiedType() instanceof BoolType and + // Exception 2: Contextual conversion from pointer + not ( + conv.getExpr().getType().getUnspecifiedType() instanceof PointerType and + isInContextualBoolContext(conv.getExpr()) + ) and + // Exception 3: Bit-field of size 1 + not isBitFieldOfSizeOne(conv.getExpr()) and + // Exception 4: While condition declaration + not isInWhileConditionDeclaration(conv.getExpr()) and + reason = "Conversion from '" + conv.getExpr().getType().toString() + "' to 'bool'" + ) + or + // Calls to conversion operators to bool + // + // Note: we flag these separately because: + // 1. If the conversion via the operator is implicit, there is no `Conversion` - only a call to + // the `ConversionOperator`. + // 2. If the conversion is explicit, the `Conversion` is from `bool` to `bool`, which is not + // flagged in the previous `Conversion` case above. + exists(Call conversionCall, ConversionOperator op | + e = conversionCall and + conversionCall.getTarget() = op and + op.getType().getUnspecifiedType() instanceof BoolType and + // Exception 1: Static cast to bool from class with explicit operator bool + not exists(StaticCast conv | + op.isExplicit() and + conv.getExpr() = conversionCall and + conv.getType().getUnspecifiedType() instanceof BoolType + ) and + // Exception 2: Contextual conversion from class with explicit operator bool is allowed + not ( + op.isExplicit() and + isInContextualBoolContext(conversionCall) + ) and + reason = + "Conversion operator call from '" + conversionCall.getQualifier().getType().toString() + + "' to 'bool'" + ) + ) +select e, reason + "." diff --git a/cpp/misra/test/rules/RULE-7-0-2/NoImplicitBoolConversion.expected b/cpp/misra/test/rules/RULE-7-0-2/NoImplicitBoolConversion.expected new file mode 100644 index 000000000..e3ce3929c --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-2/NoImplicitBoolConversion.expected @@ -0,0 +1,15 @@ +| test.cpp:46:20:46:28 | (bool)... | Conversion from 'int' to 'bool'. | +| test.cpp:50:7:50:7 | (bool)... | Conversion from 'int' to 'bool'. | +| test.cpp:52:7:52:8 | (bool)... | Conversion from 'uint8_t' to 'bool'. | +| test.cpp:54:8:54:8 | (bool)... | Conversion from 'int' to 'bool'. | +| test.cpp:58:7:58:8 | (bool)... | Conversion from 'uint8_t' to 'bool'. | +| test.cpp:69:8:69:9 | (bool)... | Conversion from 'int16_t' to 'bool'. | +| test.cpp:78:20:78:21 | (bool)... | Conversion from 'int32_t' to 'bool'. | +| test.cpp:84:31:84:32 | (bool)... | Conversion from 'int32_t' to 'bool'. | +| test.cpp:92:30:92:31 | (bool)... | Conversion from 'int32_t' to 'bool'. | +| test.cpp:103:13:103:16 | (bool)... | Conversion from 'int32_t *' to 'bool'. | +| test.cpp:108:13:108:32 | static_cast... | Conversion from 'int' to 'bool'. | +| test.cpp:109:13:109:14 | (bool)... | Conversion from 'uint8_t' to 'bool'. | +| test.cpp:121:13:121:13 | call to operator bool | Conversion operator call from 'TestClassImplicit' to 'bool'. | +| test.cpp:134:13:134:14 | (bool)... | Conversion from 'Color' to 'bool'. | +| test.cpp:135:7:135:8 | (bool)... | Conversion from 'Color' to 'bool'. | diff --git a/cpp/misra/test/rules/RULE-7-0-2/NoImplicitBoolConversion.qlref b/cpp/misra/test/rules/RULE-7-0-2/NoImplicitBoolConversion.qlref new file mode 100644 index 000000000..a7b86d71f --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-2/NoImplicitBoolConversion.qlref @@ -0,0 +1 @@ +rules/RULE-7-0-2/NoImplicitBoolConversion.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-0-2/test.cpp b/cpp/misra/test/rules/RULE-7-0-2/test.cpp new file mode 100644 index 000000000..baeb772d2 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-2/test.cpp @@ -0,0 +1,145 @@ +#include +#include + +// Global variables for testing +std::uint8_t g1 = 5; +std::uint8_t g2 = 10; +std::uint8_t g3 = 15; +std::uint8_t g4 = 20; +std::int16_t g5 = 100; +std::int32_t g6 = 200; +bool g7 = true; + +// Function declarations +std::int32_t f1(); +bool f2(); +std::int32_t *f3(); + +// Class with explicit operator bool +class TestClassExplicit { + std::int32_t m1; + +public: + explicit operator bool() const { return m1 < 0; } +}; + +class TestClassImplicit { + std::int32_t m1; + +public: + operator bool() const { return m1 < 0; } // Implicit conversion +}; + +// Bit-field struct for exception #3 +struct BitFieldStruct { + unsigned int m1 : 1; +}; + +void test_logical_operators() { + std::uint8_t l1 = 5; + std::uint8_t l2 = 10; + std::uint8_t l3 = 15; + std::uint8_t l4 = 20; + + if ((l1 < l2) && (l3 < l4)) { // COMPLIANT + } + if ((l1 < l2) && (l3 + l4)) { // NON_COMPLIANT + } + if (true && (l3 < l4)) { // COMPLIANT + } + if (1 && (l3 < l4)) { // NON_COMPLIANT + } + if (l1 && (l3 < l4)) { // NON_COMPLIANT + } + if (!0) { // NON_COMPLIANT + } + if (!false) { // COMPLIANT + } + if (l1) { // NON_COMPLIANT + } +} + +void test_conditional_operator() { + std::int32_t l1 = 100; + std::int32_t l2 = 200; + std::int32_t l3 = 300; + std::int16_t l4 = 50; + bool l5 = true; + + l1 = l4 ? l2 : l3; // NON_COMPLIANT + l1 = l5 ? l2 : l3; // COMPLIANT + l1 = (l4 < 5) ? l2 : l3; // COMPLIANT +} + +void test_if_statements() { + std::int32_t l1; + bool l2 = f2(); + + if (std::int32_t l3 = f1()) { // NON_COMPLIANT + } + if (std::int32_t l4 = f1(); l4 != 0) { // COMPLIANT + } + if (bool l5 = f2()) { // COMPLIANT + } + if (std::int32_t l6 = f1(); l6) { // NON_COMPLIANT + } +} + +void test_while_loops() { + while (std::int32_t l1 = f1()) { // COMPLIANT - exception #4 + } + + for (std::int32_t l2 = 10; l2; --l2) { // NON_COMPLIANT + } + + while (std::cin) { // COMPLIANT - exception #2 + } +} + +void test_pointer_conversions() { + if (f3()) { // COMPLIANT - exception #2 + } + + bool l1 = f3(); // NON_COMPLIANT + bool l2 = f3() != nullptr; // COMPLIANT +} + +void test_assignment_to_bool() { + bool l1 = static_cast(4); // NON_COMPLIANT + bool l2 = g1; // NON_COMPLIANT + bool l3 = (g1 < g2); // COMPLIANT + bool l4 = g7; // COMPLIANT +} + +void test_class_with_explicit_bool_operator() { + TestClassExplicit l1; + + bool l2 = static_cast(l1); // COMPLIANT - exception #1 + if (l1) { // COMPLIANT - exception #2 + } + TestClassImplicit l3; + bool l4 = l3; // NON_COMPLIANT +} + +void test_bitfield_conversion() { + BitFieldStruct l1; + + bool l2 = l1.m1; // COMPLIANT - exception #3 +} + +void test_unscoped_enum_conversion() { + enum Color { RED, GREEN, BLUE }; + Color l1 = RED; + + bool l2 = l1; // NON_COMPLIANT + if (l1) { // NON_COMPLIANT + } + bool l3 = (l1 == RED); // COMPLIANT +} + +void test_scoped_enum_conversion() { + enum class Status { ACTIVE, INACTIVE }; + Status l1 = Status::ACTIVE; + + bool l2 = (l1 == Status::ACTIVE); // COMPLIANT +} \ No newline at end of file From f92a2c97af609b7d3bf7cf03bfd57f235fcdc2d6 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Wed, 11 Jun 2025 15:04:51 +0100 Subject: [PATCH 04/57] Rule 7.0.2: Extend test case, support member function pointers --- .../RULE-7-0-2/NoImplicitBoolConversion.ql | 8 ++- .../NoImplicitBoolConversion.expected | 43 +++++++---- cpp/misra/test/rules/RULE-7-0-2/test.cpp | 71 ++++++++++++++++++- 3 files changed, 105 insertions(+), 17 deletions(-) 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 3ee3693a3..308f879f1 100644 --- a/cpp/misra/src/rules/RULE-7-0-2/NoImplicitBoolConversion.ql +++ b/cpp/misra/src/rules/RULE-7-0-2/NoImplicitBoolConversion.ql @@ -41,6 +41,12 @@ predicate isBitFieldOfSizeOne(Expr expr) { ) } +predicate isPointerType(Type t) { + t.getUnspecifiedType() instanceof PointerType or + t.getUnspecifiedType() instanceof ArrayType or + t.getUnspecifiedType() instanceof PointerToMemberType +} + from Element e, string reason where not isExcluded(e, ConversionsPackage::noImplicitBoolConversionQuery()) and @@ -52,7 +58,7 @@ where not conv.getExpr().getType().getUnspecifiedType() instanceof BoolType and // Exception 2: Contextual conversion from pointer not ( - conv.getExpr().getType().getUnspecifiedType() instanceof PointerType and + isPointerType(conv.getExpr().getType()) and isInContextualBoolContext(conv.getExpr()) ) and // Exception 3: Bit-field of size 1 diff --git a/cpp/misra/test/rules/RULE-7-0-2/NoImplicitBoolConversion.expected b/cpp/misra/test/rules/RULE-7-0-2/NoImplicitBoolConversion.expected index e3ce3929c..c000a23dd 100644 --- a/cpp/misra/test/rules/RULE-7-0-2/NoImplicitBoolConversion.expected +++ b/cpp/misra/test/rules/RULE-7-0-2/NoImplicitBoolConversion.expected @@ -1,15 +1,28 @@ -| test.cpp:46:20:46:28 | (bool)... | Conversion from 'int' to 'bool'. | -| test.cpp:50:7:50:7 | (bool)... | Conversion from 'int' to 'bool'. | -| test.cpp:52:7:52:8 | (bool)... | Conversion from 'uint8_t' to 'bool'. | -| test.cpp:54:8:54:8 | (bool)... | Conversion from 'int' to 'bool'. | -| test.cpp:58:7:58:8 | (bool)... | Conversion from 'uint8_t' to 'bool'. | -| test.cpp:69:8:69:9 | (bool)... | Conversion from 'int16_t' to 'bool'. | -| test.cpp:78:20:78:21 | (bool)... | Conversion from 'int32_t' to 'bool'. | -| test.cpp:84:31:84:32 | (bool)... | Conversion from 'int32_t' to 'bool'. | -| test.cpp:92:30:92:31 | (bool)... | Conversion from 'int32_t' to 'bool'. | -| test.cpp:103:13:103:16 | (bool)... | Conversion from 'int32_t *' to 'bool'. | -| test.cpp:108:13:108:32 | static_cast... | Conversion from 'int' to 'bool'. | -| test.cpp:109:13:109:14 | (bool)... | Conversion from 'uint8_t' to 'bool'. | -| test.cpp:121:13:121:13 | call to operator bool | Conversion operator call from 'TestClassImplicit' to 'bool'. | -| test.cpp:134:13:134:14 | (bool)... | Conversion from 'Color' to 'bool'. | -| test.cpp:135:7:135:8 | (bool)... | Conversion from 'Color' to 'bool'. | +| test.cpp:53:20:53:28 | (bool)... | Conversion from 'int' to 'bool'. | +| test.cpp:57:7:57:7 | (bool)... | Conversion from 'int' to 'bool'. | +| test.cpp:59:7:59:8 | (bool)... | Conversion from 'uint8_t' to 'bool'. | +| test.cpp:61:8:61:8 | (bool)... | Conversion from 'int' to 'bool'. | +| test.cpp:65:7:65:8 | (bool)... | Conversion from 'uint8_t' to 'bool'. | +| test.cpp:76:8:76:9 | (bool)... | Conversion from 'int16_t' to 'bool'. | +| test.cpp:85:20:85:21 | (bool)... | Conversion from 'int32_t' to 'bool'. | +| test.cpp:91:31:91:32 | (bool)... | Conversion from 'int32_t' to 'bool'. | +| test.cpp:99:30:99:31 | (bool)... | Conversion from 'int32_t' to 'bool'. | +| test.cpp:112:12:112:13 | (bool)... | Conversion from 'int32_t' to 'bool'. | +| test.cpp:127:13:127:16 | (bool)... | Conversion from 'int32_t *' to 'bool'. | +| test.cpp:130:7:130:13 | (bool)... | Conversion from 'decltype(nullptr)' to 'bool'. | +| test.cpp:135:13:135:32 | static_cast... | Conversion from 'int' to 'bool'. | +| test.cpp:136:13:136:14 | (bool)... | Conversion from 'uint8_t' to 'bool'. | +| test.cpp:148:13:148:13 | call to operator bool | Conversion operator call from 'TestClassImplicit' to 'bool'. | +| test.cpp:161:13:161:14 | (bool)... | Conversion from 'Color' to 'bool'. | +| test.cpp:162:7:162:8 | (bool)... | Conversion from 'Color' to 'bool'. | +| test.cpp:179:13:179:14 | (bool)... | Conversion from 'float' to 'bool'. | +| test.cpp:180:13:180:14 | (bool)... | Conversion from 'double' to 'bool'. | +| test.cpp:181:13:181:14 | (bool)... | Conversion from 'long double' to 'bool'. | +| test.cpp:182:7:182:8 | (bool)... | Conversion from 'float' to 'bool'. | +| test.cpp:184:7:184:8 | (bool)... | Conversion from 'double' to 'bool'. | +| test.cpp:186:7:186:8 | (bool)... | Conversion from 'long double' to 'bool'. | +| test.cpp:195:7:195:8 | (bool)... | Conversion from 'int32_t *' to 'bool'. | +| test.cpp:197:7:197:8 | (bool)... | Conversion from 'int32_t *' to 'bool'. | +| test.cpp:199:13:199:14 | (bool)... | Conversion from 'int32_t *' to 'bool'. | +| test.cpp:211:13:211:14 | (bool)... | Conversion from '..:: *' to 'bool'. | +| test.cpp:212:13:212:14 | (bool)... | Conversion from '..:: *' to 'bool'. | 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 baeb772d2..568980dea 100644 --- a/cpp/misra/test/rules/RULE-7-0-2/test.cpp +++ b/cpp/misra/test/rules/RULE-7-0-2/test.cpp @@ -9,6 +9,7 @@ std::uint8_t g4 = 20; std::int16_t g5 = 100; std::int32_t g6 = 200; bool g7 = true; +std::int32_t g8[5] = {1, 2, 3, 4, 5}; // Function declarations std::int32_t f1(); @@ -30,6 +31,12 @@ class TestClassImplicit { operator bool() const { return m1 < 0; } // Implicit conversion }; +// Class with member function pointer +class TestClassMemberFunc { +public: + void memberFunc() {} +}; + // Bit-field struct for exception #3 struct BitFieldStruct { unsigned int m1 : 1; @@ -96,12 +103,32 @@ void test_while_loops() { } } +void test_do_while_loops() { + std::int32_t l1 = 5; + bool l2 = true; + + do { + --l1; + } while (l1); // NON_COMPLIANT + + do { + --l1; + } while (l2); // COMPLIANT + + do { + --l1; + } while (l1 > 0); // COMPLIANT +} + void test_pointer_conversions() { if (f3()) { // COMPLIANT - exception #2 } bool l1 = f3(); // NON_COMPLIANT bool l2 = f3() != nullptr; // COMPLIANT + + if (nullptr) { // NON_COMPLIANT + } } void test_assignment_to_bool() { @@ -111,7 +138,7 @@ void test_assignment_to_bool() { bool l4 = g7; // COMPLIANT } -void test_class_with_explicit_bool_operator() { +void test_classes_with_bool_operators() { TestClassExplicit l1; bool l2 = static_cast(l1); // COMPLIANT - exception #1 @@ -142,4 +169,46 @@ void test_scoped_enum_conversion() { Status l1 = Status::ACTIVE; bool l2 = (l1 == Status::ACTIVE); // COMPLIANT +} + +void test_floating_point_conversion() { + float l1 = 3.14f; + double l2 = 2.71; + long double l3 = 1.41L; + + bool l4 = l1; // NON_COMPLIANT + bool l5 = l2; // NON_COMPLIANT + bool l6 = l3; // NON_COMPLIANT + if (l1) { // NON_COMPLIANT + } + if (l2) { // NON_COMPLIANT + } + if (l3) { // NON_COMPLIANT + } + bool l7 = (l1 > 0.0f); // COMPLIANT + bool l8 = (l2 != 0.0); // COMPLIANT +} + +void test_array_conversion() { + std::int32_t l1[5] = {1, 2, 3, 4, 5}; + + if (l1) { // NON_COMPLIANT + } + if (g8) { // NON_COMPLIANT + } + bool l2 = l1; // NON_COMPLIANT + bool l3 = (l1 != nullptr); // COMPLIANT +} + +void test_member_function_pointer_conversion() { + void (TestClassMemberFunc::*l1)() = &TestClassMemberFunc::memberFunc; + void (TestClassMemberFunc::*l2)() = nullptr; + + if (l1) { // COMPLIANT - exception #2 + } + if (l2) { // COMPLIANT - exception #2 + } + bool l3 = l1; // NON_COMPLIANT + bool l4 = l2; // NON_COMPLIANT + bool l5 = (l1 != nullptr); // COMPLIANT } \ No newline at end of file From b62394a0ab9e7fd991df71072b00df777ea08f9b Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Thu, 12 Jun 2025 16:18:37 +0100 Subject: [PATCH 05/57] Add Conversions exclusions file --- .../cpp/exclusions/cpp/Conversions.qll | 214 ++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 cpp/common/src/codingstandards/cpp/exclusions/cpp/Conversions.qll diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Conversions.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Conversions.qll new file mode 100644 index 000000000..f151565d1 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Conversions.qll @@ -0,0 +1,214 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype ConversionsQuery = + TNoConversionFromBoolQuery() or + TNoImplicitBoolConversionQuery() or + TNoCharacterNumericalValueQuery() or + TNoSignednessChangeFromPromotionQuery() or + TNumericAssignmentTypeMismatchQuery() or + TFunctionPointerConversionContextQuery() or + TVirtualBaseClassCastToDerivedQuery() or + TNoCStyleOrFunctionalCastsQuery() or + TIntToPointerCastProhibitedQuery() or + TNoPointerToIntegralCastQuery() or + TPointerToIntegralCastQuery() or + TNoStandaloneTypeCastExpressionQuery() + +predicate isConversionsQueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `noConversionFromBool` query + ConversionsPackage::noConversionFromBoolQuery() and + queryId = + // `@id` for the `noConversionFromBool` query + "cpp/misra/no-conversion-from-bool" and + ruleId = "RULE-7-0-1" and + category = "required" + or + query = + // `Query` instance for the `noImplicitBoolConversion` query + ConversionsPackage::noImplicitBoolConversionQuery() and + queryId = + // `@id` for the `noImplicitBoolConversion` query + "cpp/misra/no-implicit-bool-conversion" and + ruleId = "RULE-7-0-2" and + category = "required" + or + query = + // `Query` instance for the `noCharacterNumericalValue` query + ConversionsPackage::noCharacterNumericalValueQuery() and + queryId = + // `@id` for the `noCharacterNumericalValue` query + "cpp/misra/no-character-numerical-value" and + ruleId = "RULE-7-0-3" and + category = "required" + or + query = + // `Query` instance for the `noSignednessChangeFromPromotion` query + ConversionsPackage::noSignednessChangeFromPromotionQuery() and + queryId = + // `@id` for the `noSignednessChangeFromPromotion` query + "cpp/misra/no-signedness-change-from-promotion" and + ruleId = "RULE-7-0-5" and + category = "required" + or + query = + // `Query` instance for the `numericAssignmentTypeMismatch` query + ConversionsPackage::numericAssignmentTypeMismatchQuery() and + queryId = + // `@id` for the `numericAssignmentTypeMismatch` query + "cpp/misra/numeric-assignment-type-mismatch" and + ruleId = "RULE-7-0-6" and + category = "required" + or + query = + // `Query` instance for the `functionPointerConversionContext` query + ConversionsPackage::functionPointerConversionContextQuery() and + queryId = + // `@id` for the `functionPointerConversionContext` query + "cpp/misra/function-pointer-conversion-context" and + ruleId = "RULE-7-11-3" and + category = "required" + or + query = + // `Query` instance for the `virtualBaseClassCastToDerived` query + ConversionsPackage::virtualBaseClassCastToDerivedQuery() and + queryId = + // `@id` for the `virtualBaseClassCastToDerived` query + "cpp/misra/virtual-base-class-cast-to-derived" and + ruleId = "RULE-8-2-1" and + category = "required" + or + query = + // `Query` instance for the `noCStyleOrFunctionalCasts` query + ConversionsPackage::noCStyleOrFunctionalCastsQuery() and + queryId = + // `@id` for the `noCStyleOrFunctionalCasts` query + "cpp/misra/no-c-style-or-functional-casts" and + ruleId = "RULE-8-2-2" and + category = "required" + or + query = + // `Query` instance for the `intToPointerCastProhibited` query + ConversionsPackage::intToPointerCastProhibitedQuery() and + queryId = + // `@id` for the `intToPointerCastProhibited` query + "cpp/misra/int-to-pointer-cast-prohibited" and + ruleId = "RULE-8-2-6" and + category = "required" + or + query = + // `Query` instance for the `noPointerToIntegralCast` query + ConversionsPackage::noPointerToIntegralCastQuery() and + queryId = + // `@id` for the `noPointerToIntegralCast` query + "cpp/misra/no-pointer-to-integral-cast" and + ruleId = "RULE-8-2-7" and + category = "advisory" + or + query = + // `Query` instance for the `pointerToIntegralCast` query + ConversionsPackage::pointerToIntegralCastQuery() and + queryId = + // `@id` for the `pointerToIntegralCast` query + "cpp/misra/pointer-to-integral-cast" and + ruleId = "RULE-8-2-8" and + category = "required" + or + query = + // `Query` instance for the `noStandaloneTypeCastExpression` query + ConversionsPackage::noStandaloneTypeCastExpressionQuery() and + queryId = + // `@id` for the `noStandaloneTypeCastExpression` query + "cpp/misra/no-standalone-type-cast-expression" and + ruleId = "RULE-9-2-1" and + category = "required" +} + +module ConversionsPackage { + Query noConversionFromBoolQuery() { + //autogenerate `Query` type + result = + // `Query` type for `noConversionFromBool` query + TQueryCPP(TConversionsPackageQuery(TNoConversionFromBoolQuery())) + } + + Query noImplicitBoolConversionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `noImplicitBoolConversion` query + TQueryCPP(TConversionsPackageQuery(TNoImplicitBoolConversionQuery())) + } + + Query noCharacterNumericalValueQuery() { + //autogenerate `Query` type + result = + // `Query` type for `noCharacterNumericalValue` query + TQueryCPP(TConversionsPackageQuery(TNoCharacterNumericalValueQuery())) + } + + Query noSignednessChangeFromPromotionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `noSignednessChangeFromPromotion` query + TQueryCPP(TConversionsPackageQuery(TNoSignednessChangeFromPromotionQuery())) + } + + Query numericAssignmentTypeMismatchQuery() { + //autogenerate `Query` type + result = + // `Query` type for `numericAssignmentTypeMismatch` query + TQueryCPP(TConversionsPackageQuery(TNumericAssignmentTypeMismatchQuery())) + } + + Query functionPointerConversionContextQuery() { + //autogenerate `Query` type + result = + // `Query` type for `functionPointerConversionContext` query + TQueryCPP(TConversionsPackageQuery(TFunctionPointerConversionContextQuery())) + } + + Query virtualBaseClassCastToDerivedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `virtualBaseClassCastToDerived` query + TQueryCPP(TConversionsPackageQuery(TVirtualBaseClassCastToDerivedQuery())) + } + + Query noCStyleOrFunctionalCastsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `noCStyleOrFunctionalCasts` query + TQueryCPP(TConversionsPackageQuery(TNoCStyleOrFunctionalCastsQuery())) + } + + Query intToPointerCastProhibitedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `intToPointerCastProhibited` query + TQueryCPP(TConversionsPackageQuery(TIntToPointerCastProhibitedQuery())) + } + + Query noPointerToIntegralCastQuery() { + //autogenerate `Query` type + result = + // `Query` type for `noPointerToIntegralCast` query + TQueryCPP(TConversionsPackageQuery(TNoPointerToIntegralCastQuery())) + } + + Query pointerToIntegralCastQuery() { + //autogenerate `Query` type + result = + // `Query` type for `pointerToIntegralCast` query + TQueryCPP(TConversionsPackageQuery(TPointerToIntegralCastQuery())) + } + + Query noStandaloneTypeCastExpressionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `noStandaloneTypeCastExpression` query + TQueryCPP(TConversionsPackageQuery(TNoStandaloneTypeCastExpressionQuery())) + } +} From a2c991214a954e78ee79c309bf3e4de1066101e5 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Tue, 17 Jun 2025 11:20:10 +0100 Subject: [PATCH 06/57] RULE-7-0-6 - NumericAssignmentTypeMismatch Detects inappropriate assignments between numeric types that violate type compatibility requirements, including implicit conversions that may cause information loss or unexpected behavior changes. [a] --- .../NumericAssignmentTypeMismatch.ql | 300 ++++++++++++++++++ .../NumericAssignmentTypeMismatch.expected | 23 ++ .../NumericAssignmentTypeMismatch.qlref | 1 + cpp/misra/test/rules/RULE-7-0-6/test.cpp | 217 +++++++++++++ 4 files changed, 541 insertions(+) create mode 100644 cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql create mode 100644 cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected create mode 100644 cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.qlref create mode 100644 cpp/misra/test/rules/RULE-7-0-6/test.cpp diff --git a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql new file mode 100644 index 000000000..eff5361d7 --- /dev/null +++ b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql @@ -0,0 +1,300 @@ +/** + * @id cpp/misra/numeric-assignment-type-mismatch + * @name RULE-7-0-6: Assignment between numeric types shall be appropriate + * @description Assignment between numeric types with different sizes, signedness, or type + * categories can lead to unexpected information loss, undefined behavior, or silent + * overload resolution changes. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-7-0-6 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +/** + * The signedness of a numeric type. + */ +newtype Signedness = + Signed() or + Unsigned() + +/** + * The type category of a numeric type - either integral or floating-point. + */ +newtype TypeCategory = + Integral() or + FloatingPoint() + +/** + * A numeric type is a type that represents a number, either an integral or a floating-point. + * + * In addition to the basic integral and floating-point types, it includes: + * - Enum types with an explicit underlying type that is a numeric type. + * - Typedef'd types that are numeric types. + * - Numeric types with specifiers (e.g., `const`, `volatile`, `restrict`). + */ +class NumericType extends Type { + Type realType; + + NumericType() { + realType = this.getUnspecifiedType().(IntegralType) or + realType = this.getUnspecifiedType().(FloatingPointType) or + realType = this.getUnspecifiedType().(Enum).getExplicitUnderlyingType().getUnspecifiedType() + } + + Signedness getSignedness() { + if realType.(IntegralType).isUnsigned() then result = Unsigned() else result = Signed() + } + + TypeCategory getTypeCategory() { + realType instanceof IntegralType and result = Integral() + or + realType instanceof FloatingPointType and result = FloatingPoint() + } + + float getUpperBound() { result = typeUpperBound(realType) } + + float getLowerBound() { result = typeLowerBound(realType) } + + Type getRealType() { result = realType } +} + +predicate isAssignment(Expr source, NumericType targetType, string context) { + // Assignment operator + exists(Assignment assign | + assign.getRValue() = source and + context = "assignment" + | + // TODO generalize to variable init (do we need this for bitfields?) and extract + if isAssignedToBitfield(source, _) + then + exists(BitField bf | + isAssignedToBitfield(source, bf) and + targetType.(IntegralType).(NumericType).getSignedness() = + bf.getType().(NumericType).getSignedness() and + // smallest integral type that can hold the bit field value + targetType.getSize() * 8 >= bf.getNumBits() and + not exists(IntegralType other | + other.getSize() * 8 >= bf.getNumBits() and + other.(NumericType).getSignedness() = targetType.getSignedness() and + other.getSize() < targetType.getSize() + ) + ) + else targetType = assign.getLValue().getType() + ) + or + // Variable initialization + exists(Variable v, Initializer init | + init.getExpr() = source and + v.getInitializer() = init and + targetType = v.getType() and + context = "initialization" + ) + or + exists(Call call, int i | + call.getArgument(i) = source and + not targetType.stripTopLevelSpecifiers() instanceof ReferenceType and + context = "function argument" + | + targetType = call.getTarget().getParameter(i).getType() + or + // Handle varargs - use the fully converted type of the argument + call.getTarget().getNumberOfParameters() <= i and + targetType = source.getFullyConverted().getType() + ) + or + // Return statement + exists(ReturnStmt ret, Function f | + ret.getExpr() = source and + ret.getEnclosingFunction() = f and + targetType = f.getType() and + not targetType.stripTopLevelSpecifiers() instanceof ReferenceType and + context = "return" + ) + or + // Switch case + exists(SwitchCase case, SwitchStmt switch | + case.getExpr() = source and + case.getSwitchStmt() = switch and + targetType = switch.getExpr().getFullyConverted().getType() and + context = "switch case" + ) +} + +predicate isAssignedToBitfield(Expr source, BitField bf) { + exists(Assignment assign | + assign.getRValue() = source and + assign.getLValue() = bf.getAnAccess() + ) +} + +predicate isValidConstantAssignment(Expr source, NumericType targetType) { + isAssignment(source, targetType, _) and + // Source is an integer constant expression + source.isConstant() and + source.getType().(NumericType).getTypeCategory() = Integral() and + exists(float val | val = source.getValue().toFloat() | + // Bit field assignment: check if the value fits in the bit field + exists(BitField bf, int numBits | + isAssignedToBitfield(source, bf) and + numBits = bf.getNumBits() and + val >= 0 and + val < 2.pow(numBits) + ) + or + // Regular assignment: check if the value fits in the target type range + not isAssignedToBitfield(source, _) and + targetType.getLowerBound() <= val and + val <= targetType.getUpperBound() + ) +} + +predicate isValidTypeMatch(NumericType sourceType, NumericType targetType) { + // Same type category, signedness and size + sourceType.getTypeCategory() = targetType.getTypeCategory() and + sourceType.getSignedness() = targetType.getSignedness() and + sourceType.getSize() = targetType.getSize() +} + +predicate hasConstructorException(FunctionCall call) { + exists(Constructor ctor, Class c | + call.getTarget() = ctor and + c = ctor.getDeclaringType() and + // Constructor callable with single numeric argument + ctor.getNumberOfParameters() = 1 and + ctor.getParameter(0).getType() instanceof NumericType and + // No other single-argument constructors except copy/move + not exists(Constructor other | + other.getDeclaringType() = c and + other != ctor and + other.getNumberOfParameters() = 1 and + not other instanceof CopyConstructor and + not other instanceof MoveConstructor + ) + ) +} + +/** + * An id-expression that has a numeric type. + * + * This is restricted to variable accesses, that are not explicitly qualified in any way. + */ +class IdExpression extends VariableAccess { + IdExpression() { + // Not a member variable + ( + not exists(this.getQualifier()) + or + // Member variable, but the qualifier is not explicit + this.getQualifier().isCompilerGenerated() + ) and + // No name qualifiers + not exists(NameQualifier qual | qual.getExpr() = this) + } +} + +predicate isValidWidening(Expr source, NumericType sourceType, NumericType targetType) { + isAssignment(source, targetType, _) and + source.getType() = sourceType and + // Same type category and signedness, source size smaller, source is id-expression or has constructor exception + ( + source instanceof IdExpression or + hasConstructorException(any(Call call | call.getAnArgument() = source)) + ) and + sourceType.getTypeCategory() = targetType.getTypeCategory() and + sourceType.getSignedness() = targetType.getSignedness() and + sourceType.getSize() < targetType.getSize() +} + +/** + * A non-extensible call is a call that cannot be extended by adding new overloads. + */ +predicate isNonExtensible(Call c) { + exists(NameQualifier qual | qual.getExpr() = c and c.getTarget() instanceof MemberFunction) + or + exists(c.getQualifier()) and not c.getQualifier().isCompilerGenerated() + or + c.getTarget() instanceof Operator +} + +predicate isOverloadIndependent(Call call, Expr arg) { + arg = call.getAnArgument() and + ( + // Call through function pointer + call instanceof ExprCall + or + isNonExtensible(call) and + forall(Function target, Function overload, int i | + target = call.getTarget() and + ( + overload = target.getAnOverload() + or + // Instantiated function templates don't directly participate in overload resolution + // so check the templates overloads + overload = target.(FunctionTemplateInstantiation).getTemplate().getAnOverload() + ) and + overload.getNumberOfParameters() = call.getNumberOfArguments() and + call.getArgument(i) = arg + | + // Check that the parameter types match + overload.getParameter(i).getType().getUnspecifiedType() = + target.getParameter(i).getType().getUnspecifiedType() + ) + ) +} + +/** + * Check if the source expression should have the same type as the target type. + */ +predicate shouldHaveSameType(Expr source) { + exists(Call call | + call.getAnArgument() = source and + isAssignment(source, _, _) and + not hasConstructorException(call) + | + not isOverloadIndependent(call, source) + or + // Passed as a varargs parameter + exists(int i | + call.getTarget().isVarargs() and + call.getArgument(i) = source and + // Argument is greater than the number of parameters + call.getTarget().getNumberOfParameters() <= i + ) + ) +} + +predicate isValidAssignment(Expr source, NumericType targetType, string context) { + isAssignment(source, targetType, context) and + exists(NumericType sourceType | sourceType = source.getType() | + if shouldHaveSameType(source) + then sourceType.getRealType() = targetType.getRealType() + else ( + // Valid type match + isValidTypeMatch(sourceType, targetType) + or + // Valid widening assignment + isValidWidening(source, sourceType, targetType) + or + // Valid constant assignment (integer constants) + isValidConstantAssignment(source, targetType) + ) + ) +} + +from Expr source, NumericType sourceType, NumericType targetType, string context +where + not isExcluded(source, ConversionsPackage::numericAssignmentTypeMismatchQuery()) and + isAssignment(source, targetType, context) and + // The assignment must be between numeric types + sourceType = source.getType() and + not isValidAssignment(source, targetType, context) +select source, + "Assignment between incompatible numeric types from '" + sourceType.getName() + "' to '" + + targetType.getName() + "'." diff --git a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected new file mode 100644 index 000000000..fa0bdb487 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected @@ -0,0 +1,23 @@ +| test.cpp:21:8:21:11 | 300 | Assignment between incompatible numeric types from 'unsigned int' to 'uint8_t'. | +| test.cpp:24:9:24:12 | 0.0 | Assignment between incompatible numeric types from 'float' to 'double'. | +| test.cpp:30:8:30:9 | g4 | Assignment between incompatible numeric types from 'int8_t' to 'uint8_t'. | +| test.cpp:31:8:31:9 | g3 | Assignment between incompatible numeric types from 'uint8_t' to 'int8_t'. | +| test.cpp:36:8:36:9 | g5 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test.cpp:37:8:37:9 | g7 | Assignment between incompatible numeric types from 'uint64_t' to 'uint16_t'. | +| test.cpp:42:8:42:9 | g2 | Assignment between incompatible numeric types from 'int32_t' to 'float'. | +| test.cpp:43:8:43:9 | g9 | Assignment between incompatible numeric types from 'float' to 'int32_t'. | +| test.cpp:55:8:55:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test.cpp:56:21:56:27 | ... + ... | Assignment between incompatible numeric types from 'int' to 'int16_t'. | +| test.cpp:67:11:67:13 | 32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned char'. | +| test.cpp:69:11:69:12 | g5 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test.cpp:81:8:81:9 | l1 | Assignment between incompatible numeric types from 'Colour' to 'uint8_t'. | +| test.cpp:91:6:91:7 | g2 | Assignment between incompatible numeric types from 'int32_t' to 'int64_t'. | +| test.cpp:94:6:94:7 | g8 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:96:6:96:7 | l1 | Assignment between incompatible numeric types from 'int' to 'int32_t'. | +| test.cpp:109:6:109:7 | g4 | Assignment between incompatible numeric types from 'int8_t' to 'int32_t'. | +| test.cpp:118:6:118:6 | 2 | Assignment between incompatible numeric types from 'int' to 'long'. | +| test.cpp:126:14:126:15 | g3 | Assignment between incompatible numeric types from 'uint8_t' to 'int'. | +| test.cpp:138:6:138:7 | g1 | Assignment between incompatible numeric types from 'uint32_t' to 'size_t'. | +| test.cpp:160:9:160:10 | 42 | Assignment between incompatible numeric types from 'int' to 'long'. | +| test.cpp:189:23:189:24 | 42 | Assignment between incompatible numeric types from 'int' to 'unsigned long'. | +| test.cpp:199:19:199:25 | ... + ... | Assignment between incompatible numeric types from 'int' to 'int16_t'. | diff --git a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.qlref b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.qlref new file mode 100644 index 000000000..967340a41 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.qlref @@ -0,0 +1 @@ +rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-0-6/test.cpp b/cpp/misra/test/rules/RULE-7-0-6/test.cpp new file mode 100644 index 000000000..ad9aa716f --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-6/test.cpp @@ -0,0 +1,217 @@ +#include +#include + +// Global variables for testing +std::uint32_t g1; +std::int32_t g2; +std::uint8_t g3; +std::int8_t g4; +std::uint16_t g5; +std::int16_t g6; +std::uint64_t g7; +std::int64_t g8; +float g9; +double g10; + +// Test basic constant assignments +void test_constant_assignments() { + g1 = 1; // COMPLIANT + g2 = 4u * 2u; // COMPLIANT + g3 = 3u; // COMPLIANT + g3 = 300u; // NON_COMPLIANT + g9 = 1; // COMPLIANT + g9 = 9999999999; // COMPLIANT + g10 = 0.0f; // NON_COMPLIANT + g9 = 0.0f; // COMPLIANT +} + +// Test signedness violations +void test_signedness_violations() { + g3 = g4; // NON_COMPLIANT + g4 = g3; // NON_COMPLIANT +} + +// Test size violations +void test_size_violations() { + g3 = g5; // NON_COMPLIANT + g5 = g7; // NON_COMPLIANT +} + +// Test type category violations +void test_type_category_violations() { + g9 = g2; // NON_COMPLIANT + g2 = g9; // NON_COMPLIANT +} + +// Test widening of id-expressions +void test_widening_id_expressions() { + g1 = g3; // COMPLIANT + g8 = g4; // COMPLIANT + g7 = g5; // COMPLIANT +} + +// Test expression results +void test_expression_results() { + g3 = g3 + g3; // NON_COMPLIANT + std::int16_t l1 = g4 + g4; // NON_COMPLIANT +} + +// Test bit-fields +struct S { + std::uint32_t m1 : 2; +}; + +void test_bitfields() { + S l1; + l1.m1 = 2; // COMPLIANT + l1.m1 = 32u; // NON_COMPLIANT + l1.m1 = g3; // COMPLIANT + l1.m1 = g5; // NON_COMPLIANT +} + +// Test enums +enum Colour : std::uint16_t { red, green, blue }; + +enum States { enabled, disabled }; + +void test_enums() { + Colour l1 = red; + g3 = red; // COMPLIANT + g1 = red; // COMPLIANT + g3 = l1; // NON_COMPLIANT + g1 = l1; // COMPLIANT + g3 = enabled; // COMPLIANT - enabled is not numeric +} + +// Test function parameters - non-overload-independent +void f1(std::int64_t l1) {} +void f2(std::int32_t l1) {} + +void test_function_parameters_non_overload_independent() { + f1(g2); // NON_COMPLIANT + f1(g8); // COMPLIANT + f2(g2); // COMPLIANT + f2(g8); // NON_COMPLIANT + int l1 = 42; + f2(l1); // NON_COMPLIANT - needs to be the same type as the parameter + signed int l2 = 42; + f2(l2); // COMPLIANT - int32_t is defined as `signed int` in this database +} + +// Test overloaded functions, but still non-overload-independent +// because they are "extensible" (i.e., they can be extended with new +// overloads). +void f3(std::int64_t l1) {} +void f3(std::int32_t l1) {} + +void test_overloaded_functions() { + f3(g2); // COMPLIANT + f3(g4); // NON_COMPLIANT + f3(g8); // COMPLIANT +} + +// Test function pointers - always "overload-independent" +void f4(long l1) {} + +void test_function_pointers() { + void (*l1)(long) = &f4; + f4(2); // NON_COMPLIANT + l1(2); // COMPLIANT +} + +// Test variadic functions +void f5(const char *l1, ...) {} + +void test_variadic_functions() { + f5("test", g3); // NON_COMPLIANT - will be promoted to `int` + f5("test", g2); // COMPLIANT - already `int`, no promotion needed +} + +// Test member function calls - not overload-independent +struct A { + void f6(std::size_t l1, int l2) {} + void f6(std::size_t l1, std::string l2) {} + void f7(); +}; + +void A::f7() { + f6(g1, "answer"); // NON_COMPLIANT - extensible, could call a global + // function instead - e.g. `void f6(std::uint32_t l1, + // std::string l2)` + this->f6(g1, "answer"); // COMPLIANT + this->f6(g1, 42); // COMPLIANT +} + +void test_member_function_overload_independent() { + A l1; + l1.f6(42, "answer"); // COMPLIANT + A *l2 = &l1; + l2->f6(42, "answer"); // COMPLIANT +} + +// Test member function calls - not overload-independent +struct B { + void f8(int l1, int l2) {} + void f8(long l1, std::string l2) {} +}; + +void test_member_function_not_overload_independent() { + B l1; + l1.f8(42, "answer"); // NON_COMPLIANT +} + +// Test constructor exception +struct MyInt { + explicit MyInt(std::int32_t l1) {} + MyInt(std::int32_t l1, std::int32_t l2) {} +}; + +void f9(MyInt l1) {} + +void test_constructor_exception() { + f9(MyInt{g4}); // COMPLIANT + MyInt l1{g4}; // COMPLIANT +} + +// Test template functions - not overload-independent +template struct D { + void f10(T l1, int l2) {} + void f10(T l1, std::string l2) {} + template void f11(S1 l1, int l2) {} + template void f11(S2 l1, std::string l2) {} + void f11(std::int32_t l1, float f) {} +}; + +void test_template_functions() { + D l1; + l1.f10(42, "X"); // COMPLIANT + l1.f10(42, 1); // COMPLIANT + l1.f11(42, "X"); // NON_COMPLIANT - int not size_t + l1.f11(42, 1); // COMPLIANT - same as specialized type + l1.f11(42, 0.0f); // COMPLIANT - same as specialized type +} + +// Test initialization forms +std::int32_t f12(std::int8_t l1) { + std::int16_t l2 = l1; // COMPLIANT + std::int16_t l3{l1}; // COMPLIANT + std::int16_t l4(l1); // COMPLIANT + std::int16_t l5{l1 + l1}; // NON_COMPLIANT + return l1; // COMPLIANT +} + +// Test switch cases +void test_switch_cases() { + switch (g4) { + case 1: // COMPLIANT + break; + case 0x7F: // COMPLIANT + break; + case 0x80: // COMPLIANT - condition subject to promotion + break; + // Our extractor and supported compilers prohibit the below + // narrowing conversion. + // case 0xFFFF'FFFF'FFFF: + // break; + } +} \ No newline at end of file From 229705f672e8a6da59761dbeabdf7e79ebd8a79e Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Tue, 17 Jun 2025 11:28:49 +0100 Subject: [PATCH 07/57] Rule 7.0.6: Update test with better variable names --- .../NumericAssignmentTypeMismatch.expected | 26 ++--- cpp/misra/test/rules/RULE-7-0-6/test.cpp | 102 +++++++++--------- 2 files changed, 64 insertions(+), 64 deletions(-) diff --git a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected index fa0bdb487..c854bd78f 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected +++ b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected @@ -1,23 +1,23 @@ | test.cpp:21:8:21:11 | 300 | Assignment between incompatible numeric types from 'unsigned int' to 'uint8_t'. | -| test.cpp:24:9:24:12 | 0.0 | Assignment between incompatible numeric types from 'float' to 'double'. | -| test.cpp:30:8:30:9 | g4 | Assignment between incompatible numeric types from 'int8_t' to 'uint8_t'. | -| test.cpp:31:8:31:9 | g3 | Assignment between incompatible numeric types from 'uint8_t' to 'int8_t'. | -| test.cpp:36:8:36:9 | g5 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | -| test.cpp:37:8:37:9 | g7 | Assignment between incompatible numeric types from 'uint64_t' to 'uint16_t'. | -| test.cpp:42:8:42:9 | g2 | Assignment between incompatible numeric types from 'int32_t' to 'float'. | -| test.cpp:43:8:43:9 | g9 | Assignment between incompatible numeric types from 'float' to 'int32_t'. | +| test.cpp:24:7:24:10 | 0.0 | Assignment between incompatible numeric types from 'float' to 'double'. | +| test.cpp:30:8:30:9 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'uint8_t'. | +| test.cpp:31:8:31:9 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'int8_t'. | +| test.cpp:36:8:36:10 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test.cpp:37:9:37:11 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'uint16_t'. | +| test.cpp:42:7:42:9 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'float'. | +| test.cpp:43:9:43:9 | f | Assignment between incompatible numeric types from 'float' to 'int32_t'. | | test.cpp:55:8:55:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | | test.cpp:56:21:56:27 | ... + ... | Assignment between incompatible numeric types from 'int' to 'int16_t'. | | test.cpp:67:11:67:13 | 32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned char'. | -| test.cpp:69:11:69:12 | g5 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test.cpp:69:11:69:13 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | | test.cpp:81:8:81:9 | l1 | Assignment between incompatible numeric types from 'Colour' to 'uint8_t'. | -| test.cpp:91:6:91:7 | g2 | Assignment between incompatible numeric types from 'int32_t' to 'int64_t'. | -| test.cpp:94:6:94:7 | g8 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:91:6:91:8 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'int64_t'. | +| test.cpp:94:6:94:8 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | | test.cpp:96:6:96:7 | l1 | Assignment between incompatible numeric types from 'int' to 'int32_t'. | -| test.cpp:109:6:109:7 | g4 | Assignment between incompatible numeric types from 'int8_t' to 'int32_t'. | +| test.cpp:109:6:109:7 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'int32_t'. | | test.cpp:118:6:118:6 | 2 | Assignment between incompatible numeric types from 'int' to 'long'. | -| test.cpp:126:14:126:15 | g3 | Assignment between incompatible numeric types from 'uint8_t' to 'int'. | -| test.cpp:138:6:138:7 | g1 | Assignment between incompatible numeric types from 'uint32_t' to 'size_t'. | +| test.cpp:126:14:126:15 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'int'. | +| test.cpp:138:6:138:8 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'size_t'. | | test.cpp:160:9:160:10 | 42 | Assignment between incompatible numeric types from 'int' to 'long'. | | test.cpp:189:23:189:24 | 42 | Assignment between incompatible numeric types from 'int' to 'unsigned long'. | | test.cpp:199:19:199:25 | ... + ... | Assignment between incompatible numeric types from 'int' to 'int16_t'. | diff --git a/cpp/misra/test/rules/RULE-7-0-6/test.cpp b/cpp/misra/test/rules/RULE-7-0-6/test.cpp index ad9aa716f..c600c91a2 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/test.cpp +++ b/cpp/misra/test/rules/RULE-7-0-6/test.cpp @@ -2,58 +2,58 @@ #include // Global variables for testing -std::uint32_t g1; -std::int32_t g2; -std::uint8_t g3; -std::int8_t g4; -std::uint16_t g5; -std::int16_t g6; -std::uint64_t g7; -std::int64_t g8; -float g9; -double g10; +std::uint32_t u32; +std::int32_t s32; +std::uint8_t u8; +std::int8_t s8; +std::uint16_t u16; +std::int16_t s16; +std::uint64_t u64; +std::int64_t s64; +float f; +double d; // Test basic constant assignments void test_constant_assignments() { - g1 = 1; // COMPLIANT - g2 = 4u * 2u; // COMPLIANT - g3 = 3u; // COMPLIANT - g3 = 300u; // NON_COMPLIANT - g9 = 1; // COMPLIANT - g9 = 9999999999; // COMPLIANT - g10 = 0.0f; // NON_COMPLIANT - g9 = 0.0f; // COMPLIANT + u32 = 1; // COMPLIANT + s32 = 4u * 2u; // COMPLIANT + u8 = 3u; // COMPLIANT + u8 = 300u; // NON_COMPLIANT + f = 1; // COMPLIANT + f = 9999999999; // COMPLIANT + d = 0.0f; // NON_COMPLIANT + f = 0.0f; // COMPLIANT } // Test signedness violations void test_signedness_violations() { - g3 = g4; // NON_COMPLIANT - g4 = g3; // NON_COMPLIANT + u8 = s8; // NON_COMPLIANT + s8 = u8; // NON_COMPLIANT } // Test size violations void test_size_violations() { - g3 = g5; // NON_COMPLIANT - g5 = g7; // NON_COMPLIANT + u8 = u16; // NON_COMPLIANT + u16 = u64; // NON_COMPLIANT } // Test type category violations void test_type_category_violations() { - g9 = g2; // NON_COMPLIANT - g2 = g9; // NON_COMPLIANT + f = s32; // NON_COMPLIANT + s32 = f; // NON_COMPLIANT } // Test widening of id-expressions void test_widening_id_expressions() { - g1 = g3; // COMPLIANT - g8 = g4; // COMPLIANT - g7 = g5; // COMPLIANT + u32 = u8; // COMPLIANT + s64 = s8; // COMPLIANT + u64 = u16; // COMPLIANT } // Test expression results void test_expression_results() { - g3 = g3 + g3; // NON_COMPLIANT - std::int16_t l1 = g4 + g4; // NON_COMPLIANT + u8 = u8 + u8; // NON_COMPLIANT + std::int16_t l1 = s8 + s8; // NON_COMPLIANT } // Test bit-fields @@ -65,8 +65,8 @@ void test_bitfields() { S l1; l1.m1 = 2; // COMPLIANT l1.m1 = 32u; // NON_COMPLIANT - l1.m1 = g3; // COMPLIANT - l1.m1 = g5; // NON_COMPLIANT + l1.m1 = u8; // COMPLIANT + l1.m1 = u16; // NON_COMPLIANT } // Test enums @@ -76,11 +76,11 @@ enum States { enabled, disabled }; void test_enums() { Colour l1 = red; - g3 = red; // COMPLIANT - g1 = red; // COMPLIANT - g3 = l1; // NON_COMPLIANT - g1 = l1; // COMPLIANT - g3 = enabled; // COMPLIANT - enabled is not numeric + u8 = red; // COMPLIANT + u32 = red; // COMPLIANT + u8 = l1; // NON_COMPLIANT + u32 = l1; // COMPLIANT + u8 = enabled; // COMPLIANT - enabled is not numeric } // Test function parameters - non-overload-independent @@ -88,10 +88,10 @@ void f1(std::int64_t l1) {} void f2(std::int32_t l1) {} void test_function_parameters_non_overload_independent() { - f1(g2); // NON_COMPLIANT - f1(g8); // COMPLIANT - f2(g2); // COMPLIANT - f2(g8); // NON_COMPLIANT + f1(s32); // NON_COMPLIANT + f1(s64); // COMPLIANT + f2(s32); // COMPLIANT + f2(s64); // NON_COMPLIANT int l1 = 42; f2(l1); // NON_COMPLIANT - needs to be the same type as the parameter signed int l2 = 42; @@ -105,9 +105,9 @@ void f3(std::int64_t l1) {} void f3(std::int32_t l1) {} void test_overloaded_functions() { - f3(g2); // COMPLIANT - f3(g4); // NON_COMPLIANT - f3(g8); // COMPLIANT + f3(s32); // COMPLIANT + f3(s8); // NON_COMPLIANT + f3(s64); // COMPLIANT } // Test function pointers - always "overload-independent" @@ -123,8 +123,8 @@ void test_function_pointers() { void f5(const char *l1, ...) {} void test_variadic_functions() { - f5("test", g3); // NON_COMPLIANT - will be promoted to `int` - f5("test", g2); // COMPLIANT - already `int`, no promotion needed + f5("test", u8); // NON_COMPLIANT - will be promoted to `int` + f5("test", s32); // COMPLIANT - already `int`, no promotion needed } // Test member function calls - not overload-independent @@ -135,11 +135,11 @@ struct A { }; void A::f7() { - f6(g1, "answer"); // NON_COMPLIANT - extensible, could call a global + f6(u32, "answer"); // NON_COMPLIANT - extensible, could call a global // function instead - e.g. `void f6(std::uint32_t l1, // std::string l2)` - this->f6(g1, "answer"); // COMPLIANT - this->f6(g1, 42); // COMPLIANT + this->f6(u32, "answer"); // COMPLIANT + this->f6(u32, 42); // COMPLIANT } void test_member_function_overload_independent() { @@ -169,8 +169,8 @@ struct MyInt { void f9(MyInt l1) {} void test_constructor_exception() { - f9(MyInt{g4}); // COMPLIANT - MyInt l1{g4}; // COMPLIANT + f9(MyInt{s8}); // COMPLIANT + MyInt l1{s8}; // COMPLIANT } // Test template functions - not overload-independent @@ -202,7 +202,7 @@ std::int32_t f12(std::int8_t l1) { // Test switch cases void test_switch_cases() { - switch (g4) { + switch (s8) { case 1: // COMPLIANT break; case 0x7F: // COMPLIANT From f870023aa44b3f24f58e9f0e91d85d482d9ed3dd Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Tue, 17 Jun 2025 12:13:55 +0100 Subject: [PATCH 08/57] Rule 7.0.6: Fix id-expression detection - Add additional test cases - Allow name qualifiers - Prohibit explicit casts (inc. parameters) --- .../NumericAssignmentTypeMismatch.ql | 4 +- .../NumericAssignmentTypeMismatch.expected | 53 ++++++----- cpp/misra/test/rules/RULE-7-0-6/test.cpp | 87 +++++++++++++++---- 3 files changed, 103 insertions(+), 41 deletions(-) diff --git a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql index eff5361d7..b310da8cd 100644 --- a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql +++ b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql @@ -194,8 +194,8 @@ class IdExpression extends VariableAccess { // Member variable, but the qualifier is not explicit this.getQualifier().isCompilerGenerated() ) and - // No name qualifiers - not exists(NameQualifier qual | qual.getExpr() = this) + // Not an id-expression if it's an explicit conversion + not this.hasExplicitConversion() } } diff --git a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected index c854bd78f..75492f12b 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected +++ b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected @@ -1,23 +1,30 @@ -| test.cpp:21:8:21:11 | 300 | Assignment between incompatible numeric types from 'unsigned int' to 'uint8_t'. | -| test.cpp:24:7:24:10 | 0.0 | Assignment between incompatible numeric types from 'float' to 'double'. | -| test.cpp:30:8:30:9 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'uint8_t'. | -| test.cpp:31:8:31:9 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'int8_t'. | -| test.cpp:36:8:36:10 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | -| test.cpp:37:9:37:11 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'uint16_t'. | -| test.cpp:42:7:42:9 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'float'. | -| test.cpp:43:9:43:9 | f | Assignment between incompatible numeric types from 'float' to 'int32_t'. | -| test.cpp:55:8:55:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | -| test.cpp:56:21:56:27 | ... + ... | Assignment between incompatible numeric types from 'int' to 'int16_t'. | -| test.cpp:67:11:67:13 | 32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned char'. | -| test.cpp:69:11:69:13 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | -| test.cpp:81:8:81:9 | l1 | Assignment between incompatible numeric types from 'Colour' to 'uint8_t'. | -| test.cpp:91:6:91:8 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'int64_t'. | -| test.cpp:94:6:94:8 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:96:6:96:7 | l1 | Assignment between incompatible numeric types from 'int' to 'int32_t'. | -| test.cpp:109:6:109:7 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'int32_t'. | -| test.cpp:118:6:118:6 | 2 | Assignment between incompatible numeric types from 'int' to 'long'. | -| test.cpp:126:14:126:15 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'int'. | -| test.cpp:138:6:138:8 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'size_t'. | -| test.cpp:160:9:160:10 | 42 | Assignment between incompatible numeric types from 'int' to 'long'. | -| test.cpp:189:23:189:24 | 42 | Assignment between incompatible numeric types from 'int' to 'unsigned long'. | -| test.cpp:199:19:199:25 | ... + ... | Assignment between incompatible numeric types from 'int' to 'int16_t'. | +| test.cpp:36:8:36:11 | 300 | Assignment between incompatible numeric types from 'unsigned int' to 'uint8_t'. | +| test.cpp:39:7:39:10 | 0.0 | Assignment between incompatible numeric types from 'float' to 'double'. | +| test.cpp:45:8:45:9 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'uint8_t'. | +| test.cpp:46:8:46:9 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'int8_t'. | +| test.cpp:51:8:51:10 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test.cpp:52:9:52:11 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'uint16_t'. | +| test.cpp:57:7:57:9 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'float'. | +| test.cpp:58:9:58:9 | f | Assignment between incompatible numeric types from 'float' to 'int32_t'. | +| test.cpp:95:12:95:13 | m1 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | +| test.cpp:96:12:96:13 | m2 | Assignment between incompatible numeric types from 'uint16_t' to 'uint64_t'. | +| test.cpp:97:13:97:14 | m1 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | +| test.cpp:98:13:98:14 | m2 | Assignment between incompatible numeric types from 'uint16_t' to 'uint64_t'. | +| test.cpp:103:9:103:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint32_t'. | +| test.cpp:104:10:104:11 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | +| test.cpp:105:35:105:36 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | +| test.cpp:110:8:110:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test.cpp:111:21:111:27 | ... + ... | Assignment between incompatible numeric types from 'int' to 'int16_t'. | +| test.cpp:122:11:122:13 | 32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned char'. | +| test.cpp:124:11:124:13 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test.cpp:136:8:136:9 | l1 | Assignment between incompatible numeric types from 'Colour' to 'uint8_t'. | +| test.cpp:146:6:146:8 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'int64_t'. | +| test.cpp:149:6:149:8 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:151:6:151:7 | l1 | Assignment between incompatible numeric types from 'int' to 'int32_t'. | +| test.cpp:164:6:164:7 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'int32_t'. | +| test.cpp:173:6:173:6 | 2 | Assignment between incompatible numeric types from 'int' to 'long'. | +| test.cpp:181:14:181:15 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'int'. | +| test.cpp:193:6:193:8 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'size_t'. | +| test.cpp:215:9:215:10 | 42 | Assignment between incompatible numeric types from 'int' to 'long'. | +| test.cpp:244:23:244:24 | 42 | Assignment between incompatible numeric types from 'int' to 'unsigned long'. | +| test.cpp:254:19:254:25 | ... + ... | Assignment between incompatible numeric types from 'int' to 'int16_t'. | diff --git a/cpp/misra/test/rules/RULE-7-0-6/test.cpp b/cpp/misra/test/rules/RULE-7-0-6/test.cpp index c600c91a2..5be5efa19 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/test.cpp +++ b/cpp/misra/test/rules/RULE-7-0-6/test.cpp @@ -13,15 +13,30 @@ std::int64_t s64; float f; double d; +namespace TestNamespace { +std::uint8_t g1; +std::uint16_t g2; +} // namespace TestNamespace + +struct TestStruct { + std::uint8_t m1; + std::uint16_t m2; + static std::uint8_t s1; + static std::uint16_t s2; +}; + +std::uint8_t TestStruct::s1; +std::uint16_t TestStruct::s2; + // Test basic constant assignments void test_constant_assignments() { - u32 = 1; // COMPLIANT - s32 = 4u * 2u; // COMPLIANT - u8 = 3u; // COMPLIANT - u8 = 300u; // NON_COMPLIANT + u32 = 1; // COMPLIANT + s32 = 4u * 2u; // COMPLIANT + u8 = 3u; // COMPLIANT + u8 = 300u; // NON_COMPLIANT f = 1; // COMPLIANT f = 9999999999; // COMPLIANT - d = 0.0f; // NON_COMPLIANT + d = 0.0f; // NON_COMPLIANT f = 0.0f; // COMPLIANT } @@ -33,7 +48,7 @@ void test_signedness_violations() { // Test size violations void test_size_violations() { - u8 = u16; // NON_COMPLIANT + u8 = u16; // NON_COMPLIANT u16 = u64; // NON_COMPLIANT } @@ -45,9 +60,49 @@ void test_type_category_violations() { // Test widening of id-expressions void test_widening_id_expressions() { - u32 = u8; // COMPLIANT - s64 = s8; // COMPLIANT - u64 = u16; // COMPLIANT + u32 = u8; // COMPLIANT - widening of id-expression + s64 = s8; // COMPLIANT - widening of id-expression + u64 = u16; // COMPLIANT - widening of id-expression +} + +// Test widening with namespace qualifiers (allowed) +void test_widening_namespace_qualified() { + u32 = TestNamespace::g1; // COMPLIANT - namespace qualified id-expression + u64 = TestNamespace::g2; // COMPLIANT - namespace qualified id-expression +} + +// Test widening with type qualifiers (allowed) +void test_widening_type_qualified() { + u32 = TestStruct::s1; // COMPLIANT - type qualified id-expression + u64 = TestStruct::s2; // COMPLIANT - type qualified id-expression +} + +// Test widening with decltype (allowed) +void test_widening_decltype_qualified() { + std::uint8_t l1 = 42; + std::uint16_t l2 = 42; + u32 = decltype(l1){}; // COMPLIANT - treated as a constant + u64 = decltype(l2){}; // COMPLIANT - treated as a constant + TestStruct l3; + u32 = decltype(l3)::s1; // COMPLIANT - decltype qualified + u64 = decltype(l3)::s2; // COMPLIANT - decltype qualified +} + +// Test widening with object member access (not allowed) +void test_widening_object_member_access() { + TestStruct l1; + TestStruct *l2 = &l1; + u32 = l1.m1; // NON_COMPLIANT - object member access, not id-expression + u64 = l1.m2; // NON_COMPLIANT - object member access, not id-expression + u32 = l2->m1; // NON_COMPLIANT - object member access, not id-expression + u64 = l2->m2; // NON_COMPLIANT - object member access, not id-expression +} + +// Test widening with expressions (not allowed) +void test_widening_expressions() { + u32 = u8 + 0; // NON_COMPLIANT - not id-expression + u32 = (u8); // NON_COMPLIANT - not id-expression (parenthesized) + u32 = static_cast(u8); // NON_COMPLIANT - not id-expression } // Test expression results @@ -66,7 +121,7 @@ void test_bitfields() { l1.m1 = 2; // COMPLIANT l1.m1 = 32u; // NON_COMPLIANT l1.m1 = u8; // COMPLIANT - l1.m1 = u16; // NON_COMPLIANT + l1.m1 = u16; // NON_COMPLIANT } // Test enums @@ -77,9 +132,9 @@ enum States { enabled, disabled }; void test_enums() { Colour l1 = red; u8 = red; // COMPLIANT - u32 = red; // COMPLIANT + u32 = red; // COMPLIANT u8 = l1; // NON_COMPLIANT - u32 = l1; // COMPLIANT + u32 = l1; // COMPLIANT u8 = enabled; // COMPLIANT - enabled is not numeric } @@ -106,7 +161,7 @@ void f3(std::int32_t l1) {} void test_overloaded_functions() { f3(s32); // COMPLIANT - f3(s8); // NON_COMPLIANT + f3(s8); // NON_COMPLIANT f3(s64); // COMPLIANT } @@ -123,7 +178,7 @@ void test_function_pointers() { void f5(const char *l1, ...) {} void test_variadic_functions() { - f5("test", u8); // NON_COMPLIANT - will be promoted to `int` + f5("test", u8); // NON_COMPLIANT - will be promoted to `int` f5("test", s32); // COMPLIANT - already `int`, no promotion needed } @@ -136,8 +191,8 @@ struct A { void A::f7() { f6(u32, "answer"); // NON_COMPLIANT - extensible, could call a global - // function instead - e.g. `void f6(std::uint32_t l1, - // std::string l2)` + // function instead - e.g. `void f6(std::uint32_t l1, + // std::string l2)` this->f6(u32, "answer"); // COMPLIANT this->f6(u32, 42); // COMPLIANT } From 0ed1689ac4afe8bd039ce3b9b7b335d3eaa5fa27 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Tue, 17 Jun 2025 13:23:32 +0100 Subject: [PATCH 09/57] Rule 7.0.6: Support reference types Reference types can be numeric types according to the rule. In addition to making NumericTypes reference types, we also add a helper predicate which gets the size of the real type (rather than the size of the reference). --- .../NumericAssignmentTypeMismatch.ql | 12 ++- .../NumericAssignmentTypeMismatch.expected | 13 +++ cpp/misra/test/rules/RULE-7-0-6/test.cpp | 90 +++++++++++++++++++ 3 files changed, 111 insertions(+), 4 deletions(-) diff --git a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql index b310da8cd..3a30f878d 100644 --- a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql +++ b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql @@ -43,6 +43,7 @@ class NumericType extends Type { Type realType; NumericType() { + realType = this.getUnspecifiedType().(ReferenceType).getBaseType().(NumericType).getRealType() or realType = this.getUnspecifiedType().(IntegralType) or realType = this.getUnspecifiedType().(FloatingPointType) or realType = this.getUnspecifiedType().(Enum).getExplicitUnderlyingType().getUnspecifiedType() @@ -52,6 +53,8 @@ class NumericType extends Type { if realType.(IntegralType).isUnsigned() then result = Unsigned() else result = Signed() } + int getRealSize() { result = realType.getSize() } + TypeCategory getTypeCategory() { realType instanceof IntegralType and result = Integral() or @@ -76,14 +79,15 @@ predicate isAssignment(Expr source, NumericType targetType, string context) { then exists(BitField bf | isAssignedToBitfield(source, bf) and + // TODO integral after numeric? targetType.(IntegralType).(NumericType).getSignedness() = bf.getType().(NumericType).getSignedness() and // smallest integral type that can hold the bit field value - targetType.getSize() * 8 >= bf.getNumBits() and + targetType.getRealSize() * 8 >= bf.getNumBits() and not exists(IntegralType other | other.getSize() * 8 >= bf.getNumBits() and other.(NumericType).getSignedness() = targetType.getSignedness() and - other.getSize() < targetType.getSize() + other.getSize() < targetType.getRealSize() ) ) else targetType = assign.getLValue().getType() @@ -159,7 +163,7 @@ predicate isValidTypeMatch(NumericType sourceType, NumericType targetType) { // Same type category, signedness and size sourceType.getTypeCategory() = targetType.getTypeCategory() and sourceType.getSignedness() = targetType.getSignedness() and - sourceType.getSize() = targetType.getSize() + sourceType.getRealSize() = targetType.getRealSize() } predicate hasConstructorException(FunctionCall call) { @@ -209,7 +213,7 @@ predicate isValidWidening(Expr source, NumericType sourceType, NumericType targe ) and sourceType.getTypeCategory() = targetType.getTypeCategory() and sourceType.getSignedness() = targetType.getSignedness() and - sourceType.getSize() < targetType.getSize() + sourceType.getRealSize() < targetType.getRealSize() } /** diff --git a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected index 75492f12b..eb97d1b60 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected +++ b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected @@ -28,3 +28,16 @@ | test.cpp:215:9:215:10 | 42 | Assignment between incompatible numeric types from 'int' to 'long'. | | test.cpp:244:23:244:24 | 42 | Assignment between incompatible numeric types from 'int' to 'unsigned long'. | | test.cpp:254:19:254:25 | ... + ... | Assignment between incompatible numeric types from 'int' to 'int16_t'. | +| test.cpp:288:8:288:9 | l6 | Assignment between incompatible numeric types from 'uint32_t &' to 'uint8_t'. | +| test.cpp:289:8:289:9 | l7 | Assignment between incompatible numeric types from 'int8_t &' to 'uint8_t'. | +| test.cpp:290:9:290:10 | l8 | Assignment between incompatible numeric types from 'float &' to 'int32_t'. | +| test.cpp:301:6:301:7 | l3 | Assignment between incompatible numeric types from 'uint8_t &' to 'int64_t'. | +| test.cpp:302:6:302:7 | l4 | Assignment between incompatible numeric types from 'uint16_t &' to 'int32_t'. | +| test.cpp:313:8:313:9 | l3 | Assignment between incompatible numeric types from 'uint8_t &' to 'int8_t'. | +| test.cpp:314:8:314:9 | l4 | Assignment between incompatible numeric types from 'int8_t &' to 'uint8_t'. | +| test.cpp:327:9:327:10 | l4 | Assignment between incompatible numeric types from 'float &' to 'int32_t'. | +| test.cpp:328:7:328:8 | l5 | Assignment between incompatible numeric types from 'double &' to 'float'. | +| test.cpp:329:7:329:8 | l6 | Assignment between incompatible numeric types from 'int32_t &' to 'float'. | +| test.cpp:340:8:340:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test.cpp:358:7:358:8 | l3 | Assignment between incompatible numeric types from 'uint16_t &' to 'uint32_t'. | +| test.cpp:361:7:361:8 | l5 | Assignment between incompatible numeric types from 'uint64_t &' to 'uint32_t'. | diff --git a/cpp/misra/test/rules/RULE-7-0-6/test.cpp b/cpp/misra/test/rules/RULE-7-0-6/test.cpp index 5be5efa19..2f24a1c5b 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/test.cpp +++ b/cpp/misra/test/rules/RULE-7-0-6/test.cpp @@ -269,4 +269,94 @@ void test_switch_cases() { // case 0xFFFF'FFFF'FFFF: // break; } +} + +// Test reference types - references to numeric types are considered numeric +void test_reference_types_basic() { + std::uint8_t l1 = 42; + std::uint32_t l2 = 100; + std::int8_t l3 = -5; + float l4 = 3.14f; + + std::uint8_t &l5 = l1; // COMPLIANT + std::uint32_t &l6 = l2; // COMPLIANT + std::int8_t &l7 = l3; // COMPLIANT + float &l8 = l4; // COMPLIANT + + // Reference types follow same rules as their referred types + u32 = l5; // COMPLIANT - widening of id-expression (reference) + u8 = l6; // NON_COMPLIANT - narrowing from reference + u8 = l7; // NON_COMPLIANT - different signedness from reference + s32 = l8; // NON_COMPLIANT - different type category from reference +} + +void test_reference_types_function_parameters() { + std::uint8_t l1 = 42; + std::uint16_t l2 = 1000; + + std::uint8_t &l3 = l1; + std::uint16_t &l4 = l2; + + // Function calls with reference arguments + f1(l3); // NON_COMPLIANT - widening conversion through reference + f2(l4); // NON_COMPLIANT - narrowing conversion through reference +} + +void test_reference_types_signedness() { + std::uint8_t l1 = 42; + std::int8_t l2 = -5; + + std::uint8_t &l3 = l1; + std::int8_t &l4 = l2; + + // Signedness violations through references + s8 = l3; // NON_COMPLIANT - different signedness through reference + u8 = l4; // NON_COMPLIANT - different signedness through reference +} + +void test_reference_types_floating_point() { + float l1 = 3.14f; + double l2 = 2.718; + std::int32_t l3 = 42; + + float &l4 = l1; + double &l5 = l2; + std::int32_t &l6 = l3; + + // Type category violations through references + s32 = l4; // NON_COMPLIANT - different type category through reference + f = l5; // NON_COMPLIANT - different size through reference + f = l6; // NON_COMPLIANT - different type category through reference +} + +void test_reference_types_expressions() { + std::uint8_t l1 = 42; + std::uint8_t l2 = 24; + + std::uint8_t &l3 = l1; + std::uint8_t &l4 = l2; + + // Expression results with references still follow expression rules + u8 = l3 + l4; // NON_COMPLIANT - addition promotes to int + s32 = l3 + l4; // COMPLIANT - promotion to int +} + +// Test reference parameters in functions +void f13(std::uint8_t &l1) {} +void f13(std::uint16_t &l1) {} + +void f14(std::uint32_t l1) {} + +void test_references() { + std::uint8_t l1 = 42; + std::uint16_t l2 = 1000; + + f13(l1); // COMPLIANT - exact match + f13(l2); // COMPLIANT - exact match + + std::uint16_t &l3 = l2; + f14(l3); // NON_COMPLIANT - must be the same type, as non-overload-independent + std::uint64_t l4 = 1000; + std::uint64_t &l5 = l4; + f14(l5); // NON_COMPLIANT - narrowing conversion through reference } \ No newline at end of file From 778b344bf787725d871f3624974ec7b549c0026a Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Tue, 17 Jun 2025 13:34:02 +0100 Subject: [PATCH 10/57] Rule 7.0.6: Ignore compound expressions --- .../NumericAssignmentTypeMismatch.ql | 6 +++--- cpp/misra/test/rules/RULE-7-0-6/test.cpp | 21 +++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql index 3a30f878d..009cc67ff 100644 --- a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql +++ b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql @@ -69,8 +69,8 @@ class NumericType extends Type { } predicate isAssignment(Expr source, NumericType targetType, string context) { - // Assignment operator - exists(Assignment assign | + // Assignment operator (but not compound assignment) + exists(AssignExpr assign | assign.getRValue() = source and context = "assignment" | @@ -191,7 +191,7 @@ predicate hasConstructorException(FunctionCall call) { */ class IdExpression extends VariableAccess { IdExpression() { - // Not a member variable + // Not a member variable access (no dot or arrow) ( not exists(this.getQualifier()) or diff --git a/cpp/misra/test/rules/RULE-7-0-6/test.cpp b/cpp/misra/test/rules/RULE-7-0-6/test.cpp index 2f24a1c5b..ce494309c 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/test.cpp +++ b/cpp/misra/test/rules/RULE-7-0-6/test.cpp @@ -359,4 +359,25 @@ void test_references() { std::uint64_t l4 = 1000; std::uint64_t &l5 = l4; f14(l5); // NON_COMPLIANT - narrowing conversion through reference +} + +// Test compound assignments - rule does not apply to compound assignments +void test_compound_assignments() { + std::uint8_t l1 = 10; + std::uint16_t l2 = 100; + std::int8_t l3 = 5; + float l4 = 1.5f; + + l1 += l2; // COMPLIANT - compound assignment, rule does not apply + l1 -= l3; // COMPLIANT - compound assignment, rule does not apply + l2 *= l1; // COMPLIANT - compound assignment, rule does not apply + l2 /= l3; // COMPLIANT - compound assignment, rule does not apply + l1 %= l3; // COMPLIANT - compound assignment, rule does not apply + l2 &= l1; // COMPLIANT - compound assignment, rule does not apply + l2 |= l3; // COMPLIANT - compound assignment, rule does not apply + l2 ^= l1; // COMPLIANT - compound assignment, rule does not apply + l2 <<= 2; // COMPLIANT - compound assignment, rule does not apply + l2 >>= 1; // COMPLIANT - compound assignment, rule does not apply + l4 += l1; // COMPLIANT - compound assignment, rule does not apply + l4 -= s32; // COMPLIANT - compound assignment, rule does not apply } \ No newline at end of file From acfb25380e5e701e08745a02642a8edc9e22b185 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Tue, 17 Jun 2025 13:38:14 +0100 Subject: [PATCH 11/57] Rule 7.0.6: Additional function return test cases --- .../NumericAssignmentTypeMismatch.expected | 27 ++++++++++--------- cpp/misra/test/rules/RULE-7-0-6/test.cpp | 4 +++ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected index eb97d1b60..4e1c07ef3 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected +++ b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected @@ -28,16 +28,17 @@ | test.cpp:215:9:215:10 | 42 | Assignment between incompatible numeric types from 'int' to 'long'. | | test.cpp:244:23:244:24 | 42 | Assignment between incompatible numeric types from 'int' to 'unsigned long'. | | test.cpp:254:19:254:25 | ... + ... | Assignment between incompatible numeric types from 'int' to 'int16_t'. | -| test.cpp:288:8:288:9 | l6 | Assignment between incompatible numeric types from 'uint32_t &' to 'uint8_t'. | -| test.cpp:289:8:289:9 | l7 | Assignment between incompatible numeric types from 'int8_t &' to 'uint8_t'. | -| test.cpp:290:9:290:10 | l8 | Assignment between incompatible numeric types from 'float &' to 'int32_t'. | -| test.cpp:301:6:301:7 | l3 | Assignment between incompatible numeric types from 'uint8_t &' to 'int64_t'. | -| test.cpp:302:6:302:7 | l4 | Assignment between incompatible numeric types from 'uint16_t &' to 'int32_t'. | -| test.cpp:313:8:313:9 | l3 | Assignment between incompatible numeric types from 'uint8_t &' to 'int8_t'. | -| test.cpp:314:8:314:9 | l4 | Assignment between incompatible numeric types from 'int8_t &' to 'uint8_t'. | -| test.cpp:327:9:327:10 | l4 | Assignment between incompatible numeric types from 'float &' to 'int32_t'. | -| test.cpp:328:7:328:8 | l5 | Assignment between incompatible numeric types from 'double &' to 'float'. | -| test.cpp:329:7:329:8 | l6 | Assignment between incompatible numeric types from 'int32_t &' to 'float'. | -| test.cpp:340:8:340:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | -| test.cpp:358:7:358:8 | l3 | Assignment between incompatible numeric types from 'uint16_t &' to 'uint32_t'. | -| test.cpp:361:7:361:8 | l5 | Assignment between incompatible numeric types from 'uint64_t &' to 'uint32_t'. | +| test.cpp:259:10:259:12 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:292:8:292:9 | l6 | Assignment between incompatible numeric types from 'uint32_t &' to 'uint8_t'. | +| test.cpp:293:8:293:9 | l7 | Assignment between incompatible numeric types from 'int8_t &' to 'uint8_t'. | +| test.cpp:294:9:294:10 | l8 | Assignment between incompatible numeric types from 'float &' to 'int32_t'. | +| test.cpp:305:6:305:7 | l3 | Assignment between incompatible numeric types from 'uint8_t &' to 'int64_t'. | +| test.cpp:306:6:306:7 | l4 | Assignment between incompatible numeric types from 'uint16_t &' to 'int32_t'. | +| test.cpp:317:8:317:9 | l3 | Assignment between incompatible numeric types from 'uint8_t &' to 'int8_t'. | +| test.cpp:318:8:318:9 | l4 | Assignment between incompatible numeric types from 'int8_t &' to 'uint8_t'. | +| test.cpp:331:9:331:10 | l4 | Assignment between incompatible numeric types from 'float &' to 'int32_t'. | +| test.cpp:332:7:332:8 | l5 | Assignment between incompatible numeric types from 'double &' to 'float'. | +| test.cpp:333:7:333:8 | l6 | Assignment between incompatible numeric types from 'int32_t &' to 'float'. | +| test.cpp:344:8:344:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test.cpp:362:7:362:8 | l3 | Assignment between incompatible numeric types from 'uint16_t &' to 'uint32_t'. | +| test.cpp:365:7:365:8 | l5 | Assignment between incompatible numeric types from 'uint64_t &' to 'uint32_t'. | diff --git a/cpp/misra/test/rules/RULE-7-0-6/test.cpp b/cpp/misra/test/rules/RULE-7-0-6/test.cpp index ce494309c..32b5520f9 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/test.cpp +++ b/cpp/misra/test/rules/RULE-7-0-6/test.cpp @@ -255,6 +255,10 @@ std::int32_t f12(std::int8_t l1) { return l1; // COMPLIANT } +std::int32_t test_return() { + return u32; // NON_COMPLIANT - wrong signedness +} + // Test switch cases void test_switch_cases() { switch (s8) { From 9dbc39f720362136327db51f18ec19fd7292b852 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Tue, 17 Jun 2025 13:41:18 +0100 Subject: [PATCH 12/57] Rule 7.0.6: Clarify pass-by-value on parameters --- .../src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql | 1 + cpp/misra/test/rules/RULE-7-0-6/test.cpp | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql index 009cc67ff..04d454896 100644 --- a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql +++ b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql @@ -101,6 +101,7 @@ predicate isAssignment(Expr source, NumericType targetType, string context) { context = "initialization" ) or + // Passing a function parameter by value exists(Call call, int i | call.getArgument(i) = source and not targetType.stripTopLevelSpecifiers() instanceof ReferenceType and diff --git a/cpp/misra/test/rules/RULE-7-0-6/test.cpp b/cpp/misra/test/rules/RULE-7-0-6/test.cpp index 32b5520f9..dc4faf5ef 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/test.cpp +++ b/cpp/misra/test/rules/RULE-7-0-6/test.cpp @@ -351,12 +351,12 @@ void f13(std::uint16_t &l1) {} void f14(std::uint32_t l1) {} -void test_references() { +void test_references_to_parameters() { std::uint8_t l1 = 42; std::uint16_t l2 = 1000; - f13(l1); // COMPLIANT - exact match - f13(l2); // COMPLIANT - exact match + f13(l1); // COMPLIANT - not covered by rule, as pass-by-ref + f13(l2); // COMPLIANT - not covered by rule, as pass-by-ref std::uint16_t &l3 = l2; f14(l3); // NON_COMPLIANT - must be the same type, as non-overload-independent From b310e14390541abbef3e35a9d4ea05f767804662 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Tue, 17 Jun 2025 22:13:58 +0100 Subject: [PATCH 13/57] Rule 7.0.6: Add user defined operator tests --- .../NumericAssignmentTypeMismatch.expected | 73 +++++ cpp/misra/test/rules/RULE-7-0-6/test.cpp | 294 ++++++++++++++++++ 2 files changed, 367 insertions(+) diff --git a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected index 4e1c07ef3..75cdf0c69 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected +++ b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected @@ -42,3 +42,76 @@ | test.cpp:344:8:344:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | | test.cpp:362:7:362:8 | l3 | Assignment between incompatible numeric types from 'uint16_t &' to 'uint32_t'. | | test.cpp:365:7:365:8 | l5 | Assignment between incompatible numeric types from 'uint64_t &' to 'uint32_t'. | +| test.cpp:485:8:485:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:486:8:486:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:490:8:490:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:491:8:491:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:495:7:495:8 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:496:7:496:8 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:500:8:500:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:501:8:501:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:505:8:505:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:506:8:506:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:510:8:510:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:511:8:511:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:515:8:515:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:516:8:516:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:520:8:520:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:521:8:521:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:525:9:525:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:526:9:526:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:530:9:530:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:531:9:531:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:536:9:536:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:537:9:537:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:541:9:541:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:542:9:542:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:546:8:546:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:547:8:547:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:551:9:551:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:552:9:552:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:556:8:556:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:557:8:557:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:561:9:561:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:562:9:562:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:567:6:567:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:568:6:568:7 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:573:6:573:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:574:6:574:7 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:576:10:576:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:577:6:577:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:578:6:578:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:578:10:578:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:583:8:583:9 | l3 | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | +| test.cpp:584:8:584:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:585:8:585:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:589:9:589:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:590:9:590:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:594:9:594:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:595:9:595:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:599:9:599:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:600:9:600:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:604:9:604:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:605:9:605:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:609:9:609:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:610:9:610:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:614:9:614:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:615:9:615:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:619:9:619:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:620:9:620:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:624:9:624:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:625:9:625:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:629:10:629:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:630:10:630:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:634:10:634:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:635:10:635:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:640:3:640:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:641:3:641:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:645:3:645:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:646:3:646:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:650:3:650:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:651:3:651:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:663:8:663:12 | 42.0 | Assignment between incompatible numeric types from 'float' to 'int32_t'. | +| test.cpp:678:8:678:10 | 42 | Assignment between incompatible numeric types from 'long' to 'int32_t'. | +| test.cpp:679:8:679:11 | 42 | Assignment between incompatible numeric types from 'long long' to 'int32_t'. | +| test.cpp:680:8:680:10 | 42 | Assignment between incompatible numeric types from 'unsigned int' to 'int32_t'. | diff --git a/cpp/misra/test/rules/RULE-7-0-6/test.cpp b/cpp/misra/test/rules/RULE-7-0-6/test.cpp index dc4faf5ef..7747a1130 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/test.cpp +++ b/cpp/misra/test/rules/RULE-7-0-6/test.cpp @@ -384,4 +384,298 @@ void test_compound_assignments() { l2 >>= 1; // COMPLIANT - compound assignment, rule does not apply l4 += l1; // COMPLIANT - compound assignment, rule does not apply l4 -= s32; // COMPLIANT - compound assignment, rule does not apply +} + +// Test user-defined operators - always non-extensible +struct UserDefinedOperators { + UserDefinedOperators(std::int32_t l1) {} + + // Binary operators + UserDefinedOperators operator+(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator-(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator*(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator/(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator%(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator&(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator|(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator^(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator<<(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator>>(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + + // Comparison operators + bool operator==(std::int32_t l1) const { return true; } + bool operator!=(std::int32_t l1) const { return false; } + bool operator<(std::int32_t l1) const { return false; } + bool operator<=(std::int32_t l1) const { return false; } + bool operator>(std::int32_t l1) const { return false; } + bool operator>=(std::int32_t l1) const { return false; } + + // Subscript operator + std::int32_t operator[](std::int32_t l1) const { return 0; } + + // Function call operator + std::int32_t operator()(std::int32_t l1) const { return 0; } + std::int32_t operator()(std::int32_t l1, std::int32_t l2) const { return 0; } + + // Assignment operators + UserDefinedOperators &operator=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator+=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator-=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator*=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator/=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator%=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator&=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator|=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator^=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator<<=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator>>=(std::int32_t l1) { return *this; } + + // Increment/decrement operators + UserDefinedOperators &operator++() { return *this; } + UserDefinedOperators operator++(int) { return UserDefinedOperators{0}; } + UserDefinedOperators &operator--() { return *this; } + UserDefinedOperators operator--(int) { return UserDefinedOperators{0}; } +}; + +// Global user-defined operators +UserDefinedOperators operator+(std::int32_t l1, + const UserDefinedOperators &l2) { + return UserDefinedOperators{0}; +} + +UserDefinedOperators operator-(std::int32_t l1, + const UserDefinedOperators &l2) { + return UserDefinedOperators{0}; +} + +bool operator==(std::int32_t l1, const UserDefinedOperators &l2) { + return true; +} + +void test_user_defined_operators() { + UserDefinedOperators l1{42}; + std::int32_t l2 = 10; + std::int16_t l3 = 5; + std::int64_t l4 = 100; + std::uint32_t l5 = 20; + + // Member operators - non-extensible, exact type match required + l1 + l2; // COMPLIANT - exact type match + l1 + l3; // COMPLIANT - widening conversion is allowed + l1 + l4; // NON_COMPLIANT - different type + l1 + l5; // NON_COMPLIANT - different signedness + + l1 - l2; // COMPLIANT - exact type match + l1 - l3; // COMPLIANT - widening conversion is allowed + l1 - l4; // NON_COMPLIANT - different type + l1 - l5; // NON_COMPLIANT - different signedness + + l1 *l2; // COMPLIANT - exact type match + l1 *l3; // COMPLIANT - widening conversion is allowed + l1 *l4; // NON_COMPLIANT - different type + l1 *l5; // NON_COMPLIANT - different signedness + + l1 / l2; // COMPLIANT - exact type match + l1 / l3; // COMPLIANT - widening conversion is allowed + l1 / l4; // NON_COMPLIANT - different type + l1 / l5; // NON_COMPLIANT - different signedness + + l1 % l2; // COMPLIANT - exact type match + l1 % l3; // COMPLIANT - widening conversion is allowed + l1 % l4; // NON_COMPLIANT - different type + l1 % l5; // NON_COMPLIANT - different signedness + + l1 & l2; // COMPLIANT - exact type match + l1 & l3; // COMPLIANT - widening conversion is allowed + l1 & l4; // NON_COMPLIANT - different type + l1 & l5; // NON_COMPLIANT - different signedness + + l1 | l2; // COMPLIANT - exact type match + l1 | l3; // COMPLIANT - widening conversion is allowed + l1 | l4; // NON_COMPLIANT - different type + l1 | l5; // NON_COMPLIANT - different signedness + + l1 ^ l2; // COMPLIANT - exact type match + l1 ^ l3; // COMPLIANT - widening conversion is allowed + l1 ^ l4; // NON_COMPLIANT - different type + l1 ^ l5; // NON_COMPLIANT - different signedness + + l1 << l2; // COMPLIANT - exact type match + l1 << l3; // COMPLIANT - widening conversion is allowed + l1 << l4; // NON_COMPLIANT - different type + l1 << l5; // NON_COMPLIANT - different signedness + + l1 >> l2; // COMPLIANT - exact type match + l1 >> l3; // COMPLIANT - widening conversion is allowed + l1 >> l4; // NON_COMPLIANT - different type + l1 >> l5; // NON_COMPLIANT - different signedness + + // Comparison operators + l1 == l2; // COMPLIANT - exact type match + l1 == l3; // COMPLIANT - widening conversion is allowed + l1 == l4; // NON_COMPLIANT - different type + l1 == l5; // NON_COMPLIANT - different signedness + + l1 != l2; // COMPLIANT - exact type match + l1 != l3; // COMPLIANT - widening conversion is allowed + l1 != l4; // NON_COMPLIANT - different type + l1 != l5; // NON_COMPLIANT - different signedness + + l1 < l2; // COMPLIANT - exact type match + l1 < l3; // COMPLIANT - widening conversion is allowed + l1 < l4; // NON_COMPLIANT - different type + l1 < l5; // NON_COMPLIANT - different signedness + + l1 <= l2; // COMPLIANT - exact type match + l1 <= l3; // COMPLIANT - widening conversion is allowed + l1 <= l4; // NON_COMPLIANT - different type + l1 <= l5; // NON_COMPLIANT + + l1 > l2; // COMPLIANT - exact type match + l1 > l3; // COMPLIANT - widening conversion is allowed + l1 > l4; // NON_COMPLIANT + l1 > l5; // NON_COMPLIANT - different signedness + + l1 >= l2; // COMPLIANT - exact type match + l1 >= l3; // COMPLIANT - widening conversion is allowed + l1 >= l4; // NON_COMPLIANT + l1 >= l5; // NON_COMPLIANT - different signedness + + // Subscript operator + l1[l2]; // COMPLIANT - exact type match + l1[l3]; // COMPLIANT - widening conversion is allowed + l1[l4]; // NON_COMPLIANT - different type + l1[l5]; // NON_COMPLIANT - different signedness + + // Function call operator + l1(l2); // COMPLIANT - exact type match + l1(l3); // COMPLIANT - widening conversion is allowed + l1(l4); // NON_COMPLIANT - different type + l1(l5); // NON_COMPLIANT - different signedness + l1(l2, l2); // COMPLIANT - both exact type match + l1(l2, l4); // NON_COMPLIANT - second parameter different type + l1(l4, l2); // NON_COMPLIANT - first parameter different type + l1(l4, l5); // NON_COMPLIANT - both parameters different type + + // The presence of a default copy constructor for UserDefinedOperators means + // that assignments through operator= must be exact type matches. + l1 = l2; // COMPLIANT - exact type match + l1 = l3; // NON_COMPLIANT + l1 = l4; // NON_COMPLIANT + l1 = l5; // NON_COMPLIANT + + l1 += l2; // COMPLIANT - exact type match + l1 += l3; // COMPLIANT - widening conversion is allowed + l1 += l4; // NON_COMPLIANT - different type + l1 += l5; // NON_COMPLIANT - different signedness + + l1 -= l2; // COMPLIANT - exact type match + l1 -= l3; // COMPLIANT - widening conversion is allowed + l1 -= l4; // NON_COMPLIANT - different type + l1 -= l5; // NON_COMPLIANT - different signedness + + l1 *= l2; // COMPLIANT - exact type match + l1 *= l3; // COMPLIANT - widening conversion is allowed + l1 *= l4; // NON_COMPLIANT - different type + l1 *= l5; // NON_COMPLIANT - different signedness + + l1 /= l2; // COMPLIANT - exact type match + l1 /= l3; // COMPLIANT - widening conversion is allowed + l1 /= l4; // NON_COMPLIANT - different type + l1 /= l5; // NON_COMPLIANT - different signedness + + l1 %= l2; // COMPLIANT - exact type match + l1 %= l3; // COMPLIANT - widening conversion is allowed + l1 %= l4; // NON_COMPLIANT - different type + l1 %= l5; // NON_COMPLIANT - different signedness + + l1 &= l2; // COMPLIANT - exact type match + l1 &= l3; // COMPLIANT - widening conversion is allowed + l1 &= l4; // NON_COMPLIANT - different type + l1 &= l5; // NON_COMPLIANT - different signedness + + l1 |= l2; // COMPLIANT - exact type match + l1 |= l3; // COMPLIANT - widening conversion is allowed + l1 |= l4; // NON_COMPLIANT - different type + l1 |= l5; // NON_COMPLIANT - different signedness + + l1 ^= l2; // COMPLIANT - exact type match + l1 ^= l3; // COMPLIANT - widening conversion is allowed + l1 ^= l4; // NON_COMPLIANT - different type + l1 ^= l5; // NON_COMPLIANT - different signedness + + l1 <<= l2; // COMPLIANT - exact type match + l1 <<= l3; // COMPLIANT - widening conversion is allowed + l1 <<= l4; // NON_COMPLIANT - different type + l1 <<= l5; // NON_COMPLIANT - different signedness + + l1 >>= l2; // COMPLIANT - exact type match + l1 >>= l3; // COMPLIANT - widening conversion is allowed + l1 >>= l4; // NON_COMPLIANT - different type + l1 >>= l5; // NON_COMPLIANT - different signedness + + // Global operators + l2 + l1; // COMPLIANT - exact type match + l3 + l1; // COMPLIANT - widening conversion is allowed + l4 + l1; // NON_COMPLIANT - different type + l5 + l1; // NON_COMPLIANT - different signedness + + l2 - l1; // COMPLIANT - exact type match + l3 - l1; // COMPLIANT - widening conversion is allowed + l4 - l1; // NON_COMPLIANT - different type + l5 - l1; // NON_COMPLIANT - different signedness + + l2 == l1; // COMPLIANT - exact type match + l3 == l1; // COMPLIANT - widening conversion is allowed + l4 == l1; // NON_COMPLIANT - different type + l5 == l1; // NON_COMPLIANT - different signedness +} + +// Test user-defined operators with constants +void test_user_defined_operators_constants() { + UserDefinedOperators l1{42}; + + // Constants with exact type match + l1 + 42; // COMPLIANT + l1 + 42L; // COMPLIANT + l1 + 42LL; // COMPLIANT + l1 + 42U; // COMPLIANT + l1 + 42.0f; // NON_COMPLIANT - float constant + + l1 == 42; // COMPLIANT - integer constant is int/int32_t + l1 == 42L; // COMPLIANT - long constant + l1 == 42LL; // COMPLIANT - long long constant + l1 == 42U; // COMPLIANT - unsigned constant + + l1[42]; // COMPLIANT - integer constant is int/int32_t + l1[42L]; // COMPLIANT - long constant + l1[42LL]; // COMPLIANT - long long constant + l1[42U]; // COMPLIANT - unsigned constant + + // The presence of a default copy constructor for UserDefinedOperators means + // that assignments through operator= must be exact type matches. + l1 = 42; // COMPLIANT - integer constant is int/int32_t + l1 = 42L; // NON_COMPLIANT + l1 = 42LL; // NON_COMPLIANT + l1 = 42U; // NON_COMPLIANT } \ No newline at end of file From af2ff95dc40fe1eefe01104d1f973b22f10fc4fb Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Tue, 17 Jun 2025 22:30:38 +0100 Subject: [PATCH 14/57] MISRA C++ 2023: Create StandardConversions library Migrate conversion generic code to a shared library. --- .../cpp/misra/StandardConversions.qll | 125 ++++++++++++++++++ .../NumericAssignmentTypeMismatch.ql | 124 +---------------- 2 files changed, 126 insertions(+), 123 deletions(-) create mode 100644 cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll diff --git a/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll b/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll new file mode 100644 index 000000000..34de2e36a --- /dev/null +++ b/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll @@ -0,0 +1,125 @@ +import cpp +import codingstandards.cpp.misra + +/** + * The signedness of a numeric type. + */ +newtype Signedness = + Signed() or + Unsigned() + +/** + * The type category of a numeric type - either integral or floating-point. + */ +newtype TypeCategory = + Integral() or + FloatingPoint() + +/** + * A numeric type is a type that represents a number, either an integral or a floating-point. + * + * In addition to the basic integral and floating-point types, it includes: + * - Enum types with an explicit underlying type that is a numeric type. + * - Typedef'd types that are numeric types. + * - Numeric types with specifiers (e.g., `const`, `volatile`, `restrict`). + */ +class NumericType extends Type { + Type realType; + + NumericType() { + realType = this.getUnspecifiedType().(ReferenceType).getBaseType().(NumericType).getRealType() or + realType = this.getUnspecifiedType().(IntegralType) or + realType = this.getUnspecifiedType().(FloatingPointType) or + realType = this.getUnspecifiedType().(Enum).getExplicitUnderlyingType().getUnspecifiedType() + } + + Signedness getSignedness() { + if realType.(IntegralType).isUnsigned() then result = Unsigned() else result = Signed() + } + + /** Gets the size of the actual numeric type */ + int getRealSize() { result = realType.getSize() } + + TypeCategory getTypeCategory() { + realType instanceof IntegralType and result = Integral() + or + realType instanceof FloatingPointType and result = FloatingPoint() + } + + float getUpperBound() { result = typeUpperBound(realType) } + + float getLowerBound() { result = typeLowerBound(realType) } + + Type getRealType() { result = realType } +} + +predicate isAssignment(Expr source, NumericType targetType, string context) { + // Assignment operator (but not compound assignment) + exists(AssignExpr assign | + assign.getRValue() = source and + context = "assignment" + | + // TODO generalize to variable init (do we need this for bitfields?) and extract + if isAssignedToBitfield(source, _) + then + exists(BitField bf | + isAssignedToBitfield(source, bf) and + // TODO integral after numeric? + targetType.(IntegralType).(NumericType).getSignedness() = + bf.getType().(NumericType).getSignedness() and + // smallest integral type that can hold the bit field value + targetType.getRealSize() * 8 >= bf.getNumBits() and + not exists(IntegralType other | + other.getSize() * 8 >= bf.getNumBits() and + other.(NumericType).getSignedness() = targetType.getSignedness() and + other.getSize() < targetType.getRealSize() + ) + ) + else targetType = assign.getLValue().getType() + ) + or + // Variable initialization + exists(Variable v, Initializer init | + init.getExpr() = source and + v.getInitializer() = init and + targetType = v.getType() and + context = "initialization" + ) + or + // Passing a function parameter by value + exists(Call call, int i | + call.getArgument(i) = source and + not targetType.stripTopLevelSpecifiers() instanceof ReferenceType and + context = "function argument" + | + targetType = call.getTarget().getParameter(i).getType() + or + // Handle varargs - use the fully converted type of the argument + call.getTarget().getNumberOfParameters() <= i and + targetType = source.getFullyConverted().getType() + ) + or + // Return statement + exists(ReturnStmt ret, Function f | + ret.getExpr() = source and + ret.getEnclosingFunction() = f and + targetType = f.getType() and + not targetType.stripTopLevelSpecifiers() instanceof ReferenceType and + context = "return" + ) + or + // Switch case + exists(SwitchCase case, SwitchStmt switch | + case.getExpr() = source and + case.getSwitchStmt() = switch and + targetType = switch.getExpr().getFullyConverted().getType() and + context = "switch case" + ) +} + +predicate isAssignedToBitfield(Expr source, BitField bf) { + exists(Assignment assign | + assign.getRValue() = source and + assign.getLValue() = bf.getAnAccess() + ) +} diff --git a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql index 04d454896..e8f65ed00 100644 --- a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql +++ b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql @@ -15,129 +15,7 @@ import cpp import codingstandards.cpp.misra -import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis - -/** - * The signedness of a numeric type. - */ -newtype Signedness = - Signed() or - Unsigned() - -/** - * The type category of a numeric type - either integral or floating-point. - */ -newtype TypeCategory = - Integral() or - FloatingPoint() - -/** - * A numeric type is a type that represents a number, either an integral or a floating-point. - * - * In addition to the basic integral and floating-point types, it includes: - * - Enum types with an explicit underlying type that is a numeric type. - * - Typedef'd types that are numeric types. - * - Numeric types with specifiers (e.g., `const`, `volatile`, `restrict`). - */ -class NumericType extends Type { - Type realType; - - NumericType() { - realType = this.getUnspecifiedType().(ReferenceType).getBaseType().(NumericType).getRealType() or - realType = this.getUnspecifiedType().(IntegralType) or - realType = this.getUnspecifiedType().(FloatingPointType) or - realType = this.getUnspecifiedType().(Enum).getExplicitUnderlyingType().getUnspecifiedType() - } - - Signedness getSignedness() { - if realType.(IntegralType).isUnsigned() then result = Unsigned() else result = Signed() - } - - int getRealSize() { result = realType.getSize() } - - TypeCategory getTypeCategory() { - realType instanceof IntegralType and result = Integral() - or - realType instanceof FloatingPointType and result = FloatingPoint() - } - - float getUpperBound() { result = typeUpperBound(realType) } - - float getLowerBound() { result = typeLowerBound(realType) } - - Type getRealType() { result = realType } -} - -predicate isAssignment(Expr source, NumericType targetType, string context) { - // Assignment operator (but not compound assignment) - exists(AssignExpr assign | - assign.getRValue() = source and - context = "assignment" - | - // TODO generalize to variable init (do we need this for bitfields?) and extract - if isAssignedToBitfield(source, _) - then - exists(BitField bf | - isAssignedToBitfield(source, bf) and - // TODO integral after numeric? - targetType.(IntegralType).(NumericType).getSignedness() = - bf.getType().(NumericType).getSignedness() and - // smallest integral type that can hold the bit field value - targetType.getRealSize() * 8 >= bf.getNumBits() and - not exists(IntegralType other | - other.getSize() * 8 >= bf.getNumBits() and - other.(NumericType).getSignedness() = targetType.getSignedness() and - other.getSize() < targetType.getRealSize() - ) - ) - else targetType = assign.getLValue().getType() - ) - or - // Variable initialization - exists(Variable v, Initializer init | - init.getExpr() = source and - v.getInitializer() = init and - targetType = v.getType() and - context = "initialization" - ) - or - // Passing a function parameter by value - exists(Call call, int i | - call.getArgument(i) = source and - not targetType.stripTopLevelSpecifiers() instanceof ReferenceType and - context = "function argument" - | - targetType = call.getTarget().getParameter(i).getType() - or - // Handle varargs - use the fully converted type of the argument - call.getTarget().getNumberOfParameters() <= i and - targetType = source.getFullyConverted().getType() - ) - or - // Return statement - exists(ReturnStmt ret, Function f | - ret.getExpr() = source and - ret.getEnclosingFunction() = f and - targetType = f.getType() and - not targetType.stripTopLevelSpecifiers() instanceof ReferenceType and - context = "return" - ) - or - // Switch case - exists(SwitchCase case, SwitchStmt switch | - case.getExpr() = source and - case.getSwitchStmt() = switch and - targetType = switch.getExpr().getFullyConverted().getType() and - context = "switch case" - ) -} - -predicate isAssignedToBitfield(Expr source, BitField bf) { - exists(Assignment assign | - assign.getRValue() = source and - assign.getLValue() = bf.getAnAccess() - ) -} +import codingstandards.cpp.misra.StandardConversions predicate isValidConstantAssignment(Expr source, NumericType targetType) { isAssignment(source, targetType, _) and From ea7d168c2fa025cfa248f60a9b9917360aaf1e5b Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Tue, 17 Jun 2025 22:42:40 +0100 Subject: [PATCH 15/57] MISRA C++ StandardConversions - improve detection of bitfield types --- .../cpp/misra/StandardConversions.qll | 47 ++++++++++++++----- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll b/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll index 34de2e36a..944f90f27 100644 --- a/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll +++ b/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll @@ -54,26 +54,17 @@ class NumericType extends Type { } predicate isAssignment(Expr source, NumericType targetType, string context) { - // Assignment operator (but not compound assignment) + // Assignment expression (which excludes compound assignments) exists(AssignExpr assign | assign.getRValue() = source and context = "assignment" | - // TODO generalize to variable init (do we need this for bitfields?) and extract if isAssignedToBitfield(source, _) then + // For the MISRA type rules we treat bit fields as a special case exists(BitField bf | isAssignedToBitfield(source, bf) and - // TODO integral after numeric? - targetType.(IntegralType).(NumericType).getSignedness() = - bf.getType().(NumericType).getSignedness() and - // smallest integral type that can hold the bit field value - targetType.getRealSize() * 8 >= bf.getNumBits() and - not exists(IntegralType other | - other.getSize() * 8 >= bf.getNumBits() and - other.(NumericType).getSignedness() = targetType.getSignedness() and - other.getSize() < targetType.getRealSize() - ) + targetType = getBitFieldType(bf) ) else targetType = assign.getLValue().getType() ) @@ -82,8 +73,14 @@ predicate isAssignment(Expr source, NumericType targetType, string context) { exists(Variable v, Initializer init | init.getExpr() = source and v.getInitializer() = init and - targetType = v.getType() and context = "initialization" + | + // For the MISRA type rules we treat bit fields as a special case + if v instanceof BitField + then targetType = getBitFieldType(v) + else + // Regular variable initialization + targetType = v.getType() ) or // Passing a function parameter by value @@ -117,6 +114,30 @@ predicate isAssignment(Expr source, NumericType targetType, string context) { ) } +/** + * Gets the smallest integral type that can hold the value of a bit field. + * + * The type is determined by the signedness of the bit field and the number of bits. + */ +NumericType getBitFieldType(BitField bf) { + exists(NumericType bitfieldActualType | + bitfieldActualType = bf.getType() and + // Integral type with the same signedness as the bit field, and big enough to hold the bit field value + result instanceof IntegralType and + result.getSignedness() = bitfieldActualType.getSignedness() and + result.getSize() * 8 >= bf.getNumBits() and + // No smaller integral type can hold the bit field value + not exists(IntegralType other | + other.getSize() * 8 >= bf.getNumBits() and + other.(NumericType).getSignedness() = result.getSignedness() and + other.getSize() < result.getRealSize() + ) + ) +} + +/** + * Holds if the `source` expression is assigned to a bit field. + */ predicate isAssignedToBitfield(Expr source, BitField bf) { exists(Assignment assign | assign.getRValue() = source and From 58432583654ee143b0e8f00de7f65c703fcd357c Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Wed, 18 Jun 2025 09:27:37 +0100 Subject: [PATCH 16/57] Rule 7.0.6: Improve bitfield support - Add extra testing - Support signed bitfields --- .../NumericAssignmentTypeMismatch.ql | 12 +- .../NumericAssignmentTypeMismatch.expected | 240 +++++++++++------- cpp/misra/test/rules/RULE-7-0-6/test.cpp | 100 +++++++- 3 files changed, 246 insertions(+), 106 deletions(-) diff --git a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql index e8f65ed00..23409abcb 100644 --- a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql +++ b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql @@ -27,8 +27,16 @@ predicate isValidConstantAssignment(Expr source, NumericType targetType) { exists(BitField bf, int numBits | isAssignedToBitfield(source, bf) and numBits = bf.getNumBits() and - val >= 0 and - val < 2.pow(numBits) + if targetType.getSignedness() = Signed() + then + // Signed bit field: value must be in the range of signed bit field + val >= -2.pow(numBits - 1) and + val < 2.pow(numBits - 1) + else ( + // Unsigned bit field: value must be in the range of unsigned bit field + val >= 0 and + val < 2.pow(numBits) + ) ) or // Regular assignment: check if the value fits in the target type range diff --git a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected index 75cdf0c69..4159b78b2 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected +++ b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected @@ -15,103 +15,147 @@ | test.cpp:105:35:105:36 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | | test.cpp:110:8:110:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | | test.cpp:111:21:111:27 | ... + ... | Assignment between incompatible numeric types from 'int' to 'int16_t'. | -| test.cpp:122:11:122:13 | 32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned char'. | -| test.cpp:124:11:124:13 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | -| test.cpp:136:8:136:9 | l1 | Assignment between incompatible numeric types from 'Colour' to 'uint8_t'. | -| test.cpp:146:6:146:8 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'int64_t'. | -| test.cpp:149:6:149:8 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:151:6:151:7 | l1 | Assignment between incompatible numeric types from 'int' to 'int32_t'. | -| test.cpp:164:6:164:7 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'int32_t'. | -| test.cpp:173:6:173:6 | 2 | Assignment between incompatible numeric types from 'int' to 'long'. | -| test.cpp:181:14:181:15 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'int'. | -| test.cpp:193:6:193:8 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'size_t'. | -| test.cpp:215:9:215:10 | 42 | Assignment between incompatible numeric types from 'int' to 'long'. | -| test.cpp:244:23:244:24 | 42 | Assignment between incompatible numeric types from 'int' to 'unsigned long'. | -| test.cpp:254:19:254:25 | ... + ... | Assignment between incompatible numeric types from 'int' to 'int16_t'. | -| test.cpp:259:10:259:12 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:292:8:292:9 | l6 | Assignment between incompatible numeric types from 'uint32_t &' to 'uint8_t'. | -| test.cpp:293:8:293:9 | l7 | Assignment between incompatible numeric types from 'int8_t &' to 'uint8_t'. | -| test.cpp:294:9:294:10 | l8 | Assignment between incompatible numeric types from 'float &' to 'int32_t'. | -| test.cpp:305:6:305:7 | l3 | Assignment between incompatible numeric types from 'uint8_t &' to 'int64_t'. | -| test.cpp:306:6:306:7 | l4 | Assignment between incompatible numeric types from 'uint16_t &' to 'int32_t'. | -| test.cpp:317:8:317:9 | l3 | Assignment between incompatible numeric types from 'uint8_t &' to 'int8_t'. | -| test.cpp:318:8:318:9 | l4 | Assignment between incompatible numeric types from 'int8_t &' to 'uint8_t'. | -| test.cpp:331:9:331:10 | l4 | Assignment between incompatible numeric types from 'float &' to 'int32_t'. | -| test.cpp:332:7:332:8 | l5 | Assignment between incompatible numeric types from 'double &' to 'float'. | -| test.cpp:333:7:333:8 | l6 | Assignment between incompatible numeric types from 'int32_t &' to 'float'. | -| test.cpp:344:8:344:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | -| test.cpp:362:7:362:8 | l3 | Assignment between incompatible numeric types from 'uint16_t &' to 'uint32_t'. | -| test.cpp:365:7:365:8 | l5 | Assignment between incompatible numeric types from 'uint64_t &' to 'uint32_t'. | -| test.cpp:485:8:485:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:486:8:486:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:490:8:490:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:491:8:491:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:495:7:495:8 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:496:7:496:8 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:500:8:500:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:501:8:501:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:505:8:505:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:506:8:506:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:510:8:510:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:511:8:511:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:515:8:515:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:516:8:516:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:520:8:520:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:521:8:521:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:525:9:525:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:526:9:526:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:530:9:530:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:531:9:531:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:536:9:536:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:537:9:537:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:541:9:541:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:542:9:542:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:546:8:546:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:547:8:547:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:551:9:551:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:552:9:552:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:556:8:556:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:557:8:557:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:561:9:561:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:562:9:562:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:567:6:567:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:568:6:568:7 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:573:6:573:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:574:6:574:7 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:576:10:576:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:577:6:577:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:578:6:578:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:578:10:578:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:583:8:583:9 | l3 | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | -| test.cpp:584:8:584:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:585:8:585:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:589:9:589:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:590:9:590:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:594:9:594:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:595:9:595:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:599:9:599:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:600:9:600:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:604:9:604:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:605:9:605:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:609:9:609:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:610:9:610:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:614:9:614:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:615:9:615:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:619:9:619:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:620:9:620:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:134:11:134:11 | 4 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | +| test.cpp:137:11:137:13 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test.cpp:141:11:141:13 | 256 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | +| test.cpp:143:11:143:13 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test.cpp:144:11:144:13 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned char'. | +| test.cpp:148:11:148:13 | 512 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | +| test.cpp:149:11:149:15 | 65535 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | +| test.cpp:150:11:150:15 | 65536 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | +| test.cpp:153:11:153:13 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned short'. | +| test.cpp:157:11:157:15 | 65536 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | +| test.cpp:160:11:160:13 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned short'. | +| test.cpp:164:11:164:16 | 131072 | Assignment between incompatible numeric types from 'int' to 'unsigned int'. | +| test.cpp:168:11:168:13 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'unsigned int'. | +| test.cpp:172:11:172:22 | 4294967296 | Assignment between incompatible numeric types from 'unsigned long' to 'unsigned int'. | +| test.cpp:176:11:176:13 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'unsigned int'. | +| test.cpp:180:11:180:21 | 8589934592 | Assignment between incompatible numeric types from 'long' to 'unsigned long long'. | +| test.cpp:180:11:180:21 | 8589934592 | Assignment between incompatible numeric types from 'long' to 'unsigned long'. | +| test.cpp:193:11:193:12 | 16 | Assignment between incompatible numeric types from 'int32_t' to 'bool'. | +| test.cpp:193:11:193:12 | 16 | Assignment between incompatible numeric types from 'int32_t' to 'char8_t'. | +| test.cpp:193:11:193:12 | 16 | Assignment between incompatible numeric types from 'int32_t' to 'char'. | +| test.cpp:193:11:193:12 | 16 | Assignment between incompatible numeric types from 'int32_t' to 'signed char'. | +| test.cpp:194:11:194:13 | - ... | Assignment between incompatible numeric types from 'int' to 'bool'. | +| test.cpp:194:11:194:13 | - ... | Assignment between incompatible numeric types from 'int' to 'char8_t'. | +| test.cpp:194:11:194:13 | - ... | Assignment between incompatible numeric types from 'int' to 'char'. | +| test.cpp:194:11:194:13 | - ... | Assignment between incompatible numeric types from 'int' to 'signed char'. | +| test.cpp:196:11:196:13 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'bool'. | +| test.cpp:196:11:196:13 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'char8_t'. | +| test.cpp:196:11:196:13 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'char'. | +| test.cpp:196:11:196:13 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'signed char'. | +| test.cpp:200:11:200:14 | 2048 | Assignment between incompatible numeric types from 'int32_t' to 'char16_t'. | +| test.cpp:200:11:200:14 | 2048 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | +| test.cpp:200:11:200:14 | 2048 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | +| test.cpp:201:11:201:15 | - ... | Assignment between incompatible numeric types from 'int' to 'char16_t'. | +| test.cpp:201:11:201:15 | - ... | Assignment between incompatible numeric types from 'int' to 'short'. | +| test.cpp:201:11:201:15 | - ... | Assignment between incompatible numeric types from 'int' to 'signed short'. | +| test.cpp:204:11:204:13 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'char16_t'. | +| test.cpp:204:11:204:13 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | +| test.cpp:204:11:204:13 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | +| test.cpp:208:12:208:22 | 134217728 | Assignment between incompatible numeric types from 'long long' to 'char32_t'. | +| test.cpp:208:12:208:22 | 134217728 | Assignment between incompatible numeric types from 'long long' to 'int'. | +| test.cpp:208:12:208:22 | 134217728 | Assignment between incompatible numeric types from 'long long' to 'signed int'. | +| test.cpp:208:12:208:22 | 134217728 | Assignment between incompatible numeric types from 'long long' to 'wchar_t'. | +| test.cpp:209:12:209:23 | - ... | Assignment between incompatible numeric types from 'long long' to 'char32_t'. | +| test.cpp:209:12:209:23 | - ... | Assignment between incompatible numeric types from 'long long' to 'int'. | +| test.cpp:209:12:209:23 | - ... | Assignment between incompatible numeric types from 'long long' to 'signed int'. | +| test.cpp:209:12:209:23 | - ... | Assignment between incompatible numeric types from 'long long' to 'wchar_t'. | +| test.cpp:224:8:224:9 | l1 | Assignment between incompatible numeric types from 'Colour' to 'uint8_t'. | +| test.cpp:234:6:234:8 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'int64_t'. | +| test.cpp:237:6:237:8 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:239:6:239:7 | l1 | Assignment between incompatible numeric types from 'int' to 'int32_t'. | +| test.cpp:252:6:252:7 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'int32_t'. | +| test.cpp:261:6:261:6 | 2 | Assignment between incompatible numeric types from 'int' to 'long'. | +| test.cpp:269:14:269:15 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'int'. | +| test.cpp:281:6:281:8 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'size_t'. | +| test.cpp:303:9:303:10 | 42 | Assignment between incompatible numeric types from 'int' to 'long'. | +| test.cpp:332:23:332:24 | 42 | Assignment between incompatible numeric types from 'int' to 'unsigned long'. | +| test.cpp:342:19:342:25 | ... + ... | Assignment between incompatible numeric types from 'int' to 'int16_t'. | +| test.cpp:347:10:347:12 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:380:8:380:9 | l6 | Assignment between incompatible numeric types from 'uint32_t &' to 'uint8_t'. | +| test.cpp:381:8:381:9 | l7 | Assignment between incompatible numeric types from 'int8_t &' to 'uint8_t'. | +| test.cpp:382:9:382:10 | l8 | Assignment between incompatible numeric types from 'float &' to 'int32_t'. | +| test.cpp:393:6:393:7 | l3 | Assignment between incompatible numeric types from 'uint8_t &' to 'int64_t'. | +| test.cpp:394:6:394:7 | l4 | Assignment between incompatible numeric types from 'uint16_t &' to 'int32_t'. | +| test.cpp:405:8:405:9 | l3 | Assignment between incompatible numeric types from 'uint8_t &' to 'int8_t'. | +| test.cpp:406:8:406:9 | l4 | Assignment between incompatible numeric types from 'int8_t &' to 'uint8_t'. | +| test.cpp:419:9:419:10 | l4 | Assignment between incompatible numeric types from 'float &' to 'int32_t'. | +| test.cpp:420:7:420:8 | l5 | Assignment between incompatible numeric types from 'double &' to 'float'. | +| test.cpp:421:7:421:8 | l6 | Assignment between incompatible numeric types from 'int32_t &' to 'float'. | +| test.cpp:432:8:432:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test.cpp:450:7:450:8 | l3 | Assignment between incompatible numeric types from 'uint16_t &' to 'uint32_t'. | +| test.cpp:453:7:453:8 | l5 | Assignment between incompatible numeric types from 'uint64_t &' to 'uint32_t'. | +| test.cpp:573:8:573:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:574:8:574:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:578:8:578:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:579:8:579:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:583:7:583:8 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:584:7:584:8 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:588:8:588:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:589:8:589:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:593:8:593:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:594:8:594:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:598:8:598:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:599:8:599:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:603:8:603:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:604:8:604:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:608:8:608:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:609:8:609:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:613:9:613:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:614:9:614:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:618:9:618:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:619:9:619:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | | test.cpp:624:9:624:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | | test.cpp:625:9:625:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:629:10:629:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:630:10:630:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:634:10:634:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:635:10:635:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:640:3:640:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:641:3:641:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:645:3:645:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:646:3:646:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:650:3:650:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:651:3:651:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:663:8:663:12 | 42.0 | Assignment between incompatible numeric types from 'float' to 'int32_t'. | -| test.cpp:678:8:678:10 | 42 | Assignment between incompatible numeric types from 'long' to 'int32_t'. | -| test.cpp:679:8:679:11 | 42 | Assignment between incompatible numeric types from 'long long' to 'int32_t'. | -| test.cpp:680:8:680:10 | 42 | Assignment between incompatible numeric types from 'unsigned int' to 'int32_t'. | +| test.cpp:629:9:629:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:630:9:630:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:634:8:634:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:635:8:635:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:639:9:639:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:640:9:640:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:644:8:644:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:645:8:645:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:649:9:649:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:650:9:650:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:655:6:655:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:656:6:656:7 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:661:6:661:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:662:6:662:7 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:664:10:664:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:665:6:665:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:666:6:666:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:666:10:666:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:671:8:671:9 | l3 | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | +| test.cpp:672:8:672:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:673:8:673:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:677:9:677:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:678:9:678:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:682:9:682:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:683:9:683:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:687:9:687:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:688:9:688:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:692:9:692:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:693:9:693:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:697:9:697:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:698:9:698:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:702:9:702:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:703:9:703:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:707:9:707:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:708:9:708:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:712:9:712:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:713:9:713:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:717:10:717:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:718:10:718:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:722:10:722:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:723:10:723:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:728:3:728:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:729:3:729:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:733:3:733:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:734:3:734:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:738:3:738:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:739:3:739:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:751:8:751:12 | 42.0 | Assignment between incompatible numeric types from 'float' to 'int32_t'. | +| test.cpp:766:8:766:10 | 42 | Assignment between incompatible numeric types from 'long' to 'int32_t'. | +| test.cpp:767:8:767:11 | 42 | Assignment between incompatible numeric types from 'long long' to 'int32_t'. | +| test.cpp:768:8:768:10 | 42 | Assignment between incompatible numeric types from 'unsigned int' to 'int32_t'. | diff --git a/cpp/misra/test/rules/RULE-7-0-6/test.cpp b/cpp/misra/test/rules/RULE-7-0-6/test.cpp index 7747a1130..740129a4b 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/test.cpp +++ b/cpp/misra/test/rules/RULE-7-0-6/test.cpp @@ -111,17 +111,105 @@ void test_expression_results() { std::int16_t l1 = s8 + s8; // NON_COMPLIANT } -// Test bit-fields +// Test bit-fields with various sizes around boundaries struct S { - std::uint32_t m1 : 2; + std::uint32_t m1 : 2; // 2-bit field + std::uint32_t m2 : 8; // 8-bit field (boundary) + std::uint32_t m3 : 9; // 9-bit field (boundary + 1) + std::uint32_t m4 : 16; // 16-bit field (boundary) + std::uint32_t m5 : 17; // 17-bit field (boundary + 1) + std::uint32_t m6 : 32; // 32-bit field (boundary) + std::uint64_t m7 : 33; // 33-bit field (boundary + 1) + std::int32_t m8 : 5; // Signed 5-bit field + std::int32_t m9 : 12; // Signed 12-bit field + std::int32_t m10 : 28; // Signed 28-bit field }; void test_bitfields() { S l1; - l1.m1 = 2; // COMPLIANT - l1.m1 = 32u; // NON_COMPLIANT - l1.m1 = u8; // COMPLIANT - l1.m1 = u16; // NON_COMPLIANT + + // 2-bit field tests + l1.m1 = 2; // COMPLIANT - value fits in 2 bits + l1.m1 = 3; // COMPLIANT - value fits in 2 bits + l1.m1 = 4; // NON_COMPLIANT - constant does not fit in 2 bits + + l1.m1 = u8; // COMPLIANT - u8 is fine, not integer constant + l1.m1 = u16; // NON_COMPLIANT - narrowing from uint16_t + + // 8-bit boundary field tests + l1.m2 = 255; // COMPLIANT - value fits in uint8_t + l1.m2 = 256; // NON_COMPLIANT - value does not fit in unint8_t + l1.m2 = u8; // COMPLIANT - same width as uint8_t + l1.m2 = u16; // NON_COMPLIANT - narrowing from uint16_t + l1.m2 = u32; // NON_COMPLIANT - narrowing from uint32_t + + // 9-bit boundary + 1 field tests + l1.m3 = 511; // COMPLIANT + l1.m3 = 512; // NON_COMPLIANT - value does not fit in 9 bits + l1.m3 = 65535; // NON_COMPLIANT - value does not fit in 9 bits + l1.m3 = 65536; // NON_COMPLIANT - value does not fit in 9 bits + l1.m3 = u8; // COMPLIANT - widening from uint8_t to uint16_t + l1.m3 = u16; // COMPLIANT + l1.m3 = u32; // NON_COMPLIANT - narrowing from uint32_t + + // 16-bit boundary field tests + l1.m4 = 65535; // COMPLIANT - value fits in 16 bits + l1.m4 = 65536; // NON_COMPLIANT - value does not fit in 16 bits + l1.m4 = u8; // COMPLIANT - widening from uint8_t + l1.m4 = u16; // COMPLIANT - same width as uint16_t + l1.m4 = u32; // NON_COMPLIANT - narrowing from uint32_t + + // 17-bit boundary + 1 field tests + l1.m5 = 131071; // COMPLIANT - value fits in 17 bits + l1.m5 = 131072; // NON_COMPLIANT - value does not fit in 17 bits + l1.m5 = u8; // COMPLIANT - widening from uint8_t + l1.m5 = u16; // COMPLIANT - widening from uint16_t + l1.m5 = u32; // COMPLIANT + l1.m5 = u64; // NON_COMPLIANT - narrowing from uint64_t + + // 32-bit boundary field tests + l1.m6 = 4294967295U; // COMPLIANT - value fits in 32 bits + l1.m6 = 4294967296UL; // NON_COMPLIANT - value does not fit in 32 bits + l1.m6 = u8; // COMPLIANT - widening from uint8_t + l1.m6 = u16; // COMPLIANT - widening from uint16_t + l1.m6 = u32; // COMPLIANT - same width as uint32_t + l1.m6 = u64; // NON_COMPLIANT - narrowing from uint64_t + + // 33-bit boundary + 1 field tests + l1.m7 = 8589934591ULL; // COMPLIANT + l1.m7 = 8589934592L; // NON_COMPLIANT - value does not fit in 33 bits + l1.m7 = 8589934592ULL; // COMPLIANT - integer constant does not satisfy + // conditions, but the type matches the deduced type of + // the bitfield (unsigned long long), so is considered + // compliant by the rule(!) + l1.m7 = u8; // COMPLIANT - widening from uint8_t + l1.m7 = u16; // COMPLIANT - widening from uint16_t + l1.m7 = u32; // COMPLIANT - widening from uint32_t + l1.m7 = u64; // COMPLIANT - narrowing from uint64_t + + // Signed bitfield tests + l1.m8 = 15; // COMPLIANT + l1.m8 = -16; // COMPLIANT + l1.m8 = 16; // NON_COMPLIANT - value does not fit in signed 5-bit type + l1.m8 = -17; // NON_COMPLIANT - value does not fit in signed 5-bit type + l1.m8 = s8; // COMPLIANT - same width as int8_t + l1.m8 = s16; // NON_COMPLIANT - narrowing from int16_t + + l1.m9 = 2047; // COMPLIANT - value fits in signed 12-bit type + l1.m9 = -2048; // COMPLIANT - value fits in signed 12-bit type + l1.m9 = 2048; // NON_COMPLIANT - value does not fit in signed 12-bit type + l1.m9 = -2049; // NON_COMPLIANT - value does not fit in signed 12-bit type + l1.m9 = s8; // COMPLIANT - widening from int8_t + l1.m9 = s16; // COMPLIANT - same width as int16_t + l1.m9 = s32; // NON_COMPLIANT - narrowing from int32_t + + l1.m10 = 134217727; // COMPLIANT - value fits in signed 28-bit type + l1.m10 = -134217728; // COMPLIANT - value fits in signed 28-bit type + l1.m10 = 134217728LL; // NON_COMPLIANT - does not fit in signed 28-bit type + l1.m10 = -134217729LL; // NON_COMPLIANT - does not fit in signed 28-bit type + l1.m10 = s8; // COMPLIANT - widening from int8_t + l1.m10 = s16; // COMPLIANT - widening from int16_t + l1.m10 = s32; // COMPLIANT } // Test enums From 01b517d3c8b05348871c7b81490ef78b6f713181 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Wed, 18 Jun 2025 10:17:32 +0100 Subject: [PATCH 17/57] StandardConversions: Improve detection of numeric type category - Exclude booleans and chars from our determination of numeric types. - Deduplicate integer types deduced for bitfields - identifying a canonical set of integer types. --- .../cpp/misra/StandardConversions.qll | 108 ++++++++++++++---- .../NumericAssignmentTypeMismatch.expected | 22 ---- 2 files changed, 85 insertions(+), 45 deletions(-) diff --git a/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll b/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll index 944f90f27..287eb4409 100644 --- a/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll +++ b/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll @@ -2,21 +2,65 @@ import cpp import codingstandards.cpp.misra /** - * The signedness of a numeric type. + * A MISRA C++ 2023 type category. */ -newtype Signedness = - Signed() or - Unsigned() +newtype TypeCategory = + Integral() or + FloatingPoint() or + Character() or + Other() /** - * The type category of a numeric type - either integral or floating-point. + * Gets the type category of a built-in type. + * + * This does not apply the rules related to stripping specifiers or typedefs, or references. */ -newtype TypeCategory = - Integral() or - FloatingPoint() +TypeCategory getTypeCategory(BuiltInType t) { + ( + t instanceof CharType or + t instanceof WideCharType or + t instanceof Char16Type or + t instanceof Char32Type or + t instanceof Char8Type + ) and + result = Character() + or + ( + // The 5 standard integral types, covering both signed/unsigned variants + // Explicitly list the signed/unsigned `char` to avoid capturing plain `char`, which is of character type category + t instanceof SignedCharType or + t instanceof UnsignedCharType or + t instanceof ShortType or + t instanceof IntType or + t instanceof LongType or + t instanceof LongLongType + ) and + result = Integral() + or + ( + t instanceof FloatType or + t instanceof DoubleType or + t instanceof LongDoubleType + ) and + result = FloatingPoint() + or + ( + t instanceof BoolType or + t instanceof VoidType or + t instanceof NullPointerType + ) and + result = Other() +} /** - * A numeric type is a type that represents a number, either an integral or a floating-point. + * The signedness of a MISRA C++ 2023 numeric type + */ +newtype Signedness = + Signed() or + Unsigned() + +/** + * A MISRA C++ 2023 numeric type is a type that represents a number, either an integral or a floating-point. * * In addition to the basic integral and floating-point types, it includes: * - Enum types with an explicit underlying type that is a numeric type. @@ -24,27 +68,32 @@ newtype TypeCategory = * - Numeric types with specifiers (e.g., `const`, `volatile`, `restrict`). */ class NumericType extends Type { + // The actual numeric type, which is either an integral or a floating-point type. Type realType; NumericType() { - realType = this.getUnspecifiedType().(ReferenceType).getBaseType().(NumericType).getRealType() or - realType = this.getUnspecifiedType().(IntegralType) or - realType = this.getUnspecifiedType().(FloatingPointType) or - realType = this.getUnspecifiedType().(Enum).getExplicitUnderlyingType().getUnspecifiedType() + // A type which is either an integral or a floating-point type category + getTypeCategory(this) = [Integral().(TypeCategory), FloatingPoint()] and + realType = this + or + // Any type which, after stripping specifiers and typedefs, is a numeric type + realType = this.getUnspecifiedType().(NumericType).getRealType() + or + // Any reference type where the base type is a numeric type + realType = this.(ReferenceType).getBaseType().(NumericType).getRealType() + or + // Any Enum type with an explicit underlying type that is a numeric type + realType = this.(Enum).getExplicitUnderlyingType().(NumericType).getRealType() } Signedness getSignedness() { if realType.(IntegralType).isUnsigned() then result = Unsigned() else result = Signed() } - /** Gets the size of the actual numeric type */ + /** Gets the size of the actual numeric type. */ int getRealSize() { result = realType.getSize() } - TypeCategory getTypeCategory() { - realType instanceof IntegralType and result = Integral() - or - realType instanceof FloatingPointType and result = FloatingPoint() - } + TypeCategory getTypeCategory() { result = getTypeCategory(realType) } float getUpperBound() { result = typeUpperBound(realType) } @@ -53,6 +102,13 @@ class NumericType extends Type { Type getRealType() { result = realType } } +/** + * One of the 10 canonical integer types, which are the standard integer types. + */ +class CanonicalIntegerTypes extends NumericType, IntegralType { + CanonicalIntegerTypes() { this = this.getCanonicalArithmeticType() } +} + predicate isAssignment(Expr source, NumericType targetType, string context) { // Assignment expression (which excludes compound assignments) exists(AssignExpr assign | @@ -119,18 +175,24 @@ predicate isAssignment(Expr source, NumericType targetType, string context) { * * The type is determined by the signedness of the bit field and the number of bits. */ -NumericType getBitFieldType(BitField bf) { +CanonicalIntegerTypes getBitFieldType(BitField bf) { exists(NumericType bitfieldActualType | bitfieldActualType = bf.getType() and // Integral type with the same signedness as the bit field, and big enough to hold the bit field value - result instanceof IntegralType and result.getSignedness() = bitfieldActualType.getSignedness() and result.getSize() * 8 >= bf.getNumBits() and // No smaller integral type can hold the bit field value - not exists(IntegralType other | + not exists(CanonicalIntegerTypes other | other.getSize() * 8 >= bf.getNumBits() and - other.(NumericType).getSignedness() = result.getSignedness() and + other.getSignedness() = result.getSignedness() + | other.getSize() < result.getRealSize() + or + // Where multiple types exist with the same size and signedness, prefer shorter names - mainly + // to disambiguate between `unsigned long` and `unsigned long long` on platforms where they + // are the same size + other.getSize() = result.getRealSize() and + other.getName().length() < result.getName().length() ) ) } diff --git a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected index 4159b78b2..19518bf1f 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected +++ b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected @@ -30,37 +30,15 @@ | test.cpp:168:11:168:13 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'unsigned int'. | | test.cpp:172:11:172:22 | 4294967296 | Assignment between incompatible numeric types from 'unsigned long' to 'unsigned int'. | | test.cpp:176:11:176:13 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'unsigned int'. | -| test.cpp:180:11:180:21 | 8589934592 | Assignment between incompatible numeric types from 'long' to 'unsigned long long'. | | test.cpp:180:11:180:21 | 8589934592 | Assignment between incompatible numeric types from 'long' to 'unsigned long'. | -| test.cpp:193:11:193:12 | 16 | Assignment between incompatible numeric types from 'int32_t' to 'bool'. | -| test.cpp:193:11:193:12 | 16 | Assignment between incompatible numeric types from 'int32_t' to 'char8_t'. | -| test.cpp:193:11:193:12 | 16 | Assignment between incompatible numeric types from 'int32_t' to 'char'. | | test.cpp:193:11:193:12 | 16 | Assignment between incompatible numeric types from 'int32_t' to 'signed char'. | -| test.cpp:194:11:194:13 | - ... | Assignment between incompatible numeric types from 'int' to 'bool'. | -| test.cpp:194:11:194:13 | - ... | Assignment between incompatible numeric types from 'int' to 'char8_t'. | -| test.cpp:194:11:194:13 | - ... | Assignment between incompatible numeric types from 'int' to 'char'. | | test.cpp:194:11:194:13 | - ... | Assignment between incompatible numeric types from 'int' to 'signed char'. | -| test.cpp:196:11:196:13 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'bool'. | -| test.cpp:196:11:196:13 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'char8_t'. | -| test.cpp:196:11:196:13 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'char'. | | test.cpp:196:11:196:13 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'signed char'. | -| test.cpp:200:11:200:14 | 2048 | Assignment between incompatible numeric types from 'int32_t' to 'char16_t'. | | test.cpp:200:11:200:14 | 2048 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | -| test.cpp:200:11:200:14 | 2048 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | -| test.cpp:201:11:201:15 | - ... | Assignment between incompatible numeric types from 'int' to 'char16_t'. | | test.cpp:201:11:201:15 | - ... | Assignment between incompatible numeric types from 'int' to 'short'. | -| test.cpp:201:11:201:15 | - ... | Assignment between incompatible numeric types from 'int' to 'signed short'. | -| test.cpp:204:11:204:13 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'char16_t'. | | test.cpp:204:11:204:13 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | -| test.cpp:204:11:204:13 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | -| test.cpp:208:12:208:22 | 134217728 | Assignment between incompatible numeric types from 'long long' to 'char32_t'. | | test.cpp:208:12:208:22 | 134217728 | Assignment between incompatible numeric types from 'long long' to 'int'. | -| test.cpp:208:12:208:22 | 134217728 | Assignment between incompatible numeric types from 'long long' to 'signed int'. | -| test.cpp:208:12:208:22 | 134217728 | Assignment between incompatible numeric types from 'long long' to 'wchar_t'. | -| test.cpp:209:12:209:23 | - ... | Assignment between incompatible numeric types from 'long long' to 'char32_t'. | | test.cpp:209:12:209:23 | - ... | Assignment between incompatible numeric types from 'long long' to 'int'. | -| test.cpp:209:12:209:23 | - ... | Assignment between incompatible numeric types from 'long long' to 'signed int'. | -| test.cpp:209:12:209:23 | - ... | Assignment between incompatible numeric types from 'long long' to 'wchar_t'. | | test.cpp:224:8:224:9 | l1 | Assignment between incompatible numeric types from 'Colour' to 'uint8_t'. | | test.cpp:234:6:234:8 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'int64_t'. | | test.cpp:237:6:237:8 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | From 06eddfaa9aceefddaf6af67e9fbbbeaffe2fb572 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Wed, 18 Jun 2025 10:30:19 +0100 Subject: [PATCH 18/57] Rule 7.0.6: Add a test case for non-numeric (not covered) --- .../rules/RULE-7-0-6/test_non_numeric.cpp | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 cpp/misra/test/rules/RULE-7-0-6/test_non_numeric.cpp diff --git a/cpp/misra/test/rules/RULE-7-0-6/test_non_numeric.cpp b/cpp/misra/test/rules/RULE-7-0-6/test_non_numeric.cpp new file mode 100644 index 000000000..3da5c21c8 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-6/test_non_numeric.cpp @@ -0,0 +1,138 @@ +#include +#include + +// Test non-numeric type categories - these should not trigger rule violations +void test_non_numeric_type_categories() { + // Character category types + char l1 = 'a'; + wchar_t l2 = L'b'; + char16_t l3 = u'c'; + char32_t l4 = U'd'; + + // Other category types + bool l5 = true; + void *l6 = nullptr; + std::nullptr_t l7 = nullptr; + + // Assignments between character types and numeric types + // Rule should not apply since source/target involve non-numeric types + std::uint8_t l8 = 42; + std::int32_t l9 = 100; + float l10 = 3.14f; + + // Character to numeric - rule does not apply + l8 = l1; // COMPLIANT - char is character category, not numeric + l9 = l2; // COMPLIANT - wchar_t is character category, not numeric + l8 = l3; // COMPLIANT - char16_t is character category, not numeric + l9 = l4; // COMPLIANT - char32_t is character category, not numeric + l10 = l1; // COMPLIANT - char is character category, not numeric + + // Numeric to character - rule does not apply + l1 = l8; // COMPLIANT - char is character category, not numeric + l2 = l9; // COMPLIANT - wchar_t is character category, not numeric + l3 = l8; // COMPLIANT - char16_t is character category, not numeric + l4 = l9; // COMPLIANT - char32_t is character category, not numeric + l1 = l10; // COMPLIANT - char is character category, not numeric + + // Other category to numeric - rule does not apply + l8 = l5; // COMPLIANT - bool is other category, not numeric + l9 = l5; // COMPLIANT - bool is other category, not numeric + l10 = l5; // COMPLIANT - bool is other category, not numeric + + // Numeric to other category - rule does not apply + l5 = l8; // COMPLIANT - bool is other category, not numeric + l5 = l9; // COMPLIANT - bool is other category, not numeric + l5 = l10; // COMPLIANT - bool is other category, not numeric + + // Character to character - rule does not apply + l1 = l2; // COMPLIANT - both character category, not numeric + l3 = l4; // COMPLIANT - both character category, not numeric + l1 = l3; // COMPLIANT - both character category, not numeric + + // Other to other - rule does not apply + std::nullptr_t l11 = l7; // COMPLIANT - both other category, not numeric + l6 = l7; // COMPLIANT - both other category, not numeric + + // Character to other - rule does not apply + l5 = l1; // COMPLIANT - neither is numeric category + l6 = nullptr; // COMPLIANT - neither is numeric category + + // Other to character - rule does not apply + l1 = l5; // COMPLIANT - neither is numeric category +} + +// Test function parameters with non-numeric types +void f15(char l1) {} +void f16(bool l1) {} +void f17(wchar_t l1) {} + +void test_non_numeric_function_parameters() { + std::uint8_t l1 = 42; + std::int32_t l2 = 100; + char l3 = 'x'; + bool l4 = true; + wchar_t l5 = L'y'; + + // Function calls with non-numeric parameters - rule does not apply + f15(l1); // COMPLIANT - parameter is character category, not numeric + f15(l2); // COMPLIANT - parameter is character category, not numeric + f15(l3); // COMPLIANT - parameter is character category, not numeric + + f16(l1); // COMPLIANT - parameter is other category, not numeric + f16(l2); // COMPLIANT - parameter is other category, not numeric + f16(l4); // COMPLIANT - parameter is other category, not numeric + + f17(l1); // COMPLIANT - parameter is character category, not numeric + f17(l2); // COMPLIANT - parameter is character category, not numeric + f17(l5); // COMPLIANT - parameter is character category, not numeric +} + +// Test references to non-numeric types +void test_non_numeric_references() { + char l1 = 'a'; + bool l2 = true; + wchar_t l3 = L'b'; + std::uint8_t l4 = 42; + std::int32_t l5 = 100; + + char &l6 = l1; + bool &l7 = l2; + wchar_t &l8 = l3; + + // Assignments involving references to non-numeric types - rule does not apply + l4 = l6; // COMPLIANT - reference to character category, not numeric + l5 = l7; // COMPLIANT - reference to other category, not numeric + l4 = l8; // COMPLIANT - reference to character category, not numeric + + l6 = l4; // COMPLIANT - reference to character category, not numeric + l7 = l5; // COMPLIANT - reference to other category, not numeric + l8 = l4; // COMPLIANT - reference to character category, not numeric +} + +// Test bit-fields with non-numeric types (though these are rare in practice) +struct NonNumericBitFields { + bool m1 : 1; // Other category + char m2 : 7; // Character category + wchar_t m3 : 16; // Character category +}; + +void test_non_numeric_bitfields() { + NonNumericBitFields l1; + std::uint8_t l2 = 42; + std::int32_t l3 = 100; + bool l4 = true; + char l5 = 'x'; + + // Assignments to/from non-numeric bit-fields - rule does not apply + l1.m1 = l2; // COMPLIANT - bit-field is other category, not numeric + l1.m1 = l4; // COMPLIANT - bit-field is other category, not numeric + l1.m2 = l2; // COMPLIANT - bit-field is character category, not numeric + l1.m2 = l5; // COMPLIANT - bit-field is character category, not numeric + l1.m3 = l3; // COMPLIANT - bit-field is character category, not numeric + + l2 = l1.m1; // COMPLIANT - bit-field is other category, not numeric + l4 = l1.m1; // COMPLIANT - bit-field is other category, not numeric + l2 = l1.m2; // COMPLIANT - bit-field is character category, not numeric + l5 = l1.m2; // COMPLIANT - bit-field is character category, not numeric + l3 = l1.m3; // COMPLIANT - bit-field is character category, not numeric +} \ No newline at end of file From a65c4cce47dde7b2176bda68917a99f43654320e Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Wed, 18 Jun 2025 11:07:37 +0100 Subject: [PATCH 19/57] StandardConversions: Handle aggregate initialization --- .../cpp/misra/StandardConversions.qll | 27 +++-- .../NumericAssignmentTypeMismatch.expected | 33 +++++++ cpp/misra/test/rules/RULE-7-0-6/test.cpp | 99 +++++++++++++++++++ 3 files changed, 153 insertions(+), 6 deletions(-) diff --git a/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll b/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll index 287eb4409..76ee93f2a 100644 --- a/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll +++ b/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll @@ -168,6 +168,26 @@ predicate isAssignment(Expr source, NumericType targetType, string context) { targetType = switch.getExpr().getFullyConverted().getType() and context = "switch case" ) + or + // Class aggregate literal initialization + exists(ClassAggregateLiteral al, Field f | + source = al.getAFieldExpr(f) and + context = "class aggregate literal" + | + // For the MISRA type rules we treat bit fields as a special case + if f instanceof BitField + then targetType = getBitFieldType(f) + else + // Regular variable initialization + targetType = f.getType() + ) + or + // Array or vector aggregate literal initialization + exists(ArrayOrVectorAggregateLiteral vl | + source = vl.getAnElementExpr(_) and + targetType = vl.getElementType() and + context = "array or vector aggregate literal" + ) } /** @@ -200,9 +220,4 @@ CanonicalIntegerTypes getBitFieldType(BitField bf) { /** * Holds if the `source` expression is assigned to a bit field. */ -predicate isAssignedToBitfield(Expr source, BitField bf) { - exists(Assignment assign | - assign.getRValue() = source and - assign.getLValue() = bf.getAnAccess() - ) -} +predicate isAssignedToBitfield(Expr source, BitField bf) { source = bf.getAnAssignedValue() } diff --git a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected index 19518bf1f..8f9cbfe24 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected +++ b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected @@ -137,3 +137,36 @@ | test.cpp:766:8:766:10 | 42 | Assignment between incompatible numeric types from 'long' to 'int32_t'. | | test.cpp:767:8:767:11 | 42 | Assignment between incompatible numeric types from 'long long' to 'int32_t'. | | test.cpp:768:8:768:10 | 42 | Assignment between incompatible numeric types from 'unsigned int' to 'int32_t'. | +| test.cpp:786:22:786:24 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test.cpp:788:26:788:28 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'uint16_t'. | +| test.cpp:790:31:790:33 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:792:22:792:24 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'float'. | +| test.cpp:795:22:795:24 | 256 | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test.cpp:795:27:795:31 | 65536 | Assignment between incompatible numeric types from 'int' to 'uint16_t'. | +| test.cpp:796:22:796:33 | 2147483648 | Assignment between incompatible numeric types from 'long long' to 'int32_t'. | +| test.cpp:808:22:808:24 | 300 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | +| test.cpp:808:27:808:29 | 400 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | +| test.cpp:808:32:808:34 | 500 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | +| test.cpp:809:22:809:23 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'unsigned char'. | +| test.cpp:809:26:809:27 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'unsigned char'. | +| test.cpp:809:30:809:31 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'unsigned char'. | +| test.cpp:810:22:810:24 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test.cpp:810:27:810:29 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test.cpp:810:32:810:34 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test.cpp:815:26:815:28 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | +| test.cpp:815:31:815:33 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | +| test.cpp:815:38:815:40 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | +| test.cpp:815:43:815:45 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | +| test.cpp:816:26:816:27 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'signed short'. | +| test.cpp:816:30:816:31 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'signed short'. | +| test.cpp:817:26:817:27 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'signed short'. | +| test.cpp:817:30:817:31 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'signed short'. | +| test.cpp:833:8:833:10 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test.cpp:837:7:837:9 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'uint32_t'. | +| test.cpp:853:24:853:26 | 300 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | +| test.cpp:853:29:853:33 | 70000 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | +| test.cpp:853:36:853:39 | 5000 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | +| test.cpp:854:24:854:26 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test.cpp:854:29:854:31 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned short'. | +| test.cpp:854:34:854:36 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | +| test.cpp:864:28:864:30 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | diff --git a/cpp/misra/test/rules/RULE-7-0-6/test.cpp b/cpp/misra/test/rules/RULE-7-0-6/test.cpp index 740129a4b..f74ce3050 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/test.cpp +++ b/cpp/misra/test/rules/RULE-7-0-6/test.cpp @@ -766,4 +766,103 @@ void test_user_defined_operators_constants() { l1 = 42L; // NON_COMPLIANT l1 = 42LL; // NON_COMPLIANT l1 = 42U; // NON_COMPLIANT +} + +// Test aggregate initialization - struct with multiple members +struct SimpleAggregate { + std::uint8_t m1; + std::uint16_t m2; + std::int32_t m3; + float m4; +}; + +void test_aggregate_initialization_basic() { + // Compliant cases - exact types or constants that fit + SimpleAggregate l1{42, 1000, -50, 3.14f}; // COMPLIANT + SimpleAggregate l2{u8, u16, s32, f}; // COMPLIANT + SimpleAggregate l3{255, 65535, 2147483647, 0.0f}; // COMPLIANT + + // Non-compliant cases - type violations + SimpleAggregate l4{u16, u8, s32, // NON_COMPLIANT - narrowing u16 to uint8_t + f}; + SimpleAggregate l5{u8, u32, s32, // NON_COMPLIANT - narrowing u32 to uint16_t + f}; + SimpleAggregate l6{u8, u16, u32, f}; // NON_COMPLIANT - different signedness + SimpleAggregate l7{u8, u16, s32, + s32}; // NON_COMPLIANT - different type category + + // Constants that don't fit + SimpleAggregate l8{256, 65536, // NON_COMPLIANT - constants don't fit + 2147483648LL, // NON_COMPLIANT - constants don't fit + 0.0f}; + + // Widening of id-expressions is allowed + SimpleAggregate l9{u8, u8, s8, f}; // COMPLIANT - widening allowed +} + +// Test aggregate initialization - arrays +void test_aggregate_initialization_arrays() { + // Basic arrays + std::uint8_t l1[3]{10, 20, 30}; // COMPLIANT + std::uint8_t l2[3]{u8, u8, u8}; // COMPLIANT + std::uint8_t l3[3]{300, 400, 500}; // NON_COMPLIANT - constants don't fit + std::uint8_t l4[3]{s8, s8, s8}; // NON_COMPLIANT - signedness mismatch + std::uint8_t l5[3]{u16, u16, u16}; // NON_COMPLIANT - narrowing + + // Multi-dimensional arrays + std::int16_t l6[2][2]{{1, 2}, {3, 4}}; // COMPLIANT + std::int16_t l7[2][2]{{s8, s8}, {s8, s8}}; // COMPLIANT - widening allowed + std::int16_t l8[2][2]{{s32, s32}, {s32, s32}}; // NON_COMPLIANT - narrowing + std::int16_t l9[2][2]{{u8, u8}, // NON_COMPLIANT - signedness mismatch + {u8, u8}}; // NON_COMPLIANT - signedness mismatch +} + +// Test aggregate initialization - nested structs +struct NestedAggregate { + SimpleAggregate m1; + std::uint32_t m2; +}; + +void test_aggregate_initialization_nested() { + // Compliant nested initialization + NestedAggregate l1{{10, 100, -5, 1.0f}, 500}; // COMPLIANT + NestedAggregate l2{{u8, u16, s32, f}, u32}; // COMPLIANT + + // Non-compliant nested initialization + NestedAggregate l3{ + {u16, u8, s32, f}, // NON_COMPLIANT - narrowing in nested struct + u32}; + NestedAggregate l4{ + {u8, u16, s32, f}, + s32}; // NON_COMPLIANT - signedness mismatch in outer member +} + +// Test aggregate initialization - struct with bit-fields +struct BitfieldAggregate { + std::uint32_t m1 : 8; + std::uint32_t m2 : 16; + std::int32_t m3 : 12; +}; + +void test_aggregate_initialization_bitfields() { + // Compliant cases + BitfieldAggregate l1{100, 30000, -500}; // COMPLIANT + BitfieldAggregate l2{u8, u16, s16}; // COMPLIANT - appropriate sizes + + // Non-compliant cases + BitfieldAggregate l3{300, 70000, 5000}; // NON_COMPLIANT - constants don't fit + BitfieldAggregate l4{u16, u32, s32}; // NON_COMPLIANT - narrowing +} + +// Test aggregate initialization with designated initializers (C++20 feature, +// but test for basic cases) +void test_aggregate_initialization_designated() { + // Note: Designated initializers are C++20, but we can test basic aggregate + // init patterns + SimpleAggregate l1{.m1 = 10, .m2 = 100, .m3 = -5, .m4 = 1.0f}; // COMPLIANT + SimpleAggregate l2{.m1 = u8, .m2 = u16, .m3 = s32, .m4 = f}; // COMPLIANT + SimpleAggregate l3{.m1 = u16, // NON_COMPLIANT - type violation + .m2 = u8, + .m3 = s32, + .m4 = f}; } \ No newline at end of file From 04613cd353c86fa94dacf6ef2a98b668b400b975 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Thu, 19 Jun 2025 13:32:53 +0100 Subject: [PATCH 20/57] Add a library for determining constant expressions The `getValue()` provided in the database applies the conversions, which can be unhelpful when trying to write rules the refer to conversions. --- .../cpp/ConstantExpressions.qll | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 cpp/common/src/codingstandards/cpp/ConstantExpressions.qll diff --git a/cpp/common/src/codingstandards/cpp/ConstantExpressions.qll b/cpp/common/src/codingstandards/cpp/ConstantExpressions.qll new file mode 100644 index 000000000..c75df942d --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/ConstantExpressions.qll @@ -0,0 +1,97 @@ +import cpp + +final private class FinalExpr = Expr; + +/** + * An integer constant expression as defined by the C++17 standard. + */ +class IntegerConstantExpr extends FinalExpr { + IntegerConstantExpr() { + // An integer constant expression is a constant expression that has an + // integral type. + this.isConstant() and + exists(Type unspecifiedType | unspecifiedType = this.getUnspecifiedType() | + unspecifiedType instanceof IntegralType + or + // Unscoped enum type + unspecifiedType instanceof Enum and + not unspecifiedType instanceof ScopedEnum + ) + } + + /** + * Gets the value of this integer constant expression. + * + * This is only defined for expressions that are constant expressions, and + * that have a value that can be represented as a `BigInt`. + */ + QlBuiltins::BigInt getConstantValue() { + if exists(getPreConversionConstantValue()) + then result = getPreConversionConstantValue() + else result = this.getValue().toBigInt() + } + + /** + * Gets the pre-conversion constant value of this integer constant expression, if it is different + * from `getValue()`. + * + * This is required because `Expr.getValue()` returns the _converted constant expression value_ + * for non-literal constant expressions, which is the expression value after conversions have been + * applied, but for validating conversions we need the _pre-conversion constant expression value_. + */ + private QlBuiltins::BigInt getPreConversionConstantValue() { + // Access of a variable that has a constant initializer + result = + this.(VariableAccess) + .getTarget() + .getInitializer() + .getExpr() + .getFullyConverted() + .getValue() + .toBigInt() + or + result = this.(EnumConstantAccess).getTarget().getValue().toBigInt() + or + result = -this.(UnaryMinusExpr).getOperand().getFullyConverted().getValue().toBigInt() + or + result = this.(UnaryPlusExpr).getOperand().getFullyConverted().getValue().toBigInt() + or + result = this.(NotExpr).getOperand().getFullyConverted().getValue().toBigInt().bitNot() + or + exists(BinaryOperation op, QlBuiltins::BigInt left, QlBuiltins::BigInt right | + op = this and + left = op.getLeftOperand().getFullyConverted().getValue().toBigInt() and + right = op.getRightOperand().getFullyConverted().getValue().toBigInt() + | + op instanceof AddExpr and + result = left + right + or + op instanceof SubExpr and + result = left - right + or + op instanceof MulExpr and + result = left * right + or + op instanceof DivExpr and + result = left / right + or + op instanceof RemExpr and + result = left % right + or + op instanceof BitwiseAndExpr and + result = left.bitAnd(right) + or + op instanceof BitwiseOrExpr and + result = left.bitOr(right) + or + op instanceof BitwiseXorExpr and + result = left.bitXor(right) + or + op instanceof RShiftExpr and + result = left.bitShiftRightSigned(right.toInt()) + or + op instanceof LShiftExpr and + result = left.bitShiftLeft(right.toInt()) + ) + } +} From 4be1395529e7eb6fd515f4c5dae02b2f7df2fab0 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Thu, 19 Jun 2025 13:38:58 +0100 Subject: [PATCH 21/57] Rule 7.0.6: Use BigInt for constant expressions - Use IntegerConstantExpr to determine both the expressions which are constant, and the BigInt value of those constants (before final conversion). - Implement a BigInt type upper/lower bound to determine whether a constant assignment is valid. --- .../src/codingstandards/cpp/types/Type.qll | 17 ++++++++++++ .../cpp/misra/StandardConversions.qll | 11 ++++++-- .../NumericAssignmentTypeMismatch.ql | 27 +++++++++++-------- 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/cpp/common/src/codingstandards/cpp/types/Type.qll b/cpp/common/src/codingstandards/cpp/types/Type.qll index 42d77b805..9e2a74904 100644 --- a/cpp/common/src/codingstandards/cpp/types/Type.qll +++ b/cpp/common/src/codingstandards/cpp/types/Type.qll @@ -94,3 +94,20 @@ int getPrecision(IntegralType type) { or type.isExplicitlySigned() and result = type.getSize() * 8 - 1 } + +/** + * Determines the lower and upper bounds of an integral type. + */ +predicate integralTypeBounds(IntegralType integralType, QlBuiltins::BigInt lb, QlBuiltins::BigInt ub) { + exists(QlBuiltins::BigInt limit | limit = 2.toBigInt().pow(8 * integralType.getSize()) | + if integralType instanceof BoolType + then lb = 0.toBigInt() and ub = 1.toBigInt() + else + if integralType.isSigned() + then ( + lb = -(limit / 2.toBigInt()) and ub = (limit / 2.toBigInt()) - 1.toBigInt() + ) else ( + lb = 0.toBigInt() and ub = limit - 1.toBigInt() + ) + ) +} diff --git a/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll b/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll index 76ee93f2a..0f96e6120 100644 --- a/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll +++ b/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll @@ -1,5 +1,6 @@ import cpp import codingstandards.cpp.misra +import codingstandards.cpp.Type /** * A MISRA C++ 2023 type category. @@ -95,9 +96,15 @@ class NumericType extends Type { TypeCategory getTypeCategory() { result = getTypeCategory(realType) } - float getUpperBound() { result = typeUpperBound(realType) } + /** + * Gets the integeral upper bound of the numeric type, if it represents an integer type. + */ + QlBuiltins::BigInt getIntegralUpperBound() { integralTypeBounds(realType, _, result) } - float getLowerBound() { result = typeLowerBound(realType) } + /** + * Gets the integeral lower bound of the numeric type, if it represents an integer type. + */ + QlBuiltins::BigInt getIntegralLowerBound() { integralTypeBounds(realType, result, _) } Type getRealType() { result = realType } } diff --git a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql index 23409abcb..60b661911 100644 --- a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql +++ b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql @@ -15,14 +15,13 @@ import cpp import codingstandards.cpp.misra +import codingstandards.cpp.ConstantExpressions import codingstandards.cpp.misra.StandardConversions +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis -predicate isValidConstantAssignment(Expr source, NumericType targetType) { +predicate isValidConstantAssignment(IntegerConstantExpr source, NumericType targetType) { isAssignment(source, targetType, _) and - // Source is an integer constant expression - source.isConstant() and - source.getType().(NumericType).getTypeCategory() = Integral() and - exists(float val | val = source.getValue().toFloat() | + exists(QlBuiltins::BigInt val | val = source.getConstantValue() | // Bit field assignment: check if the value fits in the bit field exists(BitField bf, int numBits | isAssignedToBitfield(source, bf) and @@ -30,19 +29,25 @@ predicate isValidConstantAssignment(Expr source, NumericType targetType) { if targetType.getSignedness() = Signed() then // Signed bit field: value must be in the range of signed bit field - val >= -2.pow(numBits - 1) and - val < 2.pow(numBits - 1) + val >= -2.toBigInt().pow(numBits - 1) and + val < 2.toBigInt().pow(numBits - 1) else ( // Unsigned bit field: value must be in the range of unsigned bit field - val >= 0 and - val < 2.pow(numBits) + val >= 0.toBigInt() and + val < 2.toBigInt().pow(numBits) ) ) or // Regular assignment: check if the value fits in the target type range not isAssignedToBitfield(source, _) and - targetType.getLowerBound() <= val and - val <= targetType.getUpperBound() + ( + // Integer types: check if the value fits in the target type range + targetType.getIntegralLowerBound() <= val and + val <= targetType.getIntegralUpperBound() + or + // All floating point types can represent all integer values + targetType.getTypeCategory() = FloatingPoint() + ) ) } From e87892e9e35f9ef583fba95a8f846caef55b5208 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Thu, 19 Jun 2025 13:41:48 +0100 Subject: [PATCH 22/57] Rule 7.0.6: add tests for cv-qualified types --- .../NumericAssignmentTypeMismatch.expected | 94 +++++ .../test/rules/RULE-7-0-6/test_specified.cpp | 371 ++++++++++++++++++ 2 files changed, 465 insertions(+) create mode 100644 cpp/misra/test/rules/RULE-7-0-6/test_specified.cpp diff --git a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected index 8f9cbfe24..23665ed10 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected +++ b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected @@ -170,3 +170,97 @@ | test.cpp:854:29:854:31 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned short'. | | test.cpp:854:34:854:36 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | | test.cpp:864:28:864:30 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test_specified.cpp:37:8:37:9 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test_specified.cpp:38:8:38:9 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'uint8_t'. | +| test_specified.cpp:41:8:41:9 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'int8_t'. | +| test_specified.cpp:42:8:42:9 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'int8_t'. | +| test_specified.cpp:43:9:43:10 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'int16_t'. | +| test_specified.cpp:48:8:48:9 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'uint8_t'. | +| test_specified.cpp:49:9:49:10 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'uint16_t'. | +| test_specified.cpp:50:9:50:10 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'uint32_t'. | +| test_specified.cpp:58:9:58:10 | l7 | Assignment between incompatible numeric types from 'float' to 'int32_t'. | +| test_specified.cpp:59:9:59:10 | l8 | Assignment between incompatible numeric types from 'double' to 'int32_t'. | +| test_specified.cpp:71:7:71:8 | l8 | Assignment between incompatible numeric types from 'double' to 'float'. | +| test_specified.cpp:94:8:94:9 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test_specified.cpp:95:8:95:9 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'uint8_t'. | +| test_specified.cpp:96:9:96:10 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'uint16_t'. | +| test_specified.cpp:97:8:97:9 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'int8_t'. | +| test_specified.cpp:98:8:98:9 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'int8_t'. | +| test_specified.cpp:99:9:99:10 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'int16_t'. | +| test_specified.cpp:102:8:102:9 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'uint8_t'. | +| test_specified.cpp:103:9:103:10 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'uint16_t'. | +| test_specified.cpp:104:9:104:10 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'uint32_t'. | +| test_specified.cpp:105:8:105:9 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'int8_t'. | +| test_specified.cpp:106:9:106:10 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'int16_t'. | +| test_specified.cpp:107:9:107:10 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_specified.cpp:110:9:110:10 | l7 | Assignment between incompatible numeric types from 'float' to 'int32_t'. | +| test_specified.cpp:111:9:111:10 | l8 | Assignment between incompatible numeric types from 'double' to 'int32_t'. | +| test_specified.cpp:112:7:112:8 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'float'. | +| test_specified.cpp:113:7:113:8 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'float'. | +| test_specified.cpp:114:7:114:8 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'float'. | +| test_specified.cpp:115:7:115:8 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'double'. | +| test_specified.cpp:116:7:116:8 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'double'. | +| test_specified.cpp:117:7:117:8 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'double'. | +| test_specified.cpp:120:7:120:8 | l8 | Assignment between incompatible numeric types from 'double' to 'float'. | +| test_specified.cpp:143:8:143:9 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test_specified.cpp:144:8:144:9 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'uint8_t'. | +| test_specified.cpp:145:9:145:10 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'uint16_t'. | +| test_specified.cpp:146:8:146:9 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'int8_t'. | +| test_specified.cpp:147:8:147:9 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'int8_t'. | +| test_specified.cpp:148:9:148:10 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'int16_t'. | +| test_specified.cpp:151:8:151:9 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'uint8_t'. | +| test_specified.cpp:152:9:152:10 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'uint16_t'. | +| test_specified.cpp:153:9:153:10 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'uint32_t'. | +| test_specified.cpp:154:8:154:9 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'int8_t'. | +| test_specified.cpp:155:9:155:10 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'int16_t'. | +| test_specified.cpp:156:9:156:10 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_specified.cpp:159:9:159:10 | l7 | Assignment between incompatible numeric types from 'float' to 'int32_t'. | +| test_specified.cpp:160:9:160:10 | l8 | Assignment between incompatible numeric types from 'double' to 'int32_t'. | +| test_specified.cpp:161:7:161:8 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'float'. | +| test_specified.cpp:162:7:162:8 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'float'. | +| test_specified.cpp:163:7:163:8 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'float'. | +| test_specified.cpp:164:7:164:8 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'double'. | +| test_specified.cpp:165:7:165:8 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'double'. | +| test_specified.cpp:166:7:166:8 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'double'. | +| test_specified.cpp:169:7:169:8 | l8 | Assignment between incompatible numeric types from 'double' to 'float'. | +| test_specified.cpp:213:8:213:9 | l6 | Assignment between incompatible numeric types from 'const uint16_t &' to 'uint8_t'. | +| test_specified.cpp:214:8:214:10 | l10 | Assignment between incompatible numeric types from 'volatile uint16_t &' to 'uint8_t'. | +| test_specified.cpp:215:8:215:10 | l14 | Assignment between incompatible numeric types from 'const volatile uint16_t &' to 'uint8_t'. | +| test_specified.cpp:218:8:218:9 | l5 | Assignment between incompatible numeric types from 'const uint8_t &' to 'int8_t'. | +| test_specified.cpp:219:8:219:9 | l7 | Assignment between incompatible numeric types from 'const int8_t &' to 'uint8_t'. | +| test_specified.cpp:220:8:220:9 | l9 | Assignment between incompatible numeric types from 'volatile uint8_t &' to 'int8_t'. | +| test_specified.cpp:221:8:221:10 | l11 | Assignment between incompatible numeric types from 'volatile int8_t &' to 'uint8_t'. | +| test_specified.cpp:222:8:222:10 | l13 | Assignment between incompatible numeric types from 'const volatile uint8_t &' to 'int8_t'. | +| test_specified.cpp:223:8:223:10 | l15 | Assignment between incompatible numeric types from 'const volatile int8_t &' to 'uint8_t'. | +| test_specified.cpp:226:9:226:10 | l8 | Assignment between incompatible numeric types from 'const float &' to 'int32_t'. | +| test_specified.cpp:227:9:227:11 | l12 | Assignment between incompatible numeric types from 'volatile float &' to 'int32_t'. | +| test_specified.cpp:228:9:228:11 | l16 | Assignment between incompatible numeric types from 'const volatile float &' to 'int32_t'. | +| test_specified.cpp:229:7:229:8 | l3 | Assignment between incompatible numeric types from 'int8_t' to 'float'. | +| test_specified.cpp:230:7:230:8 | l7 | Assignment between incompatible numeric types from 'const int8_t &' to 'float'. | +| test_specified.cpp:231:7:231:9 | l11 | Assignment between incompatible numeric types from 'volatile int8_t &' to 'float'. | +| test_specified.cpp:232:7:232:9 | l15 | Assignment between incompatible numeric types from 'const volatile int8_t &' to 'float'. | +| test_specified.cpp:245:7:245:8 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'const uint32_t'. | +| test_specified.cpp:246:7:246:8 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'const uint32_t'. | +| test_specified.cpp:247:7:247:8 | l3 | Assignment between incompatible numeric types from 'int8_t' to 'volatile int64_t'. | +| test_specified.cpp:248:7:248:8 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'const volatile uint16_t'. | +| test_specified.cpp:250:7:250:8 | l3 | Assignment between incompatible numeric types from 'int8_t' to 'const volatile uint16_t'. | +| test_specified.cpp:285:8:285:22 | g4 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test_specified.cpp:287:8:287:19 | s4 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test_specified.cpp:292:9:292:23 | g5 | Assignment between incompatible numeric types from 'int8_t' to 'uint16_t'. | +| test_specified.cpp:294:9:294:20 | s5 | Assignment between incompatible numeric types from 'int8_t' to 'uint16_t'. | +| test_specified.cpp:308:23:308:25 | 300 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | +| test_specified.cpp:308:28:308:32 | 70000 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | +| test_specified.cpp:308:35:308:38 | 3000 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | +| test_specified.cpp:309:12:309:16 | 70000 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | +| test_specified.cpp:314:23:314:25 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test_specified.cpp:314:28:314:30 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned short'. | +| test_specified.cpp:314:33:314:35 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | +| test_specified.cpp:329:8:329:9 | l2 | Assignment between incompatible numeric types from 'CVColour' to 'uint8_t'. | +| test_specified.cpp:331:8:331:9 | l3 | Assignment between incompatible numeric types from 'CVColour' to 'uint8_t'. | +| test_specified.cpp:342:8:342:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test_specified.cpp:343:8:343:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test_specified.cpp:344:8:344:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test_specified.cpp:351:10:351:11 | l2 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | +| test_specified.cpp:352:10:352:11 | l3 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | +| test_specified.cpp:370:18:370:20 | 300 | Assignment between incompatible numeric types from 'int' to 'const uint8_t'. | +| test_specified.cpp:370:23:370:27 | 70000 | Assignment between incompatible numeric types from 'int' to 'volatile uint16_t'. | diff --git a/cpp/misra/test/rules/RULE-7-0-6/test_specified.cpp b/cpp/misra/test/rules/RULE-7-0-6/test_specified.cpp new file mode 100644 index 000000000..dc58ade31 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-6/test_specified.cpp @@ -0,0 +1,371 @@ +#include +#include + +// Global variables for testing +std::uint32_t u32; +std::int32_t s32; +std::uint8_t u8; +std::int8_t s8; +std::uint16_t u16; +std::int16_t s16; +std::uint64_t u64; +std::int64_t s64; +float f; +double d; + +// Test cv-qualified types +void test_cv_qualified_const() { + const std::uint8_t l1 = 42; // COMPLIANT + const std::uint16_t l2 = 1000; // COMPLIANT + const std::uint32_t l3 = 50000; // COMPLIANT + const std::int8_t l4 = -5; // COMPLIANT + const std::int16_t l5 = -1000; // COMPLIANT + const std::int32_t l6 = -50000; // COMPLIANT + const float l7 = 3.14f; // COMPLIANT + const double l8 = 2.718; // COMPLIANT + + // Widening of const id-expressions - allowed + // Also allowed because the integer constant expressions are within the range + u16 = l1; // COMPLIANT + u32 = l1; // COMPLIANT + u32 = l2; // COMPLIANT + s16 = l4; // COMPLIANT + s32 = l4; // COMPLIANT + s32 = l5; // COMPLIANT + + // Narrowing of const id-expressions - not allowed + u8 = l2; // NON_COMPLIANT + u8 = l3; // NON_COMPLIANT + // Permitted because the integer constant expression is within the range + u16 = l3; // COMPLIANT + s8 = l5; // NON_COMPLIANT + s8 = l6; // NON_COMPLIANT + s16 = l6; // NON_COMPLIANT + + // Incorrect signedness conversions, and the integer constant + // expressions are not in the range of the target type (as they are all + // negative) + u8 = l4; // NON_COMPLIANT + u16 = l5; // NON_COMPLIANT + u32 = l6; // NON_COMPLIANT + // These are signedness violations, but as they are integer constant + // expressions within range they are allowed + s8 = l1; // COMPLIANT + s16 = l2; // COMPLIANT + s32 = l3; // COMPLIANT + + // Type category errors (int to float) + s32 = l7; // NON_COMPLIANT + s32 = l8; // NON_COMPLIANT + // These are not type category errors, as they are integer constant + // expressions whose value fits within both floating point types, as the range + // is infinite + f = l4; // COMPLIANT + f = l5; // COMPLIANT + f = l6; // COMPLIANT + d = l4; // COMPLIANT + d = l5; // COMPLIANT + d = l6; // COMPLIANT + + // Size violations within same type category with const + f = l8; // NON_COMPLIANT + d = l7; // COMPLIANT +} + +void test_cv_qualified_volatile() { + volatile std::uint8_t l1 = 42; + volatile std::uint16_t l2 = 1000; + volatile std::uint32_t l3 = 50000; + volatile std::int8_t l4 = -5; + volatile std::int16_t l5 = -1000; + volatile std::int32_t l6 = -50000; + volatile float l7 = 3.14f; + volatile double l8 = 2.718; + + // Widening of volatile id-expressions - allowed + u16 = l1; // COMPLIANT + u32 = l1; // COMPLIANT + u32 = l2; // COMPLIANT + s16 = l4; // COMPLIANT + s32 = l4; // COMPLIANT + s32 = l5; // COMPLIANT + + // Narrowing of volatile id-expressions - not allowed + u8 = l2; // NON_COMPLIANT + u8 = l3; // NON_COMPLIANT + u16 = l3; // NON_COMPLIANT + s8 = l5; // NON_COMPLIANT + s8 = l6; // NON_COMPLIANT + s16 = l6; // NON_COMPLIANT + + // Signedness violations with volatile + u8 = l4; // NON_COMPLIANT + u16 = l5; // NON_COMPLIANT + u32 = l6; // NON_COMPLIANT + s8 = l1; // NON_COMPLIANT + s16 = l2; // NON_COMPLIANT + s32 = l3; // NON_COMPLIANT + + // Type category violations with volatile + s32 = l7; // NON_COMPLIANT + s32 = l8; // NON_COMPLIANT + f = l4; // NON_COMPLIANT + f = l5; // NON_COMPLIANT + f = l6; // NON_COMPLIANT + d = l4; // NON_COMPLIANT + d = l5; // NON_COMPLIANT + d = l6; // NON_COMPLIANT + + // Size violations within same type category with volatile + f = l8; // NON_COMPLIANT + d = l7; // COMPLIANT +} + +void test_cv_qualified_const_volatile() { + const volatile std::uint8_t l1 = 42; + const volatile std::uint16_t l2 = 1000; + const volatile std::uint32_t l3 = 50000; + const volatile std::int8_t l4 = -5; + const volatile std::int16_t l5 = -1000; + const volatile std::int32_t l6 = -50000; + const volatile float l7 = 3.14f; + const volatile double l8 = 2.718; + + // Widening of const volatile id-expressions - allowed + u16 = l1; // COMPLIANT + u32 = l1; // COMPLIANT + u32 = l2; // COMPLIANT + s16 = l4; // COMPLIANT + s32 = l4; // COMPLIANT + s32 = l5; // COMPLIANT + + // Narrowing of const volatile id-expressions - not allowed + u8 = l2; // NON_COMPLIANT + u8 = l3; // NON_COMPLIANT + u16 = l3; // NON_COMPLIANT + s8 = l5; // NON_COMPLIANT + s8 = l6; // NON_COMPLIANT + s16 = l6; // NON_COMPLIANT + + // Signedness violations with const volatile + u8 = l4; // NON_COMPLIANT + u16 = l5; // NON_COMPLIANT + u32 = l6; // NON_COMPLIANT + s8 = l1; // NON_COMPLIANT + s16 = l2; // NON_COMPLIANT + s32 = l3; // NON_COMPLIANT + + // Type category violations with const volatile + s32 = l7; // NON_COMPLIANT + s32 = l8; // NON_COMPLIANT + f = l4; // NON_COMPLIANT + f = l5; // NON_COMPLIANT + f = l6; // NON_COMPLIANT + d = l4; // NON_COMPLIANT + d = l5; // NON_COMPLIANT + d = l6; // NON_COMPLIANT + + // Size violations within same type category with const volatile + f = l8; // NON_COMPLIANT + d = l7; // COMPLIANT +} + +// Test cv-qualified references +void test_cv_qualified_references() { + std::uint8_t l1 = 42; + std::uint16_t l2 = 1000; + std::int8_t l3 = -5; + float l4 = 3.14f; + + const std::uint8_t &l5 = l1; + const std::uint16_t &l6 = l2; + const std::int8_t &l7 = l3; + const float &l8 = l4; + + volatile std::uint8_t &l9 = l1; + volatile std::uint16_t &l10 = l2; + volatile std::int8_t &l11 = l3; + volatile float &l12 = l4; + + const volatile std::uint8_t &l13 = l1; + const volatile std::uint16_t &l14 = l2; + const volatile std::int8_t &l15 = l3; + const volatile float &l16 = l4; + + // Widening through cv-qualified references - allowed + u16 = l5; // COMPLIANT + u32 = l5; // COMPLIANT + u32 = l6; // COMPLIANT + s16 = l7; // COMPLIANT + s32 = l7; // COMPLIANT + u16 = l9; // COMPLIANT + u32 = l9; // COMPLIANT + u32 = l10; // COMPLIANT + s16 = l11; // COMPLIANT + s32 = l11; // COMPLIANT + u16 = l13; // COMPLIANT + u32 = l13; // COMPLIANT + u32 = l14; // COMPLIANT + s16 = l15; // COMPLIANT + s32 = l15; // COMPLIANT + + // Narrowing through cv-qualified references - not allowed + u8 = l6; // NON_COMPLIANT + u8 = l10; // NON_COMPLIANT + u8 = l14; // NON_COMPLIANT + + // Signedness violations through cv-qualified references + s8 = l5; // NON_COMPLIANT + u8 = l7; // NON_COMPLIANT + s8 = l9; // NON_COMPLIANT + u8 = l11; // NON_COMPLIANT + s8 = l13; // NON_COMPLIANT + u8 = l15; // NON_COMPLIANT + + // Type category violations through cv-qualified references + s32 = l8; // NON_COMPLIANT + s32 = l12; // NON_COMPLIANT + s32 = l16; // NON_COMPLIANT + f = l3; // NON_COMPLIANT + f = l7; // NON_COMPLIANT + f = l11; // NON_COMPLIANT + f = l15; // NON_COMPLIANT +} + +// Test cv-qualified function parameters +void f15(const std::uint32_t l1) {} +void f16(volatile std::int64_t l1) {} +void f17(const volatile std::uint16_t l1) {} + +void test_cv_qualified_function_parameters() { + const std::uint8_t l1 = 42; + volatile std::uint16_t l2 = 1000; + const volatile std::int8_t l3 = -5; + + f15(l1); // NON_COMPLIANT + f15(l2); // NON_COMPLIANT + f16(l3); // NON_COMPLIANT + f17(l1); // NON_COMPLIANT + f17(l2); // COMPLIANT + f17(l3); // NON_COMPLIANT +} + +// Test cv-qualified static and namespace variables +namespace CVNamespace { +const std::uint8_t g3 = 42; +volatile std::uint16_t g4 = 1000; +const volatile std::int8_t g5 = -5; +} // namespace CVNamespace + +struct CVStruct { + static const std::uint8_t s3; + static volatile std::uint16_t s4; + static const volatile std::int8_t s5; +}; + +const std::uint8_t CVStruct::s3 = 42; +volatile std::uint16_t CVStruct::s4 = 1000; +const volatile std::int8_t CVStruct::s5 = -5; + +void test_cv_qualified_static_and_namespace() { + // Widening of cv-qualified namespace and static variables - allowed + u16 = CVNamespace::g3; // COMPLIANT + u32 = CVNamespace::g3; // COMPLIANT + u32 = CVNamespace::g4; // COMPLIANT + s16 = CVNamespace::g5; // COMPLIANT + s32 = CVNamespace::g5; // COMPLIANT + + u16 = CVStruct::s3; // COMPLIANT + u32 = CVStruct::s3; // COMPLIANT + u32 = CVStruct::s4; // COMPLIANT + s16 = CVStruct::s5; // COMPLIANT + s32 = CVStruct::s5; // COMPLIANT + + // Narrowing of cv-qualified namespace and static variables - not allowed + u8 = CVNamespace::g4; // NON_COMPLIANT + s8 = CVNamespace::g5; // COMPLIANT - constant fits + u8 = CVStruct::s4; // NON_COMPLIANT + s8 = CVStruct::s5; // COMPLIANT - constant fits + + // Signedness violations with cv-qualified namespace and static variables + s8 = CVNamespace::g3; // COMPLIANT - constant expression + u16 = CVNamespace::g5; // NON_COMPLIANT + s8 = CVStruct::s3; // COMPLIANT - constant expression + u16 = CVStruct::s5; // NON_COMPLIANT +} + +// Test cv-qualified bitfields +struct CVBitfieldStruct { + const std::uint32_t m11 : 8; + volatile std::uint32_t m12 : 16; + const volatile std::int32_t m13 : 12; +}; + +void test_cv_qualified_bitfields() { + CVBitfieldStruct l1{100, 30000, -500}; // COMPLIANT + + // CV-qualified bitfields follow same rules as regular bitfields + CVBitfieldStruct l2{300, 70000, 3000}; // NON_COMPLIANT + l2.m12 = 70000; // NON_COMPLIANT + + CVBitfieldStruct l3{u8, u16, s16}; // COMPLIANT + l1.m12 = u16; // COMPLIANT + + CVBitfieldStruct l4{u16, u32, s32}; // NON_COMPLIANT +} + +// Test cv-qualified enums +enum CVColour : std::uint16_t { cv_red, cv_green, cv_blue }; + +void test_cv_qualified_enums() { + const CVColour l1 = cv_red; + volatile CVColour l2 = cv_green; + const volatile CVColour l3 = cv_blue; + + u8 = cv_red; // COMPLIANT + u32 = cv_red; // COMPLIANT + u8 = l1; // COMPLIANT - constant fits in uint8_t + u32 = l1; // COMPLIANT + u8 = l2; // NON_COMPLIANT + u32 = l2; // COMPLIANT + u8 = l3; // NON_COMPLIANT + u32 = l3; // COMPLIANT +} + +// Test cv-qualified expressions with operators +void test_cv_qualified_expressions() { + const std::uint8_t l1 = 10; + volatile std::uint8_t l2 = 20; + const volatile std::uint8_t l3 = 30; + + // Expressions with cv-qualified operands still follow expression rules + u8 = l1 + l2; // NON_COMPLIANT + u8 = l1 + l3; // NON_COMPLIANT + u8 = l2 + l3; // NON_COMPLIANT + s32 = l1 + l2; // COMPLIANT + s32 = l1 + l3; // COMPLIANT + s32 = l2 + l3; // COMPLIANT + + // Parenthesized cv-qualified expressions are not id-expressions + u32 = (l1); // COMPLIANT - constant expression fits + u32 = (l2); // NON_COMPLIANT + u32 = (l3); // NON_COMPLIANT +} + +// Test cv-qualified aggregate initialization +struct CVAggregate { + const std::uint8_t m1; + volatile std::uint16_t m2; + const volatile std::int32_t m3; +}; + +void test_cv_qualified_aggregate_initialization() { + const std::uint8_t l1 = 42; + volatile std::uint16_t l2 = 1000; + const volatile std::int8_t l3 = -5; + + // CV-qualified aggregate members follow same rules + CVAggregate l4{10, 100, -50}; // COMPLIANT + CVAggregate l5{l1, l2, l3}; // COMPLIANT + CVAggregate l6{300, 70000, l3}; // NON_COMPLIANT +} \ No newline at end of file From 916fb3dc58cced90b34778d2854aa82c805914b8 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Thu, 19 Jun 2025 21:11:26 +0100 Subject: [PATCH 23/57] Rule 7.0.6: Support pointer to member cases - Support assignment to pointer to members - Support pointer-to-member function calls --- .../cpp/misra/StandardConversions.qll | 24 +++- .../NumericAssignmentTypeMismatch.expected | 35 +++++ .../rules/RULE-7-0-6/test_member_pointers.cpp | 128 ++++++++++++++++++ 3 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 cpp/misra/test/rules/RULE-7-0-6/test_member_pointers.cpp diff --git a/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll b/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll index 0f96e6120..d5dbbc886 100644 --- a/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll +++ b/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll @@ -129,7 +129,13 @@ predicate isAssignment(Expr source, NumericType targetType, string context) { isAssignedToBitfield(source, bf) and targetType = getBitFieldType(bf) ) - else targetType = assign.getLValue().getType() + else + exists(Type t | t = assign.getLValue().getType() | + // Unwrap PointerToMemberType e.g `l1.*l2 = x;` + if t instanceof PointerToMemberType + then targetType = t.(PointerToMemberType).getBaseType() + else targetType = t + ) ) or // Variable initialization @@ -157,6 +163,22 @@ predicate isAssignment(Expr source, NumericType targetType, string context) { // Handle varargs - use the fully converted type of the argument call.getTarget().getNumberOfParameters() <= i and targetType = source.getFullyConverted().getType() + or + // A standard expression call + targetType = call.(ExprCall).getExpr().getType().(FunctionPointerIshType).getParameterType(i) + or + // An expression call using the pointer to member operator (.* or ->*) + // This special handling is required because we don't have a CodeQL class representing the call + // to a pointer to member function, but the right hand side is extracted as the -1 child of the + // call + targetType = + call.(ExprCall) + .getChild(-1) + .getType() + .(PointerToMemberType) + .getBaseType() + .(RoutineType) + .getParameterType(i) ) or // Return statement diff --git a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected index 23665ed10..cbc6ff266 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected +++ b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected @@ -170,6 +170,41 @@ | test.cpp:854:29:854:31 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned short'. | | test.cpp:854:34:854:36 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | | test.cpp:864:28:864:30 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test_member_pointers.cpp:25:12:25:14 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_member_pointers.cpp:26:12:26:14 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:27:12:27:14 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_member_pointers.cpp:28:12:28:14 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. | +| test_member_pointers.cpp:32:13:32:15 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_member_pointers.cpp:33:13:33:15 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:34:13:34:15 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_member_pointers.cpp:35:13:35:15 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. | +| test_member_pointers.cpp:42:12:42:14 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_member_pointers.cpp:43:12:43:14 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:44:12:44:14 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_member_pointers.cpp:45:12:45:14 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. | +| test_member_pointers.cpp:49:13:49:15 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_member_pointers.cpp:50:13:50:15 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:51:13:51:15 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_member_pointers.cpp:52:13:52:15 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. | +| test_member_pointers.cpp:58:11:58:13 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | +| test_member_pointers.cpp:60:11:60:13 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:66:11:66:13 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:67:11:67:13 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_member_pointers.cpp:82:6:82:8 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_member_pointers.cpp:83:6:83:8 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:84:6:84:8 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_member_pointers.cpp:85:6:85:8 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. | +| test_member_pointers.cpp:90:6:90:8 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_member_pointers.cpp:91:6:91:8 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:92:6:92:8 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_member_pointers.cpp:93:6:93:8 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. | +| test_member_pointers.cpp:97:7:97:9 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | +| test_member_pointers.cpp:100:7:100:9 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:106:41:106:43 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:107:41:107:43 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_member_pointers.cpp:125:12:125:14 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int64_t'. | +| test_member_pointers.cpp:126:12:126:14 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int64_t'. | +| test_member_pointers.cpp:127:12:127:14 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int64_t'. | | test_specified.cpp:37:8:37:9 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | | test_specified.cpp:38:8:38:9 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'uint8_t'. | | test_specified.cpp:41:8:41:9 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'int8_t'. | diff --git a/cpp/misra/test/rules/RULE-7-0-6/test_member_pointers.cpp b/cpp/misra/test/rules/RULE-7-0-6/test_member_pointers.cpp new file mode 100644 index 000000000..d6f3de7eb --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-6/test_member_pointers.cpp @@ -0,0 +1,128 @@ +#include + +std::int16_t s16; +std::uint16_t u16; +std::int32_t s32; +std::uint32_t u32; +std::int64_t s64; +std::uint64_t u64; + +struct MemberFunctionPointerTest { + void mf15(std::int32_t l1) {} + void mf15(std::uint32_t l1) {} + void mf16(std::int32_t l1) {} +}; + +void test_pointer_to_member_functions() { + MemberFunctionPointerTest l1; + MemberFunctionPointerTest *l6 = &l1; + + // mf15 is overload independent when used as a member function pointer + void (MemberFunctionPointerTest::*l2)(std::int32_t) = + &MemberFunctionPointerTest::mf15; + (l1.*l2)(s16); // COMPLIANT - widening of id-expression + (l1.*l2)(s32); // COMPLIANT - type match + (l1.*l2)(s64); // NON_COMPLIANT - narrowing + (l1.*l2)(u16); // NON_COMPLIANT - wrong sign + (l1.*l2)(u32); // NON_COMPLIANT - wrong sign + (l1.*l2)(u64); // NON_COMPLIANT - wrong sign and narrowing + + (l6->*l2)(s16); // COMPLIANT - is widening of id-expression + (l6->*l2)(s32); // COMPLIANT - type match + (l6->*l2)(s64); // NON_COMPLIANT - narrowing + (l6->*l2)(u16); // NON_COMPLIANT - wrong sign + (l6->*l2)(u32); // NON_COMPLIANT - wrong sign + (l6->*l2)(u64); // NON_COMPLIANT - wrong sign and narrowing + + // mf16 is overload independent when used as a member function pointer + void (MemberFunctionPointerTest::*l3)(std::int32_t) = + &MemberFunctionPointerTest::mf16; + (l1.*l3)(s16); // COMPLIANT - widening of id-expression + (l1.*l3)(s32); // COMPLIANT - type match + (l1.*l3)(s64); // NON_COMPLIANT - narrowing + (l1.*l3)(u16); // NON_COMPLIANT - wrong sign + (l1.*l3)(u32); // NON_COMPLIANT - wrong sign + (l1.*l3)(u64); // NON_COMPLIANT - wrong sign and narrowing + + (l6->*l3)(s16); // COMPLIANT - widening of id-expression + (l6->*l3)(s32); // COMPLIANT - type match + (l6->*l3)(s64); // NON_COMPLIANT - narrowing + (l6->*l3)(u16); // NON_COMPLIANT - wrong sign + (l6->*l3)(u32); // NON_COMPLIANT - wrong sign + (l6->*l3)(u64); // NON_COMPLIANT - wrong sign and narrowing + + // Direct calls for comparison + + // mf15 is not overload-independent, so it should only be compliant + // where an exact type of an overload is used + l1.mf15(s16); // NON_COMPLIANT - widening not allowed + l1.mf15(s32); // COMPLIANT - exact type match + l1.mf15(u16); // NON_COMPLIANT - widening not allowed + l1.mf15(u32); // COMPLIANT - exact type match + + // A qualified call to mf16 is overload-independent + l1.mf16(s16); // COMPLIANT - widening of id-expression + l1.mf16(s32); // COMPLIANT - exact type match + l1.mf16(u16); // NON_COMPLIANT + l1.mf16(u32); // NON_COMPLIANT +} + +// Test static member function pointers - should be overload-independent +struct StaticMemberFunctionPointerTest { + static void mf19(std::int32_t l1) {} + static void mf19(std::uint32_t l1) {} + static void mf20(std::int32_t l1) {} +}; + +void test_static_member_function_pointers() { + // Static member function pointers - overload-independent + void (*l1)(std::int32_t) = &StaticMemberFunctionPointerTest::mf19; + l1(s16); // COMPLIANT - widening of id-expression + l1(s32); // COMPLIANT - type match + l1(s64); // NON_COMPLIANT - narrowing + l1(u16); // NON_COMPLIANT - wrong sign + l1(u32); // NON_COMPLIANT - wrong sign + l1(u64); // NON_COMPLIANT - wrong sign and narrowing + + void (*l2)(std::int32_t) = &StaticMemberFunctionPointerTest::mf20; + l2(s16); // COMPLIANT - widening of id-expression + l2(s32); // COMPLIANT - type match + l2(s64); // NON_COMPLIANT - narrowing + l2(u16); // NON_COMPLIANT - wrong sign + l2(u32); // NON_COMPLIANT - wrong sign + l2(u64); // NON_COMPLIANT - wrong sign and narrowing + + // Direct calls for comparison - not overload-independent + StaticMemberFunctionPointerTest::mf19( + s16); // NON_COMPLIANT - widening not allowed + StaticMemberFunctionPointerTest::mf19(s32); // COMPLIANT - exact type match + StaticMemberFunctionPointerTest::mf19( + u16); // NON_COMPLIANT - widening not allowed + StaticMemberFunctionPointerTest::mf19(u32); // COMPLIANT - exact type match + + StaticMemberFunctionPointerTest::mf20( + s16); // COMPLIANT - widening of id-expression + StaticMemberFunctionPointerTest::mf20(s32); // COMPLIANT - exact type match + StaticMemberFunctionPointerTest::mf20(u16); // NON_COMPLIANT + StaticMemberFunctionPointerTest::mf20(u32); // NON_COMPLIANT +} + +// Test member data pointers - not function calls, but test assignment to them +struct MemberDataPointerTest { + std::int64_t m1; + std::int64_t m2 : 10; +}; + +void test_member_data_pointers() { + MemberDataPointerTest l1; + + // Member data pointer assignments - follow normal assignment rules + std::int64_t MemberDataPointerTest::*l2 = &MemberDataPointerTest::m1; + + l1.*l2 = s16; // COMPLIANT - widening conversion allowed + l1.*l2 = s32; // COMPLIANT - widening conversion allowed + l1.*l2 = s64; // COMPLIANT + l1.*l2 = u16; // NON_COMPLIANT - signedness violation + l1.*l2 = u32; // NON_COMPLIANT - different signedness/size + l1.*l2 = u64; // NON_COMPLIANT - different signedness +} \ No newline at end of file From 2ec2814363fc9ccd72fa72c375539d5d733cb6b2 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Thu, 19 Jun 2025 23:15:21 +0100 Subject: [PATCH 24/57] Rule 7.0.6: Support functions with default parameters Overload independence should consider what parameters are default. --- .../NumericAssignmentTypeMismatch.ql | 9 +- .../NumericAssignmentTypeMismatch.expected | 249 +++++++++--------- cpp/misra/test/rules/RULE-7-0-6/test.cpp | 14 +- 3 files changed, 144 insertions(+), 128 deletions(-) diff --git a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql index 60b661911..718f7c48c 100644 --- a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql +++ b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql @@ -119,6 +119,10 @@ predicate isNonExtensible(Call c) { c.getTarget() instanceof Operator } +int getMinimumNumberOfParameters(Function f) { + result = count(Parameter p | p = f.getAParameter() and not p.hasInitializer() | p) +} + predicate isOverloadIndependent(Call call, Expr arg) { arg = call.getAnArgument() and ( @@ -135,7 +139,10 @@ predicate isOverloadIndependent(Call call, Expr arg) { // so check the templates overloads overload = target.(FunctionTemplateInstantiation).getTemplate().getAnOverload() ) and - overload.getNumberOfParameters() = call.getNumberOfArguments() and + // Check that the overload accepts the number of arguments provided by this call, + // considering parameters with default values may be omitted in the call + overload.getNumberOfParameters() >= call.getNumberOfArguments() and + getMinimumNumberOfParameters(overload) <= call.getNumberOfArguments() and call.getArgument(i) = arg | // Check that the parameter types match diff --git a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected index cbc6ff266..4a68c1363 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected +++ b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected @@ -46,130 +46,131 @@ | test.cpp:252:6:252:7 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'int32_t'. | | test.cpp:261:6:261:6 | 2 | Assignment between incompatible numeric types from 'int' to 'long'. | | test.cpp:269:14:269:15 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'int'. | -| test.cpp:281:6:281:8 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'size_t'. | -| test.cpp:303:9:303:10 | 42 | Assignment between incompatible numeric types from 'int' to 'long'. | -| test.cpp:332:23:332:24 | 42 | Assignment between incompatible numeric types from 'int' to 'unsigned long'. | -| test.cpp:342:19:342:25 | ... + ... | Assignment between incompatible numeric types from 'int' to 'int16_t'. | -| test.cpp:347:10:347:12 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:380:8:380:9 | l6 | Assignment between incompatible numeric types from 'uint32_t &' to 'uint8_t'. | -| test.cpp:381:8:381:9 | l7 | Assignment between incompatible numeric types from 'int8_t &' to 'uint8_t'. | -| test.cpp:382:9:382:10 | l8 | Assignment between incompatible numeric types from 'float &' to 'int32_t'. | -| test.cpp:393:6:393:7 | l3 | Assignment between incompatible numeric types from 'uint8_t &' to 'int64_t'. | -| test.cpp:394:6:394:7 | l4 | Assignment between incompatible numeric types from 'uint16_t &' to 'int32_t'. | -| test.cpp:405:8:405:9 | l3 | Assignment between incompatible numeric types from 'uint8_t &' to 'int8_t'. | -| test.cpp:406:8:406:9 | l4 | Assignment between incompatible numeric types from 'int8_t &' to 'uint8_t'. | -| test.cpp:419:9:419:10 | l4 | Assignment between incompatible numeric types from 'float &' to 'int32_t'. | -| test.cpp:420:7:420:8 | l5 | Assignment between incompatible numeric types from 'double &' to 'float'. | -| test.cpp:421:7:421:8 | l6 | Assignment between incompatible numeric types from 'int32_t &' to 'float'. | -| test.cpp:432:8:432:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | -| test.cpp:450:7:450:8 | l3 | Assignment between incompatible numeric types from 'uint16_t &' to 'uint32_t'. | -| test.cpp:453:7:453:8 | l5 | Assignment between incompatible numeric types from 'uint64_t &' to 'uint32_t'. | -| test.cpp:573:8:573:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:574:8:574:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:578:8:578:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:579:8:579:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:583:7:583:8 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:584:7:584:8 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:588:8:588:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:589:8:589:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:593:8:593:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:594:8:594:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:598:8:598:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:599:8:599:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:603:8:603:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:604:8:604:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:608:8:608:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:609:8:609:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:613:9:613:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:614:9:614:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:618:9:618:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:619:9:619:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:624:9:624:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:625:9:625:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:629:9:629:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:630:9:630:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:634:8:634:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:635:8:635:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:639:9:639:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:640:9:640:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:644:8:644:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:645:8:645:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:649:9:649:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:650:9:650:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:655:6:655:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:656:6:656:7 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:661:6:661:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:662:6:662:7 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:664:10:664:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:665:6:665:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:666:6:666:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:666:10:666:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:671:8:671:9 | l3 | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | -| test.cpp:672:8:672:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:673:8:673:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:677:9:677:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:678:9:678:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:682:9:682:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:683:9:683:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:687:9:687:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:688:9:688:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:692:9:692:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:693:9:693:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:697:9:697:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:698:9:698:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:702:9:702:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:703:9:703:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:707:9:707:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:708:9:708:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:712:9:712:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:713:9:713:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:717:10:717:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:718:10:718:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:722:10:722:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:723:10:723:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:728:3:728:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:729:3:729:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:733:3:733:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:734:3:734:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:738:3:738:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:739:3:739:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:751:8:751:12 | 42.0 | Assignment between incompatible numeric types from 'float' to 'int32_t'. | -| test.cpp:766:8:766:10 | 42 | Assignment between incompatible numeric types from 'long' to 'int32_t'. | -| test.cpp:767:8:767:11 | 42 | Assignment between incompatible numeric types from 'long long' to 'int32_t'. | -| test.cpp:768:8:768:10 | 42 | Assignment between incompatible numeric types from 'unsigned int' to 'int32_t'. | -| test.cpp:786:22:786:24 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | -| test.cpp:788:26:788:28 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'uint16_t'. | -| test.cpp:790:31:790:33 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:792:22:792:24 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'float'. | -| test.cpp:795:22:795:24 | 256 | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | -| test.cpp:795:27:795:31 | 65536 | Assignment between incompatible numeric types from 'int' to 'uint16_t'. | -| test.cpp:796:22:796:33 | 2147483648 | Assignment between incompatible numeric types from 'long long' to 'int32_t'. | -| test.cpp:808:22:808:24 | 300 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | -| test.cpp:808:27:808:29 | 400 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | -| test.cpp:808:32:808:34 | 500 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | -| test.cpp:809:22:809:23 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'unsigned char'. | -| test.cpp:809:26:809:27 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'unsigned char'. | -| test.cpp:809:30:809:31 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'unsigned char'. | -| test.cpp:810:22:810:24 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | -| test.cpp:810:27:810:29 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | -| test.cpp:810:32:810:34 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | -| test.cpp:815:26:815:28 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | -| test.cpp:815:31:815:33 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | -| test.cpp:815:38:815:40 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | -| test.cpp:815:43:815:45 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | -| test.cpp:816:26:816:27 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'signed short'. | -| test.cpp:816:30:816:31 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'signed short'. | -| test.cpp:817:26:817:27 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'signed short'. | -| test.cpp:817:30:817:31 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'signed short'. | -| test.cpp:833:8:833:10 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | -| test.cpp:837:7:837:9 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'uint32_t'. | -| test.cpp:853:24:853:26 | 300 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | -| test.cpp:853:29:853:33 | 70000 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | -| test.cpp:853:36:853:39 | 5000 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | -| test.cpp:854:24:854:26 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | -| test.cpp:854:29:854:31 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned short'. | -| test.cpp:854:34:854:36 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | -| test.cpp:864:28:864:30 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test.cpp:287:6:287:8 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'size_t'. | +| test.cpp:292:12:292:14 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | +| test.cpp:311:9:311:10 | 42 | Assignment between incompatible numeric types from 'int' to 'long'. | +| test.cpp:340:23:340:24 | 42 | Assignment between incompatible numeric types from 'int' to 'unsigned long'. | +| test.cpp:350:19:350:25 | ... + ... | Assignment between incompatible numeric types from 'int' to 'int16_t'. | +| test.cpp:355:10:355:12 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:388:8:388:9 | l6 | Assignment between incompatible numeric types from 'uint32_t &' to 'uint8_t'. | +| test.cpp:389:8:389:9 | l7 | Assignment between incompatible numeric types from 'int8_t &' to 'uint8_t'. | +| test.cpp:390:9:390:10 | l8 | Assignment between incompatible numeric types from 'float &' to 'int32_t'. | +| test.cpp:401:6:401:7 | l3 | Assignment between incompatible numeric types from 'uint8_t &' to 'int64_t'. | +| test.cpp:402:6:402:7 | l4 | Assignment between incompatible numeric types from 'uint16_t &' to 'int32_t'. | +| test.cpp:413:8:413:9 | l3 | Assignment between incompatible numeric types from 'uint8_t &' to 'int8_t'. | +| test.cpp:414:8:414:9 | l4 | Assignment between incompatible numeric types from 'int8_t &' to 'uint8_t'. | +| test.cpp:427:9:427:10 | l4 | Assignment between incompatible numeric types from 'float &' to 'int32_t'. | +| test.cpp:428:7:428:8 | l5 | Assignment between incompatible numeric types from 'double &' to 'float'. | +| test.cpp:429:7:429:8 | l6 | Assignment between incompatible numeric types from 'int32_t &' to 'float'. | +| test.cpp:440:8:440:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test.cpp:458:7:458:8 | l3 | Assignment between incompatible numeric types from 'uint16_t &' to 'uint32_t'. | +| test.cpp:461:7:461:8 | l5 | Assignment between incompatible numeric types from 'uint64_t &' to 'uint32_t'. | +| test.cpp:581:8:581:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:582:8:582:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:586:8:586:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:587:8:587:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:591:7:591:8 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:592:7:592:8 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:596:8:596:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:597:8:597:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:601:8:601:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:602:8:602:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:606:8:606:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:607:8:607:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:611:8:611:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:612:8:612:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:616:8:616:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:617:8:617:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:621:9:621:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:622:9:622:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:626:9:626:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:627:9:627:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:632:9:632:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:633:9:633:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:637:9:637:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:638:9:638:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:642:8:642:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:643:8:643:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:647:9:647:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:648:9:648:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:652:8:652:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:653:8:653:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:657:9:657:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:658:9:658:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:663:6:663:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:664:6:664:7 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:669:6:669:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:670:6:670:7 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:672:10:672:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:673:6:673:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:674:6:674:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:674:10:674:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:679:8:679:9 | l3 | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | +| test.cpp:680:8:680:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:681:8:681:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:685:9:685:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:686:9:686:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:690:9:690:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:691:9:691:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:695:9:695:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:696:9:696:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:700:9:700:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:701:9:701:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:705:9:705:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:706:9:706:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:710:9:710:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:711:9:711:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:715:9:715:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:716:9:716:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:720:9:720:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:721:9:721:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:725:10:725:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:726:10:726:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:730:10:730:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:731:10:731:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:736:3:736:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:737:3:737:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:741:3:741:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:742:3:742:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:746:3:746:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:747:3:747:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:759:8:759:12 | 42.0 | Assignment between incompatible numeric types from 'float' to 'int32_t'. | +| test.cpp:774:8:774:10 | 42 | Assignment between incompatible numeric types from 'long' to 'int32_t'. | +| test.cpp:775:8:775:11 | 42 | Assignment between incompatible numeric types from 'long long' to 'int32_t'. | +| test.cpp:776:8:776:10 | 42 | Assignment between incompatible numeric types from 'unsigned int' to 'int32_t'. | +| test.cpp:794:22:794:24 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test.cpp:796:26:796:28 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'uint16_t'. | +| test.cpp:798:31:798:33 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:800:22:800:24 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'float'. | +| test.cpp:803:22:803:24 | 256 | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test.cpp:803:27:803:31 | 65536 | Assignment between incompatible numeric types from 'int' to 'uint16_t'. | +| test.cpp:804:22:804:33 | 2147483648 | Assignment between incompatible numeric types from 'long long' to 'int32_t'. | +| test.cpp:816:22:816:24 | 300 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | +| test.cpp:816:27:816:29 | 400 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | +| test.cpp:816:32:816:34 | 500 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | +| test.cpp:817:22:817:23 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'unsigned char'. | +| test.cpp:817:26:817:27 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'unsigned char'. | +| test.cpp:817:30:817:31 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'unsigned char'. | +| test.cpp:818:22:818:24 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test.cpp:818:27:818:29 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test.cpp:818:32:818:34 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test.cpp:823:26:823:28 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | +| test.cpp:823:31:823:33 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | +| test.cpp:823:38:823:40 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | +| test.cpp:823:43:823:45 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | +| test.cpp:824:26:824:27 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'signed short'. | +| test.cpp:824:30:824:31 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'signed short'. | +| test.cpp:825:26:825:27 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'signed short'. | +| test.cpp:825:30:825:31 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'signed short'. | +| test.cpp:841:8:841:10 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test.cpp:845:7:845:9 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'uint32_t'. | +| test.cpp:861:24:861:26 | 300 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | +| test.cpp:861:29:861:33 | 70000 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | +| test.cpp:861:36:861:39 | 5000 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | +| test.cpp:862:24:862:26 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test.cpp:862:29:862:31 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned short'. | +| test.cpp:862:34:862:36 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | +| test.cpp:872:28:872:30 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | | test_member_pointers.cpp:25:12:25:14 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | | test_member_pointers.cpp:26:12:26:14 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | | test_member_pointers.cpp:27:12:27:14 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | diff --git a/cpp/misra/test/rules/RULE-7-0-6/test.cpp b/cpp/misra/test/rules/RULE-7-0-6/test.cpp index f74ce3050..0191325fb 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/test.cpp +++ b/cpp/misra/test/rules/RULE-7-0-6/test.cpp @@ -270,19 +270,27 @@ void test_variadic_functions() { f5("test", s32); // COMPLIANT - already `int`, no promotion needed } -// Test member function calls - not overload-independent struct A { + // first parameter to f6 with two arguments is overload-independent void f6(std::size_t l1, int l2) {} void f6(std::size_t l1, std::string l2) {} - void f7(); + // Different overload, so does not conflict with f6 above + void f6(std::int8_t l1, std::string l2, int x) {} + // Not overload-independent when called with one parameter + // Overload-independent when called with two parameters + void f7(float l1) {} + void f7(std::int32_t l1, int x = 1) {} + void f8(); }; -void A::f7() { +void A::f8() { f6(u32, "answer"); // NON_COMPLIANT - extensible, could call a global // function instead - e.g. `void f6(std::uint32_t l1, // std::string l2)` this->f6(u32, "answer"); // COMPLIANT this->f6(u32, 42); // COMPLIANT + this->f7(s16); // NON_COMPLIANT - no widening as not overload-independent + this->f7(s16, 2); // COMPLIANT - overload-independent, only one target } void test_member_function_overload_independent() { From e659944358097f49a001ed18b98f0df223ea2152 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Thu, 19 Jun 2025 23:23:39 +0100 Subject: [PATCH 25/57] Rule 7.0.6: Ignore deleted overloads --- .../NumericAssignmentTypeMismatch.ql | 3 + .../NumericAssignmentTypeMismatch.expected | 302 ------------------ cpp/misra/test/rules/RULE-7-0-6/test.cpp | 2 + 3 files changed, 5 insertions(+), 302 deletions(-) delete mode 100644 cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected diff --git a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql index 718f7c48c..bcab42fd0 100644 --- a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql +++ b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql @@ -143,6 +143,9 @@ predicate isOverloadIndependent(Call call, Expr arg) { // considering parameters with default values may be omitted in the call overload.getNumberOfParameters() >= call.getNumberOfArguments() and getMinimumNumberOfParameters(overload) <= call.getNumberOfArguments() and + // Ignore deleted overloads + not overload.isDeleted() and + // call.getArgument(i) = arg | // Check that the parameter types match diff --git a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected deleted file mode 100644 index 4a68c1363..000000000 --- a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected +++ /dev/null @@ -1,302 +0,0 @@ -| test.cpp:36:8:36:11 | 300 | Assignment between incompatible numeric types from 'unsigned int' to 'uint8_t'. | -| test.cpp:39:7:39:10 | 0.0 | Assignment between incompatible numeric types from 'float' to 'double'. | -| test.cpp:45:8:45:9 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'uint8_t'. | -| test.cpp:46:8:46:9 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'int8_t'. | -| test.cpp:51:8:51:10 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | -| test.cpp:52:9:52:11 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'uint16_t'. | -| test.cpp:57:7:57:9 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'float'. | -| test.cpp:58:9:58:9 | f | Assignment between incompatible numeric types from 'float' to 'int32_t'. | -| test.cpp:95:12:95:13 | m1 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | -| test.cpp:96:12:96:13 | m2 | Assignment between incompatible numeric types from 'uint16_t' to 'uint64_t'. | -| test.cpp:97:13:97:14 | m1 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | -| test.cpp:98:13:98:14 | m2 | Assignment between incompatible numeric types from 'uint16_t' to 'uint64_t'. | -| test.cpp:103:9:103:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint32_t'. | -| test.cpp:104:10:104:11 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | -| test.cpp:105:35:105:36 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | -| test.cpp:110:8:110:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | -| test.cpp:111:21:111:27 | ... + ... | Assignment between incompatible numeric types from 'int' to 'int16_t'. | -| test.cpp:134:11:134:11 | 4 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | -| test.cpp:137:11:137:13 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | -| test.cpp:141:11:141:13 | 256 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | -| test.cpp:143:11:143:13 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | -| test.cpp:144:11:144:13 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned char'. | -| test.cpp:148:11:148:13 | 512 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | -| test.cpp:149:11:149:15 | 65535 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | -| test.cpp:150:11:150:15 | 65536 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | -| test.cpp:153:11:153:13 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned short'. | -| test.cpp:157:11:157:15 | 65536 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | -| test.cpp:160:11:160:13 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned short'. | -| test.cpp:164:11:164:16 | 131072 | Assignment between incompatible numeric types from 'int' to 'unsigned int'. | -| test.cpp:168:11:168:13 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'unsigned int'. | -| test.cpp:172:11:172:22 | 4294967296 | Assignment between incompatible numeric types from 'unsigned long' to 'unsigned int'. | -| test.cpp:176:11:176:13 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'unsigned int'. | -| test.cpp:180:11:180:21 | 8589934592 | Assignment between incompatible numeric types from 'long' to 'unsigned long'. | -| test.cpp:193:11:193:12 | 16 | Assignment between incompatible numeric types from 'int32_t' to 'signed char'. | -| test.cpp:194:11:194:13 | - ... | Assignment between incompatible numeric types from 'int' to 'signed char'. | -| test.cpp:196:11:196:13 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'signed char'. | -| test.cpp:200:11:200:14 | 2048 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | -| test.cpp:201:11:201:15 | - ... | Assignment between incompatible numeric types from 'int' to 'short'. | -| test.cpp:204:11:204:13 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | -| test.cpp:208:12:208:22 | 134217728 | Assignment between incompatible numeric types from 'long long' to 'int'. | -| test.cpp:209:12:209:23 | - ... | Assignment between incompatible numeric types from 'long long' to 'int'. | -| test.cpp:224:8:224:9 | l1 | Assignment between incompatible numeric types from 'Colour' to 'uint8_t'. | -| test.cpp:234:6:234:8 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'int64_t'. | -| test.cpp:237:6:237:8 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:239:6:239:7 | l1 | Assignment between incompatible numeric types from 'int' to 'int32_t'. | -| test.cpp:252:6:252:7 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'int32_t'. | -| test.cpp:261:6:261:6 | 2 | Assignment between incompatible numeric types from 'int' to 'long'. | -| test.cpp:269:14:269:15 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'int'. | -| test.cpp:287:6:287:8 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'size_t'. | -| test.cpp:292:12:292:14 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | -| test.cpp:311:9:311:10 | 42 | Assignment between incompatible numeric types from 'int' to 'long'. | -| test.cpp:340:23:340:24 | 42 | Assignment between incompatible numeric types from 'int' to 'unsigned long'. | -| test.cpp:350:19:350:25 | ... + ... | Assignment between incompatible numeric types from 'int' to 'int16_t'. | -| test.cpp:355:10:355:12 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:388:8:388:9 | l6 | Assignment between incompatible numeric types from 'uint32_t &' to 'uint8_t'. | -| test.cpp:389:8:389:9 | l7 | Assignment between incompatible numeric types from 'int8_t &' to 'uint8_t'. | -| test.cpp:390:9:390:10 | l8 | Assignment between incompatible numeric types from 'float &' to 'int32_t'. | -| test.cpp:401:6:401:7 | l3 | Assignment between incompatible numeric types from 'uint8_t &' to 'int64_t'. | -| test.cpp:402:6:402:7 | l4 | Assignment between incompatible numeric types from 'uint16_t &' to 'int32_t'. | -| test.cpp:413:8:413:9 | l3 | Assignment between incompatible numeric types from 'uint8_t &' to 'int8_t'. | -| test.cpp:414:8:414:9 | l4 | Assignment between incompatible numeric types from 'int8_t &' to 'uint8_t'. | -| test.cpp:427:9:427:10 | l4 | Assignment between incompatible numeric types from 'float &' to 'int32_t'. | -| test.cpp:428:7:428:8 | l5 | Assignment between incompatible numeric types from 'double &' to 'float'. | -| test.cpp:429:7:429:8 | l6 | Assignment between incompatible numeric types from 'int32_t &' to 'float'. | -| test.cpp:440:8:440:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | -| test.cpp:458:7:458:8 | l3 | Assignment between incompatible numeric types from 'uint16_t &' to 'uint32_t'. | -| test.cpp:461:7:461:8 | l5 | Assignment between incompatible numeric types from 'uint64_t &' to 'uint32_t'. | -| test.cpp:581:8:581:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:582:8:582:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:586:8:586:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:587:8:587:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:591:7:591:8 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:592:7:592:8 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:596:8:596:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:597:8:597:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:601:8:601:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:602:8:602:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:606:8:606:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:607:8:607:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:611:8:611:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:612:8:612:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:616:8:616:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:617:8:617:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:621:9:621:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:622:9:622:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:626:9:626:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:627:9:627:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:632:9:632:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:633:9:633:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:637:9:637:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:638:9:638:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:642:8:642:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:643:8:643:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:647:9:647:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:648:9:648:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:652:8:652:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:653:8:653:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:657:9:657:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:658:9:658:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:663:6:663:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:664:6:664:7 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:669:6:669:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:670:6:670:7 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:672:10:672:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:673:6:673:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:674:6:674:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:674:10:674:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:679:8:679:9 | l3 | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | -| test.cpp:680:8:680:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:681:8:681:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:685:9:685:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:686:9:686:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:690:9:690:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:691:9:691:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:695:9:695:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:696:9:696:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:700:9:700:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:701:9:701:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:705:9:705:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:706:9:706:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:710:9:710:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:711:9:711:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:715:9:715:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:716:9:716:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:720:9:720:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:721:9:721:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:725:10:725:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:726:10:726:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:730:10:730:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:731:10:731:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:736:3:736:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:737:3:737:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:741:3:741:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:742:3:742:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:746:3:746:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:747:3:747:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:759:8:759:12 | 42.0 | Assignment between incompatible numeric types from 'float' to 'int32_t'. | -| test.cpp:774:8:774:10 | 42 | Assignment between incompatible numeric types from 'long' to 'int32_t'. | -| test.cpp:775:8:775:11 | 42 | Assignment between incompatible numeric types from 'long long' to 'int32_t'. | -| test.cpp:776:8:776:10 | 42 | Assignment between incompatible numeric types from 'unsigned int' to 'int32_t'. | -| test.cpp:794:22:794:24 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | -| test.cpp:796:26:796:28 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'uint16_t'. | -| test.cpp:798:31:798:33 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:800:22:800:24 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'float'. | -| test.cpp:803:22:803:24 | 256 | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | -| test.cpp:803:27:803:31 | 65536 | Assignment between incompatible numeric types from 'int' to 'uint16_t'. | -| test.cpp:804:22:804:33 | 2147483648 | Assignment between incompatible numeric types from 'long long' to 'int32_t'. | -| test.cpp:816:22:816:24 | 300 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | -| test.cpp:816:27:816:29 | 400 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | -| test.cpp:816:32:816:34 | 500 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | -| test.cpp:817:22:817:23 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'unsigned char'. | -| test.cpp:817:26:817:27 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'unsigned char'. | -| test.cpp:817:30:817:31 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'unsigned char'. | -| test.cpp:818:22:818:24 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | -| test.cpp:818:27:818:29 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | -| test.cpp:818:32:818:34 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | -| test.cpp:823:26:823:28 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | -| test.cpp:823:31:823:33 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | -| test.cpp:823:38:823:40 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | -| test.cpp:823:43:823:45 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | -| test.cpp:824:26:824:27 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'signed short'. | -| test.cpp:824:30:824:31 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'signed short'. | -| test.cpp:825:26:825:27 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'signed short'. | -| test.cpp:825:30:825:31 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'signed short'. | -| test.cpp:841:8:841:10 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | -| test.cpp:845:7:845:9 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'uint32_t'. | -| test.cpp:861:24:861:26 | 300 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | -| test.cpp:861:29:861:33 | 70000 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | -| test.cpp:861:36:861:39 | 5000 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | -| test.cpp:862:24:862:26 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | -| test.cpp:862:29:862:31 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned short'. | -| test.cpp:862:34:862:36 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | -| test.cpp:872:28:872:30 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | -| test_member_pointers.cpp:25:12:25:14 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test_member_pointers.cpp:26:12:26:14 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | -| test_member_pointers.cpp:27:12:27:14 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test_member_pointers.cpp:28:12:28:14 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. | -| test_member_pointers.cpp:32:13:32:15 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test_member_pointers.cpp:33:13:33:15 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | -| test_member_pointers.cpp:34:13:34:15 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test_member_pointers.cpp:35:13:35:15 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. | -| test_member_pointers.cpp:42:12:42:14 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test_member_pointers.cpp:43:12:43:14 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | -| test_member_pointers.cpp:44:12:44:14 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test_member_pointers.cpp:45:12:45:14 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. | -| test_member_pointers.cpp:49:13:49:15 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test_member_pointers.cpp:50:13:50:15 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | -| test_member_pointers.cpp:51:13:51:15 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test_member_pointers.cpp:52:13:52:15 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. | -| test_member_pointers.cpp:58:11:58:13 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | -| test_member_pointers.cpp:60:11:60:13 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | -| test_member_pointers.cpp:66:11:66:13 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | -| test_member_pointers.cpp:67:11:67:13 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test_member_pointers.cpp:82:6:82:8 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test_member_pointers.cpp:83:6:83:8 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | -| test_member_pointers.cpp:84:6:84:8 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test_member_pointers.cpp:85:6:85:8 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. | -| test_member_pointers.cpp:90:6:90:8 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test_member_pointers.cpp:91:6:91:8 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | -| test_member_pointers.cpp:92:6:92:8 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test_member_pointers.cpp:93:6:93:8 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. | -| test_member_pointers.cpp:97:7:97:9 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | -| test_member_pointers.cpp:100:7:100:9 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | -| test_member_pointers.cpp:106:41:106:43 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | -| test_member_pointers.cpp:107:41:107:43 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test_member_pointers.cpp:125:12:125:14 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int64_t'. | -| test_member_pointers.cpp:126:12:126:14 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int64_t'. | -| test_member_pointers.cpp:127:12:127:14 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int64_t'. | -| test_specified.cpp:37:8:37:9 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | -| test_specified.cpp:38:8:38:9 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'uint8_t'. | -| test_specified.cpp:41:8:41:9 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'int8_t'. | -| test_specified.cpp:42:8:42:9 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'int8_t'. | -| test_specified.cpp:43:9:43:10 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'int16_t'. | -| test_specified.cpp:48:8:48:9 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'uint8_t'. | -| test_specified.cpp:49:9:49:10 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'uint16_t'. | -| test_specified.cpp:50:9:50:10 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'uint32_t'. | -| test_specified.cpp:58:9:58:10 | l7 | Assignment between incompatible numeric types from 'float' to 'int32_t'. | -| test_specified.cpp:59:9:59:10 | l8 | Assignment between incompatible numeric types from 'double' to 'int32_t'. | -| test_specified.cpp:71:7:71:8 | l8 | Assignment between incompatible numeric types from 'double' to 'float'. | -| test_specified.cpp:94:8:94:9 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | -| test_specified.cpp:95:8:95:9 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'uint8_t'. | -| test_specified.cpp:96:9:96:10 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'uint16_t'. | -| test_specified.cpp:97:8:97:9 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'int8_t'. | -| test_specified.cpp:98:8:98:9 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'int8_t'. | -| test_specified.cpp:99:9:99:10 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'int16_t'. | -| test_specified.cpp:102:8:102:9 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'uint8_t'. | -| test_specified.cpp:103:9:103:10 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'uint16_t'. | -| test_specified.cpp:104:9:104:10 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'uint32_t'. | -| test_specified.cpp:105:8:105:9 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'int8_t'. | -| test_specified.cpp:106:9:106:10 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'int16_t'. | -| test_specified.cpp:107:9:107:10 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test_specified.cpp:110:9:110:10 | l7 | Assignment between incompatible numeric types from 'float' to 'int32_t'. | -| test_specified.cpp:111:9:111:10 | l8 | Assignment between incompatible numeric types from 'double' to 'int32_t'. | -| test_specified.cpp:112:7:112:8 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'float'. | -| test_specified.cpp:113:7:113:8 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'float'. | -| test_specified.cpp:114:7:114:8 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'float'. | -| test_specified.cpp:115:7:115:8 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'double'. | -| test_specified.cpp:116:7:116:8 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'double'. | -| test_specified.cpp:117:7:117:8 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'double'. | -| test_specified.cpp:120:7:120:8 | l8 | Assignment between incompatible numeric types from 'double' to 'float'. | -| test_specified.cpp:143:8:143:9 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | -| test_specified.cpp:144:8:144:9 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'uint8_t'. | -| test_specified.cpp:145:9:145:10 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'uint16_t'. | -| test_specified.cpp:146:8:146:9 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'int8_t'. | -| test_specified.cpp:147:8:147:9 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'int8_t'. | -| test_specified.cpp:148:9:148:10 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'int16_t'. | -| test_specified.cpp:151:8:151:9 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'uint8_t'. | -| test_specified.cpp:152:9:152:10 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'uint16_t'. | -| test_specified.cpp:153:9:153:10 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'uint32_t'. | -| test_specified.cpp:154:8:154:9 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'int8_t'. | -| test_specified.cpp:155:9:155:10 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'int16_t'. | -| test_specified.cpp:156:9:156:10 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test_specified.cpp:159:9:159:10 | l7 | Assignment between incompatible numeric types from 'float' to 'int32_t'. | -| test_specified.cpp:160:9:160:10 | l8 | Assignment between incompatible numeric types from 'double' to 'int32_t'. | -| test_specified.cpp:161:7:161:8 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'float'. | -| test_specified.cpp:162:7:162:8 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'float'. | -| test_specified.cpp:163:7:163:8 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'float'. | -| test_specified.cpp:164:7:164:8 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'double'. | -| test_specified.cpp:165:7:165:8 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'double'. | -| test_specified.cpp:166:7:166:8 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'double'. | -| test_specified.cpp:169:7:169:8 | l8 | Assignment between incompatible numeric types from 'double' to 'float'. | -| test_specified.cpp:213:8:213:9 | l6 | Assignment between incompatible numeric types from 'const uint16_t &' to 'uint8_t'. | -| test_specified.cpp:214:8:214:10 | l10 | Assignment between incompatible numeric types from 'volatile uint16_t &' to 'uint8_t'. | -| test_specified.cpp:215:8:215:10 | l14 | Assignment between incompatible numeric types from 'const volatile uint16_t &' to 'uint8_t'. | -| test_specified.cpp:218:8:218:9 | l5 | Assignment between incompatible numeric types from 'const uint8_t &' to 'int8_t'. | -| test_specified.cpp:219:8:219:9 | l7 | Assignment between incompatible numeric types from 'const int8_t &' to 'uint8_t'. | -| test_specified.cpp:220:8:220:9 | l9 | Assignment between incompatible numeric types from 'volatile uint8_t &' to 'int8_t'. | -| test_specified.cpp:221:8:221:10 | l11 | Assignment between incompatible numeric types from 'volatile int8_t &' to 'uint8_t'. | -| test_specified.cpp:222:8:222:10 | l13 | Assignment between incompatible numeric types from 'const volatile uint8_t &' to 'int8_t'. | -| test_specified.cpp:223:8:223:10 | l15 | Assignment between incompatible numeric types from 'const volatile int8_t &' to 'uint8_t'. | -| test_specified.cpp:226:9:226:10 | l8 | Assignment between incompatible numeric types from 'const float &' to 'int32_t'. | -| test_specified.cpp:227:9:227:11 | l12 | Assignment between incompatible numeric types from 'volatile float &' to 'int32_t'. | -| test_specified.cpp:228:9:228:11 | l16 | Assignment between incompatible numeric types from 'const volatile float &' to 'int32_t'. | -| test_specified.cpp:229:7:229:8 | l3 | Assignment between incompatible numeric types from 'int8_t' to 'float'. | -| test_specified.cpp:230:7:230:8 | l7 | Assignment between incompatible numeric types from 'const int8_t &' to 'float'. | -| test_specified.cpp:231:7:231:9 | l11 | Assignment between incompatible numeric types from 'volatile int8_t &' to 'float'. | -| test_specified.cpp:232:7:232:9 | l15 | Assignment between incompatible numeric types from 'const volatile int8_t &' to 'float'. | -| test_specified.cpp:245:7:245:8 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'const uint32_t'. | -| test_specified.cpp:246:7:246:8 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'const uint32_t'. | -| test_specified.cpp:247:7:247:8 | l3 | Assignment between incompatible numeric types from 'int8_t' to 'volatile int64_t'. | -| test_specified.cpp:248:7:248:8 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'const volatile uint16_t'. | -| test_specified.cpp:250:7:250:8 | l3 | Assignment between incompatible numeric types from 'int8_t' to 'const volatile uint16_t'. | -| test_specified.cpp:285:8:285:22 | g4 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | -| test_specified.cpp:287:8:287:19 | s4 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | -| test_specified.cpp:292:9:292:23 | g5 | Assignment between incompatible numeric types from 'int8_t' to 'uint16_t'. | -| test_specified.cpp:294:9:294:20 | s5 | Assignment between incompatible numeric types from 'int8_t' to 'uint16_t'. | -| test_specified.cpp:308:23:308:25 | 300 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | -| test_specified.cpp:308:28:308:32 | 70000 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | -| test_specified.cpp:308:35:308:38 | 3000 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | -| test_specified.cpp:309:12:309:16 | 70000 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | -| test_specified.cpp:314:23:314:25 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | -| test_specified.cpp:314:28:314:30 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned short'. | -| test_specified.cpp:314:33:314:35 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | -| test_specified.cpp:329:8:329:9 | l2 | Assignment between incompatible numeric types from 'CVColour' to 'uint8_t'. | -| test_specified.cpp:331:8:331:9 | l3 | Assignment between incompatible numeric types from 'CVColour' to 'uint8_t'. | -| test_specified.cpp:342:8:342:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | -| test_specified.cpp:343:8:343:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | -| test_specified.cpp:344:8:344:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | -| test_specified.cpp:351:10:351:11 | l2 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | -| test_specified.cpp:352:10:352:11 | l3 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | -| test_specified.cpp:370:18:370:20 | 300 | Assignment between incompatible numeric types from 'int' to 'const uint8_t'. | -| test_specified.cpp:370:23:370:27 | 70000 | Assignment between incompatible numeric types from 'int' to 'volatile uint16_t'. | diff --git a/cpp/misra/test/rules/RULE-7-0-6/test.cpp b/cpp/misra/test/rules/RULE-7-0-6/test.cpp index 0191325fb..cc477d1f3 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/test.cpp +++ b/cpp/misra/test/rules/RULE-7-0-6/test.cpp @@ -276,6 +276,8 @@ struct A { void f6(std::size_t l1, std::string l2) {} // Different overload, so does not conflict with f6 above void f6(std::int8_t l1, std::string l2, int x) {} + // Deleted function, ignored for overload independence calculations + void f6(float l1, float l2) = delete; // Not overload-independent when called with one parameter // Overload-independent when called with two parameters void f7(float l1) {} From c0fe44dd335d76c5a790b10a4d588754222b54ca Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Thu, 19 Jun 2025 23:31:20 +0100 Subject: [PATCH 26/57] Rule 7.0.6: Refactor overload independent code --- .../NumericAssignmentTypeMismatch.ql | 49 ++++++++++--------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql index bcab42fd0..16931f433 100644 --- a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql +++ b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql @@ -123,34 +123,37 @@ int getMinimumNumberOfParameters(Function f) { result = count(Parameter p | p = f.getAParameter() and not p.hasInitializer() | p) } -predicate isOverloadIndependent(Call call, Expr arg) { - arg = call.getAnArgument() and +/** Get an overload of the function f, excluding deleted overloads. */ +Function getAnOverload(Function f) { ( + result = f.getAnOverload() + or + // Instantiated function templates don't directly participate in overload resolution + // so check the templates overloads + result = f.(FunctionTemplateInstantiation).getTemplate().getAnOverload() + ) and + // Exclude deleted overloads + not result.isDeleted() +} + +predicate isOverloadIndependent(Call call, Expr arg) { + exists(int i | arg = call.getArgument(i) | // Call through function pointer call instanceof ExprCall or isNonExtensible(call) and - forall(Function target, Function overload, int i | - target = call.getTarget() and - ( - overload = target.getAnOverload() - or - // Instantiated function templates don't directly participate in overload resolution - // so check the templates overloads - overload = target.(FunctionTemplateInstantiation).getTemplate().getAnOverload() - ) and - // Check that the overload accepts the number of arguments provided by this call, - // considering parameters with default values may be omitted in the call - overload.getNumberOfParameters() >= call.getNumberOfArguments() and - getMinimumNumberOfParameters(overload) <= call.getNumberOfArguments() and - // Ignore deleted overloads - not overload.isDeleted() and - // - call.getArgument(i) = arg - | - // Check that the parameter types match - overload.getParameter(i).getType().getUnspecifiedType() = - target.getParameter(i).getType().getUnspecifiedType() + exists(Function target | target = call.getTarget() | + forall(Function overload | + overload = getAnOverload(target) and + // Check that the overload accepts the number of arguments provided by this call, + // considering parameters with default values may be omitted in the call + overload.getNumberOfParameters() >= call.getNumberOfArguments() and + getMinimumNumberOfParameters(overload) <= call.getNumberOfArguments() + | + // Check that the parameter types match + overload.getParameter(i).getType().getUnspecifiedType() = + target.getParameter(i).getType().getUnspecifiedType() + ) ) ) } From 06ffbfbcab8307c2af16a7810717b8614c8cae8c Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Thu, 19 Jun 2025 23:36:26 +0100 Subject: [PATCH 27/57] Rule 7.0.6: Move aggregate tests to new file --- .../NumericAssignmentTypeMismatch.expected | 302 ++++++++++++++++++ cpp/misra/test/rules/RULE-7-0-6/test.cpp | 99 ------ .../test/rules/RULE-7-0-6/test_aggregate.cpp | 111 +++++++ 3 files changed, 413 insertions(+), 99 deletions(-) create mode 100644 cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected create mode 100644 cpp/misra/test/rules/RULE-7-0-6/test_aggregate.cpp diff --git a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected new file mode 100644 index 000000000..c0c9e50ec --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected @@ -0,0 +1,302 @@ +| test.cpp:36:8:36:11 | 300 | Assignment between incompatible numeric types from 'unsigned int' to 'uint8_t'. | +| test.cpp:39:7:39:10 | 0.0 | Assignment between incompatible numeric types from 'float' to 'double'. | +| test.cpp:45:8:45:9 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'uint8_t'. | +| test.cpp:46:8:46:9 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'int8_t'. | +| test.cpp:51:8:51:10 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test.cpp:52:9:52:11 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'uint16_t'. | +| test.cpp:57:7:57:9 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'float'. | +| test.cpp:58:9:58:9 | f | Assignment between incompatible numeric types from 'float' to 'int32_t'. | +| test.cpp:95:12:95:13 | m1 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | +| test.cpp:96:12:96:13 | m2 | Assignment between incompatible numeric types from 'uint16_t' to 'uint64_t'. | +| test.cpp:97:13:97:14 | m1 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | +| test.cpp:98:13:98:14 | m2 | Assignment between incompatible numeric types from 'uint16_t' to 'uint64_t'. | +| test.cpp:103:9:103:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint32_t'. | +| test.cpp:104:10:104:11 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | +| test.cpp:105:35:105:36 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | +| test.cpp:110:8:110:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test.cpp:111:21:111:27 | ... + ... | Assignment between incompatible numeric types from 'int' to 'int16_t'. | +| test.cpp:134:11:134:11 | 4 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | +| test.cpp:137:11:137:13 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test.cpp:141:11:141:13 | 256 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | +| test.cpp:143:11:143:13 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test.cpp:144:11:144:13 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned char'. | +| test.cpp:148:11:148:13 | 512 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | +| test.cpp:149:11:149:15 | 65535 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | +| test.cpp:150:11:150:15 | 65536 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | +| test.cpp:153:11:153:13 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned short'. | +| test.cpp:157:11:157:15 | 65536 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | +| test.cpp:160:11:160:13 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned short'. | +| test.cpp:164:11:164:16 | 131072 | Assignment between incompatible numeric types from 'int' to 'unsigned int'. | +| test.cpp:168:11:168:13 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'unsigned int'. | +| test.cpp:172:11:172:22 | 4294967296 | Assignment between incompatible numeric types from 'unsigned long' to 'unsigned int'. | +| test.cpp:176:11:176:13 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'unsigned int'. | +| test.cpp:180:11:180:21 | 8589934592 | Assignment between incompatible numeric types from 'long' to 'unsigned long'. | +| test.cpp:193:11:193:12 | 16 | Assignment between incompatible numeric types from 'int32_t' to 'signed char'. | +| test.cpp:194:11:194:13 | - ... | Assignment between incompatible numeric types from 'int' to 'signed char'. | +| test.cpp:196:11:196:13 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'signed char'. | +| test.cpp:200:11:200:14 | 2048 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | +| test.cpp:201:11:201:15 | - ... | Assignment between incompatible numeric types from 'int' to 'short'. | +| test.cpp:204:11:204:13 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | +| test.cpp:208:12:208:22 | 134217728 | Assignment between incompatible numeric types from 'long long' to 'int'. | +| test.cpp:209:12:209:23 | - ... | Assignment between incompatible numeric types from 'long long' to 'int'. | +| test.cpp:224:8:224:9 | l1 | Assignment between incompatible numeric types from 'Colour' to 'uint8_t'. | +| test.cpp:234:6:234:8 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'int64_t'. | +| test.cpp:237:6:237:8 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:239:6:239:7 | l1 | Assignment between incompatible numeric types from 'int' to 'int32_t'. | +| test.cpp:252:6:252:7 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'int32_t'. | +| test.cpp:261:6:261:6 | 2 | Assignment between incompatible numeric types from 'int' to 'long'. | +| test.cpp:269:14:269:15 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'int'. | +| test.cpp:289:6:289:8 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'size_t'. | +| test.cpp:294:12:294:14 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | +| test.cpp:313:9:313:10 | 42 | Assignment between incompatible numeric types from 'int' to 'long'. | +| test.cpp:342:23:342:24 | 42 | Assignment between incompatible numeric types from 'int' to 'unsigned long'. | +| test.cpp:352:19:352:25 | ... + ... | Assignment between incompatible numeric types from 'int' to 'int16_t'. | +| test.cpp:357:10:357:12 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:390:8:390:9 | l6 | Assignment between incompatible numeric types from 'uint32_t &' to 'uint8_t'. | +| test.cpp:391:8:391:9 | l7 | Assignment between incompatible numeric types from 'int8_t &' to 'uint8_t'. | +| test.cpp:392:9:392:10 | l8 | Assignment between incompatible numeric types from 'float &' to 'int32_t'. | +| test.cpp:403:6:403:7 | l3 | Assignment between incompatible numeric types from 'uint8_t &' to 'int64_t'. | +| test.cpp:404:6:404:7 | l4 | Assignment between incompatible numeric types from 'uint16_t &' to 'int32_t'. | +| test.cpp:415:8:415:9 | l3 | Assignment between incompatible numeric types from 'uint8_t &' to 'int8_t'. | +| test.cpp:416:8:416:9 | l4 | Assignment between incompatible numeric types from 'int8_t &' to 'uint8_t'. | +| test.cpp:429:9:429:10 | l4 | Assignment between incompatible numeric types from 'float &' to 'int32_t'. | +| test.cpp:430:7:430:8 | l5 | Assignment between incompatible numeric types from 'double &' to 'float'. | +| test.cpp:431:7:431:8 | l6 | Assignment between incompatible numeric types from 'int32_t &' to 'float'. | +| test.cpp:442:8:442:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test.cpp:460:7:460:8 | l3 | Assignment between incompatible numeric types from 'uint16_t &' to 'uint32_t'. | +| test.cpp:463:7:463:8 | l5 | Assignment between incompatible numeric types from 'uint64_t &' to 'uint32_t'. | +| test.cpp:583:8:583:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:584:8:584:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:588:8:588:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:589:8:589:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:593:7:593:8 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:594:7:594:8 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:598:8:598:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:599:8:599:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:603:8:603:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:604:8:604:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:608:8:608:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:609:8:609:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:613:8:613:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:614:8:614:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:618:8:618:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:619:8:619:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:623:9:623:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:624:9:624:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:628:9:628:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:629:9:629:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:634:9:634:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:635:9:635:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:639:9:639:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:640:9:640:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:644:8:644:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:645:8:645:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:649:9:649:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:650:9:650:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:654:8:654:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:655:8:655:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:659:9:659:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:660:9:660:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:665:6:665:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:666:6:666:7 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:671:6:671:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:672:6:672:7 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:674:10:674:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:675:6:675:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:676:6:676:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:676:10:676:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:681:8:681:9 | l3 | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | +| test.cpp:682:8:682:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:683:8:683:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:687:9:687:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:688:9:688:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:692:9:692:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:693:9:693:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:697:9:697:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:698:9:698:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:702:9:702:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:703:9:703:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:707:9:707:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:708:9:708:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:712:9:712:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:713:9:713:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:717:9:717:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:718:9:718:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:722:9:722:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:723:9:723:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:727:10:727:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:728:10:728:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:732:10:732:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:733:10:733:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:738:3:738:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:739:3:739:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:743:3:743:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:744:3:744:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:748:3:748:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:749:3:749:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:761:8:761:12 | 42.0 | Assignment between incompatible numeric types from 'float' to 'int32_t'. | +| test.cpp:776:8:776:10 | 42 | Assignment between incompatible numeric types from 'long' to 'int32_t'. | +| test.cpp:777:8:777:11 | 42 | Assignment between incompatible numeric types from 'long long' to 'int32_t'. | +| test.cpp:778:8:778:10 | 42 | Assignment between incompatible numeric types from 'unsigned int' to 'int32_t'. | +| test_aggregate.cpp:29:22:29:24 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test_aggregate.cpp:31:26:31:28 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'uint16_t'. | +| test_aggregate.cpp:33:31:33:33 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_aggregate.cpp:35:22:35:24 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'float'. | +| test_aggregate.cpp:38:22:38:24 | 256 | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test_aggregate.cpp:38:27:38:31 | 65536 | Assignment between incompatible numeric types from 'int' to 'uint16_t'. | +| test_aggregate.cpp:39:22:39:33 | 2147483648 | Assignment between incompatible numeric types from 'long long' to 'int32_t'. | +| test_aggregate.cpp:51:22:51:24 | 300 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | +| test_aggregate.cpp:51:27:51:29 | 400 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | +| test_aggregate.cpp:51:32:51:34 | 500 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | +| test_aggregate.cpp:52:22:52:23 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'unsigned char'. | +| test_aggregate.cpp:52:26:52:27 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'unsigned char'. | +| test_aggregate.cpp:52:30:52:31 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'unsigned char'. | +| test_aggregate.cpp:53:22:53:24 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test_aggregate.cpp:53:27:53:29 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test_aggregate.cpp:53:32:53:34 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test_aggregate.cpp:58:26:58:28 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | +| test_aggregate.cpp:58:31:58:33 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | +| test_aggregate.cpp:58:38:58:40 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | +| test_aggregate.cpp:58:43:58:45 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | +| test_aggregate.cpp:59:26:59:27 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'signed short'. | +| test_aggregate.cpp:59:30:59:31 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'signed short'. | +| test_aggregate.cpp:60:26:60:27 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'signed short'. | +| test_aggregate.cpp:60:30:60:31 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'signed short'. | +| test_aggregate.cpp:76:8:76:10 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test_aggregate.cpp:80:7:80:9 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'uint32_t'. | +| test_aggregate.cpp:96:24:96:26 | 300 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | +| test_aggregate.cpp:96:29:96:33 | 70000 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | +| test_aggregate.cpp:96:36:96:39 | 5000 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | +| test_aggregate.cpp:97:24:97:26 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test_aggregate.cpp:97:29:97:31 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned short'. | +| test_aggregate.cpp:97:34:97:36 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | +| test_aggregate.cpp:107:28:107:30 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test_member_pointers.cpp:25:12:25:14 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_member_pointers.cpp:26:12:26:14 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:27:12:27:14 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_member_pointers.cpp:28:12:28:14 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. | +| test_member_pointers.cpp:32:13:32:15 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_member_pointers.cpp:33:13:33:15 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:34:13:34:15 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_member_pointers.cpp:35:13:35:15 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. | +| test_member_pointers.cpp:42:12:42:14 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_member_pointers.cpp:43:12:43:14 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:44:12:44:14 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_member_pointers.cpp:45:12:45:14 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. | +| test_member_pointers.cpp:49:13:49:15 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_member_pointers.cpp:50:13:50:15 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:51:13:51:15 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_member_pointers.cpp:52:13:52:15 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. | +| test_member_pointers.cpp:58:11:58:13 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | +| test_member_pointers.cpp:60:11:60:13 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:66:11:66:13 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:67:11:67:13 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_member_pointers.cpp:82:6:82:8 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_member_pointers.cpp:83:6:83:8 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:84:6:84:8 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_member_pointers.cpp:85:6:85:8 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. | +| test_member_pointers.cpp:90:6:90:8 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_member_pointers.cpp:91:6:91:8 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:92:6:92:8 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_member_pointers.cpp:93:6:93:8 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. | +| test_member_pointers.cpp:97:7:97:9 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | +| test_member_pointers.cpp:100:7:100:9 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:106:41:106:43 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:107:41:107:43 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_member_pointers.cpp:125:12:125:14 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int64_t'. | +| test_member_pointers.cpp:126:12:126:14 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int64_t'. | +| test_member_pointers.cpp:127:12:127:14 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int64_t'. | +| test_specified.cpp:37:8:37:9 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test_specified.cpp:38:8:38:9 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'uint8_t'. | +| test_specified.cpp:41:8:41:9 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'int8_t'. | +| test_specified.cpp:42:8:42:9 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'int8_t'. | +| test_specified.cpp:43:9:43:10 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'int16_t'. | +| test_specified.cpp:48:8:48:9 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'uint8_t'. | +| test_specified.cpp:49:9:49:10 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'uint16_t'. | +| test_specified.cpp:50:9:50:10 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'uint32_t'. | +| test_specified.cpp:58:9:58:10 | l7 | Assignment between incompatible numeric types from 'float' to 'int32_t'. | +| test_specified.cpp:59:9:59:10 | l8 | Assignment between incompatible numeric types from 'double' to 'int32_t'. | +| test_specified.cpp:71:7:71:8 | l8 | Assignment between incompatible numeric types from 'double' to 'float'. | +| test_specified.cpp:94:8:94:9 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test_specified.cpp:95:8:95:9 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'uint8_t'. | +| test_specified.cpp:96:9:96:10 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'uint16_t'. | +| test_specified.cpp:97:8:97:9 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'int8_t'. | +| test_specified.cpp:98:8:98:9 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'int8_t'. | +| test_specified.cpp:99:9:99:10 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'int16_t'. | +| test_specified.cpp:102:8:102:9 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'uint8_t'. | +| test_specified.cpp:103:9:103:10 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'uint16_t'. | +| test_specified.cpp:104:9:104:10 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'uint32_t'. | +| test_specified.cpp:105:8:105:9 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'int8_t'. | +| test_specified.cpp:106:9:106:10 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'int16_t'. | +| test_specified.cpp:107:9:107:10 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_specified.cpp:110:9:110:10 | l7 | Assignment between incompatible numeric types from 'float' to 'int32_t'. | +| test_specified.cpp:111:9:111:10 | l8 | Assignment between incompatible numeric types from 'double' to 'int32_t'. | +| test_specified.cpp:112:7:112:8 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'float'. | +| test_specified.cpp:113:7:113:8 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'float'. | +| test_specified.cpp:114:7:114:8 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'float'. | +| test_specified.cpp:115:7:115:8 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'double'. | +| test_specified.cpp:116:7:116:8 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'double'. | +| test_specified.cpp:117:7:117:8 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'double'. | +| test_specified.cpp:120:7:120:8 | l8 | Assignment between incompatible numeric types from 'double' to 'float'. | +| test_specified.cpp:143:8:143:9 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test_specified.cpp:144:8:144:9 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'uint8_t'. | +| test_specified.cpp:145:9:145:10 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'uint16_t'. | +| test_specified.cpp:146:8:146:9 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'int8_t'. | +| test_specified.cpp:147:8:147:9 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'int8_t'. | +| test_specified.cpp:148:9:148:10 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'int16_t'. | +| test_specified.cpp:151:8:151:9 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'uint8_t'. | +| test_specified.cpp:152:9:152:10 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'uint16_t'. | +| test_specified.cpp:153:9:153:10 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'uint32_t'. | +| test_specified.cpp:154:8:154:9 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'int8_t'. | +| test_specified.cpp:155:9:155:10 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'int16_t'. | +| test_specified.cpp:156:9:156:10 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_specified.cpp:159:9:159:10 | l7 | Assignment between incompatible numeric types from 'float' to 'int32_t'. | +| test_specified.cpp:160:9:160:10 | l8 | Assignment between incompatible numeric types from 'double' to 'int32_t'. | +| test_specified.cpp:161:7:161:8 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'float'. | +| test_specified.cpp:162:7:162:8 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'float'. | +| test_specified.cpp:163:7:163:8 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'float'. | +| test_specified.cpp:164:7:164:8 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'double'. | +| test_specified.cpp:165:7:165:8 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'double'. | +| test_specified.cpp:166:7:166:8 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'double'. | +| test_specified.cpp:169:7:169:8 | l8 | Assignment between incompatible numeric types from 'double' to 'float'. | +| test_specified.cpp:213:8:213:9 | l6 | Assignment between incompatible numeric types from 'const uint16_t &' to 'uint8_t'. | +| test_specified.cpp:214:8:214:10 | l10 | Assignment between incompatible numeric types from 'volatile uint16_t &' to 'uint8_t'. | +| test_specified.cpp:215:8:215:10 | l14 | Assignment between incompatible numeric types from 'const volatile uint16_t &' to 'uint8_t'. | +| test_specified.cpp:218:8:218:9 | l5 | Assignment between incompatible numeric types from 'const uint8_t &' to 'int8_t'. | +| test_specified.cpp:219:8:219:9 | l7 | Assignment between incompatible numeric types from 'const int8_t &' to 'uint8_t'. | +| test_specified.cpp:220:8:220:9 | l9 | Assignment between incompatible numeric types from 'volatile uint8_t &' to 'int8_t'. | +| test_specified.cpp:221:8:221:10 | l11 | Assignment between incompatible numeric types from 'volatile int8_t &' to 'uint8_t'. | +| test_specified.cpp:222:8:222:10 | l13 | Assignment between incompatible numeric types from 'const volatile uint8_t &' to 'int8_t'. | +| test_specified.cpp:223:8:223:10 | l15 | Assignment between incompatible numeric types from 'const volatile int8_t &' to 'uint8_t'. | +| test_specified.cpp:226:9:226:10 | l8 | Assignment between incompatible numeric types from 'const float &' to 'int32_t'. | +| test_specified.cpp:227:9:227:11 | l12 | Assignment between incompatible numeric types from 'volatile float &' to 'int32_t'. | +| test_specified.cpp:228:9:228:11 | l16 | Assignment between incompatible numeric types from 'const volatile float &' to 'int32_t'. | +| test_specified.cpp:229:7:229:8 | l3 | Assignment between incompatible numeric types from 'int8_t' to 'float'. | +| test_specified.cpp:230:7:230:8 | l7 | Assignment between incompatible numeric types from 'const int8_t &' to 'float'. | +| test_specified.cpp:231:7:231:9 | l11 | Assignment between incompatible numeric types from 'volatile int8_t &' to 'float'. | +| test_specified.cpp:232:7:232:9 | l15 | Assignment between incompatible numeric types from 'const volatile int8_t &' to 'float'. | +| test_specified.cpp:245:7:245:8 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'const uint32_t'. | +| test_specified.cpp:246:7:246:8 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'const uint32_t'. | +| test_specified.cpp:247:7:247:8 | l3 | Assignment between incompatible numeric types from 'int8_t' to 'volatile int64_t'. | +| test_specified.cpp:248:7:248:8 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'const volatile uint16_t'. | +| test_specified.cpp:250:7:250:8 | l3 | Assignment between incompatible numeric types from 'int8_t' to 'const volatile uint16_t'. | +| test_specified.cpp:285:8:285:22 | g4 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test_specified.cpp:287:8:287:19 | s4 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test_specified.cpp:292:9:292:23 | g5 | Assignment between incompatible numeric types from 'int8_t' to 'uint16_t'. | +| test_specified.cpp:294:9:294:20 | s5 | Assignment between incompatible numeric types from 'int8_t' to 'uint16_t'. | +| test_specified.cpp:308:23:308:25 | 300 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | +| test_specified.cpp:308:28:308:32 | 70000 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | +| test_specified.cpp:308:35:308:38 | 3000 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | +| test_specified.cpp:309:12:309:16 | 70000 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | +| test_specified.cpp:314:23:314:25 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test_specified.cpp:314:28:314:30 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned short'. | +| test_specified.cpp:314:33:314:35 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | +| test_specified.cpp:329:8:329:9 | l2 | Assignment between incompatible numeric types from 'CVColour' to 'uint8_t'. | +| test_specified.cpp:331:8:331:9 | l3 | Assignment between incompatible numeric types from 'CVColour' to 'uint8_t'. | +| test_specified.cpp:342:8:342:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test_specified.cpp:343:8:343:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test_specified.cpp:344:8:344:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test_specified.cpp:351:10:351:11 | l2 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | +| test_specified.cpp:352:10:352:11 | l3 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | +| test_specified.cpp:370:18:370:20 | 300 | Assignment between incompatible numeric types from 'int' to 'const uint8_t'. | +| test_specified.cpp:370:23:370:27 | 70000 | Assignment between incompatible numeric types from 'int' to 'volatile uint16_t'. | diff --git a/cpp/misra/test/rules/RULE-7-0-6/test.cpp b/cpp/misra/test/rules/RULE-7-0-6/test.cpp index cc477d1f3..9b7d6dbd0 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/test.cpp +++ b/cpp/misra/test/rules/RULE-7-0-6/test.cpp @@ -776,103 +776,4 @@ void test_user_defined_operators_constants() { l1 = 42L; // NON_COMPLIANT l1 = 42LL; // NON_COMPLIANT l1 = 42U; // NON_COMPLIANT -} - -// Test aggregate initialization - struct with multiple members -struct SimpleAggregate { - std::uint8_t m1; - std::uint16_t m2; - std::int32_t m3; - float m4; -}; - -void test_aggregate_initialization_basic() { - // Compliant cases - exact types or constants that fit - SimpleAggregate l1{42, 1000, -50, 3.14f}; // COMPLIANT - SimpleAggregate l2{u8, u16, s32, f}; // COMPLIANT - SimpleAggregate l3{255, 65535, 2147483647, 0.0f}; // COMPLIANT - - // Non-compliant cases - type violations - SimpleAggregate l4{u16, u8, s32, // NON_COMPLIANT - narrowing u16 to uint8_t - f}; - SimpleAggregate l5{u8, u32, s32, // NON_COMPLIANT - narrowing u32 to uint16_t - f}; - SimpleAggregate l6{u8, u16, u32, f}; // NON_COMPLIANT - different signedness - SimpleAggregate l7{u8, u16, s32, - s32}; // NON_COMPLIANT - different type category - - // Constants that don't fit - SimpleAggregate l8{256, 65536, // NON_COMPLIANT - constants don't fit - 2147483648LL, // NON_COMPLIANT - constants don't fit - 0.0f}; - - // Widening of id-expressions is allowed - SimpleAggregate l9{u8, u8, s8, f}; // COMPLIANT - widening allowed -} - -// Test aggregate initialization - arrays -void test_aggregate_initialization_arrays() { - // Basic arrays - std::uint8_t l1[3]{10, 20, 30}; // COMPLIANT - std::uint8_t l2[3]{u8, u8, u8}; // COMPLIANT - std::uint8_t l3[3]{300, 400, 500}; // NON_COMPLIANT - constants don't fit - std::uint8_t l4[3]{s8, s8, s8}; // NON_COMPLIANT - signedness mismatch - std::uint8_t l5[3]{u16, u16, u16}; // NON_COMPLIANT - narrowing - - // Multi-dimensional arrays - std::int16_t l6[2][2]{{1, 2}, {3, 4}}; // COMPLIANT - std::int16_t l7[2][2]{{s8, s8}, {s8, s8}}; // COMPLIANT - widening allowed - std::int16_t l8[2][2]{{s32, s32}, {s32, s32}}; // NON_COMPLIANT - narrowing - std::int16_t l9[2][2]{{u8, u8}, // NON_COMPLIANT - signedness mismatch - {u8, u8}}; // NON_COMPLIANT - signedness mismatch -} - -// Test aggregate initialization - nested structs -struct NestedAggregate { - SimpleAggregate m1; - std::uint32_t m2; -}; - -void test_aggregate_initialization_nested() { - // Compliant nested initialization - NestedAggregate l1{{10, 100, -5, 1.0f}, 500}; // COMPLIANT - NestedAggregate l2{{u8, u16, s32, f}, u32}; // COMPLIANT - - // Non-compliant nested initialization - NestedAggregate l3{ - {u16, u8, s32, f}, // NON_COMPLIANT - narrowing in nested struct - u32}; - NestedAggregate l4{ - {u8, u16, s32, f}, - s32}; // NON_COMPLIANT - signedness mismatch in outer member -} - -// Test aggregate initialization - struct with bit-fields -struct BitfieldAggregate { - std::uint32_t m1 : 8; - std::uint32_t m2 : 16; - std::int32_t m3 : 12; -}; - -void test_aggregate_initialization_bitfields() { - // Compliant cases - BitfieldAggregate l1{100, 30000, -500}; // COMPLIANT - BitfieldAggregate l2{u8, u16, s16}; // COMPLIANT - appropriate sizes - - // Non-compliant cases - BitfieldAggregate l3{300, 70000, 5000}; // NON_COMPLIANT - constants don't fit - BitfieldAggregate l4{u16, u32, s32}; // NON_COMPLIANT - narrowing -} - -// Test aggregate initialization with designated initializers (C++20 feature, -// but test for basic cases) -void test_aggregate_initialization_designated() { - // Note: Designated initializers are C++20, but we can test basic aggregate - // init patterns - SimpleAggregate l1{.m1 = 10, .m2 = 100, .m3 = -5, .m4 = 1.0f}; // COMPLIANT - SimpleAggregate l2{.m1 = u8, .m2 = u16, .m3 = s32, .m4 = f}; // COMPLIANT - SimpleAggregate l3{.m1 = u16, // NON_COMPLIANT - type violation - .m2 = u8, - .m3 = s32, - .m4 = f}; } \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-0-6/test_aggregate.cpp b/cpp/misra/test/rules/RULE-7-0-6/test_aggregate.cpp new file mode 100644 index 000000000..7e215bc60 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-6/test_aggregate.cpp @@ -0,0 +1,111 @@ +#include + +std::uint32_t u32; +std::int32_t s32; +std::uint8_t u8; +std::int8_t s8; +std::uint16_t u16; +std::int16_t s16; +std::uint64_t u64; +std::int64_t s64; +float f; +double d; + +// Test aggregate initialization - struct with multiple members +struct SimpleAggregate { + std::uint8_t m1; + std::uint16_t m2; + std::int32_t m3; + float m4; +}; + +void test_aggregate_initialization_basic() { + // Compliant cases - exact types or constants that fit + SimpleAggregate l1{42, 1000, -50, 3.14f}; // COMPLIANT + SimpleAggregate l2{u8, u16, s32, f}; // COMPLIANT + SimpleAggregate l3{255, 65535, 2147483647, 0.0f}; // COMPLIANT + + // Non-compliant cases - type violations + SimpleAggregate l4{u16, u8, s32, // NON_COMPLIANT - narrowing u16 to uint8_t + f}; + SimpleAggregate l5{u8, u32, s32, // NON_COMPLIANT - narrowing u32 to uint16_t + f}; + SimpleAggregate l6{u8, u16, u32, f}; // NON_COMPLIANT - different signedness + SimpleAggregate l7{u8, u16, s32, + s32}; // NON_COMPLIANT - different type category + + // Constants that don't fit + SimpleAggregate l8{256, 65536, // NON_COMPLIANT - constants don't fit + 2147483648LL, // NON_COMPLIANT - constants don't fit + 0.0f}; + + // Widening of id-expressions is allowed + SimpleAggregate l9{u8, u8, s8, f}; // COMPLIANT - widening allowed +} + +// Test aggregate initialization - arrays +void test_aggregate_initialization_arrays() { + // Basic arrays + std::uint8_t l1[3]{10, 20, 30}; // COMPLIANT + std::uint8_t l2[3]{u8, u8, u8}; // COMPLIANT + std::uint8_t l3[3]{300, 400, 500}; // NON_COMPLIANT - constants don't fit + std::uint8_t l4[3]{s8, s8, s8}; // NON_COMPLIANT - signedness mismatch + std::uint8_t l5[3]{u16, u16, u16}; // NON_COMPLIANT - narrowing + + // Multi-dimensional arrays + std::int16_t l6[2][2]{{1, 2}, {3, 4}}; // COMPLIANT + std::int16_t l7[2][2]{{s8, s8}, {s8, s8}}; // COMPLIANT - widening allowed + std::int16_t l8[2][2]{{s32, s32}, {s32, s32}}; // NON_COMPLIANT - narrowing + std::int16_t l9[2][2]{{u8, u8}, // NON_COMPLIANT - signedness mismatch + {u8, u8}}; // NON_COMPLIANT - signedness mismatch +} + +// Test aggregate initialization - nested structs +struct NestedAggregate { + SimpleAggregate m1; + std::uint32_t m2; +}; + +void test_aggregate_initialization_nested() { + // Compliant nested initialization + NestedAggregate l1{{10, 100, -5, 1.0f}, 500}; // COMPLIANT + NestedAggregate l2{{u8, u16, s32, f}, u32}; // COMPLIANT + + // Non-compliant nested initialization + NestedAggregate l3{ + {u16, u8, s32, f}, // NON_COMPLIANT - narrowing in nested struct + u32}; + NestedAggregate l4{ + {u8, u16, s32, f}, + s32}; // NON_COMPLIANT - signedness mismatch in outer member +} + +// Test aggregate initialization - struct with bit-fields +struct BitfieldAggregate { + std::uint32_t m1 : 8; + std::uint32_t m2 : 16; + std::int32_t m3 : 12; +}; + +void test_aggregate_initialization_bitfields() { + // Compliant cases + BitfieldAggregate l1{100, 30000, -500}; // COMPLIANT + BitfieldAggregate l2{u8, u16, s16}; // COMPLIANT - appropriate sizes + + // Non-compliant cases + BitfieldAggregate l3{300, 70000, 5000}; // NON_COMPLIANT - constants don't fit + BitfieldAggregate l4{u16, u32, s32}; // NON_COMPLIANT - narrowing +} + +// Test aggregate initialization with designated initializers (C++20 feature, +// but test for basic cases) +void test_aggregate_initialization_designated() { + // Note: Designated initializers are C++20, but we can test basic aggregate + // init patterns + SimpleAggregate l1{.m1 = 10, .m2 = 100, .m3 = -5, .m4 = 1.0f}; // COMPLIANT + SimpleAggregate l2{.m1 = u8, .m2 = u16, .m3 = s32, .m4 = f}; // COMPLIANT + SimpleAggregate l3{.m1 = u16, // NON_COMPLIANT - type violation + .m2 = u8, + .m3 = s32, + .m4 = f}; +} \ No newline at end of file From fc63db1d69434de645fa3db6c4e330194dd84459 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Thu, 19 Jun 2025 23:38:24 +0100 Subject: [PATCH 28/57] Rule 7.0.6: Move operator tests to separate file --- .../NumericAssignmentTypeMismatch.expected | 146 ++++----- cpp/misra/test/rules/RULE-7-0-6/test.cpp | 294 ----------------- .../test/rules/RULE-7-0-6/test_operators.cpp | 295 ++++++++++++++++++ 3 files changed, 368 insertions(+), 367 deletions(-) create mode 100644 cpp/misra/test/rules/RULE-7-0-6/test_operators.cpp diff --git a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected index c0c9e50ec..e9ac08e3c 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected +++ b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected @@ -65,79 +65,6 @@ | test.cpp:442:8:442:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | | test.cpp:460:7:460:8 | l3 | Assignment between incompatible numeric types from 'uint16_t &' to 'uint32_t'. | | test.cpp:463:7:463:8 | l5 | Assignment between incompatible numeric types from 'uint64_t &' to 'uint32_t'. | -| test.cpp:583:8:583:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:584:8:584:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:588:8:588:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:589:8:589:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:593:7:593:8 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:594:7:594:8 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:598:8:598:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:599:8:599:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:603:8:603:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:604:8:604:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:608:8:608:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:609:8:609:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:613:8:613:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:614:8:614:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:618:8:618:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:619:8:619:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:623:9:623:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:624:9:624:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:628:9:628:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:629:9:629:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:634:9:634:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:635:9:635:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:639:9:639:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:640:9:640:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:644:8:644:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:645:8:645:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:649:9:649:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:650:9:650:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:654:8:654:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:655:8:655:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:659:9:659:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:660:9:660:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:665:6:665:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:666:6:666:7 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:671:6:671:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:672:6:672:7 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:674:10:674:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:675:6:675:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:676:6:676:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:676:10:676:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:681:8:681:9 | l3 | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | -| test.cpp:682:8:682:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:683:8:683:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:687:9:687:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:688:9:688:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:692:9:692:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:693:9:693:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:697:9:697:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:698:9:698:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:702:9:702:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:703:9:703:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:707:9:707:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:708:9:708:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:712:9:712:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:713:9:713:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:717:9:717:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:718:9:718:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:722:9:722:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:723:9:723:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:727:10:727:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:728:10:728:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:732:10:732:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:733:10:733:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:738:3:738:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:739:3:739:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:743:3:743:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:744:3:744:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:748:3:748:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | -| test.cpp:749:3:749:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:761:8:761:12 | 42.0 | Assignment between incompatible numeric types from 'float' to 'int32_t'. | -| test.cpp:776:8:776:10 | 42 | Assignment between incompatible numeric types from 'long' to 'int32_t'. | -| test.cpp:777:8:777:11 | 42 | Assignment between incompatible numeric types from 'long long' to 'int32_t'. | -| test.cpp:778:8:778:10 | 42 | Assignment between incompatible numeric types from 'unsigned int' to 'int32_t'. | | test_aggregate.cpp:29:22:29:24 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | | test_aggregate.cpp:31:26:31:28 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'uint16_t'. | | test_aggregate.cpp:33:31:33:33 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | @@ -206,6 +133,79 @@ | test_member_pointers.cpp:125:12:125:14 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int64_t'. | | test_member_pointers.cpp:126:12:126:14 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int64_t'. | | test_member_pointers.cpp:127:12:127:14 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int64_t'. | +| test_operators.cpp:99:8:99:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:100:8:100:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:104:8:104:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:105:8:105:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:109:7:109:8 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:110:7:110:8 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:114:8:114:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:115:8:115:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:119:8:119:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:120:8:120:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:124:8:124:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:125:8:125:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:129:8:129:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:130:8:130:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:134:8:134:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:135:8:135:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:139:9:139:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:140:9:140:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:144:9:144:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:145:9:145:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:150:9:150:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:151:9:151:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:155:9:155:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:156:9:156:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:160:8:160:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:161:8:161:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:165:9:165:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:166:9:166:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:170:8:170:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:171:8:171:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:175:9:175:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:176:9:176:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:181:6:181:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:182:6:182:7 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:187:6:187:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:188:6:188:7 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:190:10:190:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:191:6:191:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:192:6:192:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:192:10:192:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:197:8:197:9 | l3 | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | +| test_operators.cpp:198:8:198:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:199:8:199:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:203:9:203:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:204:9:204:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:208:9:208:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:209:9:209:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:213:9:213:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:214:9:214:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:218:9:218:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:219:9:219:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:223:9:223:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:224:9:224:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:228:9:228:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:229:9:229:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:233:9:233:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:234:9:234:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:238:9:238:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:239:9:239:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:243:10:243:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:244:10:244:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:248:10:248:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:249:10:249:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:254:3:254:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:255:3:255:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:259:3:259:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:260:3:260:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:264:3:264:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:265:3:265:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:277:8:277:12 | 42.0 | Assignment between incompatible numeric types from 'float' to 'int32_t'. | +| test_operators.cpp:292:8:292:10 | 42 | Assignment between incompatible numeric types from 'long' to 'int32_t'. | +| test_operators.cpp:293:8:293:11 | 42 | Assignment between incompatible numeric types from 'long long' to 'int32_t'. | +| test_operators.cpp:294:8:294:10 | 42 | Assignment between incompatible numeric types from 'unsigned int' to 'int32_t'. | | test_specified.cpp:37:8:37:9 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | | test_specified.cpp:38:8:38:9 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'uint8_t'. | | test_specified.cpp:41:8:41:9 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'int8_t'. | diff --git a/cpp/misra/test/rules/RULE-7-0-6/test.cpp b/cpp/misra/test/rules/RULE-7-0-6/test.cpp index 9b7d6dbd0..9b8bdc002 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/test.cpp +++ b/cpp/misra/test/rules/RULE-7-0-6/test.cpp @@ -482,298 +482,4 @@ void test_compound_assignments() { l2 >>= 1; // COMPLIANT - compound assignment, rule does not apply l4 += l1; // COMPLIANT - compound assignment, rule does not apply l4 -= s32; // COMPLIANT - compound assignment, rule does not apply -} - -// Test user-defined operators - always non-extensible -struct UserDefinedOperators { - UserDefinedOperators(std::int32_t l1) {} - - // Binary operators - UserDefinedOperators operator+(std::int32_t l1) const { - return UserDefinedOperators{0}; - } - UserDefinedOperators operator-(std::int32_t l1) const { - return UserDefinedOperators{0}; - } - UserDefinedOperators operator*(std::int32_t l1) const { - return UserDefinedOperators{0}; - } - UserDefinedOperators operator/(std::int32_t l1) const { - return UserDefinedOperators{0}; - } - UserDefinedOperators operator%(std::int32_t l1) const { - return UserDefinedOperators{0}; - } - UserDefinedOperators operator&(std::int32_t l1) const { - return UserDefinedOperators{0}; - } - UserDefinedOperators operator|(std::int32_t l1) const { - return UserDefinedOperators{0}; - } - UserDefinedOperators operator^(std::int32_t l1) const { - return UserDefinedOperators{0}; - } - UserDefinedOperators operator<<(std::int32_t l1) const { - return UserDefinedOperators{0}; - } - UserDefinedOperators operator>>(std::int32_t l1) const { - return UserDefinedOperators{0}; - } - - // Comparison operators - bool operator==(std::int32_t l1) const { return true; } - bool operator!=(std::int32_t l1) const { return false; } - bool operator<(std::int32_t l1) const { return false; } - bool operator<=(std::int32_t l1) const { return false; } - bool operator>(std::int32_t l1) const { return false; } - bool operator>=(std::int32_t l1) const { return false; } - - // Subscript operator - std::int32_t operator[](std::int32_t l1) const { return 0; } - - // Function call operator - std::int32_t operator()(std::int32_t l1) const { return 0; } - std::int32_t operator()(std::int32_t l1, std::int32_t l2) const { return 0; } - - // Assignment operators - UserDefinedOperators &operator=(std::int32_t l1) { return *this; } - UserDefinedOperators &operator+=(std::int32_t l1) { return *this; } - UserDefinedOperators &operator-=(std::int32_t l1) { return *this; } - UserDefinedOperators &operator*=(std::int32_t l1) { return *this; } - UserDefinedOperators &operator/=(std::int32_t l1) { return *this; } - UserDefinedOperators &operator%=(std::int32_t l1) { return *this; } - UserDefinedOperators &operator&=(std::int32_t l1) { return *this; } - UserDefinedOperators &operator|=(std::int32_t l1) { return *this; } - UserDefinedOperators &operator^=(std::int32_t l1) { return *this; } - UserDefinedOperators &operator<<=(std::int32_t l1) { return *this; } - UserDefinedOperators &operator>>=(std::int32_t l1) { return *this; } - - // Increment/decrement operators - UserDefinedOperators &operator++() { return *this; } - UserDefinedOperators operator++(int) { return UserDefinedOperators{0}; } - UserDefinedOperators &operator--() { return *this; } - UserDefinedOperators operator--(int) { return UserDefinedOperators{0}; } -}; - -// Global user-defined operators -UserDefinedOperators operator+(std::int32_t l1, - const UserDefinedOperators &l2) { - return UserDefinedOperators{0}; -} - -UserDefinedOperators operator-(std::int32_t l1, - const UserDefinedOperators &l2) { - return UserDefinedOperators{0}; -} - -bool operator==(std::int32_t l1, const UserDefinedOperators &l2) { - return true; -} - -void test_user_defined_operators() { - UserDefinedOperators l1{42}; - std::int32_t l2 = 10; - std::int16_t l3 = 5; - std::int64_t l4 = 100; - std::uint32_t l5 = 20; - - // Member operators - non-extensible, exact type match required - l1 + l2; // COMPLIANT - exact type match - l1 + l3; // COMPLIANT - widening conversion is allowed - l1 + l4; // NON_COMPLIANT - different type - l1 + l5; // NON_COMPLIANT - different signedness - - l1 - l2; // COMPLIANT - exact type match - l1 - l3; // COMPLIANT - widening conversion is allowed - l1 - l4; // NON_COMPLIANT - different type - l1 - l5; // NON_COMPLIANT - different signedness - - l1 *l2; // COMPLIANT - exact type match - l1 *l3; // COMPLIANT - widening conversion is allowed - l1 *l4; // NON_COMPLIANT - different type - l1 *l5; // NON_COMPLIANT - different signedness - - l1 / l2; // COMPLIANT - exact type match - l1 / l3; // COMPLIANT - widening conversion is allowed - l1 / l4; // NON_COMPLIANT - different type - l1 / l5; // NON_COMPLIANT - different signedness - - l1 % l2; // COMPLIANT - exact type match - l1 % l3; // COMPLIANT - widening conversion is allowed - l1 % l4; // NON_COMPLIANT - different type - l1 % l5; // NON_COMPLIANT - different signedness - - l1 & l2; // COMPLIANT - exact type match - l1 & l3; // COMPLIANT - widening conversion is allowed - l1 & l4; // NON_COMPLIANT - different type - l1 & l5; // NON_COMPLIANT - different signedness - - l1 | l2; // COMPLIANT - exact type match - l1 | l3; // COMPLIANT - widening conversion is allowed - l1 | l4; // NON_COMPLIANT - different type - l1 | l5; // NON_COMPLIANT - different signedness - - l1 ^ l2; // COMPLIANT - exact type match - l1 ^ l3; // COMPLIANT - widening conversion is allowed - l1 ^ l4; // NON_COMPLIANT - different type - l1 ^ l5; // NON_COMPLIANT - different signedness - - l1 << l2; // COMPLIANT - exact type match - l1 << l3; // COMPLIANT - widening conversion is allowed - l1 << l4; // NON_COMPLIANT - different type - l1 << l5; // NON_COMPLIANT - different signedness - - l1 >> l2; // COMPLIANT - exact type match - l1 >> l3; // COMPLIANT - widening conversion is allowed - l1 >> l4; // NON_COMPLIANT - different type - l1 >> l5; // NON_COMPLIANT - different signedness - - // Comparison operators - l1 == l2; // COMPLIANT - exact type match - l1 == l3; // COMPLIANT - widening conversion is allowed - l1 == l4; // NON_COMPLIANT - different type - l1 == l5; // NON_COMPLIANT - different signedness - - l1 != l2; // COMPLIANT - exact type match - l1 != l3; // COMPLIANT - widening conversion is allowed - l1 != l4; // NON_COMPLIANT - different type - l1 != l5; // NON_COMPLIANT - different signedness - - l1 < l2; // COMPLIANT - exact type match - l1 < l3; // COMPLIANT - widening conversion is allowed - l1 < l4; // NON_COMPLIANT - different type - l1 < l5; // NON_COMPLIANT - different signedness - - l1 <= l2; // COMPLIANT - exact type match - l1 <= l3; // COMPLIANT - widening conversion is allowed - l1 <= l4; // NON_COMPLIANT - different type - l1 <= l5; // NON_COMPLIANT - - l1 > l2; // COMPLIANT - exact type match - l1 > l3; // COMPLIANT - widening conversion is allowed - l1 > l4; // NON_COMPLIANT - l1 > l5; // NON_COMPLIANT - different signedness - - l1 >= l2; // COMPLIANT - exact type match - l1 >= l3; // COMPLIANT - widening conversion is allowed - l1 >= l4; // NON_COMPLIANT - l1 >= l5; // NON_COMPLIANT - different signedness - - // Subscript operator - l1[l2]; // COMPLIANT - exact type match - l1[l3]; // COMPLIANT - widening conversion is allowed - l1[l4]; // NON_COMPLIANT - different type - l1[l5]; // NON_COMPLIANT - different signedness - - // Function call operator - l1(l2); // COMPLIANT - exact type match - l1(l3); // COMPLIANT - widening conversion is allowed - l1(l4); // NON_COMPLIANT - different type - l1(l5); // NON_COMPLIANT - different signedness - l1(l2, l2); // COMPLIANT - both exact type match - l1(l2, l4); // NON_COMPLIANT - second parameter different type - l1(l4, l2); // NON_COMPLIANT - first parameter different type - l1(l4, l5); // NON_COMPLIANT - both parameters different type - - // The presence of a default copy constructor for UserDefinedOperators means - // that assignments through operator= must be exact type matches. - l1 = l2; // COMPLIANT - exact type match - l1 = l3; // NON_COMPLIANT - l1 = l4; // NON_COMPLIANT - l1 = l5; // NON_COMPLIANT - - l1 += l2; // COMPLIANT - exact type match - l1 += l3; // COMPLIANT - widening conversion is allowed - l1 += l4; // NON_COMPLIANT - different type - l1 += l5; // NON_COMPLIANT - different signedness - - l1 -= l2; // COMPLIANT - exact type match - l1 -= l3; // COMPLIANT - widening conversion is allowed - l1 -= l4; // NON_COMPLIANT - different type - l1 -= l5; // NON_COMPLIANT - different signedness - - l1 *= l2; // COMPLIANT - exact type match - l1 *= l3; // COMPLIANT - widening conversion is allowed - l1 *= l4; // NON_COMPLIANT - different type - l1 *= l5; // NON_COMPLIANT - different signedness - - l1 /= l2; // COMPLIANT - exact type match - l1 /= l3; // COMPLIANT - widening conversion is allowed - l1 /= l4; // NON_COMPLIANT - different type - l1 /= l5; // NON_COMPLIANT - different signedness - - l1 %= l2; // COMPLIANT - exact type match - l1 %= l3; // COMPLIANT - widening conversion is allowed - l1 %= l4; // NON_COMPLIANT - different type - l1 %= l5; // NON_COMPLIANT - different signedness - - l1 &= l2; // COMPLIANT - exact type match - l1 &= l3; // COMPLIANT - widening conversion is allowed - l1 &= l4; // NON_COMPLIANT - different type - l1 &= l5; // NON_COMPLIANT - different signedness - - l1 |= l2; // COMPLIANT - exact type match - l1 |= l3; // COMPLIANT - widening conversion is allowed - l1 |= l4; // NON_COMPLIANT - different type - l1 |= l5; // NON_COMPLIANT - different signedness - - l1 ^= l2; // COMPLIANT - exact type match - l1 ^= l3; // COMPLIANT - widening conversion is allowed - l1 ^= l4; // NON_COMPLIANT - different type - l1 ^= l5; // NON_COMPLIANT - different signedness - - l1 <<= l2; // COMPLIANT - exact type match - l1 <<= l3; // COMPLIANT - widening conversion is allowed - l1 <<= l4; // NON_COMPLIANT - different type - l1 <<= l5; // NON_COMPLIANT - different signedness - - l1 >>= l2; // COMPLIANT - exact type match - l1 >>= l3; // COMPLIANT - widening conversion is allowed - l1 >>= l4; // NON_COMPLIANT - different type - l1 >>= l5; // NON_COMPLIANT - different signedness - - // Global operators - l2 + l1; // COMPLIANT - exact type match - l3 + l1; // COMPLIANT - widening conversion is allowed - l4 + l1; // NON_COMPLIANT - different type - l5 + l1; // NON_COMPLIANT - different signedness - - l2 - l1; // COMPLIANT - exact type match - l3 - l1; // COMPLIANT - widening conversion is allowed - l4 - l1; // NON_COMPLIANT - different type - l5 - l1; // NON_COMPLIANT - different signedness - - l2 == l1; // COMPLIANT - exact type match - l3 == l1; // COMPLIANT - widening conversion is allowed - l4 == l1; // NON_COMPLIANT - different type - l5 == l1; // NON_COMPLIANT - different signedness -} - -// Test user-defined operators with constants -void test_user_defined_operators_constants() { - UserDefinedOperators l1{42}; - - // Constants with exact type match - l1 + 42; // COMPLIANT - l1 + 42L; // COMPLIANT - l1 + 42LL; // COMPLIANT - l1 + 42U; // COMPLIANT - l1 + 42.0f; // NON_COMPLIANT - float constant - - l1 == 42; // COMPLIANT - integer constant is int/int32_t - l1 == 42L; // COMPLIANT - long constant - l1 == 42LL; // COMPLIANT - long long constant - l1 == 42U; // COMPLIANT - unsigned constant - - l1[42]; // COMPLIANT - integer constant is int/int32_t - l1[42L]; // COMPLIANT - long constant - l1[42LL]; // COMPLIANT - long long constant - l1[42U]; // COMPLIANT - unsigned constant - - // The presence of a default copy constructor for UserDefinedOperators means - // that assignments through operator= must be exact type matches. - l1 = 42; // COMPLIANT - integer constant is int/int32_t - l1 = 42L; // NON_COMPLIANT - l1 = 42LL; // NON_COMPLIANT - l1 = 42U; // NON_COMPLIANT } \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-0-6/test_operators.cpp b/cpp/misra/test/rules/RULE-7-0-6/test_operators.cpp new file mode 100644 index 000000000..f8823bfa0 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-6/test_operators.cpp @@ -0,0 +1,295 @@ +#include + +// Test user-defined operators - always non-extensible +struct UserDefinedOperators { + UserDefinedOperators(std::int32_t l1) {} + + // Binary operators + UserDefinedOperators operator+(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator-(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator*(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator/(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator%(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator&(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator|(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator^(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator<<(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator>>(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + + // Comparison operators + bool operator==(std::int32_t l1) const { return true; } + bool operator!=(std::int32_t l1) const { return false; } + bool operator<(std::int32_t l1) const { return false; } + bool operator<=(std::int32_t l1) const { return false; } + bool operator>(std::int32_t l1) const { return false; } + bool operator>=(std::int32_t l1) const { return false; } + + // Subscript operator + std::int32_t operator[](std::int32_t l1) const { return 0; } + + // Function call operator + std::int32_t operator()(std::int32_t l1) const { return 0; } + std::int32_t operator()(std::int32_t l1, std::int32_t l2) const { return 0; } + + // Assignment operators + UserDefinedOperators &operator=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator+=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator-=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator*=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator/=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator%=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator&=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator|=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator^=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator<<=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator>>=(std::int32_t l1) { return *this; } + + // Increment/decrement operators + UserDefinedOperators &operator++() { return *this; } + UserDefinedOperators operator++(int) { return UserDefinedOperators{0}; } + UserDefinedOperators &operator--() { return *this; } + UserDefinedOperators operator--(int) { return UserDefinedOperators{0}; } +}; + +// Global user-defined operators +UserDefinedOperators operator+(std::int32_t l1, + const UserDefinedOperators &l2) { + return UserDefinedOperators{0}; +} + +UserDefinedOperators operator-(std::int32_t l1, + const UserDefinedOperators &l2) { + return UserDefinedOperators{0}; +} + +bool operator==(std::int32_t l1, const UserDefinedOperators &l2) { + return true; +} + +void test_user_defined_operators() { + UserDefinedOperators l1{42}; + std::int32_t l2 = 10; + std::int16_t l3 = 5; + std::int64_t l4 = 100; + std::uint32_t l5 = 20; + + // Member operators - non-extensible, exact type match required + l1 + l2; // COMPLIANT - exact type match + l1 + l3; // COMPLIANT - widening conversion is allowed + l1 + l4; // NON_COMPLIANT - different type + l1 + l5; // NON_COMPLIANT - different signedness + + l1 - l2; // COMPLIANT - exact type match + l1 - l3; // COMPLIANT - widening conversion is allowed + l1 - l4; // NON_COMPLIANT - different type + l1 - l5; // NON_COMPLIANT - different signedness + + l1 *l2; // COMPLIANT - exact type match + l1 *l3; // COMPLIANT - widening conversion is allowed + l1 *l4; // NON_COMPLIANT - different type + l1 *l5; // NON_COMPLIANT - different signedness + + l1 / l2; // COMPLIANT - exact type match + l1 / l3; // COMPLIANT - widening conversion is allowed + l1 / l4; // NON_COMPLIANT - different type + l1 / l5; // NON_COMPLIANT - different signedness + + l1 % l2; // COMPLIANT - exact type match + l1 % l3; // COMPLIANT - widening conversion is allowed + l1 % l4; // NON_COMPLIANT - different type + l1 % l5; // NON_COMPLIANT - different signedness + + l1 & l2; // COMPLIANT - exact type match + l1 & l3; // COMPLIANT - widening conversion is allowed + l1 & l4; // NON_COMPLIANT - different type + l1 & l5; // NON_COMPLIANT - different signedness + + l1 | l2; // COMPLIANT - exact type match + l1 | l3; // COMPLIANT - widening conversion is allowed + l1 | l4; // NON_COMPLIANT - different type + l1 | l5; // NON_COMPLIANT - different signedness + + l1 ^ l2; // COMPLIANT - exact type match + l1 ^ l3; // COMPLIANT - widening conversion is allowed + l1 ^ l4; // NON_COMPLIANT - different type + l1 ^ l5; // NON_COMPLIANT - different signedness + + l1 << l2; // COMPLIANT - exact type match + l1 << l3; // COMPLIANT - widening conversion is allowed + l1 << l4; // NON_COMPLIANT - different type + l1 << l5; // NON_COMPLIANT - different signedness + + l1 >> l2; // COMPLIANT - exact type match + l1 >> l3; // COMPLIANT - widening conversion is allowed + l1 >> l4; // NON_COMPLIANT - different type + l1 >> l5; // NON_COMPLIANT - different signedness + + // Comparison operators + l1 == l2; // COMPLIANT - exact type match + l1 == l3; // COMPLIANT - widening conversion is allowed + l1 == l4; // NON_COMPLIANT - different type + l1 == l5; // NON_COMPLIANT - different signedness + + l1 != l2; // COMPLIANT - exact type match + l1 != l3; // COMPLIANT - widening conversion is allowed + l1 != l4; // NON_COMPLIANT - different type + l1 != l5; // NON_COMPLIANT - different signedness + + l1 < l2; // COMPLIANT - exact type match + l1 < l3; // COMPLIANT - widening conversion is allowed + l1 < l4; // NON_COMPLIANT - different type + l1 < l5; // NON_COMPLIANT - different signedness + + l1 <= l2; // COMPLIANT - exact type match + l1 <= l3; // COMPLIANT - widening conversion is allowed + l1 <= l4; // NON_COMPLIANT - different type + l1 <= l5; // NON_COMPLIANT + + l1 > l2; // COMPLIANT - exact type match + l1 > l3; // COMPLIANT - widening conversion is allowed + l1 > l4; // NON_COMPLIANT + l1 > l5; // NON_COMPLIANT - different signedness + + l1 >= l2; // COMPLIANT - exact type match + l1 >= l3; // COMPLIANT - widening conversion is allowed + l1 >= l4; // NON_COMPLIANT + l1 >= l5; // NON_COMPLIANT - different signedness + + // Subscript operator + l1[l2]; // COMPLIANT - exact type match + l1[l3]; // COMPLIANT - widening conversion is allowed + l1[l4]; // NON_COMPLIANT - different type + l1[l5]; // NON_COMPLIANT - different signedness + + // Function call operator + l1(l2); // COMPLIANT - exact type match + l1(l3); // COMPLIANT - widening conversion is allowed + l1(l4); // NON_COMPLIANT - different type + l1(l5); // NON_COMPLIANT - different signedness + l1(l2, l2); // COMPLIANT - both exact type match + l1(l2, l4); // NON_COMPLIANT - second parameter different type + l1(l4, l2); // NON_COMPLIANT - first parameter different type + l1(l4, l5); // NON_COMPLIANT - both parameters different type + + // The presence of a default copy constructor for UserDefinedOperators means + // that assignments through operator= must be exact type matches. + l1 = l2; // COMPLIANT - exact type match + l1 = l3; // NON_COMPLIANT + l1 = l4; // NON_COMPLIANT + l1 = l5; // NON_COMPLIANT + + l1 += l2; // COMPLIANT - exact type match + l1 += l3; // COMPLIANT - widening conversion is allowed + l1 += l4; // NON_COMPLIANT - different type + l1 += l5; // NON_COMPLIANT - different signedness + + l1 -= l2; // COMPLIANT - exact type match + l1 -= l3; // COMPLIANT - widening conversion is allowed + l1 -= l4; // NON_COMPLIANT - different type + l1 -= l5; // NON_COMPLIANT - different signedness + + l1 *= l2; // COMPLIANT - exact type match + l1 *= l3; // COMPLIANT - widening conversion is allowed + l1 *= l4; // NON_COMPLIANT - different type + l1 *= l5; // NON_COMPLIANT - different signedness + + l1 /= l2; // COMPLIANT - exact type match + l1 /= l3; // COMPLIANT - widening conversion is allowed + l1 /= l4; // NON_COMPLIANT - different type + l1 /= l5; // NON_COMPLIANT - different signedness + + l1 %= l2; // COMPLIANT - exact type match + l1 %= l3; // COMPLIANT - widening conversion is allowed + l1 %= l4; // NON_COMPLIANT - different type + l1 %= l5; // NON_COMPLIANT - different signedness + + l1 &= l2; // COMPLIANT - exact type match + l1 &= l3; // COMPLIANT - widening conversion is allowed + l1 &= l4; // NON_COMPLIANT - different type + l1 &= l5; // NON_COMPLIANT - different signedness + + l1 |= l2; // COMPLIANT - exact type match + l1 |= l3; // COMPLIANT - widening conversion is allowed + l1 |= l4; // NON_COMPLIANT - different type + l1 |= l5; // NON_COMPLIANT - different signedness + + l1 ^= l2; // COMPLIANT - exact type match + l1 ^= l3; // COMPLIANT - widening conversion is allowed + l1 ^= l4; // NON_COMPLIANT - different type + l1 ^= l5; // NON_COMPLIANT - different signedness + + l1 <<= l2; // COMPLIANT - exact type match + l1 <<= l3; // COMPLIANT - widening conversion is allowed + l1 <<= l4; // NON_COMPLIANT - different type + l1 <<= l5; // NON_COMPLIANT - different signedness + + l1 >>= l2; // COMPLIANT - exact type match + l1 >>= l3; // COMPLIANT - widening conversion is allowed + l1 >>= l4; // NON_COMPLIANT - different type + l1 >>= l5; // NON_COMPLIANT - different signedness + + // Global operators + l2 + l1; // COMPLIANT - exact type match + l3 + l1; // COMPLIANT - widening conversion is allowed + l4 + l1; // NON_COMPLIANT - different type + l5 + l1; // NON_COMPLIANT - different signedness + + l2 - l1; // COMPLIANT - exact type match + l3 - l1; // COMPLIANT - widening conversion is allowed + l4 - l1; // NON_COMPLIANT - different type + l5 - l1; // NON_COMPLIANT - different signedness + + l2 == l1; // COMPLIANT - exact type match + l3 == l1; // COMPLIANT - widening conversion is allowed + l4 == l1; // NON_COMPLIANT - different type + l5 == l1; // NON_COMPLIANT - different signedness +} + +// Test user-defined operators with constants +void test_user_defined_operators_constants() { + UserDefinedOperators l1{42}; + + // Constants with exact type match + l1 + 42; // COMPLIANT + l1 + 42L; // COMPLIANT + l1 + 42LL; // COMPLIANT + l1 + 42U; // COMPLIANT + l1 + 42.0f; // NON_COMPLIANT - float constant + + l1 == 42; // COMPLIANT - integer constant is int/int32_t + l1 == 42L; // COMPLIANT - long constant + l1 == 42LL; // COMPLIANT - long long constant + l1 == 42U; // COMPLIANT - unsigned constant + + l1[42]; // COMPLIANT - integer constant is int/int32_t + l1[42L]; // COMPLIANT - long constant + l1[42LL]; // COMPLIANT - long long constant + l1[42U]; // COMPLIANT - unsigned constant + + // The presence of a default copy constructor for UserDefinedOperators means + // that assignments through operator= must be exact type matches. + l1 = 42; // COMPLIANT - integer constant is int/int32_t + l1 = 42L; // NON_COMPLIANT + l1 = 42LL; // NON_COMPLIANT + l1 = 42U; // NON_COMPLIANT +} \ No newline at end of file From f68658a4d2f650c05ef6fb0879278b2ef8f4f9e4 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Thu, 19 Jun 2025 23:50:31 +0100 Subject: [PATCH 29/57] Rule 7.0.6: Support constructor field initializers --- .../cpp/misra/StandardConversions.qll | 12 +++ .../NumericAssignmentTypeMismatch.expected | 19 +++++ cpp/misra/test/rules/RULE-7-0-6/test.cpp | 84 +++++++++++++++++++ 3 files changed, 115 insertions(+) diff --git a/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll b/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll index d5dbbc886..fe3ddd234 100644 --- a/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll +++ b/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll @@ -152,6 +152,18 @@ predicate isAssignment(Expr source, NumericType targetType, string context) { targetType = v.getType() ) or + exists(ConstructorFieldInit fi | + fi.getExpr() = source and + context = "constructor field initialization" + | + // For the MISRA type rules we treat bit fields as a special case + if fi.getTarget() instanceof BitField + then targetType = getBitFieldType(fi.getTarget()) + else + // Regular variable initialization + targetType = fi.getTarget().getType() + ) + or // Passing a function parameter by value exists(Call call, int i | call.getArgument(i) = source and diff --git a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected index e9ac08e3c..85ee49754 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected +++ b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected @@ -65,6 +65,25 @@ | test.cpp:442:8:442:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | | test.cpp:460:7:460:8 | l3 | Assignment between incompatible numeric types from 'uint16_t &' to 'uint32_t'. | | test.cpp:463:7:463:8 | l5 | Assignment between incompatible numeric types from 'uint64_t &' to 'uint32_t'. | +| test.cpp:512:12:512:13 | l1 | Assignment between incompatible numeric types from 'uint32_t' to 'uint8_t'. | +| test.cpp:513:12:513:13 | l1 | Assignment between incompatible numeric types from 'uint32_t' to 'uint16_t'. | +| test.cpp:515:12:515:13 | l2 | Assignment between incompatible numeric types from 'int32_t' to 'int8_t'. | +| test.cpp:516:12:516:13 | l2 | Assignment between incompatible numeric types from 'int32_t' to 'int16_t'. | +| test.cpp:536:12:536:14 | 300 | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test.cpp:537:12:537:16 | 70000 | Assignment between incompatible numeric types from 'int' to 'uint16_t'. | +| test.cpp:538:12:538:27 | 4294967296 | Assignment between incompatible numeric types from 'unsigned long long' to 'uint32_t'. | +| test.cpp:539:12:539:14 | 200 | Assignment between incompatible numeric types from 'int' to 'int8_t'. | +| test.cpp:540:12:540:16 | 40000 | Assignment between incompatible numeric types from 'int' to 'int16_t'. | +| test.cpp:541:12:541:26 | 4294967296 | Assignment between incompatible numeric types from 'long long' to 'int32_t'. | +| test.cpp:542:12:542:14 | 1.0 | Assignment between incompatible numeric types from 'double' to 'float'. | +| test.cpp:543:12:543:15 | 1.0 | Assignment between incompatible numeric types from 'float' to 'double'. | +| test.cpp:548:12:548:18 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test.cpp:549:12:549:18 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint16_t'. | +| test.cpp:550:12:550:18 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint32_t'. | +| test.cpp:552:12:552:13 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'int16_t'. | +| test.cpp:553:12:553:13 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'int32_t'. | +| test.cpp:554:12:554:13 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'float'. | +| test.cpp:555:12:555:13 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'double'. | | test_aggregate.cpp:29:22:29:24 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | | test_aggregate.cpp:31:26:31:28 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'uint16_t'. | | test_aggregate.cpp:33:31:33:33 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | diff --git a/cpp/misra/test/rules/RULE-7-0-6/test.cpp b/cpp/misra/test/rules/RULE-7-0-6/test.cpp index 9b8bdc002..61669f284 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/test.cpp +++ b/cpp/misra/test/rules/RULE-7-0-6/test.cpp @@ -482,4 +482,88 @@ void test_compound_assignments() { l2 >>= 1; // COMPLIANT - compound assignment, rule does not apply l4 += l1; // COMPLIANT - compound assignment, rule does not apply l4 -= s32; // COMPLIANT - compound assignment, rule does not apply +} + +// Test constructor field initializers +struct ConstructorTest { + std::uint8_t m1; + std::uint16_t m2; + std::uint32_t m3; + std::int8_t m4; + std::int16_t m5; + std::int32_t m6; + float m7; + double m8; + + // Constructor with various member initializer scenarios + ConstructorTest(std::uint8_t l1, std::uint16_t l2, std::int8_t l3) + : m1(l1), // COMPLIANT - same type + m2(l2), // COMPLIANT - same type + m3(l1), // COMPLIANT - widening of id-expression + m4(l3), // COMPLIANT - same type + m5(l3), // COMPLIANT - widening of id-expression + m6(l3), // COMPLIANT - widening of id-expression + m7(1.0f), // COMPLIANT - same type + m8(1.0) { // COMPLIANT - same type + } + + // Constructor with non-compliant initializers + ConstructorTest(std::uint32_t l1, std::int32_t l2, float l3) + : m1(l1), // NON_COMPLIANT - narrowing + m2(l1), // NON_COMPLIANT - narrowing + m3(l1), // COMPLIANT - same type + m4(l2), // NON_COMPLIANT - narrowing and different signedness + m5(l2), // NON_COMPLIANT - narrowing and different signedness + m6(l2), // COMPLIANT - same type + m7(l3), // COMPLIANT - same type + m8(l3) { // COMPLIANT - allowed to use float to initialize double + } + + // Constructor with constant initializers + ConstructorTest() + : m1(100), // COMPLIANT - constant fits + m2(65535), // COMPLIANT - constant fits + m3(4294967295U), // COMPLIANT - constant fits + m4(127), // COMPLIANT - constant fits + m5(32767), // COMPLIANT - constant fits + m6(2147483647), // COMPLIANT - constant fits + m7(3.14f), // COMPLIANT - same type constant + m8(2.718) { // COMPLIANT - same type constant + } + + // Constructor with non-compliant constant initializers + ConstructorTest(int) + : m1(300), // NON_COMPLIANT - constant too large + m2(70000), // NON_COMPLIANT - constant too large + m3(0x1'0000'0000ULL), // NON_COMPLIANT - constant too large + m4(200), // NON_COMPLIANT - constant too large + m5(40000), // NON_COMPLIANT - constant too large + m6(0x1'0000'0000LL), // NON_COMPLIANT - constant too large + m7(1.0), // NON_COMPLIANT - different size + m8(1.0f) { // NON_COMPLIANT - different size + } + + // Constructor with expression initializers + ConstructorTest(std::uint8_t l1, std::uint8_t l2, std::int8_t l3) + : m1(l1 + l2), // NON_COMPLIANT - expression result is int + m2(l1 + l2), // NON_COMPLIANT - expression result is int + m3(l1 + l2), // NON_COMPLIANT - expression result is int + m4(l3), // COMPLIANT - widening of id-expression + m5(l1), // NON_COMPLIANT - different signedness + m6(l1), // NON_COMPLIANT - different signedness + m7(l1), // NON_COMPLIANT - different type category + m8(l1) { // NON_COMPLIANT - different type category + } +}; + +void test_constructor_field_initializers() { + std::uint8_t l1 = 42; + std::uint16_t l2 = 1000; + std::int8_t l3 = 10; + + ConstructorTest l4(l1, l2, l3); // Test first constructor + ConstructorTest l5(u32, s32, f); // Test second constructor + ConstructorTest l6; // Test third constructor + ConstructorTest l7(0); // Test fourth constructor + ConstructorTest l8(l1, l1, l3); // Test fifth constructor } \ No newline at end of file From 3fdaa98f8f9bb1f8bcd5a20a8f4fdb5c3bbaa5a7 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Fri, 20 Jun 2025 00:06:59 +0100 Subject: [PATCH 30/57] Rule 7.0.6: Handle explicit conversions Consider the conversion as the source, not the pre-conversion value. --- .../cpp/misra/StandardConversions.qll | 11 +++- .../NumericAssignmentTypeMismatch.ql | 6 +-- .../NumericAssignmentTypeMismatch.expected | 9 ++-- cpp/misra/test/rules/RULE-7-0-6/test.cpp | 52 +++++++++++++++++++ 4 files changed, 70 insertions(+), 8 deletions(-) diff --git a/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll b/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll index fe3ddd234..8c010fa71 100644 --- a/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll +++ b/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll @@ -117,6 +117,13 @@ class CanonicalIntegerTypes extends NumericType, IntegralType { } predicate isAssignment(Expr source, NumericType targetType, string context) { + exists(Expr preConversionAssignment | + isPreConversionAssignment(preConversionAssignment, targetType, context) and + preConversionAssignment.getExplicitlyConverted() = source + ) +} + +predicate isPreConversionAssignment(Expr source, NumericType targetType, string context) { // Assignment expression (which excludes compound assignments) exists(AssignExpr assign | assign.getRValue() = source and @@ -261,4 +268,6 @@ CanonicalIntegerTypes getBitFieldType(BitField bf) { /** * Holds if the `source` expression is assigned to a bit field. */ -predicate isAssignedToBitfield(Expr source, BitField bf) { source = bf.getAnAssignedValue() } +predicate isAssignedToBitfield(Expr source, BitField bf) { + source = bf.getAnAssignedValue().getExplicitlyConverted() +} diff --git a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql index 16931f433..4ff284fe7 100644 --- a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql +++ b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql @@ -101,7 +101,7 @@ predicate isValidWidening(Expr source, NumericType sourceType, NumericType targe // Same type category and signedness, source size smaller, source is id-expression or has constructor exception ( source instanceof IdExpression or - hasConstructorException(any(Call call | call.getAnArgument() = source)) + hasConstructorException(any(Call call | call.getAnArgument().getExplicitlyConverted() = source)) ) and sourceType.getTypeCategory() = targetType.getTypeCategory() and sourceType.getSignedness() = targetType.getSignedness() and @@ -163,7 +163,7 @@ predicate isOverloadIndependent(Call call, Expr arg) { */ predicate shouldHaveSameType(Expr source) { exists(Call call | - call.getAnArgument() = source and + call.getAnArgument().getExplicitlyConverted() = source and isAssignment(source, _, _) and not hasConstructorException(call) | @@ -172,7 +172,7 @@ predicate shouldHaveSameType(Expr source) { // Passed as a varargs parameter exists(int i | call.getTarget().isVarargs() and - call.getArgument(i) = source and + call.getArgument(i).getExplicitlyConverted() = source and // Argument is greater than the number of parameters call.getTarget().getNumberOfParameters() <= i ) diff --git a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected index 85ee49754..870f579d7 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected +++ b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected @@ -11,8 +11,8 @@ | test.cpp:97:13:97:14 | m1 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | | test.cpp:98:13:98:14 | m2 | Assignment between incompatible numeric types from 'uint16_t' to 'uint64_t'. | | test.cpp:103:9:103:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint32_t'. | -| test.cpp:104:10:104:11 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | -| test.cpp:105:35:105:36 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | +| test.cpp:104:9:104:12 | (...) | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | +| test.cpp:105:9:105:37 | static_cast... | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | | test.cpp:110:8:110:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | | test.cpp:111:21:111:27 | ... + ... | Assignment between incompatible numeric types from 'int' to 'int16_t'. | | test.cpp:134:11:134:11 | 4 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | @@ -84,6 +84,7 @@ | test.cpp:553:12:553:13 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'int32_t'. | | test.cpp:554:12:554:13 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'float'. | | test.cpp:555:12:555:13 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'double'. | +| test.cpp:585:9:585:37 | static_cast... | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | | test_aggregate.cpp:29:22:29:24 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | | test_aggregate.cpp:31:26:31:28 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'uint16_t'. | | test_aggregate.cpp:33:31:33:33 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | @@ -315,7 +316,7 @@ | test_specified.cpp:342:8:342:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | | test_specified.cpp:343:8:343:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | | test_specified.cpp:344:8:344:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | -| test_specified.cpp:351:10:351:11 | l2 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | -| test_specified.cpp:352:10:352:11 | l3 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | +| test_specified.cpp:351:9:351:12 | (...) | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | +| test_specified.cpp:352:9:352:12 | (...) | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | | test_specified.cpp:370:18:370:20 | 300 | Assignment between incompatible numeric types from 'int' to 'const uint8_t'. | | test_specified.cpp:370:23:370:27 | 70000 | Assignment between incompatible numeric types from 'int' to 'volatile uint16_t'. | diff --git a/cpp/misra/test/rules/RULE-7-0-6/test.cpp b/cpp/misra/test/rules/RULE-7-0-6/test.cpp index 61669f284..659c26825 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/test.cpp +++ b/cpp/misra/test/rules/RULE-7-0-6/test.cpp @@ -566,4 +566,56 @@ void test_constructor_field_initializers() { ConstructorTest l6; // Test third constructor ConstructorTest l7(0); // Test fourth constructor ConstructorTest l8(l1, l1, l3); // Test fifth constructor +} + +// Test explicit casts +void test_explicit_casts() { + std::uint8_t l1 = 42; + std::uint16_t l2 = 1000; + std::int8_t l3 = -10; + std::int32_t l4 = -100; + float l5 = 3.14f; + double l6 = 2.718; + + // Explicit cast expressions are treated as expressions, not id-expressions + u8 = static_cast(l2); // COMPLIANT + u16 = static_cast(l1); // COMPLIANT + s8 = static_cast(l4); // COMPLIANT + s32 = static_cast(l3); // COMPLIANT + s32 = static_cast(l3); // NON_COMPLIANT + + // Type category conversions with explicit casts + f = static_cast(l4); // COMPLIANT + s32 = static_cast(l5); // COMPLIANT + + // Size conversions with explicit casts + d = static_cast(l5); // COMPLIANT + l5 = static_cast(l6); // COMPLIANT + + // C-style casts (also expressions) + u8 = (std::uint8_t)l2; // COMPLIANT + s8 = (std::int8_t)l4; // COMPLIANT + f = (float)l4; // COMPLIANT + + // Functional style casts (also expressions) + u8 = std::uint8_t(l2); // COMPLIANT + s8 = std::int8_t(l4); // COMPLIANT + f = float(l4); // COMPLIANT + + // Const_cast (creates expressions) + const std::uint8_t l7 = 100; + u8 = const_cast(l7); // COMPLIANT + + // Reinterpret_cast (creates expressions) + u32 = reinterpret_cast(l4); // COMPLIANT + + // Assignment to variables through explicit casts + std::uint32_t l8; + std::uint16_t l9; + l8 = static_cast(l9); // COMPLIANT + l9 = static_cast(l8); // COMPLIANT + + // Function calls with explicit casts + f1(static_cast(l4)); // COMPLIANT + f2(static_cast(l4)); // COMPLIANT } \ No newline at end of file From 063a5ccff14c05e6ece1dd87bd51d750cf951c85 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Fri, 20 Jun 2025 09:10:21 +0100 Subject: [PATCH 31/57] Rule 7.0.6: Improve tests for templates --- .../NumericAssignmentTypeMismatch.expected | 72 +++++++++---------- cpp/misra/test/rules/RULE-7-0-6/test.cpp | 20 ++++-- 2 files changed, 49 insertions(+), 43 deletions(-) diff --git a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected index 870f579d7..a6473c6df 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected +++ b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected @@ -49,42 +49,42 @@ | test.cpp:289:6:289:8 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'size_t'. | | test.cpp:294:12:294:14 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | | test.cpp:313:9:313:10 | 42 | Assignment between incompatible numeric types from 'int' to 'long'. | -| test.cpp:342:23:342:24 | 42 | Assignment between incompatible numeric types from 'int' to 'unsigned long'. | -| test.cpp:352:19:352:25 | ... + ... | Assignment between incompatible numeric types from 'int' to 'int16_t'. | -| test.cpp:357:10:357:12 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | -| test.cpp:390:8:390:9 | l6 | Assignment between incompatible numeric types from 'uint32_t &' to 'uint8_t'. | -| test.cpp:391:8:391:9 | l7 | Assignment between incompatible numeric types from 'int8_t &' to 'uint8_t'. | -| test.cpp:392:9:392:10 | l8 | Assignment between incompatible numeric types from 'float &' to 'int32_t'. | -| test.cpp:403:6:403:7 | l3 | Assignment between incompatible numeric types from 'uint8_t &' to 'int64_t'. | -| test.cpp:404:6:404:7 | l4 | Assignment between incompatible numeric types from 'uint16_t &' to 'int32_t'. | -| test.cpp:415:8:415:9 | l3 | Assignment between incompatible numeric types from 'uint8_t &' to 'int8_t'. | -| test.cpp:416:8:416:9 | l4 | Assignment between incompatible numeric types from 'int8_t &' to 'uint8_t'. | -| test.cpp:429:9:429:10 | l4 | Assignment between incompatible numeric types from 'float &' to 'int32_t'. | -| test.cpp:430:7:430:8 | l5 | Assignment between incompatible numeric types from 'double &' to 'float'. | -| test.cpp:431:7:431:8 | l6 | Assignment between incompatible numeric types from 'int32_t &' to 'float'. | -| test.cpp:442:8:442:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | -| test.cpp:460:7:460:8 | l3 | Assignment between incompatible numeric types from 'uint16_t &' to 'uint32_t'. | -| test.cpp:463:7:463:8 | l5 | Assignment between incompatible numeric types from 'uint64_t &' to 'uint32_t'. | -| test.cpp:512:12:512:13 | l1 | Assignment between incompatible numeric types from 'uint32_t' to 'uint8_t'. | -| test.cpp:513:12:513:13 | l1 | Assignment between incompatible numeric types from 'uint32_t' to 'uint16_t'. | -| test.cpp:515:12:515:13 | l2 | Assignment between incompatible numeric types from 'int32_t' to 'int8_t'. | -| test.cpp:516:12:516:13 | l2 | Assignment between incompatible numeric types from 'int32_t' to 'int16_t'. | -| test.cpp:536:12:536:14 | 300 | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | -| test.cpp:537:12:537:16 | 70000 | Assignment between incompatible numeric types from 'int' to 'uint16_t'. | -| test.cpp:538:12:538:27 | 4294967296 | Assignment between incompatible numeric types from 'unsigned long long' to 'uint32_t'. | -| test.cpp:539:12:539:14 | 200 | Assignment between incompatible numeric types from 'int' to 'int8_t'. | -| test.cpp:540:12:540:16 | 40000 | Assignment between incompatible numeric types from 'int' to 'int16_t'. | -| test.cpp:541:12:541:26 | 4294967296 | Assignment between incompatible numeric types from 'long long' to 'int32_t'. | -| test.cpp:542:12:542:14 | 1.0 | Assignment between incompatible numeric types from 'double' to 'float'. | -| test.cpp:543:12:543:15 | 1.0 | Assignment between incompatible numeric types from 'float' to 'double'. | -| test.cpp:548:12:548:18 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | -| test.cpp:549:12:549:18 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint16_t'. | -| test.cpp:550:12:550:18 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint32_t'. | -| test.cpp:552:12:552:13 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'int16_t'. | -| test.cpp:553:12:553:13 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'int32_t'. | -| test.cpp:554:12:554:13 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'float'. | -| test.cpp:555:12:555:13 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'double'. | -| test.cpp:585:9:585:37 | static_cast... | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | +| test.cpp:346:25:346:27 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned long'. | +| test.cpp:358:19:358:25 | ... + ... | Assignment between incompatible numeric types from 'int' to 'int16_t'. | +| test.cpp:363:10:363:12 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:396:8:396:9 | l6 | Assignment between incompatible numeric types from 'uint32_t &' to 'uint8_t'. | +| test.cpp:397:8:397:9 | l7 | Assignment between incompatible numeric types from 'int8_t &' to 'uint8_t'. | +| test.cpp:398:9:398:10 | l8 | Assignment between incompatible numeric types from 'float &' to 'int32_t'. | +| test.cpp:409:6:409:7 | l3 | Assignment between incompatible numeric types from 'uint8_t &' to 'int64_t'. | +| test.cpp:410:6:410:7 | l4 | Assignment between incompatible numeric types from 'uint16_t &' to 'int32_t'. | +| test.cpp:421:8:421:9 | l3 | Assignment between incompatible numeric types from 'uint8_t &' to 'int8_t'. | +| test.cpp:422:8:422:9 | l4 | Assignment between incompatible numeric types from 'int8_t &' to 'uint8_t'. | +| test.cpp:435:9:435:10 | l4 | Assignment between incompatible numeric types from 'float &' to 'int32_t'. | +| test.cpp:436:7:436:8 | l5 | Assignment between incompatible numeric types from 'double &' to 'float'. | +| test.cpp:437:7:437:8 | l6 | Assignment between incompatible numeric types from 'int32_t &' to 'float'. | +| test.cpp:448:8:448:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test.cpp:466:7:466:8 | l3 | Assignment between incompatible numeric types from 'uint16_t &' to 'uint32_t'. | +| test.cpp:469:7:469:8 | l5 | Assignment between incompatible numeric types from 'uint64_t &' to 'uint32_t'. | +| test.cpp:518:12:518:13 | l1 | Assignment between incompatible numeric types from 'uint32_t' to 'uint8_t'. | +| test.cpp:519:12:519:13 | l1 | Assignment between incompatible numeric types from 'uint32_t' to 'uint16_t'. | +| test.cpp:521:12:521:13 | l2 | Assignment between incompatible numeric types from 'int32_t' to 'int8_t'. | +| test.cpp:522:12:522:13 | l2 | Assignment between incompatible numeric types from 'int32_t' to 'int16_t'. | +| test.cpp:542:12:542:14 | 300 | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test.cpp:543:12:543:16 | 70000 | Assignment between incompatible numeric types from 'int' to 'uint16_t'. | +| test.cpp:544:12:544:27 | 4294967296 | Assignment between incompatible numeric types from 'unsigned long long' to 'uint32_t'. | +| test.cpp:545:12:545:14 | 200 | Assignment between incompatible numeric types from 'int' to 'int8_t'. | +| test.cpp:546:12:546:16 | 40000 | Assignment between incompatible numeric types from 'int' to 'int16_t'. | +| test.cpp:547:12:547:26 | 4294967296 | Assignment between incompatible numeric types from 'long long' to 'int32_t'. | +| test.cpp:548:12:548:14 | 1.0 | Assignment between incompatible numeric types from 'double' to 'float'. | +| test.cpp:549:12:549:15 | 1.0 | Assignment between incompatible numeric types from 'float' to 'double'. | +| test.cpp:554:12:554:18 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test.cpp:555:12:555:18 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint16_t'. | +| test.cpp:556:12:556:18 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint32_t'. | +| test.cpp:558:12:558:13 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'int16_t'. | +| test.cpp:559:12:559:13 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'int32_t'. | +| test.cpp:560:12:560:13 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'float'. | +| test.cpp:561:12:561:13 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'double'. | +| test.cpp:591:9:591:37 | static_cast... | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | | test_aggregate.cpp:29:22:29:24 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | | test_aggregate.cpp:31:26:31:28 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'uint16_t'. | | test_aggregate.cpp:33:31:33:33 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | diff --git a/cpp/misra/test/rules/RULE-7-0-6/test.cpp b/cpp/misra/test/rules/RULE-7-0-6/test.cpp index 659c26825..ae7310bc3 100644 --- a/cpp/misra/test/rules/RULE-7-0-6/test.cpp +++ b/cpp/misra/test/rules/RULE-7-0-6/test.cpp @@ -326,22 +326,28 @@ void test_constructor_exception() { MyInt l1{s8}; // COMPLIANT } -// Test template functions - not overload-independent template struct D { + // Overload-independent - f10 parameters are always the same type void f10(T l1, int l2) {} void f10(T l1, std::string l2) {} + // Not overload-independent template void f11(S1 l1, int l2) {} template void f11(S2 l1, std::string l2) {} void f11(std::int32_t l1, float f) {} }; void test_template_functions() { - D l1; - l1.f10(42, "X"); // COMPLIANT - l1.f10(42, 1); // COMPLIANT - l1.f11(42, "X"); // NON_COMPLIANT - int not size_t - l1.f11(42, 1); // COMPLIANT - same as specialized type - l1.f11(42, 0.0f); // COMPLIANT - same as specialized type + D l1; + l1.f10(u32, "X"); // COMPLIANT - can widen, because always same type + l1.f10(u32, 1); // COMPLIANT - can widen, because always same type + D l2; + l2.f10(u16, "X"); // COMPLIANT - can widen, because always same type + l2.f10(u16, 1); // COMPLIANT - can widen, because always same type + l1.f11(u32, "X"); // NON_COMPLIANT - not overload-independent + // and not the same type as the parameter + // so cannot widen - must be the same type + l1.f11(s32, 1); // COMPLIANT - same as specialized type + l1.f11(s32, 0.0f); // COMPLIANT - matches parameter type } // Test initialization forms From 96d5c1bfb447ba237949cb989e85bf03470669a9 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Fri, 20 Jun 2025 09:22:32 +0100 Subject: [PATCH 32/57] MISRA C++ 2023: Rename StandardConversions library These relate to the built-in type rules. --- .../misra/{StandardConversions.qll => BuiltInTypeRules.qll} | 6 +++++- .../src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) rename cpp/misra/src/codingstandards/cpp/misra/{StandardConversions.qll => BuiltInTypeRules.qll} (98%) diff --git a/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll b/cpp/misra/src/codingstandards/cpp/misra/BuiltInTypeRules.qll similarity index 98% rename from cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll rename to cpp/misra/src/codingstandards/cpp/misra/BuiltInTypeRules.qll index 8c010fa71..0116d8c3c 100644 --- a/cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll +++ b/cpp/misra/src/codingstandards/cpp/misra/BuiltInTypeRules.qll @@ -1,3 +1,7 @@ +/** + * A library for utility classes related to the built-in type rules in MISRA C++ 2023 (Section 4.7.0). + */ + import cpp import codingstandards.cpp.misra import codingstandards.cpp.Type @@ -54,7 +58,7 @@ TypeCategory getTypeCategory(BuiltInType t) { } /** - * The signedness of a MISRA C++ 2023 numeric type + * The signedness of a MISRA C++ 2023 numeric type. */ newtype Signedness = Signed() or diff --git a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql index 4ff284fe7..3b0f22739 100644 --- a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql +++ b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql @@ -16,7 +16,7 @@ import cpp import codingstandards.cpp.misra import codingstandards.cpp.ConstantExpressions -import codingstandards.cpp.misra.StandardConversions +import codingstandards.cpp.misra.BuiltInTypeRules import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis predicate isValidConstantAssignment(IntegerConstantExpr source, NumericType targetType) { From 7c5fb879f39c1474d49209adb327754036bdfe22 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Fri, 20 Jun 2025 09:54:20 +0100 Subject: [PATCH 33/57] Rule 7.0.6: Address performance issues - Extract the determination of ExprCall FunctionTypes - Ensure type matching is inlined --- .../codingstandards/cpp/types/Compatible.qll | 19 +---------- .../src/codingstandards/cpp/types/Type.qll | 18 ++++++++++ .../cpp/misra/BuiltInTypeRules.qll | 34 ++++++++++--------- .../NumericAssignmentTypeMismatch.ql | 2 ++ 4 files changed, 39 insertions(+), 34 deletions(-) diff --git a/cpp/common/src/codingstandards/cpp/types/Compatible.qll b/cpp/common/src/codingstandards/cpp/types/Compatible.qll index c4ee9a22e..399ba8062 100644 --- a/cpp/common/src/codingstandards/cpp/types/Compatible.qll +++ b/cpp/common/src/codingstandards/cpp/types/Compatible.qll @@ -1,6 +1,7 @@ import cpp import codeql.util.Boolean import codingstandards.cpp.types.Graph +import codingstandards.cpp.types.Type module TypeNamesMatchConfig implements TypeEquivalenceSig { predicate resolveTypedefs() { @@ -522,24 +523,6 @@ module FunctionDeclarationTypeEquivalence< } } -/** - * Convenience class to reduce the awkwardness of how `RoutineType` and `FunctionPointerIshType` - * don't have a common ancestor. - */ -private class FunctionType extends Type { - FunctionType() { this instanceof RoutineType or this instanceof FunctionPointerIshType } - - Type getReturnType() { - result = this.(RoutineType).getReturnType() or - result = this.(FunctionPointerIshType).getReturnType() - } - - Type getParameterType(int i) { - result = this.(RoutineType).getParameterType(i) or - result = this.(FunctionPointerIshType).getParameterType(i) - } -} - private class LeafType extends Type { LeafType() { not this instanceof DerivedType and diff --git a/cpp/common/src/codingstandards/cpp/types/Type.qll b/cpp/common/src/codingstandards/cpp/types/Type.qll index 9e2a74904..b1b0b7aba 100644 --- a/cpp/common/src/codingstandards/cpp/types/Type.qll +++ b/cpp/common/src/codingstandards/cpp/types/Type.qll @@ -111,3 +111,21 @@ predicate integralTypeBounds(IntegralType integralType, QlBuiltins::BigInt lb, Q ) ) } + +/** + * Convenience class to reduce the awkwardness of how `RoutineType` and `FunctionPointerIshType` + * don't have a common ancestor. + */ +class FunctionType extends Type { + FunctionType() { this instanceof RoutineType or this instanceof FunctionPointerIshType } + + Type getReturnType() { + result = this.(RoutineType).getReturnType() or + result = this.(FunctionPointerIshType).getReturnType() + } + + Type getParameterType(int i) { + result = this.(RoutineType).getParameterType(i) or + result = this.(FunctionPointerIshType).getParameterType(i) + } +} diff --git a/cpp/misra/src/codingstandards/cpp/misra/BuiltInTypeRules.qll b/cpp/misra/src/codingstandards/cpp/misra/BuiltInTypeRules.qll index 0116d8c3c..4e892efcf 100644 --- a/cpp/misra/src/codingstandards/cpp/misra/BuiltInTypeRules.qll +++ b/cpp/misra/src/codingstandards/cpp/misra/BuiltInTypeRules.qll @@ -120,6 +120,19 @@ class CanonicalIntegerTypes extends NumericType, IntegralType { CanonicalIntegerTypes() { this = this.getCanonicalArithmeticType() } } +FunctionType getExprCallFunctionType(ExprCall call) { + // A standard expression call + // Returns a FunctionPointerIshType + result = call.(ExprCall).getExpr().getType() + or + // An expression call using the pointer to member operator (.* or ->*) + // This special handling is required because we don't have a CodeQL class representing the call + // to a pointer to member function, but the right hand side is extracted as the -1 child of the + // call. + // Returns a RoutineType + result = call.(ExprCall).getChild(-1).getType().(PointerToMemberType).getBaseType() +} + predicate isAssignment(Expr source, NumericType targetType, string context) { exists(Expr preConversionAssignment | isPreConversionAssignment(preConversionAssignment, targetType, context) and @@ -181,27 +194,16 @@ predicate isPreConversionAssignment(Expr source, NumericType targetType, string not targetType.stripTopLevelSpecifiers() instanceof ReferenceType and context = "function argument" | + // A regular function call targetType = call.getTarget().getParameter(i).getType() or - // Handle varargs - use the fully converted type of the argument + // A function call where the argument is passed as varargs call.getTarget().getNumberOfParameters() <= i and + // The rule states that the type should match the "adjusted" type of the argument targetType = source.getFullyConverted().getType() or - // A standard expression call - targetType = call.(ExprCall).getExpr().getType().(FunctionPointerIshType).getParameterType(i) - or - // An expression call using the pointer to member operator (.* or ->*) - // This special handling is required because we don't have a CodeQL class representing the call - // to a pointer to member function, but the right hand side is extracted as the -1 child of the - // call - targetType = - call.(ExprCall) - .getChild(-1) - .getType() - .(PointerToMemberType) - .getBaseType() - .(RoutineType) - .getParameterType(i) + // An expression call - get the function type, then the parameter type + targetType = getExprCallFunctionType(call).getParameterType(i) ) or // Return statement diff --git a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql index 3b0f22739..94f5210c8 100644 --- a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql +++ b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql @@ -51,6 +51,8 @@ predicate isValidConstantAssignment(IntegerConstantExpr source, NumericType targ ) } +bindingset[sourceType, targetType] +pragma[inline_late] predicate isValidTypeMatch(NumericType sourceType, NumericType targetType) { // Same type category, signedness and size sourceType.getTypeCategory() = targetType.getTypeCategory() and From 6ddab35740b224c62ec8a55455368db4bc0db557 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Fri, 20 Jun 2025 09:56:58 +0100 Subject: [PATCH 34/57] Create Call library --- cpp/common/src/codingstandards/cpp/Call.qll | 18 ++++++++++++++++++ .../cpp/misra/BuiltInTypeRules.qll | 14 +------------- 2 files changed, 19 insertions(+), 13 deletions(-) create mode 100644 cpp/common/src/codingstandards/cpp/Call.qll diff --git a/cpp/common/src/codingstandards/cpp/Call.qll b/cpp/common/src/codingstandards/cpp/Call.qll new file mode 100644 index 000000000..706d66e01 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/Call.qll @@ -0,0 +1,18 @@ +import cpp +import codingstandards.cpp.types.Type + +/** + * Gets the `FunctionType` of an expression call. + */ +FunctionType getExprCallFunctionType(ExprCall call) { + // A standard expression call + // Returns a FunctionPointerIshType + result = call.(ExprCall).getExpr().getType() + or + // An expression call using the pointer to member operator (.* or ->*) + // This special handling is required because we don't have a CodeQL class representing the call + // to a pointer to member function, but the right hand side is extracted as the -1 child of the + // call. + // Returns a RoutineType + result = call.(ExprCall).getChild(-1).getType().(PointerToMemberType).getBaseType() +} diff --git a/cpp/misra/src/codingstandards/cpp/misra/BuiltInTypeRules.qll b/cpp/misra/src/codingstandards/cpp/misra/BuiltInTypeRules.qll index 4e892efcf..17a3e257a 100644 --- a/cpp/misra/src/codingstandards/cpp/misra/BuiltInTypeRules.qll +++ b/cpp/misra/src/codingstandards/cpp/misra/BuiltInTypeRules.qll @@ -4,6 +4,7 @@ import cpp import codingstandards.cpp.misra +import codingstandards.cpp.Call import codingstandards.cpp.Type /** @@ -120,19 +121,6 @@ class CanonicalIntegerTypes extends NumericType, IntegralType { CanonicalIntegerTypes() { this = this.getCanonicalArithmeticType() } } -FunctionType getExprCallFunctionType(ExprCall call) { - // A standard expression call - // Returns a FunctionPointerIshType - result = call.(ExprCall).getExpr().getType() - or - // An expression call using the pointer to member operator (.* or ->*) - // This special handling is required because we don't have a CodeQL class representing the call - // to a pointer to member function, but the right hand side is extracted as the -1 child of the - // call. - // Returns a RoutineType - result = call.(ExprCall).getChild(-1).getType().(PointerToMemberType).getBaseType() -} - predicate isAssignment(Expr source, NumericType targetType, string context) { exists(Expr preConversionAssignment | isPreConversionAssignment(preConversionAssignment, targetType, context) and From 983f2568608d4ce88b8173030bc019f69c51de54 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Wed, 25 Jun 2025 09:59:54 +0100 Subject: [PATCH 35/57] Extend ios stubs for C++ --- .../test/includes/standard-library/ios.h | 3 +++ .../test/includes/standard-library/ostream.h | 26 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/cpp/common/test/includes/standard-library/ios.h b/cpp/common/test/includes/standard-library/ios.h index 1a78db28a..55e7e8db5 100644 --- a/cpp/common/test/includes/standard-library/ios.h +++ b/cpp/common/test/includes/standard-library/ios.h @@ -67,5 +67,8 @@ template class basic_ios : public std::ios_base { ios_base &hex(ios_base &str); +std::ios_base &boolalpha(std::ios_base &str); +std::ios_base &noboolalpha(std::ios_base &str); + } // namespace std #endif // _GHLIBCPP_IOS \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/ostream.h b/cpp/common/test/includes/standard-library/ostream.h index 9f2c6d906..23117a93d 100644 --- a/cpp/common/test/includes/standard-library/ostream.h +++ b/cpp/common/test/includes/standard-library/ostream.h @@ -10,6 +10,18 @@ class basic_ostream : virtual public basic_ios { typedef charT char_type; basic_ostream &operator<<(int n); + basic_ostream &operator<<(bool n); + basic_ostream &operator<<(short n); + basic_ostream &operator<<(unsigned short n); + basic_ostream &operator<<(unsigned int n); + basic_ostream &operator<<(long n); + basic_ostream &operator<<(unsigned long n); + basic_ostream &operator<<(long long n); + basic_ostream &operator<<(unsigned long long n); + basic_ostream &operator<<(float f); + basic_ostream &operator<<(double f); + basic_ostream &operator<<(long double f); + basic_ostream &operator<<(const void *p); basic_ostream &put(char_type c); basic_ostream &write(const char_type *s, streamsize n); @@ -25,6 +37,20 @@ template basic_ostream &operator<<( basic_ostream &, basic_ostream &(*func)(basic_ostream &)); + +template +basic_ostream & +operator<<(basic_ostream &, + std::ios_base &(*func)(std::ios_base &)); +template +basic_ostream &operator<<( + basic_ostream &, + std::basic_ios &(*func)(std::basic_ios &)); + +template +basic_ostream &operator<<(basic_ostream &os, + const void *p); + template basic_ostream &endl(basic_ostream &); From bb9f5a1f0c372d1063f8deca7b6d87de68a445fb Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Wed, 25 Jun 2025 10:00:31 +0100 Subject: [PATCH 36/57] RULE-7-11-3 - FunctionPointerConversionContext Detects inappropriate implicit conversions from function type to pointer-to-function type outside of static_cast or assignment contexts. Prevents ambiguous code where function pointer usage in boolean or arithmetic expressions may contradict developer intent. [a] --- .../cpp/misra/BuiltInTypeRules.qll | 4 +- .../FunctionPointerConversionContext.ql | 63 ++++++++++++ .../FunctionPointerConversionContext.expected | 16 +++ .../FunctionPointerConversionContext.qlref | 1 + cpp/misra/test/rules/RULE-7-11-3/test.cpp | 98 +++++++++++++++++++ 5 files changed, 180 insertions(+), 2 deletions(-) create mode 100644 cpp/misra/src/rules/RULE-7-11-3/FunctionPointerConversionContext.ql create mode 100644 cpp/misra/test/rules/RULE-7-11-3/FunctionPointerConversionContext.expected create mode 100644 cpp/misra/test/rules/RULE-7-11-3/FunctionPointerConversionContext.qlref create mode 100644 cpp/misra/test/rules/RULE-7-11-3/test.cpp diff --git a/cpp/misra/src/codingstandards/cpp/misra/BuiltInTypeRules.qll b/cpp/misra/src/codingstandards/cpp/misra/BuiltInTypeRules.qll index 17a3e257a..e728aa7e4 100644 --- a/cpp/misra/src/codingstandards/cpp/misra/BuiltInTypeRules.qll +++ b/cpp/misra/src/codingstandards/cpp/misra/BuiltInTypeRules.qll @@ -121,14 +121,14 @@ class CanonicalIntegerTypes extends NumericType, IntegralType { CanonicalIntegerTypes() { this = this.getCanonicalArithmeticType() } } -predicate isAssignment(Expr source, NumericType targetType, string context) { +predicate isAssignment(Expr source, Type targetType, string context) { exists(Expr preConversionAssignment | isPreConversionAssignment(preConversionAssignment, targetType, context) and preConversionAssignment.getExplicitlyConverted() = source ) } -predicate isPreConversionAssignment(Expr source, NumericType targetType, string context) { +predicate isPreConversionAssignment(Expr source, Type targetType, string context) { // Assignment expression (which excludes compound assignments) exists(AssignExpr assign | assign.getRValue() = source and diff --git a/cpp/misra/src/rules/RULE-7-11-3/FunctionPointerConversionContext.ql b/cpp/misra/src/rules/RULE-7-11-3/FunctionPointerConversionContext.ql new file mode 100644 index 000000000..7dadbb734 --- /dev/null +++ b/cpp/misra/src/rules/RULE-7-11-3/FunctionPointerConversionContext.ql @@ -0,0 +1,63 @@ +/** + * @id cpp/misra/function-pointer-conversion-context + * @name RULE-7-11-3: A conversion from function type to pointer-to-function type shall only occur in appropriate contexts + * @description Converting a function type to a pointer-to-function type outside of static_cast or + * assignment to a pointer-to-function object creates ambiguous behavior and potential + * unintended effects. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-7-11-3 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.misra.BuiltInTypeRules +import codingstandards.cpp.Type + +/** + * An `Expr` representing an implicit conversion from a function type to a pointer-to-function type. + * + * Despite the name, these are not `Conversion`s in our model. Instead, they are expressions + * representing functions that have been implicilty converted to function pointers. + */ +abstract class FunctionToFunctionPointerConversion extends Expr { } + +/** + * A `FunctionAccess` that has been implicitly converted to a function pointer type. + */ +class FunctionAccessConversionToFunctionPointer extends FunctionAccess, + FunctionToFunctionPointerConversion +{ + FunctionAccessConversionToFunctionPointer() { + this.getType().getUnspecifiedType() instanceof FunctionPointerIshType + } +} + +/** + * A `Call` to a `ConversionOperator` that converts a lambda to a function pointer type. + */ +class LambdaFunctionPointerConversion extends Call, FunctionToFunctionPointerConversion { + LambdaFunctionPointerConversion() { + this.getTarget().(ConversionOperator).getDestType() instanceof FunctionPointerIshType and + this.getQualifier().getType().getUnspecifiedType() instanceof Closure + } +} + +from FunctionToFunctionPointerConversion f +where + not isExcluded(f, ConversionsPackage::functionPointerConversionContextQuery()) and + // Not converted by an explicit static cast + not exists(Conversion c | + c.getExpr() = f and + not c.isImplicit() and + c.getType().getUnspecifiedType() instanceof FunctionPointerIshType + ) and + // Not a MISRA compliant assignment to a function pointer type + not exists(FunctionPointerIshType targetType | isAssignment(f, targetType, _)) +select f, + "Inappropriate conversion from function type to pointer-to-function type in '" + f.toString() + + "'." diff --git a/cpp/misra/test/rules/RULE-7-11-3/FunctionPointerConversionContext.expected b/cpp/misra/test/rules/RULE-7-11-3/FunctionPointerConversionContext.expected new file mode 100644 index 000000000..be07c4b10 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-11-3/FunctionPointerConversionContext.expected @@ -0,0 +1,16 @@ +| test.cpp:11:7:11:7 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. | +| test.cpp:14:7:14:7 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. | +| test.cpp:18:16:18:16 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. | +| test.cpp:21:14:21:14 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. | +| test.cpp:25:7:25:7 | call to operator void (*)() | Inappropriate conversion from function type to pointer-to-function type in 'call to operator void (*)()'. | +| test.cpp:33:14:33:14 | call to operator void (*)() | Inappropriate conversion from function type to pointer-to-function type in 'call to operator void (*)()'. | +| test.cpp:61:13:61:13 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. | +| test.cpp:64:13:64:13 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. | +| test.cpp:67:7:67:7 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. | +| test.cpp:71:7:71:7 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. | +| test.cpp:74:7:74:7 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. | +| test.cpp:77:7:77:7 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. | +| test.cpp:83:7:83:7 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. | +| test.cpp:87:7:87:7 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. | +| test.cpp:92:7:92:7 | call to operator void (*)() | Inappropriate conversion from function type to pointer-to-function type in 'call to operator void (*)()'. | +| test.cpp:96:7:96:7 | call to operator void (*)() | Inappropriate conversion from function type to pointer-to-function type in 'call to operator void (*)()'. | diff --git a/cpp/misra/test/rules/RULE-7-11-3/FunctionPointerConversionContext.qlref b/cpp/misra/test/rules/RULE-7-11-3/FunctionPointerConversionContext.qlref new file mode 100644 index 000000000..d05d05586 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-11-3/FunctionPointerConversionContext.qlref @@ -0,0 +1 @@ +rules/RULE-7-11-3/FunctionPointerConversionContext.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-11-3/test.cpp b/cpp/misra/test/rules/RULE-7-11-3/test.cpp new file mode 100644 index 000000000..0522a703f --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-11-3/test.cpp @@ -0,0 +1,98 @@ +#include +#include + +// Test functions +extern int *f(); +void f1(double); +void f1(std::uint32_t); +void simple_function(); + +void test_function_to_pointer_conversion() { + if (f) { // NON_COMPLIANT + } + + if (f == nullptr) { // NON_COMPLIANT + } + + std::cout << std::boolalpha // COMPLIANT - considered assignment + << f; // NON_COMPLIANT - not assignment + + // Non-compliant: Unary plus operator causing pointer decay + auto l1 = +f; // NON_COMPLIANT + + auto lam = []() {}; + // Lambda used in boolean context + if (lam) { // NON_COMPLIANT + } + + // Lambda used in address-of operator + if (&lam) { // COMPLIANT + } + + // Unary plus on lambda + auto l2 = +lam; // NON_COMPLIANT + + // Using address-of operator + if (&f != nullptr) { // COMPLIANT + } + + // Function call + (f)(); // COMPLIANT + lam(); // COMPLIANT + + // static_cast conversion + auto selected = static_cast(f1); // COMPLIANT + + // Assignment to pointer-to-function type + void (*p)() = lam; // COMPLIANT + + // Assignment to pointer-to-function type + int *(*func_ptr)() = f; // COMPLIANT + + // Using address-of operator + void (*simple_ptr)() = &simple_function; // COMPLIANT + + // Direct assignment without conversion + void (*simple_ptr2)() = simple_function; // COMPLIANT +} + +void test_arithmetic_expressions() { + // Function used in arithmetic expression (triggers pointer decay) + auto l3 = f + 0; // NON_COMPLIANT + + // Function used in arithmetic subtraction expression + auto l4 = f - 0; // NON_COMPLIANT + + // Function used in comparison with non-null pointer + if (f > nullptr) { // NON_COMPLIANT + } + + // Function used in relational operators + if (f < nullptr) { // NON_COMPLIANT + } + + if (f <= nullptr) { // NON_COMPLIANT + } + + if (f >= nullptr) { // NON_COMPLIANT + } +} + +void test_logical_expressions() { + // Function used in logical AND expression + if (f && true) { // NON_COMPLIANT + } + + // Function used in logical OR expression + if (f || false) { // NON_COMPLIANT + } + + // Lambda used in logical AND expression + auto lam = []() {}; + if (lam && true) { // NON_COMPLIANT + } + + // Lambda used in logical OR expression + if (lam || false) { // NON_COMPLIANT + } +} \ No newline at end of file From 2e753103a3272c06977a83b2d8de15f88fb7c1eb Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Wed, 25 Jun 2025 10:35:02 +0100 Subject: [PATCH 37/57] C++: Improve char_traits stubs --- .../test/includes/standard-library/cwchar | 23 +++++ .../test/includes/standard-library/ios.h | 3 + .../test/includes/standard-library/string | 93 +++++++++++++++++-- 3 files changed, 113 insertions(+), 6 deletions(-) diff --git a/cpp/common/test/includes/standard-library/cwchar b/cpp/common/test/includes/standard-library/cwchar index e69de29bb..cba1ea724 100644 --- a/cpp/common/test/includes/standard-library/cwchar +++ b/cpp/common/test/includes/standard-library/cwchar @@ -0,0 +1,23 @@ +#ifndef _GHLIBCPP_CWCHAR +#define _GHLIBCPP_CWCHAR + +#include "stddef.h" + +namespace std { +// Character classification and conversion types +typedef struct { + int __count; + union { + unsigned int __wch; + char __wchb[4]; + } __value; +} mbstate_t; + +typedef unsigned int wint_t; + +// Wide character constants +static const wint_t WEOF = static_cast(-1); + +} // namespace std + +#endif // _GHLIBCPP_CWCHAR \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/ios.h b/cpp/common/test/includes/standard-library/ios.h index 55e7e8db5..08d32a6c3 100644 --- a/cpp/common/test/includes/standard-library/ios.h +++ b/cpp/common/test/includes/standard-library/ios.h @@ -5,6 +5,9 @@ namespace std { typedef size_t streamsize; typedef int pos_type; +typedef long long streamoff; +typedef pos_type streampos; +typedef pos_type wstreampos; // Bitmask type as specified by [bitmask.types] // Operators omitted as not required for our test cases diff --git a/cpp/common/test/includes/standard-library/string b/cpp/common/test/includes/standard-library/string index a3f22f5e8..cb76a7742 100644 --- a/cpp/common/test/includes/standard-library/string +++ b/cpp/common/test/includes/standard-library/string @@ -1,12 +1,92 @@ #ifndef _GHLIBCPP_STRING #define _GHLIBCPP_STRING +#include "cwchar" #include "initializer_list" +#include "ios.h" #include "iosfwd.h" #include "iterator.h" #include "stddef.h" namespace std { -template struct char_traits; +template struct char_traits { + typedef charT char_type; + typedef int int_type; + typedef streamoff off_type; + typedef streampos pos_type; + typedef mbstate_t state_type; + + static void assign(char_type &c1, const char_type &c2); + static bool eq(const char_type &c1, const char_type &c2); + static bool lt(const char_type &c1, const char_type &c2); + + static int compare(const char_type *s1, const char_type *s2, size_t n); + static size_t length(const char_type *s); + static const char_type *find(const char_type *s, size_t n, + const char_type &a); + static char_type *move(char_type *s1, const char_type *s2, size_t n); + static char_type *copy(char_type *s1, const char_type *s2, size_t n); + static char_type *assign(char_type *s, size_t n, char_type a); + + static int_type not_eof(const int_type &c); + static char_type to_char_type(const int_type &c); + static int_type to_int_type(const char_type &c); + static bool eq_int_type(const int_type &c1, const int_type &c2); + static int_type eof(); +}; + +// Specialization for char +template <> struct char_traits { + typedef char char_type; + typedef int int_type; + typedef streamoff off_type; + typedef streampos pos_type; + typedef mbstate_t state_type; + + static void assign(char_type &c1, const char_type &c2); + static bool eq(const char_type &c1, const char_type &c2); + static bool lt(const char_type &c1, const char_type &c2); + + static int compare(const char_type *s1, const char_type *s2, size_t n); + static size_t length(const char_type *s); + static const char_type *find(const char_type *s, size_t n, + const char_type &a); + static char_type *move(char_type *s1, const char_type *s2, size_t n); + static char_type *copy(char_type *s1, const char_type *s2, size_t n); + static char_type *assign(char_type *s, size_t n, char_type a); + + static int_type not_eof(const int_type &c); + static char_type to_char_type(const int_type &c); + static int_type to_int_type(const char_type &c); + static bool eq_int_type(const int_type &c1, const int_type &c2); + static int_type eof(); +}; + +// Specialization for wchar_t +template <> struct char_traits { + typedef wchar_t char_type; + typedef wint_t int_type; + typedef streamoff off_type; + typedef wstreampos pos_type; + typedef mbstate_t state_type; + + static void assign(char_type &c1, const char_type &c2); + static bool eq(const char_type &c1, const char_type &c2); + static bool lt(const char_type &c1, const char_type &c2); + + static int compare(const char_type *s1, const char_type *s2, size_t n); + static size_t length(const char_type *s); + static const char_type *find(const char_type *s, size_t n, + const char_type &a); + static char_type *move(char_type *s1, const char_type *s2, size_t n); + static char_type *copy(char_type *s1, const char_type *s2, size_t n); + static char_type *assign(char_type *s, size_t n, char_type a); + + static int_type not_eof(const int_type &c); + static char_type to_char_type(const int_type &c); + static int_type to_int_type(const char_type &c); + static bool eq_int_type(const int_type &c1, const int_type &c2); + static int_type eof(); +}; template class allocator { public: @@ -114,14 +194,15 @@ public: basic_string &replace(size_type pos, size_type n1, size_type n2, charT c); basic_string &replace(__const_iterator i1, __const_iterator i2, const basic_string &str); - basic_string &replace(__const_iterator i1, __const_iterator i2, const charT *s, - size_type n); - basic_string &replace(__const_iterator i1, __const_iterator i2, const charT *s); + basic_string &replace(__const_iterator i1, __const_iterator i2, + const charT *s, size_type n); + basic_string &replace(__const_iterator i1, __const_iterator i2, + const charT *s); basic_string &replace(__const_iterator i1, __const_iterator i2, size_type n, charT c); template - basic_string &replace(__const_iterator i1, __const_iterator i2, InputIterator j1, - InputIterator j2); + basic_string &replace(__const_iterator i1, __const_iterator i2, + InputIterator j1, InputIterator j2); basic_string &replace(__const_iterator, __const_iterator, initializer_list); From 4bf8d51d5f16d5502d3d818d118eed506adffbed Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Fri, 6 Jun 2025 14:50:00 +0100 Subject: [PATCH 38/57] Improve C++ stubs for locales --- .../test/includes/standard-library/clocale | 5 +- .../test/includes/standard-library/locale | 25 ++++++++ .../test/includes/standard-library/locale.h | 59 +++++++++---------- 3 files changed, 58 insertions(+), 31 deletions(-) create mode 100644 cpp/common/test/includes/standard-library/locale diff --git a/cpp/common/test/includes/standard-library/clocale b/cpp/common/test/includes/standard-library/clocale index 430c36daa..8ec16234b 100644 --- a/cpp/common/test/includes/standard-library/clocale +++ b/cpp/common/test/includes/standard-library/clocale @@ -1,4 +1,5 @@ -#pragma once +#ifndef _GHLIBCPP_CLOCALE +#define _GHLIBCPP_CLOCALE #define NULL 0 #define LC_ALL 0 @@ -15,3 +16,5 @@ using ::lconv; using ::localeconv; using ::setlocale; } // namespace std + +#endif // _GHLIBCPP_CLOCALE \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/locale b/cpp/common/test/includes/standard-library/locale new file mode 100644 index 000000000..4d19e3dba --- /dev/null +++ b/cpp/common/test/includes/standard-library/locale @@ -0,0 +1,25 @@ + +#ifndef _GHLIBCPP_LOCALE +#define _GHLIBCPP_LOCALE + +namespace std { + +class locale {}; + +template bool isspace(charT c, const locale &loc); +template bool isprint(charT c, const locale &loc); +template bool iscntrl(charT c, const locale &loc); +template bool isupper(charT c, const locale &loc); +template bool islower(charT c, const locale &loc); +template bool isalpha(charT c, const locale &loc); +template bool isdigit(charT c, const locale &loc); +template bool ispunct(charT c, const locale &loc); +template bool isxdigit(charT c, const locale &loc); +template bool isalnum(charT c, const locale &loc); +template bool isgraph(charT c, const locale &loc); +template bool isblank(charT c, const locale &loc); +template charT toupper(charT c, const locale &loc); +template charT tolower(charT c, const locale &loc); +} // namespace std + +#endif // _GHLIBCPP_LOCALE \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/locale.h b/cpp/common/test/includes/standard-library/locale.h index 19a890553..2501c9c5d 100644 --- a/cpp/common/test/includes/standard-library/locale.h +++ b/cpp/common/test/includes/standard-library/locale.h @@ -1,38 +1,37 @@ -#ifndef _GHLIBCPP_LOCALE -#define _GHLIBCPP_LOCALE +#ifndef _GHLIBCPP_LOCALE_H +#define _GHLIBCPP_LOCALE_H -#define LC_ALL 6 +#define LC_ALL 6 struct lconv { - char *decimal_point; - char *thousands_sep; - char *grouping; + char *decimal_point; + char *thousands_sep; + char *grouping; - char *int_curr_symbol; - char *currency_symbol; - char *mon_decimal_point; - char *mon_thousands_sep; - char *mon_grouping; - char *positive_sign; - char *negative_sign; - char int_frac_digits; - char frac_digits; - char p_cs_precedes; - char p_sep_by_space; - char n_cs_precedes; - char n_sep_by_space; - char p_sign_posn; - char n_sign_posn; - char int_p_cs_precedes; - char int_p_sep_by_space; - char int_n_cs_precedes; - char int_n_sep_by_space; - char int_p_sign_posn; - char int_n_sign_posn; + char *int_curr_symbol; + char *currency_symbol; + char *mon_decimal_point; + char *mon_thousands_sep; + char *mon_grouping; + char *positive_sign; + char *negative_sign; + char int_frac_digits; + char frac_digits; + char p_cs_precedes; + char p_sep_by_space; + char n_cs_precedes; + char n_sep_by_space; + char p_sign_posn; + char n_sign_posn; + char int_p_cs_precedes; + char int_p_sep_by_space; + char int_n_cs_precedes; + char int_n_sep_by_space; + char int_p_sign_posn; + char int_n_sign_posn; }; - -char *setlocale (int, const char *); +char *setlocale(int, const char *); struct lconv *localeconv(void); -#endif // _GHLIBCPP_LOCALE \ No newline at end of file +#endif // _GHLIBCPP_LOCALE_H \ No newline at end of file From 4d5e35f1d3281ac31de87067c21ecd18d2ee0cf1 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Fri, 6 Jun 2025 17:24:47 +0100 Subject: [PATCH 39/57] Extend C++ stubs for locale --- .../test/includes/standard-library/locale | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/cpp/common/test/includes/standard-library/locale b/cpp/common/test/includes/standard-library/locale index 4d19e3dba..755c5f6ee 100644 --- a/cpp/common/test/includes/standard-library/locale +++ b/cpp/common/test/includes/standard-library/locale @@ -2,9 +2,45 @@ #ifndef _GHLIBCPP_LOCALE #define _GHLIBCPP_LOCALE +#include + namespace std { -class locale {}; +class locale { +public: + class facet; + class id; + typedef int category; + + static const category none = 0, collate = 0x010, ctype = 0x020, + monetary = 0x040, numeric = 0x080, time = 0x100, + messages = 0x200, + all = collate | ctype | monetary | numeric | time | + messages; + + locale() noexcept; + locale(const locale &other) noexcept; + explicit locale(const char *std_name); + explicit locale(const string &std_name); + locale(const locale &other, const char *std_name, category); + locale(const locale &other, const string &std_name, category); + template locale(const locale &other, Facet *f); + locale(const locale &other, const locale &one, category); + ~locale(); + const locale &operator=(const locale &other) noexcept; + template locale combine(const locale &other) const; + + basic_string name() const; + + bool operator==(const locale &other) const; + bool operator!=(const locale &other) const; + template + bool operator()(const basic_string &s1, + const basic_string &s2) const; + + static locale global(const locale &); + static const locale &classic(); +}; template bool isspace(charT c, const locale &loc); template bool isprint(charT c, const locale &loc); From f2b5410bf2e9edad2c2643b1d861845e580b2d32 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Wed, 25 Jun 2025 10:41:21 +0100 Subject: [PATCH 40/57] C++: Add optional stubs --- .../test/includes/standard-library/optional | 165 ++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 cpp/common/test/includes/standard-library/optional diff --git a/cpp/common/test/includes/standard-library/optional b/cpp/common/test/includes/standard-library/optional new file mode 100644 index 000000000..8b129a36c --- /dev/null +++ b/cpp/common/test/includes/standard-library/optional @@ -0,0 +1,165 @@ +#ifndef _GHLIBCPP_OPTIONAL +#define _GHLIBCPP_OPTIONAL + +#include "initializer_list" +#include "stddef.h" + +namespace std { + +// Forward declarations and helper types +struct in_place_t { + explicit in_place_t() = default; +}; +constexpr in_place_t in_place{}; + +// Type trait helper +template struct decay { + typedef T type; +}; + +// nullopt_t type and nullopt constant +struct nullopt_t { + explicit constexpr nullopt_t(int) {} +}; +constexpr nullopt_t nullopt{0}; + +// bad_optional_access exception +class bad_optional_access {}; + +// optional template class +template class optional { +public: + typedef T value_type; + + // Constructors + constexpr optional() noexcept; + constexpr optional(nullopt_t) noexcept; + constexpr optional(const optional &other); + constexpr optional(optional &&other) noexcept; + template constexpr explicit optional(const optional &other); + template constexpr explicit optional(optional &&other); + template + constexpr explicit optional(in_place_t, Args &&...args); + template + constexpr explicit optional(in_place_t, initializer_list ilist, + Args &&...args); + template constexpr explicit optional(U &&value); + + // Destructor + ~optional(); + + // Assignment operators + optional &operator=(nullopt_t) noexcept; + constexpr optional &operator=(const optional &other); + constexpr optional &operator=(optional &&other) noexcept; + template optional &operator=(U &&value); + template optional &operator=(const optional &other); + template optional &operator=(optional &&other); + + // Observers + constexpr const T *operator->() const; + constexpr T *operator->(); + constexpr const T &operator*() const &; + constexpr T &operator*() &; + constexpr const T &&operator*() const &&; + constexpr T &&operator*() &&; + constexpr explicit operator bool() const noexcept; + constexpr bool has_value() const noexcept; + constexpr const T &value() const &; + constexpr T &value() &; + constexpr const T &&value() const &&; + constexpr T &&value() &&; + template constexpr T value_or(U &&default_value) const &; + template constexpr T value_or(U &&default_value) &&; + + // Modifiers + void swap(optional &other) noexcept; + void reset() noexcept; + template T &emplace(Args &&...args); + template + T &emplace(initializer_list ilist, Args &&...args); +}; + +// Deduction guides +template optional(T) -> optional; + +// Comparison operators +template +constexpr bool operator==(const optional &lhs, const optional &rhs); +template +constexpr bool operator!=(const optional &lhs, const optional &rhs); +template +constexpr bool operator<(const optional &lhs, const optional &rhs); +template +constexpr bool operator<=(const optional &lhs, const optional &rhs); +template +constexpr bool operator>(const optional &lhs, const optional &rhs); +template +constexpr bool operator>=(const optional &lhs, const optional &rhs); + +// Comparison with nullopt +template +constexpr bool operator==(const optional &opt, nullopt_t) noexcept; +template +constexpr bool operator==(nullopt_t, const optional &opt) noexcept; +template +constexpr bool operator!=(const optional &opt, nullopt_t) noexcept; +template +constexpr bool operator!=(nullopt_t, const optional &opt) noexcept; +template +constexpr bool operator<(const optional &opt, nullopt_t) noexcept; +template +constexpr bool operator<(nullopt_t, const optional &opt) noexcept; +template +constexpr bool operator<=(const optional &opt, nullopt_t) noexcept; +template +constexpr bool operator<=(nullopt_t, const optional &opt) noexcept; +template +constexpr bool operator>(const optional &opt, nullopt_t) noexcept; +template +constexpr bool operator>(nullopt_t, const optional &opt) noexcept; +template +constexpr bool operator>=(const optional &opt, nullopt_t) noexcept; +template +constexpr bool operator>=(nullopt_t, const optional &opt) noexcept; + +// Comparison with T +template +constexpr bool operator==(const optional &opt, const U &value); +template +constexpr bool operator==(const T &value, const optional &opt); +template +constexpr bool operator!=(const optional &opt, const U &value); +template +constexpr bool operator!=(const T &value, const optional &opt); +template +constexpr bool operator<(const optional &opt, const U &value); +template +constexpr bool operator<(const T &value, const optional &opt); +template +constexpr bool operator<=(const optional &opt, const U &value); +template +constexpr bool operator<=(const T &value, const optional &opt); +template +constexpr bool operator>(const optional &opt, const U &value); +template +constexpr bool operator>(const T &value, const optional &opt); +template +constexpr bool operator>=(const optional &opt, const U &value); +template +constexpr bool operator>=(const T &value, const optional &opt); + +// Specialized algorithms +template void swap(optional &lhs, optional &rhs) noexcept; + +// Factory functions +template +constexpr optional::type> make_optional(T &&value); +template +constexpr optional make_optional(Args &&...args); +template +constexpr optional make_optional(initializer_list ilist, Args &&...args); + +} // namespace std + +#endif // _GHLIBCPP_OPTIONAL \ No newline at end of file From 74946cf7f0d46f66606a71d3436c987e73308889 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Wed, 25 Jun 2025 19:47:22 +0100 Subject: [PATCH 41/57] Rule 7.0.3: NoCharacterNumericalValue.ql Adds a query that identifies when a numerical value of a character has been used. Also fixes a character type category bug, exposed getBuiltinType and provide a CharacterType class. --- .../cpp/misra/BuiltInTypeRules.qll | 49 +++++-- .../RULE-7-0-3/NoCharacterNumericalValue.ql | 61 ++++++++ .../NoCharacterNumericalValue.expected | 27 ++++ .../NoCharacterNumericalValue.qlref | 1 + cpp/misra/test/rules/RULE-7-0-3/test.cpp | 131 ++++++++++++++++++ 5 files changed, 256 insertions(+), 13 deletions(-) create mode 100644 cpp/misra/src/rules/RULE-7-0-3/NoCharacterNumericalValue.ql create mode 100644 cpp/misra/test/rules/RULE-7-0-3/NoCharacterNumericalValue.expected create mode 100644 cpp/misra/test/rules/RULE-7-0-3/NoCharacterNumericalValue.qlref create mode 100644 cpp/misra/test/rules/RULE-7-0-3/test.cpp diff --git a/cpp/misra/src/codingstandards/cpp/misra/BuiltInTypeRules.qll b/cpp/misra/src/codingstandards/cpp/misra/BuiltInTypeRules.qll index e728aa7e4..0ce4bfd42 100644 --- a/cpp/misra/src/codingstandards/cpp/misra/BuiltInTypeRules.qll +++ b/cpp/misra/src/codingstandards/cpp/misra/BuiltInTypeRules.qll @@ -23,7 +23,7 @@ newtype TypeCategory = */ TypeCategory getTypeCategory(BuiltInType t) { ( - t instanceof CharType or + t instanceof PlainCharType or t instanceof WideCharType or t instanceof Char16Type or t instanceof Char32Type or @@ -58,6 +58,25 @@ TypeCategory getTypeCategory(BuiltInType t) { result = Other() } +/** + * Gets the built-in type of a type, if it is a built-in type. + * + * This function will strip specifiers and typedefs to get the underlying built-in type. + */ +BuiltInType getBuiltInType(Type t) { + // Get the built-in type of a type, if it is a built-in type + result = t + or + // Strip specifiers and typedefs to get the built-in type + result = getBuiltInType(t.getUnspecifiedType()) + or + // For reference types, get the base type and then the built-in type + result = getBuiltInType(t.(ReferenceType).getBaseType()) + or + // For enum types, get the explicit underlying type and then the built-in type + result = getBuiltInType(t.(Enum).getExplicitUnderlyingType()) +} + /** * The signedness of a MISRA C++ 2023 numeric type. */ @@ -65,6 +84,19 @@ newtype Signedness = Signed() or Unsigned() +class CharacterType extends Type { + // The actual character type, which is either a plain char or a wide char + BuiltInType realType; + + CharacterType() { + // A type whose type category is character + getTypeCategory(realType) = Character() and + realType = getBuiltInType(this) + } + + Type getRealType() { result = realType } +} + /** * A MISRA C++ 2023 numeric type is a type that represents a number, either an integral or a floating-point. * @@ -78,18 +110,9 @@ class NumericType extends Type { Type realType; NumericType() { - // A type which is either an integral or a floating-point type category - getTypeCategory(this) = [Integral().(TypeCategory), FloatingPoint()] and - realType = this - or - // Any type which, after stripping specifiers and typedefs, is a numeric type - realType = this.getUnspecifiedType().(NumericType).getRealType() - or - // Any reference type where the base type is a numeric type - realType = this.(ReferenceType).getBaseType().(NumericType).getRealType() - or - // Any Enum type with an explicit underlying type that is a numeric type - realType = this.(Enum).getExplicitUnderlyingType().(NumericType).getRealType() + // A type whose type category is either integral or a floating-point + getTypeCategory(realType) = [Integral().(TypeCategory), FloatingPoint()] and + realType = getBuiltInType(this) } Signedness getSignedness() { diff --git a/cpp/misra/src/rules/RULE-7-0-3/NoCharacterNumericalValue.ql b/cpp/misra/src/rules/RULE-7-0-3/NoCharacterNumericalValue.ql new file mode 100644 index 000000000..c4d294a0b --- /dev/null +++ b/cpp/misra/src/rules/RULE-7-0-3/NoCharacterNumericalValue.ql @@ -0,0 +1,61 @@ +/** + * @id cpp/misra/no-character-numerical-value + * @name RULE-7-0-3: The numerical value of a character shall not be used + * @description Using the numerical value of a character type may lead to inconsistent behavior due + * to encoding dependencies and should be avoided in favor of safer C++ Standard + * Library functions. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-7-0-3 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra.BuiltInTypeRules + +from Conversion c, Expr expr, Type sourceType, Type targetType +where + expr = c.getExpr() and + sourceType = expr.getType() and + targetType = c.getType() and + ( + // Conversion from character type to non-character type + sourceType instanceof CharacterType and + not targetType instanceof CharacterType + or + // Conversion from non-character type to character type + not sourceType instanceof CharacterType and + targetType instanceof CharacterType + // or + // // Conversion between different character types + // getTypeCategory(sourceType) instanceof Character and + // getTypeCategory(targetType) instanceof Character and + // not sourceType = targetType + ) and + // Exclude conversions where both operands have the same character type in equality operations + not exists(EqualityOperation eq, CharacterType leftType, CharacterType rightType | + eq.getAnOperand() = expr and + leftType = eq.getLeftOperand().getType() and + rightType = eq.getRightOperand().getType() and + leftType.getRealType() = rightType.getRealType() + ) and + // Exclude unevaluated operands + not ( + expr.getParent*() instanceof SizeofExprOperator or + expr.getParent*() instanceof SizeofTypeOperator or + expr.getParent*() instanceof TypeidOperator or + expr.getParent*() = any(Decltype dt).getExpr() or + expr.getParent*() instanceof StaticAssert + ) and + // Exclude optional comparisons that don't involve conversion + not exists(FunctionCall fc | + fc.getTarget().hasName("operator==") and + fc.getAnArgument() = expr and + fc.getQualifier().getType().hasName("optional") + ) +select expr, + "Conversion of character type '" + sourceType.getName() + "' to '" + targetType.getName() + + "' uses numerical value of character." diff --git a/cpp/misra/test/rules/RULE-7-0-3/NoCharacterNumericalValue.expected b/cpp/misra/test/rules/RULE-7-0-3/NoCharacterNumericalValue.expected new file mode 100644 index 000000000..0b6088d34 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-3/NoCharacterNumericalValue.expected @@ -0,0 +1,27 @@ +| test.cpp:17:13:17:14 | 10 | Conversion of character type 'int' to 'char' uses numerical value of character. | +| test.cpp:18:13:18:14 | 65 | Conversion of character type 'int' to 'char' uses numerical value of character. | +| test.cpp:19:13:19:13 | 0 | Conversion of character type 'int' to 'char' uses numerical value of character. | +| test.cpp:24:20:24:22 | 97 | Conversion of character type 'char' to 'int8_t' uses numerical value of character. | +| test.cpp:25:21:25:24 | 13 | Conversion of character type 'char' to 'uint8_t' uses numerical value of character. | +| test.cpp:26:12:26:14 | 98 | Conversion of character type 'char' to 'int' uses numerical value of character. | +| test.cpp:43:13:43:14 | l1 | Conversion of character type 'char' to 'int' uses numerical value of character. | +| test.cpp:43:13:43:20 | ... - ... | Conversion of character type 'int' to 'char' uses numerical value of character. | +| test.cpp:43:18:43:20 | 48 | Conversion of character type 'char' to 'int' uses numerical value of character. | +| test.cpp:44:13:44:14 | l1 | Conversion of character type 'char' to 'int' uses numerical value of character. | +| test.cpp:44:13:44:18 | ... + ... | Conversion of character type 'int' to 'char' uses numerical value of character. | +| test.cpp:45:13:45:14 | l1 | Conversion of character type 'char' to 'int' uses numerical value of character. | +| test.cpp:45:13:45:18 | ... * ... | Conversion of character type 'int' to 'char' uses numerical value of character. | +| test.cpp:51:7:51:8 | l1 | Conversion of character type 'char' to 'bool' uses numerical value of character. | +| test.cpp:51:13:51:14 | l2 | Conversion of character type 'char' to 'bool' uses numerical value of character. | +| test.cpp:53:7:53:8 | l1 | Conversion of character type 'char' to 'bool' uses numerical value of character. | +| test.cpp:55:8:55:9 | l2 | Conversion of character type 'char' to 'bool' uses numerical value of character. | +| test.cpp:73:39:73:41 | 97 | Conversion of character type 'char' to 'int_type' uses numerical value of character. | +| test.cpp:89:8:89:9 | l1 | Conversion of character type 'char' to 'int' uses numerical value of character. | +| test.cpp:89:14:89:16 | 48 | Conversion of character type 'char' to 'int' uses numerical value of character. | +| test.cpp:89:23:89:24 | l1 | Conversion of character type 'char' to 'int' uses numerical value of character. | +| test.cpp:89:29:89:31 | 57 | Conversion of character type 'char' to 'int' uses numerical value of character. | +| test.cpp:102:25:102:26 | l1 | Conversion of character type 'char' to 'int' uses numerical value of character. | +| test.cpp:117:15:117:16 | l2 | Conversion of character type 'int_type' to 'char' uses numerical value of character. | +| test.cpp:123:31:123:32 | 65 | Conversion of character type 'int' to 'char' uses numerical value of character. | +| test.cpp:124:29:124:31 | 65 | Conversion of character type 'char' to 'int' uses numerical value of character. | +| test.cpp:130:6:130:7 | l2 | Conversion of character type 'char' to 'int' uses numerical value of character. | diff --git a/cpp/misra/test/rules/RULE-7-0-3/NoCharacterNumericalValue.qlref b/cpp/misra/test/rules/RULE-7-0-3/NoCharacterNumericalValue.qlref new file mode 100644 index 000000000..b1254bda1 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-3/NoCharacterNumericalValue.qlref @@ -0,0 +1 @@ +rules/RULE-7-0-3/NoCharacterNumericalValue.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-0-3/test.cpp b/cpp/misra/test/rules/RULE-7-0-3/test.cpp new file mode 100644 index 000000000..205712b1f --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-3/test.cpp @@ -0,0 +1,131 @@ +#include +#include +#include +#include +#include +#include +#include + +void test_character_literal_assignment() { + char l1 = 'a'; // COMPLIANT + char l2 = '\r'; // COMPLIANT + char l3 = '\n'; // COMPLIANT + char l4 = '\0'; // COMPLIANT +} + +void test_implicit_conversion_from_int() { + char l1 = 10; // NON_COMPLIANT + char l2 = 65; // NON_COMPLIANT + char l3 = 0; // NON_COMPLIANT +} + +void test_implicit_conversion_to_int() { + char l1 = 'a'; + std::int8_t l2 = 'a'; // NON_COMPLIANT + std::uint8_t l3 = '\r'; // NON_COMPLIANT + int l4 = 'b'; // NON_COMPLIANT +} + +void test_signed_char_assignment() { + signed char l1 = 11; // COMPLIANT + signed char l2 = 65; // COMPLIANT +} + +void test_conversion_between_character_types() { + char l1 = L'A'; // COMPLIANT + wchar_t l2 = 'B'; // COMPLIANT + char16_t l3 = 'C'; // COMPLIANT + char32_t l4 = 'D'; // COMPLIANT +} + +void test_arithmetic_operations() { + char l1 = 'a'; + char l2 = l1 - '0'; // NON_COMPLIANT + char l3 = l1 + 1; // NON_COMPLIANT + char l4 = l1 * 2; // NON_COMPLIANT +} + +void test_boolean_conversion() { + char l1 = 'a'; + char l2 = '\0'; + if (l1 && l2) { // NON_COMPLIANT + } + if (l1) { // NON_COMPLIANT + } + if (!l2) { // NON_COMPLIANT + } +} + +void test_same_type_comparison() { + char l1 = 'a'; + if (l1 != 'q') { // COMPLIANT + } + if (l1 == 'b') { // COMPLIANT + } +} + +void test_char_traits_usage() { + using CT = std::char_traits; + char l1 = 'a'; + if (CT::eq(l1, 'q')) { // COMPLIANT + } + auto l2 = CT::to_int_type('a'); // COMPLIANT + auto l3 = static_cast('a'); // NON_COMPLIANT +} + +void test_optional_comparison() { + std::optional l1; + if (l1 == 'r') { // COMPLIANT + } +} + +void test_unevaluated_operand() { + decltype('s' + 't') l1; // COMPLIANT + static_assert(sizeof('x') > 0); // COMPLIANT +} + +void test_range_check_non_compliant() { + char l1 = 'a'; + if ((l1 >= '0') && (l1 <= '9')) { // NON_COMPLIANT + } +} + +void test_range_check_compliant() { + using CT = std::char_traits; + char l1 = 'a'; + if (!CT::lt(l1, '0') && !CT::lt('9', l1)) { // COMPLIANT + } +} + +void test_isdigit_non_compliant() { + char l1 = 'a'; + if (0 == std::isdigit(l1)) { // NON_COMPLIANT + } +} + +void test_isdigit_compliant() { + char l1 = 'a'; + if (std::isdigit(l1, std::locale{})) { // COMPLIANT + } +} + +void test_stream_conversion() { + std::istream &l1 = std::cin; + auto l2 = l1.get(); + using CT = std::char_traits; + if (CT::not_eof(l2)) { + char l3 = l2; // NON_COMPLIANT + char l4 = CT::to_char_type(l2); // COMPLIANT + } +} + +void test_explicit_cast() { + char l1 = static_cast(65); // NON_COMPLIANT + int l2 = static_cast('A'); // NON_COMPLIANT +} + +void test_function_parameter_conversion() { + auto f1 = [](int l1) {}; + char l2 = 'x'; + f1(l2); // NON_COMPLIANT +} \ No newline at end of file From a2d7ee33ccf5d9eee4fe0bf25d93b624c330e907 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Thu, 26 Jun 2025 20:41:27 +0100 Subject: [PATCH 42/57] RULE-7-0-5 - NoSignednessChangeFromPromotion Detects integral promotions and arithmetic conversions that change the signedness or type category of operands, preventing unexpected behavior from implicit type conversions. [a] --- .../NoSignednessChangeFromPromotion.ql | 88 +++++++++++ .../NoSignednessChangeFromPromotion.expected | 47 ++++++ .../NoSignednessChangeFromPromotion.qlref | 1 + cpp/misra/test/rules/RULE-7-0-5/test.cpp | 146 ++++++++++++++++++ 4 files changed, 282 insertions(+) create mode 100644 cpp/misra/src/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.ql create mode 100644 cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected create mode 100644 cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.qlref create mode 100644 cpp/misra/test/rules/RULE-7-0-5/test.cpp diff --git a/cpp/misra/src/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.ql b/cpp/misra/src/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.ql new file mode 100644 index 000000000..f9a4fb6f5 --- /dev/null +++ b/cpp/misra/src/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.ql @@ -0,0 +1,88 @@ +/** + * @id cpp/misra/no-signedness-change-from-promotion + * @name RULE-7-0-5: Integral promotion and the usual arithmetic conversions shall not change the signedness or the type + * @description Integral promotion and usual arithmetic conversions that change operand signedness + * or type category may cause unexpected behavior or undefined behavior when operations + * overflow. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-7-0-5 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.misra.BuiltInTypeRules + +/** + * A `Conversion` that is relevant for the rule. + */ +abstract class RelevantConversion extends Conversion { + NumericType fromType; + NumericType toType; + + RelevantConversion() { + fromType = this.getExpr().getType().getUnspecifiedType() and + toType = this.getType().getUnspecifiedType() and + this.isImplicit() + } + + Type getFromType() { result = fromType } + + Type getToType() { result = toType } +} + +class UsualArithmeticConversion extends RelevantConversion { + UsualArithmeticConversion() { + ( + exists(BinaryOperation op | op.getAnOperand().getFullyConverted() = this) or + exists(UnaryOperation uao | uao.getOperand().getFullyConverted() = this) or + exists(AssignArithmeticOperation ao | ao.getAnOperand().getFullyConverted() = this) + ) + } +} + +class IntegerPromotion extends RelevantConversion { + IntegerPromotion() { + // Only consider cases where the integer promotion is the last conversion applied + exists(Expr e | e.getFullyConverted() = this) and + // Integer promotion occurs where the from type is smaller than int + fromType.getRealSize() < any(IntType i).(NumericType).getRealSize() and + // To type is bigger than or equal to int + toType.getRealSize() >= any(IntType i).(NumericType).getRealSize() and + fromType.getTypeCategory() = Integral() and + toType.getTypeCategory() = Integral() + } +} + +from Expr e, RelevantConversion c, NumericType fromType, NumericType toType, string changeType +where + not isExcluded(e, ConversionsPackage::noSignednessChangeFromPromotionQuery()) and + c = e.getConversion() and + fromType = c.getFromType() and + toType = c.getToType() and + ( + fromType.getSignedness() != toType.getSignedness() and changeType = "signedness" + or + fromType.getTypeCategory() != toType.getTypeCategory() and changeType = "type category" + ) and + // Ignore crement operations + not exists(CrementOperation cop | cop.getAnOperand() = e) and + // Exception 1: allow safe constant conversions + not ( + e.getValue().toInt() >= 0 and + fromType.(IntegralType).isSigned() and + toType.(IntegralType).isUnsigned() + ) and + // Exception 2: allow safe conversions from integral to floating-point types + not ( + e.isConstant() and + fromType.getTypeCategory() = Integral() and + toType.getTypeCategory() = FloatingPoint() + ) +select e, + "Conversion from '" + fromType.getName() + "' to '" + toType.getName() + "' changes " + changeType + + "." diff --git a/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected b/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected new file mode 100644 index 000000000..0a2df7404 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected @@ -0,0 +1,47 @@ +| test.cpp:24:5:24:6 | l1 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:24:10:24:11 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:25:5:25:6 | l1 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:25:10:25:11 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:26:5:26:6 | l1 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:26:10:26:11 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:27:5:27:6 | l1 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:27:10:27:11 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:28:5:28:6 | l1 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:28:10:28:11 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:29:5:29:6 | l1 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:29:10:29:11 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:30:5:30:6 | l1 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:30:10:30:11 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:31:5:31:6 | l1 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:31:10:31:11 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:45:11:45:12 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:46:11:46:12 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:47:11:47:12 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:48:11:48:12 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:49:11:49:12 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:50:11:50:12 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:51:11:51:12 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:52:11:52:12 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:64:5:64:6 | l1 | Conversion from 'signed int' to 'unsigned int' changes signedness. | +| test.cpp:65:5:65:6 | l1 | Conversion from 'signed int' to 'unsigned int' changes signedness. | +| test.cpp:66:5:66:6 | l1 | Conversion from 'signed int' to 'unsigned int' changes signedness. | +| test.cpp:67:5:67:6 | l1 | Conversion from 'signed int' to 'unsigned int' changes signedness. | +| test.cpp:68:5:68:6 | l1 | Conversion from 'signed int' to 'unsigned int' changes signedness. | +| test.cpp:69:5:69:6 | l1 | Conversion from 'signed int' to 'unsigned int' changes signedness. | +| test.cpp:71:5:71:6 | l3 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:71:10:71:11 | l4 | Conversion from 'unsigned short' to 'int' changes signedness. | +| test.cpp:72:5:72:6 | l3 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:72:10:72:11 | l4 | Conversion from 'unsigned short' to 'int' changes signedness. | +| test.cpp:82:10:82:11 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:82:15:82:16 | l4 | Conversion from 'unsigned short' to 'int' changes signedness. | +| test.cpp:89:5:89:6 | l1 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:90:5:90:6 | l1 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:100:6:100:7 | l1 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:102:6:102:7 | l1 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:104:6:104:7 | l1 | Conversion from 'unsigned char' to 'int' changes signedness. | +| test.cpp:143:11:143:12 | l2 | Conversion from 'unsigned int' to 'float' changes signedness. | +| test.cpp:143:11:143:12 | l2 | Conversion from 'unsigned int' to 'float' changes type category. | +| test.cpp:144:11:144:12 | l2 | Conversion from 'unsigned int' to 'float' changes signedness. | +| test.cpp:144:11:144:12 | l2 | Conversion from 'unsigned int' to 'float' changes type category. | +| test.cpp:145:11:145:12 | l2 | Conversion from 'unsigned int' to 'float' changes signedness. | +| test.cpp:145:11:145:12 | l2 | Conversion from 'unsigned int' to 'float' changes type category. | diff --git a/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.qlref b/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.qlref new file mode 100644 index 000000000..ae020269b --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.qlref @@ -0,0 +1 @@ +rules/RULE-7-0-5/NoSignednessChangeFromPromotion.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-0-5/test.cpp b/cpp/misra/test/rules/RULE-7-0-5/test.cpp new file mode 100644 index 000000000..f851f2ac5 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-5/test.cpp @@ -0,0 +1,146 @@ +#include + +// Global variables for testing +std::uint8_t g1 = 5; +std::uint8_t g2 = 10; +std::uint16_t g3 = 100; +std::uint32_t g4 = 1000; +std::int8_t g5 = -5; +std::int32_t g6 = -1000; +float g7 = 3.14f; + +constexpr std::int32_t f1(std::int32_t i) { + return i * i; +} + +void test_binary_arithmetic_operations() { + std::uint8_t l1 = 5; + std::uint8_t l2 = 10; + std::uint16_t l3 = 100; + std::uint32_t l4 = 1000; + std::int8_t l5 = -5; + std::int32_t l6 = -1000; + + l1 + l2; // NON_COMPLIANT - u8 + u8 -> signed int + l1 * l2; // NON_COMPLIANT - u8 * u8 -> signed int + l1 - l2; // NON_COMPLIANT - u8 - u8 -> signed int + l1 / l2; // NON_COMPLIANT - u8 / u8 -> signed int + l1 % l2; // NON_COMPLIANT - u8 % u8 -> signed int + l1 & l2; // NON_COMPLIANT - u8 & u8 -> signed int + l1 | l2; // NON_COMPLIANT - u8 | u8 -> signed int + l1 ^ l2; // NON_COMPLIANT - u8 ^ u8 -> signed int + + static_cast(l1) + l2; // COMPLIANT - l2 -> unsigned int + l1 + static_cast(l2); // COMPLIANT - l1 -> unsigned int + + l6 * l5; // COMPLIANT - l5 -> signed int + l4 / l1; // COMPLIANT - l1 -> unsigned int +} + +void test_assignment_operations() { + std::uint8_t l1 = 5; + std::uint8_t l2 = 10; + std::uint32_t l3 = 1000; + + l1 += l2; // NON_COMPLIANT - same as l1 + l2 + l1 -= l2; // NON_COMPLIANT - same as l1 - l2 + l1 *= l2; // NON_COMPLIANT - same as l1 * l2 + l1 /= l2; // NON_COMPLIANT - same as l1 / l2 + l1 %= l2; // NON_COMPLIANT - same as l1 % l2 + l1 &= l2; // NON_COMPLIANT - same as l1 & l2 + l1 |= l2; // NON_COMPLIANT - same as l1 | l2 + l1 ^= l2; // NON_COMPLIANT - same as l1 ^ l2 + + l1 += static_cast(l2); // COMPLIANT - l1 -> unsigned int + l1 += l3; // COMPLIANT - l1 -> unsigned int +} + +void test_comparison_operations() { + std::int32_t l1 = -1000; + std::uint32_t l2 = 1000; + std::uint8_t l3 = 5; + std::uint16_t l4 = 100; + + l1 > l2; // NON_COMPLIANT - l1 -> unsigned int + l1 < l2; // NON_COMPLIANT - l1 -> unsigned int + l1 >= l2; // NON_COMPLIANT - l1 -> unsigned int + l1 <= l2; // NON_COMPLIANT - l1 -> unsigned int + l1 == l2; // NON_COMPLIANT - l1 -> unsigned int + l1 != l2; // NON_COMPLIANT - l1 -> unsigned int + + l3 > l4; // NON_COMPLIANT - l3 and l4 -> signed int + l3 < l4; // NON_COMPLIANT - l3 and l4 -> signed int +} + +void test_conditional_operator() { + bool l1 = true; + std::uint8_t l2 = 5; + std::uint8_t l3 = 10; + std::uint16_t l4 = 100; + + l1 ? l2 : l3; // COMPLIANT - no conversion + l1 ? l2 : l4; // NON_COMPLIANT - l2 and l4 -> signed int +} + +void test_shift_operations() { + std::uint8_t l1 = 5; + std::uint32_t l2 = 1000; + + l1 << 2; // NON_COMPLIANT - l1 -> signed int + l1 >> 1; // NON_COMPLIANT - l1 -> signed int + l2 << 2; // COMPLIANT + l2 >> 1; // COMPLIANT +} + +void test_unary_operations() { + std::uint8_t l1 = 5; + std::uint32_t l2 = 1000; + std::int8_t l3 = -5; + + ~l1; // NON_COMPLIANT - l1 -> signed int + ~l2; // COMPLIANT + -l1; // NON_COMPLIANT - l1 -> signed int + -l3; // COMPLIANT - l3 is signed + +l1; // NON_COMPLIANT - l1 -> signed int +} + +void test_increment_decrement() { + std::uint8_t l1 = 5; + std::uint16_t l2 = 100; + + l1++; // COMPLIANT - rule does not apply + ++l1; // COMPLIANT - rule does not apply + l1--; // COMPLIANT - rule does not apply + --l1; // COMPLIANT - rule does not apply + l2++; // COMPLIANT - rule does not apply + ++l2; // COMPLIANT - rule does not apply +} + +void test_array_subscript() { + int l1[10]; + std::uint8_t l2 = 5; + + l1[l2]; // COMPLIANT - rule does not apply +} + +void test_exception_compile_time_constants() { + std::uint32_t l1 = 1000; + float l2 = 3.14f; + std::int32_t l3 = 5; + + l1 - 1; // COMPLIANT - exception #1 + l1 + 42; // COMPLIANT - exception #1 + l2 += 1; // COMPLIANT - exception #2 + l2 += 0x10001; // COMPLIANT - exception #2 + l3 + f1(10); // COMPLIANT - exception #1 + l2 + f1(10); // COMPLIANT - exception #2 +} + +void test_floating_point_conversions() { + float l1; + std::uint32_t l2; + + l1 += l2; // NON_COMPLIANT - l2 -> floating + l1 *= l2; // NON_COMPLIANT - l2 -> floating + l1 /= l2; // NON_COMPLIANT - l2 -> floating +} \ No newline at end of file From c25057d70fdfc7e8ade419aca8cd85ecfbdef098 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Fri, 27 Jun 2025 11:47:59 +0100 Subject: [PATCH 43/57] Add sizeOfInt() predicate For determining the size of int on the given platform. --- cpp/common/src/codingstandards/cpp/types/Type.qll | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/cpp/common/src/codingstandards/cpp/types/Type.qll b/cpp/common/src/codingstandards/cpp/types/Type.qll index b1b0b7aba..dba3af7a4 100644 --- a/cpp/common/src/codingstandards/cpp/types/Type.qll +++ b/cpp/common/src/codingstandards/cpp/types/Type.qll @@ -112,6 +112,19 @@ predicate integralTypeBounds(IntegralType integralType, QlBuiltins::BigInt lb, Q ) } +/** + * The size of the smallest `int` type in the database in bytes. + */ +int sizeOfInt() { + // The size of int is implementation-defined + result = + min(int size | + size = any(IntType i | i.isSigned()).getCanonicalArithmeticType().getSize() + | + size + ) +} + /** * Convenience class to reduce the awkwardness of how `RoutineType` and `FunctionPointerIshType` * don't have a common ancestor. From 3a8dab1de3f64f3ea74a07d9dfb103f79a01ace3 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Fri, 27 Jun 2025 11:50:20 +0100 Subject: [PATCH 44/57] Rule 7.0.5: Refactor to enable non-Conversions Not all integer promotions or usual arithmetic conversions are actually Conversions in our model. --- .../NoSignednessChangeFromPromotion.ql | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/cpp/misra/src/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.ql b/cpp/misra/src/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.ql index f9a4fb6f5..cc56e93dd 100644 --- a/cpp/misra/src/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.ql +++ b/cpp/misra/src/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.ql @@ -17,25 +17,37 @@ import cpp import codingstandards.cpp.misra import codingstandards.cpp.misra.BuiltInTypeRules +abstract class RelevantConversion extends Expr { + abstract Type getFromType(); + + abstract Type getToType(); + + abstract Expr getConvertedExpr(); + + abstract string getKindOfConversion(); +} + /** * A `Conversion` that is relevant for the rule. */ -abstract class RelevantConversion extends Conversion { +abstract class RelevantRealConversion extends RelevantConversion, Conversion { NumericType fromType; NumericType toType; - RelevantConversion() { - fromType = this.getExpr().getType().getUnspecifiedType() and - toType = this.getType().getUnspecifiedType() and + RelevantRealConversion() { + fromType = this.getExpr().getType() and + toType = this.getType() and this.isImplicit() } - Type getFromType() { result = fromType } + override Type getFromType() { result = fromType } + + override Type getToType() { result = toType } - Type getToType() { result = toType } + override Expr getConvertedExpr() { result = this.getExpr() } } -class UsualArithmeticConversion extends RelevantConversion { +class UsualArithmeticConversion extends RelevantRealConversion { UsualArithmeticConversion() { ( exists(BinaryOperation op | op.getAnOperand().getFullyConverted() = this) or @@ -43,9 +55,11 @@ class UsualArithmeticConversion extends RelevantConversion { exists(AssignArithmeticOperation ao | ao.getAnOperand().getFullyConverted() = this) ) } + + override string getKindOfConversion() { result = "Usual arithmetic conversion" } } -class IntegerPromotion extends RelevantConversion { +class IntegerPromotion extends RelevantRealConversion { IntegerPromotion() { // Only consider cases where the integer promotion is the last conversion applied exists(Expr e | e.getFullyConverted() = this) and @@ -61,7 +75,7 @@ class IntegerPromotion extends RelevantConversion { from Expr e, RelevantConversion c, NumericType fromType, NumericType toType, string changeType where not isExcluded(e, ConversionsPackage::noSignednessChangeFromPromotionQuery()) and - c = e.getConversion() and + c.getConvertedExpr() = e and fromType = c.getFromType() and toType = c.getToType() and ( @@ -84,5 +98,5 @@ where toType.getTypeCategory() = FloatingPoint() ) select e, - "Conversion from '" + fromType.getName() + "' to '" + toType.getName() + "' changes " + changeType - + "." + c.getKindOfConversion() + " from '" + fromType.getName() + "' to '" + toType.getName() + + "' changes " + changeType + "." From 3cf9eacdf44c1caf31522f8396385e8f345dd7ac Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Fri, 27 Jun 2025 12:07:15 +0100 Subject: [PATCH 45/57] Improve detection of integer promotions and usual arithmetic conversions --- .../NoSignednessChangeFromPromotion.ql | 32 ++++++- .../NoSignednessChangeFromPromotion.expected | 94 +++++++++---------- 2 files changed, 74 insertions(+), 52 deletions(-) diff --git a/cpp/misra/src/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.ql b/cpp/misra/src/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.ql index cc56e93dd..f35c18bae 100644 --- a/cpp/misra/src/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.ql +++ b/cpp/misra/src/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.ql @@ -50,9 +50,22 @@ abstract class RelevantRealConversion extends RelevantConversion, Conversion { class UsualArithmeticConversion extends RelevantRealConversion { UsualArithmeticConversion() { ( - exists(BinaryOperation op | op.getAnOperand().getFullyConverted() = this) or - exists(UnaryOperation uao | uao.getOperand().getFullyConverted() = this) or - exists(AssignArithmeticOperation ao | ao.getAnOperand().getFullyConverted() = this) + // Most binary operations from and to numeric types participate in usual arithmetic conversions + exists(BinaryOperation op | + // Shifts do not participate in usual arithmetic conversions + not op instanceof LShiftExpr and + not op instanceof RShiftExpr and + op.getAnOperand().getFullyConverted() = this + ) + or + // Most binary assignment operations from and to numeric types participate in usual arithmetic + // conversions + exists(AssignOperation ao | + // Shifts do not participate in usual arithmetic conversions + not ao instanceof AssignLShiftExpr and + not ao instanceof AssignRShiftExpr and + ao.getRValue().getFullyConverted() = this + ) ) } @@ -61,15 +74,24 @@ class UsualArithmeticConversion extends RelevantRealConversion { class IntegerPromotion extends RelevantRealConversion { IntegerPromotion() { + // Exclude integer promotions combined with usual arithmetic conversions, which are handled separately + not this instanceof UsualArithmeticConversion and // Only consider cases where the integer promotion is the last conversion applied exists(Expr e | e.getFullyConverted() = this) and // Integer promotion occurs where the from type is smaller than int - fromType.getRealSize() < any(IntType i).(NumericType).getRealSize() and + fromType.getRealSize() < sizeOfInt() and // To type is bigger than or equal to int - toType.getRealSize() >= any(IntType i).(NumericType).getRealSize() and + toType.getRealSize() >= sizeOfInt() and + // An integer promotion is a conversion from an integral type to an integral type + // + // This deliberately excludes integer promotions from `bool` and unscoped enums which do not + // have a fixed underlying type, because neither of these are considered integral types in the + // MISRA C++ rules. fromType.getTypeCategory() = Integral() and toType.getTypeCategory() = Integral() } + + override string getKindOfConversion() { result = "Integer promotion" } } from Expr e, RelevantConversion c, NumericType fromType, NumericType toType, string changeType diff --git a/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected b/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected index 0a2df7404..5fbabb205 100644 --- a/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected +++ b/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected @@ -1,47 +1,47 @@ -| test.cpp:24:5:24:6 | l1 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:24:10:24:11 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:25:5:25:6 | l1 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:25:10:25:11 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:26:5:26:6 | l1 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:26:10:26:11 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:27:5:27:6 | l1 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:27:10:27:11 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:28:5:28:6 | l1 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:28:10:28:11 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:29:5:29:6 | l1 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:29:10:29:11 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:30:5:30:6 | l1 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:30:10:30:11 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:31:5:31:6 | l1 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:31:10:31:11 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:45:11:45:12 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:46:11:46:12 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:47:11:47:12 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:48:11:48:12 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:49:11:49:12 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:50:11:50:12 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:51:11:51:12 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:52:11:52:12 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:64:5:64:6 | l1 | Conversion from 'signed int' to 'unsigned int' changes signedness. | -| test.cpp:65:5:65:6 | l1 | Conversion from 'signed int' to 'unsigned int' changes signedness. | -| test.cpp:66:5:66:6 | l1 | Conversion from 'signed int' to 'unsigned int' changes signedness. | -| test.cpp:67:5:67:6 | l1 | Conversion from 'signed int' to 'unsigned int' changes signedness. | -| test.cpp:68:5:68:6 | l1 | Conversion from 'signed int' to 'unsigned int' changes signedness. | -| test.cpp:69:5:69:6 | l1 | Conversion from 'signed int' to 'unsigned int' changes signedness. | -| test.cpp:71:5:71:6 | l3 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:71:10:71:11 | l4 | Conversion from 'unsigned short' to 'int' changes signedness. | -| test.cpp:72:5:72:6 | l3 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:72:10:72:11 | l4 | Conversion from 'unsigned short' to 'int' changes signedness. | -| test.cpp:82:10:82:11 | l2 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:82:15:82:16 | l4 | Conversion from 'unsigned short' to 'int' changes signedness. | -| test.cpp:89:5:89:6 | l1 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:90:5:90:6 | l1 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:100:6:100:7 | l1 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:102:6:102:7 | l1 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:104:6:104:7 | l1 | Conversion from 'unsigned char' to 'int' changes signedness. | -| test.cpp:143:11:143:12 | l2 | Conversion from 'unsigned int' to 'float' changes signedness. | -| test.cpp:143:11:143:12 | l2 | Conversion from 'unsigned int' to 'float' changes type category. | -| test.cpp:144:11:144:12 | l2 | Conversion from 'unsigned int' to 'float' changes signedness. | -| test.cpp:144:11:144:12 | l2 | Conversion from 'unsigned int' to 'float' changes type category. | -| test.cpp:145:11:145:12 | l2 | Conversion from 'unsigned int' to 'float' changes signedness. | -| test.cpp:145:11:145:12 | l2 | Conversion from 'unsigned int' to 'float' changes type category. | +| test.cpp:24:5:24:6 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:24:10:24:11 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:25:5:25:6 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:25:10:25:11 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:26:5:26:6 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:26:10:26:11 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:27:5:27:6 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:27:10:27:11 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:28:5:28:6 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:28:10:28:11 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:29:5:29:6 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:29:10:29:11 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:30:5:30:6 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:30:10:30:11 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:31:5:31:6 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:31:10:31:11 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:45:11:45:12 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:46:11:46:12 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:47:11:47:12 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:48:11:48:12 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:49:11:49:12 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:50:11:50:12 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:51:11:51:12 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:52:11:52:12 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:64:5:64:6 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | +| test.cpp:65:5:65:6 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | +| test.cpp:66:5:66:6 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | +| test.cpp:67:5:67:6 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | +| test.cpp:68:5:68:6 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | +| test.cpp:69:5:69:6 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | +| test.cpp:71:5:71:6 | l3 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:71:10:71:11 | l4 | Usual arithmetic conversion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:72:5:72:6 | l3 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:72:10:72:11 | l4 | Usual arithmetic conversion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:82:10:82:11 | l2 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:82:15:82:16 | l4 | Integer promotion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:89:5:89:6 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:90:5:90:6 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:100:6:100:7 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:102:6:102:7 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:104:6:104:7 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:143:11:143:12 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes signedness. | +| test.cpp:143:11:143:12 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes type category. | +| test.cpp:144:11:144:12 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes signedness. | +| test.cpp:144:11:144:12 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes type category. | +| test.cpp:145:11:145:12 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes signedness. | +| test.cpp:145:11:145:12 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes type category. | From f50baa9166523b6c39fc5c5c3e8831ecc6a2db30 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Fri, 27 Jun 2025 12:08:12 +0100 Subject: [PATCH 46/57] Format test case --- .../NoSignednessChangeFromPromotion.expected | 94 ++++---- cpp/misra/test/rules/RULE-7-0-5/test.cpp | 208 +++++++++--------- 2 files changed, 150 insertions(+), 152 deletions(-) diff --git a/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected b/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected index 5fbabb205..ede74bd18 100644 --- a/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected +++ b/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected @@ -1,47 +1,47 @@ -| test.cpp:24:5:24:6 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:24:10:24:11 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:25:5:25:6 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:25:10:25:11 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:26:5:26:6 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:26:10:26:11 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:27:5:27:6 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:27:10:27:11 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:28:5:28:6 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:28:10:28:11 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:29:5:29:6 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:29:10:29:11 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:30:5:30:6 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:30:10:30:11 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:31:5:31:6 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:31:10:31:11 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:45:11:45:12 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:46:11:46:12 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:47:11:47:12 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:48:11:48:12 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:49:11:49:12 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:50:11:50:12 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:51:11:51:12 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:52:11:52:12 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:64:5:64:6 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | -| test.cpp:65:5:65:6 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | -| test.cpp:66:5:66:6 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | -| test.cpp:67:5:67:6 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | -| test.cpp:68:5:68:6 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | -| test.cpp:69:5:69:6 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | -| test.cpp:71:5:71:6 | l3 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:71:10:71:11 | l4 | Usual arithmetic conversion from 'uint16_t' to 'int' changes signedness. | -| test.cpp:72:5:72:6 | l3 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:72:10:72:11 | l4 | Usual arithmetic conversion from 'uint16_t' to 'int' changes signedness. | -| test.cpp:82:10:82:11 | l2 | Integer promotion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:82:15:82:16 | l4 | Integer promotion from 'uint16_t' to 'int' changes signedness. | -| test.cpp:89:5:89:6 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:90:5:90:6 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:100:6:100:7 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:102:6:102:7 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:104:6:104:7 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:143:11:143:12 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes signedness. | -| test.cpp:143:11:143:12 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes type category. | -| test.cpp:144:11:144:12 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes signedness. | -| test.cpp:144:11:144:12 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes type category. | -| test.cpp:145:11:145:12 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes signedness. | -| test.cpp:145:11:145:12 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes type category. | +| test.cpp:22:3:22:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:22:8:22:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:23:3:23:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:23:7:23:8 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:24:3:24:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:24:8:24:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:25:3:25:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:25:8:25:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:26:3:26:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:26:8:26:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:27:3:27:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:27:8:27:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:28:3:28:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:28:8:28:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:29:3:29:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:29:8:29:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:43:9:43:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:44:9:44:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:45:9:45:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:46:9:46:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:47:9:47:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:48:9:48:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:49:9:49:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:50:9:50:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:62:3:62:4 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | +| test.cpp:63:3:63:4 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | +| test.cpp:64:3:64:4 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | +| test.cpp:65:3:65:4 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | +| test.cpp:66:3:66:4 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | +| test.cpp:67:3:67:4 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | +| test.cpp:69:3:69:4 | l3 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:69:8:69:9 | l4 | Usual arithmetic conversion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:70:3:70:4 | l3 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:70:8:70:9 | l4 | Usual arithmetic conversion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:80:8:80:9 | l2 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:80:13:80:14 | l4 | Integer promotion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:87:3:87:4 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:88:3:88:4 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:98:4:98:5 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:100:4:100:5 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:102:4:102:5 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:141:9:141:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes signedness. | +| test.cpp:141:9:141:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes type category. | +| test.cpp:142:9:142:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes signedness. | +| test.cpp:142:9:142:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes type category. | +| test.cpp:143:9:143:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes signedness. | +| test.cpp:143:9:143:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes type category. | diff --git a/cpp/misra/test/rules/RULE-7-0-5/test.cpp b/cpp/misra/test/rules/RULE-7-0-5/test.cpp index f851f2ac5..272b699a6 100644 --- a/cpp/misra/test/rules/RULE-7-0-5/test.cpp +++ b/cpp/misra/test/rules/RULE-7-0-5/test.cpp @@ -9,138 +9,136 @@ std::int8_t g5 = -5; std::int32_t g6 = -1000; float g7 = 3.14f; -constexpr std::int32_t f1(std::int32_t i) { - return i * i; -} +constexpr std::int32_t f1(std::int32_t i) { return i * i; } void test_binary_arithmetic_operations() { - std::uint8_t l1 = 5; - std::uint8_t l2 = 10; - std::uint16_t l3 = 100; - std::uint32_t l4 = 1000; - std::int8_t l5 = -5; - std::int32_t l6 = -1000; - - l1 + l2; // NON_COMPLIANT - u8 + u8 -> signed int - l1 * l2; // NON_COMPLIANT - u8 * u8 -> signed int - l1 - l2; // NON_COMPLIANT - u8 - u8 -> signed int - l1 / l2; // NON_COMPLIANT - u8 / u8 -> signed int - l1 % l2; // NON_COMPLIANT - u8 % u8 -> signed int - l1 & l2; // NON_COMPLIANT - u8 & u8 -> signed int - l1 | l2; // NON_COMPLIANT - u8 | u8 -> signed int - l1 ^ l2; // NON_COMPLIANT - u8 ^ u8 -> signed int - - static_cast(l1) + l2; // COMPLIANT - l2 -> unsigned int - l1 + static_cast(l2); // COMPLIANT - l1 -> unsigned int - - l6 * l5; // COMPLIANT - l5 -> signed int - l4 / l1; // COMPLIANT - l1 -> unsigned int + std::uint8_t l1 = 5; + std::uint8_t l2 = 10; + std::uint16_t l3 = 100; + std::uint32_t l4 = 1000; + std::int8_t l5 = -5; + std::int32_t l6 = -1000; + + l1 + l2; // NON_COMPLIANT - u8 + u8 -> signed int + l1 *l2; // NON_COMPLIANT - u8 * u8 -> signed int + l1 - l2; // NON_COMPLIANT - u8 - u8 -> signed int + l1 / l2; // NON_COMPLIANT - u8 / u8 -> signed int + l1 % l2; // NON_COMPLIANT - u8 % u8 -> signed int + l1 & l2; // NON_COMPLIANT - u8 & u8 -> signed int + l1 | l2; // NON_COMPLIANT - u8 | u8 -> signed int + l1 ^ l2; // NON_COMPLIANT - u8 ^ u8 -> signed int + + static_cast(l1) + l2; // COMPLIANT - l2 -> unsigned int + l1 + static_cast(l2); // COMPLIANT - l1 -> unsigned int + + l6 *l5; // COMPLIANT - l5 -> signed int + l4 / l1; // COMPLIANT - l1 -> unsigned int } void test_assignment_operations() { - std::uint8_t l1 = 5; - std::uint8_t l2 = 10; - std::uint32_t l3 = 1000; - - l1 += l2; // NON_COMPLIANT - same as l1 + l2 - l1 -= l2; // NON_COMPLIANT - same as l1 - l2 - l1 *= l2; // NON_COMPLIANT - same as l1 * l2 - l1 /= l2; // NON_COMPLIANT - same as l1 / l2 - l1 %= l2; // NON_COMPLIANT - same as l1 % l2 - l1 &= l2; // NON_COMPLIANT - same as l1 & l2 - l1 |= l2; // NON_COMPLIANT - same as l1 | l2 - l1 ^= l2; // NON_COMPLIANT - same as l1 ^ l2 - - l1 += static_cast(l2); // COMPLIANT - l1 -> unsigned int - l1 += l3; // COMPLIANT - l1 -> unsigned int + std::uint8_t l1 = 5; + std::uint8_t l2 = 10; + std::uint32_t l3 = 1000; + + l1 += l2; // NON_COMPLIANT - same as l1 + l2 + l1 -= l2; // NON_COMPLIANT - same as l1 - l2 + l1 *= l2; // NON_COMPLIANT - same as l1 * l2 + l1 /= l2; // NON_COMPLIANT - same as l1 / l2 + l1 %= l2; // NON_COMPLIANT - same as l1 % l2 + l1 &= l2; // NON_COMPLIANT - same as l1 & l2 + l1 |= l2; // NON_COMPLIANT - same as l1 | l2 + l1 ^= l2; // NON_COMPLIANT - same as l1 ^ l2 + + l1 += static_cast(l2); // COMPLIANT - l1 -> unsigned int + l1 += l3; // COMPLIANT - l1 -> unsigned int } void test_comparison_operations() { - std::int32_t l1 = -1000; - std::uint32_t l2 = 1000; - std::uint8_t l3 = 5; - std::uint16_t l4 = 100; - - l1 > l2; // NON_COMPLIANT - l1 -> unsigned int - l1 < l2; // NON_COMPLIANT - l1 -> unsigned int - l1 >= l2; // NON_COMPLIANT - l1 -> unsigned int - l1 <= l2; // NON_COMPLIANT - l1 -> unsigned int - l1 == l2; // NON_COMPLIANT - l1 -> unsigned int - l1 != l2; // NON_COMPLIANT - l1 -> unsigned int - - l3 > l4; // NON_COMPLIANT - l3 and l4 -> signed int - l3 < l4; // NON_COMPLIANT - l3 and l4 -> signed int + std::int32_t l1 = -1000; + std::uint32_t l2 = 1000; + std::uint8_t l3 = 5; + std::uint16_t l4 = 100; + + l1 > l2; // NON_COMPLIANT - l1 -> unsigned int + l1 < l2; // NON_COMPLIANT - l1 -> unsigned int + l1 >= l2; // NON_COMPLIANT - l1 -> unsigned int + l1 <= l2; // NON_COMPLIANT - l1 -> unsigned int + l1 == l2; // NON_COMPLIANT - l1 -> unsigned int + l1 != l2; // NON_COMPLIANT - l1 -> unsigned int + + l3 > l4; // NON_COMPLIANT - l3 and l4 -> signed int + l3 < l4; // NON_COMPLIANT - l3 and l4 -> signed int } void test_conditional_operator() { - bool l1 = true; - std::uint8_t l2 = 5; - std::uint8_t l3 = 10; - std::uint16_t l4 = 100; - - l1 ? l2 : l3; // COMPLIANT - no conversion - l1 ? l2 : l4; // NON_COMPLIANT - l2 and l4 -> signed int + bool l1 = true; + std::uint8_t l2 = 5; + std::uint8_t l3 = 10; + std::uint16_t l4 = 100; + + l1 ? l2 : l3; // COMPLIANT - no conversion + l1 ? l2 : l4; // NON_COMPLIANT - l2 and l4 -> signed int } void test_shift_operations() { - std::uint8_t l1 = 5; - std::uint32_t l2 = 1000; - - l1 << 2; // NON_COMPLIANT - l1 -> signed int - l1 >> 1; // NON_COMPLIANT - l1 -> signed int - l2 << 2; // COMPLIANT - l2 >> 1; // COMPLIANT + std::uint8_t l1 = 5; + std::uint32_t l2 = 1000; + + l1 << 2; // NON_COMPLIANT - l1 -> signed int + l1 >> 1; // NON_COMPLIANT - l1 -> signed int + l2 << 2; // COMPLIANT + l2 >> 1; // COMPLIANT } void test_unary_operations() { - std::uint8_t l1 = 5; - std::uint32_t l2 = 1000; - std::int8_t l3 = -5; - - ~l1; // NON_COMPLIANT - l1 -> signed int - ~l2; // COMPLIANT - -l1; // NON_COMPLIANT - l1 -> signed int - -l3; // COMPLIANT - l3 is signed - +l1; // NON_COMPLIANT - l1 -> signed int + std::uint8_t l1 = 5; + std::uint32_t l2 = 1000; + std::int8_t l3 = -5; + + ~l1; // NON_COMPLIANT - l1 -> signed int + ~l2; // COMPLIANT + -l1; // NON_COMPLIANT - l1 -> signed int + -l3; // COMPLIANT - l3 is signed + +l1; // NON_COMPLIANT - l1 -> signed int } void test_increment_decrement() { - std::uint8_t l1 = 5; - std::uint16_t l2 = 100; - - l1++; // COMPLIANT - rule does not apply - ++l1; // COMPLIANT - rule does not apply - l1--; // COMPLIANT - rule does not apply - --l1; // COMPLIANT - rule does not apply - l2++; // COMPLIANT - rule does not apply - ++l2; // COMPLIANT - rule does not apply + std::uint8_t l1 = 5; + std::uint16_t l2 = 100; + + l1++; // COMPLIANT - rule does not apply + ++l1; // COMPLIANT - rule does not apply + l1--; // COMPLIANT - rule does not apply + --l1; // COMPLIANT - rule does not apply + l2++; // COMPLIANT - rule does not apply + ++l2; // COMPLIANT - rule does not apply } void test_array_subscript() { - int l1[10]; - std::uint8_t l2 = 5; - - l1[l2]; // COMPLIANT - rule does not apply + int l1[10]; + std::uint8_t l2 = 5; + + l1[l2]; // COMPLIANT - rule does not apply } void test_exception_compile_time_constants() { - std::uint32_t l1 = 1000; - float l2 = 3.14f; - std::int32_t l3 = 5; - - l1 - 1; // COMPLIANT - exception #1 - l1 + 42; // COMPLIANT - exception #1 - l2 += 1; // COMPLIANT - exception #2 - l2 += 0x10001; // COMPLIANT - exception #2 - l3 + f1(10); // COMPLIANT - exception #1 - l2 + f1(10); // COMPLIANT - exception #2 + std::uint32_t l1 = 1000; + float l2 = 3.14f; + std::int32_t l3 = 5; + + l1 - 1; // COMPLIANT - exception #1 + l1 + 42; // COMPLIANT - exception #1 + l2 += 1; // COMPLIANT - exception #2 + l2 += 0x10001; // COMPLIANT - exception #2 + l3 + f1(10); // COMPLIANT - exception #1 + l2 + f1(10); // COMPLIANT - exception #2 } void test_floating_point_conversions() { - float l1; - std::uint32_t l2; - - l1 += l2; // NON_COMPLIANT - l2 -> floating - l1 *= l2; // NON_COMPLIANT - l2 -> floating - l1 /= l2; // NON_COMPLIANT - l2 -> floating + float l1; + std::uint32_t l2; + + l1 += l2; // NON_COMPLIANT - l2 -> floating + l1 *= l2; // NON_COMPLIANT - l2 -> floating + l1 /= l2; // NON_COMPLIANT - l2 -> floating } \ No newline at end of file From a7ce6f5b476201dedf27b7bd643db62a02818080 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Fri, 27 Jun 2025 12:16:13 +0100 Subject: [PATCH 47/57] Rule 7.0.5: Expand test cases - More shift testing - Mixed signed/unsigned --- .../NoSignednessChangeFromPromotion.expected | 66 ++++++++++------ cpp/misra/test/rules/RULE-7-0-5/test.cpp | 78 ++++++++++++++++--- 2 files changed, 111 insertions(+), 33 deletions(-) diff --git a/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected b/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected index ede74bd18..b97aa3ee2 100644 --- a/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected +++ b/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected @@ -22,26 +22,46 @@ | test.cpp:48:9:48:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | | test.cpp:49:9:49:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | | test.cpp:50:9:50:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:62:3:62:4 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | -| test.cpp:63:3:63:4 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | -| test.cpp:64:3:64:4 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | -| test.cpp:65:3:65:4 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | -| test.cpp:66:3:66:4 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | -| test.cpp:67:3:67:4 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | -| test.cpp:69:3:69:4 | l3 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:69:8:69:9 | l4 | Usual arithmetic conversion from 'uint16_t' to 'int' changes signedness. | -| test.cpp:70:3:70:4 | l3 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:70:8:70:9 | l4 | Usual arithmetic conversion from 'uint16_t' to 'int' changes signedness. | -| test.cpp:80:8:80:9 | l2 | Integer promotion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:80:13:80:14 | l4 | Integer promotion from 'uint16_t' to 'int' changes signedness. | -| test.cpp:87:3:87:4 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:88:3:88:4 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:98:4:98:5 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:100:4:100:5 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:102:4:102:5 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | -| test.cpp:141:9:141:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes signedness. | -| test.cpp:141:9:141:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes type category. | -| test.cpp:142:9:142:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes signedness. | -| test.cpp:142:9:142:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes type category. | -| test.cpp:143:9:143:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes signedness. | -| test.cpp:143:9:143:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes type category. | +| test.cpp:62:3:62:4 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:63:3:63:4 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:64:3:64:4 | l2 | Integer promotion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:65:3:65:4 | l2 | Integer promotion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:68:9:68:10 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:69:9:69:10 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:79:10:79:11 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:80:10:80:11 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:91:3:91:4 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | +| test.cpp:92:3:92:4 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | +| test.cpp:93:3:93:4 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | +| test.cpp:94:3:94:4 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | +| test.cpp:95:3:95:4 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | +| test.cpp:96:3:96:4 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | +| test.cpp:98:3:98:4 | l3 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:98:8:98:9 | l4 | Usual arithmetic conversion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:99:3:99:4 | l3 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:99:8:99:9 | l4 | Usual arithmetic conversion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:109:8:109:9 | l2 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:109:13:109:14 | l4 | Integer promotion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:117:4:117:5 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:119:4:119:5 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:121:4:121:5 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:164:8:164:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:165:8:165:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:166:7:166:8 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:167:8:167:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:168:8:168:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:169:8:169:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:170:8:170:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:171:8:171:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:173:8:173:9 | l4 | Usual arithmetic conversion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:174:8:174:9 | l4 | Usual arithmetic conversion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:175:7:175:8 | l4 | Usual arithmetic conversion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:177:8:177:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:178:8:178:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:179:9:179:10 | l4 | Usual arithmetic conversion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:199:9:199:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes signedness. | +| test.cpp:199:9:199:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes type category. | +| test.cpp:200:9:200:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes signedness. | +| test.cpp:200:9:200:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes type category. | +| test.cpp:201:9:201:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes signedness. | +| test.cpp:201:9:201:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes type category. | diff --git a/cpp/misra/test/rules/RULE-7-0-5/test.cpp b/cpp/misra/test/rules/RULE-7-0-5/test.cpp index 272b699a6..af2150e9a 100644 --- a/cpp/misra/test/rules/RULE-7-0-5/test.cpp +++ b/cpp/misra/test/rules/RULE-7-0-5/test.cpp @@ -53,6 +53,35 @@ void test_assignment_operations() { l1 += l3; // COMPLIANT - l1 -> unsigned int } +void test_shift_operations() { + std::uint8_t l1 = 5; + std::uint16_t l2 = 100; + std::uint32_t l3 = 1000; + std::int16_t l4; + + l1 << 2; // NON_COMPLIANT - l1 -> signed int + l1 >> 1; // NON_COMPLIANT - l1 -> signed int + l2 << 2; // NON_COMPLIANT - l2 -> signed int + l2 >> 1; // NON_COMPLIANT - l2 -> signed int + l3 << 2; // COMPLIANT + l3 >> 1; // COMPLIANT + l3 << l1; // NON_COMPLIANT - l1 -> signed int + l3 >> l1; // NON_COMPLIANT - l1 -> signed int + l3 << l4; // COMPLIANT + l3 >> l4; // COMPLIANT + + l1 <<= 2; // NON_COMPLIANT - l1 -> signed int + l1 >>= 1; // NON_COMPLIANT - l1 -> signed int + l2 <<= 2; // NON_COMPLIANT - l2 -> signed int + l2 >>= 1; // NON_COMPLIANT - l2 -> signed int + l3 <<= 2; // COMPLIANT + l3 >>= 1; // COMPLIANT + l3 <<= l1; // NON_COMPLIANT - l1 -> signed int + l3 >>= l1; // NON_COMPLIANT - l1 -> signed int + l3 <<= l4; // COMPLIANT + l3 >>= l4; // COMPLIANT +} + void test_comparison_operations() { std::int32_t l1 = -1000; std::uint32_t l2 = 1000; @@ -80,16 +109,6 @@ void test_conditional_operator() { l1 ? l2 : l4; // NON_COMPLIANT - l2 and l4 -> signed int } -void test_shift_operations() { - std::uint8_t l1 = 5; - std::uint32_t l2 = 1000; - - l1 << 2; // NON_COMPLIANT - l1 -> signed int - l1 >> 1; // NON_COMPLIANT - l1 -> signed int - l2 << 2; // COMPLIANT - l2 >> 1; // COMPLIANT -} - void test_unary_operations() { std::uint8_t l1 = 5; std::uint32_t l2 = 1000; @@ -121,6 +140,45 @@ void test_array_subscript() { l1[l2]; // COMPLIANT - rule does not apply } +void test_logical_operators() { + std::uint8_t l1 = 5; + std::uint8_t l2 = 10; + std::uint16_t l3 = 100; + std::int8_t l4 = -5; + bool l5 = true; + + l1 &&l2; // COMPLIANT - rule does not apply to logical operators + l1 || l2; // COMPLIANT - rule does not apply to logical operators + l1 &&l3; // COMPLIANT - rule does not apply to logical operators + l4 &&l1; // COMPLIANT - rule does not apply to logical operators + l5 &&l1; // COMPLIANT - rule does not apply to logical operators + l1 &&l5; // COMPLIANT - rule does not apply to logical operators +} + +void test_mixed_signed_unsigned_arithmetic() { + std::int8_t l1 = -5; + std::uint8_t l2 = 10; + std::int16_t l3 = -100; + std::uint16_t l4 = 200; + + l1 + l2; // NON_COMPLIANT - l1 and l2 -> signed int + l1 - l2; // NON_COMPLIANT - l1 and l2 -> signed int + l1 *l2; // NON_COMPLIANT - l1 and l2 -> signed int + l1 / l2; // NON_COMPLIANT - l1 and l2 -> signed int + l1 % l2; // NON_COMPLIANT - l1 and l2 -> signed int + l1 & l2; // NON_COMPLIANT - l1 and l2 -> signed int + l1 | l2; // NON_COMPLIANT - l1 and l2 -> signed int + l1 ^ l2; // NON_COMPLIANT - l1 and l2 -> signed int + + l3 + l4; // NON_COMPLIANT - l3 and l4 -> signed int + l3 - l4; // NON_COMPLIANT - l3 and l4 -> signed int + l3 *l4; // NON_COMPLIANT - l3 and l4 -> signed int + + l1 < l2; // NON_COMPLIANT - l1 and l2 -> signed int + l1 > l2; // NON_COMPLIANT - l1 and l2 -> signed int + l3 == l4; // NON_COMPLIANT - l3 and l4 -> signed int +} + void test_exception_compile_time_constants() { std::uint32_t l1 = 1000; float l2 = 3.14f; From 9aed463df7f38cce841938e280fc9d07b854c636 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Fri, 27 Jun 2025 12:17:57 +0100 Subject: [PATCH 48/57] Ruley 7.0.5: Support lvalue conversions on assign operations AssignOperations do not include a Conversion in the database for lvalues, so create new classes to capture those cases. --- .../NoSignednessChangeFromPromotion.ql | 91 +++++++++++++++++++ .../NoSignednessChangeFromPromotion.expected | 12 +++ 2 files changed, 103 insertions(+) diff --git a/cpp/misra/src/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.ql b/cpp/misra/src/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.ql index f35c18bae..8fe555fcb 100644 --- a/cpp/misra/src/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.ql +++ b/cpp/misra/src/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.ql @@ -94,6 +94,97 @@ class IntegerPromotion extends RelevantRealConversion { override string getKindOfConversion() { result = "Integer promotion" } } +class ImpliedUsualArithmeticConversion extends RelevantConversion { + NumericType fromType; + NumericType toType; + + ImpliedUsualArithmeticConversion() { + // The lvalue of an assignment operation does not have a `Conversion` in our model, but + // it is still subject to usual arithmetic conversions (excepting shifts). + // + // rvalues are handled separately in the `UsualArithmeticConversion` class. + exists(AssignOperation aop | + not aop instanceof AssignLShiftExpr and + not aop instanceof AssignRShiftExpr and + // lvalue subject to usual arithmetic conversions + aop.getLValue() = this and + // From type is the type of the lvalue, which should be a numeric type under the MISRA rule + fromType = this.getType() and + // Under usual arithmetic conversions, the converted types of both arguments will be the same, + // so even though we don't have an explicit conversion, we can still deduce that the target + // type will be the same as the converted type of the rvalue. + toType = aop.getRValue().getFullyConverted().getType() and + // Only consider cases where the conversion is not a no-op, for consistency with the `Conversion` class + not fromType.getRealType() = toType.getRealType() + ) + } + + override Type getFromType() { result = fromType } + + override Type getToType() { result = toType } + + override Expr getConvertedExpr() { result = this } + + override string getKindOfConversion() { result = "Usual arithmetic conversion" } +} + +class ImpliedIntegerPromotion extends RelevantConversion { + NumericType fromType; + + ImpliedIntegerPromotion() { + ( + exists(AssignLShiftExpr aop | aop.getLValue() = this) or + exists(AssignRShiftExpr aop | aop.getLValue() = this) + ) and + // Only consider integer promotions from MISRA C++ "numeric types" as per the rule + fromType = this.getType() and + fromType.getTypeCategory() = Integral() and + // If the size is less than int, then it is an implied integer promotion + fromType.getRealSize() < sizeOfInt() + } + + override Type getFromType() { result = fromType } + + override IntegralType getToType() { + // Only report the canonical type - e.g. `int` not `signed int` + result = result.getCanonicalArithmeticType() and + if result instanceof Char16Type or result instanceof Char32Type or result instanceof Wchar_t + then + // Smallest type that can hold the value of the `fromType` + result = + min(NumericType candidateType | + ( + candidateType instanceof IntType or + candidateType instanceof LongType or + candidateType instanceof LongLongType + ) and + fromType.getIntegralUpperBound() <= candidateType.getIntegralUpperBound() + | + candidateType order by candidateType.getIntegralUpperBound() + ) + else ( + // The result is always `int` or `unsigned int` + result instanceof IntType and + if + // If the `fromType` is signed, the result must be signed + fromType.getSignedness() = Signed() + or + // If the `fromType` is unsigned, but the result can fit into the signed int type, then the + // result must be signed as well. + fromType.getIntegralUpperBound() <= + any(IntType t | t.isSigned()).(NumericType).getIntegralUpperBound() + then result.isSigned() + else + // Otherwise an unsigned type is returned + result.isUnsigned() + ) + } + + override Expr getConvertedExpr() { result = this } + + override string getKindOfConversion() { result = "Integer promotion" } +} + from Expr e, RelevantConversion c, NumericType fromType, NumericType toType, string changeType where not isExcluded(e, ConversionsPackage::noSignednessChangeFromPromotionQuery()) and diff --git a/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected b/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected index b97aa3ee2..1bde6e9a1 100644 --- a/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected +++ b/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected @@ -14,13 +14,21 @@ | test.cpp:28:8:28:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | | test.cpp:29:3:29:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | | test.cpp:29:8:29:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:43:3:43:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | | test.cpp:43:9:43:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:44:3:44:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | | test.cpp:44:9:44:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:45:3:45:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | | test.cpp:45:9:45:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:46:3:46:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | | test.cpp:46:9:46:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:47:3:47:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | | test.cpp:47:9:47:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:48:3:48:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | | test.cpp:48:9:48:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:49:3:49:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | | test.cpp:49:9:49:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:50:3:50:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | | test.cpp:50:9:50:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | | test.cpp:62:3:62:4 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | | test.cpp:63:3:63:4 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | @@ -28,6 +36,10 @@ | test.cpp:65:3:65:4 | l2 | Integer promotion from 'uint16_t' to 'int' changes signedness. | | test.cpp:68:9:68:10 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | | test.cpp:69:9:69:10 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:73:3:73:4 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:74:3:74:4 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:75:3:75:4 | l2 | Integer promotion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:76:3:76:4 | l2 | Integer promotion from 'uint16_t' to 'int' changes signedness. | | test.cpp:79:10:79:11 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | | test.cpp:80:10:80:11 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | | test.cpp:91:3:91:4 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | From aac2dc21194f656b24a09f3d9e6522ee3ad45517 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Fri, 27 Jun 2025 12:20:36 +0100 Subject: [PATCH 49/57] Refactor to use NumericType --- .../NoSignednessChangeFromPromotion.ql | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/cpp/misra/src/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.ql b/cpp/misra/src/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.ql index 8fe555fcb..30d75e3de 100644 --- a/cpp/misra/src/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.ql +++ b/cpp/misra/src/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.ql @@ -18,9 +18,9 @@ import codingstandards.cpp.misra import codingstandards.cpp.misra.BuiltInTypeRules abstract class RelevantConversion extends Expr { - abstract Type getFromType(); + abstract NumericType getFromType(); - abstract Type getToType(); + abstract NumericType getToType(); abstract Expr getConvertedExpr(); @@ -40,9 +40,9 @@ abstract class RelevantRealConversion extends RelevantConversion, Conversion { this.isImplicit() } - override Type getFromType() { result = fromType } + override NumericType getFromType() { result = fromType } - override Type getToType() { result = toType } + override NumericType getToType() { result = toType } override Expr getConvertedExpr() { result = this.getExpr() } } @@ -119,9 +119,9 @@ class ImpliedUsualArithmeticConversion extends RelevantConversion { ) } - override Type getFromType() { result = fromType } + override NumericType getFromType() { result = fromType } - override Type getToType() { result = toType } + override NumericType getToType() { result = toType } override Expr getConvertedExpr() { result = this } @@ -143,11 +143,11 @@ class ImpliedIntegerPromotion extends RelevantConversion { fromType.getRealSize() < sizeOfInt() } - override Type getFromType() { result = fromType } + override NumericType getFromType() { result = fromType } - override IntegralType getToType() { + override NumericType getToType() { // Only report the canonical type - e.g. `int` not `signed int` - result = result.getCanonicalArithmeticType() and + result = result.(IntegralType).getCanonicalArithmeticType() and if result instanceof Char16Type or result instanceof Char32Type or result instanceof Wchar_t then // Smallest type that can hold the value of the `fromType` @@ -163,8 +163,6 @@ class ImpliedIntegerPromotion extends RelevantConversion { candidateType order by candidateType.getIntegralUpperBound() ) else ( - // The result is always `int` or `unsigned int` - result instanceof IntType and if // If the `fromType` is signed, the result must be signed fromType.getSignedness() = Signed() @@ -173,10 +171,12 @@ class ImpliedIntegerPromotion extends RelevantConversion { // result must be signed as well. fromType.getIntegralUpperBound() <= any(IntType t | t.isSigned()).(NumericType).getIntegralUpperBound() - then result.isSigned() + then + // `int` is returned + result.(IntType).isSigned() else - // Otherwise an unsigned type is returned - result.isUnsigned() + // Otherwise `unsigned int` is returned + result.(IntType).isUnsigned() ) } From 92c7dac28d7dfa21bdee67132b4c8fde935db87c Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Fri, 27 Jun 2025 13:17:08 +0100 Subject: [PATCH 50/57] Rule 7.0.5: Add pointer tests (should be ignored) --- cpp/misra/test/rules/RULE-7-0-5/test.cpp | 37 ++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/cpp/misra/test/rules/RULE-7-0-5/test.cpp b/cpp/misra/test/rules/RULE-7-0-5/test.cpp index af2150e9a..e6f853ff9 100644 --- a/cpp/misra/test/rules/RULE-7-0-5/test.cpp +++ b/cpp/misra/test/rules/RULE-7-0-5/test.cpp @@ -199,4 +199,41 @@ void test_floating_point_conversions() { l1 += l2; // NON_COMPLIANT - l2 -> floating l1 *= l2; // NON_COMPLIANT - l2 -> floating l1 /= l2; // NON_COMPLIANT - l2 -> floating +} + +void test_pointer_arithmetic() { + int l1[10]; + std::uint8_t l2 = 5; + std::uint16_t l3 = 2; + std::int8_t l4 = 3; + std::int32_t l5 = 1; + int *l6 = l1; + + l1 + l2; // COMPLIANT - rule does not apply to pointer arithmetic + l1 + l3; // COMPLIANT - rule does not apply to pointer arithmetic + l1 + l4; // COMPLIANT - rule does not apply to pointer arithmetic + l1 + l5; // COMPLIANT - rule does not apply to pointer arithmetic + l6 + l2; // COMPLIANT - rule does not apply to pointer arithmetic + l6 + l3; // COMPLIANT - rule does not apply to pointer arithmetic + + l1 - l2; // COMPLIANT - rule does not apply to pointer arithmetic + l6 - l3; // COMPLIANT - rule does not apply to pointer arithmetic + + l6 - l1; // COMPLIANT - rule does not apply to pointer arithmetic +} + +void test_pointer_assignment_arithmetic() { + int l1[10]; + std::uint8_t l2 = 5; + std::uint16_t l3 = 2; + std::int8_t l4 = 3; + int *l5 = l1; + + l5 += l2; // COMPLIANT - rule does not apply to pointer arithmetic + l5 += l3; // COMPLIANT - rule does not apply to pointer arithmetic + l5 += l4; // COMPLIANT - rule does not apply to pointer arithmetic + + l5 -= l2; // COMPLIANT - rule does not apply to pointer arithmetic + l5 -= l3; // COMPLIANT - rule does not apply to pointer arithmetic + l5 -= l4; // COMPLIANT - rule does not apply to pointer arithmetic } \ No newline at end of file From 4be88a3754d2cc90ffa67aba90bd2805b6be0a2c Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Fri, 27 Jun 2025 14:01:22 +0100 Subject: [PATCH 51/57] Rule 7.0.5: Add failing test case --- cpp/misra/test/rules/RULE-7-0-5/test.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/cpp/misra/test/rules/RULE-7-0-5/test.cpp b/cpp/misra/test/rules/RULE-7-0-5/test.cpp index e6f853ff9..9ee2c9bf6 100644 --- a/cpp/misra/test/rules/RULE-7-0-5/test.cpp +++ b/cpp/misra/test/rules/RULE-7-0-5/test.cpp @@ -236,4 +236,24 @@ void test_pointer_assignment_arithmetic() { l5 -= l2; // COMPLIANT - rule does not apply to pointer arithmetic l5 -= l3; // COMPLIANT - rule does not apply to pointer arithmetic l5 -= l4; // COMPLIANT - rule does not apply to pointer arithmetic -} \ No newline at end of file +} + +#define A 100LL // intmax_t +#define B 200LL // intmax_t +#define C 300ULL // uintmax_t +#define D 400ULL // uintmax_t + +#if A + B > 250 // COMPLIANT - both intmax_t, no conversion +; +#elif C + D < 800 // COMPLIANT - both uintmax_t, no conversion +; +#endif + +#define SIGNED_MAX 9223372036854775807LL // intmax_t +#define UNSIGNED_VAL 1ULL // uintmax_t + +#if SIGNED_MAX + UNSIGNED_VAL > 0 // NON_COMPLIANT[FALSE_NEGATIVE] +// intmax_t + uintmax_t → both converted to uintmax_t +// This changes SIGNED_MAX from signed to unsigned +; +#endif From f1502d6193706ac87675a23ea7a909c25bf670bf Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Tue, 1 Jul 2025 14:07:46 +0100 Subject: [PATCH 52/57] Rule 7.0.5: Add test cases for enum conversions --- .../NoSignednessChangeFromPromotion.expected | 31 ++++++++ cpp/misra/test/rules/RULE-7-0-5/test.cpp | 71 +++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected b/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected index 1bde6e9a1..a9f1f37c6 100644 --- a/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected +++ b/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected @@ -77,3 +77,34 @@ | test.cpp:200:9:200:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes type category. | | test.cpp:201:9:201:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes signedness. | | test.cpp:201:9:201:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes type category. | +| test.cpp:270:3:270:4 | l3 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:270:8:270:9 | l4 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:271:3:271:4 | l3 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:271:7:271:8 | l4 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:272:3:272:4 | l3 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:272:8:272:9 | l4 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:273:3:273:4 | l3 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:273:8:273:9 | l4 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:274:3:274:4 | l3 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:274:8:274:9 | l4 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:275:3:275:4 | l3 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:275:8:275:9 | l4 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:278:3:278:4 | l3 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:278:8:278:9 | l7 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:279:3:279:4 | l3 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:279:7:279:8 | l7 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:280:3:280:4 | l7 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:280:8:280:9 | l3 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:286:4:286:5 | l3 | Integer promotion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:287:4:287:5 | l3 | Integer promotion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:288:4:288:5 | l3 | Integer promotion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:294:3:294:31 | static_cast... | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:295:7:296:46 | static_cast... | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:299:3:299:4 | l3 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:299:8:299:9 | l4 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:300:3:300:4 | l3 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:300:9:300:10 | l4 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:301:3:301:4 | l3 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:301:9:301:10 | l7 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:304:3:304:4 | l3 | Integer promotion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:305:3:305:4 | l3 | Integer promotion from 'UnscopedEnumExplicit' to 'int' changes signedness. | diff --git a/cpp/misra/test/rules/RULE-7-0-5/test.cpp b/cpp/misra/test/rules/RULE-7-0-5/test.cpp index 9ee2c9bf6..6e36d80d6 100644 --- a/cpp/misra/test/rules/RULE-7-0-5/test.cpp +++ b/cpp/misra/test/rules/RULE-7-0-5/test.cpp @@ -238,6 +238,77 @@ void test_pointer_assignment_arithmetic() { l5 -= l4; // COMPLIANT - rule does not apply to pointer arithmetic } +// Enum types for testing +enum UnscopedEnum { VALUE1, VALUE2, VALUE3 }; +enum class ScopedEnum { VALUE1, VALUE2, VALUE3 }; +enum UnscopedEnumExplicit : std::uint8_t { + EXPLICIT_VALUE1 = 1, + EXPLICIT_VALUE2 = 2 +}; +enum class ScopedEnumExplicit : std::uint8_t { + EXPLICIT_VALUE1 = 1, + EXPLICIT_VALUE2 = 2 +}; + +void test_enum_types() { + UnscopedEnum l1 = VALUE1; + UnscopedEnum l2 = VALUE2; + UnscopedEnumExplicit l3 = EXPLICIT_VALUE1; + UnscopedEnumExplicit l4 = EXPLICIT_VALUE2; + ScopedEnum l5 = ScopedEnum::VALUE1; + ScopedEnumExplicit l6 = ScopedEnumExplicit::EXPLICIT_VALUE1; + std::uint8_t l7 = 5; + std::uint32_t l8 = 10; + + // Unscoped enum without explicit underlying type - not considered numeric + // type + l1 + l2; // COMPLIANT - rule does not apply + l1 *l2; // COMPLIANT - rule does not apply + l1 & l2; // COMPLIANT - rule does not apply + + // Unscoped enum with explicit underlying type - considered numeric type + l3 + l4; // NON_COMPLIANT - uint8_t + uint8_t -> signed int + l3 *l4; // NON_COMPLIANT - uint8_t * uint8_t -> signed int + l3 & l4; // NON_COMPLIANT - uint8_t & uint8_t -> signed int + l3 - l4; // NON_COMPLIANT - uint8_t - uint8_t -> signed int + l3 | l4; // NON_COMPLIANT - uint8_t | uint8_t -> signed int + l3 ^ l4; // NON_COMPLIANT - uint8_t ^ uint8_t -> signed int + + // Mixed enum and integer arithmetic + l3 + l7; // NON_COMPLIANT - uint8_t + uint8_t -> signed int + l3 *l7; // NON_COMPLIANT - uint8_t * uint8_t -> signed int + l7 - l3; // NON_COMPLIANT - uint8_t - uint8_t -> signed int + + l3 + l8; // COMPLIANT - uint8_t -> signed int (matches l8) + l8 *l3; // COMPLIANT - uint8_t -> signed int (matches l8) + + // Unary operations on enum with explicit underlying type + ~l3; // NON_COMPLIANT - uint8_t -> signed int + -l3; // NON_COMPLIANT - uint8_t -> signed int + +l3; // NON_COMPLIANT - uint8_t -> signed int + + // Scoped enums - not considered numeric type regardless of underlying type + static_cast(l5) + + static_cast(ScopedEnum::VALUE2); // COMPLIANT - rule does not apply + // to explicit casts + static_cast(l6) + // NON_COMPLIANT + static_cast( // NON_COMPLIANT + ScopedEnumExplicit::EXPLICIT_VALUE2); + + // Comparison operations with enum + l3 > l4; // NON_COMPLIANT - uint8_t > uint8_t -> signed int + l3 == l4; // NON_COMPLIANT - uint8_t == uint8_t -> signed int + l3 != l7; // NON_COMPLIANT - uint8_t != uint8_t -> signed int + + // Shift operations with enum + l3 << 1; // NON_COMPLIANT - uint8_t -> signed int + l3 >> 1; // NON_COMPLIANT - uint8_t -> signed int + + // Conditional operator with enum + true ? l3 : l4; // COMPLIANT - same types, no conversion + true ? l3 : l8; // COMPLIANT - same underlying types, no conversion +} + #define A 100LL // intmax_t #define B 200LL // intmax_t #define C 300ULL // uintmax_t From 89485d528411eb39e1a465aa762df45736814616 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Tue, 1 Jul 2025 14:25:08 +0100 Subject: [PATCH 53/57] Conversions: Swap some queries around Rule 7.0.4 is now in the Conversions package. Rules 8.x in the Conversions package are now moved to Conversions2. This is to avoid having too many complex queries in one package, and because 7.0.4 is more closely related to the other 7.x rules. --- .../cpp/exclusions/cpp/Conversions.qll | 121 +++------------ rule_packages/cpp/Conversions.json | 144 ++++++++++++++++++ rules.csv | 14 +- 3 files changed, 169 insertions(+), 110 deletions(-) create mode 100644 rule_packages/cpp/Conversions.json diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Conversions.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Conversions.qll index f151565d1..393f0cbdf 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Conversions.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Conversions.qll @@ -7,15 +7,10 @@ newtype ConversionsQuery = TNoConversionFromBoolQuery() or TNoImplicitBoolConversionQuery() or TNoCharacterNumericalValueQuery() or + TInappropriateBitwiseOrShiftOperandsQuery() or TNoSignednessChangeFromPromotionQuery() or TNumericAssignmentTypeMismatchQuery() or - TFunctionPointerConversionContextQuery() or - TVirtualBaseClassCastToDerivedQuery() or - TNoCStyleOrFunctionalCastsQuery() or - TIntToPointerCastProhibitedQuery() or - TNoPointerToIntegralCastQuery() or - TPointerToIntegralCastQuery() or - TNoStandaloneTypeCastExpressionQuery() + TFunctionPointerConversionContextQuery() predicate isConversionsQueryMetadata(Query query, string queryId, string ruleId, string category) { query = @@ -45,6 +40,15 @@ predicate isConversionsQueryMetadata(Query query, string queryId, string ruleId, ruleId = "RULE-7-0-3" and category = "required" or + query = + // `Query` instance for the `inappropriateBitwiseOrShiftOperands` query + ConversionsPackage::inappropriateBitwiseOrShiftOperandsQuery() and + queryId = + // `@id` for the `inappropriateBitwiseOrShiftOperands` query + "cpp/misra/inappropriate-bitwise-or-shift-operands" and + ruleId = "RULE-7-0-4" and + category = "required" + or query = // `Query` instance for the `noSignednessChangeFromPromotion` query ConversionsPackage::noSignednessChangeFromPromotionQuery() and @@ -71,60 +75,6 @@ predicate isConversionsQueryMetadata(Query query, string queryId, string ruleId, "cpp/misra/function-pointer-conversion-context" and ruleId = "RULE-7-11-3" and category = "required" - or - query = - // `Query` instance for the `virtualBaseClassCastToDerived` query - ConversionsPackage::virtualBaseClassCastToDerivedQuery() and - queryId = - // `@id` for the `virtualBaseClassCastToDerived` query - "cpp/misra/virtual-base-class-cast-to-derived" and - ruleId = "RULE-8-2-1" and - category = "required" - or - query = - // `Query` instance for the `noCStyleOrFunctionalCasts` query - ConversionsPackage::noCStyleOrFunctionalCastsQuery() and - queryId = - // `@id` for the `noCStyleOrFunctionalCasts` query - "cpp/misra/no-c-style-or-functional-casts" and - ruleId = "RULE-8-2-2" and - category = "required" - or - query = - // `Query` instance for the `intToPointerCastProhibited` query - ConversionsPackage::intToPointerCastProhibitedQuery() and - queryId = - // `@id` for the `intToPointerCastProhibited` query - "cpp/misra/int-to-pointer-cast-prohibited" and - ruleId = "RULE-8-2-6" and - category = "required" - or - query = - // `Query` instance for the `noPointerToIntegralCast` query - ConversionsPackage::noPointerToIntegralCastQuery() and - queryId = - // `@id` for the `noPointerToIntegralCast` query - "cpp/misra/no-pointer-to-integral-cast" and - ruleId = "RULE-8-2-7" and - category = "advisory" - or - query = - // `Query` instance for the `pointerToIntegralCast` query - ConversionsPackage::pointerToIntegralCastQuery() and - queryId = - // `@id` for the `pointerToIntegralCast` query - "cpp/misra/pointer-to-integral-cast" and - ruleId = "RULE-8-2-8" and - category = "required" - or - query = - // `Query` instance for the `noStandaloneTypeCastExpression` query - ConversionsPackage::noStandaloneTypeCastExpressionQuery() and - queryId = - // `@id` for the `noStandaloneTypeCastExpression` query - "cpp/misra/no-standalone-type-cast-expression" and - ruleId = "RULE-9-2-1" and - category = "required" } module ConversionsPackage { @@ -149,6 +99,13 @@ module ConversionsPackage { TQueryCPP(TConversionsPackageQuery(TNoCharacterNumericalValueQuery())) } + Query inappropriateBitwiseOrShiftOperandsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `inappropriateBitwiseOrShiftOperands` query + TQueryCPP(TConversionsPackageQuery(TInappropriateBitwiseOrShiftOperandsQuery())) + } + Query noSignednessChangeFromPromotionQuery() { //autogenerate `Query` type result = @@ -169,46 +126,4 @@ module ConversionsPackage { // `Query` type for `functionPointerConversionContext` query TQueryCPP(TConversionsPackageQuery(TFunctionPointerConversionContextQuery())) } - - Query virtualBaseClassCastToDerivedQuery() { - //autogenerate `Query` type - result = - // `Query` type for `virtualBaseClassCastToDerived` query - TQueryCPP(TConversionsPackageQuery(TVirtualBaseClassCastToDerivedQuery())) - } - - Query noCStyleOrFunctionalCastsQuery() { - //autogenerate `Query` type - result = - // `Query` type for `noCStyleOrFunctionalCasts` query - TQueryCPP(TConversionsPackageQuery(TNoCStyleOrFunctionalCastsQuery())) - } - - Query intToPointerCastProhibitedQuery() { - //autogenerate `Query` type - result = - // `Query` type for `intToPointerCastProhibited` query - TQueryCPP(TConversionsPackageQuery(TIntToPointerCastProhibitedQuery())) - } - - Query noPointerToIntegralCastQuery() { - //autogenerate `Query` type - result = - // `Query` type for `noPointerToIntegralCast` query - TQueryCPP(TConversionsPackageQuery(TNoPointerToIntegralCastQuery())) - } - - Query pointerToIntegralCastQuery() { - //autogenerate `Query` type - result = - // `Query` type for `pointerToIntegralCast` query - TQueryCPP(TConversionsPackageQuery(TPointerToIntegralCastQuery())) - } - - Query noStandaloneTypeCastExpressionQuery() { - //autogenerate `Query` type - result = - // `Query` type for `noStandaloneTypeCastExpression` query - TQueryCPP(TConversionsPackageQuery(TNoStandaloneTypeCastExpressionQuery())) - } } diff --git a/rule_packages/cpp/Conversions.json b/rule_packages/cpp/Conversions.json new file mode 100644 index 000000000..9835110e2 --- /dev/null +++ b/rule_packages/cpp/Conversions.json @@ -0,0 +1,144 @@ +{ + "MISRA-C++-2023": { + "RULE-7-0-1": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Converting a bool type (implicitly or explicitly) to another type can lead to unintended behavior and code obfuscation, particularly when using bitwise operators instead of logical operators.", + "kind": "problem", + "name": "There shall be no conversion from type bool", + "precision": "very-high", + "severity": "error", + "short_name": "NoConversionFromBool", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "There shall be no conversion from type bool" + }, + "RULE-7-0-2": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Implicit and contextual conversions to bool from fundamental types, unscoped enums, or pointers may lead to unintended behavior, except for specific cases like pointer checks and explicit operator bool conversions.", + "kind": "problem", + "name": "There shall be no conversion to type bool", + "precision": "very-high", + "severity": "error", + "short_name": "NoImplicitBoolConversion", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "There shall be no conversion to type bool" + }, + "RULE-7-0-3": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Using the numerical value of a character type may lead to inconsistent behavior due to encoding dependencies and should be avoided in favor of safer C++ Standard Library functions.", + "kind": "problem", + "name": "The numerical value of a character shall not be used", + "precision": "very-high", + "severity": "error", + "short_name": "NoCharacterNumericalValue", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "The numerical value of a character shall not be used" + }, + "RULE-7-0-4": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Bitwise and shift operators should only be applied to operands of appropriate types and values to avoid implementation-defined or undefined behavior.", + "kind": "problem", + "name": "The operands of bitwise operators and shift operators shall be appropriate", + "precision": "very-high", + "severity": "error", + "short_name": "InappropriateBitwiseOrShiftOperands", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "The operands of bitwise operators and shift operators shall be appropriate" + }, + "RULE-7-0-5": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Integral promotion and usual arithmetic conversions that change operand signedness or type category may cause unexpected behavior or undefined behavior when operations overflow.", + "kind": "problem", + "name": "Integral promotion and the usual arithmetic conversions shall not change the signedness or the type", + "precision": "very-high", + "severity": "error", + "short_name": "NoSignednessChangeFromPromotion", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "Integral promotion and the usual arithmetic conversions shall not change the signedness or the type category of an operand" + }, + "RULE-7-0-6": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Assignment between numeric types with different sizes, signedness, or type categories can lead to unexpected information loss, undefined behavior, or silent overload resolution changes.", + "kind": "problem", + "name": "Assignment between numeric types shall be appropriate", + "precision": "high", + "severity": "error", + "short_name": "NumericAssignmentTypeMismatch", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "Assignment between numeric types shall be appropriate" + }, + "RULE-7-11-3": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Converting a function type to a pointer-to-function type outside of static_cast or assignment to a pointer-to-function object creates ambiguous behavior and potential unintended effects.", + "kind": "problem", + "name": "A conversion from function type to pointer-to-function type shall only occur in appropriate contexts", + "precision": "very-high", + "severity": "error", + "short_name": "FunctionPointerConversionContext", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "A conversion from function type to pointer-to-function type shall only occur in appropriate contexts" + } + } +} \ No newline at end of file diff --git a/rules.csv b/rules.csv index 68049625e..3cb64a0ba 100644 --- a/rules.csv +++ b/rules.csv @@ -873,7 +873,7 @@ cpp,MISRA-C++-2023,RULE-6-9-2,Yes,Advisory,Decidable,Single Translation Unit,The cpp,MISRA-C++-2023,RULE-7-0-1,Yes,Required,Decidable,Single Translation Unit,There shall be no conversion from type bool,,Conversions,Easy, cpp,MISRA-C++-2023,RULE-7-0-2,Yes,Required,Decidable,Single Translation Unit,There shall be no conversion to type bool,,Conversions,Easy, cpp,MISRA-C++-2023,RULE-7-0-3,Yes,Required,Decidable,Single Translation Unit,The numerical value of a character shall not be used,M5-0-11,Conversions,Medium, -cpp,MISRA-C++-2023,RULE-7-0-4,Yes,Required,Decidable,Single Translation Unit,The operands of bitwise operators and shift operators shall be appropriate,RULE-10-1,Preconditions,Medium, +cpp,MISRA-C++-2023,RULE-7-0-4,Yes,Required,Decidable,Single Translation Unit,The operands of bitwise operators and shift operators shall be appropriate,RULE-10-1,Conversions,Medium, cpp,MISRA-C++-2023,RULE-7-0-5,Yes,Required,Decidable,Single Translation Unit,Integral promotion and the usual arithmetic conversions shall not change the signedness or the type category of an operand,"M5-0-4,M5-0-9,INT31-C",Conversions,Medium, cpp,MISRA-C++-2023,RULE-7-0-6,Yes,Required,Decidable,Single Translation Unit,Assignment between numeric types shall be appropriate,,Conversions,Hard, cpp,MISRA-C++-2023,RULE-7-11-1,Yes,Required,Decidable,Single Translation Unit,nullptr shall be the only form of the null-pointer-constant,A4-10-1,ImportMisra23,Import, @@ -882,14 +882,14 @@ cpp,MISRA-C++-2023,RULE-7-11-3,Yes,Required,Decidable,Single Translation Unit,A cpp,MISRA-C++-2023,RULE-8-0-1,Yes,Advisory,Decidable,Single Translation Unit,Parentheses should be used to make the meaning of an expression appropriately explicit,M5-0-2,Expressions2,Medium, cpp,MISRA-C++-2023,RULE-8-1-1,Yes,Required,Decidable,Single Translation Unit,A non-transient lambda shall not implicitly capture this,,Expressions2,Easy, cpp,MISRA-C++-2023,RULE-8-1-2,Yes,Advisory,Decidable,Single Translation Unit,Variables should be captured explicitly in a non-transient lambda,A5-1-2,Expressions2,Easy, -cpp,MISRA-C++-2023,RULE-8-2-1,Yes,Required,Decidable,Single Translation Unit,A virtual base class shall only be cast to a derived class by means of dynamic_cast,,Conversions,Easy, -cpp,MISRA-C++-2023,RULE-8-2-2,Yes,Required,Decidable,Single Translation Unit,C-style casts and functional notation casts shall not be used,A5-2-2,Conversions,Easy, +cpp,MISRA-C++-2023,RULE-8-2-1,Yes,Required,Decidable,Single Translation Unit,A virtual base class shall only be cast to a derived class by means of dynamic_cast,,Conversions2,Easy, +cpp,MISRA-C++-2023,RULE-8-2-2,Yes,Required,Decidable,Single Translation Unit,C-style casts and functional notation casts shall not be used,A5-2-2,Conversions2,Easy, cpp,MISRA-C++-2023,RULE-8-2-3,Yes,Required,Decidable,Single Translation Unit,A cast shall not remove any const or volatile qualification from the type accessed via a pointer or by reference,A5-2-3,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-8-2-4,Yes,Required,Decidable,Single Translation Unit,Casts shall not be performed between a pointer to function and any other type,M5-2-6,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-8-2-5,Yes,Required,Decidable,Single Translation Unit,reinterpret_cast shall not be used,A5-2-4,ImportMisra23,Import, -cpp,MISRA-C++-2023,RULE-8-2-6,Yes,Required,Decidable,Single Translation Unit,"An object with integral, enumerated, or pointer to void type shall not be cast to a pointer type","RULE-11-6, INT36-C",Conversions,Easy, -cpp,MISRA-C++-2023,RULE-8-2-7,Yes,Advisory,Decidable,Single Translation Unit,A cast should not convert a pointer type to an integral type,"RULE-11-6, INT36-C",Conversions,Easy, -cpp,MISRA-C++-2023,RULE-8-2-8,Yes,Required,Decidable,Single Translation Unit,An object pointer type shall not be cast to an integral type other than std::uintptr_t or std::intptr_t,"RULE-11-6, INT36-C",Conversions,Easy, +cpp,MISRA-C++-2023,RULE-8-2-6,Yes,Required,Decidable,Single Translation Unit,"An object with integral, enumerated, or pointer to void type shall not be cast to a pointer type","RULE-11-6, INT36-C",Conversions2,Easy, +cpp,MISRA-C++-2023,RULE-8-2-7,Yes,Advisory,Decidable,Single Translation Unit,A cast should not convert a pointer type to an integral type,"RULE-11-6, INT36-C",Conversions2,Easy, +cpp,MISRA-C++-2023,RULE-8-2-8,Yes,Required,Decidable,Single Translation Unit,An object pointer type shall not be cast to an integral type other than std::uintptr_t or std::intptr_t,"RULE-11-6, INT36-C",Conversions2,Easy, cpp,MISRA-C++-2023,RULE-8-2-9,Yes,Required,Decidable,Single Translation Unit,The operand to typeid shall not be an expression of polymorphic class type,,Preconditions,Easy, cpp,MISRA-C++-2023,RULE-8-2-10,Yes,Required,Undecidable,System,"Functions shall not call themselves, either directly or indirectly",A7-5-2,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-8-2-11,Yes,Required,Decidable,Single Translation Unit,An argument passed via ellipsis shall have an appropriate type,,Preconditions,Easy, @@ -903,7 +903,7 @@ cpp,MISRA-C++-2023,RULE-8-18-1,Yes,Mandatory,Undecidable,System,An object or sub cpp,MISRA-C++-2023,RULE-8-18-2,Yes,Advisory,Decidable,Single Translation Unit,The result of an assignment operator should not be used,RULE-13-4,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-8-19-1,Yes,Advisory,Decidable,Single Translation Unit,The comma operator should not be used,M5-18-1,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-8-20-1,Yes,Advisory,Decidable,Single Translation Unit,An unsigned arithmetic operation with constant operands should not wrap,INT30-C,ImportMisra23,Import, -cpp,MISRA-C++-2023,RULE-9-2-1,Yes,Required,Decidable,Single Translation Unit,An explicit type conversion shall not be an expression statement,DCL53-CPP,Conversions,Easy, +cpp,MISRA-C++-2023,RULE-9-2-1,Yes,Required,Decidable,Single Translation Unit,An explicit type conversion shall not be an expression statement,DCL53-CPP,Conversions2,Easy, cpp,MISRA-C++-2023,RULE-9-3-1,Yes,Required,Decidable,Single Translation Unit,The body of an iteration-statement or a selection-statement shall be a compound-statement,RULE-15-6,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-9-4-1,Yes,Required,Decidable,Single Translation Unit,All if ... else if constructs shall be terminated with an else statement,RULE-15-7,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-9-4-2,Yes,Required,Decidable,Single Translation Unit,The structure of a switch statement shall be appropriate,"RULE-16-1, RULE-16-2,RULE-16-3,RULE-16-4,RULE-16-5,RULE-16-6,RULE-16-7",Statements,Medium, From 6845cdc76431de06772c55af5804b9d3b3e67fa8 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Fri, 4 Jul 2025 11:23:10 +0100 Subject: [PATCH 54/57] RULE-7-0-4 - InappropriateBitwiseOrShiftOperands Detects inappropriate operands for bitwise and shift operators that violate type requirements. Identifies signed operands in bitwise operations and improper shift operand types that may cause undefined behavior. [a] --- .../InappropriateBitwiseOrShiftOperands.ql | 124 ++++++++++++++++++ ...appropriateBitwiseOrShiftOperands.expected | 25 ++++ .../InappropriateBitwiseOrShiftOperands.qlref | 1 + cpp/misra/test/rules/RULE-7-0-4/test.cpp | 121 +++++++++++++++++ 4 files changed, 271 insertions(+) create mode 100644 cpp/misra/src/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.ql create mode 100644 cpp/misra/test/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.expected create mode 100644 cpp/misra/test/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.qlref create mode 100644 cpp/misra/test/rules/RULE-7-0-4/test.cpp diff --git a/cpp/misra/src/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.ql b/cpp/misra/src/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.ql new file mode 100644 index 000000000..45f8e5193 --- /dev/null +++ b/cpp/misra/src/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.ql @@ -0,0 +1,124 @@ +/** + * @id cpp/misra/inappropriate-bitwise-or-shift-operands + * @name RULE-7-0-4: The operands of bitwise operators and shift operators shall be appropriate + * @description Bitwise and shift operators should only be applied to operands of appropriate types + * and values to avoid implementation-defined or undefined behavior. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-7-0-4 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.misra.BuiltInTypeRules + +predicate isSignedType(NumericType t) { t.getSignedness() = Signed() } + +predicate isUnsignedType(NumericType t) { t.getSignedness() = Unsigned() } + +predicate isConstantExpression(Expr e) { + e instanceof Literal or + e.isConstant() +} + +predicate isValidShiftConstantRange(Expr right, Type leftType) { + exists(int value | + value = right.getValue().toInt() and + value >= 0 and + value < leftType.getSize() * 8 + ) +} + +predicate isSignedConstantLeftShiftException(LShiftExpr shift) { + exists(Expr left, Expr right, NumericType leftType, int leftVal, int rightVal, int maxBit | + left = shift.getLeftOperand() and + right = shift.getRightOperand() and + leftType = left.getType() and + isConstantExpression(left) and + isConstantExpression(right) and + isSignedType(leftType) and + isValidShiftConstantRange(right, leftType) and + leftVal = left.getValue().toInt() and + rightVal = right.getValue().toInt() and + leftVal >= 0 and + maxBit = leftType.getSize() * 8 - 1 and + // Check that no set bit is shifted into or beyond the sign bit + leftVal * 2.pow(rightVal) < 2.pow(maxBit) + ) +} + +class BinaryShiftOperation extends BinaryOperation { + BinaryShiftOperation() { + this instanceof LShiftExpr or + this instanceof RShiftExpr + } +} + +class AssignShiftOperation extends AssignOperation { + AssignShiftOperation() { + this instanceof AssignLShiftExpr or + this instanceof AssignRShiftExpr + } +} + +from Expr x, string message +where + not isExcluded(x, ConversionsPackage::inappropriateBitwiseOrShiftOperandsQuery()) and + ( + // Binary bitwise operators (excluding shift operations) - both operands must be unsigned + exists(BinaryBitwiseOperation op | + not op instanceof BinaryShiftOperation and + op = x and + not ( + isUnsignedType(op.getLeftOperand().getExplicitlyConverted().getType()) and + isUnsignedType(op.getRightOperand().getExplicitlyConverted().getType()) + ) and + message = + "Binary bitwise operator '" + op.getOperator() + "' requires both operands to be unsigned." + ) + or + // Compound assignment bitwise operators - both operands must be unsigned + exists(AssignBitwiseOperation op | + not op instanceof AssignShiftOperation and + op = x and + not ( + isUnsignedType(op.getLValue().getExplicitlyConverted().getType()) and + isUnsignedType(op.getRValue().getExplicitlyConverted().getType()) + ) and + message = + "Compound assignment bitwise operator '" + op.getOperator() + + "' requires both operands to be unsigned." + ) + or + // Bit complement operator - operand must be unsigned + exists(ComplementExpr comp | + comp = x and + not isUnsignedType(comp.getOperand().getExplicitlyConverted().getType()) and + message = "Bit complement operator '~' requires unsigned operand." + ) + or + // Shift operators - left operand must be unsigned + exists(BinaryShiftOperation shift | + shift = x and + not isUnsignedType(shift.getLeftOperand().getExplicitlyConverted().getType()) and + not isSignedConstantLeftShiftException(shift) and + message = "Shift operator '" + shift.getOperator() + "' requires unsigned left operand." + ) + or + // Shift operators - right operand must be unsigned or constant in valid range + exists(BinaryShiftOperation shift, Expr right | + shift = x and + shift = x and + right = shift.getRightOperand() and + not isUnsignedType(right.getExplicitlyConverted().getType()) and + not isValidShiftConstantRange(right, shift.getLeftOperand().getExplicitlyConverted().getType()) and + message = + "Shift operator '" + shift.getOperator() + + "' requires unsigned right operand or constant in valid range." + ) + ) +select x, message diff --git a/cpp/misra/test/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.expected b/cpp/misra/test/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.expected new file mode 100644 index 000000000..fbc22131a --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.expected @@ -0,0 +1,25 @@ +| test.cpp:14:3:14:13 | ... & ... | Binary bitwise operator '&' requires both operands to be unsigned. | +| test.cpp:15:3:15:13 | ... \| ... | Binary bitwise operator '\|' requires both operands to be unsigned. | +| test.cpp:16:3:16:13 | ... ^ ... | Binary bitwise operator '^' requires both operands to be unsigned. | +| test.cpp:18:3:18:13 | ... & ... | Binary bitwise operator '&' requires both operands to be unsigned. | +| test.cpp:19:3:19:13 | ... \| ... | Binary bitwise operator '\|' requires both operands to be unsigned. | +| test.cpp:30:3:30:15 | ... &= ... | Compound assignment bitwise operator '&=' requires both operands to be unsigned. | +| test.cpp:31:3:31:15 | ... \|= ... | Compound assignment bitwise operator '\|=' requires both operands to be unsigned. | +| test.cpp:32:3:32:15 | ... ^= ... | Compound assignment bitwise operator '^=' requires both operands to be unsigned. | +| test.cpp:42:3:42:6 | ~ ... | Bit complement operator '~' requires unsigned operand. | +| test.cpp:54:3:54:9 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:55:3:55:11 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:63:3:63:18 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:74:3:74:11 | ... << ... | Shift operator '<<' requires unsigned right operand or constant in valid range. | +| test.cpp:75:3:75:11 | ... >> ... | Shift operator '>>' requires unsigned right operand or constant in valid range. | +| test.cpp:85:3:85:11 | ... << ... | Shift operator '<<' requires unsigned right operand or constant in valid range. | +| test.cpp:86:3:86:11 | ... << ... | Shift operator '<<' requires unsigned right operand or constant in valid range. | +| test.cpp:87:3:87:11 | ... >> ... | Shift operator '>>' requires unsigned right operand or constant in valid range. | +| test.cpp:97:3:97:9 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:98:3:98:9 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:99:3:99:9 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:100:3:100:9 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:104:3:104:11 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:106:3:106:17 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:112:3:112:10 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:120:3:120:11 | ... >> ... | Shift operator '>>' requires unsigned left operand. | diff --git a/cpp/misra/test/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.qlref b/cpp/misra/test/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.qlref new file mode 100644 index 000000000..df3c14d75 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.qlref @@ -0,0 +1 @@ +rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-0-4/test.cpp b/cpp/misra/test/rules/RULE-7-0-4/test.cpp new file mode 100644 index 000000000..314c10541 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-4/test.cpp @@ -0,0 +1,121 @@ +#include +#include + +void test_binary_bitwise_operators_unsigned_operands() { + std::uint32_t u32a = 0x12345678U; + std::uint32_t u32b = 0x87654321U; + std::int32_t s32a = 0x12345678; + std::int32_t s32b = 0x87654321; + + u32a & u32b; // COMPLIANT + u32a | u32b; // COMPLIANT + u32a ^ u32b; // COMPLIANT + + s32a & s32b; // NON_COMPLIANT + s32a | s32b; // NON_COMPLIANT + s32a ^ s32b; // NON_COMPLIANT + + s32a & u32b; // NON_COMPLIANT + u32a | s32b; // NON_COMPLIANT +} + +void test_compound_assignment_bitwise_operators() { + std::uint32_t u32 = 0x12345678U; + std::int32_t s32 = 0x12345678; + + u32 &= 0xFFFFU; // COMPLIANT + u32 |= 0x1000U; // COMPLIANT + u32 ^= 0x5555U; // COMPLIANT + + s32 &= 0xFFFF; // NON_COMPLIANT + s32 |= 0x1000; // NON_COMPLIANT + s32 ^= 0x5555; // NON_COMPLIANT +} + +void test_bit_complement_operator() { + std::uint32_t u32 = 0x12345678U; + std::uint8_t u8 = 0x55U; + std::int32_t s32 = 0x12345678; + + ~u32; // COMPLIANT + ~u8; // COMPLIANT + ~s32; // NON_COMPLIANT +} + +void test_shift_operators_left_operand_type() { + std::uint32_t u32 = 0x12345678U; + std::uint8_t u8 = 2U; + std::int32_t s32 = 0x12345678; + + 1U << u8; // COMPLIANT + 1U << 31; // COMPLIANT + u32 << 2U; // COMPLIANT + + 1 << u8; // NON_COMPLIANT + s32 << 2U; // NON_COMPLIANT +} + +void test_shift_operators_with_promotion() { + std::uint8_t u8 = 1U; + std::uint16_t u16 = 2U; + + static_cast(u8 + u16) << 2U; // COMPLIANT + (u8 + u16) << 2U; // NON_COMPLIANT +} + +void test_shift_operators_right_operand_unsigned() { + std::uint32_t u32 = 0x12345678U; + std::uint8_t u8 = 2U; + std::int8_t s8 = 2; + + u32 << u8; // COMPLIANT + u32 >> u8; // COMPLIANT + + u32 << s8; // NON_COMPLIANT + u32 >> s8; // NON_COMPLIANT +} + +void test_shift_operators_right_operand_constant_range() { + std::uint32_t u32 = 0x12345678U; + + u32 << 0; // COMPLIANT + u32 << 31; // COMPLIANT + u32 >> 15; // COMPLIANT + + u32 << 32; // NON_COMPLIANT + u32 << 64; // NON_COMPLIANT + u32 >> 32; // NON_COMPLIANT +} + +void test_exception_signed_constant_left_operand() { + // Exception cases for signed constant expressions + 1 << 30; // COMPLIANT + 2 << 29; // COMPLIANT + 4 << 28; // COMPLIANT + 8 << 27; // COMPLIANT + + 1 << 31; // NON_COMPLIANT + 2 << 30; // NON_COMPLIANT + 4 << 29; // NON_COMPLIANT + 8 << 28; // NON_COMPLIANT + + 1LL << 31; // COMPLIANT - 64 bit type + 1LL << 62; // COMPLIANT - 64 bit type + 1LL << 63; // NON_COMPLIANT - 64 bit type + + 0x40000000 << 1; // NON_COMPLIANT +} + +void test_exception_non_constant_signed_operand() { + std::int32_t s32 = 1; + + s32 << 2; // NON_COMPLIANT +} + +void test_right_shift_signed_operands() { + std::uint32_t u32 = 0x80000000U; + std::int32_t s32 = -1; + + u32 >> 1U; // COMPLIANT + s32 >> 1U; // NON_COMPLIANT +} \ No newline at end of file From db5870420f1611e71e37b5a770c2f706ee9a6ab8 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Fri, 4 Jul 2025 12:13:05 +0100 Subject: [PATCH 55/57] Test large and negative constants, use BigInt --- .../InappropriateBitwiseOrShiftOperands.ql | 12 ++++++----- ...appropriateBitwiseOrShiftOperands.expected | 21 ++++++++++++------- cpp/misra/test/rules/RULE-7-0-4/test.cpp | 21 +++++++++++++++---- 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/cpp/misra/src/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.ql b/cpp/misra/src/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.ql index 45f8e5193..5c24d9fa2 100644 --- a/cpp/misra/src/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.ql +++ b/cpp/misra/src/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.ql @@ -34,7 +34,10 @@ predicate isValidShiftConstantRange(Expr right, Type leftType) { } predicate isSignedConstantLeftShiftException(LShiftExpr shift) { - exists(Expr left, Expr right, NumericType leftType, int leftVal, int rightVal, int maxBit | + exists( + Expr left, Expr right, NumericType leftType, QlBuiltins::BigInt leftVal, int rightVal, + int maxBit + | left = shift.getLeftOperand() and right = shift.getRightOperand() and leftType = left.getType() and @@ -42,12 +45,12 @@ predicate isSignedConstantLeftShiftException(LShiftExpr shift) { isConstantExpression(right) and isSignedType(leftType) and isValidShiftConstantRange(right, leftType) and - leftVal = left.getValue().toInt() and + leftVal = left.getValue().toBigInt() and rightVal = right.getValue().toInt() and - leftVal >= 0 and + leftVal >= 0.toBigInt() and maxBit = leftType.getSize() * 8 - 1 and // Check that no set bit is shifted into or beyond the sign bit - leftVal * 2.pow(rightVal) < 2.pow(maxBit) + leftVal * 2.toBigInt().pow(rightVal) < 2.toBigInt().pow(maxBit) ) } @@ -111,7 +114,6 @@ where or // Shift operators - right operand must be unsigned or constant in valid range exists(BinaryShiftOperation shift, Expr right | - shift = x and shift = x and right = shift.getRightOperand() and not isUnsignedType(right.getExplicitlyConverted().getType()) and diff --git a/cpp/misra/test/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.expected b/cpp/misra/test/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.expected index fbc22131a..74d010b94 100644 --- a/cpp/misra/test/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.expected +++ b/cpp/misra/test/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.expected @@ -15,11 +15,16 @@ | test.cpp:85:3:85:11 | ... << ... | Shift operator '<<' requires unsigned right operand or constant in valid range. | | test.cpp:86:3:86:11 | ... << ... | Shift operator '<<' requires unsigned right operand or constant in valid range. | | test.cpp:87:3:87:11 | ... >> ... | Shift operator '>>' requires unsigned right operand or constant in valid range. | -| test.cpp:97:3:97:9 | ... << ... | Shift operator '<<' requires unsigned left operand. | -| test.cpp:98:3:98:9 | ... << ... | Shift operator '<<' requires unsigned left operand. | -| test.cpp:99:3:99:9 | ... << ... | Shift operator '<<' requires unsigned left operand. | -| test.cpp:100:3:100:9 | ... << ... | Shift operator '<<' requires unsigned left operand. | -| test.cpp:104:3:104:11 | ... << ... | Shift operator '<<' requires unsigned left operand. | -| test.cpp:106:3:106:17 | ... << ... | Shift operator '<<' requires unsigned left operand. | -| test.cpp:112:3:112:10 | ... << ... | Shift operator '<<' requires unsigned left operand. | -| test.cpp:120:3:120:11 | ... >> ... | Shift operator '>>' requires unsigned left operand. | +| test.cpp:93:3:93:11 | ... << ... | Shift operator '<<' requires unsigned right operand or constant in valid range. | +| test.cpp:94:3:94:11 | ... << ... | Shift operator '<<' requires unsigned right operand or constant in valid range. | +| test.cpp:95:3:95:11 | ... >> ... | Shift operator '>>' requires unsigned right operand or constant in valid range. | +| test.cpp:96:3:96:11 | ... >> ... | Shift operator '>>' requires unsigned right operand or constant in valid range. | +| test.cpp:106:3:106:9 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:107:3:107:9 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:108:3:108:9 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:109:3:109:9 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:113:3:113:11 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:117:3:117:30 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:119:3:119:17 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:125:3:125:10 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:133:3:133:11 | ... >> ... | Shift operator '>>' requires unsigned left operand. | diff --git a/cpp/misra/test/rules/RULE-7-0-4/test.cpp b/cpp/misra/test/rules/RULE-7-0-4/test.cpp index 314c10541..b67a7b44e 100644 --- a/cpp/misra/test/rules/RULE-7-0-4/test.cpp +++ b/cpp/misra/test/rules/RULE-7-0-4/test.cpp @@ -87,7 +87,16 @@ void test_shift_operators_right_operand_constant_range() { u32 >> 32; // NON_COMPLIANT } -void test_exception_signed_constant_left_operand() { +void test_shift_operators_negative_right_operand() { + std::uint32_t u32 = 0x12345678U; + + u32 << -1; // NON_COMPLIANT + u32 << -5; // NON_COMPLIANT + u32 >> -1; // NON_COMPLIANT + u32 >> -3; // NON_COMPLIANT +} + +void test_exception_signed_constant_left_operand_exception() { // Exception cases for signed constant expressions 1 << 30; // COMPLIANT 2 << 29; // COMPLIANT @@ -99,9 +108,13 @@ void test_exception_signed_constant_left_operand() { 4 << 29; // NON_COMPLIANT 8 << 28; // NON_COMPLIANT - 1LL << 31; // COMPLIANT - 64 bit type - 1LL << 62; // COMPLIANT - 64 bit type - 1LL << 63; // NON_COMPLIANT - 64 bit type + 1LL << 31; // COMPLIANT - 64 bit type + 1LL << 62; // COMPLIANT - 64 bit type + 1LL << 63; // NON_COMPLIANT - 64 bit type + 0x1000'0000'0000'0000LL << 2; // COMPLIANT + 0x2000'0000'0000'0000LL << 1; // COMPLIANT + + 0x4000'0000'0000'0000LL << 1; // NON_COMPLIANT 0x40000000 << 1; // NON_COMPLIANT } From 3d2616aedad1ccec0873ecda7a6c62bfbb821626 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Mon, 7 Jul 2025 14:10:22 +0100 Subject: [PATCH 56/57] Rule 7.0.4: Add support for shift-assignment operators --- .../InappropriateBitwiseOrShiftOperands.ql | 18 +++++++++ ...appropriateBitwiseOrShiftOperands.expected | 31 +++++++++++----- cpp/misra/test/rules/RULE-7-0-4/test.cpp | 37 +++++++++++++++++++ 3 files changed, 77 insertions(+), 9 deletions(-) diff --git a/cpp/misra/src/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.ql b/cpp/misra/src/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.ql index 5c24d9fa2..b256beaf7 100644 --- a/cpp/misra/src/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.ql +++ b/cpp/misra/src/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.ql @@ -112,6 +112,13 @@ where message = "Shift operator '" + shift.getOperator() + "' requires unsigned left operand." ) or + // Compound assignment shift operators - left operand must be unsigned + exists(AssignShiftOperation shift | + shift = x and + not isUnsignedType(shift.getLValue().getExplicitlyConverted().getType()) and + message = "Shift operator '" + shift.getOperator() + "' requires unsigned left operand." + ) + or // Shift operators - right operand must be unsigned or constant in valid range exists(BinaryShiftOperation shift, Expr right | shift = x and @@ -122,5 +129,16 @@ where "Shift operator '" + shift.getOperator() + "' requires unsigned right operand or constant in valid range." ) + or + // Compound assignment shift operators - right operand must be unsigned or constant in valid range + exists(AssignShiftOperation shift, Expr right | + shift = x and + right = shift.getRValue() and + not isUnsignedType(right.getExplicitlyConverted().getType()) and + not isValidShiftConstantRange(right, shift.getLValue().getExplicitlyConverted().getType()) and + message = + "Shift operator '" + shift.getOperator() + + "' requires unsigned right operand or constant in valid range." + ) ) select x, message diff --git a/cpp/misra/test/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.expected b/cpp/misra/test/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.expected index 74d010b94..0c9144d01 100644 --- a/cpp/misra/test/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.expected +++ b/cpp/misra/test/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.expected @@ -19,12 +19,25 @@ | test.cpp:94:3:94:11 | ... << ... | Shift operator '<<' requires unsigned right operand or constant in valid range. | | test.cpp:95:3:95:11 | ... >> ... | Shift operator '>>' requires unsigned right operand or constant in valid range. | | test.cpp:96:3:96:11 | ... >> ... | Shift operator '>>' requires unsigned right operand or constant in valid range. | -| test.cpp:106:3:106:9 | ... << ... | Shift operator '<<' requires unsigned left operand. | -| test.cpp:107:3:107:9 | ... << ... | Shift operator '<<' requires unsigned left operand. | -| test.cpp:108:3:108:9 | ... << ... | Shift operator '<<' requires unsigned left operand. | -| test.cpp:109:3:109:9 | ... << ... | Shift operator '<<' requires unsigned left operand. | -| test.cpp:113:3:113:11 | ... << ... | Shift operator '<<' requires unsigned left operand. | -| test.cpp:117:3:117:30 | ... << ... | Shift operator '<<' requires unsigned left operand. | -| test.cpp:119:3:119:17 | ... << ... | Shift operator '<<' requires unsigned left operand. | -| test.cpp:125:3:125:10 | ... << ... | Shift operator '<<' requires unsigned left operand. | -| test.cpp:133:3:133:11 | ... >> ... | Shift operator '>>' requires unsigned left operand. | +| test.cpp:115:3:115:12 | ... <<= ... | Shift operator '<<=' requires unsigned left operand. | +| test.cpp:116:3:116:12 | ... >>= ... | Shift operator '>>=' requires unsigned left operand. | +| test.cpp:117:3:117:11 | ... <<= ... | Shift operator '<<=' requires unsigned left operand. | +| test.cpp:118:3:118:11 | ... >>= ... | Shift operator '>>=' requires unsigned left operand. | +| test.cpp:121:3:121:12 | ... <<= ... | Shift operator '<<=' requires unsigned right operand or constant in valid range. | +| test.cpp:122:3:122:12 | ... >>= ... | Shift operator '>>=' requires unsigned right operand or constant in valid range. | +| test.cpp:125:3:125:12 | ... <<= ... | Shift operator '<<=' requires unsigned right operand or constant in valid range. | +| test.cpp:126:3:126:12 | ... <<= ... | Shift operator '<<=' requires unsigned right operand or constant in valid range. | +| test.cpp:127:3:127:12 | ... >>= ... | Shift operator '>>=' requires unsigned right operand or constant in valid range. | +| test.cpp:130:3:130:12 | ... <<= ... | Shift operator '<<=' requires unsigned right operand or constant in valid range. | +| test.cpp:131:3:131:12 | ... <<= ... | Shift operator '<<=' requires unsigned right operand or constant in valid range. | +| test.cpp:132:3:132:12 | ... >>= ... | Shift operator '>>=' requires unsigned right operand or constant in valid range. | +| test.cpp:133:3:133:12 | ... >>= ... | Shift operator '>>=' requires unsigned right operand or constant in valid range. | +| test.cpp:143:3:143:9 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:144:3:144:9 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:145:3:145:9 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:146:3:146:9 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:150:3:150:11 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:154:3:154:30 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:156:3:156:17 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:162:3:162:10 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:170:3:170:11 | ... >> ... | Shift operator '>>' requires unsigned left operand. | diff --git a/cpp/misra/test/rules/RULE-7-0-4/test.cpp b/cpp/misra/test/rules/RULE-7-0-4/test.cpp index b67a7b44e..1735d59e5 100644 --- a/cpp/misra/test/rules/RULE-7-0-4/test.cpp +++ b/cpp/misra/test/rules/RULE-7-0-4/test.cpp @@ -96,6 +96,43 @@ void test_shift_operators_negative_right_operand() { u32 >> -3; // NON_COMPLIANT } +void test_compound_assignment_shift_operators() { + std::uint32_t u32 = 0x12345678U; + std::uint8_t u8 = 2U; + std::int32_t s32 = 0x12345678; + std::int8_t s8 = 2; + + // Unsigned left operand with unsigned right operand + u32 <<= u8; // COMPLIANT + u32 >>= u8; // COMPLIANT + + // Unsigned left operand with constant right operand in valid range + u32 <<= 0; // COMPLIANT + u32 <<= 31; // COMPLIANT + u32 >>= 15; // COMPLIANT + + // Signed left operand + s32 <<= u8; // NON_COMPLIANT + s32 >>= u8; // NON_COMPLIANT + s32 <<= 2; // NON_COMPLIANT + s32 >>= 2; // NON_COMPLIANT + + // Unsigned left operand with signed right operand + u32 <<= s8; // NON_COMPLIANT + u32 >>= s8; // NON_COMPLIANT + + // Right operand out of range + u32 <<= 32; // NON_COMPLIANT + u32 <<= 64; // NON_COMPLIANT + u32 >>= 32; // NON_COMPLIANT + + // Negative right operand + u32 <<= -1; // NON_COMPLIANT + u32 <<= -5; // NON_COMPLIANT + u32 >>= -1; // NON_COMPLIANT + u32 >>= -3; // NON_COMPLIANT +} + void test_exception_signed_constant_left_operand_exception() { // Exception cases for signed constant expressions 1 << 30; // COMPLIANT From 05cfc2b03e32ea368baeda7e4d589e179143d96a Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Mon, 7 Jul 2025 14:16:44 +0100 Subject: [PATCH 57/57] Rule 7.0.5: Add an implementation_scope Preprocessor directives are not currently supported. --- rule_packages/cpp/Conversions.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rule_packages/cpp/Conversions.json b/rule_packages/cpp/Conversions.json index 9835110e2..441b5aa85 100644 --- a/rule_packages/cpp/Conversions.json +++ b/rule_packages/cpp/Conversions.json @@ -95,7 +95,10 @@ "short_name": "NoSignednessChangeFromPromotion", "tags": [ "scope/single-translation-unit" - ] + ], + "implementation_scope": { + "description": "Arithmetic conversions in preprocessor directives are not supported." + } } ], "title": "Integral promotion and the usual arithmetic conversions shall not change the signedness or the type category of an operand"