Skip to content

Commit ba3afac

Browse files
committed
Add enumFitsInType and related test cases
1 parent 1a08d11 commit ba3afac

File tree

2 files changed

+140
-30
lines changed

2 files changed

+140
-30
lines changed

cpp/misra/src/rules/RULE-10-2-3/UnscopedEnumWithoutFixedUnderlyingTypeUsed.ql

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ predicate relationalEqualityOperationUsesUnscopedUnfixedEnum(RelationalEqualityB
8282
*/
8383

8484
leftOperandType = binOp.getLeftOperand().getExplicitlyConverted().getUnderlyingType() and
85-
rightOperandType = binOp.getRightOperand().getExplicitlyConverted().getUnderlyingType() and
85+
rightOperandType = binOp.getRightOperand().getExplicitlyConverted().getUnderlyingType()
86+
|
8687
(
8788
isUnscopedEnumWithoutFixedUnderlyingType(leftOperandType)
8889
or
@@ -105,7 +106,63 @@ predicate compoundAssignmentUsesUnscopedUnfixedEnum(
105106
isUnscopedEnumWithoutFixedUnderlyingType(compoundAssignment.getAnOperand().getUnderlyingType())
106107
}
107108

108-
predicate assignmentSourceIsUnscopedUnfixedEnum(AssignExpr assign) { none() }
109+
predicate assignmentSourceIsUnscopedUnfixedEnum(AssignExpr assign) {
110+
isUnscopedEnumWithoutFixedUnderlyingType(assign.getRValue().getUnderlyingType())
111+
}
112+
113+
/**
114+
* Gets the minimum number of bits required to hold all values of enum `e`.
115+
*/
116+
int enumMinBits(Enum e, boolean signed) {
117+
exists(QlBuiltins::BigInt minVal, QlBuiltins::BigInt maxVal |
118+
minVal = min(EnumConstant c | c.getDeclaringEnum() = e | c.getValue().toBigInt()) and
119+
maxVal = max(EnumConstant c | c.getDeclaringEnum() = e | c.getValue().toBigInt())
120+
|
121+
// 8 bits: signed [-128, 127] or unsigned [0, 255]
122+
if minVal >= "-128".toBigInt() and maxVal <= "127".toBigInt()
123+
then result = 8 and signed = true
124+
else
125+
if minVal >= "0".toBigInt() and maxVal <= "255".toBigInt()
126+
then (
127+
result = 8 and signed = false
128+
) else
129+
// 16 bits: signed [-32768, 32767] or unsigned [0, 65535]
130+
if minVal >= "-32768".toBigInt() and maxVal <= "32767".toBigInt()
131+
then (
132+
result = 16 and signed = true
133+
) else
134+
if minVal >= "0".toBigInt() and maxVal <= "65535".toBigInt()
135+
then (
136+
result = 16 and signed = false
137+
) else
138+
// 32 bits: signed [-2147483648, 2147483647] or unsigned [0, 4294967295]
139+
if minVal >= "-2147483648".toBigInt() and maxVal <= "2147483647".toBigInt()
140+
then (
141+
result = 32 and signed = true
142+
) else
143+
if minVal >= "0".toBigInt() and maxVal <= "4294967295".toBigInt()
144+
then (
145+
result = 32 and signed = false
146+
) else (
147+
// 64 bits: everything else
148+
result = 64 and signed = true
149+
)
150+
)
151+
}
152+
153+
/**
154+
* Holds if the enum `e` can fit in an integral type `type`.
155+
*/
156+
predicate enumFitsInType(Enum e, IntegralType type) {
157+
exists(int minBits, boolean signed | minBits = enumMinBits(e, signed) |
158+
type.getSize() * 8 = minBits and
159+
(
160+
signed = true and type.isSigned()
161+
or
162+
signed = false and type.isUnsigned()
163+
)
164+
)
165+
}
109166

110167
predicate staticCastSourceIsUnscopedUnfixedEnumVariant(StaticCast cast) { none() }
111168

cpp/misra/test/rules/RULE-10-2-3/test.cpp

Lines changed: 81 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,20 @@
22

33
/* ========== 1. Fixtures ========== */
44

5-
// Unscoped, no fixed type
5+
// Unscoped, no fixed type - narrowest possible: char
66
enum Unfixed { U0, U1, U2 };
77
enum Unfixed2 { V0 = 10, V1, V2 };
88

9+
// Unscoped, no fixed type - narrowest possible: std::int16_t (needs > 127
10+
// signed)
11+
enum UnfixedMedium { UM0 = -1, UM1 = 1000 };
12+
13+
// Unscoped, no fixed type - narrowest possible: std::uint32_t
14+
enum UnfixedLargeU { ULU0 = 0x7FFFFFFF, ULU1 };
15+
16+
// Unscoped, no fixed type - narrowest possible: std::int64_t
17+
enum UnfixedLargeS { ULS0 = -1, ULS1 = 0x80000000 };
18+
919
// Unscoped, with fixed type
1020
enum Fixed : std::int32_t { F0 = 20, F1, F2 };
1121
enum Fixed2 : std::int32_t { G0 = 30, G1, G2 };
@@ -16,9 +26,6 @@ enum class Scoped { S0, S1, S2 };
1626
// Scoped, with fixed type
1727
enum class ScopedFixed : std::int32_t { SF0, SF1, SF2 };
1828

19-
// Unscoped, no fixed type, large values
20-
enum UnfixedLarge { UL0 = 0x7FFFFFFF, UL1 };
21-
2229
/* ========== 2. Arithmetic operators ========== */
2330

2431
void arithmetic() {
@@ -121,19 +128,47 @@ void relational() {
121128
/* ========== 8. Assignment ========== */
122129

123130
void assignment() {
124-
Unfixed u = U0;
131+
Unfixed u = U0; // Narrowest: char
132+
UnfixedMedium um = UM0; // Narrowest: std::int16_t
133+
UnfixedLargeU ulu = ULU0; // Narrowest: std::uint32_t
134+
UnfixedLargeS uls = ULS0; // Narrowest: std::int64_t
125135
Fixed f = F0;
126136

127-
std::int32_t i32;
128137
std::int8_t i8;
138+
std::int16_t i16;
139+
std::int32_t i32;
140+
std::uint32_t u32;
129141
std::int64_t i64;
130142

131-
i32 = u; // COMPLIANT: std::int32_t large enough
132-
i64 = u; // COMPLIANT: std::int64_t large enough
133-
i8 = u; // NON_COMPLIANT: std::int8_t might not hold all values
134-
135-
i32 = f; // COMPLIANT: fixed underlying type
143+
// Unfixed { U0, U1, U2 } - narrowest: char
144+
i8 = u; // COMPLIANT: std::int8_t >= char
145+
i16 = u; // COMPLIANT: std::int16_t >= char
146+
i32 = u; // COMPLIANT: std::int32_t >= char
147+
i64 = u; // COMPLIANT: std::int64_t >= char
148+
149+
// UnfixedMedium { UM0 = -1, UM1 = 1000 } - narrowest: std::int16_t
150+
i8 = um; // NON_COMPLIANT: std::int8_t < std::int16_t
151+
i16 = um; // COMPLIANT: std::int16_t >= std::int16_t
152+
i32 = um; // COMPLIANT: std::int32_t >= std::int16_t
153+
i64 = um; // COMPLIANT: std::int64_t >= std::int16_t
154+
155+
// UnfixedLargeU { ULU0 = 0x7FFFFFFF, ULU1 } - narrowest: std::uint32_t
156+
i8 = ulu; // NON_COMPLIANT: std::int8_t < std::uint32_t
157+
i16 = ulu; // NON_COMPLIANT: std::int16_t < std::uint32_t
158+
i32 = ulu; // NON_COMPLIANT: std::int32_t < std::uint32_t (signed vs unsigned)
159+
u32 = ulu; // COMPLIANT: std::uint32_t >= std::uint32_t
160+
i64 = ulu; // COMPLIANT: std::int64_t >= std::uint32_t
161+
162+
// UnfixedLargeS { ULS0 = -1, ULS1 = 0x80000000 } - narrowest: std::int64_t
163+
i8 = uls; // NON_COMPLIANT: std::int8_t < std::int64_t
164+
i16 = uls; // NON_COMPLIANT: std::int16_t < std::int64_t
165+
i32 = uls; // NON_COMPLIANT: std::int32_t < std::int64_t
166+
u32 = uls; // NON_COMPLIANT: std::uint32_t < std::int64_t
167+
i64 = uls; // COMPLIANT: std::int64_t >= std::int64_t
168+
169+
// Fixed underlying type - rule doesn't apply
136170
i8 = f; // COMPLIANT: fixed underlying type
171+
i32 = f; // COMPLIANT: fixed underlying type
137172

138173
Unfixed u2;
139174
u2 = u; // COMPLIANT: same enum type
@@ -179,25 +214,44 @@ void switch_statements() {
179214
/* ========== 10. static_cast FROM unfixed enum ========== */
180215

181216
void static_cast_from_unfixed() {
182-
Unfixed u = U0;
217+
Unfixed u = U0; // Narrowest: char
218+
UnfixedMedium um = UM0; // Narrowest: std::int16_t
219+
UnfixedLargeU ulu = ULU0; // Narrowest: std::uint32_t
220+
UnfixedLargeS uls = ULS0; // Narrowest: std::int64_t
183221

184222
// Target is same enumeration type
185223
static_cast<Unfixed>(u); // COMPLIANT: same enum type
186224

187-
// Target is integer type large enough
188-
static_cast<int>(u); // COMPLIANT: int guaranteed large enough
189-
static_cast<long>(u); // COMPLIANT: long >= int
190-
static_cast<std::int64_t>(u); // COMPLIANT: std::int64_t >= int
191-
static_cast<unsigned int>(u); // COMPLIANT: unsigned int large enough
192-
193-
// Target is integer type that might not be large enough
194-
static_cast<std::int8_t>(
195-
u); // NON_COMPLIANT: std::int8_t might not hold all values
196-
static_cast<std::uint8_t>(
197-
u); // NON_COMPLIANT: std::uint8_t might not hold all values
198-
static_cast<std::int16_t>(
199-
u); // NON_COMPLIANT: std::int16_t might not hold all values
200-
static_cast<char>(u); // NON_COMPLIANT: char might not hold all values
225+
// Unfixed { U0, U1, U2 } - narrowest: char
226+
static_cast<std::int8_t>(u); // COMPLIANT: std::int8_t >= char
227+
static_cast<std::int16_t>(u); // COMPLIANT: std::int16_t >= char
228+
static_cast<std::int32_t>(u); // COMPLIANT: std::int32_t >= char
229+
static_cast<std::int64_t>(u); // COMPLIANT: std::int64_t >= char
230+
static_cast<int>(u); // COMPLIANT: int >= char
231+
static_cast<long>(u); // COMPLIANT: long >= char
232+
static_cast<unsigned int>(u); // COMPLIANT: unsigned int >= char
233+
234+
// UnfixedMedium { UM0 = -1, UM1 = 1000 } - narrowest: std::int16_t
235+
static_cast<std::int8_t>(um); // NON_COMPLIANT: std::int8_t < std::int16_t
236+
static_cast<char>(um); // NON_COMPLIANT: char < std::int16_t
237+
static_cast<std::int16_t>(um); // COMPLIANT: std::int16_t >= std::int16_t
238+
static_cast<std::int32_t>(um); // COMPLIANT: std::int32_t >= std::int16_t
239+
static_cast<std::int64_t>(um); // COMPLIANT: std::int64_t >= std::int16_t
240+
241+
// UnfixedLargeU { ULU0 = 0x7FFFFFFF, ULU1 } - narrowest: std::uint32_t
242+
static_cast<std::int8_t>(ulu); // NON_COMPLIANT: std::int8_t < std::uint32_t
243+
static_cast<std::int16_t>(ulu); // NON_COMPLIANT: std::int16_t < std::uint32_t
244+
static_cast<std::int32_t>(ulu); // NON_COMPLIANT: std::int32_t < std::uint32_t
245+
static_cast<std::uint32_t>(ulu); // COMPLIANT: std::uint32_t >= std::uint32_t
246+
static_cast<std::int64_t>(ulu); // COMPLIANT: std::int64_t >= std::uint32_t
247+
248+
// UnfixedLargeS { ULS0 = -1, ULS1 = 0x80000000 } - narrowest: std::int64_t
249+
static_cast<std::int8_t>(uls); // NON_COMPLIANT: std::int8_t < std::int64_t
250+
static_cast<std::int16_t>(uls); // NON_COMPLIANT: std::int16_t < std::int64_t
251+
static_cast<std::int32_t>(uls); // NON_COMPLIANT: std::int32_t < std::int64_t
252+
static_cast<std::uint32_t>(
253+
uls); // NON_COMPLIANT: std::uint32_t < std::int64_t
254+
static_cast<std::int64_t>(uls); // COMPLIANT: std::int64_t >= std::int64_t
201255

202256
// Target is different enumeration type
203257
static_cast<Unfixed2>(u); // NON_COMPLIANT: different enum type
@@ -287,7 +341,6 @@ void cross_enum_relational() {
287341

288342
// Unfixed vs Scoped - with cast
289343
u == static_cast<int>(s); // NON_COMPLIANT: unfixed enum compared to int
290-
291344
static_cast<int>(u) == static_cast<int>(s); // COMPLIANT: comparing ints
292345

293346
// Fixed vs Scoped - with cast
@@ -411,4 +464,4 @@ class C1 {
411464
}
412465
};
413466

414-
int main() { return 0; }
467+
int main() { return 0; }

0 commit comments

Comments
 (0)