-
Notifications
You must be signed in to change notification settings - Fork 76
Narrowly focused implementation of RULE 15-0-1 #1121
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
a31cf70
ba6c2ca
a31aac3
057e33b
3584a15
d37827a
5000f27
184da86
e63c088
658571a
6e9dde1
d04d4df
96e2ed0
ce43802
a1c1a9e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,112 @@ | ||||||
| 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() 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)) | ||||||
|
||||||
| isUserDeclared(c.getAConstructor().(CopyAssignmentOperator)) | |
| isUserDeclared(c.getAMemberFunction().(CopyAssignmentOperator)) |
Copilot
AI
Apr 30, 2026
There was a problem hiding this comment.
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
AI
Apr 30, 2026
There was a problem hiding this comment.
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.
| 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 ") } | ||||||||||
|
||||||||||
| string missingKinds(Class c) { result = concat(missingKind(c), " and ") } | |
| string missingKinds(Class c) { result = strictconcat(missingKind(c), " and ") } |
Copilot
AI
Apr 30, 2026
There was a problem hiding this comment.
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.
| "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 + "." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This
.qllfile 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: