Skip to content

Commit 9aed463

Browse files
committed
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.
1 parent a7ce6f5 commit 9aed463

File tree

2 files changed

+103
-0
lines changed

2 files changed

+103
-0
lines changed

cpp/misra/src/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.ql

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,97 @@ class IntegerPromotion extends RelevantRealConversion {
9494
override string getKindOfConversion() { result = "Integer promotion" }
9595
}
9696

97+
class ImpliedUsualArithmeticConversion extends RelevantConversion {
98+
NumericType fromType;
99+
NumericType toType;
100+
101+
ImpliedUsualArithmeticConversion() {
102+
// The lvalue of an assignment operation does not have a `Conversion` in our model, but
103+
// it is still subject to usual arithmetic conversions (excepting shifts).
104+
//
105+
// rvalues are handled separately in the `UsualArithmeticConversion` class.
106+
exists(AssignOperation aop |
107+
not aop instanceof AssignLShiftExpr and
108+
not aop instanceof AssignRShiftExpr and
109+
// lvalue subject to usual arithmetic conversions
110+
aop.getLValue() = this and
111+
// From type is the type of the lvalue, which should be a numeric type under the MISRA rule
112+
fromType = this.getType() and
113+
// Under usual arithmetic conversions, the converted types of both arguments will be the same,
114+
// so even though we don't have an explicit conversion, we can still deduce that the target
115+
// type will be the same as the converted type of the rvalue.
116+
toType = aop.getRValue().getFullyConverted().getType() and
117+
// Only consider cases where the conversion is not a no-op, for consistency with the `Conversion` class
118+
not fromType.getRealType() = toType.getRealType()
119+
)
120+
}
121+
122+
override Type getFromType() { result = fromType }
123+
124+
override Type getToType() { result = toType }
125+
126+
override Expr getConvertedExpr() { result = this }
127+
128+
override string getKindOfConversion() { result = "Usual arithmetic conversion" }
129+
}
130+
131+
class ImpliedIntegerPromotion extends RelevantConversion {
132+
NumericType fromType;
133+
134+
ImpliedIntegerPromotion() {
135+
(
136+
exists(AssignLShiftExpr aop | aop.getLValue() = this) or
137+
exists(AssignRShiftExpr aop | aop.getLValue() = this)
138+
) and
139+
// Only consider integer promotions from MISRA C++ "numeric types" as per the rule
140+
fromType = this.getType() and
141+
fromType.getTypeCategory() = Integral() and
142+
// If the size is less than int, then it is an implied integer promotion
143+
fromType.getRealSize() < sizeOfInt()
144+
}
145+
146+
override Type getFromType() { result = fromType }
147+
148+
override IntegralType getToType() {
149+
// Only report the canonical type - e.g. `int` not `signed int`
150+
result = result.getCanonicalArithmeticType() and
151+
if result instanceof Char16Type or result instanceof Char32Type or result instanceof Wchar_t
152+
then
153+
// Smallest type that can hold the value of the `fromType`
154+
result =
155+
min(NumericType candidateType |
156+
(
157+
candidateType instanceof IntType or
158+
candidateType instanceof LongType or
159+
candidateType instanceof LongLongType
160+
) and
161+
fromType.getIntegralUpperBound() <= candidateType.getIntegralUpperBound()
162+
|
163+
candidateType order by candidateType.getIntegralUpperBound()
164+
)
165+
else (
166+
// The result is always `int` or `unsigned int`
167+
result instanceof IntType and
168+
if
169+
// If the `fromType` is signed, the result must be signed
170+
fromType.getSignedness() = Signed()
171+
or
172+
// If the `fromType` is unsigned, but the result can fit into the signed int type, then the
173+
// result must be signed as well.
174+
fromType.getIntegralUpperBound() <=
175+
any(IntType t | t.isSigned()).(NumericType).getIntegralUpperBound()
176+
then result.isSigned()
177+
else
178+
// Otherwise an unsigned type is returned
179+
result.isUnsigned()
180+
)
181+
}
182+
183+
override Expr getConvertedExpr() { result = this }
184+
185+
override string getKindOfConversion() { result = "Integer promotion" }
186+
}
187+
97188
from Expr e, RelevantConversion c, NumericType fromType, NumericType toType, string changeType
98189
where
99190
not isExcluded(e, ConversionsPackage::noSignednessChangeFromPromotionQuery()) and

cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,32 @@
1414
| test.cpp:28:8:28:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. |
1515
| test.cpp:29:3:29:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. |
1616
| test.cpp:29:8:29:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. |
17+
| test.cpp:43:3:43:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. |
1718
| test.cpp:43:9:43:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. |
19+
| test.cpp:44:3:44:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. |
1820
| test.cpp:44:9:44:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. |
21+
| test.cpp:45:3:45:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. |
1922
| test.cpp:45:9:45:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. |
23+
| test.cpp:46:3:46:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. |
2024
| test.cpp:46:9:46:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. |
25+
| test.cpp:47:3:47:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. |
2126
| test.cpp:47:9:47:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. |
27+
| test.cpp:48:3:48:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. |
2228
| test.cpp:48:9:48:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. |
29+
| test.cpp:49:3:49:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. |
2330
| test.cpp:49:9:49:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. |
31+
| test.cpp:50:3:50:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. |
2432
| test.cpp:50:9:50:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. |
2533
| test.cpp:62:3:62:4 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. |
2634
| test.cpp:63:3:63:4 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. |
2735
| test.cpp:64:3:64:4 | l2 | Integer promotion from 'uint16_t' to 'int' changes signedness. |
2836
| test.cpp:65:3:65:4 | l2 | Integer promotion from 'uint16_t' to 'int' changes signedness. |
2937
| test.cpp:68:9:68:10 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. |
3038
| test.cpp:69:9:69:10 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. |
39+
| test.cpp:73:3:73:4 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. |
40+
| test.cpp:74:3:74:4 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. |
41+
| test.cpp:75:3:75:4 | l2 | Integer promotion from 'uint16_t' to 'int' changes signedness. |
42+
| test.cpp:76:3:76:4 | l2 | Integer promotion from 'uint16_t' to 'int' changes signedness. |
3143
| test.cpp:79:10:79:11 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. |
3244
| test.cpp:80:10:80:11 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. |
3345
| test.cpp:91:3:91:4 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. |

0 commit comments

Comments
 (0)