Skip to content

Commit af2ff95

Browse files
committed
MISRA C++ 2023: Create StandardConversions library
Migrate conversion generic code to a shared library.
1 parent b310e14 commit af2ff95

File tree

2 files changed

+126
-123
lines changed

2 files changed

+126
-123
lines changed
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import cpp
2+
import codingstandards.cpp.misra
3+
4+
/**
5+
* The signedness of a numeric type.
6+
*/
7+
newtype Signedness =
8+
Signed() or
9+
Unsigned()
10+
11+
/**
12+
* The type category of a numeric type - either integral or floating-point.
13+
*/
14+
newtype TypeCategory =
15+
Integral() or
16+
FloatingPoint()
17+
18+
/**
19+
* A numeric type is a type that represents a number, either an integral or a floating-point.
20+
*
21+
* In addition to the basic integral and floating-point types, it includes:
22+
* - Enum types with an explicit underlying type that is a numeric type.
23+
* - Typedef'd types that are numeric types.
24+
* - Numeric types with specifiers (e.g., `const`, `volatile`, `restrict`).
25+
*/
26+
class NumericType extends Type {
27+
Type realType;
28+
29+
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()
34+
}
35+
36+
Signedness getSignedness() {
37+
if realType.(IntegralType).isUnsigned() then result = Unsigned() else result = Signed()
38+
}
39+
40+
/** Gets the size of the actual numeric type */
41+
int getRealSize() { result = realType.getSize() }
42+
43+
TypeCategory getTypeCategory() {
44+
realType instanceof IntegralType and result = Integral()
45+
or
46+
realType instanceof FloatingPointType and result = FloatingPoint()
47+
}
48+
49+
float getUpperBound() { result = typeUpperBound(realType) }
50+
51+
float getLowerBound() { result = typeLowerBound(realType) }
52+
53+
Type getRealType() { result = realType }
54+
}
55+
56+
predicate isAssignment(Expr source, NumericType targetType, string context) {
57+
// Assignment operator (but not compound assignment)
58+
exists(AssignExpr assign |
59+
assign.getRValue() = source and
60+
context = "assignment"
61+
|
62+
// TODO generalize to variable init (do we need this for bitfields?) and extract
63+
if isAssignedToBitfield(source, _)
64+
then
65+
exists(BitField bf |
66+
isAssignedToBitfield(source, bf) and
67+
// TODO integral after numeric?
68+
targetType.(IntegralType).(NumericType).getSignedness() =
69+
bf.getType().(NumericType).getSignedness() and
70+
// smallest integral type that can hold the bit field value
71+
targetType.getRealSize() * 8 >= bf.getNumBits() and
72+
not exists(IntegralType other |
73+
other.getSize() * 8 >= bf.getNumBits() and
74+
other.(NumericType).getSignedness() = targetType.getSignedness() and
75+
other.getSize() < targetType.getRealSize()
76+
)
77+
)
78+
else targetType = assign.getLValue().getType()
79+
)
80+
or
81+
// Variable initialization
82+
exists(Variable v, Initializer init |
83+
init.getExpr() = source and
84+
v.getInitializer() = init and
85+
targetType = v.getType() and
86+
context = "initialization"
87+
)
88+
or
89+
// Passing a function parameter by value
90+
exists(Call call, int i |
91+
call.getArgument(i) = source and
92+
not targetType.stripTopLevelSpecifiers() instanceof ReferenceType and
93+
context = "function argument"
94+
|
95+
targetType = call.getTarget().getParameter(i).getType()
96+
or
97+
// Handle varargs - use the fully converted type of the argument
98+
call.getTarget().getNumberOfParameters() <= i and
99+
targetType = source.getFullyConverted().getType()
100+
)
101+
or
102+
// Return statement
103+
exists(ReturnStmt ret, Function f |
104+
ret.getExpr() = source and
105+
ret.getEnclosingFunction() = f and
106+
targetType = f.getType() and
107+
not targetType.stripTopLevelSpecifiers() instanceof ReferenceType and
108+
context = "return"
109+
)
110+
or
111+
// Switch case
112+
exists(SwitchCase case, SwitchStmt switch |
113+
case.getExpr() = source and
114+
case.getSwitchStmt() = switch and
115+
targetType = switch.getExpr().getFullyConverted().getType() and
116+
context = "switch case"
117+
)
118+
}
119+
120+
predicate isAssignedToBitfield(Expr source, BitField bf) {
121+
exists(Assignment assign |
122+
assign.getRValue() = source and
123+
assign.getLValue() = bf.getAnAccess()
124+
)
125+
}

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

Lines changed: 1 addition & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -15,129 +15,7 @@
1515

1616
import cpp
1717
import codingstandards.cpp.misra
18-
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
19-
20-
/**
21-
* The signedness of a numeric type.
22-
*/
23-
newtype Signedness =
24-
Signed() or
25-
Unsigned()
26-
27-
/**
28-
* The type category of a numeric type - either integral or floating-point.
29-
*/
30-
newtype TypeCategory =
31-
Integral() or
32-
FloatingPoint()
33-
34-
/**
35-
* A numeric type is a type that represents a number, either an integral or a floating-point.
36-
*
37-
* In addition to the basic integral and floating-point types, it includes:
38-
* - Enum types with an explicit underlying type that is a numeric type.
39-
* - Typedef'd types that are numeric types.
40-
* - Numeric types with specifiers (e.g., `const`, `volatile`, `restrict`).
41-
*/
42-
class NumericType extends Type {
43-
Type realType;
44-
45-
NumericType() {
46-
realType = this.getUnspecifiedType().(ReferenceType).getBaseType().(NumericType).getRealType() or
47-
realType = this.getUnspecifiedType().(IntegralType) or
48-
realType = this.getUnspecifiedType().(FloatingPointType) or
49-
realType = this.getUnspecifiedType().(Enum).getExplicitUnderlyingType().getUnspecifiedType()
50-
}
51-
52-
Signedness getSignedness() {
53-
if realType.(IntegralType).isUnsigned() then result = Unsigned() else result = Signed()
54-
}
55-
56-
int getRealSize() { result = realType.getSize() }
57-
58-
TypeCategory getTypeCategory() {
59-
realType instanceof IntegralType and result = Integral()
60-
or
61-
realType instanceof FloatingPointType and result = FloatingPoint()
62-
}
63-
64-
float getUpperBound() { result = typeUpperBound(realType) }
65-
66-
float getLowerBound() { result = typeLowerBound(realType) }
67-
68-
Type getRealType() { result = realType }
69-
}
70-
71-
predicate isAssignment(Expr source, NumericType targetType, string context) {
72-
// Assignment operator (but not compound assignment)
73-
exists(AssignExpr assign |
74-
assign.getRValue() = source and
75-
context = "assignment"
76-
|
77-
// TODO generalize to variable init (do we need this for bitfields?) and extract
78-
if isAssignedToBitfield(source, _)
79-
then
80-
exists(BitField bf |
81-
isAssignedToBitfield(source, bf) and
82-
// TODO integral after numeric?
83-
targetType.(IntegralType).(NumericType).getSignedness() =
84-
bf.getType().(NumericType).getSignedness() and
85-
// smallest integral type that can hold the bit field value
86-
targetType.getRealSize() * 8 >= bf.getNumBits() and
87-
not exists(IntegralType other |
88-
other.getSize() * 8 >= bf.getNumBits() and
89-
other.(NumericType).getSignedness() = targetType.getSignedness() and
90-
other.getSize() < targetType.getRealSize()
91-
)
92-
)
93-
else targetType = assign.getLValue().getType()
94-
)
95-
or
96-
// Variable initialization
97-
exists(Variable v, Initializer init |
98-
init.getExpr() = source and
99-
v.getInitializer() = init and
100-
targetType = v.getType() and
101-
context = "initialization"
102-
)
103-
or
104-
// Passing a function parameter by value
105-
exists(Call call, int i |
106-
call.getArgument(i) = source and
107-
not targetType.stripTopLevelSpecifiers() instanceof ReferenceType and
108-
context = "function argument"
109-
|
110-
targetType = call.getTarget().getParameter(i).getType()
111-
or
112-
// Handle varargs - use the fully converted type of the argument
113-
call.getTarget().getNumberOfParameters() <= i and
114-
targetType = source.getFullyConverted().getType()
115-
)
116-
or
117-
// Return statement
118-
exists(ReturnStmt ret, Function f |
119-
ret.getExpr() = source and
120-
ret.getEnclosingFunction() = f and
121-
targetType = f.getType() and
122-
not targetType.stripTopLevelSpecifiers() instanceof ReferenceType and
123-
context = "return"
124-
)
125-
or
126-
// Switch case
127-
exists(SwitchCase case, SwitchStmt switch |
128-
case.getExpr() = source and
129-
case.getSwitchStmt() = switch and
130-
targetType = switch.getExpr().getFullyConverted().getType() and
131-
context = "switch case"
132-
)
133-
}
134-
135-
predicate isAssignedToBitfield(Expr source, BitField bf) {
136-
exists(Assignment assign |
137-
assign.getRValue() = source and
138-
assign.getLValue() = bf.getAnAccess()
139-
)
140-
}
18+
import codingstandards.cpp.misra.StandardConversions
14119

14220
predicate isValidConstantAssignment(Expr source, NumericType targetType) {
14321
isAssignment(source, targetType, _) and

0 commit comments

Comments
 (0)