Skip to content

Commit 19fd572

Browse files
Implement Preconditions2, appropriate types sent via ellipsis.
1 parent b03547b commit 19fd572

7 files changed

Lines changed: 276 additions & 0 deletions

File tree

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/
2+
import cpp
3+
import RuleMetadata
4+
import codingstandards.cpp.exclusions.RuleMetadata
5+
6+
newtype Preconditions2Query = TInappropriateArgumentTypePassedViaEllipsisQuery()
7+
8+
predicate isPreconditions2QueryMetadata(Query query, string queryId, string ruleId, string category) {
9+
query =
10+
// `Query` instance for the `inappropriateArgumentTypePassedViaEllipsis` query
11+
Preconditions2Package::inappropriateArgumentTypePassedViaEllipsisQuery() and
12+
queryId =
13+
// `@id` for the `inappropriateArgumentTypePassedViaEllipsis` query
14+
"cpp/misra/inappropriate-argument-type-passed-via-ellipsis" and
15+
ruleId = "RULE-8-2-11" and
16+
category = "required"
17+
}
18+
19+
module Preconditions2Package {
20+
Query inappropriateArgumentTypePassedViaEllipsisQuery() {
21+
//autogenerate `Query` type
22+
result =
23+
// `Query` type for `inappropriateArgumentTypePassedViaEllipsis` query
24+
TQueryCPP(TPreconditions2PackageQuery(TInappropriateArgumentTypePassedViaEllipsisQuery()))
25+
}
26+
}

cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ import OrderOfEvaluation
8181
import OutOfBounds
8282
import Pointers
8383
import Preconditions1
84+
import Preconditions2
8485
import Preconditions3
8586
import Preconditions4
8687
import Preconditions5
@@ -188,6 +189,7 @@ newtype TCPPQuery =
188189
TOutOfBoundsPackageQuery(OutOfBoundsQuery q) or
189190
TPointersPackageQuery(PointersQuery q) or
190191
TPreconditions1PackageQuery(Preconditions1Query q) or
192+
TPreconditions2PackageQuery(Preconditions2Query q) or
191193
TPreconditions3PackageQuery(Preconditions3Query q) or
192194
TPreconditions4PackageQuery(Preconditions4Query q) or
193195
TPreconditions5PackageQuery(Preconditions5Query q) or
@@ -295,6 +297,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat
295297
isOutOfBoundsQueryMetadata(query, queryId, ruleId, category) or
296298
isPointersQueryMetadata(query, queryId, ruleId, category) or
297299
isPreconditions1QueryMetadata(query, queryId, ruleId, category) or
300+
isPreconditions2QueryMetadata(query, queryId, ruleId, category) or
298301
isPreconditions3QueryMetadata(query, queryId, ruleId, category) or
299302
isPreconditions4QueryMetadata(query, queryId, ruleId, category) or
300303
isPreconditions5QueryMetadata(query, queryId, ruleId, category) or
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/**
2+
* @id cpp/misra/inappropriate-argument-type-passed-via-ellipsis
3+
* @name RULE-8-2-11: An argument passed via ellipsis shall have an appropriate type
4+
* @description Passing arguments of certain class types via an ellipsis parameter is only
5+
* conditionally-supported with implementation-defined behaviour.
6+
* @kind problem
7+
* @precision very-high
8+
* @problem.severity error
9+
* @tags external/misra/id/rule-8-2-11
10+
* scope/single-translation-unit
11+
* correctness
12+
* external/misra/enforcement/decidable
13+
* external/misra/obligation/required
14+
*/
15+
16+
import cpp
17+
import codingstandards.cpp.misra
18+
import codingstandards.cpp.types.TrivialType
19+
20+
/**
21+
* Holds if `arg` is an argument passed via ellipsis in `call`.
22+
*/
23+
predicate isEllipsisArgument(FunctionCall call, Expr arg, int i) {
24+
call.getTarget().isVarargs() and
25+
arg = call.getArgument(i) and
26+
i >= call.getTarget().getNumberOfParameters()
27+
}
28+
29+
/**
30+
* Holds if `c` has virtual member functions.
31+
*/
32+
predicate hasVirtualMemberFunctions(Class c) { c.getAMemberFunction().isVirtual() }
33+
34+
/**
35+
* Holds if `c` has non-trivial copy or move operations.
36+
*/
37+
predicate hasNonTrivialCopyOrMove(Class c) {
38+
exists(CopyConstructor cc | cc = c.getAConstructor() |
39+
not cc instanceof TrivialCopyOrMoveConstructor
40+
)
41+
or
42+
exists(MoveConstructor mc | mc = c.getAConstructor() |
43+
not mc instanceof TrivialCopyOrMoveConstructor
44+
)
45+
or
46+
exists(CopyAssignmentOperator ca | ca = c.getAMemberFunction() |
47+
not ca instanceof TrivialCopyOrMoveAssignmentOperator
48+
)
49+
or
50+
exists(MoveAssignmentOperator ma | ma = c.getAMemberFunction() |
51+
not ma instanceof TrivialCopyOrMoveAssignmentOperator
52+
)
53+
}
54+
55+
/**
56+
* Holds if `c` has a non-trivial destructor.
57+
*/
58+
predicate hasNonTrivialDestructor(Class c) { not hasTrivialDestructor(c) }
59+
60+
/**
61+
* Gets the primary reason why `c` is an inappropriate type to pass via ellipsis, prioritized
62+
* by the order of conditions in the rule amplification.
63+
*/
64+
string getInappropriateReason(Class c) {
65+
if hasVirtualMemberFunctions(c)
66+
then result = "has virtual member functions"
67+
else
68+
if hasNonTrivialCopyOrMove(c)
69+
then result = "has non-trivial copy or move operations"
70+
else (
71+
hasNonTrivialDestructor(c) and result = "has a non-trivial destructor"
72+
)
73+
}
74+
75+
from FunctionCall call, Expr arg, int i, Class argType, string reason
76+
where
77+
not isExcluded(arg, Preconditions2Package::inappropriateArgumentTypePassedViaEllipsisQuery()) and
78+
isEllipsisArgument(call, arg, i) and
79+
// `arg.getType()` is void for some constructor calls. These seem to have a conversion of class
80+
// `TemporaryObjectExpr`, which seems to have the correct type.
81+
argType = arg.getFullyConverted().getUnspecifiedType() and
82+
reason = getInappropriateReason(argType) and
83+
// Exclude unevaluated contexts
84+
not arg.isUnevaluated() and
85+
not call.isUnevaluated()
86+
select arg,
87+
"Argument of type '" + argType.getName() + "' passed via ellipsis to $@ " + reason + ".",
88+
call.getTarget(), call.getTarget().getName()
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
| test.cpp:87:20:87:39 | call to VirtualMemberClass | Argument of type 'VirtualMemberClass' passed via ellipsis to $@ has virtual member functions. | test.cpp:78:6:78:18 | variadic_func | variadic_func |
2+
| test.cpp:88:20:88:43 | call to VirtualDestructorClass | Argument of type 'VirtualDestructorClass' passed via ellipsis to $@ has virtual member functions. | test.cpp:78:6:78:18 | variadic_func | variadic_func |
3+
| test.cpp:89:20:89:40 | 0 | Argument of type 'NonTrivialCopyClass' passed via ellipsis to $@ has non-trivial copy or move operations. | test.cpp:78:6:78:18 | variadic_func | variadic_func |
4+
| test.cpp:90:20:90:40 | 0 | Argument of type 'NonTrivialMoveClass' passed via ellipsis to $@ has non-trivial copy or move operations. | test.cpp:78:6:78:18 | variadic_func | variadic_func |
5+
| test.cpp:91:20:91:46 | 0 | Argument of type 'NonTrivialCopyAssignClass' passed via ellipsis to $@ has non-trivial copy or move operations. | test.cpp:78:6:78:18 | variadic_func | variadic_func |
6+
| test.cpp:92:20:92:46 | 0 | Argument of type 'NonTrivialMoveAssignClass' passed via ellipsis to $@ has non-trivial copy or move operations. | test.cpp:78:6:78:18 | variadic_func | variadic_func |
7+
| test.cpp:93:20:93:46 | 0 | Argument of type 'NonTrivialDestructorClass' passed via ellipsis to $@ has a non-trivial destructor. | test.cpp:78:6:78:18 | variadic_func | variadic_func |
8+
| test.cpp:94:20:94:39 | call to DerivedFromVirtual | Argument of type 'DerivedFromVirtual' passed via ellipsis to $@ has virtual member functions. | test.cpp:78:6:78:18 | variadic_func | variadic_func |
9+
| test.cpp:95:20:95:40 | call to MultipleIssuesClass | Argument of type 'MultipleIssuesClass' passed via ellipsis to $@ has virtual member functions. | test.cpp:78:6:78:18 | variadic_func | variadic_func |
10+
| test.cpp:96:20:96:33 | call to VirtualMemberClass | Argument of type 'VirtualMemberClass' passed via ellipsis to $@ has virtual member functions. | test.cpp:78:6:78:18 | variadic_func | variadic_func |
11+
| test.cpp:97:20:97:39 | call to VirtualMemberClass | Argument of type 'VirtualMemberClass' passed via ellipsis to $@ has virtual member functions. | test.cpp:78:6:78:18 | variadic_func | variadic_func |
12+
| test.cpp:99:36:99:55 | call to VirtualMemberClass | Argument of type 'VirtualMemberClass' passed via ellipsis to $@ has virtual member functions. | test.cpp:78:6:78:18 | variadic_func | variadic_func |
13+
| test.cpp:119:25:119:44 | call to VirtualMemberClass | Argument of type 'VirtualMemberClass' passed via ellipsis to $@ has virtual member functions. | test.cpp:115:6:115:26 | variadic_take_objects | variadic_take_objects |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/RULE-8-2-11/InappropriateArgumentTypePassedViaEllipsis.ql
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// Trivial class - no virtual functions, trivial special members
2+
struct TrivialClass {
3+
int m1;
4+
};
5+
6+
// Class with virtual member function
7+
struct VirtualMemberClass {
8+
int m1;
9+
virtual void f1() {}
10+
};
11+
12+
// Class with virtual destructor
13+
struct VirtualDestructorClass {
14+
int m1;
15+
virtual ~VirtualDestructorClass() = default;
16+
};
17+
18+
// Class with user-defined (non-trivial) copy constructor
19+
struct NonTrivialCopyClass {
20+
int m1;
21+
NonTrivialCopyClass() = default;
22+
NonTrivialCopyClass(const NonTrivialCopyClass &other) {}
23+
};
24+
25+
// Class with user-defined (non-trivial) move constructor
26+
struct NonTrivialMoveClass {
27+
int m1;
28+
NonTrivialMoveClass() = default;
29+
NonTrivialMoveClass(NonTrivialMoveClass &&other) {}
30+
};
31+
32+
// Class with user-defined (non-trivial) copy assignment operator
33+
struct NonTrivialCopyAssignClass {
34+
int m1;
35+
NonTrivialCopyAssignClass &operator=(const NonTrivialCopyAssignClass &other) {
36+
}
37+
};
38+
39+
// Class with user-defined (non-trivial) move assignment operator
40+
struct NonTrivialMoveAssignClass {
41+
int m1;
42+
NonTrivialMoveAssignClass &operator=(NonTrivialMoveAssignClass &&other) {}
43+
};
44+
45+
// Class with user-defined (non-trivial) destructor
46+
struct NonTrivialDestructorClass {
47+
~NonTrivialDestructorClass() {}
48+
};
49+
50+
// Derived class inheriting virtual
51+
struct DerivedFromVirtual : public VirtualMemberClass {
52+
void f1() override {}
53+
};
54+
55+
// Class with multiple inappropriate properties
56+
struct MultipleIssuesClass {
57+
virtual void f1() {}
58+
MultipleIssuesClass() = default;
59+
MultipleIssuesClass(const MultipleIssuesClass &) {}
60+
~MultipleIssuesClass() {}
61+
};
62+
63+
// Class with defaulted special members (trivial)
64+
struct DefaultedClass {
65+
int m1;
66+
DefaultedClass() = default;
67+
DefaultedClass(const DefaultedClass &) = default;
68+
DefaultedClass(DefaultedClass &&) = default;
69+
DefaultedClass &operator=(const DefaultedClass &) = default;
70+
DefaultedClass &operator=(DefaultedClass &&) = default;
71+
~DefaultedClass() = default;
72+
};
73+
74+
// A typedef for an inappropriate type
75+
using VirtualAlias = VirtualMemberClass;
76+
77+
// User-defined variadic function
78+
void variadic_func(int p1, ...) {}
79+
80+
void f() {
81+
variadic_func(0, 42); // COMPLIANT
82+
variadic_func(0, 3.14); // COMPLIANT
83+
variadic_func(0, (void *)nullptr); // COMPLIANT
84+
85+
variadic_func(0, TrivialClass()); // COMPLIANT
86+
variadic_func(0, DefaultedClass()); // COMPLIANT
87+
variadic_func(0, VirtualMemberClass()); // NON_COMPLIANT
88+
variadic_func(0, VirtualDestructorClass()); // NON_COMPLIANT
89+
variadic_func(0, NonTrivialCopyClass()); // NON_COMPLIANT
90+
variadic_func(0, NonTrivialMoveClass()); // NON_COMPLIANT
91+
variadic_func(0, NonTrivialCopyAssignClass()); // NON_COMPLIANT
92+
variadic_func(0, NonTrivialMoveAssignClass()); // NON_COMPLIANT
93+
variadic_func(0, NonTrivialDestructorClass()); // NON_COMPLIANT
94+
variadic_func(0, DerivedFromVirtual()); // NON_COMPLIANT
95+
variadic_func(0, MultipleIssuesClass()); // NON_COMPLIANT
96+
variadic_func(0, VirtualAlias()); // NON_COMPLIANT
97+
variadic_func(0, VirtualMemberClass()); // NON_COMPLIANT
98+
99+
variadic_func(0, TrivialClass(), VirtualMemberClass()); // NON_COMPLIANT
100+
}
101+
102+
void test_unevaluated_context() {
103+
sizeof(variadic_func(0, VirtualMemberClass())); // COMPLIANT
104+
}
105+
106+
// Parameter pack is not ellipsis
107+
template <typename... Args> void parameter_pack_func(Args... p1) {}
108+
109+
void test_parameter_pack() {
110+
VirtualMemberClass l1;
111+
NonTrivialCopyClass l2;
112+
parameter_pack_func(l1, l2); // COMPLIANT
113+
}
114+
115+
void variadic_take_objects(VirtualMemberClass v, ...);
116+
void test_named_parameter_of_variadic_function() {
117+
variadic_take_objects(VirtualMemberClass()); // COMPLIANT
118+
variadic_take_objects(VirtualMemberClass(),
119+
VirtualMemberClass()); // NON_COMPLIANT
120+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"MISRA-C++-2023": {
3+
"RULE-8-2-11": {
4+
"properties": {
5+
"enforcement": "decidable",
6+
"obligation": "required"
7+
},
8+
"queries": [
9+
{
10+
"description": "Passing arguments of certain class types via an ellipsis parameter is only conditionally-supported with implementation-defined behaviour.",
11+
"kind": "problem",
12+
"name": "An argument passed via ellipsis shall have an appropriate type",
13+
"precision": "very-high",
14+
"severity": "error",
15+
"short_name": "InappropriateArgumentTypePassedViaEllipsis",
16+
"tags": [
17+
"scope/single-translation-unit",
18+
"correctness"
19+
]
20+
}
21+
],
22+
"title": "An argument passed via ellipsis shall have an appropriate type"
23+
}
24+
}
25+
}

0 commit comments

Comments
 (0)