Skip to content

Commit 916fb3d

Browse files
committed
Rule 7.0.6: Support pointer to member cases
- Support assignment to pointer to members - Support pointer-to-member function calls
1 parent e87892e commit 916fb3d

File tree

3 files changed

+186
-1
lines changed

3 files changed

+186
-1
lines changed

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,13 @@ predicate isAssignment(Expr source, NumericType targetType, string context) {
129129
isAssignedToBitfield(source, bf) and
130130
targetType = getBitFieldType(bf)
131131
)
132-
else targetType = assign.getLValue().getType()
132+
else
133+
exists(Type t | t = assign.getLValue().getType() |
134+
// Unwrap PointerToMemberType e.g `l1.*l2 = x;`
135+
if t instanceof PointerToMemberType
136+
then targetType = t.(PointerToMemberType).getBaseType()
137+
else targetType = t
138+
)
133139
)
134140
or
135141
// Variable initialization
@@ -157,6 +163,22 @@ predicate isAssignment(Expr source, NumericType targetType, string context) {
157163
// Handle varargs - use the fully converted type of the argument
158164
call.getTarget().getNumberOfParameters() <= i and
159165
targetType = source.getFullyConverted().getType()
166+
or
167+
// A standard expression call
168+
targetType = call.(ExprCall).getExpr().getType().(FunctionPointerIshType).getParameterType(i)
169+
or
170+
// An expression call using the pointer to member operator (.* or ->*)
171+
// This special handling is required because we don't have a CodeQL class representing the call
172+
// to a pointer to member function, but the right hand side is extracted as the -1 child of the
173+
// call
174+
targetType =
175+
call.(ExprCall)
176+
.getChild(-1)
177+
.getType()
178+
.(PointerToMemberType)
179+
.getBaseType()
180+
.(RoutineType)
181+
.getParameterType(i)
160182
)
161183
or
162184
// Return statement

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,41 @@
170170
| test.cpp:854:29:854:31 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned short'. |
171171
| test.cpp:854:34:854:36 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'short'. |
172172
| test.cpp:864:28:864:30 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. |
173+
| test_member_pointers.cpp:25:12:25:14 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. |
174+
| test_member_pointers.cpp:26:12:26:14 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. |
175+
| test_member_pointers.cpp:27:12:27:14 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. |
176+
| test_member_pointers.cpp:28:12:28:14 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. |
177+
| test_member_pointers.cpp:32:13:32:15 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. |
178+
| test_member_pointers.cpp:33:13:33:15 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. |
179+
| test_member_pointers.cpp:34:13:34:15 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. |
180+
| test_member_pointers.cpp:35:13:35:15 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. |
181+
| test_member_pointers.cpp:42:12:42:14 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. |
182+
| test_member_pointers.cpp:43:12:43:14 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. |
183+
| test_member_pointers.cpp:44:12:44:14 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. |
184+
| test_member_pointers.cpp:45:12:45:14 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. |
185+
| test_member_pointers.cpp:49:13:49:15 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. |
186+
| test_member_pointers.cpp:50:13:50:15 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. |
187+
| test_member_pointers.cpp:51:13:51:15 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. |
188+
| test_member_pointers.cpp:52:13:52:15 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. |
189+
| test_member_pointers.cpp:58:11:58:13 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. |
190+
| test_member_pointers.cpp:60:11:60:13 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. |
191+
| test_member_pointers.cpp:66:11:66:13 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. |
192+
| test_member_pointers.cpp:67:11:67:13 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. |
193+
| test_member_pointers.cpp:82:6:82:8 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. |
194+
| test_member_pointers.cpp:83:6:83:8 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. |
195+
| test_member_pointers.cpp:84:6:84:8 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. |
196+
| test_member_pointers.cpp:85:6:85:8 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. |
197+
| test_member_pointers.cpp:90:6:90:8 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. |
198+
| test_member_pointers.cpp:91:6:91:8 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. |
199+
| test_member_pointers.cpp:92:6:92:8 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. |
200+
| test_member_pointers.cpp:93:6:93:8 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. |
201+
| test_member_pointers.cpp:97:7:97:9 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. |
202+
| test_member_pointers.cpp:100:7:100:9 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. |
203+
| test_member_pointers.cpp:106:41:106:43 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. |
204+
| test_member_pointers.cpp:107:41:107:43 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. |
205+
| test_member_pointers.cpp:125:12:125:14 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int64_t'. |
206+
| test_member_pointers.cpp:126:12:126:14 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int64_t'. |
207+
| test_member_pointers.cpp:127:12:127:14 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int64_t'. |
173208
| test_specified.cpp:37:8:37:9 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. |
174209
| test_specified.cpp:38:8:38:9 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'uint8_t'. |
175210
| test_specified.cpp:41:8:41:9 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'int8_t'. |
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#include <cstdint>
2+
3+
std::int16_t s16;
4+
std::uint16_t u16;
5+
std::int32_t s32;
6+
std::uint32_t u32;
7+
std::int64_t s64;
8+
std::uint64_t u64;
9+
10+
struct MemberFunctionPointerTest {
11+
void mf15(std::int32_t l1) {}
12+
void mf15(std::uint32_t l1) {}
13+
void mf16(std::int32_t l1) {}
14+
};
15+
16+
void test_pointer_to_member_functions() {
17+
MemberFunctionPointerTest l1;
18+
MemberFunctionPointerTest *l6 = &l1;
19+
20+
// mf15 is overload independent when used as a member function pointer
21+
void (MemberFunctionPointerTest::*l2)(std::int32_t) =
22+
&MemberFunctionPointerTest::mf15;
23+
(l1.*l2)(s16); // COMPLIANT - widening of id-expression
24+
(l1.*l2)(s32); // COMPLIANT - type match
25+
(l1.*l2)(s64); // NON_COMPLIANT - narrowing
26+
(l1.*l2)(u16); // NON_COMPLIANT - wrong sign
27+
(l1.*l2)(u32); // NON_COMPLIANT - wrong sign
28+
(l1.*l2)(u64); // NON_COMPLIANT - wrong sign and narrowing
29+
30+
(l6->*l2)(s16); // COMPLIANT - is widening of id-expression
31+
(l6->*l2)(s32); // COMPLIANT - type match
32+
(l6->*l2)(s64); // NON_COMPLIANT - narrowing
33+
(l6->*l2)(u16); // NON_COMPLIANT - wrong sign
34+
(l6->*l2)(u32); // NON_COMPLIANT - wrong sign
35+
(l6->*l2)(u64); // NON_COMPLIANT - wrong sign and narrowing
36+
37+
// mf16 is overload independent when used as a member function pointer
38+
void (MemberFunctionPointerTest::*l3)(std::int32_t) =
39+
&MemberFunctionPointerTest::mf16;
40+
(l1.*l3)(s16); // COMPLIANT - widening of id-expression
41+
(l1.*l3)(s32); // COMPLIANT - type match
42+
(l1.*l3)(s64); // NON_COMPLIANT - narrowing
43+
(l1.*l3)(u16); // NON_COMPLIANT - wrong sign
44+
(l1.*l3)(u32); // NON_COMPLIANT - wrong sign
45+
(l1.*l3)(u64); // NON_COMPLIANT - wrong sign and narrowing
46+
47+
(l6->*l3)(s16); // COMPLIANT - widening of id-expression
48+
(l6->*l3)(s32); // COMPLIANT - type match
49+
(l6->*l3)(s64); // NON_COMPLIANT - narrowing
50+
(l6->*l3)(u16); // NON_COMPLIANT - wrong sign
51+
(l6->*l3)(u32); // NON_COMPLIANT - wrong sign
52+
(l6->*l3)(u64); // NON_COMPLIANT - wrong sign and narrowing
53+
54+
// Direct calls for comparison
55+
56+
// mf15 is not overload-independent, so it should only be compliant
57+
// where an exact type of an overload is used
58+
l1.mf15(s16); // NON_COMPLIANT - widening not allowed
59+
l1.mf15(s32); // COMPLIANT - exact type match
60+
l1.mf15(u16); // NON_COMPLIANT - widening not allowed
61+
l1.mf15(u32); // COMPLIANT - exact type match
62+
63+
// A qualified call to mf16 is overload-independent
64+
l1.mf16(s16); // COMPLIANT - widening of id-expression
65+
l1.mf16(s32); // COMPLIANT - exact type match
66+
l1.mf16(u16); // NON_COMPLIANT
67+
l1.mf16(u32); // NON_COMPLIANT
68+
}
69+
70+
// Test static member function pointers - should be overload-independent
71+
struct StaticMemberFunctionPointerTest {
72+
static void mf19(std::int32_t l1) {}
73+
static void mf19(std::uint32_t l1) {}
74+
static void mf20(std::int32_t l1) {}
75+
};
76+
77+
void test_static_member_function_pointers() {
78+
// Static member function pointers - overload-independent
79+
void (*l1)(std::int32_t) = &StaticMemberFunctionPointerTest::mf19;
80+
l1(s16); // COMPLIANT - widening of id-expression
81+
l1(s32); // COMPLIANT - type match
82+
l1(s64); // NON_COMPLIANT - narrowing
83+
l1(u16); // NON_COMPLIANT - wrong sign
84+
l1(u32); // NON_COMPLIANT - wrong sign
85+
l1(u64); // NON_COMPLIANT - wrong sign and narrowing
86+
87+
void (*l2)(std::int32_t) = &StaticMemberFunctionPointerTest::mf20;
88+
l2(s16); // COMPLIANT - widening of id-expression
89+
l2(s32); // COMPLIANT - type match
90+
l2(s64); // NON_COMPLIANT - narrowing
91+
l2(u16); // NON_COMPLIANT - wrong sign
92+
l2(u32); // NON_COMPLIANT - wrong sign
93+
l2(u64); // NON_COMPLIANT - wrong sign and narrowing
94+
95+
// Direct calls for comparison - not overload-independent
96+
StaticMemberFunctionPointerTest::mf19(
97+
s16); // NON_COMPLIANT - widening not allowed
98+
StaticMemberFunctionPointerTest::mf19(s32); // COMPLIANT - exact type match
99+
StaticMemberFunctionPointerTest::mf19(
100+
u16); // NON_COMPLIANT - widening not allowed
101+
StaticMemberFunctionPointerTest::mf19(u32); // COMPLIANT - exact type match
102+
103+
StaticMemberFunctionPointerTest::mf20(
104+
s16); // COMPLIANT - widening of id-expression
105+
StaticMemberFunctionPointerTest::mf20(s32); // COMPLIANT - exact type match
106+
StaticMemberFunctionPointerTest::mf20(u16); // NON_COMPLIANT
107+
StaticMemberFunctionPointerTest::mf20(u32); // NON_COMPLIANT
108+
}
109+
110+
// Test member data pointers - not function calls, but test assignment to them
111+
struct MemberDataPointerTest {
112+
std::int64_t m1;
113+
std::int64_t m2 : 10;
114+
};
115+
116+
void test_member_data_pointers() {
117+
MemberDataPointerTest l1;
118+
119+
// Member data pointer assignments - follow normal assignment rules
120+
std::int64_t MemberDataPointerTest::*l2 = &MemberDataPointerTest::m1;
121+
122+
l1.*l2 = s16; // COMPLIANT - widening conversion allowed
123+
l1.*l2 = s32; // COMPLIANT - widening conversion allowed
124+
l1.*l2 = s64; // COMPLIANT
125+
l1.*l2 = u16; // NON_COMPLIANT - signedness violation
126+
l1.*l2 = u32; // NON_COMPLIANT - different signedness/size
127+
l1.*l2 = u64; // NON_COMPLIANT - different signedness
128+
}

0 commit comments

Comments
 (0)