Skip to content

Commit 3fdaa98

Browse files
committed
Rule 7.0.6: Handle explicit conversions
Consider the conversion as the source, not the pre-conversion value.
1 parent f68658a commit 3fdaa98

File tree

4 files changed

+70
-8
lines changed

4 files changed

+70
-8
lines changed

cpp/misra/src/codingstandards/cpp/misra/StandardConversions.qll

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,13 @@ class CanonicalIntegerTypes extends NumericType, IntegralType {
117117
}
118118

119119
predicate isAssignment(Expr source, NumericType targetType, string context) {
120+
exists(Expr preConversionAssignment |
121+
isPreConversionAssignment(preConversionAssignment, targetType, context) and
122+
preConversionAssignment.getExplicitlyConverted() = source
123+
)
124+
}
125+
126+
predicate isPreConversionAssignment(Expr source, NumericType targetType, string context) {
120127
// Assignment expression (which excludes compound assignments)
121128
exists(AssignExpr assign |
122129
assign.getRValue() = source and
@@ -261,4 +268,6 @@ CanonicalIntegerTypes getBitFieldType(BitField bf) {
261268
/**
262269
* Holds if the `source` expression is assigned to a bit field.
263270
*/
264-
predicate isAssignedToBitfield(Expr source, BitField bf) { source = bf.getAnAssignedValue() }
271+
predicate isAssignedToBitfield(Expr source, BitField bf) {
272+
source = bf.getAnAssignedValue().getExplicitlyConverted()
273+
}

cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ predicate isValidWidening(Expr source, NumericType sourceType, NumericType targe
101101
// Same type category and signedness, source size smaller, source is id-expression or has constructor exception
102102
(
103103
source instanceof IdExpression or
104-
hasConstructorException(any(Call call | call.getAnArgument() = source))
104+
hasConstructorException(any(Call call | call.getAnArgument().getExplicitlyConverted() = source))
105105
) and
106106
sourceType.getTypeCategory() = targetType.getTypeCategory() and
107107
sourceType.getSignedness() = targetType.getSignedness() and
@@ -163,7 +163,7 @@ predicate isOverloadIndependent(Call call, Expr arg) {
163163
*/
164164
predicate shouldHaveSameType(Expr source) {
165165
exists(Call call |
166-
call.getAnArgument() = source and
166+
call.getAnArgument().getExplicitlyConverted() = source and
167167
isAssignment(source, _, _) and
168168
not hasConstructorException(call)
169169
|
@@ -172,7 +172,7 @@ predicate shouldHaveSameType(Expr source) {
172172
// Passed as a varargs parameter
173173
exists(int i |
174174
call.getTarget().isVarargs() and
175-
call.getArgument(i) = source and
175+
call.getArgument(i).getExplicitlyConverted() = source and
176176
// Argument is greater than the number of parameters
177177
call.getTarget().getNumberOfParameters() <= i
178178
)

cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
| test.cpp:97:13:97:14 | m1 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. |
1212
| test.cpp:98:13:98:14 | m2 | Assignment between incompatible numeric types from 'uint16_t' to 'uint64_t'. |
1313
| test.cpp:103:9:103:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint32_t'. |
14-
| test.cpp:104:10:104:11 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. |
15-
| test.cpp:105:35:105:36 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. |
14+
| test.cpp:104:9:104:12 | (...) | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. |
15+
| test.cpp:105:9:105:37 | static_cast<uint8_t>... | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. |
1616
| test.cpp:110:8:110:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. |
1717
| test.cpp:111:21:111:27 | ... + ... | Assignment between incompatible numeric types from 'int' to 'int16_t'. |
1818
| test.cpp:134:11:134:11 | 4 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. |
@@ -84,6 +84,7 @@
8484
| test.cpp:553:12:553:13 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'int32_t'. |
8585
| test.cpp:554:12:554:13 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'float'. |
8686
| test.cpp:555:12:555:13 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'double'. |
87+
| test.cpp:585:9:585:37 | static_cast<int16_t>... | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. |
8788
| test_aggregate.cpp:29:22:29:24 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. |
8889
| test_aggregate.cpp:31:26:31:28 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'uint16_t'. |
8990
| test_aggregate.cpp:33:31:33:33 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. |
@@ -315,7 +316,7 @@
315316
| test_specified.cpp:342:8:342:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. |
316317
| test_specified.cpp:343:8:343:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. |
317318
| test_specified.cpp:344:8:344:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. |
318-
| test_specified.cpp:351:10:351:11 | l2 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. |
319-
| test_specified.cpp:352:10:352:11 | l3 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. |
319+
| test_specified.cpp:351:9:351:12 | (...) | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. |
320+
| test_specified.cpp:352:9:352:12 | (...) | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. |
320321
| test_specified.cpp:370:18:370:20 | 300 | Assignment between incompatible numeric types from 'int' to 'const uint8_t'. |
321322
| test_specified.cpp:370:23:370:27 | 70000 | Assignment between incompatible numeric types from 'int' to 'volatile uint16_t'. |

cpp/misra/test/rules/RULE-7-0-6/test.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,4 +566,56 @@ void test_constructor_field_initializers() {
566566
ConstructorTest l6; // Test third constructor
567567
ConstructorTest l7(0); // Test fourth constructor
568568
ConstructorTest l8(l1, l1, l3); // Test fifth constructor
569+
}
570+
571+
// Test explicit casts
572+
void test_explicit_casts() {
573+
std::uint8_t l1 = 42;
574+
std::uint16_t l2 = 1000;
575+
std::int8_t l3 = -10;
576+
std::int32_t l4 = -100;
577+
float l5 = 3.14f;
578+
double l6 = 2.718;
579+
580+
// Explicit cast expressions are treated as expressions, not id-expressions
581+
u8 = static_cast<std::uint8_t>(l2); // COMPLIANT
582+
u16 = static_cast<std::uint16_t>(l1); // COMPLIANT
583+
s8 = static_cast<std::int8_t>(l4); // COMPLIANT
584+
s32 = static_cast<std::int32_t>(l3); // COMPLIANT
585+
s32 = static_cast<std::int16_t>(l3); // NON_COMPLIANT
586+
587+
// Type category conversions with explicit casts
588+
f = static_cast<float>(l4); // COMPLIANT
589+
s32 = static_cast<std::int32_t>(l5); // COMPLIANT
590+
591+
// Size conversions with explicit casts
592+
d = static_cast<double>(l5); // COMPLIANT
593+
l5 = static_cast<float>(l6); // COMPLIANT
594+
595+
// C-style casts (also expressions)
596+
u8 = (std::uint8_t)l2; // COMPLIANT
597+
s8 = (std::int8_t)l4; // COMPLIANT
598+
f = (float)l4; // COMPLIANT
599+
600+
// Functional style casts (also expressions)
601+
u8 = std::uint8_t(l2); // COMPLIANT
602+
s8 = std::int8_t(l4); // COMPLIANT
603+
f = float(l4); // COMPLIANT
604+
605+
// Const_cast (creates expressions)
606+
const std::uint8_t l7 = 100;
607+
u8 = const_cast<std::uint8_t &>(l7); // COMPLIANT
608+
609+
// Reinterpret_cast (creates expressions)
610+
u32 = reinterpret_cast<std::uint32_t &>(l4); // COMPLIANT
611+
612+
// Assignment to variables through explicit casts
613+
std::uint32_t l8;
614+
std::uint16_t l9;
615+
l8 = static_cast<std::uint32_t>(l9); // COMPLIANT
616+
l9 = static_cast<std::uint16_t>(l8); // COMPLIANT
617+
618+
// Function calls with explicit casts
619+
f1(static_cast<std::int64_t>(l4)); // COMPLIANT
620+
f2(static_cast<std::int32_t>(l4)); // COMPLIANT
569621
}

0 commit comments

Comments
 (0)