Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
a31cf70
First round tests
MichaelRFairhurst Apr 29, 2026
ba6c2ca
First implementation
MichaelRFairhurst Apr 29, 2026
a31aac3
All tests passing for main query
MichaelRFairhurst Apr 29, 2026
057e33b
Implement audit query
MichaelRFairhurst Apr 29, 2026
3584a15
Add some cases
jeongsoolee09 Apr 29, 2026
d37827a
Use correct audit tag
MichaelRFairhurst Apr 29, 2026
5000f27
Just omit functions by hand that's supposed to not be declared
jeongsoolee09 Apr 29, 2026
184da86
Merge branch 'michaelrfairhurst/classes-3-take-2-rule-15-0-1' of gith…
jeongsoolee09 Apr 29, 2026
e63c088
Fix RuleMetadata.qll (package from a different rule/branch)
MichaelRFairhurst Apr 30, 2026
658571a
clang format
MichaelRFairhurst Apr 30, 2026
6e9dde1
Change the label of a case and break it down
jeongsoolee09 Apr 30, 2026
d04d4df
Merge branch 'michaelrfairhurst/classes-3-take-2-rule-15-0-1' of gith…
jeongsoolee09 Apr 30, 2026
96e2ed0
Some formatting, make copy-enabled class no longer copy-assignable
MichaelRFairhurst Apr 30, 2026
ce43802
Handle suppressed move operations
MichaelRFairhurst Apr 30, 2026
a1c1a9e
Updates to expected file
MichaelRFairhurst Apr 30, 2026
2813136
Address feedback
MichaelRFairhurst Apr 30, 2026
51985b9
Give code examples for when copy operations masquerade as moves.
MichaelRFairhurst Apr 30, 2026
61a4dd9
Review feedback
MichaelRFairhurst May 1, 2026
e4cea41
Address feedback
MichaelRFairhurst May 5, 2026
705b598
Merge branch 'main' into michaelrfairhurst/classes-3-take-2-rule-15-0-1
jeongsoolee09 May 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions cpp/common/src/codingstandards/cpp/exclusions/cpp/Classes3.qll
Original file line number Diff line number Diff line change
@@ -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()))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import BannedSyntax
import BannedTypes
import Classes
import Classes2
import Classes3
import Classes4
import Comments
import Concurrency
Expand Down Expand Up @@ -126,6 +127,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
Expand Down Expand Up @@ -234,6 +236,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
Expand Down
70 changes: 70 additions & 0 deletions cpp/misra/src/rules/RULE-15-0-1/AnalyzableClass.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import cpp

Comment thread
MichaelRFairhurst marked this conversation as resolved.
private predicate isUsable(MemberFunction f) {
not f.isDeleted() and
f.isPublic()
}

private predicate isMemberCustomized(MemberFunction f) {
exists(f.getDefinition()) and
not f.isDefaulted() and
not f.isDeleted() and
not f.isCompilerGenerated()
}

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)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/**
* @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
import AnalyzableClass

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 isUnmovable(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
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) {
c.isCustomized(TDestructor()) and
(
c.moveConstructible() and
not c.isCustomized(TMoveConstructor()) and
reason = "has customized the destructor, but does not customize the move constructor."
or
c.moveAssignable() and
not c.isCustomized(TMoveAssignmentOperator()) and
reason = "has customized the destructor, but does not customize the move assignment operator."
or
c.copyConstructible() and
not c.isCustomized(TCopyConstructor()) and
reason = "has customized the destructor, but does not customize the copy constructor."
or
c.copyAssignable() and
not c.isCustomized(TCopyAssignmentOperator()) and
reason = "has customized the destructor, but does not customize the copy assignment operator."
)
}
Comment thread
jeongsoolee09 marked this conversation as resolved.

predicate isPublicBase(AnalyzableClass c) {
exists(ClassDerivation d |
d.getBaseClass() = c and
d.hasSpecifier("public")
)
}

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 = "does not fall into a valid category (isUnmovable, move-only, or copy-enabled)."
or
violatesCustomizedMoveOrCopyRequirements(c, message)
or
violatesCustomizedDestructorRequirements(c, message)
or
not satisfiesInheritanceRequirements(c) and
message = "violates inheritance requirements for special member functions."
)
select c, "Class '" + c.getName() + "' " + message
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* @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
* external/misra/audit
* maintainability
* external/misra/enforcement/decidable
* external/misra/obligation/required
*/

import cpp
import codingstandards.cpp.misra
import AnalyzableClass

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 ") }
Comment thread
MichaelRFairhurst marked this conversation as resolved.

from Class c, string kinds
where
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."
Comment thread
MichaelRFairhurst marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
| 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. |
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctions.ql
Original file line number Diff line number Diff line change
@@ -0,0 +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. |
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rules/RULE-15-0-1/ImproperlyProvidedSpecialMemberFunctionsAudit.ql
Loading
Loading