From a31cf706bf06138d50e1f905fdbf669e3be838ff Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Wed, 29 Apr 2026 11:16:00 -0700 Subject: [PATCH 01/15] First round tests --- .../cpp/exclusions/cpp/Classes3.qll | 44 +++ .../cpp/exclusions/cpp/RuleMetadata.qll | 6 + ...mproperlyProvidedSpecialMemberFunctions.ql | 23 ++ ...erlyProvidedSpecialMemberFunctionsAudit.ql | 24 ++ ...rlyProvidedSpecialMemberFunctions.expected | 1 + ...operlyProvidedSpecialMemberFunctions.qlref | 1 + ...ovidedSpecialMemberFunctionsAudit.expected | 1 + ...yProvidedSpecialMemberFunctionsAudit.qlref | 1 + cpp/misra/test/rules/RULE-15-0-1/test.cpp | 311 ++++++++++++++++++ rule_packages/cpp/Classes3.json | 58 ++++ 10 files changed, 470 insertions(+) create mode 100644 cpp/common/src/codingstandards/cpp/exclusions/cpp/Classes3.qll create mode 100644 cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.ql create mode 100644 cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.ql create mode 100644 cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.expected create mode 100644 cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.qlref create mode 100644 cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.expected create mode 100644 cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.qlref create mode 100644 cpp/misra/test/rules/RULE-15-0-1/test.cpp create mode 100644 rule_packages/cpp/Classes3.json diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Classes3.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Classes3.qll new file mode 100644 index 0000000000..4eec671987 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Classes3.qll @@ -0,0 +1,44 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Classes3Query = + TImproperlyProvidedSpecialMemberFunctionsQuery() or + TImproperlyProvidedSpecialMemberFunctionsAuditQuery() + +predicate isClasses3QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `improperlyProvidedSpecialMemberFunctions` query + Classes3Package::improperlyProvidedSpecialMemberFunctionsQuery() and + queryId = + // `@id` for the `improperlyProvidedSpecialMemberFunctions` query + "cpp/misra/improperly-provided-special-member-functions" and + ruleId = "RULE-15-0-1" and + category = "required" + or + query = + // `Query` instance for the `improperlyProvidedSpecialMemberFunctionsAudit` query + Classes3Package::improperlyProvidedSpecialMemberFunctionsAuditQuery() and + queryId = + // `@id` for the `improperlyProvidedSpecialMemberFunctionsAudit` query + "cpp/misra/improperly-provided-special-member-functions-audit" and + ruleId = "RULE-15-0-1" and + category = "required" +} + +module Classes3Package { + Query improperlyProvidedSpecialMemberFunctionsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `improperlyProvidedSpecialMemberFunctions` query + TQueryCPP(TClasses3PackageQuery(TImproperlyProvidedSpecialMemberFunctionsQuery())) + } + + Query improperlyProvidedSpecialMemberFunctionsAuditQuery() { + //autogenerate `Query` type + result = + // `Query` type for `improperlyProvidedSpecialMemberFunctionsAudit` query + TQueryCPP(TClasses3PackageQuery(TImproperlyProvidedSpecialMemberFunctionsAuditQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll index d0ac06886e..38278d8cc3 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll @@ -18,6 +18,7 @@ import BannedSyntax import BannedTypes import Classes import Classes2 +import Classes3 import Classes4 import Comments import Concurrency @@ -82,6 +83,7 @@ import OrderOfEvaluation import OutOfBounds import Pointers import Preconditions1 +import Preconditions2 import Preconditions3 import Preconditions4 import Preconditions5 @@ -126,6 +128,7 @@ newtype TCPPQuery = TBannedTypesPackageQuery(BannedTypesQuery q) or TClassesPackageQuery(ClassesQuery q) or TClasses2PackageQuery(Classes2Query q) or + TClasses3PackageQuery(Classes3Query q) or TClasses4PackageQuery(Classes4Query q) or TCommentsPackageQuery(CommentsQuery q) or TConcurrencyPackageQuery(ConcurrencyQuery q) or @@ -190,6 +193,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 @@ -234,6 +238,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat isBannedTypesQueryMetadata(query, queryId, ruleId, category) or isClassesQueryMetadata(query, queryId, ruleId, category) or isClasses2QueryMetadata(query, queryId, ruleId, category) or + isClasses3QueryMetadata(query, queryId, ruleId, category) or isClasses4QueryMetadata(query, queryId, ruleId, category) or isCommentsQueryMetadata(query, queryId, ruleId, category) or isConcurrencyQueryMetadata(query, queryId, ruleId, category) or @@ -298,6 +303,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 diff --git a/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.ql b/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.ql new file mode 100644 index 0000000000..7e60c14ad0 --- /dev/null +++ b/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.ql @@ -0,0 +1,23 @@ +/** + * @id cpp/misra/improperly-provided-special-member-functions + * @name RULE-15-0-1: Special member functions shall be provided appropriately + * @description Incorrect provision of special member functions can lead to unexpected or undefined + * behavior when objects of the class are copied, moved, or destroyed. + * @kind problem + * @precision medium + * @problem.severity warning + * @tags external/misra/id/rule-15-0-1 + * scope/single-translation-unit + * correctness + * maintainability + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra + +from +where + not isExcluded(x, Classes3Package::improperlyProvidedSpecialMemberFunctionsQuery()) and +select diff --git a/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.ql b/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.ql new file mode 100644 index 0000000000..104ca228af --- /dev/null +++ b/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.ql @@ -0,0 +1,24 @@ +/** + * @id cpp/misra/improperly-provided-special-member-functions-audit + * @name RULE-15-0-1: Special member functions shall be provided appropriately, Audit + * @description Audit: incorrect provision of special member functions can lead to unexpected or + * undefined behavior when objects of the class are copied, moved, or destroyed. + * @kind problem + * @precision low + * @problem.severity warning + * @tags external/misra/id/rule-15-0-1 + * scope/single-translation-unit + * correctness + * audit + * maintainability + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra + +from +where + not isExcluded(x, Classes3Package::improperlyProvidedSpecialMemberFunctionsAuditQuery()) and +select diff --git a/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.expected b/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.expected new file mode 100644 index 0000000000..2ec1a0ac6c --- /dev/null +++ b/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.expected @@ -0,0 +1 @@ +No expected results have yet been specified \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.qlref b/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.qlref new file mode 100644 index 0000000000..4c14a48a2d --- /dev/null +++ b/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.qlref @@ -0,0 +1 @@ +rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.expected b/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.expected new file mode 100644 index 0000000000..2ec1a0ac6c --- /dev/null +++ b/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.expected @@ -0,0 +1 @@ +No expected results have yet been specified \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.qlref b/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.qlref new file mode 100644 index 0000000000..1a3eac2ba7 --- /dev/null +++ b/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.qlref @@ -0,0 +1 @@ +rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-15-0-1/test.cpp b/cpp/misra/test/rules/RULE-15-0-1/test.cpp new file mode 100644 index 0000000000..66aadb60da --- /dev/null +++ b/cpp/misra/test/rules/RULE-15-0-1/test.cpp @@ -0,0 +1,311 @@ +namespace helpers { +void f(); +} + +// A macro to generate `T(const T&)` that may be followed by `= default;` or `= +// delete;` etc. +#define COPY_CTOR(NAME) NAME(const NAME &) + +// A macro to generate `T(T&&)` that may be followed by `= default;` or `= +// delete;` etc. +#define MOVE_CTOR(NAME) NAME(NAME &&) + +// A macro to generate `T& operator=(const T&)` that may be followed by `= +// default;` or `= delete;` etc. +#define COPY_ASSIGN(NAME) NAME &operator=(const NAME &) + +// A macro to generate `T& operator=(T&&)` that may be followed by `= default;` +// or `= delete;` etc. +#define MOVE_ASSIGN(NAME) NAME &operator=(NAME &&) + +// A macro to generate `~T()` that may be followed by `= default;` or `= +// delete;` etc. +#define DTOR(NAME) ~NAME() + +// clang-format off +// A macro to generate all five special member functions for a class `NAME` +// with the ability to specify for each whether it is `= default;`, `= delete;`, +// or a non-trivial definition via the corresponding `_SPEC` parameter. +#define DEFINE_ALL_SPECIAL_MEMBERS(NAME, \ + COPY_CTOR_SPEC, MOVE_CTOR_SPEC, COPY_ASSIGN_SPEC, MOVE_ASSIGN_SPEC, DTOR_SPEC) \ + COPY_CTOR(NAME) COPY_CTOR_SPEC /* T(const T&) = default; */ \ + MOVE_CTOR(NAME) MOVE_CTOR_SPEC /* T(T&&) = default; */ \ + COPY_ASSIGN(NAME) COPY_ASSIGN_SPEC /* T& operator=(const T&) = default; */ \ + MOVE_ASSIGN(NAME) MOVE_ASSIGN_SPEC /* T& operator=(T&&) = default; */ \ + DTOR(NAME) DTOR_SPEC /* ~T() = default; */ + +// Macros to generate `= default;`, `= delete;`, or a non-trivial definition for a +// special member function. +#define DEFAULTED = default; +#define DELETED = delete; +#define CUSTOMIZED { helpers::f(); } +// clang-format on + +namespace fully_specified { +/** + * Go through all combinations of defaulted and deleted copy/move + * ctor/assignment. + */ +namespace combinations { + +// NOTE: THIS USES THE MISRA SPECIFICATION TABLE ORDER (move ctor, move assign, +// copy ctor, copy assign) AND NOT THE ORDER OF `DEFINE_ALL_SPECIAL_MEMBERS` +// (copy ctor, move ctor, copy assign, move assign) !!!!!!! +#define TABLE_ROW(NAME, MOVE_CTOR_SPEC, MOVE_ASSIGN_SPEC, COPY_CTOR_SPEC, \ + COPY_ASSIGN_SPEC) \ + class NAME { \ + int x; \ + \ + public: \ + DEFINE_ALL_SPECIAL_MEMBERS(NAME, COPY_CTOR_SPEC, MOVE_CTOR_SPEC, \ + COPY_ASSIGN_SPEC, MOVE_ASSIGN_SPEC, DEFAULTED) \ + }; + +// clang-format off +#define YES DEFAULTED +#define NO DELETED +// See `TABLE_ROW` for ordering notes!! This matches the table from the spec. +TABLE_ROW(C1, YES, YES, YES, YES) // COMPLIANT - Copy-enabled +TABLE_ROW(C2, YES, YES, YES, NO ) // NON_COMPLIANT +TABLE_ROW(C3, YES, YES, NO, YES) // NON_COMPLIANT +TABLE_ROW(C4, YES, YES, NO, NO ) // COMPLIANT - Move-only +TABLE_ROW(C5, YES, NO, YES, YES) // NON_COMPLIANT +TABLE_ROW(C6, YES, NO, YES, NO ) // COMPLIANT - Copy-enabled +TABLE_ROW(C7, YES, NO, NO, YES) // NON_COMPLIANT +TABLE_ROW(C8, YES, NO, NO, NO ) // COMPLIANT - Move-only +TABLE_ROW(C9, NO, YES, YES, YES) // NON_COMPLIANT +TABLE_ROW(C10, NO, YES, YES, NO ) // NON_COMPLIANT +TABLE_ROW(C11, NO, YES, NO, YES) // NON_COMPLIANT +TABLE_ROW(C12, NO, YES, NO, NO ) // NON_COMPLIANT +TABLE_ROW(C13, NO, NO, YES, YES) // NON_COMPLIANT +TABLE_ROW(C14, NO, NO, YES, NO ) // NON_COMPLIANT +TABLE_ROW(C15, NO, NO, NO, YES) // NON_COMPLIANT +TABLE_ROW(C16, NO, NO, NO, NO ) // COMPLIANT - Unmovable +// clang-format on + +#undef YES +#undef NO +#undef TABLE_ROW +} // namespace combinations + +class AllPrivate { // COMPLIANT -- unmovable +private: + int x; + +public: + DEFINE_ALL_SPECIAL_MEMBERS(AllPrivate, DEFAULTED, DEFAULTED, DEFAULTED, + DEFAULTED, DEFAULTED) +}; + +class PrivateCopyCtor { // NON_COMPLIANT + int x; + +private: + COPY_CTOR(PrivateCopyCtor) = default; + +public: + MOVE_CTOR(PrivateCopyCtor) = default; + COPY_ASSIGN(PrivateCopyCtor) = default; + MOVE_ASSIGN(PrivateCopyCtor) = default; + DTOR(PrivateCopyCtor) = default; +}; + +class PrivateMoveCtor { // NON_COMPLIANT + int x; + +private: + MOVE_CTOR(PrivateMoveCtor) = default; + +public: + COPY_CTOR(PrivateMoveCtor) = default; + COPY_ASSIGN(PrivateMoveCtor) = default; + MOVE_ASSIGN(PrivateMoveCtor) = default; + DTOR(PrivateMoveCtor) = default; +}; + +class PrivateCopyAssign { // NON_COMPLIANT + int x; + +private: + COPY_ASSIGN(PrivateCopyAssign) = default; + +public: + COPY_CTOR(PrivateCopyAssign) = default; + MOVE_CTOR(PrivateCopyAssign) = default; + MOVE_ASSIGN(PrivateCopyAssign) = default; + DTOR(PrivateCopyAssign) = default; +}; + +class PrivateMoveAssign { // NON_COMPLIANT + int x; + +private: + MOVE_ASSIGN(PrivateMoveAssign) = default; + +public: + COPY_CTOR(PrivateMoveAssign) = default; + MOVE_CTOR(PrivateMoveAssign) = default; + COPY_ASSIGN(PrivateMoveAssign) = default; + DTOR(PrivateMoveAssign) = default; +}; + +namespace additional_requirements { + +class CustomizedCopyCtorCompliant { // COMPLIANT + DEFINE_ALL_SPECIAL_MEMBERS(CustomizedCopyCtorCompliant, CUSTOMIZED, DEFAULTED, + DEFAULTED, DEFAULTED, CUSTOMIZED) +}; + +class CustomizedMoveCtorCompliant { // COMPLIANT + DEFINE_ALL_SPECIAL_MEMBERS(CustomizedMoveCtorCompliant, DEFAULTED, CUSTOMIZED, + DEFAULTED, DEFAULTED, CUSTOMIZED) +}; + +class CustomizedCopyAssignCompliant { // COMPLIANT + DEFINE_ALL_SPECIAL_MEMBERS(CustomizedCopyAssignCompliant, DEFAULTED, + DEFAULTED, CUSTOMIZED, DEFAULTED, CUSTOMIZED) +}; + +class CustomizedMoveAssignCompliant { // COMPLIANT + DEFINE_ALL_SPECIAL_MEMBERS(CustomizedMoveAssignCompliant, DEFAULTED, + DEFAULTED, DEFAULTED, CUSTOMIZED, CUSTOMIZED) +}; + +class CustomizedCopyCtorDefaultDtor { // NON_COMPLIANT + DEFINE_ALL_SPECIAL_MEMBERS(CustomizedCopyCtorDefaultDtor, CUSTOMIZED, + DEFAULTED, DEFAULTED, DEFAULTED, DEFAULTED) +}; + +class CustomizedCopyCtorDeletedDtor { // NON_COMPLIANT + DEFINE_ALL_SPECIAL_MEMBERS(CustomizedCopyCtorDeletedDtor, CUSTOMIZED, + DEFAULTED, DEFAULTED, DEFAULTED, DELETED) +}; + +class CustomizedMoveCtorNonCompliant { // NON_COMPLIANT + DEFINE_ALL_SPECIAL_MEMBERS(CustomizedMoveCtorNonCompliant, CUSTOMIZED, + DEFAULTED, DEFAULTED, DEFAULTED, DEFAULTED) +}; + +class CustomizedCopyAssignNonCompliant { // NON_COMPLIANT + DEFINE_ALL_SPECIAL_MEMBERS(CustomizedCopyAssignNonCompliant, DEFAULTED, + DEFAULTED, CUSTOMIZED, DEFAULTED, DEFAULTED) +}; + +class CustomizedMoveAssignNonCompliant { // NON_COMPLIANT + DEFINE_ALL_SPECIAL_MEMBERS(CustomizedMoveAssignNonCompliant, DEFAULTED, + DEFAULTED, DEFAULTED, CUSTOMIZED, DEFAULTED) +}; + +// Move-only with a customized dtor requires customized move operations. +class MoveOnlyNotCustomizedNonCompliant { // NON_COMPLIANT + DEFINE_ALL_SPECIAL_MEMBERS(MoveOnlyNotCustomizedNonCompliant, DELETED, + DEFAULTED, DELETED, DELETED, CUSTOMIZED) +}; + +// Move-only with a customized dtor requires customized move operations. +class MoveOnlyAssignableNotCustomizedNonCompliant { // NON_COMPLIANT + DEFINE_ALL_SPECIAL_MEMBERS(MoveOnlyAssignableNotCustomizedNonCompliant, + DELETED, DEFAULTED, DELETED, DEFAULTED, CUSTOMIZED) +}; + +class MoveOnlyCustomizedCompliant { // COMPLIANT -- customized move + DEFINE_ALL_SPECIAL_MEMBERS(MoveOnlyCustomizedCompliant, DELETED, CUSTOMIZED, + DELETED, DELETED, CUSTOMIZED) +}; + +class MoveOnlyAssignableCustomizedCompliant { // COMPLIANT -- customized move + DEFINE_ALL_SPECIAL_MEMBERS(MoveOnlyAssignableCustomizedCompliant, DELETED, + CUSTOMIZED, DELETED, CUSTOMIZED, CUSTOMIZED) +}; + +class MoveOnlyNotCustomizedCompliant { // COMPLIANT -- default dtor + DEFINE_ALL_SPECIAL_MEMBERS(MoveOnlyNotCustomizedCompliant, DELETED, DEFAULTED, + DELETED, DELETED, DEFAULTED) +}; + +class MoveOnlyAssignableNotCustomizedCompliant { // COMPLIANT -- default dtor + DEFINE_ALL_SPECIAL_MEMBERS(MoveOnlyAssignableNotCustomizedCompliant, DELETED, + DEFAULTED, DELETED, DEFAULTED, DEFAULTED) +}; + +// Copy-enabled with customized dtor requires customized copy and move +class CopyEnabledCustomizedDtorNonCompliant { // NON_COMPLIANT + DEFINE_ALL_SPECIAL_MEMBERS(CopyEnabledCustomizedDtorNonCompliant, DEFAULTED, + DEFAULTED, DEFAULTED, DEFAULTED, CUSTOMIZED) +}; + +class CopyEnabledNonCustomizedDtorCompliant { // COMPLIANT + DEFINE_ALL_SPECIAL_MEMBERS(CopyEnabledNonCustomizedDtorCompliant, DEFAULTED, + DEFAULTED, DEFAULTED, DEFAULTED, DEFAULTED) +}; + +class CopyEnabledCustomizedDtorCompliant { // COMPLIANT + DEFINE_ALL_SPECIAL_MEMBERS(CopyEnabledCustomizedDtorCompliant, CUSTOMIZED, + CUSTOMIZED, CUSTOMIZED, CUSTOMIZED, CUSTOMIZED) +}; + +// A public unmovable base class shall have a public virtual destructor +class UnmovableBaseNonvirtualDtor { // NON_COMPLIANT +public: + DEFINE_ALL_SPECIAL_MEMBERS(UnmovableBaseNonvirtualDtor, DELETED, DELETED, + DELETED, DELETED, DEFAULTED) +}; + +class UnmovableNonvirtualDerived : public UnmovableBaseNonvirtualDtor {}; + +class UnmovableBasePublicVirtualDtor { // COMPLIANT +public: + COPY_CTOR(UnmovableBasePublicVirtualDtor) = delete; + MOVE_CTOR(UnmovableBasePublicVirtualDtor) = delete; + COPY_ASSIGN(UnmovableBasePublicVirtualDtor) = delete; + MOVE_ASSIGN(UnmovableBasePublicVirtualDtor) = delete; + + virtual ~UnmovableBasePublicVirtualDtor() = default; +}; + +class UnmovableDerivedPublicVirtualDtor + : public UnmovableBasePublicVirtualDtor {}; + +class UnmovablePrivateVirtualDtor { // NON_COMPLIANT +public: + COPY_CTOR(UnmovablePrivateVirtualDtor) = delete; + MOVE_CTOR(UnmovablePrivateVirtualDtor) = delete; + COPY_ASSIGN(UnmovablePrivateVirtualDtor) = delete; + MOVE_ASSIGN(UnmovablePrivateVirtualDtor) = delete; + +private: + virtual ~UnmovablePrivateVirtualDtor() = default; +}; + +class UnmovablePrivateVirtualDtorDerived : public UnmovablePrivateVirtualDtor { +}; + +class BaseProtectedDtor { // COMPLIANT +public: + COPY_CTOR(BaseProtectedDtor) = default; + MOVE_CTOR(BaseProtectedDtor) = default; + COPY_ASSIGN(BaseProtectedDtor) = default; + MOVE_ASSIGN(BaseProtectedDtor) = delete; + +protected: + ~BaseProtectedDtor() = default; +}; + +class ProtectedDtorDerived : public BaseProtectedDtor {}; + +class BaseVirtualProtectedDtor { // NON_COMPLIANT +public: + COPY_CTOR(BaseVirtualProtectedDtor) = default; + MOVE_CTOR(BaseVirtualProtectedDtor) = default; + COPY_ASSIGN(BaseVirtualProtectedDtor) = default; + MOVE_ASSIGN(BaseVirtualProtectedDtor) = delete; + +protected: + virtual ~BaseVirtualProtectedDtor() = default; +}; + +class VirtualProtectedDtorDerived : public BaseVirtualProtectedDtor {}; + +} // namespace additional_requirements + +} // namespace fully_specified diff --git a/rule_packages/cpp/Classes3.json b/rule_packages/cpp/Classes3.json new file mode 100644 index 0000000000..ce8c68216a --- /dev/null +++ b/rule_packages/cpp/Classes3.json @@ -0,0 +1,58 @@ +{ + "MISRA-C++-2023": { + "RULE-15-0-1": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Incorrect provision of special member functions can lead to unexpected or undefined behavior when objects of the class are copied, moved, or destroyed.", + "kind": "problem", + "name": "Special member functions shall be provided appropriately", + "precision": "medium", + "severity": "warning", + "short_name": "ImproperlyProvidedSpecialMemberFunctions", + "tags": [ + "scope/single-translation-unit", + "correctness", + "maintainability" + ], + "implementation_scope": { + "description": "This query does not model `std::is_(copy|move)_(constructible|assignable)` traits or the implicit generation of certain special member functions.", + "items": [ + "The only classes that will be analyzed are classes for which the CodeQL database contains an entry for each special member function. See the audit version of this query to find classes we do not analyze.", + "The CodeQL database does not contain certain special member function definitions, such as those that are trivial and not ODR-used. As a result, a query cannot determine whether such special member functions exist or are correctly provided.", + "The CodeQL database does not contain the necessary compiler intrinsics to query `std::is_(copy|move)_(constructible|assignable)` traits.", + "Fully implementing `std::is_(copy|move)_(constructible|assignable)` traits without access to these intrinsics requires a very thorough reimplementation of C++ overload resolution rules, which are spread across dozens of sections of the language specification.", + "Fully implementing implicit special member function generation also requires performing overload resolution on the member-wise direct initialization and assignment for potentially constructed base objects.", + "Therefore, this query considers classes with a public, non-deleted move/copy constructor/assignment operator, as being copy/move constructible/assignable.", + "And this query does not analyze classes for which we do not have a complete set of special member function definitions in the CodeQL database.", + "To analyze the remaining classes, one can run the audit version of this query to find those that are excluded, and for each excluded class, one can either explicitly define all as `= default` or `= delete` or perform a human review of compliance and correctness." + ] + } + }, + { + "description": "Audit: incorrect provision of special member functions can lead to unexpected or undefined behavior when objects of the class are copied, moved, or destroyed.", + "kind": "problem", + "name": "Special member functions shall be provided appropriately, Audit", + "precision": "low", + "severity": "warning", + "short_name": "ImproperlyProvidedSpecialMemberFunctionsAudit", + "tags": [ + "scope/single-translation-unit", + "correctness", + "audit", + "maintainability" + ], + "implementation_scope": { + "description": "This query finds all classes for which the CodeQL database does not contain a complete set of special member function definitions, allowing a human reviewer to audit compliance where the automated query does not, or provide explicit `= default` or `= delete` definitions so the main query will analyze them.", + "items": [ + ] + } + } + ], + "title": "Special member functions shall be provided appropriately" + } + } +} \ No newline at end of file From ba6c2ca9345d0fddde2e53192d1497be242c83e8 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Wed, 29 Apr 2026 12:30:12 -0700 Subject: [PATCH 02/15] First implementation --- .../src/rules/RULE-15-0-1/AnalyzableClass.qll | 68 ++++++++++++++ ...mproperlyProvidedSpecialMemberFunctions.ql | 89 ++++++++++++++++++- cpp/misra/test/rules/RULE-15-0-1/test.cpp | 18 ++++ 3 files changed, 172 insertions(+), 3 deletions(-) create mode 100644 cpp/misra/src/rules/RULE-15-0-1/AnalyzableClass.qll diff --git a/cpp/misra/src/rules/RULE-15-0-1/AnalyzableClass.qll b/cpp/misra/src/rules/RULE-15-0-1/AnalyzableClass.qll new file mode 100644 index 0000000000..fad93c00af --- /dev/null +++ b/cpp/misra/src/rules/RULE-15-0-1/AnalyzableClass.qll @@ -0,0 +1,68 @@ +import cpp + +private predicate isUsable(MemberFunction f) { + not f.isDeleted() and + f.isPublic() +} + +private predicate isMemberCustomized(MemberFunction f) { + exists(f.getDefinition()) and + not f.isDefaulted() +} + +newtype TSpecialMember = + TMoveConstructor() or + TMoveAssignmentOperator() or + TCopyConstructor() or + TCopyAssignmentOperator() or + TDestructor() + +class AnalyzableClass extends Class { + MoveConstructor moveCtor; + MoveAssignmentOperator moveAssign; + CopyConstructor copyCtor; + CopyAssignmentOperator copyAssign; + Destructor dtor; + + AnalyzableClass() { + moveCtor = this.getAConstructor() and + copyCtor = this.getAConstructor() and + moveAssign = this.getAMemberFunction() and + copyAssign = this.getAMemberFunction() and + dtor = this.getDestructor() + } + + predicate exposes(TSpecialMember m) { + m instanceof TMoveConstructor and moveConstructible() + or + m instanceof TMoveAssignmentOperator and moveAssignable() + or + m instanceof TCopyConstructor and copyConstructible() + or + m instanceof TCopyAssignmentOperator and copyAssignable() + or + m instanceof TDestructor and destructible() + } + + predicate moveConstructible() { isUsable(moveCtor) } + + predicate copyConstructible() { isUsable(copyCtor) } + + predicate moveAssignable() { isUsable(moveAssign) } + + predicate copyAssignable() { isUsable(copyAssign) } + + predicate destructible() { isUsable(dtor) } + + predicate isCustomized(TSpecialMember s) { + s instanceof TMoveConstructor and isMemberCustomized(moveCtor) + or + s instanceof TMoveAssignmentOperator and isMemberCustomized(moveAssign) + or + s instanceof TCopyConstructor and isMemberCustomized(copyCtor) + or + s instanceof TCopyAssignmentOperator and isMemberCustomized(copyAssign) + or + s instanceof TDestructor and isMemberCustomized(dtor) + } +} diff --git a/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.ql b/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.ql index 7e60c14ad0..f1bb8abb73 100644 --- a/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.ql +++ b/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.ql @@ -16,8 +16,91 @@ import cpp import codingstandards.cpp.misra +import AnalyzableClass -from +predicate isCopyEnabled(AnalyzableClass c) { + c.moveConstructible() and + not c.moveAssignable() and + c.copyConstructible() and + not c.copyAssignable() + or + c.moveConstructible() and + c.moveAssignable() and + c.copyConstructible() and + c.copyAssignable() +} + +predicate isMoveOnly(AnalyzableClass c) { + c.moveConstructible() and + not c.moveAssignable() and + not c.copyConstructible() and + not c.copyAssignable() + or + c.moveConstructible() and + c.moveAssignable() and + not c.copyConstructible() and + not c.copyAssignable() +} + +predicate unmovable(AnalyzableClass c) { + not c.moveConstructible() and + not c.moveAssignable() and + not c.copyConstructible() and + not c.copyAssignable() +} + +predicate isValidCategory(AnalyzableClass c) { + isCopyEnabled(c) or + isMoveOnly(c) or + unmovable(c) +} + +predicate violatesCustomizedDestructorRequirements(AnalyzableClass c, string reason) { + c.isCustomized(TDestructor()) and + ( + c.moveConstructible() and + not c.isCustomized(TMoveConstructor()) and + reason = "Move constructor is not customized" + or + c.moveAssignable() and + not c.isCustomized(TMoveAssignmentOperator()) and + reason = "Move assignment operator is not customized" + or + c.copyConstructible() and + not c.isCustomized(TCopyConstructor()) and + reason = "Copy constructor is not customized" + or + c.copyAssignable() and + not c.isCustomized(TCopyAssignmentOperator()) and + reason = "Copy assignment operator is not customized" + ) +} + +predicate violatesInheritanceRequirements(AnalyzableClass c) { + exists(ClassDerivation d | + d.getBaseClass() = c and + d.hasSpecifier("public") + ) and + ( + isMoveOnly(c) and + c.getDestructor().isPublic() and + c.getDestructor().isVirtual() + or + c.getDestructor().isProtected() and + not c.getDestructor().isVirtual() + ) +} + +from AnalyzableClass c, string message where - not isExcluded(x, Classes3Package::improperlyProvidedSpecialMemberFunctionsQuery()) and -select + not isExcluded(c, Classes3Package::improperlyProvidedSpecialMemberFunctionsQuery()) and + ( + not isValidCategory(c) and + message = "Class does not fall into a valid category (unmovable, move-only, or copy-enabled)." + or + violatesCustomizedDestructorRequirements(c, message) + or + violatesInheritanceRequirements(c) and + message = "Class violates inheritance requirements for special member functions." + ) +select c, message diff --git a/cpp/misra/test/rules/RULE-15-0-1/test.cpp b/cpp/misra/test/rules/RULE-15-0-1/test.cpp index 66aadb60da..5d839fda4a 100644 --- a/cpp/misra/test/rules/RULE-15-0-1/test.cpp +++ b/cpp/misra/test/rules/RULE-15-0-1/test.cpp @@ -152,94 +152,112 @@ class PrivateMoveAssign { // NON_COMPLIANT namespace additional_requirements { class CustomizedCopyCtorCompliant { // COMPLIANT +public: DEFINE_ALL_SPECIAL_MEMBERS(CustomizedCopyCtorCompliant, CUSTOMIZED, DEFAULTED, DEFAULTED, DEFAULTED, CUSTOMIZED) }; class CustomizedMoveCtorCompliant { // COMPLIANT +public: DEFINE_ALL_SPECIAL_MEMBERS(CustomizedMoveCtorCompliant, DEFAULTED, CUSTOMIZED, DEFAULTED, DEFAULTED, CUSTOMIZED) }; class CustomizedCopyAssignCompliant { // COMPLIANT +public: DEFINE_ALL_SPECIAL_MEMBERS(CustomizedCopyAssignCompliant, DEFAULTED, DEFAULTED, CUSTOMIZED, DEFAULTED, CUSTOMIZED) }; class CustomizedMoveAssignCompliant { // COMPLIANT +public: DEFINE_ALL_SPECIAL_MEMBERS(CustomizedMoveAssignCompliant, DEFAULTED, DEFAULTED, DEFAULTED, CUSTOMIZED, CUSTOMIZED) }; class CustomizedCopyCtorDefaultDtor { // NON_COMPLIANT +public: DEFINE_ALL_SPECIAL_MEMBERS(CustomizedCopyCtorDefaultDtor, CUSTOMIZED, DEFAULTED, DEFAULTED, DEFAULTED, DEFAULTED) }; class CustomizedCopyCtorDeletedDtor { // NON_COMPLIANT +public: DEFINE_ALL_SPECIAL_MEMBERS(CustomizedCopyCtorDeletedDtor, CUSTOMIZED, DEFAULTED, DEFAULTED, DEFAULTED, DELETED) }; class CustomizedMoveCtorNonCompliant { // NON_COMPLIANT +public: DEFINE_ALL_SPECIAL_MEMBERS(CustomizedMoveCtorNonCompliant, CUSTOMIZED, DEFAULTED, DEFAULTED, DEFAULTED, DEFAULTED) }; class CustomizedCopyAssignNonCompliant { // NON_COMPLIANT +public: DEFINE_ALL_SPECIAL_MEMBERS(CustomizedCopyAssignNonCompliant, DEFAULTED, DEFAULTED, CUSTOMIZED, DEFAULTED, DEFAULTED) }; class CustomizedMoveAssignNonCompliant { // NON_COMPLIANT +public: DEFINE_ALL_SPECIAL_MEMBERS(CustomizedMoveAssignNonCompliant, DEFAULTED, DEFAULTED, DEFAULTED, CUSTOMIZED, DEFAULTED) }; // Move-only with a customized dtor requires customized move operations. class MoveOnlyNotCustomizedNonCompliant { // NON_COMPLIANT +public: DEFINE_ALL_SPECIAL_MEMBERS(MoveOnlyNotCustomizedNonCompliant, DELETED, DEFAULTED, DELETED, DELETED, CUSTOMIZED) }; // Move-only with a customized dtor requires customized move operations. class MoveOnlyAssignableNotCustomizedNonCompliant { // NON_COMPLIANT +public: DEFINE_ALL_SPECIAL_MEMBERS(MoveOnlyAssignableNotCustomizedNonCompliant, DELETED, DEFAULTED, DELETED, DEFAULTED, CUSTOMIZED) }; class MoveOnlyCustomizedCompliant { // COMPLIANT -- customized move +public: DEFINE_ALL_SPECIAL_MEMBERS(MoveOnlyCustomizedCompliant, DELETED, CUSTOMIZED, DELETED, DELETED, CUSTOMIZED) }; class MoveOnlyAssignableCustomizedCompliant { // COMPLIANT -- customized move +public: DEFINE_ALL_SPECIAL_MEMBERS(MoveOnlyAssignableCustomizedCompliant, DELETED, CUSTOMIZED, DELETED, CUSTOMIZED, CUSTOMIZED) }; class MoveOnlyNotCustomizedCompliant { // COMPLIANT -- default dtor +public: DEFINE_ALL_SPECIAL_MEMBERS(MoveOnlyNotCustomizedCompliant, DELETED, DEFAULTED, DELETED, DELETED, DEFAULTED) }; class MoveOnlyAssignableNotCustomizedCompliant { // COMPLIANT -- default dtor +public: DEFINE_ALL_SPECIAL_MEMBERS(MoveOnlyAssignableNotCustomizedCompliant, DELETED, DEFAULTED, DELETED, DEFAULTED, DEFAULTED) }; // Copy-enabled with customized dtor requires customized copy and move class CopyEnabledCustomizedDtorNonCompliant { // NON_COMPLIANT +public: DEFINE_ALL_SPECIAL_MEMBERS(CopyEnabledCustomizedDtorNonCompliant, DEFAULTED, DEFAULTED, DEFAULTED, DEFAULTED, CUSTOMIZED) }; class CopyEnabledNonCustomizedDtorCompliant { // COMPLIANT +public: DEFINE_ALL_SPECIAL_MEMBERS(CopyEnabledNonCustomizedDtorCompliant, DEFAULTED, DEFAULTED, DEFAULTED, DEFAULTED, DEFAULTED) }; class CopyEnabledCustomizedDtorCompliant { // COMPLIANT +public: DEFINE_ALL_SPECIAL_MEMBERS(CopyEnabledCustomizedDtorCompliant, CUSTOMIZED, CUSTOMIZED, CUSTOMIZED, CUSTOMIZED, CUSTOMIZED) }; From a31aac348f1013ac7045ce645bf68c1ecdcc8502 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Wed, 29 Apr 2026 13:12:53 -0700 Subject: [PATCH 03/15] All tests passing for main query --- .../src/rules/RULE-15-0-1/AnalyzableClass.qll | 3 +- ...mproperlyProvidedSpecialMemberFunctions.ql | 67 +++++++++++++------ ...rlyProvidedSpecialMemberFunctions.expected | 31 ++++++++- cpp/misra/test/rules/RULE-15-0-1/test.cpp | 48 ++++++------- 4 files changed, 105 insertions(+), 44 deletions(-) diff --git a/cpp/misra/src/rules/RULE-15-0-1/AnalyzableClass.qll b/cpp/misra/src/rules/RULE-15-0-1/AnalyzableClass.qll index fad93c00af..cddc6ddd8a 100644 --- a/cpp/misra/src/rules/RULE-15-0-1/AnalyzableClass.qll +++ b/cpp/misra/src/rules/RULE-15-0-1/AnalyzableClass.qll @@ -7,7 +7,8 @@ private predicate isUsable(MemberFunction f) { private predicate isMemberCustomized(MemberFunction f) { exists(f.getDefinition()) and - not f.isDefaulted() + not f.isDefaulted() and + not f.isDeleted() } newtype TSpecialMember = diff --git a/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.ql b/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.ql index f1bb8abb73..32721cf58a 100644 --- a/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.ql +++ b/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.ql @@ -42,7 +42,7 @@ predicate isMoveOnly(AnalyzableClass c) { not c.copyAssignable() } -predicate unmovable(AnalyzableClass c) { +predicate isUnmovable(AnalyzableClass c) { not c.moveConstructible() and not c.moveAssignable() and not c.copyConstructible() and @@ -52,7 +52,31 @@ predicate unmovable(AnalyzableClass c) { predicate isValidCategory(AnalyzableClass c) { isCopyEnabled(c) or isMoveOnly(c) or - unmovable(c) + isUnmovable(c) +} + +string specialMemberName(TSpecialMember f) { + f = TCopyConstructor() and result = "copy constructor" + or + f = TMoveConstructor() and result = "move constructor" + or + f = TCopyAssignmentOperator() and result = "copy assignment operator" + or + f = TMoveAssignmentOperator() and result = "move assignment operator" +} + +predicate violatesCustomizedMoveOrCopyRequirements(AnalyzableClass c, string reason) { + not c.isCustomized(TDestructor()) and + exists(string concatenated | + concatenated = + strictconcat(TSpecialMember f | + not f = TDestructor() and + c.isCustomized(f) + | + specialMemberName(f), ", " + ) and + reason = "has customized " + concatenated + ", but does not customize the destructor." + ) } predicate violatesCustomizedDestructorRequirements(AnalyzableClass c, string reason) { @@ -60,47 +84,52 @@ predicate violatesCustomizedDestructorRequirements(AnalyzableClass c, string rea ( c.moveConstructible() and not c.isCustomized(TMoveConstructor()) and - reason = "Move constructor is not customized" + reason = "has customized the destructor, but does not customize the move constructor." or c.moveAssignable() and not c.isCustomized(TMoveAssignmentOperator()) and - reason = "Move assignment operator is not customized" + reason = "has customized the destructor, but does not customize the move assignment operator." or c.copyConstructible() and not c.isCustomized(TCopyConstructor()) and - reason = "Copy constructor is not customized" + reason = "has customized the destructor, but does not customize the copy constructor." or c.copyAssignable() and not c.isCustomized(TCopyAssignmentOperator()) and - reason = "Copy assignment operator is not customized" + reason = "has customized the destructor, but does not customize the copy assignment operator." ) } -predicate violatesInheritanceRequirements(AnalyzableClass c) { +predicate isPublicBase(AnalyzableClass c) { exists(ClassDerivation d | d.getBaseClass() = c and d.hasSpecifier("public") - ) and - ( - isMoveOnly(c) and - c.getDestructor().isPublic() and - c.getDestructor().isVirtual() - or - c.getDestructor().isProtected() and - not c.getDestructor().isVirtual() ) } +predicate satisfiesInheritanceRequirements(AnalyzableClass c) { + not isPublicBase(c) + or + isUnmovable(c) and + c.getDestructor().isPublic() and + c.getDestructor().isVirtual() + or + c.getDestructor().isProtected() and + not c.getDestructor().isVirtual() +} + from AnalyzableClass c, string message where not isExcluded(c, Classes3Package::improperlyProvidedSpecialMemberFunctionsQuery()) and ( not isValidCategory(c) and - message = "Class does not fall into a valid category (unmovable, move-only, or copy-enabled)." + message = "does not fall into a valid category (isUnmovable, move-only, or copy-enabled)." + or + violatesCustomizedMoveOrCopyRequirements(c, message) or violatesCustomizedDestructorRequirements(c, message) or - violatesInheritanceRequirements(c) and - message = "Class violates inheritance requirements for special member functions." + not satisfiesInheritanceRequirements(c) and + message = "violates inheritance requirements for special member functions." ) -select c, message +select c, "Class '" + c.getName() + "' " + message diff --git a/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.expected b/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.expected index 2ec1a0ac6c..1b3577f875 100644 --- a/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.expected +++ b/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.expected @@ -1 +1,30 @@ -No expected results have yet been specified \ No newline at end of file +| test.cpp:69:11:69:12 | C2 | Class 'C2' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:70:11:70:12 | C3 | Class 'C3' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:72:11:72:12 | C5 | Class 'C5' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:74:11:74:12 | C7 | Class 'C7' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:76:11:76:12 | C9 | Class 'C9' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:77:11:77:13 | C10 | Class 'C10' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:78:11:78:13 | C11 | Class 'C11' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:79:11:79:13 | C12 | Class 'C12' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:80:11:80:13 | C13 | Class 'C13' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:81:11:81:13 | C14 | Class 'C14' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:82:11:82:13 | C15 | Class 'C15' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:100:7:100:21 | PrivateCopyCtor | Class 'PrivateCopyCtor' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:113:7:113:21 | PrivateMoveCtor | Class 'PrivateMoveCtor' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:126:7:126:23 | PrivateCopyAssign | Class 'PrivateCopyAssign' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:139:7:139:23 | PrivateMoveAssign | Class 'PrivateMoveAssign' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:179:7:179:45 | CustomizedCopyCtorDefaultedNonCompliant | Class 'CustomizedCopyCtorDefaultedNonCompliant' has customized copy constructor, but does not customize the destructor. | +| test.cpp:186:7:186:43 | CustomizedCopyCtorDeletedNonCompliant | Class 'CustomizedCopyCtorDeletedNonCompliant' has customized copy constructor, but does not customize the destructor. | +| test.cpp:192:7:192:36 | CustomizedMoveCtorNonCompliant | Class 'CustomizedMoveCtorNonCompliant' has customized move constructor, but does not customize the destructor. | +| test.cpp:198:7:198:38 | CustomizedCopyAssignNonCompliant | Class 'CustomizedCopyAssignNonCompliant' has customized copy assignment operator, but does not customize the destructor. | +| test.cpp:204:7:204:38 | CustomizedMoveAssignNonCompliant | Class 'CustomizedMoveAssignNonCompliant' has customized copy constructor, move assignment operator, move constructor, but does not customize the destructor. | +| test.cpp:211:7:211:39 | MoveOnlyNotCustomizedNonCompliant | Class 'MoveOnlyNotCustomizedNonCompliant' has customized the destructor, but does not customize the move constructor. | +| test.cpp:218:7:218:49 | MoveOnlyAssignableNotCustomizedNonCompliant | Class 'MoveOnlyAssignableNotCustomizedNonCompliant' has customized the destructor, but does not customize the move assignment operator. | +| test.cpp:218:7:218:49 | MoveOnlyAssignableNotCustomizedNonCompliant | Class 'MoveOnlyAssignableNotCustomizedNonCompliant' has customized the destructor, but does not customize the move constructor. | +| test.cpp:249:7:249:43 | CopyEnabledCustomizedDtorNonCompliant | Class 'CopyEnabledCustomizedDtorNonCompliant' has customized the destructor, but does not customize the copy assignment operator. | +| test.cpp:249:7:249:43 | CopyEnabledCustomizedDtorNonCompliant | Class 'CopyEnabledCustomizedDtorNonCompliant' has customized the destructor, but does not customize the copy constructor. | +| test.cpp:249:7:249:43 | CopyEnabledCustomizedDtorNonCompliant | Class 'CopyEnabledCustomizedDtorNonCompliant' has customized the destructor, but does not customize the move assignment operator. | +| test.cpp:249:7:249:43 | CopyEnabledCustomizedDtorNonCompliant | Class 'CopyEnabledCustomizedDtorNonCompliant' has customized the destructor, but does not customize the move constructor. | +| test.cpp:268:7:268:33 | UnmovableBaseNonvirtualDtor | Class 'UnmovableBaseNonvirtualDtor' violates inheritance requirements for special member functions. | +| test.cpp:289:7:289:33 | UnmovablePrivateVirtualDtor | Class 'UnmovablePrivateVirtualDtor' violates inheritance requirements for special member functions. | +| test.cpp:316:7:316:30 | BaseVirtualProtectedDtor | Class 'BaseVirtualProtectedDtor' violates inheritance requirements for special member functions. | diff --git a/cpp/misra/test/rules/RULE-15-0-1/test.cpp b/cpp/misra/test/rules/RULE-15-0-1/test.cpp index 5d839fda4a..2e9cd8cce3 100644 --- a/cpp/misra/test/rules/RULE-15-0-1/test.cpp +++ b/cpp/misra/test/rules/RULE-15-0-1/test.cpp @@ -151,46 +151,48 @@ class PrivateMoveAssign { // NON_COMPLIANT namespace additional_requirements { -class CustomizedCopyCtorCompliant { // COMPLIANT +// A class with a customized copy constructor requires a customized destructor +class CustomizedMoveCtorCompliant { // COMPLIANT public: - DEFINE_ALL_SPECIAL_MEMBERS(CustomizedCopyCtorCompliant, CUSTOMIZED, DEFAULTED, - DEFAULTED, DEFAULTED, CUSTOMIZED) + DEFINE_ALL_SPECIAL_MEMBERS(CustomizedMoveCtorCompliant, DELETED, CUSTOMIZED, + DELETED, DELETED, CUSTOMIZED) }; -class CustomizedMoveCtorCompliant { // COMPLIANT +class CustomizedCtorsCompliant { // COMPLIANT public: - DEFINE_ALL_SPECIAL_MEMBERS(CustomizedMoveCtorCompliant, DEFAULTED, CUSTOMIZED, - DEFAULTED, DEFAULTED, CUSTOMIZED) + DEFINE_ALL_SPECIAL_MEMBERS(CustomizedCtorsCompliant, CUSTOMIZED, CUSTOMIZED, + DELETED, DELETED, CUSTOMIZED) }; -class CustomizedCopyAssignCompliant { // COMPLIANT +class CustomizedMoveAssignCompliant { // COMPLIANT public: - DEFINE_ALL_SPECIAL_MEMBERS(CustomizedCopyAssignCompliant, DEFAULTED, - DEFAULTED, CUSTOMIZED, DEFAULTED, CUSTOMIZED) + DEFINE_ALL_SPECIAL_MEMBERS(CustomizedMoveAssignCompliant, DELETED, CUSTOMIZED, + DELETED, CUSTOMIZED, CUSTOMIZED) }; -class CustomizedMoveAssignCompliant { // COMPLIANT +class CustomizedAssignsCompliant { // COMPLIANT public: - DEFINE_ALL_SPECIAL_MEMBERS(CustomizedMoveAssignCompliant, DEFAULTED, - DEFAULTED, DEFAULTED, CUSTOMIZED, CUSTOMIZED) + DEFINE_ALL_SPECIAL_MEMBERS(CustomizedAssignsCompliant, CUSTOMIZED, CUSTOMIZED, + CUSTOMIZED, CUSTOMIZED, CUSTOMIZED) }; -class CustomizedCopyCtorDefaultDtor { // NON_COMPLIANT +class CustomizedCopyCtorDefaultedNonCompliant { // NON_COMPLIANT public: - DEFINE_ALL_SPECIAL_MEMBERS(CustomizedCopyCtorDefaultDtor, CUSTOMIZED, - DEFAULTED, DEFAULTED, DEFAULTED, DEFAULTED) + DEFINE_ALL_SPECIAL_MEMBERS(CustomizedCopyCtorDefaultedNonCompliant, + CUSTOMIZED, DEFAULTED, DEFAULTED, DEFAULTED, + DEFAULTED) }; -class CustomizedCopyCtorDeletedDtor { // NON_COMPLIANT +class CustomizedCopyCtorDeletedNonCompliant { // NON_COMPLIANT public: - DEFINE_ALL_SPECIAL_MEMBERS(CustomizedCopyCtorDeletedDtor, CUSTOMIZED, + DEFINE_ALL_SPECIAL_MEMBERS(CustomizedCopyCtorDeletedNonCompliant, CUSTOMIZED, DEFAULTED, DEFAULTED, DEFAULTED, DELETED) }; class CustomizedMoveCtorNonCompliant { // NON_COMPLIANT public: - DEFINE_ALL_SPECIAL_MEMBERS(CustomizedMoveCtorNonCompliant, CUSTOMIZED, - DEFAULTED, DEFAULTED, DEFAULTED, DEFAULTED) + DEFINE_ALL_SPECIAL_MEMBERS(CustomizedMoveCtorNonCompliant, DEFAULTED, + CUSTOMIZED, DEFAULTED, DEFAULTED, DEFAULTED) }; class CustomizedCopyAssignNonCompliant { // NON_COMPLIANT @@ -201,8 +203,8 @@ class CustomizedCopyAssignNonCompliant { // NON_COMPLIANT class CustomizedMoveAssignNonCompliant { // NON_COMPLIANT public: - DEFINE_ALL_SPECIAL_MEMBERS(CustomizedMoveAssignNonCompliant, DEFAULTED, - DEFAULTED, DEFAULTED, CUSTOMIZED, DEFAULTED) + DEFINE_ALL_SPECIAL_MEMBERS(CustomizedMoveAssignNonCompliant, CUSTOMIZED, + CUSTOMIZED, DEFAULTED, CUSTOMIZED, DEFAULTED) }; // Move-only with a customized dtor requires customized move operations. @@ -302,7 +304,7 @@ class BaseProtectedDtor { // COMPLIANT public: COPY_CTOR(BaseProtectedDtor) = default; MOVE_CTOR(BaseProtectedDtor) = default; - COPY_ASSIGN(BaseProtectedDtor) = default; + COPY_ASSIGN(BaseProtectedDtor) = delete; MOVE_ASSIGN(BaseProtectedDtor) = delete; protected: @@ -315,7 +317,7 @@ class BaseVirtualProtectedDtor { // NON_COMPLIANT public: COPY_CTOR(BaseVirtualProtectedDtor) = default; MOVE_CTOR(BaseVirtualProtectedDtor) = default; - COPY_ASSIGN(BaseVirtualProtectedDtor) = default; + COPY_ASSIGN(BaseVirtualProtectedDtor) = delete; MOVE_ASSIGN(BaseVirtualProtectedDtor) = delete; protected: From 057e33b321cb9493e8184b7c7b49df97140d068b Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Wed, 29 Apr 2026 14:42:56 -0700 Subject: [PATCH 04/15] Implement audit query --- .../src/rules/RULE-15-0-1/AnalyzableClass.qll | 3 +- ...erlyProvidedSpecialMemberFunctionsAudit.ql | 31 +++++++++- ...rlyProvidedSpecialMemberFunctions.expected | 60 +++++++++---------- ...ovidedSpecialMemberFunctionsAudit.expected | 9 ++- cpp/misra/test/rules/RULE-15-0-1/test.cpp | 55 +++++++++++++++++ 5 files changed, 123 insertions(+), 35 deletions(-) diff --git a/cpp/misra/src/rules/RULE-15-0-1/AnalyzableClass.qll b/cpp/misra/src/rules/RULE-15-0-1/AnalyzableClass.qll index cddc6ddd8a..bc9cfed536 100644 --- a/cpp/misra/src/rules/RULE-15-0-1/AnalyzableClass.qll +++ b/cpp/misra/src/rules/RULE-15-0-1/AnalyzableClass.qll @@ -8,7 +8,8 @@ private predicate isUsable(MemberFunction f) { private predicate isMemberCustomized(MemberFunction f) { exists(f.getDefinition()) and not f.isDefaulted() and - not f.isDeleted() + not f.isDeleted() and + not f.isCompilerGenerated() } newtype TSpecialMember = diff --git a/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.ql b/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.ql index 104ca228af..37da8f087e 100644 --- a/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.ql +++ b/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.ql @@ -17,8 +17,33 @@ import cpp import codingstandards.cpp.misra +import AnalyzableClass -from +string missingKind(Class c) { + not c.getAConstructor() instanceof MoveConstructor and + result = "move constructor" + or + not c.getAMemberFunction() instanceof MoveAssignmentOperator and + result = "move assignment operator" + or + not c.getAConstructor() instanceof CopyConstructor and + result = "copy constructor" + or + not c.getAMemberFunction() instanceof CopyAssignmentOperator and + result = "copy assignment operator" + or + not c.getAMemberFunction() instanceof Destructor and + result = "destructor" +} + +string missingKinds(Class c) { result = concat(missingKind(c), " and ") } + +from Class c, string kinds where - not isExcluded(x, Classes3Package::improperlyProvidedSpecialMemberFunctionsAuditQuery()) and -select + not isExcluded(c, Classes3Package::improperlyProvidedSpecialMemberFunctionsAuditQuery()) and + not c instanceof AnalyzableClass and + not c.isPod() and + kinds = missingKinds(c) +select c, + "Class '" + c.getName() + "' is not analyzable because the " + kinds + + " are not present in the CodeQL database." diff --git a/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.expected b/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.expected index 1b3577f875..bdfa47f75f 100644 --- a/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.expected +++ b/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.expected @@ -1,30 +1,30 @@ -| test.cpp:69:11:69:12 | C2 | Class 'C2' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | -| test.cpp:70:11:70:12 | C3 | Class 'C3' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | -| test.cpp:72:11:72:12 | C5 | Class 'C5' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | -| test.cpp:74:11:74:12 | C7 | Class 'C7' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | -| test.cpp:76:11:76:12 | C9 | Class 'C9' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | -| test.cpp:77:11:77:13 | C10 | Class 'C10' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | -| test.cpp:78:11:78:13 | C11 | Class 'C11' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | -| test.cpp:79:11:79:13 | C12 | Class 'C12' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | -| test.cpp:80:11:80:13 | C13 | Class 'C13' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | -| test.cpp:81:11:81:13 | C14 | Class 'C14' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | -| test.cpp:82:11:82:13 | C15 | Class 'C15' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | -| test.cpp:100:7:100:21 | PrivateCopyCtor | Class 'PrivateCopyCtor' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | -| test.cpp:113:7:113:21 | PrivateMoveCtor | Class 'PrivateMoveCtor' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | -| test.cpp:126:7:126:23 | PrivateCopyAssign | Class 'PrivateCopyAssign' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | -| test.cpp:139:7:139:23 | PrivateMoveAssign | Class 'PrivateMoveAssign' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | -| test.cpp:179:7:179:45 | CustomizedCopyCtorDefaultedNonCompliant | Class 'CustomizedCopyCtorDefaultedNonCompliant' has customized copy constructor, but does not customize the destructor. | -| test.cpp:186:7:186:43 | CustomizedCopyCtorDeletedNonCompliant | Class 'CustomizedCopyCtorDeletedNonCompliant' has customized copy constructor, but does not customize the destructor. | -| test.cpp:192:7:192:36 | CustomizedMoveCtorNonCompliant | Class 'CustomizedMoveCtorNonCompliant' has customized move constructor, but does not customize the destructor. | -| test.cpp:198:7:198:38 | CustomizedCopyAssignNonCompliant | Class 'CustomizedCopyAssignNonCompliant' has customized copy assignment operator, but does not customize the destructor. | -| test.cpp:204:7:204:38 | CustomizedMoveAssignNonCompliant | Class 'CustomizedMoveAssignNonCompliant' has customized copy constructor, move assignment operator, move constructor, but does not customize the destructor. | -| test.cpp:211:7:211:39 | MoveOnlyNotCustomizedNonCompliant | Class 'MoveOnlyNotCustomizedNonCompliant' has customized the destructor, but does not customize the move constructor. | -| test.cpp:218:7:218:49 | MoveOnlyAssignableNotCustomizedNonCompliant | Class 'MoveOnlyAssignableNotCustomizedNonCompliant' has customized the destructor, but does not customize the move assignment operator. | -| test.cpp:218:7:218:49 | MoveOnlyAssignableNotCustomizedNonCompliant | Class 'MoveOnlyAssignableNotCustomizedNonCompliant' has customized the destructor, but does not customize the move constructor. | -| test.cpp:249:7:249:43 | CopyEnabledCustomizedDtorNonCompliant | Class 'CopyEnabledCustomizedDtorNonCompliant' has customized the destructor, but does not customize the copy assignment operator. | -| test.cpp:249:7:249:43 | CopyEnabledCustomizedDtorNonCompliant | Class 'CopyEnabledCustomizedDtorNonCompliant' has customized the destructor, but does not customize the copy constructor. | -| test.cpp:249:7:249:43 | CopyEnabledCustomizedDtorNonCompliant | Class 'CopyEnabledCustomizedDtorNonCompliant' has customized the destructor, but does not customize the move assignment operator. | -| test.cpp:249:7:249:43 | CopyEnabledCustomizedDtorNonCompliant | Class 'CopyEnabledCustomizedDtorNonCompliant' has customized the destructor, but does not customize the move constructor. | -| test.cpp:268:7:268:33 | UnmovableBaseNonvirtualDtor | Class 'UnmovableBaseNonvirtualDtor' violates inheritance requirements for special member functions. | -| test.cpp:289:7:289:33 | UnmovablePrivateVirtualDtor | Class 'UnmovablePrivateVirtualDtor' violates inheritance requirements for special member functions. | -| test.cpp:316:7:316:30 | BaseVirtualProtectedDtor | Class 'BaseVirtualProtectedDtor' violates inheritance requirements for special member functions. | +| test.cpp:71:11:71:12 | C2 | Class 'C2' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:72:11:72:12 | C3 | Class 'C3' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:74:11:74:12 | C5 | Class 'C5' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:76:11:76:12 | C7 | Class 'C7' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:78:11:78:12 | C9 | Class 'C9' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:79:11:79:13 | C10 | Class 'C10' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:80:11:80:13 | C11 | Class 'C11' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:81:11:81:13 | C12 | Class 'C12' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:82:11:82:13 | C13 | Class 'C13' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:83:11:83:13 | C14 | Class 'C14' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:84:11:84:13 | C15 | Class 'C15' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:102:7:102:21 | PrivateCopyCtor | Class 'PrivateCopyCtor' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:115:7:115:21 | PrivateMoveCtor | Class 'PrivateMoveCtor' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:128:7:128:23 | PrivateCopyAssign | Class 'PrivateCopyAssign' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:141:7:141:23 | PrivateMoveAssign | Class 'PrivateMoveAssign' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:181:7:181:45 | CustomizedCopyCtorDefaultedNonCompliant | Class 'CustomizedCopyCtorDefaultedNonCompliant' has customized copy constructor, but does not customize the destructor. | +| test.cpp:188:7:188:43 | CustomizedCopyCtorDeletedNonCompliant | Class 'CustomizedCopyCtorDeletedNonCompliant' has customized copy constructor, but does not customize the destructor. | +| test.cpp:194:7:194:36 | CustomizedMoveCtorNonCompliant | Class 'CustomizedMoveCtorNonCompliant' has customized move constructor, but does not customize the destructor. | +| test.cpp:200:7:200:38 | CustomizedCopyAssignNonCompliant | Class 'CustomizedCopyAssignNonCompliant' has customized copy assignment operator, but does not customize the destructor. | +| test.cpp:206:7:206:38 | CustomizedMoveAssignNonCompliant | Class 'CustomizedMoveAssignNonCompliant' has customized copy constructor, move assignment operator, move constructor, but does not customize the destructor. | +| test.cpp:213:7:213:39 | MoveOnlyNotCustomizedNonCompliant | Class 'MoveOnlyNotCustomizedNonCompliant' has customized the destructor, but does not customize the move constructor. | +| test.cpp:220:7:220:49 | MoveOnlyAssignableNotCustomizedNonCompliant | Class 'MoveOnlyAssignableNotCustomizedNonCompliant' has customized the destructor, but does not customize the move assignment operator. | +| test.cpp:220:7:220:49 | MoveOnlyAssignableNotCustomizedNonCompliant | Class 'MoveOnlyAssignableNotCustomizedNonCompliant' has customized the destructor, but does not customize the move constructor. | +| test.cpp:251:7:251:43 | CopyEnabledCustomizedDtorNonCompliant | Class 'CopyEnabledCustomizedDtorNonCompliant' has customized the destructor, but does not customize the copy assignment operator. | +| test.cpp:251:7:251:43 | CopyEnabledCustomizedDtorNonCompliant | Class 'CopyEnabledCustomizedDtorNonCompliant' has customized the destructor, but does not customize the copy constructor. | +| test.cpp:251:7:251:43 | CopyEnabledCustomizedDtorNonCompliant | Class 'CopyEnabledCustomizedDtorNonCompliant' has customized the destructor, but does not customize the move assignment operator. | +| test.cpp:251:7:251:43 | CopyEnabledCustomizedDtorNonCompliant | Class 'CopyEnabledCustomizedDtorNonCompliant' has customized the destructor, but does not customize the move constructor. | +| test.cpp:270:7:270:33 | UnmovableBaseNonvirtualDtor | Class 'UnmovableBaseNonvirtualDtor' violates inheritance requirements for special member functions. | +| test.cpp:291:7:291:33 | UnmovablePrivateVirtualDtor | Class 'UnmovablePrivateVirtualDtor' violates inheritance requirements for special member functions. | +| test.cpp:318:7:318:30 | BaseVirtualProtectedDtor | Class 'BaseVirtualProtectedDtor' violates inheritance requirements for special member functions. | diff --git a/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.expected b/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.expected index 2ec1a0ac6c..99faa7d929 100644 --- a/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.expected +++ b/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.expected @@ -1 +1,8 @@ -No expected results have yet been specified \ No newline at end of file +| test.cpp:276:7:276:32 | UnmovableNonvirtualDerived | Class 'UnmovableNonvirtualDerived' is not analyzable because the destructor and move assignment operator and move constructor are not present in the CodeQL database. | +| test.cpp:288:7:288:39 | UnmovableDerivedPublicVirtualDtor | Class 'UnmovableDerivedPublicVirtualDtor' is not analyzable because the move assignment operator and move constructor are not present in the CodeQL database. | +| test.cpp:302:7:302:40 | UnmovablePrivateVirtualDtorDerived | Class 'UnmovablePrivateVirtualDtorDerived' is not analyzable because the move assignment operator and move constructor are not present in the CodeQL database. | +| test.cpp:316:7:316:26 | ProtectedDtorDerived | Class 'ProtectedDtorDerived' is not analyzable because the destructor and move assignment operator are not present in the CodeQL database. | +| test.cpp:329:7:329:33 | VirtualProtectedDtorDerived | Class 'VirtualProtectedDtorDerived' is not analyzable because the move assignment operator are not present in the CodeQL database. | +| test.cpp:342:8:342:19 | TrivialClass | Class 'TrivialClass' is not analyzable because the destructor and move assignment operator and move constructor are not present in the CodeQL database. | +| test.cpp:348:7:348:21 | NonTrivialClass | Class 'NonTrivialClass' is not analyzable because the move assignment operator and move constructor are not present in the CodeQL database. | +| test.cpp:362:7:362:14 | CopyOnly | Class 'CopyOnly' is not analyzable because the destructor are not present in the CodeQL database. | diff --git a/cpp/misra/test/rules/RULE-15-0-1/test.cpp b/cpp/misra/test/rules/RULE-15-0-1/test.cpp index 2e9cd8cce3..7e143204e2 100644 --- a/cpp/misra/test/rules/RULE-15-0-1/test.cpp +++ b/cpp/misra/test/rules/RULE-15-0-1/test.cpp @@ -1,3 +1,5 @@ +#include + namespace helpers { void f(); } @@ -329,3 +331,56 @@ class VirtualProtectedDtorDerived : public BaseVirtualProtectedDtor {}; } // namespace additional_requirements } // namespace fully_specified + +namespace audit_results { + +struct PodClass { // COMPLIANT - we know PODs are OK. + int x; + int y; +}; + +struct TrivialClass { // NON_COMPLIANT - audit result + int x; + int y; + COPY_CTOR(TrivialClass) = default; +}; + +class NonTrivialClass { // NON_COMPLIANT - audit result + int x; + int y; + +public: + COPY_CTOR(NonTrivialClass) { + x = 1; + y = 2; + } + ~NonTrivialClass() { x = 1; } +}; + +// This class is not a valid category but hard to analyze in the general case. +// This should not be reported as a violation except by the audit query. +class CopyOnly { // NON_COMPLIANT - audit result + COPY_CTOR(CopyOnly) = default; + MOVE_CTOR(CopyOnly) = delete; + MOVE_ASSIGN(CopyOnly) = delete; +}; + +// this class should not appear in the audit results, because we have all +// members in the database even though they aren't all explicitly declared +class OdrUsedMoveEnabled { // COMPLIANT + NonTrivialClass x; + +public: + // copy operations are generated as deleted + MOVE_CTOR(OdrUsedMoveEnabled) = default; + MOVE_ASSIGN(OdrUsedMoveEnabled) = default; + // destructor is generated as defaulted since it's non trivial and ODR-used. +}; + +void f(OdrUsedMoveEnabled o) { + // This function ensures the non-trivial destructor is ODR-used. + OdrUsedMoveEnabled o3 = std::move(o); + o3 = std::move(o); +} + +} // namespace audit_results \ No newline at end of file From 3584a15087d261d08f8047766d4820097dc627f2 Mon Sep 17 00:00:00 2001 From: jeongsoolee09 Date: Wed, 29 Apr 2026 18:06:22 -0400 Subject: [PATCH 05/15] Add some cases --- cpp/misra/test/rules/RULE-15-0-1/test.cpp | 99 ++++++++++++++++++----- 1 file changed, 78 insertions(+), 21 deletions(-) diff --git a/cpp/misra/test/rules/RULE-15-0-1/test.cpp b/cpp/misra/test/rules/RULE-15-0-1/test.cpp index 7e143204e2..ac998b45c0 100644 --- a/cpp/misra/test/rules/RULE-15-0-1/test.cpp +++ b/cpp/misra/test/rules/RULE-15-0-1/test.cpp @@ -40,6 +40,7 @@ void f(); // special member function. #define DEFAULTED = default; #define DELETED = delete; +#define NOT_DECLARED ; // null statement #define CUSTOMIZED { helpers::f(); } // clang-format on @@ -154,118 +155,174 @@ class PrivateMoveAssign { // NON_COMPLIANT namespace additional_requirements { // A class with a customized copy constructor requires a customized destructor -class CustomizedMoveCtorCompliant { // COMPLIANT +class CustomizedMoveCtorCompliant { // COMPLIANT: move-only (1) public: DEFINE_ALL_SPECIAL_MEMBERS(CustomizedMoveCtorCompliant, DELETED, CUSTOMIZED, DELETED, DELETED, CUSTOMIZED) }; -class CustomizedCtorsCompliant { // COMPLIANT +class CustomizedCtorsCompliant { // COMPLIANT: copy-enabled (1) public: DEFINE_ALL_SPECIAL_MEMBERS(CustomizedCtorsCompliant, CUSTOMIZED, CUSTOMIZED, DELETED, DELETED, CUSTOMIZED) }; -class CustomizedMoveAssignCompliant { // COMPLIANT +class CustomizedMoveAssignCompliant { // COMPLIANT: move-only (2) public: DEFINE_ALL_SPECIAL_MEMBERS(CustomizedMoveAssignCompliant, DELETED, CUSTOMIZED, DELETED, CUSTOMIZED, CUSTOMIZED) }; -class CustomizedAssignsCompliant { // COMPLIANT +class CustomizedAssignsCompliant { // COMPLIANT: copy-enabled (2) public: DEFINE_ALL_SPECIAL_MEMBERS(CustomizedAssignsCompliant, CUSTOMIZED, CUSTOMIZED, CUSTOMIZED, CUSTOMIZED, CUSTOMIZED) }; -class CustomizedCopyCtorDefaultedNonCompliant { // NON_COMPLIANT +class CustomizedCopyCtorDefaultedNonCompliant { // NON_COMPLIANT: copy-enabled (2) public: DEFINE_ALL_SPECIAL_MEMBERS(CustomizedCopyCtorDefaultedNonCompliant, CUSTOMIZED, DEFAULTED, DEFAULTED, DEFAULTED, DEFAULTED) }; -class CustomizedCopyCtorDeletedNonCompliant { // NON_COMPLIANT +class CustomizedCopyCtorDeletedNonCompliant { // NON_COMPLIANT: copy-enabled (2) public: DEFINE_ALL_SPECIAL_MEMBERS(CustomizedCopyCtorDeletedNonCompliant, CUSTOMIZED, DEFAULTED, DEFAULTED, DEFAULTED, DELETED) }; -class CustomizedMoveCtorNonCompliant { // NON_COMPLIANT +class CustomizedMoveCtorNonCompliant { // NON_COMPLIANT: copy-enabled (2) public: DEFINE_ALL_SPECIAL_MEMBERS(CustomizedMoveCtorNonCompliant, DEFAULTED, CUSTOMIZED, DEFAULTED, DEFAULTED, DEFAULTED) }; -class CustomizedCopyAssignNonCompliant { // NON_COMPLIANT +class CustomizedCopyAssignNonCompliant { // NON_COMPLIANT: copy-enabled (2) public: DEFINE_ALL_SPECIAL_MEMBERS(CustomizedCopyAssignNonCompliant, DEFAULTED, DEFAULTED, CUSTOMIZED, DEFAULTED, DEFAULTED) }; -class CustomizedMoveAssignNonCompliant { // NON_COMPLIANT +class CustomizedMoveAssignNonCompliant { // NON_COMPLIANT: copy-enabled (2) public: DEFINE_ALL_SPECIAL_MEMBERS(CustomizedMoveAssignNonCompliant, CUSTOMIZED, CUSTOMIZED, DEFAULTED, CUSTOMIZED, DEFAULTED) }; -// Move-only with a customized dtor requires customized move operations. -class MoveOnlyNotCustomizedNonCompliant { // NON_COMPLIANT +// Move-only with a customized dtor requires customized move constructor. +class MoveOnlyNotCustomizedNonCompliant { // NON_COMPLIANT: move-only (1) public: DEFINE_ALL_SPECIAL_MEMBERS(MoveOnlyNotCustomizedNonCompliant, DELETED, DEFAULTED, DELETED, DELETED, CUSTOMIZED) }; -// Move-only with a customized dtor requires customized move operations. -class MoveOnlyAssignableNotCustomizedNonCompliant { // NON_COMPLIANT +// Move-only with a customized dtor requires customized move assignment +// operator. +class MoveOnlyAssignableNotCustomizedNonCompliant { // NON_COMPLIANT: move-only (2) public: DEFINE_ALL_SPECIAL_MEMBERS(MoveOnlyAssignableNotCustomizedNonCompliant, DELETED, DEFAULTED, DELETED, DEFAULTED, CUSTOMIZED) }; -class MoveOnlyCustomizedCompliant { // COMPLIANT -- customized move +class MoveOnlyCustomizedCompliant { // COMPLIANT -- customized move: move-only (1) public: DEFINE_ALL_SPECIAL_MEMBERS(MoveOnlyCustomizedCompliant, DELETED, CUSTOMIZED, DELETED, DELETED, CUSTOMIZED) }; -class MoveOnlyAssignableCustomizedCompliant { // COMPLIANT -- customized move +class MoveOnlyAssignableCustomizedCompliant { // COMPLIANT -- customized move: move-only (2) public: DEFINE_ALL_SPECIAL_MEMBERS(MoveOnlyAssignableCustomizedCompliant, DELETED, CUSTOMIZED, DELETED, CUSTOMIZED, CUSTOMIZED) }; -class MoveOnlyNotCustomizedCompliant { // COMPLIANT -- default dtor +class MoveOnlyNotCustomizedCompliant { // COMPLIANT -- default dtor: move-only (1) public: DEFINE_ALL_SPECIAL_MEMBERS(MoveOnlyNotCustomizedCompliant, DELETED, DEFAULTED, DELETED, DELETED, DEFAULTED) }; -class MoveOnlyAssignableNotCustomizedCompliant { // COMPLIANT -- default dtor +class MoveOnlyAssignableNotCustomizedCompliant { // COMPLIANT -- default dtor: move-only (2) public: DEFINE_ALL_SPECIAL_MEMBERS(MoveOnlyAssignableNotCustomizedCompliant, DELETED, DEFAULTED, DELETED, DEFAULTED, DEFAULTED) }; // Copy-enabled with customized dtor requires customized copy and move -class CopyEnabledCustomizedDtorNonCompliant { // NON_COMPLIANT +class CopyEnabledCustomizedDtorNonCompliant { // NON_COMPLIANT: copy-enabled (2) public: DEFINE_ALL_SPECIAL_MEMBERS(CopyEnabledCustomizedDtorNonCompliant, DEFAULTED, DEFAULTED, DEFAULTED, DEFAULTED, CUSTOMIZED) }; -class CopyEnabledNonCustomizedDtorCompliant { // COMPLIANT +class CopyEnabledNonCustomizedDtorCompliant { // COMPLIANT: copy-enabled (2) public: DEFINE_ALL_SPECIAL_MEMBERS(CopyEnabledNonCustomizedDtorCompliant, DEFAULTED, DEFAULTED, DEFAULTED, DEFAULTED, DEFAULTED) }; -class CopyEnabledCustomizedDtorCompliant { // COMPLIANT +class CopyEnabledCustomizedDtorCompliant1 { // COMPLIANT: copy-enabled (2) public: - DEFINE_ALL_SPECIAL_MEMBERS(CopyEnabledCustomizedDtorCompliant, CUSTOMIZED, + DEFINE_ALL_SPECIAL_MEMBERS(CopyEnabledCustomizedDtorCompliant1, CUSTOMIZED, CUSTOMIZED, CUSTOMIZED, CUSTOMIZED, CUSTOMIZED) }; +class CopyEnabledCustomizedDtorCompliant2 { // COMPLIANT: copy-enabled (2) +public: + DEFINE_ALL_SPECIAL_MEMBERS(CopyEnabledCustomizedDtorCompliant2, CUSTOMIZED, + DEFAULTED, CUSTOMIZED, DEFAULTED, CUSTOMIZED) +}; + +class CopyAssignableCustomizedDtorCompliant1 { // COMPLIANT: copy-assignable class with both move operations customized +public: + DEFINE_ALL_SPECIAL_MEMBERS(CopyAssignableCustomizedDtorCompliant1, DEFAULTED, + CUSTOMIZED, CUSTOMIZED, CUSTOMIZED, CUSTOMIZED) +}; + +class CopyAssignableCustomizedDtorCompliant2 { // COMPLIANT: copy-assignable class with both move operations not declared +public: + DEFINE_ALL_SPECIAL_MEMBERS(CopyAssignableCustomizedDtorCompliant2, DEFAULTED, + NOT_DECLARED, CUSTOMIZED, NOT_DECLARED, CUSTOMIZED) +}; + +class CopyAssignableCustomizedDtorNonCompliant1 { // NON_COMPLIANT: copy-assignable class with only one of move operations not declared +public: + DEFINE_ALL_SPECIAL_MEMBERS(CopyAssignableCustomizedDtorNonCompliant1, CUSTOMIZED, + CUSTOMIZED, CUSTOMIZED, NOT_DECLARED, CUSTOMIZED) +}; + +class CopyAssignableCustomizedDtorNonCompliant2 { // NON_COMPLIANT: copy-assignable class with only one of move operations not declared +public: + DEFINE_ALL_SPECIAL_MEMBERS(CopyAssignableCustomizedDtorNonCompliant2, CUSTOMIZED, + NOT_DECLARED, CUSTOMIZED, CUSTOMIZED, CUSTOMIZED) +}; + +class CopyAssignableCustomizedDtorNonCompliant3 { // NON_COMPLIANT: copy-assignable class with only one of move operations not declared +public: + DEFINE_ALL_SPECIAL_MEMBERS(CopyAssignableCustomizedDtorNonCompliant3, CUSTOMIZED, + CUSTOMIZED, CUSTOMIZED, NOT_DECLARED, CUSTOMIZED) +}; + +class CopyAssignableCustomizedDtorNonCompliant4 { // NON_COMPLIANT: copy-assignable class with only one of move operations not declared +public: + DEFINE_ALL_SPECIAL_MEMBERS(CopyAssignableCustomizedDtorNonCompliant4, CUSTOMIZED, + NOT_DECLARED, CUSTOMIZED, CUSTOMIZED, CUSTOMIZED) +}; + +class CopyAssignableCustomizedDtorNonCompliant5 { // NON_COMPLIANT: copy-assignable class with only one of move operations not declared +public: + DEFINE_ALL_SPECIAL_MEMBERS(CopyAssignableCustomizedDtorNonCompliant5, CUSTOMIZED, + DEFAULTED, CUSTOMIZED, NOT_DECLARED, CUSTOMIZED) +}; + +class CopyAssignableCustomizedDtorNonCompliant6 { // NON_COMPLIANT: copy-assignable class with only one of move operations not declared +public: + DEFINE_ALL_SPECIAL_MEMBERS(CopyAssignableCustomizedDtorNonCompliant6, CUSTOMIZED, + NOT_DECLARED, CUSTOMIZED, DEFAULTED, CUSTOMIZED) +}; + + // A public unmovable base class shall have a public virtual destructor class UnmovableBaseNonvirtualDtor { // NON_COMPLIANT public: From d37827aa3e8469996743bd1053bf1e3de7ea2df8 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Wed, 29 Apr 2026 16:33:40 -0700 Subject: [PATCH 06/15] Use correct audit tag --- .../ImproperlyProvidedSpecialMemberFunctionsAudit.ql | 2 +- rule_packages/cpp/Classes3.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.ql b/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.ql index 37da8f087e..1c10475ae8 100644 --- a/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.ql +++ b/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.ql @@ -9,7 +9,7 @@ * @tags external/misra/id/rule-15-0-1 * scope/single-translation-unit * correctness - * audit + * external/misra/audit * maintainability * external/misra/enforcement/decidable * external/misra/obligation/required diff --git a/rule_packages/cpp/Classes3.json b/rule_packages/cpp/Classes3.json index ce8c68216a..c2f4384729 100644 --- a/rule_packages/cpp/Classes3.json +++ b/rule_packages/cpp/Classes3.json @@ -42,7 +42,7 @@ "tags": [ "scope/single-translation-unit", "correctness", - "audit", + "external/misra/audit", "maintainability" ], "implementation_scope": { From 5000f27f139626688f69956d53dbbc3e4d2147bf Mon Sep 17 00:00:00 2001 From: jeongsoolee09 Date: Wed, 29 Apr 2026 19:48:53 -0400 Subject: [PATCH 07/15] Just omit functions by hand that's supposed to not be declared --- cpp/misra/test/rules/RULE-15-0-1/test.cpp | 55 ++++++++++++++++------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/cpp/misra/test/rules/RULE-15-0-1/test.cpp b/cpp/misra/test/rules/RULE-15-0-1/test.cpp index ac998b45c0..7a43f3d70c 100644 --- a/cpp/misra/test/rules/RULE-15-0-1/test.cpp +++ b/cpp/misra/test/rules/RULE-15-0-1/test.cpp @@ -40,7 +40,6 @@ void f(); // special member function. #define DEFAULTED = default; #define DELETED = delete; -#define NOT_DECLARED ; // null statement #define CUSTOMIZED { helpers::f(); } // clang-format on @@ -282,47 +281,69 @@ class CopyAssignableCustomizedDtorCompliant1 { // COMPLIANT: copy-assignable cl class CopyAssignableCustomizedDtorCompliant2 { // COMPLIANT: copy-assignable class with both move operations not declared public: - DEFINE_ALL_SPECIAL_MEMBERS(CopyAssignableCustomizedDtorCompliant2, DEFAULTED, - NOT_DECLARED, CUSTOMIZED, NOT_DECLARED, CUSTOMIZED) + COPY_CTOR(CopyAssignableCustomizedDtorCompliant2) DEFAULTED + // Move constructor is not declared + COPY_ASSIGN(CopyAssignableCustomizedDtorCompliant2) CUSTOMIZED + // Move assignment operator is not declared + DTOR(CopyAssignableCustomizedDtorCompliant2) CUSTOMIZED }; class CopyAssignableCustomizedDtorNonCompliant1 { // NON_COMPLIANT: copy-assignable class with only one of move operations not declared public: - DEFINE_ALL_SPECIAL_MEMBERS(CopyAssignableCustomizedDtorNonCompliant1, CUSTOMIZED, - CUSTOMIZED, CUSTOMIZED, NOT_DECLARED, CUSTOMIZED) + COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant1) CUSTOMIZED + MOVE_CTOR(CopyAssignableCustomizedDtorNonCompliant1) CUSTOMIZED + COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant1) CUSTOMIZED + // Move assignment operator is not declared + DTOR(CopyAssignableCustomizedDtorNonCompliant1) CUSTOMIZED }; class CopyAssignableCustomizedDtorNonCompliant2 { // NON_COMPLIANT: copy-assignable class with only one of move operations not declared public: - DEFINE_ALL_SPECIAL_MEMBERS(CopyAssignableCustomizedDtorNonCompliant2, CUSTOMIZED, - NOT_DECLARED, CUSTOMIZED, CUSTOMIZED, CUSTOMIZED) + COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant2) CUSTOMIZED + // Move constructor is not declared + COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant2) CUSTOMIZED + MOVE_ASSIGN(CopyAssignableCustomizedDtorNonCompliant2) CUSTOMIZED + DTOR(CopyAssignableCustomizedDtorNonCompliant2) CUSTOMIZED }; class CopyAssignableCustomizedDtorNonCompliant3 { // NON_COMPLIANT: copy-assignable class with only one of move operations not declared public: - DEFINE_ALL_SPECIAL_MEMBERS(CopyAssignableCustomizedDtorNonCompliant3, CUSTOMIZED, - CUSTOMIZED, CUSTOMIZED, NOT_DECLARED, CUSTOMIZED) + + COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant3) CUSTOMIZED + MOVE_CTOR(CopyAssignableCustomizedDtorNonCompliant3) CUSTOMIZED + // Copy assignment operator is not declared + MOVE_ASSIGN(CopyAssignableCustomizedDtorNonCompliant3) CUSTOMIZED + DTOR(CopyAssignableCustomizedDtorNonCompliant3) CUSTOMIZED + }; class CopyAssignableCustomizedDtorNonCompliant4 { // NON_COMPLIANT: copy-assignable class with only one of move operations not declared public: - DEFINE_ALL_SPECIAL_MEMBERS(CopyAssignableCustomizedDtorNonCompliant4, CUSTOMIZED, - NOT_DECLARED, CUSTOMIZED, CUSTOMIZED, CUSTOMIZED) + COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant4) CUSTOMIZED + // Move constructor is not declared + COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant4) CUSTOMIZED + MOVE_ASSIGN(CopyAssignableCustomizedDtorNonCompliant4) CUSTOMIZED + DTOR(CopyAssignableCustomizedDtorNonCompliant4) CUSTOMIZED }; class CopyAssignableCustomizedDtorNonCompliant5 { // NON_COMPLIANT: copy-assignable class with only one of move operations not declared public: - DEFINE_ALL_SPECIAL_MEMBERS(CopyAssignableCustomizedDtorNonCompliant5, CUSTOMIZED, - DEFAULTED, CUSTOMIZED, NOT_DECLARED, CUSTOMIZED) + COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant5) CUSTOMIZED + MOVE_CTOR(CopyAssignableCustomizedDtorNonCompliant5) DEFAULTED + COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant5) CUSTOMIZED + // Move assignment operator is not declared + DTOR(CopyAssignableCustomizedDtorNonCompliant5) CUSTOMIZED }; class CopyAssignableCustomizedDtorNonCompliant6 { // NON_COMPLIANT: copy-assignable class with only one of move operations not declared public: - DEFINE_ALL_SPECIAL_MEMBERS(CopyAssignableCustomizedDtorNonCompliant6, CUSTOMIZED, - NOT_DECLARED, CUSTOMIZED, DEFAULTED, CUSTOMIZED) + COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant6) CUSTOMIZED + // Move constructor is not declared + COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant6) CUSTOMIZED + MOVE_ASSIGN(CopyAssignableCustomizedDtorNonCompliant6) DEFAULTED + DTOR(CopyAssignableCustomizedDtorNonCompliant6) CUSTOMIZED }; - // A public unmovable base class shall have a public virtual destructor class UnmovableBaseNonvirtualDtor { // NON_COMPLIANT public: @@ -440,4 +461,4 @@ void f(OdrUsedMoveEnabled o) { o3 = std::move(o); } -} // namespace audit_results \ No newline at end of file +} // namespace audit_results From e63c088cc07f4709863642a54e7832f43c399968 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Wed, 29 Apr 2026 17:09:19 -0700 Subject: [PATCH 08/15] Fix RuleMetadata.qll (package from a different rule/branch) --- .../src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll | 3 --- 1 file changed, 3 deletions(-) diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll index 38278d8cc3..3d0b195b0c 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll @@ -83,7 +83,6 @@ import OrderOfEvaluation import OutOfBounds import Pointers import Preconditions1 -import Preconditions2 import Preconditions3 import Preconditions4 import Preconditions5 @@ -193,7 +192,6 @@ 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 @@ -303,7 +301,6 @@ 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 From 658571a5465ba1d4460a0697bd6b592c2222fa69 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Wed, 29 Apr 2026 17:17:00 -0700 Subject: [PATCH 09/15] clang format --- cpp/misra/test/rules/RULE-15-0-1/test.cpp | 107 +++++++++++++--------- 1 file changed, 62 insertions(+), 45 deletions(-) diff --git a/cpp/misra/test/rules/RULE-15-0-1/test.cpp b/cpp/misra/test/rules/RULE-15-0-1/test.cpp index 7a43f3d70c..769ccb765b 100644 --- a/cpp/misra/test/rules/RULE-15-0-1/test.cpp +++ b/cpp/misra/test/rules/RULE-15-0-1/test.cpp @@ -178,7 +178,8 @@ class CustomizedAssignsCompliant { // COMPLIANT: copy-enabled (2) CUSTOMIZED, CUSTOMIZED, CUSTOMIZED) }; -class CustomizedCopyCtorDefaultedNonCompliant { // NON_COMPLIANT: copy-enabled (2) +// copy-enabled (2) +class CustomizedCopyCtorDefaultedNonCompliant { // NON_COMPLIANT public: DEFINE_ALL_SPECIAL_MEMBERS(CustomizedCopyCtorDefaultedNonCompliant, CUSTOMIZED, DEFAULTED, DEFAULTED, DEFAULTED, @@ -218,31 +219,36 @@ class MoveOnlyNotCustomizedNonCompliant { // NON_COMPLIANT: move-only (1) // Move-only with a customized dtor requires customized move assignment // operator. -class MoveOnlyAssignableNotCustomizedNonCompliant { // NON_COMPLIANT: move-only (2) +// move-only (2) +class MoveOnlyAssignableNotCustomizedNonCompliant { // NON_COMPLIANT public: DEFINE_ALL_SPECIAL_MEMBERS(MoveOnlyAssignableNotCustomizedNonCompliant, DELETED, DEFAULTED, DELETED, DEFAULTED, CUSTOMIZED) }; -class MoveOnlyCustomizedCompliant { // COMPLIANT -- customized move: move-only (1) +// customized move: move-only (1) +class MoveOnlyCustomizedCompliant { // COMPLIANT public: DEFINE_ALL_SPECIAL_MEMBERS(MoveOnlyCustomizedCompliant, DELETED, CUSTOMIZED, DELETED, DELETED, CUSTOMIZED) }; -class MoveOnlyAssignableCustomizedCompliant { // COMPLIANT -- customized move: move-only (2) +// customized move: move-only (2) +class MoveOnlyAssignableCustomizedCompliant { // COMPLIANT public: DEFINE_ALL_SPECIAL_MEMBERS(MoveOnlyAssignableCustomizedCompliant, DELETED, CUSTOMIZED, DELETED, CUSTOMIZED, CUSTOMIZED) }; -class MoveOnlyNotCustomizedCompliant { // COMPLIANT -- default dtor: move-only (1) +// default dtor: move-only (1) +class MoveOnlyNotCustomizedCompliant { // COMPLIANT public: DEFINE_ALL_SPECIAL_MEMBERS(MoveOnlyNotCustomizedCompliant, DELETED, DEFAULTED, DELETED, DELETED, DEFAULTED) }; -class MoveOnlyAssignableNotCustomizedCompliant { // COMPLIANT -- default dtor: move-only (2) +// default dtor: move-only (2) +class MoveOnlyAssignableNotCustomizedCompliant { // COMPLIANT public: DEFINE_ALL_SPECIAL_MEMBERS(MoveOnlyAssignableNotCustomizedCompliant, DELETED, DEFAULTED, DELETED, DEFAULTED, DEFAULTED) @@ -273,75 +279,86 @@ class CopyEnabledCustomizedDtorCompliant2 { // COMPLIANT: copy-enabled (2) DEFAULTED, CUSTOMIZED, DEFAULTED, CUSTOMIZED) }; -class CopyAssignableCustomizedDtorCompliant1 { // COMPLIANT: copy-assignable class with both move operations customized +// copy-assignable class with both move operations customized +class CopyAssignableCustomizedDtorCompliant1 { // COMPLIANT public: DEFINE_ALL_SPECIAL_MEMBERS(CopyAssignableCustomizedDtorCompliant1, DEFAULTED, CUSTOMIZED, CUSTOMIZED, CUSTOMIZED, CUSTOMIZED) }; -class CopyAssignableCustomizedDtorCompliant2 { // COMPLIANT: copy-assignable class with both move operations not declared +// copy-assignable class with both move operations not declared +class CopyAssignableCustomizedDtorCompliant2 { // COMPLIANT public: - COPY_CTOR(CopyAssignableCustomizedDtorCompliant2) DEFAULTED + COPY_CTOR(CopyAssignableCustomizedDtorCompliant2) + DEFAULTED // Move constructor is not declared - COPY_ASSIGN(CopyAssignableCustomizedDtorCompliant2) CUSTOMIZED + COPY_ASSIGN(CopyAssignableCustomizedDtorCompliant2) + CUSTOMIZED // Move assignment operator is not declared DTOR(CopyAssignableCustomizedDtorCompliant2) CUSTOMIZED }; -class CopyAssignableCustomizedDtorNonCompliant1 { // NON_COMPLIANT: copy-assignable class with only one of move operations not declared +// copy-assignable class with only one of move operations not declared +class CopyAssignableCustomizedDtorNonCompliant1 { // NON_COMPLIANT public: - COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant1) CUSTOMIZED - MOVE_CTOR(CopyAssignableCustomizedDtorNonCompliant1) CUSTOMIZED - COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant1) CUSTOMIZED - // Move assignment operator is not declared - DTOR(CopyAssignableCustomizedDtorNonCompliant1) CUSTOMIZED + COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant1) + CUSTOMIZED MOVE_CTOR(CopyAssignableCustomizedDtorNonCompliant1) CUSTOMIZED + COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant1) CUSTOMIZED + // Move assignment operator is not declared + DTOR(CopyAssignableCustomizedDtorNonCompliant1) CUSTOMIZED }; -class CopyAssignableCustomizedDtorNonCompliant2 { // NON_COMPLIANT: copy-assignable class with only one of move operations not declared +// copy-assignable class with only one of move operations not declared +class CopyAssignableCustomizedDtorNonCompliant2 { // NON_COMPLIANT public: - COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant2) CUSTOMIZED + COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant2) + CUSTOMIZED // Move constructor is not declared - COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant2) CUSTOMIZED - MOVE_ASSIGN(CopyAssignableCustomizedDtorNonCompliant2) CUSTOMIZED - DTOR(CopyAssignableCustomizedDtorNonCompliant2) CUSTOMIZED + COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant2) + CUSTOMIZED MOVE_ASSIGN(CopyAssignableCustomizedDtorNonCompliant2) CUSTOMIZED + DTOR(CopyAssignableCustomizedDtorNonCompliant2) CUSTOMIZED }; -class CopyAssignableCustomizedDtorNonCompliant3 { // NON_COMPLIANT: copy-assignable class with only one of move operations not declared +// copy-assignable class with only one of move operations not declared +class CopyAssignableCustomizedDtorNonCompliant3 { // NON_COMPLIANT public: - - COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant3) CUSTOMIZED - MOVE_CTOR(CopyAssignableCustomizedDtorNonCompliant3) CUSTOMIZED - // Copy assignment operator is not declared - MOVE_ASSIGN(CopyAssignableCustomizedDtorNonCompliant3) CUSTOMIZED - DTOR(CopyAssignableCustomizedDtorNonCompliant3) CUSTOMIZED - + COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant3) + CUSTOMIZED MOVE_CTOR(CopyAssignableCustomizedDtorNonCompliant3) CUSTOMIZED + // Copy assignment operator is not declared + MOVE_ASSIGN(CopyAssignableCustomizedDtorNonCompliant3) CUSTOMIZED + DTOR(CopyAssignableCustomizedDtorNonCompliant3) CUSTOMIZED }; -class CopyAssignableCustomizedDtorNonCompliant4 { // NON_COMPLIANT: copy-assignable class with only one of move operations not declared +// copy-assignable class with only one of move operations not declared +class CopyAssignableCustomizedDtorNonCompliant4 { // NON_COMPLIANT public: - COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant4) CUSTOMIZED + COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant4) + CUSTOMIZED // Move constructor is not declared - COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant4) CUSTOMIZED - MOVE_ASSIGN(CopyAssignableCustomizedDtorNonCompliant4) CUSTOMIZED - DTOR(CopyAssignableCustomizedDtorNonCompliant4) CUSTOMIZED + COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant4) + CUSTOMIZED MOVE_ASSIGN(CopyAssignableCustomizedDtorNonCompliant4) CUSTOMIZED + DTOR(CopyAssignableCustomizedDtorNonCompliant4) CUSTOMIZED }; -class CopyAssignableCustomizedDtorNonCompliant5 { // NON_COMPLIANT: copy-assignable class with only one of move operations not declared +// copy-assignable class with only one of move operations not declared +class CopyAssignableCustomizedDtorNonCompliant5 { // NON_COMPLIANT public: - COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant5) CUSTOMIZED - MOVE_CTOR(CopyAssignableCustomizedDtorNonCompliant5) DEFAULTED - COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant5) CUSTOMIZED - // Move assignment operator is not declared - DTOR(CopyAssignableCustomizedDtorNonCompliant5) CUSTOMIZED + COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant5) + CUSTOMIZED MOVE_CTOR(CopyAssignableCustomizedDtorNonCompliant5) DEFAULTED + COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant5) CUSTOMIZED + // Move assignment operator is not declared + DTOR(CopyAssignableCustomizedDtorNonCompliant5) CUSTOMIZED }; -class CopyAssignableCustomizedDtorNonCompliant6 { // NON_COMPLIANT: copy-assignable class with only one of move operations not declared +// copy-assignable class with only one of move operations not declared +class CopyAssignableCustomizedDtorNonCompliant6 { // NON_COMPLIANT public: - COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant6) CUSTOMIZED + COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant6) + CUSTOMIZED // Move constructor is not declared - COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant6) CUSTOMIZED - MOVE_ASSIGN(CopyAssignableCustomizedDtorNonCompliant6) DEFAULTED - DTOR(CopyAssignableCustomizedDtorNonCompliant6) CUSTOMIZED + COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant6) + CUSTOMIZED MOVE_ASSIGN(CopyAssignableCustomizedDtorNonCompliant6) DEFAULTED + DTOR(CopyAssignableCustomizedDtorNonCompliant6) CUSTOMIZED }; // A public unmovable base class shall have a public virtual destructor From 6e9dde18f3dba0bf9d5f9893e28f7035430d4839 Mon Sep 17 00:00:00 2001 From: jeongsoolee09 Date: Wed, 29 Apr 2026 20:30:23 -0400 Subject: [PATCH 10/15] Change the label of a case and break it down --- cpp/misra/test/rules/RULE-15-0-1/test.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/cpp/misra/test/rules/RULE-15-0-1/test.cpp b/cpp/misra/test/rules/RULE-15-0-1/test.cpp index 7a43f3d70c..1154b648a4 100644 --- a/cpp/misra/test/rules/RULE-15-0-1/test.cpp +++ b/cpp/misra/test/rules/RULE-15-0-1/test.cpp @@ -267,10 +267,13 @@ class CopyEnabledCustomizedDtorCompliant1 { // COMPLIANT: copy-enabled (2) CUSTOMIZED, CUSTOMIZED, CUSTOMIZED, CUSTOMIZED) }; -class CopyEnabledCustomizedDtorCompliant2 { // COMPLIANT: copy-enabled (2) +class CopyEnabledCustomizedDtorCompliant2 { // NON-COMPLIANT: copy-enabled (2) public: - DEFINE_ALL_SPECIAL_MEMBERS(CopyEnabledCustomizedDtorCompliant2, CUSTOMIZED, - DEFAULTED, CUSTOMIZED, DEFAULTED, CUSTOMIZED) + COPY_CTOR(CopyEnabledCustomizedDtorCompliant2) CUSTOMIZED + // Move constructor is not declared + COPY_ASSIGN(CopyEnabledCustomizedDtorCompliant2) CUSTOMIZED + MOVE_ASSIGN(CopyEnabledCustomizedDtorCompliant2) DEFAULTED + DTOR(CopyEnabledCustomizedDtorCompliant2) CUSTOMIZED }; class CopyAssignableCustomizedDtorCompliant1 { // COMPLIANT: copy-assignable class with both move operations customized From 96e2ed08f159df8745e270a3d1993daf4b4675b2 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Wed, 29 Apr 2026 18:26:00 -0700 Subject: [PATCH 11/15] Some formatting, make copy-enabled class no longer copy-assignable --- cpp/misra/test/rules/RULE-15-0-1/test.cpp | 79 +++++++++++------------ 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/cpp/misra/test/rules/RULE-15-0-1/test.cpp b/cpp/misra/test/rules/RULE-15-0-1/test.cpp index a331f354d3..d5a1b15aec 100644 --- a/cpp/misra/test/rules/RULE-15-0-1/test.cpp +++ b/cpp/misra/test/rules/RULE-15-0-1/test.cpp @@ -275,28 +275,30 @@ class CopyEnabledCustomizedDtorCompliant1 { // COMPLIANT: copy-enabled (2) class CopyEnabledCustomizedDtorCompliant2 { // NON-COMPLIANT: copy-enabled (2) public: - COPY_CTOR(CopyEnabledCustomizedDtorCompliant2) CUSTOMIZED - // Move constructor is not declared - COPY_ASSIGN(CopyEnabledCustomizedDtorCompliant2) CUSTOMIZED - MOVE_ASSIGN(CopyEnabledCustomizedDtorCompliant2) DEFAULTED - DTOR(CopyEnabledCustomizedDtorCompliant2) CUSTOMIZED + COPY_CTOR(CopyEnabledCustomizedDtorCompliant2) CUSTOMIZED; + // No move constructor declared + COPY_ASSIGN(CopyEnabledCustomizedDtorCompliant2) DELETED; + MOVE_ASSIGN(CopyEnabledCustomizedDtorCompliant2) DELETED; + ~CopyEnabledCustomizedDtorCompliant2() CUSTOMIZED; }; // copy-assignable class with both move operations customized class CopyAssignableCustomizedDtorCompliant1 { // COMPLIANT public: - DEFINE_ALL_SPECIAL_MEMBERS(CopyAssignableCustomizedDtorCompliant1, DEFAULTED, - CUSTOMIZED, CUSTOMIZED, CUSTOMIZED, CUSTOMIZED) + COPY_CTOR(CopyAssignableCustomizedDtorCompliant1) CUSTOMIZED; + MOVE_CTOR(CopyAssignableCustomizedDtorCompliant1) CUSTOMIZED; + // No move constructor declared + COPY_ASSIGN(CopyAssignableCustomizedDtorCompliant1) DELETED; + MOVE_ASSIGN(CopyAssignableCustomizedDtorCompliant1) DELETED; + ~CopyAssignableCustomizedDtorCompliant1() CUSTOMIZED; }; // copy-assignable class with both move operations not declared class CopyAssignableCustomizedDtorCompliant2 { // COMPLIANT public: - COPY_CTOR(CopyAssignableCustomizedDtorCompliant2) - DEFAULTED + COPY_CTOR(CopyAssignableCustomizedDtorCompliant2) DEFAULTED; // Move constructor is not declared - COPY_ASSIGN(CopyAssignableCustomizedDtorCompliant2) - CUSTOMIZED + COPY_ASSIGN(CopyAssignableCustomizedDtorCompliant2) CUSTOMIZED; // Move assignment operator is not declared DTOR(CopyAssignableCustomizedDtorCompliant2) CUSTOMIZED }; @@ -305,63 +307,60 @@ class CopyAssignableCustomizedDtorCompliant2 { // COMPLIANT class CopyAssignableCustomizedDtorNonCompliant1 { // NON_COMPLIANT public: COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant1) - CUSTOMIZED MOVE_CTOR(CopyAssignableCustomizedDtorNonCompliant1) CUSTOMIZED - COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant1) CUSTOMIZED - // Move assignment operator is not declared - DTOR(CopyAssignableCustomizedDtorNonCompliant1) CUSTOMIZED + CUSTOMIZED MOVE_CTOR(CopyAssignableCustomizedDtorNonCompliant1) CUSTOMIZED; + COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant1) CUSTOMIZED; + // Move assignment operator is not declared + DTOR(CopyAssignableCustomizedDtorNonCompliant1) CUSTOMIZED; }; // copy-assignable class with only one of move operations not declared class CopyAssignableCustomizedDtorNonCompliant2 { // NON_COMPLIANT public: - COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant2) - CUSTOMIZED + COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant2) CUSTOMIZED; // Move constructor is not declared - COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant2) - CUSTOMIZED MOVE_ASSIGN(CopyAssignableCustomizedDtorNonCompliant2) CUSTOMIZED - DTOR(CopyAssignableCustomizedDtorNonCompliant2) CUSTOMIZED + COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant2) CUSTOMIZED; + MOVE_ASSIGN(CopyAssignableCustomizedDtorNonCompliant2) CUSTOMIZED; + DTOR(CopyAssignableCustomizedDtorNonCompliant2) CUSTOMIZED; }; // copy-assignable class with only one of move operations not declared class CopyAssignableCustomizedDtorNonCompliant3 { // NON_COMPLIANT public: - COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant3) - CUSTOMIZED MOVE_CTOR(CopyAssignableCustomizedDtorNonCompliant3) CUSTOMIZED - // Copy assignment operator is not declared - MOVE_ASSIGN(CopyAssignableCustomizedDtorNonCompliant3) CUSTOMIZED - DTOR(CopyAssignableCustomizedDtorNonCompliant3) CUSTOMIZED + COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant3) CUSTOMIZED; + MOVE_CTOR(CopyAssignableCustomizedDtorNonCompliant3) CUSTOMIZED; + // Copy assignment operator is not declared + MOVE_ASSIGN(CopyAssignableCustomizedDtorNonCompliant3) CUSTOMIZED; + DTOR(CopyAssignableCustomizedDtorNonCompliant3) CUSTOMIZED; }; // copy-assignable class with only one of move operations not declared class CopyAssignableCustomizedDtorNonCompliant4 { // NON_COMPLIANT public: - COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant4) - CUSTOMIZED + COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant4) CUSTOMIZED; // Move constructor is not declared - COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant4) - CUSTOMIZED MOVE_ASSIGN(CopyAssignableCustomizedDtorNonCompliant4) CUSTOMIZED - DTOR(CopyAssignableCustomizedDtorNonCompliant4) CUSTOMIZED + COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant4) CUSTOMIZED; + MOVE_ASSIGN(CopyAssignableCustomizedDtorNonCompliant4) CUSTOMIZED; + DTOR(CopyAssignableCustomizedDtorNonCompliant4) CUSTOMIZED }; // copy-assignable class with only one of move operations not declared class CopyAssignableCustomizedDtorNonCompliant5 { // NON_COMPLIANT public: - COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant5) - CUSTOMIZED MOVE_CTOR(CopyAssignableCustomizedDtorNonCompliant5) DEFAULTED - COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant5) CUSTOMIZED - // Move assignment operator is not declared - DTOR(CopyAssignableCustomizedDtorNonCompliant5) CUSTOMIZED + COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant5) CUSTOMIZED; + MOVE_CTOR(CopyAssignableCustomizedDtorNonCompliant5) DEFAULTED; + COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant5) CUSTOMIZED; + // Move assignment operator is not declared + DTOR(CopyAssignableCustomizedDtorNonCompliant5) CUSTOMIZED; }; // copy-assignable class with only one of move operations not declared class CopyAssignableCustomizedDtorNonCompliant6 { // NON_COMPLIANT public: - COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant6) - CUSTOMIZED + COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant6) CUSTOMIZED; // Move constructor is not declared - COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant6) - CUSTOMIZED MOVE_ASSIGN(CopyAssignableCustomizedDtorNonCompliant6) DEFAULTED - DTOR(CopyAssignableCustomizedDtorNonCompliant6) CUSTOMIZED + COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant6) CUSTOMIZED; + MOVE_ASSIGN(CopyAssignableCustomizedDtorNonCompliant6) DEFAULTED; + DTOR(CopyAssignableCustomizedDtorNonCompliant6) CUSTOMIZED; }; // A public unmovable base class shall have a public virtual destructor From ce438029364ff7f0b6427d493cd94305fcff9ec4 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Wed, 29 Apr 2026 19:23:45 -0700 Subject: [PATCH 12/15] Handle suppressed move operations --- .../src/rules/RULE-15-0-1/AnalyzableClass.qll | 54 ++++++++++++++++--- ...mproperlyProvidedSpecialMemberFunctions.ql | 15 ++++++ ...rlyProvidedSpecialMemberFunctions.expected | 37 +++++++------ cpp/misra/test/rules/RULE-15-0-1/test.cpp | 34 ++++-------- 4 files changed, 95 insertions(+), 45 deletions(-) diff --git a/cpp/misra/src/rules/RULE-15-0-1/AnalyzableClass.qll b/cpp/misra/src/rules/RULE-15-0-1/AnalyzableClass.qll index bc9cfed536..a556320b69 100644 --- a/cpp/misra/src/rules/RULE-15-0-1/AnalyzableClass.qll +++ b/cpp/misra/src/rules/RULE-15-0-1/AnalyzableClass.qll @@ -12,6 +12,38 @@ private predicate isMemberCustomized(MemberFunction f) { not f.isCompilerGenerated() } +private predicate isUserDeclared(MemberFunction f) { not f.isCompilerGenerated() } + +/** + * Holds if the implicit move constructor or move assignment operator of the class `c` will not be + * declared. + * + * See [class.copy]/8 and [class.copy] + */ +private predicate implicitMoveIsSuppressed(Class c) { + isUserDeclared(c.getAConstructor().(CopyConstructor)) + or + isUserDeclared(c.getAConstructor().(CopyAssignmentOperator)) + or + isUserDeclared(c.getDestructor()) +} + +Constructor getMoveConstructor(Class c) { + if + not exists(MoveConstructor mc | mc = c.getAConstructor() and isUserDeclared(mc)) and + implicitMoveIsSuppressed(c) + then result = c.getAConstructor().(CopyConstructor) + else result = c.getAConstructor().(MoveConstructor) +} + +Operator getMoveAssign(Class c) { + if + not exists(MoveAssignmentOperator mc | mc = c.getAMemberFunction() and isUserDeclared(mc)) and + implicitMoveIsSuppressed(c) + then result = c.getAMemberFunction().(CopyAssignmentOperator) + else result = c.getAMemberFunction().(MoveAssignmentOperator) +} + newtype TSpecialMember = TMoveConstructor() or TMoveAssignmentOperator() or @@ -20,17 +52,19 @@ newtype TSpecialMember = TDestructor() class AnalyzableClass extends Class { - MoveConstructor moveCtor; - MoveAssignmentOperator moveAssign; CopyConstructor copyCtor; + // The move constructor may be suppressed, and the copy constructor may be used during moves. + Constructor moveCtor; CopyAssignmentOperator copyAssign; + // The move assignment operator may be suppressed, and the copy assignment operator may be used during moves. + Operator moveAssign; Destructor dtor; AnalyzableClass() { - moveCtor = this.getAConstructor() and copyCtor = this.getAConstructor() and - moveAssign = this.getAMemberFunction() and + moveCtor = getMoveConstructor(this) and copyAssign = this.getAMemberFunction() and + moveAssign = getMoveAssign(this) and dtor = this.getDestructor() } @@ -57,9 +91,13 @@ class AnalyzableClass extends Class { predicate destructible() { isUsable(dtor) } predicate isCustomized(TSpecialMember s) { - s instanceof TMoveConstructor and isMemberCustomized(moveCtor) + s instanceof TMoveConstructor and + isMemberCustomized(moveCtor) and + declaresMoveConstructor() or - s instanceof TMoveAssignmentOperator and isMemberCustomized(moveAssign) + s instanceof TMoveAssignmentOperator and + isMemberCustomized(moveAssign) and + declaresMoveAssignmentOperator() or s instanceof TCopyConstructor and isMemberCustomized(copyCtor) or @@ -67,4 +105,8 @@ class AnalyzableClass extends Class { or s instanceof TDestructor and isMemberCustomized(dtor) } + + predicate declaresMoveConstructor() { not moveCtor = copyCtor } + + predicate declaresMoveAssignmentOperator() { not moveAssign = copyAssign } } diff --git a/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.ql b/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.ql index 32721cf58a..e1930ae423 100644 --- a/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.ql +++ b/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.ql @@ -79,14 +79,29 @@ predicate violatesCustomizedMoveOrCopyRequirements(AnalyzableClass c, string rea ) } +private predicate undeclaredMoveException(AnalyzableClass c) { + // A copy-enabled class may have an undeclared move constructor. + isCopyEnabled(c) and + not c.moveAssignable() and + not c.declaresMoveConstructor() + or + // A copy-assignable class may leave both move operations undeclared. + isCopyEnabled(c) and + c.moveAssignable() and + not c.declaresMoveConstructor() and + not c.declaresMoveAssignmentOperator() +} + predicate violatesCustomizedDestructorRequirements(AnalyzableClass c, string reason) { c.isCustomized(TDestructor()) and ( c.moveConstructible() and not c.isCustomized(TMoveConstructor()) and + not undeclaredMoveException(c) and reason = "has customized the destructor, but does not customize the move constructor." or c.moveAssignable() and + not undeclaredMoveException(c) and not c.isCustomized(TMoveAssignmentOperator()) and reason = "has customized the destructor, but does not customize the move assignment operator." or diff --git a/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.expected b/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.expected index bdfa47f75f..eeab1781ad 100644 --- a/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.expected +++ b/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.expected @@ -13,18 +13,25 @@ | test.cpp:115:7:115:21 | PrivateMoveCtor | Class 'PrivateMoveCtor' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | | test.cpp:128:7:128:23 | PrivateCopyAssign | Class 'PrivateCopyAssign' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | | test.cpp:141:7:141:23 | PrivateMoveAssign | Class 'PrivateMoveAssign' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | -| test.cpp:181:7:181:45 | CustomizedCopyCtorDefaultedNonCompliant | Class 'CustomizedCopyCtorDefaultedNonCompliant' has customized copy constructor, but does not customize the destructor. | -| test.cpp:188:7:188:43 | CustomizedCopyCtorDeletedNonCompliant | Class 'CustomizedCopyCtorDeletedNonCompliant' has customized copy constructor, but does not customize the destructor. | -| test.cpp:194:7:194:36 | CustomizedMoveCtorNonCompliant | Class 'CustomizedMoveCtorNonCompliant' has customized move constructor, but does not customize the destructor. | -| test.cpp:200:7:200:38 | CustomizedCopyAssignNonCompliant | Class 'CustomizedCopyAssignNonCompliant' has customized copy assignment operator, but does not customize the destructor. | -| test.cpp:206:7:206:38 | CustomizedMoveAssignNonCompliant | Class 'CustomizedMoveAssignNonCompliant' has customized copy constructor, move assignment operator, move constructor, but does not customize the destructor. | -| test.cpp:213:7:213:39 | MoveOnlyNotCustomizedNonCompliant | Class 'MoveOnlyNotCustomizedNonCompliant' has customized the destructor, but does not customize the move constructor. | -| test.cpp:220:7:220:49 | MoveOnlyAssignableNotCustomizedNonCompliant | Class 'MoveOnlyAssignableNotCustomizedNonCompliant' has customized the destructor, but does not customize the move assignment operator. | -| test.cpp:220:7:220:49 | MoveOnlyAssignableNotCustomizedNonCompliant | Class 'MoveOnlyAssignableNotCustomizedNonCompliant' has customized the destructor, but does not customize the move constructor. | -| test.cpp:251:7:251:43 | CopyEnabledCustomizedDtorNonCompliant | Class 'CopyEnabledCustomizedDtorNonCompliant' has customized the destructor, but does not customize the copy assignment operator. | -| test.cpp:251:7:251:43 | CopyEnabledCustomizedDtorNonCompliant | Class 'CopyEnabledCustomizedDtorNonCompliant' has customized the destructor, but does not customize the copy constructor. | -| test.cpp:251:7:251:43 | CopyEnabledCustomizedDtorNonCompliant | Class 'CopyEnabledCustomizedDtorNonCompliant' has customized the destructor, but does not customize the move assignment operator. | -| test.cpp:251:7:251:43 | CopyEnabledCustomizedDtorNonCompliant | Class 'CopyEnabledCustomizedDtorNonCompliant' has customized the destructor, but does not customize the move constructor. | -| test.cpp:270:7:270:33 | UnmovableBaseNonvirtualDtor | Class 'UnmovableBaseNonvirtualDtor' violates inheritance requirements for special member functions. | -| test.cpp:291:7:291:33 | UnmovablePrivateVirtualDtor | Class 'UnmovablePrivateVirtualDtor' violates inheritance requirements for special member functions. | -| test.cpp:318:7:318:30 | BaseVirtualProtectedDtor | Class 'BaseVirtualProtectedDtor' violates inheritance requirements for special member functions. | +| test.cpp:182:7:182:45 | CustomizedCopyCtorDefaultedNonCompliant | Class 'CustomizedCopyCtorDefaultedNonCompliant' has customized copy constructor, but does not customize the destructor. | +| test.cpp:189:7:189:43 | CustomizedCopyCtorDeletedNonCompliant | Class 'CustomizedCopyCtorDeletedNonCompliant' has customized copy constructor, but does not customize the destructor. | +| test.cpp:195:7:195:36 | CustomizedMoveCtorNonCompliant | Class 'CustomizedMoveCtorNonCompliant' has customized move constructor, but does not customize the destructor. | +| test.cpp:201:7:201:38 | CustomizedCopyAssignNonCompliant | Class 'CustomizedCopyAssignNonCompliant' has customized copy assignment operator, but does not customize the destructor. | +| test.cpp:207:7:207:38 | CustomizedMoveAssignNonCompliant | Class 'CustomizedMoveAssignNonCompliant' has customized copy constructor, move assignment operator, move constructor, but does not customize the destructor. | +| test.cpp:214:7:214:39 | MoveOnlyNotCustomizedNonCompliant | Class 'MoveOnlyNotCustomizedNonCompliant' has customized the destructor, but does not customize the move constructor. | +| test.cpp:223:7:223:49 | MoveOnlyAssignableNotCustomizedNonCompliant | Class 'MoveOnlyAssignableNotCustomizedNonCompliant' has customized the destructor, but does not customize the move assignment operator. | +| test.cpp:223:7:223:49 | MoveOnlyAssignableNotCustomizedNonCompliant | Class 'MoveOnlyAssignableNotCustomizedNonCompliant' has customized the destructor, but does not customize the move constructor. | +| test.cpp:258:7:258:43 | CopyEnabledCustomizedDtorNonCompliant | Class 'CopyEnabledCustomizedDtorNonCompliant' has customized the destructor, but does not customize the copy assignment operator. | +| test.cpp:258:7:258:43 | CopyEnabledCustomizedDtorNonCompliant | Class 'CopyEnabledCustomizedDtorNonCompliant' has customized the destructor, but does not customize the copy constructor. | +| test.cpp:258:7:258:43 | CopyEnabledCustomizedDtorNonCompliant | Class 'CopyEnabledCustomizedDtorNonCompliant' has customized the destructor, but does not customize the move assignment operator. | +| test.cpp:258:7:258:43 | CopyEnabledCustomizedDtorNonCompliant | Class 'CopyEnabledCustomizedDtorNonCompliant' has customized the destructor, but does not customize the move constructor. | +| test.cpp:307:7:307:47 | CopyAssignableCustomizedDtorNonCompliant1 | Class 'CopyAssignableCustomizedDtorNonCompliant1' has customized the destructor, but does not customize the move assignment operator. | +| test.cpp:317:7:317:47 | CopyAssignableCustomizedDtorNonCompliant2 | Class 'CopyAssignableCustomizedDtorNonCompliant2' has customized the destructor, but does not customize the move constructor. | +| test.cpp:327:7:327:47 | CopyAssignableCustomizedDtorNonCompliant3 | Class 'CopyAssignableCustomizedDtorNonCompliant3' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:337:7:337:47 | CopyAssignableCustomizedDtorNonCompliant4 | Class 'CopyAssignableCustomizedDtorNonCompliant4' has customized the destructor, but does not customize the move assignment operator. | +| test.cpp:337:7:337:47 | CopyAssignableCustomizedDtorNonCompliant4 | Class 'CopyAssignableCustomizedDtorNonCompliant4' has customized the destructor, but does not customize the move constructor. | +| test.cpp:347:7:347:47 | CopyAssignableCustomizedDtorNonCompliant5 | Class 'CopyAssignableCustomizedDtorNonCompliant5' has customized the destructor, but does not customize the move assignment operator. | +| test.cpp:347:7:347:47 | CopyAssignableCustomizedDtorNonCompliant5 | Class 'CopyAssignableCustomizedDtorNonCompliant5' has customized the destructor, but does not customize the move constructor. | +| test.cpp:357:7:357:33 | UnmovableBaseNonvirtualDtor | Class 'UnmovableBaseNonvirtualDtor' violates inheritance requirements for special member functions. | +| test.cpp:378:7:378:33 | UnmovablePrivateVirtualDtor | Class 'UnmovablePrivateVirtualDtor' violates inheritance requirements for special member functions. | +| test.cpp:405:7:405:30 | BaseVirtualProtectedDtor | Class 'BaseVirtualProtectedDtor' violates inheritance requirements for special member functions. | diff --git a/cpp/misra/test/rules/RULE-15-0-1/test.cpp b/cpp/misra/test/rules/RULE-15-0-1/test.cpp index d5a1b15aec..17810bbd7b 100644 --- a/cpp/misra/test/rules/RULE-15-0-1/test.cpp +++ b/cpp/misra/test/rules/RULE-15-0-1/test.cpp @@ -273,7 +273,7 @@ class CopyEnabledCustomizedDtorCompliant1 { // COMPLIANT: copy-enabled (2) CUSTOMIZED, CUSTOMIZED, CUSTOMIZED, CUSTOMIZED) }; -class CopyEnabledCustomizedDtorCompliant2 { // NON-COMPLIANT: copy-enabled (2) +class CopyEnabledCustomizedDtorCompliant2 { // COMPLIANT: copy-enabled (2) public: COPY_CTOR(CopyEnabledCustomizedDtorCompliant2) CUSTOMIZED; // No move constructor declared @@ -296,7 +296,7 @@ class CopyAssignableCustomizedDtorCompliant1 { // COMPLIANT // copy-assignable class with both move operations not declared class CopyAssignableCustomizedDtorCompliant2 { // COMPLIANT public: - COPY_CTOR(CopyAssignableCustomizedDtorCompliant2) DEFAULTED; + COPY_CTOR(CopyAssignableCustomizedDtorCompliant2) CUSTOMIZED; // Move constructor is not declared COPY_ASSIGN(CopyAssignableCustomizedDtorCompliant2) CUSTOMIZED; // Move assignment operator is not declared @@ -306,8 +306,8 @@ class CopyAssignableCustomizedDtorCompliant2 { // COMPLIANT // copy-assignable class with only one of move operations not declared class CopyAssignableCustomizedDtorNonCompliant1 { // NON_COMPLIANT public: - COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant1) - CUSTOMIZED MOVE_CTOR(CopyAssignableCustomizedDtorNonCompliant1) CUSTOMIZED; + COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant1) CUSTOMIZED; + MOVE_CTOR(CopyAssignableCustomizedDtorNonCompliant1) CUSTOMIZED; COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant1) CUSTOMIZED; // Move assignment operator is not declared DTOR(CopyAssignableCustomizedDtorNonCompliant1) CUSTOMIZED; @@ -323,7 +323,7 @@ class CopyAssignableCustomizedDtorNonCompliant2 { // NON_COMPLIANT DTOR(CopyAssignableCustomizedDtorNonCompliant2) CUSTOMIZED; }; -// copy-assignable class with only one of move operations not declared +// copy-assignable class with only a copy operation not declared class CopyAssignableCustomizedDtorNonCompliant3 { // NON_COMPLIANT public: COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant3) CUSTOMIZED; @@ -337,32 +337,22 @@ class CopyAssignableCustomizedDtorNonCompliant3 { // NON_COMPLIANT class CopyAssignableCustomizedDtorNonCompliant4 { // NON_COMPLIANT public: COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant4) CUSTOMIZED; - // Move constructor is not declared + MOVE_CTOR(CopyAssignableCustomizedDtorNonCompliant4) DEFAULTED; COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant4) CUSTOMIZED; - MOVE_ASSIGN(CopyAssignableCustomizedDtorNonCompliant4) CUSTOMIZED; - DTOR(CopyAssignableCustomizedDtorNonCompliant4) CUSTOMIZED + // Move assignment operator is not declared + DTOR(CopyAssignableCustomizedDtorNonCompliant4) CUSTOMIZED; }; // copy-assignable class with only one of move operations not declared class CopyAssignableCustomizedDtorNonCompliant5 { // NON_COMPLIANT public: COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant5) CUSTOMIZED; - MOVE_CTOR(CopyAssignableCustomizedDtorNonCompliant5) DEFAULTED; + // Move constructor is not declared COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant5) CUSTOMIZED; - // Move assignment operator is not declared + MOVE_ASSIGN(CopyAssignableCustomizedDtorNonCompliant5) DEFAULTED; DTOR(CopyAssignableCustomizedDtorNonCompliant5) CUSTOMIZED; }; -// copy-assignable class with only one of move operations not declared -class CopyAssignableCustomizedDtorNonCompliant6 { // NON_COMPLIANT -public: - COPY_CTOR(CopyAssignableCustomizedDtorNonCompliant6) CUSTOMIZED; - // Move constructor is not declared - COPY_ASSIGN(CopyAssignableCustomizedDtorNonCompliant6) CUSTOMIZED; - MOVE_ASSIGN(CopyAssignableCustomizedDtorNonCompliant6) DEFAULTED; - DTOR(CopyAssignableCustomizedDtorNonCompliant6) CUSTOMIZED; -}; - // A public unmovable base class shall have a public virtual destructor class UnmovableBaseNonvirtualDtor { // NON_COMPLIANT public: @@ -447,10 +437,6 @@ class NonTrivialClass { // NON_COMPLIANT - audit result int y; public: - COPY_CTOR(NonTrivialClass) { - x = 1; - y = 2; - } ~NonTrivialClass() { x = 1; } }; From a1c1a9e0cc022bc5132ca1861ca9057b569ab60c Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Wed, 29 Apr 2026 23:03:46 -0700 Subject: [PATCH 13/15] Updates to expected file --- ...yProvidedSpecialMemberFunctionsAudit.expected | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.expected b/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.expected index 99faa7d929..cf289aa069 100644 --- a/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.expected +++ b/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.expected @@ -1,8 +1,8 @@ -| test.cpp:276:7:276:32 | UnmovableNonvirtualDerived | Class 'UnmovableNonvirtualDerived' is not analyzable because the destructor and move assignment operator and move constructor are not present in the CodeQL database. | -| test.cpp:288:7:288:39 | UnmovableDerivedPublicVirtualDtor | Class 'UnmovableDerivedPublicVirtualDtor' is not analyzable because the move assignment operator and move constructor are not present in the CodeQL database. | -| test.cpp:302:7:302:40 | UnmovablePrivateVirtualDtorDerived | Class 'UnmovablePrivateVirtualDtorDerived' is not analyzable because the move assignment operator and move constructor are not present in the CodeQL database. | -| test.cpp:316:7:316:26 | ProtectedDtorDerived | Class 'ProtectedDtorDerived' is not analyzable because the destructor and move assignment operator are not present in the CodeQL database. | -| test.cpp:329:7:329:33 | VirtualProtectedDtorDerived | Class 'VirtualProtectedDtorDerived' is not analyzable because the move assignment operator are not present in the CodeQL database. | -| test.cpp:342:8:342:19 | TrivialClass | Class 'TrivialClass' is not analyzable because the destructor and move assignment operator and move constructor are not present in the CodeQL database. | -| test.cpp:348:7:348:21 | NonTrivialClass | Class 'NonTrivialClass' is not analyzable because the move assignment operator and move constructor are not present in the CodeQL database. | -| test.cpp:362:7:362:14 | CopyOnly | Class 'CopyOnly' is not analyzable because the destructor are not present in the CodeQL database. | +| test.cpp:363:7:363:32 | UnmovableNonvirtualDerived | Class 'UnmovableNonvirtualDerived' is not analyzable because the destructor and move assignment operator and move constructor are not present in the CodeQL database. | +| test.cpp:375:7:375:39 | UnmovableDerivedPublicVirtualDtor | Class 'UnmovableDerivedPublicVirtualDtor' is not analyzable because the move assignment operator and move constructor are not present in the CodeQL database. | +| test.cpp:389:7:389:40 | UnmovablePrivateVirtualDtorDerived | Class 'UnmovablePrivateVirtualDtorDerived' is not analyzable because the move assignment operator and move constructor are not present in the CodeQL database. | +| test.cpp:403:7:403:26 | ProtectedDtorDerived | Class 'ProtectedDtorDerived' is not analyzable because the destructor and move assignment operator are not present in the CodeQL database. | +| test.cpp:416:7:416:33 | VirtualProtectedDtorDerived | Class 'VirtualProtectedDtorDerived' is not analyzable because the move assignment operator are not present in the CodeQL database. | +| test.cpp:429:8:429:19 | TrivialClass | Class 'TrivialClass' is not analyzable because the destructor and move assignment operator and move constructor are not present in the CodeQL database. | +| test.cpp:435:7:435:21 | NonTrivialClass | Class 'NonTrivialClass' is not analyzable because the copy constructor and move assignment operator and move constructor are not present in the CodeQL database. | +| test.cpp:445:7:445:14 | CopyOnly | Class 'CopyOnly' is not analyzable because the destructor are not present in the CodeQL database. | From 281313682c8722bc4f16d9bf7f4e9cf077b895e2 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Thu, 30 Apr 2026 13:57:12 -0700 Subject: [PATCH 14/15] Address feedback --- .../src/rules/RULE-15-0-1/AnalyzableClass.qll | 152 +++++++++++++++--- ...erlyProvidedSpecialMemberFunctionsAudit.ql | 5 +- ...rlyProvidedSpecialMemberFunctions.expected | 20 +-- ...ovidedSpecialMemberFunctionsAudit.expected | 16 +- cpp/misra/test/rules/RULE-15-0-1/test.cpp | 22 +-- 5 files changed, 161 insertions(+), 54 deletions(-) diff --git a/cpp/misra/src/rules/RULE-15-0-1/AnalyzableClass.qll b/cpp/misra/src/rules/RULE-15-0-1/AnalyzableClass.qll index a556320b69..7c58a7f809 100644 --- a/cpp/misra/src/rules/RULE-15-0-1/AnalyzableClass.qll +++ b/cpp/misra/src/rules/RULE-15-0-1/AnalyzableClass.qll @@ -1,10 +1,36 @@ +/** + * Provides predicates and classes to perform a precursor analysis of which classes the rule can + * potentially analyze, or should be excluded and instead selected by the audit query. + * + * For example, the class `AnalyzableClass` resolves all of the special member functions that we + * must have in order to determine rule compliance. + * + * Part of what this module does is perform a very approximate analysis of which classes will + * produce a value of true in `std::is_(copy|move)_(constructible|assignable)_v`. + * + * Fully predicting these standard type traits requires performing a very thorough overload + * resolution analysis, including value category propagation and reference binding and user defined + * conversion operators and standard conversions and promotions and ranking viable candidates and + * properly handling ambiguous overloads. + */ + import cpp +/** + * For `std::is_(copy|move)_(constructible|assignable)_v` to return true for a given class, the + * member must exist, it must not be deleted, and it must be publicly accessible. + * + * This is a very coarse approximation of true behavior of the standard type traits. + */ private predicate isUsable(MemberFunction f) { not f.isDeleted() and f.isPublic() } +/** + * Holds if the member function `f` has been "customized" by the user, e.g., they explicitly wrote + * the implementation of the function. + */ private predicate isMemberCustomized(MemberFunction f) { exists(f.getDefinition()) and not f.isDefaulted() and @@ -12,6 +38,13 @@ private predicate isMemberCustomized(MemberFunction f) { not f.isCompilerGenerated() } +/** + * Holds if the user declared the member function `f`, as opposed to it being implicitly declared + * by the compiler. + * + * Note that `T(T&&) = default` and `T(T&&) = delete` are both user declared. This is not to be + * confused with "user defined." + */ private predicate isUserDeclared(MemberFunction f) { not f.isCompilerGenerated() } /** @@ -23,12 +56,16 @@ private predicate isUserDeclared(MemberFunction f) { not f.isCompilerGenerated() private predicate implicitMoveIsSuppressed(Class c) { isUserDeclared(c.getAConstructor().(CopyConstructor)) or - isUserDeclared(c.getAConstructor().(CopyAssignmentOperator)) + isUserDeclared(c.getAMemberFunction().(CopyAssignmentOperator)) or isUserDeclared(c.getDestructor()) } -Constructor getMoveConstructor(Class c) { +/** + * Returns the move constructor of the class `c` if it exists, or the copy constructor if it does + * not exist and the implicit definition was suppressed by the compiler. + */ +private Constructor getMoveConstructor(Class c) { if not exists(MoveConstructor mc | mc = c.getAConstructor() and isUserDeclared(mc)) and implicitMoveIsSuppressed(c) @@ -36,7 +73,11 @@ Constructor getMoveConstructor(Class c) { else result = c.getAConstructor().(MoveConstructor) } -Operator getMoveAssign(Class c) { +/** + * Returns the move assignment operator of the class `c` if it exists, or the copy assignment + * operator if it does not exist and the implicit definition was suppressed by the compiler. + */ +private Operator getMoveAssign(Class c) { if not exists(MoveAssignmentOperator mc | mc = c.getAMemberFunction() and isUserDeclared(mc)) and implicitMoveIsSuppressed(c) @@ -44,6 +85,9 @@ Operator getMoveAssign(Class c) { else result = c.getAMemberFunction().(MoveAssignmentOperator) } +/** + * The types of special member functions that the `AnalyzableClass` tracks and analyzes. + */ newtype TSpecialMember = TMoveConstructor() or TMoveAssignmentOperator() or @@ -51,6 +95,39 @@ newtype TSpecialMember = TCopyAssignmentOperator() or TDestructor() +/** + * A class for which we can see all special member functions, including implicitly declared ones, + * and therefore we can attempt to analyze it in the current rule. + * + * If one of the special member functions cannot be found, we cannot know if it is missing because + * it should not have been generated, or if EDG did not emit a definition for it. For instance, EDG + * may not generate these functions if they are trivial, or if they are delete, or not ODR used. We, + * the authors of this project, do not know the exact conditions we have to consider in this case. + * + * Determining for ourselves whether a certain constructor would be implicitly declared, and with + * what signature, and whether it is deleted, requires implementing a significant portion of the C++ + * language rules regarding special member function generation, including a significant portion of + * C++ overload resolution rules which are non-trivial. + * + * Therefore we must find a definition for each special member in the database to proceed. The only + * exception we allow is certain missing `MoveConstructor` or `MoveAssignmentOperator` members; if + * the class defines copy operations or the destructor, we expect these to be missing, and typically + * this means the corresponding copy operation acts in place of the equivalent move. + * + * The last difficulty in analysis that this class attempts to handle is the values of the type + * traits `std::is_(copy|move)_(constructible|assignable)`. These type traits are defined as true if + * certain C++ expressions, such as `T(declval())` or `declval() = declval()`, are + * well-formed. We cannot correctly determine this in all cases without implementing a significant + * portion of the C++ language rules for reference binding and overload resolution. + * + * To handle these type traits, we take a very rough approximation. If the corresponding special + * member function is public and not deleted, then we assume the type trait will evaluate to true. + * We also handle the case where a user declared copy operation suppresses the implicit move + * operations, which typically means overload resolution selects the copy operation. (This is not + * the case when the move operations are declared as deleted). We handle this by treating the copy + * operation as effectively acting in place of the move operation for the purposes of evaluating + * the type traits. + */ class AnalyzableClass extends Class { CopyConstructor copyCtor; // The move constructor may be suppressed, and the copy constructor may be used during moves. @@ -68,28 +145,49 @@ class AnalyzableClass extends Class { dtor = this.getDestructor() } - predicate exposes(TSpecialMember m) { - m instanceof TMoveConstructor and moveConstructible() - or - m instanceof TMoveAssignmentOperator and moveAssignable() - or - m instanceof TCopyConstructor and copyConstructible() - or - m instanceof TCopyAssignmentOperator and copyAssignable() - or - m instanceof TDestructor and destructible() - } - + /** + * Holds `std::is_move_constructible_v` is likely true for this class. + * + * Specifically this holds if there's a non-deleted public move constructor available for this + * class, or if there is a non-deleted public copy constructor that acts as the move constructor. + */ predicate moveConstructible() { isUsable(moveCtor) } + /** + * Holds `std::is_copy_constructible_v` is likely true for this class. + * + * Specifically this holds if there's a non-deleted public copy constructor available for this + * class. + */ predicate copyConstructible() { isUsable(copyCtor) } + /** + * Holds `std::is_move_assignable_v` is likely true for this class. + * + * Specifically this holds if there's a non-deleted public move assignment operator available for + * this class, or if there is a non-deleted public copy assignment operator that acts as the move + * assignment operator. + */ predicate moveAssignable() { isUsable(moveAssign) } + /** + * Holds `std::is_copy_assignable_v` is likely true for this class. + * + * Specifically this holds if there's a non-deleted public copy assignment operator available for + * this class. + */ predicate copyAssignable() { isUsable(copyAssign) } - predicate destructible() { isUsable(dtor) } - + /** + * Holds if the given special member function `s` is customized for this class. + * + * For most cases, this checks that the given special member function `s` has a user-provided + * body (other than `= default;` or `= delete;`). + * + * If the class has copy operations that act in place of the move operations, that means the + * corresponding move operation was not declared, so we say this predicate does not hold for the + * given move operation `s`. + */ predicate isCustomized(TSpecialMember s) { s instanceof TMoveConstructor and isMemberCustomized(moveCtor) and @@ -106,7 +204,21 @@ class AnalyzableClass extends Class { s instanceof TDestructor and isMemberCustomized(dtor) } - predicate declaresMoveConstructor() { not moveCtor = copyCtor } - - predicate declaresMoveAssignmentOperator() { not moveAssign = copyAssign } + /** + * Holds if this class declares a move constructor. + * + * This will be true if move constructor resolution found a non-implicit constructor that is not + * the copy constructor masquerading as a move constructor. + */ + predicate declaresMoveConstructor() { not moveCtor = copyCtor and isUserDeclared(moveCtor) } + + /** + * Holds if this class declares a move assignment operator. + * + * This will be true if move assignment resolution found a non-implicit operator that is not + * the copy assignment operator masquerading as a move assignment operator. + */ + predicate declaresMoveAssignmentOperator() { + not moveAssign = copyAssign and isUserDeclared(moveAssign) + } } diff --git a/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.ql b/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.ql index 1c10475ae8..66abfad039 100644 --- a/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.ql +++ b/cpp/misra/src/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.ql @@ -18,6 +18,7 @@ import cpp import codingstandards.cpp.misra import AnalyzableClass +import qtil.Qtil string missingKind(Class c) { not c.getAConstructor() instanceof MoveConstructor and @@ -45,5 +46,5 @@ where not c.isPod() and kinds = missingKinds(c) select c, - "Class '" + c.getName() + "' is not analyzable because the " + kinds + - " are not present in the CodeQL database." + "Class '" + c.getName() + "' is not analyzable because the " + kinds + " " + + Qtil::plural("is", "are", count(missingKind(c))) + " not present in the CodeQL database." diff --git a/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.expected b/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.expected index eeab1781ad..35857a311c 100644 --- a/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.expected +++ b/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.expected @@ -25,13 +25,13 @@ | test.cpp:258:7:258:43 | CopyEnabledCustomizedDtorNonCompliant | Class 'CopyEnabledCustomizedDtorNonCompliant' has customized the destructor, but does not customize the copy constructor. | | test.cpp:258:7:258:43 | CopyEnabledCustomizedDtorNonCompliant | Class 'CopyEnabledCustomizedDtorNonCompliant' has customized the destructor, but does not customize the move assignment operator. | | test.cpp:258:7:258:43 | CopyEnabledCustomizedDtorNonCompliant | Class 'CopyEnabledCustomizedDtorNonCompliant' has customized the destructor, but does not customize the move constructor. | -| test.cpp:307:7:307:47 | CopyAssignableCustomizedDtorNonCompliant1 | Class 'CopyAssignableCustomizedDtorNonCompliant1' has customized the destructor, but does not customize the move assignment operator. | -| test.cpp:317:7:317:47 | CopyAssignableCustomizedDtorNonCompliant2 | Class 'CopyAssignableCustomizedDtorNonCompliant2' has customized the destructor, but does not customize the move constructor. | -| test.cpp:327:7:327:47 | CopyAssignableCustomizedDtorNonCompliant3 | Class 'CopyAssignableCustomizedDtorNonCompliant3' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | -| test.cpp:337:7:337:47 | CopyAssignableCustomizedDtorNonCompliant4 | Class 'CopyAssignableCustomizedDtorNonCompliant4' has customized the destructor, but does not customize the move assignment operator. | -| test.cpp:337:7:337:47 | CopyAssignableCustomizedDtorNonCompliant4 | Class 'CopyAssignableCustomizedDtorNonCompliant4' has customized the destructor, but does not customize the move constructor. | -| test.cpp:347:7:347:47 | CopyAssignableCustomizedDtorNonCompliant5 | Class 'CopyAssignableCustomizedDtorNonCompliant5' has customized the destructor, but does not customize the move assignment operator. | -| test.cpp:347:7:347:47 | CopyAssignableCustomizedDtorNonCompliant5 | Class 'CopyAssignableCustomizedDtorNonCompliant5' has customized the destructor, but does not customize the move constructor. | -| test.cpp:357:7:357:33 | UnmovableBaseNonvirtualDtor | Class 'UnmovableBaseNonvirtualDtor' violates inheritance requirements for special member functions. | -| test.cpp:378:7:378:33 | UnmovablePrivateVirtualDtor | Class 'UnmovablePrivateVirtualDtor' violates inheritance requirements for special member functions. | -| test.cpp:405:7:405:30 | BaseVirtualProtectedDtor | Class 'BaseVirtualProtectedDtor' violates inheritance requirements for special member functions. | +| test.cpp:296:7:296:47 | CopyAssignableCustomizedDtorNonCompliant1 | Class 'CopyAssignableCustomizedDtorNonCompliant1' has customized the destructor, but does not customize the move assignment operator. | +| test.cpp:306:7:306:47 | CopyAssignableCustomizedDtorNonCompliant2 | Class 'CopyAssignableCustomizedDtorNonCompliant2' has customized the destructor, but does not customize the move constructor. | +| test.cpp:316:7:316:47 | CopyAssignableCustomizedDtorNonCompliant3 | Class 'CopyAssignableCustomizedDtorNonCompliant3' does not fall into a valid category (isUnmovable, move-only, or copy-enabled). | +| test.cpp:326:7:326:47 | CopyAssignableCustomizedDtorNonCompliant4 | Class 'CopyAssignableCustomizedDtorNonCompliant4' has customized the destructor, but does not customize the move assignment operator. | +| test.cpp:326:7:326:47 | CopyAssignableCustomizedDtorNonCompliant4 | Class 'CopyAssignableCustomizedDtorNonCompliant4' has customized the destructor, but does not customize the move constructor. | +| test.cpp:336:7:336:47 | CopyAssignableCustomizedDtorNonCompliant5 | Class 'CopyAssignableCustomizedDtorNonCompliant5' has customized the destructor, but does not customize the move assignment operator. | +| test.cpp:336:7:336:47 | CopyAssignableCustomizedDtorNonCompliant5 | Class 'CopyAssignableCustomizedDtorNonCompliant5' has customized the destructor, but does not customize the move constructor. | +| test.cpp:346:7:346:33 | UnmovableBaseNonvirtualDtor | Class 'UnmovableBaseNonvirtualDtor' violates inheritance requirements for special member functions. | +| test.cpp:369:7:369:33 | UnmovablePrivateVirtualDtor | Class 'UnmovablePrivateVirtualDtor' violates inheritance requirements for special member functions. | +| test.cpp:398:7:398:30 | BaseVirtualProtectedDtor | Class 'BaseVirtualProtectedDtor' violates inheritance requirements for special member functions. | diff --git a/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.expected b/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.expected index cf289aa069..8eae8e4fe2 100644 --- a/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.expected +++ b/cpp/misra/test/rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.expected @@ -1,8 +1,8 @@ -| test.cpp:363:7:363:32 | UnmovableNonvirtualDerived | Class 'UnmovableNonvirtualDerived' is not analyzable because the destructor and move assignment operator and move constructor are not present in the CodeQL database. | -| test.cpp:375:7:375:39 | UnmovableDerivedPublicVirtualDtor | Class 'UnmovableDerivedPublicVirtualDtor' is not analyzable because the move assignment operator and move constructor are not present in the CodeQL database. | -| test.cpp:389:7:389:40 | UnmovablePrivateVirtualDtorDerived | Class 'UnmovablePrivateVirtualDtorDerived' is not analyzable because the move assignment operator and move constructor are not present in the CodeQL database. | -| test.cpp:403:7:403:26 | ProtectedDtorDerived | Class 'ProtectedDtorDerived' is not analyzable because the destructor and move assignment operator are not present in the CodeQL database. | -| test.cpp:416:7:416:33 | VirtualProtectedDtorDerived | Class 'VirtualProtectedDtorDerived' is not analyzable because the move assignment operator are not present in the CodeQL database. | -| test.cpp:429:8:429:19 | TrivialClass | Class 'TrivialClass' is not analyzable because the destructor and move assignment operator and move constructor are not present in the CodeQL database. | -| test.cpp:435:7:435:21 | NonTrivialClass | Class 'NonTrivialClass' is not analyzable because the copy constructor and move assignment operator and move constructor are not present in the CodeQL database. | -| test.cpp:445:7:445:14 | CopyOnly | Class 'CopyOnly' is not analyzable because the destructor are not present in the CodeQL database. | +| test.cpp:353:7:353:32 | UnmovableNonvirtualDerived | Class 'UnmovableNonvirtualDerived' is not analyzable because the destructor and move assignment operator and move constructor are not present in the CodeQL database. | +| test.cpp:366:7:366:39 | UnmovableDerivedPublicVirtualDtor | Class 'UnmovableDerivedPublicVirtualDtor' is not analyzable because the move assignment operator and move constructor are not present in the CodeQL database. | +| test.cpp:381:7:381:40 | UnmovablePrivateVirtualDtorDerived | Class 'UnmovablePrivateVirtualDtorDerived' is not analyzable because the move assignment operator and move constructor are not present in the CodeQL database. | +| test.cpp:396:7:396:26 | ProtectedDtorDerived | Class 'ProtectedDtorDerived' is not analyzable because the destructor and move assignment operator are not present in the CodeQL database. | +| test.cpp:410:7:410:33 | VirtualProtectedDtorDerived | Class 'VirtualProtectedDtorDerived' is not analyzable because the move assignment operator is not present in the CodeQL database. | +| test.cpp:423:8:423:19 | TrivialClass | Class 'TrivialClass' is not analyzable because the destructor and move assignment operator and move constructor are not present in the CodeQL database. | +| test.cpp:429:7:429:21 | NonTrivialClass | Class 'NonTrivialClass' is not analyzable because the copy constructor and move assignment operator and move constructor are not present in the CodeQL database. | +| test.cpp:439:7:439:14 | CopyOnly | Class 'CopyOnly' is not analyzable because the destructor is not present in the CodeQL database. | diff --git a/cpp/misra/test/rules/RULE-15-0-1/test.cpp b/cpp/misra/test/rules/RULE-15-0-1/test.cpp index 17810bbd7b..e8c3255733 100644 --- a/cpp/misra/test/rules/RULE-15-0-1/test.cpp +++ b/cpp/misra/test/rules/RULE-15-0-1/test.cpp @@ -282,25 +282,14 @@ class CopyEnabledCustomizedDtorCompliant2 { // COMPLIANT: copy-enabled (2) ~CopyEnabledCustomizedDtorCompliant2() CUSTOMIZED; }; -// copy-assignable class with both move operations customized +// copy-assignable class with both move operations not declared class CopyAssignableCustomizedDtorCompliant1 { // COMPLIANT public: COPY_CTOR(CopyAssignableCustomizedDtorCompliant1) CUSTOMIZED; - MOVE_CTOR(CopyAssignableCustomizedDtorCompliant1) CUSTOMIZED; - // No move constructor declared - COPY_ASSIGN(CopyAssignableCustomizedDtorCompliant1) DELETED; - MOVE_ASSIGN(CopyAssignableCustomizedDtorCompliant1) DELETED; - ~CopyAssignableCustomizedDtorCompliant1() CUSTOMIZED; -}; - -// copy-assignable class with both move operations not declared -class CopyAssignableCustomizedDtorCompliant2 { // COMPLIANT -public: - COPY_CTOR(CopyAssignableCustomizedDtorCompliant2) CUSTOMIZED; // Move constructor is not declared - COPY_ASSIGN(CopyAssignableCustomizedDtorCompliant2) CUSTOMIZED; + COPY_ASSIGN(CopyAssignableCustomizedDtorCompliant1) CUSTOMIZED; // Move assignment operator is not declared - DTOR(CopyAssignableCustomizedDtorCompliant2) CUSTOMIZED + DTOR(CopyAssignableCustomizedDtorCompliant1) CUSTOMIZED }; // copy-assignable class with only one of move operations not declared @@ -360,6 +349,7 @@ class UnmovableBaseNonvirtualDtor { // NON_COMPLIANT DELETED, DELETED, DEFAULTED) }; +// NON_COMPLIANT - reported by the audit query for having missing info. class UnmovableNonvirtualDerived : public UnmovableBaseNonvirtualDtor {}; class UnmovableBasePublicVirtualDtor { // COMPLIANT @@ -372,6 +362,7 @@ class UnmovableBasePublicVirtualDtor { // COMPLIANT virtual ~UnmovableBasePublicVirtualDtor() = default; }; +// NON_COMPLIANT - reported by the audit query for having missing info. class UnmovableDerivedPublicVirtualDtor : public UnmovableBasePublicVirtualDtor {}; @@ -386,6 +377,7 @@ class UnmovablePrivateVirtualDtor { // NON_COMPLIANT virtual ~UnmovablePrivateVirtualDtor() = default; }; +// NON_COMPLIANT - reported by the audit query for having missing info. class UnmovablePrivateVirtualDtorDerived : public UnmovablePrivateVirtualDtor { }; @@ -400,6 +392,7 @@ class BaseProtectedDtor { // COMPLIANT ~BaseProtectedDtor() = default; }; +// NON_COMPLIANT - reported by the audit query for having missing info. class ProtectedDtorDerived : public BaseProtectedDtor {}; class BaseVirtualProtectedDtor { // NON_COMPLIANT @@ -413,6 +406,7 @@ class BaseVirtualProtectedDtor { // NON_COMPLIANT virtual ~BaseVirtualProtectedDtor() = default; }; +// NON_COMPLIANT - reported by the audit query for having missing info. class VirtualProtectedDtorDerived : public BaseVirtualProtectedDtor {}; } // namespace additional_requirements From 51985b938e063a7f67a33b4720e7a18acbcd31b7 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Thu, 30 Apr 2026 14:07:54 -0700 Subject: [PATCH 15/15] Give code examples for when copy operations masquerade as moves. --- .../src/rules/RULE-15-0-1/AnalyzableClass.qll | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/cpp/misra/src/rules/RULE-15-0-1/AnalyzableClass.qll b/cpp/misra/src/rules/RULE-15-0-1/AnalyzableClass.qll index 7c58a7f809..11a194a521 100644 --- a/cpp/misra/src/rules/RULE-15-0-1/AnalyzableClass.qll +++ b/cpp/misra/src/rules/RULE-15-0-1/AnalyzableClass.qll @@ -64,6 +64,23 @@ private predicate implicitMoveIsSuppressed(Class c) { /** * Returns the move constructor of the class `c` if it exists, or the copy constructor if it does * not exist and the implicit definition was suppressed by the compiler. + * + * For example: + * ```cpp + * class OnlyCopyCtor { + * public: + * OnlyCopyCtor(const OnlyCopyCtor &) = default; + * }; + * + * static_assert(std::is_copy_constructible_v); // Succeeds + * static_assert(std::is_move_constructible_v); // Also succeeds + * ``` + * + * Note that without the declared copy constructor, the compiler may define an implicit move + * constructor. + * + * Additionally note that if the move constructor was declared as `= delete;`, then the second + * assertion in the above example would fail. */ private Constructor getMoveConstructor(Class c) { if @@ -76,6 +93,23 @@ private Constructor getMoveConstructor(Class c) { /** * Returns the move assignment operator of the class `c` if it exists, or the copy assignment * operator if it does not exist and the implicit definition was suppressed by the compiler. + * + * For example: + * ```cpp + * class OnlyCopyAssign { + * public: + * OnlyCopyAssign& operator=(const OnlyCopyAssign &) = default; + * }; + * + * static_assert(std::is_copy_assignable_v); // Succeeds + * static_assert(std::is_move_assignable_v); // Also succeeds + * ``` + * + * Note that without the declared copy assignment operator, the compiler may define an implicit move + * assignment operator. + * + * Additionally note that if the move assignment operator was declared as `= delete;`, then the second + * assertion in the above example would fail. */ private Operator getMoveAssign(Class c) { if