Skip to content

Commit 01b517d

Browse files
committed
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.
1 parent 5843258 commit 01b517d

File tree

2 files changed

+85
-45
lines changed

2 files changed

+85
-45
lines changed

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

Lines changed: 85 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,49 +2,98 @@ import cpp
22
import codingstandards.cpp.misra
33

44
/**
5-
* The signedness of a numeric type.
5+
* A MISRA C++ 2023 type category.
66
*/
7-
newtype Signedness =
8-
Signed() or
9-
Unsigned()
7+
newtype TypeCategory =
8+
Integral() or
9+
FloatingPoint() or
10+
Character() or
11+
Other()
1012

1113
/**
12-
* The type category of a numeric type - either integral or floating-point.
14+
* Gets the type category of a built-in type.
15+
*
16+
* This does not apply the rules related to stripping specifiers or typedefs, or references.
1317
*/
14-
newtype TypeCategory =
15-
Integral() or
16-
FloatingPoint()
18+
TypeCategory getTypeCategory(BuiltInType t) {
19+
(
20+
t instanceof CharType or
21+
t instanceof WideCharType or
22+
t instanceof Char16Type or
23+
t instanceof Char32Type or
24+
t instanceof Char8Type
25+
) and
26+
result = Character()
27+
or
28+
(
29+
// The 5 standard integral types, covering both signed/unsigned variants
30+
// Explicitly list the signed/unsigned `char` to avoid capturing plain `char`, which is of character type category
31+
t instanceof SignedCharType or
32+
t instanceof UnsignedCharType or
33+
t instanceof ShortType or
34+
t instanceof IntType or
35+
t instanceof LongType or
36+
t instanceof LongLongType
37+
) and
38+
result = Integral()
39+
or
40+
(
41+
t instanceof FloatType or
42+
t instanceof DoubleType or
43+
t instanceof LongDoubleType
44+
) and
45+
result = FloatingPoint()
46+
or
47+
(
48+
t instanceof BoolType or
49+
t instanceof VoidType or
50+
t instanceof NullPointerType
51+
) and
52+
result = Other()
53+
}
1754

1855
/**
19-
* A numeric type is a type that represents a number, either an integral or a floating-point.
56+
* The signedness of a MISRA C++ 2023 numeric type
57+
*/
58+
newtype Signedness =
59+
Signed() or
60+
Unsigned()
61+
62+
/**
63+
* A MISRA C++ 2023 numeric type is a type that represents a number, either an integral or a floating-point.
2064
*
2165
* In addition to the basic integral and floating-point types, it includes:
2266
* - Enum types with an explicit underlying type that is a numeric type.
2367
* - Typedef'd types that are numeric types.
2468
* - Numeric types with specifiers (e.g., `const`, `volatile`, `restrict`).
2569
*/
2670
class NumericType extends Type {
71+
// The actual numeric type, which is either an integral or a floating-point type.
2772
Type realType;
2873

2974
NumericType() {
30-
realType = this.getUnspecifiedType().(ReferenceType).getBaseType().(NumericType).getRealType() or
31-
realType = this.getUnspecifiedType().(IntegralType) or
32-
realType = this.getUnspecifiedType().(FloatingPointType) or
33-
realType = this.getUnspecifiedType().(Enum).getExplicitUnderlyingType().getUnspecifiedType()
75+
// A type which is either an integral or a floating-point type category
76+
getTypeCategory(this) = [Integral().(TypeCategory), FloatingPoint()] and
77+
realType = this
78+
or
79+
// Any type which, after stripping specifiers and typedefs, is a numeric type
80+
realType = this.getUnspecifiedType().(NumericType).getRealType()
81+
or
82+
// Any reference type where the base type is a numeric type
83+
realType = this.(ReferenceType).getBaseType().(NumericType).getRealType()
84+
or
85+
// Any Enum type with an explicit underlying type that is a numeric type
86+
realType = this.(Enum).getExplicitUnderlyingType().(NumericType).getRealType()
3487
}
3588

3689
Signedness getSignedness() {
3790
if realType.(IntegralType).isUnsigned() then result = Unsigned() else result = Signed()
3891
}
3992

40-
/** Gets the size of the actual numeric type */
93+
/** Gets the size of the actual numeric type. */
4194
int getRealSize() { result = realType.getSize() }
4295

43-
TypeCategory getTypeCategory() {
44-
realType instanceof IntegralType and result = Integral()
45-
or
46-
realType instanceof FloatingPointType and result = FloatingPoint()
47-
}
96+
TypeCategory getTypeCategory() { result = getTypeCategory(realType) }
4897

4998
float getUpperBound() { result = typeUpperBound(realType) }
5099

@@ -53,6 +102,13 @@ class NumericType extends Type {
53102
Type getRealType() { result = realType }
54103
}
55104

105+
/**
106+
* One of the 10 canonical integer types, which are the standard integer types.
107+
*/
108+
class CanonicalIntegerTypes extends NumericType, IntegralType {
109+
CanonicalIntegerTypes() { this = this.getCanonicalArithmeticType() }
110+
}
111+
56112
predicate isAssignment(Expr source, NumericType targetType, string context) {
57113
// Assignment expression (which excludes compound assignments)
58114
exists(AssignExpr assign |
@@ -119,18 +175,24 @@ predicate isAssignment(Expr source, NumericType targetType, string context) {
119175
*
120176
* The type is determined by the signedness of the bit field and the number of bits.
121177
*/
122-
NumericType getBitFieldType(BitField bf) {
178+
CanonicalIntegerTypes getBitFieldType(BitField bf) {
123179
exists(NumericType bitfieldActualType |
124180
bitfieldActualType = bf.getType() and
125181
// Integral type with the same signedness as the bit field, and big enough to hold the bit field value
126-
result instanceof IntegralType and
127182
result.getSignedness() = bitfieldActualType.getSignedness() and
128183
result.getSize() * 8 >= bf.getNumBits() and
129184
// No smaller integral type can hold the bit field value
130-
not exists(IntegralType other |
185+
not exists(CanonicalIntegerTypes other |
131186
other.getSize() * 8 >= bf.getNumBits() and
132-
other.(NumericType).getSignedness() = result.getSignedness() and
187+
other.getSignedness() = result.getSignedness()
188+
|
133189
other.getSize() < result.getRealSize()
190+
or
191+
// Where multiple types exist with the same size and signedness, prefer shorter names - mainly
192+
// to disambiguate between `unsigned long` and `unsigned long long` on platforms where they
193+
// are the same size
194+
other.getSize() = result.getRealSize() and
195+
other.getName().length() < result.getName().length()
134196
)
135197
)
136198
}

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

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -30,37 +30,15 @@
3030
| test.cpp:168:11:168:13 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'unsigned int'. |
3131
| test.cpp:172:11:172:22 | 4294967296 | Assignment between incompatible numeric types from 'unsigned long' to 'unsigned int'. |
3232
| test.cpp:176:11:176:13 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'unsigned int'. |
33-
| test.cpp:180:11:180:21 | 8589934592 | Assignment between incompatible numeric types from 'long' to 'unsigned long long'. |
3433
| test.cpp:180:11:180:21 | 8589934592 | Assignment between incompatible numeric types from 'long' to 'unsigned long'. |
35-
| test.cpp:193:11:193:12 | 16 | Assignment between incompatible numeric types from 'int32_t' to 'bool'. |
36-
| test.cpp:193:11:193:12 | 16 | Assignment between incompatible numeric types from 'int32_t' to 'char8_t'. |
37-
| test.cpp:193:11:193:12 | 16 | Assignment between incompatible numeric types from 'int32_t' to 'char'. |
3834
| test.cpp:193:11:193:12 | 16 | Assignment between incompatible numeric types from 'int32_t' to 'signed char'. |
39-
| test.cpp:194:11:194:13 | - ... | Assignment between incompatible numeric types from 'int' to 'bool'. |
40-
| test.cpp:194:11:194:13 | - ... | Assignment between incompatible numeric types from 'int' to 'char8_t'. |
41-
| test.cpp:194:11:194:13 | - ... | Assignment between incompatible numeric types from 'int' to 'char'. |
4235
| test.cpp:194:11:194:13 | - ... | Assignment between incompatible numeric types from 'int' to 'signed char'. |
43-
| test.cpp:196:11:196:13 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'bool'. |
44-
| test.cpp:196:11:196:13 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'char8_t'. |
45-
| test.cpp:196:11:196:13 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'char'. |
4636
| test.cpp:196:11:196:13 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'signed char'. |
47-
| test.cpp:200:11:200:14 | 2048 | Assignment between incompatible numeric types from 'int32_t' to 'char16_t'. |
4837
| test.cpp:200:11:200:14 | 2048 | Assignment between incompatible numeric types from 'int32_t' to 'short'. |
49-
| test.cpp:200:11:200:14 | 2048 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. |
50-
| test.cpp:201:11:201:15 | - ... | Assignment between incompatible numeric types from 'int' to 'char16_t'. |
5138
| test.cpp:201:11:201:15 | - ... | Assignment between incompatible numeric types from 'int' to 'short'. |
52-
| test.cpp:201:11:201:15 | - ... | Assignment between incompatible numeric types from 'int' to 'signed short'. |
53-
| test.cpp:204:11:204:13 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'char16_t'. |
5439
| test.cpp:204:11:204:13 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'short'. |
55-
| test.cpp:204:11:204:13 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. |
56-
| test.cpp:208:12:208:22 | 134217728 | Assignment between incompatible numeric types from 'long long' to 'char32_t'. |
5740
| test.cpp:208:12:208:22 | 134217728 | Assignment between incompatible numeric types from 'long long' to 'int'. |
58-
| test.cpp:208:12:208:22 | 134217728 | Assignment between incompatible numeric types from 'long long' to 'signed int'. |
59-
| test.cpp:208:12:208:22 | 134217728 | Assignment between incompatible numeric types from 'long long' to 'wchar_t'. |
60-
| test.cpp:209:12:209:23 | - ... | Assignment between incompatible numeric types from 'long long' to 'char32_t'. |
6141
| test.cpp:209:12:209:23 | - ... | Assignment between incompatible numeric types from 'long long' to 'int'. |
62-
| test.cpp:209:12:209:23 | - ... | Assignment between incompatible numeric types from 'long long' to 'signed int'. |
63-
| test.cpp:209:12:209:23 | - ... | Assignment between incompatible numeric types from 'long long' to 'wchar_t'. |
6442
| test.cpp:224:8:224:9 | l1 | Assignment between incompatible numeric types from 'Colour' to 'uint8_t'. |
6543
| test.cpp:234:6:234:8 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'int64_t'. |
6644
| test.cpp:237:6:237:8 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. |

0 commit comments

Comments
 (0)