Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/
import cpp
import RuleMetadata
import codingstandards.cpp.exclusions.RuleMetadata

newtype Preconditions2Query = TInappropriateArgumentTypePassedViaEllipsisQuery()

predicate isPreconditions2QueryMetadata(Query query, string queryId, string ruleId, string category) {
query =
// `Query` instance for the `inappropriateArgumentTypePassedViaEllipsis` query
Preconditions2Package::inappropriateArgumentTypePassedViaEllipsisQuery() and
queryId =
// `@id` for the `inappropriateArgumentTypePassedViaEllipsis` query
"cpp/misra/inappropriate-argument-type-passed-via-ellipsis" and
ruleId = "RULE-8-2-11" and
category = "required"
}

module Preconditions2Package {
Query inappropriateArgumentTypePassedViaEllipsisQuery() {
//autogenerate `Query` type
result =
// `Query` type for `inappropriateArgumentTypePassedViaEllipsis` query
TQueryCPP(TPreconditions2PackageQuery(TInappropriateArgumentTypePassedViaEllipsisQuery()))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ import OrderOfEvaluation
import OutOfBounds
import Pointers
import Preconditions1
import Preconditions2
import Preconditions3
import Preconditions4
import Preconditions5
Expand Down Expand Up @@ -190,6 +191,7 @@ newtype TCPPQuery =
TOutOfBoundsPackageQuery(OutOfBoundsQuery q) or
TPointersPackageQuery(PointersQuery q) or
TPreconditions1PackageQuery(Preconditions1Query q) or
TPreconditions2PackageQuery(Preconditions2Query q) or
TPreconditions3PackageQuery(Preconditions3Query q) or
TPreconditions4PackageQuery(Preconditions4Query q) or
TPreconditions5PackageQuery(Preconditions5Query q) or
Expand Down Expand Up @@ -298,6 +300,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat
isOutOfBoundsQueryMetadata(query, queryId, ruleId, category) or
isPointersQueryMetadata(query, queryId, ruleId, category) or
isPreconditions1QueryMetadata(query, queryId, ruleId, category) or
isPreconditions2QueryMetadata(query, queryId, ruleId, category) or
isPreconditions3QueryMetadata(query, queryId, ruleId, category) or
isPreconditions4QueryMetadata(query, queryId, ruleId, category) or
isPreconditions5QueryMetadata(query, queryId, ruleId, category) or
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* @id cpp/misra/inappropriate-argument-type-passed-via-ellipsis
* @name RULE-8-2-11: An argument passed via ellipsis shall have an appropriate type
* @description Passing arguments of certain class types via an ellipsis parameter is only
* conditionally-supported with implementation-defined behaviour.
* @kind problem
* @precision very-high
* @problem.severity error
* @tags external/misra/id/rule-8-2-11
* scope/single-translation-unit
* correctness
* external/misra/enforcement/decidable
* external/misra/obligation/required
*/

import cpp
import codingstandards.cpp.misra
import codingstandards.cpp.types.TrivialType

/**
* Holds if `arg` is an argument passed via ellipsis in `call`.
*/
predicate isEllipsisArgument(FunctionCall call, Expr arg, int i) {
call.getTarget().isVarargs() and
arg = call.getArgument(i) and
i >= call.getTarget().getNumberOfParameters()
}

/**
* Holds if `c` has virtual member functions.
*/
predicate hasVirtualMemberFunctions(Class c) { c.getAMemberFunction().isVirtual() }

/**
* Holds if `c` has non-trivial copy or move operations.
*/
predicate hasNonTrivialCopyOrMove(Class c) {
exists(CopyConstructor cc | cc = c.getAConstructor() |
not cc instanceof TrivialCopyOrMoveConstructor
)
or
exists(MoveConstructor mc | mc = c.getAConstructor() |
not mc instanceof TrivialCopyOrMoveConstructor
)
or
exists(CopyAssignmentOperator ca | ca = c.getAMemberFunction() |
not ca instanceof TrivialCopyOrMoveAssignmentOperator
)
or
exists(MoveAssignmentOperator ma | ma = c.getAMemberFunction() |
not ma instanceof TrivialCopyOrMoveAssignmentOperator
)
}

/**
* Holds if `c` has a non-trivial destructor.
*/
predicate hasNonTrivialDestructor(Class c) { not hasTrivialDestructor(c) }

/**
* Gets the primary reason why `c` is an inappropriate type to pass via ellipsis, prioritized
* by the order of conditions in the rule amplification.
*/
string getInappropriateReason(Class c) {
if hasVirtualMemberFunctions(c)
then result = "has virtual member functions"
else
if hasNonTrivialCopyOrMove(c)
then result = "has non-trivial copy or move operations"
else (
hasNonTrivialDestructor(c) and result = "has a non-trivial destructor"
)
}

from FunctionCall call, Expr arg, int i, Class argType, string reason
where
not isExcluded(arg, Preconditions2Package::inappropriateArgumentTypePassedViaEllipsisQuery()) and
isEllipsisArgument(call, arg, i) and
// `arg.getType()` is void for some constructor calls. These seem to have a conversion of class
// `TemporaryObjectExpr`, which seems to have the correct type.
argType = arg.getFullyConverted().getUnspecifiedType() and
reason = getInappropriateReason(argType) and
// Exclude unevaluated contexts
not arg.isUnevaluated() and
not call.isUnevaluated()
select arg,
"Argument of type '" + argType.getName() + "' passed via ellipsis to $@ " + reason + ".",
call.getTarget(), call.getTarget().getName()
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rules/RULE-8-2-11/InappropriateArgumentTypePassedViaEllipsis.ql
120 changes: 120 additions & 0 deletions cpp/misra/test/rules/RULE-8-2-11/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Trivial class - no virtual functions, trivial special members
struct TrivialClass {
int m1;
};

// Class with virtual member function
struct VirtualMemberClass {
int m1;
virtual void f1() {}
};

// Class with virtual destructor
struct VirtualDestructorClass {
int m1;
virtual ~VirtualDestructorClass() = default;
};

// Class with user-defined (non-trivial) copy constructor
struct NonTrivialCopyClass {
int m1;
NonTrivialCopyClass() = default;
NonTrivialCopyClass(const NonTrivialCopyClass &other) {}
};

// Class with user-defined (non-trivial) move constructor
struct NonTrivialMoveClass {
int m1;
NonTrivialMoveClass() = default;
NonTrivialMoveClass(NonTrivialMoveClass &&other) {}
};

// Class with user-defined (non-trivial) copy assignment operator
struct NonTrivialCopyAssignClass {
int m1;
NonTrivialCopyAssignClass &operator=(const NonTrivialCopyAssignClass &other) {
}
};

// Class with user-defined (non-trivial) move assignment operator
struct NonTrivialMoveAssignClass {
int m1;
NonTrivialMoveAssignClass &operator=(NonTrivialMoveAssignClass &&other) {}
Comment thread
MichaelRFairhurst marked this conversation as resolved.
Outdated
};

// Class with user-defined (non-trivial) destructor
struct NonTrivialDestructorClass {
~NonTrivialDestructorClass() {}
};

// Derived class inheriting virtual
struct DerivedFromVirtual : public VirtualMemberClass {
void f1() override {}
};

// Class with multiple inappropriate properties
struct MultipleIssuesClass {
virtual void f1() {}
MultipleIssuesClass() = default;
MultipleIssuesClass(const MultipleIssuesClass &) {}
~MultipleIssuesClass() {}
};

// Class with defaulted special members (trivial)
struct DefaultedClass {
int m1;
DefaultedClass() = default;
DefaultedClass(const DefaultedClass &) = default;
DefaultedClass(DefaultedClass &&) = default;
DefaultedClass &operator=(const DefaultedClass &) = default;
DefaultedClass &operator=(DefaultedClass &&) = default;
~DefaultedClass() = default;
};

// A typedef for an inappropriate type
using VirtualAlias = VirtualMemberClass;

// User-defined variadic function
void variadic_func(int p1, ...) {}

void f() {
variadic_func(0, 42); // COMPLIANT
variadic_func(0, 3.14); // COMPLIANT
variadic_func(0, (void *)nullptr); // COMPLIANT

variadic_func(0, TrivialClass()); // COMPLIANT
variadic_func(0, DefaultedClass()); // COMPLIANT
variadic_func(0, VirtualMemberClass()); // NON_COMPLIANT
variadic_func(0, VirtualDestructorClass()); // NON_COMPLIANT
variadic_func(0, NonTrivialCopyClass()); // NON_COMPLIANT
variadic_func(0, NonTrivialMoveClass()); // NON_COMPLIANT
variadic_func(0, NonTrivialCopyAssignClass()); // NON_COMPLIANT
variadic_func(0, NonTrivialMoveAssignClass()); // NON_COMPLIANT
variadic_func(0, NonTrivialDestructorClass()); // NON_COMPLIANT
variadic_func(0, DerivedFromVirtual()); // NON_COMPLIANT
variadic_func(0, MultipleIssuesClass()); // NON_COMPLIANT
variadic_func(0, VirtualAlias()); // NON_COMPLIANT
variadic_func(0, VirtualMemberClass()); // NON_COMPLIANT

variadic_func(0, TrivialClass(), VirtualMemberClass()); // NON_COMPLIANT
}

void test_unevaluated_context() {
sizeof(variadic_func(0, VirtualMemberClass())); // COMPLIANT
Comment thread
MichaelRFairhurst marked this conversation as resolved.
}

// Parameter pack is not ellipsis
template <typename... Args> void parameter_pack_func(Args... p1) {}

void test_parameter_pack() {
VirtualMemberClass l1;
NonTrivialCopyClass l2;
parameter_pack_func(l1, l2); // COMPLIANT
}

void variadic_take_objects(VirtualMemberClass v, ...);
void test_named_parameter_of_variadic_function() {
variadic_take_objects(VirtualMemberClass()); // COMPLIANT
variadic_take_objects(VirtualMemberClass(),
VirtualMemberClass()); // NON_COMPLIANT
}
25 changes: 25 additions & 0 deletions rule_packages/cpp/Preconditions2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"MISRA-C++-2023": {
"RULE-8-2-11": {
"properties": {
"enforcement": "decidable",
"obligation": "required"
},
"queries": [
{
"description": "Passing arguments of certain class types via an ellipsis parameter is only conditionally-supported with implementation-defined behaviour.",
"kind": "problem",
"name": "An argument passed via ellipsis shall have an appropriate type",
"precision": "very-high",
"severity": "error",
"short_name": "InappropriateArgumentTypePassedViaEllipsis",
"tags": [
"scope/single-translation-unit",
"correctness"
]
}
],
"title": "An argument passed via ellipsis shall have an appropriate type"
}
}
}
Loading