Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
112 changes: 112 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,112 @@
import cpp

Comment on lines +1 to +2
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This .qll file is missing a top-level QLDoc header comment. Repository guidelines require library files to start with a documentation comment explaining the purpose of the library.

See below for a potential fix:

@ -1,3 +1,7 @@
/**
 * Provides classes and predicates for identifying analyzable classes and
 * relevant special member functions for MISRA rule `RULE-15-0-1`.
 */

Copilot uses AI. Check for mistakes.
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()
}

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))
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

implicitMoveIsSuppressed checks c.getAConstructor().(CopyAssignmentOperator), but copy assignment operators are not constructors. This makes the suppression logic incorrect for classes with a user-declared copy assignment operator, which can affect how move operations are modeled. Use c.getAMemberFunction().(CopyAssignmentOperator) (or equivalent) here.

Suggested change
isUserDeclared(c.getAConstructor().(CopyAssignmentOperator))
isUserDeclared(c.getAMemberFunction().(CopyAssignmentOperator))

Copilot uses AI. Check for mistakes.
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)
}
Comment on lines +31 to +45
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getMoveConstructor and getMoveAssign are public predicates/functions in a library file but have no QLDoc. Either make them private if they are internal helpers, or add QLDoc documenting their behavior and parameters.

Copilot uses AI. Check for mistakes.

newtype TSpecialMember =
TMoveConstructor() or
TMoveAssignmentOperator() or
TCopyConstructor() or
TCopyAssignmentOperator() or
TDestructor()

class AnalyzableClass extends Class {
CopyConstructor copyCtor;
// The move constructor may be suppressed, and the copy constructor may be used during moves.
Comment on lines +47 to +56
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TSpecialMember and AnalyzableClass are public declarations without QLDoc. Repository guidelines require all public types/classes/newtypes in .qll libraries to be documented, or marked private if they are not part of the public API.

Copilot uses AI. Check for mistakes.
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() {
copyCtor = this.getAConstructor() and
moveCtor = getMoveConstructor(this) and
copyAssign = this.getAMemberFunction() and
moveAssign = getMoveAssign(this) 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) and
declaresMoveConstructor()
or
s instanceof TMoveAssignmentOperator and
isMemberCustomized(moveAssign) and
declaresMoveAssignmentOperator()
or
s instanceof TCopyConstructor and isMemberCustomized(copyCtor)
or
s instanceof TCopyAssignmentOperator and isMemberCustomized(copyAssign)
or
s instanceof TDestructor and isMemberCustomized(dtor)
}

predicate declaresMoveConstructor() { not moveCtor = copyCtor }

predicate declaresMoveAssignmentOperator() { not moveAssign = copyAssign }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/**
* @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."
)
}

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
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."
)
}

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 ") }
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missingKinds uses concat(...), which can yield non-deterministic ordering when multiple kinds are missing, making alert messages (and .expected files) flaky. Consider using strictconcat(...) or otherwise imposing a stable order on the joined kinds.

Suggested change
string missingKinds(Class c) { result = concat(missingKind(c), " and ") }
string missingKinds(Class c) { result = strictconcat(missingKind(c), " and ") }

Copilot uses AI. Check for mistakes.

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 on lines +48 to +49
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The message always uses the plural verb "are" ("the X are not present"), which is grammatically incorrect when only one kind is missing (for example, "the destructor"). Adjust the message to use "is" for a single missing kind, or restructure the sentence to avoid number agreement issues.

Suggested change
"Class '" + c.getName() + "' is not analyzable because the " + kinds +
" are not present in the CodeQL database."
"Class '" + c.getName() + "' is not analyzable because the CodeQL database does not " +
"contain the " + kinds + "."

Copilot uses AI. Check for mistakes.
Loading
Loading