From b4af29910173cf709983b24f5eaf8c680186bc7f Mon Sep 17 00:00:00 2001 From: Kristen Newbury Date: Thu, 2 Apr 2026 16:23:36 -0400 Subject: [PATCH 01/10] Add RULE-6-9-1 --- .../cpp/exclusions/cpp/Declarations5.qll | 44 +++++++++++++++++ .../cpp/exclusions/cpp/RuleMetadata.qll | 3 ++ .../RULE-6-9-1/TypeAliasesDeclaration.ql | 33 +++++++++++++ .../TypeAliasesDeclaration.expected | 2 + .../RULE-6-9-1/TypeAliasesDeclaration.qlref | 1 + cpp/misra/test/rules/RULE-6-9-1/test.cpp | 15 ++++++ rule_packages/cpp/Declarations5.json | 47 +++++++++++++++++++ rules.csv | 4 +- 8 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 cpp/common/src/codingstandards/cpp/exclusions/cpp/Declarations5.qll create mode 100644 cpp/misra/src/rules/RULE-6-9-1/TypeAliasesDeclaration.ql create mode 100644 cpp/misra/test/rules/RULE-6-9-1/TypeAliasesDeclaration.expected create mode 100644 cpp/misra/test/rules/RULE-6-9-1/TypeAliasesDeclaration.qlref create mode 100644 cpp/misra/test/rules/RULE-6-9-1/test.cpp create mode 100644 rule_packages/cpp/Declarations5.json diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Declarations5.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Declarations5.qll new file mode 100644 index 000000000..ff4806a5b --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Declarations5.qll @@ -0,0 +1,44 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Declarations5Query = + TMemberFunctionsRefqualifiedQuery() or + TTypeAliasesDeclarationQuery() + +predicate isDeclarations5QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `memberFunctionsRefqualified` query + Declarations5Package::memberFunctionsRefqualifiedQuery() and + queryId = + // `@id` for the `memberFunctionsRefqualified` query + "cpp/misra/member-functions-refqualified" and + ruleId = "RULE-6-8-4" and + category = "advisory" + or + query = + // `Query` instance for the `typeAliasesDeclaration` query + Declarations5Package::typeAliasesDeclarationQuery() and + queryId = + // `@id` for the `typeAliasesDeclaration` query + "cpp/misra/type-aliases-declaration" and + ruleId = "RULE-6-9-1" and + category = "required" +} + +module Declarations5Package { + Query memberFunctionsRefqualifiedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `memberFunctionsRefqualified` query + TQueryCPP(TDeclarations5PackageQuery(TMemberFunctionsRefqualifiedQuery())) + } + + Query typeAliasesDeclarationQuery() { + //autogenerate `Query` type + result = + // `Query` type for `typeAliasesDeclaration` query + TQueryCPP(TDeclarations5PackageQuery(TTypeAliasesDeclarationQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll index 652d12307..f7a0fac93 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll @@ -33,6 +33,7 @@ import DeadCode8 import DeadCode9 import Declarations import Declarations1 +import Declarations5 import ExceptionSafety import Exceptions1 import Exceptions2 @@ -129,6 +130,7 @@ newtype TCPPQuery = TDeadCode9PackageQuery(DeadCode9Query q) or TDeclarationsPackageQuery(DeclarationsQuery q) or TDeclarations1PackageQuery(Declarations1Query q) or + TDeclarations5PackageQuery(Declarations5Query q) or TExceptionSafetyPackageQuery(ExceptionSafetyQuery q) or TExceptions1PackageQuery(Exceptions1Query q) or TExceptions2PackageQuery(Exceptions2Query q) or @@ -225,6 +227,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat isDeadCode9QueryMetadata(query, queryId, ruleId, category) or isDeclarationsQueryMetadata(query, queryId, ruleId, category) or isDeclarations1QueryMetadata(query, queryId, ruleId, category) or + isDeclarations5QueryMetadata(query, queryId, ruleId, category) or isExceptionSafetyQueryMetadata(query, queryId, ruleId, category) or isExceptions1QueryMetadata(query, queryId, ruleId, category) or isExceptions2QueryMetadata(query, queryId, ruleId, category) or diff --git a/cpp/misra/src/rules/RULE-6-9-1/TypeAliasesDeclaration.ql b/cpp/misra/src/rules/RULE-6-9-1/TypeAliasesDeclaration.ql new file mode 100644 index 000000000..d8898377d --- /dev/null +++ b/cpp/misra/src/rules/RULE-6-9-1/TypeAliasesDeclaration.ql @@ -0,0 +1,33 @@ +/** + * @id cpp/misra/type-aliases-declaration + * @name RULE-6-9-1: The same type aliases shall be used in all declarations of the same entity + * @description Using different type aliases on redeclarations can make code hard to understand and + * maintain. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-6-9-1 + * maintainability + * readability + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra + +from DeclarationEntry decl1, DeclarationEntry decl2, TypedefType t +where + not isExcluded(decl1, Declarations5Package::typeAliasesDeclarationQuery()) and + not isExcluded(decl2, Declarations5Package::typeAliasesDeclarationQuery()) and + not decl1 = decl2 and + decl1.getDeclaration() = decl2.getDeclaration() and + t.getATypeNameUse() = decl1 and + not t.getATypeNameUse() = decl2 and + //exception cases - we dont want to disallow struct typedef name use + not t.getBaseType() instanceof Struct and + not t.getBaseType() instanceof Enum +select decl1, + "Declaration entry has a different type alias than $@ where the type alias used is '$@'.", decl2, + decl2.getName(), t, t.getName() diff --git a/cpp/misra/test/rules/RULE-6-9-1/TypeAliasesDeclaration.expected b/cpp/misra/test/rules/RULE-6-9-1/TypeAliasesDeclaration.expected new file mode 100644 index 000000000..db0ef7b72 --- /dev/null +++ b/cpp/misra/test/rules/RULE-6-9-1/TypeAliasesDeclaration.expected @@ -0,0 +1,2 @@ +| test.cpp:4:5:4:5 | definition of i | Declaration entry has a different type alias than $@ where the type alias used is '$@'. | test.cpp:5:12:5:12 | declaration of i | i | test.cpp:1:13:1:15 | INT | INT | +| test.cpp:11:20:11:20 | declaration of i | Declaration entry has a different type alias than $@ where the type alias used is '$@'. | test.cpp:10:12:10:12 | declaration of i | i | test.cpp:2:7:2:11 | Index | Index | diff --git a/cpp/misra/test/rules/RULE-6-9-1/TypeAliasesDeclaration.qlref b/cpp/misra/test/rules/RULE-6-9-1/TypeAliasesDeclaration.qlref new file mode 100644 index 000000000..a3cf4823c --- /dev/null +++ b/cpp/misra/test/rules/RULE-6-9-1/TypeAliasesDeclaration.qlref @@ -0,0 +1 @@ +rules/RULE-6-9-1/TypeAliasesDeclaration.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-6-9-1/test.cpp b/cpp/misra/test/rules/RULE-6-9-1/test.cpp new file mode 100644 index 000000000..dfc18becf --- /dev/null +++ b/cpp/misra/test/rules/RULE-6-9-1/test.cpp @@ -0,0 +1,15 @@ +typedef int INT; +using Index = int; + +INT i; +extern int i; // NON_COMPLIANT + +INT j; +extern INT j; // COMPLIANT + +void g(int i); +void g(Index const i); // NON_COMPLIANT + +void h(Index i); +void h(Index const i); // COMPLIANT +void h(int *i); // COMPLIANT \ No newline at end of file diff --git a/rule_packages/cpp/Declarations5.json b/rule_packages/cpp/Declarations5.json new file mode 100644 index 000000000..4f4c08e7d --- /dev/null +++ b/rule_packages/cpp/Declarations5.json @@ -0,0 +1,47 @@ +{ + "MISRA-C++-2023": { + "RULE-6-8-4": { + "properties": { + "enforcement": "decidable", + "obligation": "advisory" + }, + "queries": [ + { + "description": "Member functions that return references to temporary objects (or subobjects) can lead to dangling pointers.", + "kind": "problem", + "name": "Member functions returning references to their object should be refqualified appropriately", + "precision": "very-high", + "severity": "error", + "short_name": "MemberFunctionsRefqualified", + "tags": [ + "correctness", + "scope/single-translation-unit" + ] + } + ], + "title": "Member functions returning references to their object should be refqualified appropriately" + }, + "RULE-6-9-1": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Using different type aliases on redeclarations can make code hard to understand and maintain.", + "kind": "problem", + "name": "The same type aliases shall be used in all declarations of the same entity", + "precision": "very-high", + "severity": "warning", + "short_name": "TypeAliasesDeclaration", + "tags": [ + "maintainability", + "readability", + "scope/single-translation-unit" + ] + } + ], + "title": "The same type aliases shall be used in all declarations of the same entity" + } + } +} \ No newline at end of file diff --git a/rules.csv b/rules.csv index 008020f37..7863e5791 100644 --- a/rules.csv +++ b/rules.csv @@ -867,8 +867,8 @@ cpp,MISRA-C++-2023,RULE-6-7-2,Yes,Required,Decidable,Single Translation Unit,Glo cpp,MISRA-C++-2023,RULE-6-8-1,Yes,Required,Undecidable,System,An object shall not be accessed outside of its lifetime,A3-8-1,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-6-8-2,Yes,Mandatory,Decidable,Single Translation Unit,A function must not return a reference or a pointer to a local variable with automatic storage duration,M7-5-1,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-6-8-3,Yes,Required,Decidable,Single Translation Unit,An assignment operator shall not assign the address of an object with automatic storage duration to an object with a greater lifetime,,Lifetime,Medium, -cpp,MISRA-C++-2023,RULE-6-8-4,Yes,Advisory,Decidable,Single Translation Unit,Member functions returning references to their object should be refqualified appropriately,,Declarations2,Medium, -cpp,MISRA-C++-2023,RULE-6-9-1,Yes,Required,Decidable,Single Translation Unit,The same type aliases shall be used in all declarations of the same entity,,Declarations2,Medium, +cpp,MISRA-C++-2023,RULE-6-8-4,Yes,Advisory,Decidable,Single Translation Unit,Member functions returning references to their object should be refqualified appropriately,,Declarations5,Medium, +cpp,MISRA-C++-2023,RULE-6-9-1,Yes,Required,Decidable,Single Translation Unit,The same type aliases shall be used in all declarations of the same entity,,Declarations5,Medium, cpp,MISRA-C++-2023,RULE-6-9-2,Yes,Advisory,Decidable,Single Translation Unit,The names of the standard signed integer types and standard unsigned integer types should not be used,A3-9-1,BannedAPIs,Easy, cpp,MISRA-C++-2023,RULE-7-0-1,Yes,Required,Decidable,Single Translation Unit,There shall be no conversion from type bool,,Conversions,Easy, cpp,MISRA-C++-2023,RULE-7-0-2,Yes,Required,Decidable,Single Translation Unit,There shall be no conversion to type bool,,Conversions,Easy, From 603244e89dbbb66186bd3e24469131ecbdb19dbb Mon Sep 17 00:00:00 2001 From: Kristen Newbury Date: Tue, 7 Apr 2026 14:18:39 -0400 Subject: [PATCH 02/10] Add RULE-6-8-4 --- .../RULE-6-8-4/MemberFunctionsRefqualified.ql | 110 ++++++++++++++++++ .../MemberFunctionsRefqualified.expected | 7 ++ .../MemberFunctionsRefqualified.qlref | 1 + cpp/misra/test/rules/RULE-6-8-4/test.cpp | 54 +++++++++ 4 files changed, 172 insertions(+) create mode 100644 cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql create mode 100644 cpp/misra/test/rules/RULE-6-8-4/MemberFunctionsRefqualified.expected create mode 100644 cpp/misra/test/rules/RULE-6-8-4/MemberFunctionsRefqualified.qlref create mode 100644 cpp/misra/test/rules/RULE-6-8-4/test.cpp diff --git a/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql b/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql new file mode 100644 index 000000000..826a80a1d --- /dev/null +++ b/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql @@ -0,0 +1,110 @@ +/** + * @id cpp/misra/member-functions-refqualified + * @name RULE-6-8-4: Member functions returning references to their object should be refqualified appropriately + * @description Member functions that return references to temporary objects (or subobjects) can + * lead to dangling pointers. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-6-8-4 + * correctness + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.types.Compatible +import codingstandards.cpp.Operator + +abstract class MembersReturningObjectOrSubobject extends MemberFunction { + string toString() { result = "Members returning object or subobject" } +} + +class MembersReturningObject extends MembersReturningObjectOrSubobject { + MembersReturningObject() { + exists(ReturnStmt r, ThisExpr t | + r.getEnclosingFunction() = this and + ( + r.getAChild*() = t + or + exists(PointerDereferenceExpr p | + p.getAChild*() = t and + r.getAChild*() = p + ) + ) and + t.getActualType().stripType() = this.getDeclaringType() + ) + } +} + +class MembersReturningSubObject extends MembersReturningObjectOrSubobject { + MembersReturningSubObject() { + exists(ReturnStmt r, FieldSubObjectDeclaration field | + r.getEnclosingFunction() = this and + ( + r.getAChild*() = field.(Field).getAnAccess() + or + exists(PointerDereferenceExpr p | + p.getAChild*() = field.(Field).getAnAccess() and + r.getAChild*() = p + ) + ) and + field.(Field).getDeclaringType() = this.getDeclaringType() + ) + } +} + +predicate relevantTypes(Type a, Type b) { + exists(MembersReturningObject f, MemberFunction overload | + f.getAnOverload() = overload and + exists(int i | + f.getParameter(i).getType() = a and + overload.getParameter(i).getType() = b + ) + ) +} + +class AppropriatelyQualified extends MembersReturningObjectOrSubobject { + AppropriatelyQualified() { + //non-const-lvalue-ref-qualified + this.isLValueRefQualified() and + not this.hasSpecifier("const") + or + //const-lvalue-ref-qualified + this.isLValueRefQualified() and + this.hasSpecifier("const") and + //and overload exists that is rvalue-ref-qualified + exists(MemberFunction overload | + this.getAnOverload() = overload and + overload.isRValueRefQualified() and + //and has same param list + forall(int i | exists([this, overload].getParameter(i)) | + TypeEquivalence::equalTypes(this.getParameter(i) + .getType(), overload.getParameter(i).getType()) + ) + ) + } +} + +/** + * Fields that are not reference type can be subobjects + */ +class FieldSubObjectDeclaration extends Declaration { + FieldSubObjectDeclaration() { + not this.getADeclarationEntry().getType() instanceof ReferenceType and + this instanceof Field + } +} + +class DefaultedAssignmentOperator extends AssignmentOperator { + DefaultedAssignmentOperator() { this.isDefaulted() } +} + +from MembersReturningObjectOrSubobject f +where + not isExcluded(f, Declarations5Package::memberFunctionsRefqualifiedQuery()) and + not f instanceof AppropriatelyQualified and + not f instanceof DefaultedAssignmentOperator +select f, "Member function is not properly ref qualified." diff --git a/cpp/misra/test/rules/RULE-6-8-4/MemberFunctionsRefqualified.expected b/cpp/misra/test/rules/RULE-6-8-4/MemberFunctionsRefqualified.expected new file mode 100644 index 000000000..c59015b98 --- /dev/null +++ b/cpp/misra/test/rules/RULE-6-8-4/MemberFunctionsRefqualified.expected @@ -0,0 +1,7 @@ +| test.cpp:10:7:10:11 | Members returning object or subobject | Member function is not properly ref qualified. | +| test.cpp:12:14:12:20 | Members returning object or subobject | Member function is not properly ref qualified. | +| test.cpp:24:12:24:23 | Members returning object or subobject | Member function is not properly ref qualified. | +| test.cpp:28:6:28:18 | Members returning object or subobject | Member function is not properly ref qualified. | +| test.cpp:42:16:42:16 | Members returning object or subobject | Member function is not properly ref qualified. | +| test.cpp:42:16:42:16 | Members returning object or subobject | Member function is not properly ref qualified. | +| test.cpp:42:16:42:16 | Members returning object or subobject | Member function is not properly ref qualified. | diff --git a/cpp/misra/test/rules/RULE-6-8-4/MemberFunctionsRefqualified.qlref b/cpp/misra/test/rules/RULE-6-8-4/MemberFunctionsRefqualified.qlref new file mode 100644 index 000000000..c214d5ca1 --- /dev/null +++ b/cpp/misra/test/rules/RULE-6-8-4/MemberFunctionsRefqualified.qlref @@ -0,0 +1 @@ +rules/RULE-6-8-4/MemberFunctionsRefqualified.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-6-8-4/test.cpp b/cpp/misra/test/rules/RULE-6-8-4/test.cpp new file mode 100644 index 000000000..936fa5386 --- /dev/null +++ b/cpp/misra/test/rules/RULE-6-8-4/test.cpp @@ -0,0 +1,54 @@ +struct A { + int a; + int &b; + + int &geta() & { return a; } // COMPLIANT + + int const &geta2() const & { // COMPLIANT -- due to overload below + return a; + } + int geta2() && { return a; } // NON_COMPLIANT + + int const &getabad() const & { // NON_COMPLIANT -- no overload provided + return a; + } + + int getb() && { return b; } // COMPLIANT -- b is not a subobject + + A const *getstruct() const & { // COMPLIANT -- due to overload below + return this; + } + + A getstruct() const && = delete; + + A const *getstructbad() const & { // NON_COMPLIANT -- no overload provided + return this; + } + + A &getstructbad2() { return *this; } // NON_COMPLIANT +}; + +class C { + C *f() { // COMPLIANT -- this is not explicitly designated therefore this is + // not + // relevant for this rule + C *thisclass = this; + return thisclass; + } +}; + +struct Templ { + template + Templ const *f(T) const & { // NON_COMPLIANT -- for an instantiation below + return this; + } + + void f(int) const && = delete; +}; + +void f(int p, float p1) { + Templ t; + t.f(p); + t.f(p1); // instantiation that causes issue due to parameter list type meaning + // there is no overload +} \ No newline at end of file From b7f2378a0cfd08f637171bc263926a983d11ea56 Mon Sep 17 00:00:00 2001 From: Kristen Newbury Date: Tue, 7 Apr 2026 14:42:40 -0400 Subject: [PATCH 03/10] Fix rules duplicate rows --- rules.csv | 2 -- 1 file changed, 2 deletions(-) diff --git a/rules.csv b/rules.csv index 0ccc6a580..beb82215d 100644 --- a/rules.csv +++ b/rules.csv @@ -869,8 +869,6 @@ cpp,MISRA-C++-2023,RULE-6-8-2,Yes,Mandatory,Decidable,Single Translation Unit,A cpp,MISRA-C++-2023,RULE-6-8-3,Yes,Required,Decidable,Single Translation Unit,An assignment operator shall not assign the address of an object with automatic storage duration to an object with a greater lifetime,,Lifetime,Medium, cpp,MISRA-C++-2023,RULE-6-8-4,Yes,Advisory,Decidable,Single Translation Unit,Member functions returning references to their object should be refqualified appropriately,,Declarations5,Medium, cpp,MISRA-C++-2023,RULE-6-9-1,Yes,Required,Decidable,Single Translation Unit,The same type aliases shall be used in all declarations of the same entity,,Declarations5,Medium, -cpp,MISRA-C++-2023,RULE-6-8-4,Yes,Advisory,Decidable,Single Translation Unit,Member functions returning references to their object should be refqualified appropriately,,Declarations3,Medium, -cpp,MISRA-C++-2023,RULE-6-9-1,Yes,Required,Decidable,Single Translation Unit,The same type aliases shall be used in all declarations of the same entity,,Declarations3,Medium, cpp,MISRA-C++-2023,RULE-6-9-2,Yes,Advisory,Decidable,Single Translation Unit,The names of the standard signed integer types and standard unsigned integer types should not be used,A3-9-1,BannedAPIs,Easy, cpp,MISRA-C++-2023,RULE-7-0-1,Yes,Required,Decidable,Single Translation Unit,There shall be no conversion from type bool,,Conversions,Easy, cpp,MISRA-C++-2023,RULE-7-0-2,Yes,Required,Decidable,Single Translation Unit,There shall be no conversion to type bool,,Conversions,Easy, From 17f18cc33fb8c274ef7073e9c5e88ce0b63794a6 Mon Sep 17 00:00:00 2001 From: Kristen Newbury Date: Tue, 7 Apr 2026 15:11:18 -0400 Subject: [PATCH 04/10] Fix implementation RULE-6-8-4 --- .../codingstandards/cpp/exclusions/cpp/RuleMetadata.qll | 6 +++--- .../src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql | 9 ++++++++- .../RULE-6-8-4/MemberFunctionsRefqualified.expected | 1 - cpp/misra/test/rules/RULE-6-8-4/test.cpp | 2 +- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll index 5a39757c2..4ac25ca67 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll @@ -33,8 +33,8 @@ import DeadCode8 import DeadCode9 import Declarations import Declarations1 -import Declarations5 import Declarations2 +import Declarations5 import ExceptionSafety import Exceptions1 import Exceptions2 @@ -131,8 +131,8 @@ newtype TCPPQuery = TDeadCode9PackageQuery(DeadCode9Query q) or TDeclarationsPackageQuery(DeclarationsQuery q) or TDeclarations1PackageQuery(Declarations1Query q) or - TDeclarations5PackageQuery(Declarations5Query q) or TDeclarations2PackageQuery(Declarations2Query q) or + TDeclarations5PackageQuery(Declarations5Query q) or TExceptionSafetyPackageQuery(ExceptionSafetyQuery q) or TExceptions1PackageQuery(Exceptions1Query q) or TExceptions2PackageQuery(Exceptions2Query q) or @@ -229,8 +229,8 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat isDeadCode9QueryMetadata(query, queryId, ruleId, category) or isDeclarationsQueryMetadata(query, queryId, ruleId, category) or isDeclarations1QueryMetadata(query, queryId, ruleId, category) or - isDeclarations5QueryMetadata(query, queryId, ruleId, category) or isDeclarations2QueryMetadata(query, queryId, ruleId, category) or + isDeclarations5QueryMetadata(query, queryId, ruleId, category) or isExceptionSafetyQueryMetadata(query, queryId, ruleId, category) or isExceptions1QueryMetadata(query, queryId, ruleId, category) or isExceptions2QueryMetadata(query, queryId, ruleId, category) or diff --git a/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql b/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql index 826a80a1d..360d17f0e 100644 --- a/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql +++ b/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql @@ -18,7 +18,14 @@ import codingstandards.cpp.misra import codingstandards.cpp.types.Compatible import codingstandards.cpp.Operator -abstract class MembersReturningObjectOrSubobject extends MemberFunction { +class MembersReturningPointerOrRef extends MemberFunction { + MembersReturningPointerOrRef() { + this.getUnspecifiedType() instanceof PointerType or + this.getUnspecifiedType() instanceof ReferenceType + } +} + +abstract class MembersReturningObjectOrSubobject extends MembersReturningPointerOrRef { string toString() { result = "Members returning object or subobject" } } diff --git a/cpp/misra/test/rules/RULE-6-8-4/MemberFunctionsRefqualified.expected b/cpp/misra/test/rules/RULE-6-8-4/MemberFunctionsRefqualified.expected index c59015b98..d81930a4d 100644 --- a/cpp/misra/test/rules/RULE-6-8-4/MemberFunctionsRefqualified.expected +++ b/cpp/misra/test/rules/RULE-6-8-4/MemberFunctionsRefqualified.expected @@ -1,4 +1,3 @@ -| test.cpp:10:7:10:11 | Members returning object or subobject | Member function is not properly ref qualified. | | test.cpp:12:14:12:20 | Members returning object or subobject | Member function is not properly ref qualified. | | test.cpp:24:12:24:23 | Members returning object or subobject | Member function is not properly ref qualified. | | test.cpp:28:6:28:18 | Members returning object or subobject | Member function is not properly ref qualified. | diff --git a/cpp/misra/test/rules/RULE-6-8-4/test.cpp b/cpp/misra/test/rules/RULE-6-8-4/test.cpp index 936fa5386..dacfdde4a 100644 --- a/cpp/misra/test/rules/RULE-6-8-4/test.cpp +++ b/cpp/misra/test/rules/RULE-6-8-4/test.cpp @@ -7,7 +7,7 @@ struct A { int const &geta2() const & { // COMPLIANT -- due to overload below return a; } - int geta2() && { return a; } // NON_COMPLIANT + int geta2() && { return a; } int const &getabad() const & { // NON_COMPLIANT -- no overload provided return a; From 3e54854cfd3a438fa4fc90d77c64ffbef17cce04 Mon Sep 17 00:00:00 2001 From: Kristen Newbury Date: Tue, 28 Apr 2026 16:39:33 -0400 Subject: [PATCH 05/10] Address feedback --- .../cpp/lifetimes/CppObjects.qll | 31 +++++-- .../RULE-6-8-4/MemberFunctionsRefqualified.ql | 58 +++++++------ .../MemberFunctionsRefqualified.expected | 14 +++ cpp/misra/test/rules/RULE-6-8-4/test.cpp | 86 ++++++++++++++++++- 4 files changed, 155 insertions(+), 34 deletions(-) diff --git a/cpp/common/src/codingstandards/cpp/lifetimes/CppObjects.qll b/cpp/common/src/codingstandards/cpp/lifetimes/CppObjects.qll index 1c38859cb..b82df69bb 100644 --- a/cpp/common/src/codingstandards/cpp/lifetimes/CppObjects.qll +++ b/cpp/common/src/codingstandards/cpp/lifetimes/CppObjects.qll @@ -2,6 +2,7 @@ import cpp import codingstandards.cpp.lifetimes.StorageDuration import semmle.code.cpp.valuenumbering.HashCons import codingstandards.cpp.Clvalues +import codingstandards.cpp.types.Pointers /** * A library for handling "Objects" in C++. @@ -136,19 +137,39 @@ abstract class ObjectIdentityBase extends Element { } /** - * Finds expressions `e.x` or `e[x]` for expression `e`, recursively. Does not resolve pointers. + * Finds expressions `e.x` or `e[x]` for expression `e`, recursively. + * + * Omits accesses to reference fields, as they are not subobjects. * * Note that this does not hold for `e->x` or `e[x]` where `e` is a pointer. */ -private Expr getASubobjectAccessOf(Expr e) { - result = e - or - result.(DotFieldAccess).getQualifier() = getASubobjectAccessOf(e) +Expr getASubobjectAccessOf(Expr e) { + exists(Field f | + not f.getUnderlyingType() instanceof ReferenceType and + f.getAnAccess().(FieldAccess) = result.getAChild*() + ) and + ( + result = e + or + result.(DotFieldAccess).getQualifier() = getASubobjectAccessOf(e) + ) or result.(ArrayExpr).getArrayBase() = getASubobjectAccessOf(e) and not result.(ArrayExpr).getArrayBase().getUnspecifiedType() instanceof PointerType } +/** + * gets an access where the pointee is the subobject + */ +Expr getASubobjectAccessOfPointee(Expr e) { + e.getParent() instanceof AddressOfExpr and + result = getASubobjectAccessOf(e.getParent()) + or + // the accessed field is a pointer to a subobject + e.getParent().(PointerFieldAccess).getTarget().getUnspecifiedType() instanceof PointerType and + result = getASubobjectAccessOf(e.getParent()) +} + /** * Find the object types that are embedded within the current type. * diff --git a/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql b/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql index 360d17f0e..6e537bc66 100644 --- a/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql +++ b/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql @@ -17,28 +17,32 @@ import cpp import codingstandards.cpp.misra import codingstandards.cpp.types.Compatible import codingstandards.cpp.Operator +import codingstandards.cpp.types.Pointers +import codingstandards.cpp.lifetimes.CppObjects class MembersReturningPointerOrRef extends MemberFunction { - MembersReturningPointerOrRef() { - this.getUnspecifiedType() instanceof PointerType or - this.getUnspecifiedType() instanceof ReferenceType - } + MembersReturningPointerOrRef() { this.getType() instanceof PointerLikeType } } abstract class MembersReturningObjectOrSubobject extends MembersReturningPointerOrRef { string toString() { result = "Members returning object or subobject" } } +/** + * Members that have an explicit `this` access within their return statement. + */ class MembersReturningObject extends MembersReturningObjectOrSubobject { MembersReturningObject() { exists(ReturnStmt r, ThisExpr t | r.getEnclosingFunction() = this and ( - r.getAChild*() = t + //direct access only + r.getAChild() = t or + //or one level of indirection exists(PointerDereferenceExpr p | - p.getAChild*() = t and - r.getAChild*() = p + p.getAChild() = t and + r.getAChild() = p ) ) and t.getActualType().stripType() = this.getDeclaringType() @@ -46,25 +50,33 @@ class MembersReturningObject extends MembersReturningObjectOrSubobject { } } +/** + * Members that have an explicit subobject access within their return statement. + * + * Specifically, this captures when the return is a reference or pointer + * to a subobject. + */ class MembersReturningSubObject extends MembersReturningObjectOrSubobject { MembersReturningSubObject() { - exists(ReturnStmt r, FieldSubObjectDeclaration field | + exists(ReturnStmt r, FieldAccess access, Expr e | r.getEnclosingFunction() = this and + //direct access only + r.getAChild() = e and ( - r.getAChild*() = field.(Field).getAnAccess() + //pointer or reference to pointer subobject returned + e = getASubobjectAccessOfPointee(access) and + (e.getType() instanceof PointerType or e.getType() instanceof ReferenceType) or - exists(PointerDereferenceExpr p | - p.getAChild*() = field.(Field).getAnAccess() and - r.getAChild*() = p - ) - ) and - field.(Field).getDeclaringType() = this.getDeclaringType() + //reference to subobject returned + (this.getType() instanceof ReferenceType or e.getType() instanceof ReferenceType) and + not access.getTarget().getType() instanceof PointerType + ) ) } } predicate relevantTypes(Type a, Type b) { - exists(MembersReturningObject f, MemberFunction overload | + exists(MembersReturningObjectOrSubobject f, MemberFunction overload | f.getAnOverload() = overload and exists(int i | f.getParameter(i).getType() = a and @@ -77,11 +89,11 @@ class AppropriatelyQualified extends MembersReturningObjectOrSubobject { AppropriatelyQualified() { //non-const-lvalue-ref-qualified this.isLValueRefQualified() and - not this.hasSpecifier("const") + this.getType().(PointerLikeType).pointsToNonConst() or //const-lvalue-ref-qualified this.isLValueRefQualified() and - this.hasSpecifier("const") and + this.getType().(PointerLikeType).pointsToConst() and //and overload exists that is rvalue-ref-qualified exists(MemberFunction overload | this.getAnOverload() = overload and @@ -95,16 +107,6 @@ class AppropriatelyQualified extends MembersReturningObjectOrSubobject { } } -/** - * Fields that are not reference type can be subobjects - */ -class FieldSubObjectDeclaration extends Declaration { - FieldSubObjectDeclaration() { - not this.getADeclarationEntry().getType() instanceof ReferenceType and - this instanceof Field - } -} - class DefaultedAssignmentOperator extends AssignmentOperator { DefaultedAssignmentOperator() { this.isDefaulted() } } diff --git a/cpp/misra/test/rules/RULE-6-8-4/MemberFunctionsRefqualified.expected b/cpp/misra/test/rules/RULE-6-8-4/MemberFunctionsRefqualified.expected index d81930a4d..98a5b94c3 100644 --- a/cpp/misra/test/rules/RULE-6-8-4/MemberFunctionsRefqualified.expected +++ b/cpp/misra/test/rules/RULE-6-8-4/MemberFunctionsRefqualified.expected @@ -4,3 +4,17 @@ | test.cpp:42:16:42:16 | Members returning object or subobject | Member function is not properly ref qualified. | | test.cpp:42:16:42:16 | Members returning object or subobject | Member function is not properly ref qualified. | | test.cpp:42:16:42:16 | Members returning object or subobject | Member function is not properly ref qualified. | +| test.cpp:61:8:61:8 | Members returning object or subobject | Member function is not properly ref qualified. | +| test.cpp:71:9:71:10 | Members returning object or subobject | Member function is not properly ref qualified. | +| test.cpp:79:8:79:9 | Members returning object or subobject | Member function is not properly ref qualified. | +| test.cpp:82:8:82:9 | Members returning object or subobject | Member function is not properly ref qualified. | +| test.cpp:85:8:85:9 | Members returning object or subobject | Member function is not properly ref qualified. | +| test.cpp:89:9:89:10 | Members returning object or subobject | Member function is not properly ref qualified. | +| test.cpp:93:9:93:10 | Members returning object or subobject | Member function is not properly ref qualified. | +| test.cpp:103:8:103:8 | Members returning object or subobject | Member function is not properly ref qualified. | +| test.cpp:113:9:113:10 | Members returning object or subobject | Member function is not properly ref qualified. | +| test.cpp:121:8:121:9 | Members returning object or subobject | Member function is not properly ref qualified. | +| test.cpp:124:8:124:9 | Members returning object or subobject | Member function is not properly ref qualified. | +| test.cpp:127:8:127:9 | Members returning object or subobject | Member function is not properly ref qualified. | +| test.cpp:131:9:131:10 | Members returning object or subobject | Member function is not properly ref qualified. | +| test.cpp:135:9:135:10 | Members returning object or subobject | Member function is not properly ref qualified. | diff --git a/cpp/misra/test/rules/RULE-6-8-4/test.cpp b/cpp/misra/test/rules/RULE-6-8-4/test.cpp index dacfdde4a..066354b41 100644 --- a/cpp/misra/test/rules/RULE-6-8-4/test.cpp +++ b/cpp/misra/test/rules/RULE-6-8-4/test.cpp @@ -51,4 +51,88 @@ void f(int p, float p1) { t.f(p); t.f(p1); // instantiation that causes issue due to parameter list type meaning // there is no overload -} \ No newline at end of file +} + +class B { + int i; + int *ip; + int **ip2; + + int *f() { + return &i; // NON_COMPLIANT + } + int *f1() { + return ip; // COMPLIANT -- reads pointer + } + int *f2() { + return *ip2; // COMPLIANT -- reads pointer + } + + int **f3() { + return &ip; // NON_COMPLIANT + } + + int **f4() { + return ip2; // COMPLIANT -- copies pointer + } + + int &f5() { + return i; // NON_COMPLIANT + } + int &f6() { + return *ip; // COMPLIANT[FALSE_POSITIVE] -- reads pointer + } + int &f7() { + return **ip2; // COMPLIANT[FALSE_POSITIVE]-- reads pointer + } + + int *&f8() { + // return &p; // won't compile + return ip; // NON_COMPLIANT + } + int *&f9() { + return *ip2; // COMPLIANT[FALSE_POSITIVE] -- reads pointer + } +}; + +class D { + int i; + int *ip; + int **ip2; + + int *f() { + return &(this->i); // NON_COMPLIANT + } + int *f1() { + return this->ip; // COMPLIANT -- reads pointer + } + int *f2() { + return *this->ip2; // COMPLIANT -- reads pointer + } + + int **f3() { + return &this->ip; // NON_COMPLIANT + } + + int **f4() { + return this->ip2; // COMPLIANT -- copies pointer + } + + int &f5() { + return this->i; // NON_COMPLIANT + } + int &f6() { + return *this->ip; // COMPLIANT[FALSE_POSITIVE] -- reads pointer + } + int &f7() { + return **this->ip2; // COMPLIANT[FALSE_POSITIVE] -- reads pointer + } + + int *&f8() { + // return &p; // won't compile + return this->ip; // NON_COMPLIANT + } + int *&f9() { + return *this->ip2; // COMPLIANT[FALSE_POSITIVE] -- reads pointer + } +}; \ No newline at end of file From 3365b5ae9139282accbf4ae7c84ab6e73ea8764e Mon Sep 17 00:00:00 2001 From: Kristen Newbury Date: Wed, 29 Apr 2026 14:03:03 -0400 Subject: [PATCH 06/10] Address feedback comments --- .../cpp/lifetimes/CppObjects.qll | 27 +++++++++---------- .../RULE-6-8-4/MemberFunctionsRefqualified.ql | 26 +++++++++--------- .../MemberFunctionsRefqualified.expected | 7 ----- cpp/misra/test/rules/RULE-6-8-4/test.cpp | 12 ++++----- 4 files changed, 32 insertions(+), 40 deletions(-) diff --git a/cpp/common/src/codingstandards/cpp/lifetimes/CppObjects.qll b/cpp/common/src/codingstandards/cpp/lifetimes/CppObjects.qll index b82df69bb..8fe2dd016 100644 --- a/cpp/common/src/codingstandards/cpp/lifetimes/CppObjects.qll +++ b/cpp/common/src/codingstandards/cpp/lifetimes/CppObjects.qll @@ -144,30 +144,29 @@ abstract class ObjectIdentityBase extends Element { * Note that this does not hold for `e->x` or `e[x]` where `e` is a pointer. */ Expr getASubobjectAccessOf(Expr e) { - exists(Field f | - not f.getUnderlyingType() instanceof ReferenceType and - f.getAnAccess().(FieldAccess) = result.getAChild*() - ) and - ( - result = e - or - result.(DotFieldAccess).getQualifier() = getASubobjectAccessOf(e) - ) + result = e + or + result.(DotFieldAccess).getQualifier() = getASubobjectAccessOf(e) and + not result.(DotFieldAccess).getTarget().getUnderlyingType() instanceof ReferenceType or result.(ArrayExpr).getArrayBase() = getASubobjectAccessOf(e) and not result.(ArrayExpr).getArrayBase().getUnspecifiedType() instanceof PointerType } /** - * gets an access where the pointee is the subobject + * Finds subobjects of the pointee for expression `e`, + * where `e` has the type pointer type. + * + * For `e` this will be subobject accesses of `*e`. + * Or for `e->x` the subobject access is `x`. */ Expr getASubobjectAccessOfPointee(Expr e) { - e.getParent() instanceof AddressOfExpr and + e.getParent() instanceof PointerDereferenceExpr and result = getASubobjectAccessOf(e.getParent()) or - // the accessed field is a pointer to a subobject - e.getParent().(PointerFieldAccess).getTarget().getUnspecifiedType() instanceof PointerType and - result = getASubobjectAccessOf(e.getParent()) + // for e1->e2 : getASubobjectAccessOfPointee(e1) = e2 and or subobjects of e2 + result = getASubobjectAccessOf(e.getParent().(PointerFieldAccess)) and + not result.(PointerFieldAccess).getTarget().getUnderlyingType() instanceof ReferenceType } /** diff --git a/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql b/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql index 6e537bc66..2ad54ee68 100644 --- a/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql +++ b/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql @@ -36,14 +36,11 @@ class MembersReturningObject extends MembersReturningObjectOrSubobject { exists(ReturnStmt r, ThisExpr t | r.getEnclosingFunction() = this and ( - //direct access only + //return `this` r.getAChild() = t or - //or one level of indirection - exists(PointerDereferenceExpr p | - p.getAChild() = t and - r.getAChild() = p - ) + //accesses of subobjects through the `this` pointer + r.getAChild() = getASubobjectAccessOf(t) ) and t.getActualType().stripType() = this.getDeclaringType() ) @@ -60,16 +57,16 @@ class MembersReturningSubObject extends MembersReturningObjectOrSubobject { MembersReturningSubObject() { exists(ReturnStmt r, FieldAccess access, Expr e | r.getEnclosingFunction() = this and - //direct access only - r.getAChild() = e and ( - //pointer or reference to pointer subobject returned - e = getASubobjectAccessOfPointee(access) and - (e.getType() instanceof PointerType or e.getType() instanceof ReferenceType) + //subobject returned by address + r.getAChild() = access.getParent() and + e = getASubobjectAccessOf(access) and + access.getParent() instanceof AddressOfExpr or //reference to subobject returned - (this.getType() instanceof ReferenceType or e.getType() instanceof ReferenceType) and - not access.getTarget().getType() instanceof PointerType + r.getAChild() = e and + e = getASubobjectAccessOf(access) and + this.getType() instanceof ReferenceType ) ) } @@ -117,3 +114,6 @@ where not f instanceof AppropriatelyQualified and not f instanceof DefaultedAssignmentOperator select f, "Member function is not properly ref qualified." +// from Expr e, PointerFieldAccess p +// where e.getParent() = p +// select e, p, p.getTarget(), p.getQualifier(), p.getParent() diff --git a/cpp/misra/test/rules/RULE-6-8-4/MemberFunctionsRefqualified.expected b/cpp/misra/test/rules/RULE-6-8-4/MemberFunctionsRefqualified.expected index 98a5b94c3..f01cedf9e 100644 --- a/cpp/misra/test/rules/RULE-6-8-4/MemberFunctionsRefqualified.expected +++ b/cpp/misra/test/rules/RULE-6-8-4/MemberFunctionsRefqualified.expected @@ -1,20 +1,13 @@ | test.cpp:12:14:12:20 | Members returning object or subobject | Member function is not properly ref qualified. | | test.cpp:24:12:24:23 | Members returning object or subobject | Member function is not properly ref qualified. | -| test.cpp:28:6:28:18 | Members returning object or subobject | Member function is not properly ref qualified. | | test.cpp:42:16:42:16 | Members returning object or subobject | Member function is not properly ref qualified. | | test.cpp:42:16:42:16 | Members returning object or subobject | Member function is not properly ref qualified. | | test.cpp:42:16:42:16 | Members returning object or subobject | Member function is not properly ref qualified. | | test.cpp:61:8:61:8 | Members returning object or subobject | Member function is not properly ref qualified. | | test.cpp:71:9:71:10 | Members returning object or subobject | Member function is not properly ref qualified. | | test.cpp:79:8:79:9 | Members returning object or subobject | Member function is not properly ref qualified. | -| test.cpp:82:8:82:9 | Members returning object or subobject | Member function is not properly ref qualified. | -| test.cpp:85:8:85:9 | Members returning object or subobject | Member function is not properly ref qualified. | | test.cpp:89:9:89:10 | Members returning object or subobject | Member function is not properly ref qualified. | -| test.cpp:93:9:93:10 | Members returning object or subobject | Member function is not properly ref qualified. | | test.cpp:103:8:103:8 | Members returning object or subobject | Member function is not properly ref qualified. | | test.cpp:113:9:113:10 | Members returning object or subobject | Member function is not properly ref qualified. | | test.cpp:121:8:121:9 | Members returning object or subobject | Member function is not properly ref qualified. | -| test.cpp:124:8:124:9 | Members returning object or subobject | Member function is not properly ref qualified. | -| test.cpp:127:8:127:9 | Members returning object or subobject | Member function is not properly ref qualified. | | test.cpp:131:9:131:10 | Members returning object or subobject | Member function is not properly ref qualified. | -| test.cpp:135:9:135:10 | Members returning object or subobject | Member function is not properly ref qualified. | diff --git a/cpp/misra/test/rules/RULE-6-8-4/test.cpp b/cpp/misra/test/rules/RULE-6-8-4/test.cpp index 066354b41..8cf9af32d 100644 --- a/cpp/misra/test/rules/RULE-6-8-4/test.cpp +++ b/cpp/misra/test/rules/RULE-6-8-4/test.cpp @@ -80,10 +80,10 @@ class B { return i; // NON_COMPLIANT } int &f6() { - return *ip; // COMPLIANT[FALSE_POSITIVE] -- reads pointer + return *ip; // COMPLIANT -- reads pointer } int &f7() { - return **ip2; // COMPLIANT[FALSE_POSITIVE]-- reads pointer + return **ip2; // COMPLIANT -- reads pointer } int *&f8() { @@ -91,7 +91,7 @@ class B { return ip; // NON_COMPLIANT } int *&f9() { - return *ip2; // COMPLIANT[FALSE_POSITIVE] -- reads pointer + return *ip2; // COMPLIANT -- reads pointer } }; @@ -122,10 +122,10 @@ class D { return this->i; // NON_COMPLIANT } int &f6() { - return *this->ip; // COMPLIANT[FALSE_POSITIVE] -- reads pointer + return *this->ip; // COMPLIANT -- reads pointer } int &f7() { - return **this->ip2; // COMPLIANT[FALSE_POSITIVE] -- reads pointer + return **this->ip2; // COMPLIANT -- reads pointer } int *&f8() { @@ -133,6 +133,6 @@ class D { return this->ip; // NON_COMPLIANT } int *&f9() { - return *this->ip2; // COMPLIANT[FALSE_POSITIVE] -- reads pointer + return *this->ip2; // COMPLIANT -- reads pointer } }; \ No newline at end of file From f4a39b482bcff5200337617d3ac8485b6f549b4e Mon Sep 17 00:00:00 2001 From: Kristen Newbury Date: Wed, 29 Apr 2026 15:17:22 -0400 Subject: [PATCH 07/10] Remove duplicate predicate result and mark FN --- .../src/codingstandards/cpp/lifetimes/CppObjects.qll | 3 +++ .../rules/RULE-6-8-4/MemberFunctionsRefqualified.ql | 12 ++---------- cpp/misra/test/rules/RULE-6-8-4/test.cpp | 2 +- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/cpp/common/src/codingstandards/cpp/lifetimes/CppObjects.qll b/cpp/common/src/codingstandards/cpp/lifetimes/CppObjects.qll index 8fe2dd016..9501f7e06 100644 --- a/cpp/common/src/codingstandards/cpp/lifetimes/CppObjects.qll +++ b/cpp/common/src/codingstandards/cpp/lifetimes/CppObjects.qll @@ -159,6 +159,9 @@ Expr getASubobjectAccessOf(Expr e) { * * For `e` this will be subobject accesses of `*e`. * Or for `e->x` the subobject access is `x`. + * + * This predicate is not used/tested extensively so + * verify it before use. */ Expr getASubobjectAccessOfPointee(Expr e) { e.getParent() instanceof PointerDereferenceExpr and diff --git a/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql b/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql index 2ad54ee68..4d6dc3ea0 100644 --- a/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql +++ b/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql @@ -35,13 +35,8 @@ class MembersReturningObject extends MembersReturningObjectOrSubobject { MembersReturningObject() { exists(ReturnStmt r, ThisExpr t | r.getEnclosingFunction() = this and - ( - //return `this` - r.getAChild() = t - or - //accesses of subobjects through the `this` pointer - r.getAChild() = getASubobjectAccessOf(t) - ) and + //return `this` + r.getAChild() = t and t.getActualType().stripType() = this.getDeclaringType() ) } @@ -114,6 +109,3 @@ where not f instanceof AppropriatelyQualified and not f instanceof DefaultedAssignmentOperator select f, "Member function is not properly ref qualified." -// from Expr e, PointerFieldAccess p -// where e.getParent() = p -// select e, p, p.getTarget(), p.getQualifier(), p.getParent() diff --git a/cpp/misra/test/rules/RULE-6-8-4/test.cpp b/cpp/misra/test/rules/RULE-6-8-4/test.cpp index 8cf9af32d..2fcc80088 100644 --- a/cpp/misra/test/rules/RULE-6-8-4/test.cpp +++ b/cpp/misra/test/rules/RULE-6-8-4/test.cpp @@ -25,7 +25,7 @@ struct A { return this; } - A &getstructbad2() { return *this; } // NON_COMPLIANT + A &getstructbad2() { return *this; } // NON_COMPLIANT[FALSE_NEGATIVE] }; class C { From 81375faeca89394fd0e0012d0fe05fbd47bb03e7 Mon Sep 17 00:00:00 2001 From: Kristen Newbury Date: Wed, 29 Apr 2026 17:18:39 -0400 Subject: [PATCH 08/10] Fix missing check for qualifiers on members returning subobject refs --- cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql b/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql index 4d6dc3ea0..39e30f5cf 100644 --- a/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql +++ b/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql @@ -52,6 +52,7 @@ class MembersReturningSubObject extends MembersReturningObjectOrSubobject { MembersReturningSubObject() { exists(ReturnStmt r, FieldAccess access, Expr e | r.getEnclosingFunction() = this and + (not exists(access.getQualifier()) or access.getQualifier() instanceof ThisExpr) and ( //subobject returned by address r.getAChild() = access.getParent() and From e8d60bc01a758637fe58187906c5ed816ed58183 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Wed, 29 Apr 2026 13:57:15 -0700 Subject: [PATCH 09/10] 2nd try, performance fix that preserves test results. --- .../codingstandards/cpp/types/Compatible.qll | 37 ++++++++++++++++--- .../RULE-6-8-4/MemberFunctionsRefqualified.ql | 19 +++------- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/cpp/common/src/codingstandards/cpp/types/Compatible.qll b/cpp/common/src/codingstandards/cpp/types/Compatible.qll index e2f8654b8..152ea2fc8 100644 --- a/cpp/common/src/codingstandards/cpp/types/Compatible.qll +++ b/cpp/common/src/codingstandards/cpp/types/Compatible.qll @@ -486,11 +486,11 @@ signature predicate interestedInFunctionDeclarations( ); module FunctionDeclarationTypeEquivalence< - TypeEquivalenceSig Config, interestedInFunctionDeclarations/2 interestedInFunctions> + TypeEquivalenceSig Config, interestedInFunctionDeclarations/2 interestedInFunDecls> { private predicate interestedInReturnTypes(Type a, Type b) { exists(FunctionDeclarationEntry aFun, FunctionDeclarationEntry bFun | - interestedInFunctions(pragma[only_bind_into](aFun), pragma[only_bind_into](bFun)) and + interestedInFunDecls(pragma[only_bind_into](aFun), pragma[only_bind_into](bFun)) and a = aFun.getType() and b = bFun.getType() ) @@ -498,19 +498,19 @@ module FunctionDeclarationTypeEquivalence< private predicate interestedInParameterTypes(Type a, Type b) { exists(FunctionDeclarationEntry aFun, FunctionDeclarationEntry bFun, int i | - interestedInFunctions(pragma[only_bind_into](aFun), pragma[only_bind_into](bFun)) and + interestedInFunDecls(pragma[only_bind_into](aFun), pragma[only_bind_into](bFun)) and a = aFun.getParameterDeclarationEntry(i).getType() and b = bFun.getParameterDeclarationEntry(i).getType() ) } predicate equalReturnTypes(FunctionDeclarationEntry f1, FunctionDeclarationEntry f2) { - interestedInFunctions(f1, f2) and + interestedInFunDecls(f1, f2) and TypeEquivalence::equalTypes(f1.getType(), f2.getType()) } predicate equalParameterTypes(FunctionDeclarationEntry f1, FunctionDeclarationEntry f2) { - interestedInFunctions(f1, f2) and + interestedInFunDecls(f1, f2) and f1.getDeclaration() = f2.getDeclaration() and forall(int i | exists([f1, f2].getParameterDeclarationEntry(i)) | equalParameterTypesAt(f1, f2, pragma[only_bind_into](i)) @@ -518,13 +518,38 @@ module FunctionDeclarationTypeEquivalence< } predicate equalParameterTypesAt(FunctionDeclarationEntry f1, FunctionDeclarationEntry f2, int i) { - interestedInFunctions(f1, f2) and + interestedInFunDecls(f1, f2) and f1.getDeclaration() = f2.getDeclaration() and TypeEquivalence::equalTypes(f1.getParameterDeclarationEntry(pragma[only_bind_into](i)) .getType(), f2.getParameterDeclarationEntry(pragma[only_bind_into](i)).getType()) } } +signature predicate interestedInFunctions(Function f1, Function f2); + +module FunctionEquivalence { + private predicate interestedInParameterTypes(Type a, Type b) { + exists(Function aFun, Function bFun, int i | + interestedInFuncs(pragma[only_bind_into](aFun), pragma[only_bind_into](bFun)) and + a = aFun.getParameter(i).getType() and + b = bFun.getParameter(i).getType() + ) + } + + predicate equalParameterTypes(Function f1, Function f2) { + interestedInFuncs(f1, f2) and + forall(int i | exists([f1, f2].getParameter(i)) | + equalParameterTypesAt(f1, f2, pragma[only_bind_into](i)) + ) + } + + predicate equalParameterTypesAt(Function f1, Function f2, int i) { + interestedInFuncs(f1, f2) and + TypeEquivalence::equalTypes(f1.getParameter(pragma[only_bind_into](i)) + .getType(), f2.getParameter(pragma[only_bind_into](i)).getType()) + } +} + private class LeafType extends Type { LeafType() { not this instanceof DerivedType and diff --git a/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql b/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql index 4d6dc3ea0..79bba5d86 100644 --- a/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql +++ b/cpp/misra/src/rules/RULE-6-8-4/MemberFunctionsRefqualified.ql @@ -67,17 +67,12 @@ class MembersReturningSubObject extends MembersReturningObjectOrSubobject { } } -predicate relevantTypes(Type a, Type b) { - exists(MembersReturningObjectOrSubobject f, MemberFunction overload | - f.getAnOverload() = overload and - exists(int i | - f.getParameter(i).getType() = a and - overload.getParameter(i).getType() = b - ) - ) +predicate relevantFunctions(Function a, Function b) { + a instanceof MembersReturningObjectOrSubobject and + a.getAnOverload() = b } -class AppropriatelyQualified extends MembersReturningObjectOrSubobject { +class AppropriatelyQualified extends MemberFunction { AppropriatelyQualified() { //non-const-lvalue-ref-qualified this.isLValueRefQualified() and @@ -91,10 +86,8 @@ class AppropriatelyQualified extends MembersReturningObjectOrSubobject { this.getAnOverload() = overload and overload.isRValueRefQualified() and //and has same param list - forall(int i | exists([this, overload].getParameter(i)) | - TypeEquivalence::equalTypes(this.getParameter(i) - .getType(), overload.getParameter(i).getType()) - ) + FunctionEquivalence::equalParameterTypes(this, + overload) ) } } From 706029df14454267fb195ec4a23f9126ce90d92b Mon Sep 17 00:00:00 2001 From: Kristen Newbury Date: Thu, 30 Apr 2026 13:36:33 -0400 Subject: [PATCH 10/10] fix signature predicate name to not conflict with other definitions --- cpp/common/src/codingstandards/cpp/types/Compatible.qll | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cpp/common/src/codingstandards/cpp/types/Compatible.qll b/cpp/common/src/codingstandards/cpp/types/Compatible.qll index 152ea2fc8..091742a29 100644 --- a/cpp/common/src/codingstandards/cpp/types/Compatible.qll +++ b/cpp/common/src/codingstandards/cpp/types/Compatible.qll @@ -525,9 +525,11 @@ module FunctionDeclarationTypeEquivalence< } } -signature predicate interestedInFunctions(Function f1, Function f2); +signature predicate interestedInFunctionsByFunction(Function f1, Function f2); -module FunctionEquivalence { +module FunctionEquivalence< + TypeEquivalenceSig Config, interestedInFunctionsByFunction/2 interestedInFuncs> +{ private predicate interestedInParameterTypes(Type a, Type b) { exists(Function aFun, Function bFun, int i | interestedInFuncs(pragma[only_bind_into](aFun), pragma[only_bind_into](bFun)) and