From 2b1c06bf1c279171806e8757d5ffda4d1417ccdd Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Fri, 15 Aug 2025 10:19:32 +0100 Subject: [PATCH 01/23] M5-2-2: Make into shared query This shared implementation will be used for implementing MISRA C++ 2023 8.2.1. --- ...ointerToAVirtualBaseClassCastToAPointer.ql | 16 +++++------- ...terToAVirtualBaseClassCastToAPointer.qlref | 1 - ...interToAVirtualBaseClassCastToAPointer.qll | 26 +++++++++++++++++++ ...ToAVirtualBaseClassCastToAPointer.expected | 0 ...ointerToAVirtualBaseClassCastToAPointer.ql | 4 +++ .../test.cpp | 0 rule_packages/cpp/Pointers.json | 3 ++- 7 files changed, 38 insertions(+), 12 deletions(-) delete mode 100644 cpp/autosar/test/rules/M5-2-2/PointerToAVirtualBaseClassCastToAPointer.qlref create mode 100644 cpp/common/src/codingstandards/cpp/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.qll rename cpp/{autosar/test/rules/M5-2-2 => common/test/rules/pointertoavirtualbaseclasscasttoapointer}/PointerToAVirtualBaseClassCastToAPointer.expected (100%) create mode 100644 cpp/common/test/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.ql rename cpp/{autosar/test/rules/M5-2-2 => common/test/rules/pointertoavirtualbaseclasscasttoapointer}/test.cpp (100%) diff --git a/cpp/autosar/src/rules/M5-2-2/PointerToAVirtualBaseClassCastToAPointer.ql b/cpp/autosar/src/rules/M5-2-2/PointerToAVirtualBaseClassCastToAPointer.ql index 086aa40ae7..57eda7f146 100644 --- a/cpp/autosar/src/rules/M5-2-2/PointerToAVirtualBaseClassCastToAPointer.ql +++ b/cpp/autosar/src/rules/M5-2-2/PointerToAVirtualBaseClassCastToAPointer.ql @@ -15,14 +15,10 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.pointertoavirtualbaseclasscasttoapointer.PointerToAVirtualBaseClassCastToAPointer -from Cast cast, VirtualBaseClass castFrom, Class castTo -where - not isExcluded(cast, PointersPackage::pointerToAVirtualBaseClassCastToAPointerQuery()) and - not cast instanceof DynamicCast and - castFrom = cast.getExpr().getType().(PointerType).getBaseType() and - cast.getType().(PointerType).getBaseType() = castTo and - castTo = castFrom.getADerivedClass+() -select cast, - "A pointer to virtual base class $@ is not cast to a pointer of derived class $@ using a dynamic_cast.", - castFrom, castFrom.getName(), castTo, castTo.getName() +class PointerToAVirtualBaseClassCastToAPointerQuery extends PointerToAVirtualBaseClassCastToAPointerSharedQuery { + PointerToAVirtualBaseClassCastToAPointerQuery() { + this = PointersPackage::pointerToAVirtualBaseClassCastToAPointerQuery() + } +} diff --git a/cpp/autosar/test/rules/M5-2-2/PointerToAVirtualBaseClassCastToAPointer.qlref b/cpp/autosar/test/rules/M5-2-2/PointerToAVirtualBaseClassCastToAPointer.qlref deleted file mode 100644 index c48e9498cc..0000000000 --- a/cpp/autosar/test/rules/M5-2-2/PointerToAVirtualBaseClassCastToAPointer.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M5-2-2/PointerToAVirtualBaseClassCastToAPointer.ql \ No newline at end of file diff --git a/cpp/common/src/codingstandards/cpp/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.qll b/cpp/common/src/codingstandards/cpp/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.qll new file mode 100644 index 0000000000..37425e0dd2 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.qll @@ -0,0 +1,26 @@ +/** + * Provides a library with a `problems` predicate for the following issue: + * A pointer to a virtual base class shall only be cast to a pointer to a derived class + * by means of dynamic_cast, otherwise the cast has undefined behavior. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class PointerToAVirtualBaseClassCastToAPointerSharedQuery extends Query { } + +Query getQuery() { result instanceof PointerToAVirtualBaseClassCastToAPointerSharedQuery } + +query predicate problems(Cast cast, string message) { + exists(VirtualBaseClass castFrom, Class castTo | + not isExcluded(cast, getQuery()) and + not cast instanceof DynamicCast and + castFrom = cast.getExpr().getType().(PointerType).getBaseType() and + cast.getType().(PointerType).getBaseType() = castTo and + castTo = castFrom.getADerivedClass+() and + message = + "A pointer to virtual base class " + castFrom.getName() + + " is not cast to a pointer of derived class " + castTo.getName() + " using a dynamic_cast." + ) +} diff --git a/cpp/autosar/test/rules/M5-2-2/PointerToAVirtualBaseClassCastToAPointer.expected b/cpp/common/test/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.expected similarity index 100% rename from cpp/autosar/test/rules/M5-2-2/PointerToAVirtualBaseClassCastToAPointer.expected rename to cpp/common/test/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.expected diff --git a/cpp/common/test/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.ql b/cpp/common/test/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.ql new file mode 100644 index 0000000000..973b707e13 --- /dev/null +++ b/cpp/common/test/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.ql @@ -0,0 +1,4 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.pointertoavirtualbaseclasscasttoapointer.PointerToAVirtualBaseClassCastToAPointer + +class TestFileQuery extends PointerToAVirtualBaseClassCastToAPointerSharedQuery, TestQuery { } diff --git a/cpp/autosar/test/rules/M5-2-2/test.cpp b/cpp/common/test/rules/pointertoavirtualbaseclasscasttoapointer/test.cpp similarity index 100% rename from cpp/autosar/test/rules/M5-2-2/test.cpp rename to cpp/common/test/rules/pointertoavirtualbaseclasscasttoapointer/test.cpp diff --git a/rule_packages/cpp/Pointers.json b/rule_packages/cpp/Pointers.json index fb1fbe2918..3815ba521c 100644 --- a/rule_packages/cpp/Pointers.json +++ b/rule_packages/cpp/Pointers.json @@ -306,7 +306,8 @@ "short_name": "PointerToAVirtualBaseClassCastToAPointer", "tags": [ "correctness" - ] + ], + "shared_implementation_short_name": "PointerToAVirtualBaseClassCastToAPointer" } ], "title": "A pointer to a virtual base class shall only be cast to a pointer to a derived class by means of dynamic_cast." From 7e9fe0bef5c4a6da56cbb1571090da1165280f6c Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Fri, 15 Aug 2025 10:24:31 +0100 Subject: [PATCH 02/23] Add Conversions2 package --- .../cpp/exclusions/cpp/Conversions2.qll | 112 ++++++++++++++++ .../cpp/exclusions/cpp/RuleMetadata.qll | 3 + rule_packages/cpp/Conversions2.json | 125 ++++++++++++++++++ 3 files changed, 240 insertions(+) create mode 100644 cpp/common/src/codingstandards/cpp/exclusions/cpp/Conversions2.qll create mode 100644 rule_packages/cpp/Conversions2.json diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Conversions2.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Conversions2.qll new file mode 100644 index 0000000000..a250b4b5b1 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Conversions2.qll @@ -0,0 +1,112 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Conversions2Query = + TVirtualBaseClassCastToDerivedQuery() or + TNoCStyleOrFunctionalCastsQuery() or + TIntToPointerCastProhibitedQuery() or + TNoPointerToIntegralCastQuery() or + TPointerToIntegralCastQuery() or + TNoStandaloneTypeCastExpressionQuery() + +predicate isConversions2QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `virtualBaseClassCastToDerived` query + Conversions2Package::virtualBaseClassCastToDerivedQuery() and + queryId = + // `@id` for the `virtualBaseClassCastToDerived` query + "cpp/misra/virtual-base-class-cast-to-derived" and + ruleId = "RULE-8-2-1" and + category = "required" + or + query = + // `Query` instance for the `noCStyleOrFunctionalCasts` query + Conversions2Package::noCStyleOrFunctionalCastsQuery() and + queryId = + // `@id` for the `noCStyleOrFunctionalCasts` query + "cpp/misra/no-c-style-or-functional-casts" and + ruleId = "RULE-8-2-2" and + category = "required" + or + query = + // `Query` instance for the `intToPointerCastProhibited` query + Conversions2Package::intToPointerCastProhibitedQuery() and + queryId = + // `@id` for the `intToPointerCastProhibited` query + "cpp/misra/int-to-pointer-cast-prohibited" and + ruleId = "RULE-8-2-6" and + category = "required" + or + query = + // `Query` instance for the `noPointerToIntegralCast` query + Conversions2Package::noPointerToIntegralCastQuery() and + queryId = + // `@id` for the `noPointerToIntegralCast` query + "cpp/misra/no-pointer-to-integral-cast" and + ruleId = "RULE-8-2-7" and + category = "advisory" + or + query = + // `Query` instance for the `pointerToIntegralCast` query + Conversions2Package::pointerToIntegralCastQuery() and + queryId = + // `@id` for the `pointerToIntegralCast` query + "cpp/misra/pointer-to-integral-cast" and + ruleId = "RULE-8-2-8" and + category = "required" + or + query = + // `Query` instance for the `noStandaloneTypeCastExpression` query + Conversions2Package::noStandaloneTypeCastExpressionQuery() and + queryId = + // `@id` for the `noStandaloneTypeCastExpression` query + "cpp/misra/no-standalone-type-cast-expression" and + ruleId = "RULE-9-2-1" and + category = "required" +} + +module Conversions2Package { + Query virtualBaseClassCastToDerivedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `virtualBaseClassCastToDerived` query + TQueryCPP(TConversions2PackageQuery(TVirtualBaseClassCastToDerivedQuery())) + } + + Query noCStyleOrFunctionalCastsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `noCStyleOrFunctionalCasts` query + TQueryCPP(TConversions2PackageQuery(TNoCStyleOrFunctionalCastsQuery())) + } + + Query intToPointerCastProhibitedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `intToPointerCastProhibited` query + TQueryCPP(TConversions2PackageQuery(TIntToPointerCastProhibitedQuery())) + } + + Query noPointerToIntegralCastQuery() { + //autogenerate `Query` type + result = + // `Query` type for `noPointerToIntegralCast` query + TQueryCPP(TConversions2PackageQuery(TNoPointerToIntegralCastQuery())) + } + + Query pointerToIntegralCastQuery() { + //autogenerate `Query` type + result = + // `Query` type for `pointerToIntegralCast` query + TQueryCPP(TConversions2PackageQuery(TPointerToIntegralCastQuery())) + } + + Query noStandaloneTypeCastExpressionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `noStandaloneTypeCastExpression` query + TQueryCPP(TConversions2PackageQuery(TNoStandaloneTypeCastExpressionQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll index 88e4d55358..27ae7e2f52 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll @@ -13,6 +13,7 @@ import Concurrency import Conditionals import Const import Conversions +import Conversions2 import DeadCode import Declarations import ExceptionSafety @@ -69,6 +70,7 @@ newtype TCPPQuery = TConditionalsPackageQuery(ConditionalsQuery q) or TConstPackageQuery(ConstQuery q) or TConversionsPackageQuery(ConversionsQuery q) or + TConversions2PackageQuery(Conversions2Query q) or TDeadCodePackageQuery(DeadCodeQuery q) or TDeclarationsPackageQuery(DeclarationsQuery q) or TExceptionSafetyPackageQuery(ExceptionSafetyQuery q) or @@ -125,6 +127,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat isConditionalsQueryMetadata(query, queryId, ruleId, category) or isConstQueryMetadata(query, queryId, ruleId, category) or isConversionsQueryMetadata(query, queryId, ruleId, category) or + isConversions2QueryMetadata(query, queryId, ruleId, category) or isDeadCodeQueryMetadata(query, queryId, ruleId, category) or isDeclarationsQueryMetadata(query, queryId, ruleId, category) or isExceptionSafetyQueryMetadata(query, queryId, ruleId, category) or diff --git a/rule_packages/cpp/Conversions2.json b/rule_packages/cpp/Conversions2.json new file mode 100644 index 0000000000..6c178effc6 --- /dev/null +++ b/rule_packages/cpp/Conversions2.json @@ -0,0 +1,125 @@ +{ + "MISRA-C++-2023": { + "RULE-8-2-1": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Casting from a virtual base class to a derived class using anything other than dynamic_cast causes undefined behavior.", + "kind": "problem", + "name": "A virtual base class shall only be cast to a derived class by means of dynamic_cast", + "precision": "very-high", + "severity": "error", + "short_name": "VirtualBaseClassCastToDerived", + "tags": [ + "scope/single-translation-unit" + ], + "shared_implementation_short_name": "PointerToAVirtualBaseClassCastToAPointer" + } + ], + "title": "A virtual base class shall only be cast to a derived class by means of dynamic_cast" + }, + "RULE-8-2-2": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Using C-style casts or functional notation casts allows unsafe type conversions and makes code harder to maintain compared to using named casts like const_cast, dynamic_cast, static_cast and reinterpret_cast.", + "kind": "problem", + "name": "C-style casts and functional notation casts shall not be used", + "precision": "very-high", + "severity": "error", + "short_name": "NoCStyleOrFunctionalCasts", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "C-style casts and functional notation casts shall not be used" + }, + "RULE-8-2-6": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Casting from an integral type, enumerated type, or pointer to void type to a pointer type leads to unspecified behavior and is error prone.", + "kind": "problem", + "name": "An object with integral, enumerated, or pointer to void type shall not be cast to a pointer type", + "precision": "very-high", + "severity": "error", + "short_name": "IntToPointerCastProhibited", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "An object with integral, enumerated, or pointer to void type shall not be cast to a pointer type" + }, + "RULE-8-2-7": { + "properties": { + "enforcement": "decidable", + "obligation": "advisory" + }, + "queries": [ + { + "description": "Casting between pointer types and integral types makes code behavior harder to understand and may cause pointer tracking tools to become unreliable.", + "kind": "problem", + "name": "A cast should not convert a pointer type to an integral type", + "precision": "very-high", + "severity": "error", + "short_name": "NoPointerToIntegralCast", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "A cast should not convert a pointer type to an integral type" + }, + "RULE-8-2-8": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Casting object pointers to integral types other than std::uintptr_t or std::intptr_t can lead to implementation-defined behavior and potential loss of pointer information.", + "kind": "problem", + "name": "An object pointer type shall not be cast to an integral type other than std::uintptr_t or", + "precision": "very-high", + "severity": "error", + "short_name": "PointerToIntegralCast", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "An object pointer type shall not be cast to an integral type other than std::uintptr_t or std::intptr_t" + }, + "RULE-9-2-1": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Using an explicit type conversion as an expression statement creates a temporary object that is immediately discarded, which can lead to unintended premature resource cleanup.", + "kind": "problem", + "name": "An explicit type conversion shall not be an expression statement", + "precision": "very-high", + "severity": "error", + "short_name": "NoStandaloneTypeCastExpression", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "An explicit type conversion shall not be an expression statement" + } + } +} \ No newline at end of file From c81f2462cc61ebd439c50bf6956cee6c0a32e4a2 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Fri, 15 Aug 2025 10:28:57 +0100 Subject: [PATCH 03/23] Rule 8.2.1: Implement as a shared query --- .../VirtualBaseClassCastToDerived.ql | 23 +++++++++++++++++++ .../VirtualBaseClassCastToDerived.testref | 1 + 2 files changed, 24 insertions(+) create mode 100644 cpp/misra/src/rules/RULE-8-2-1/VirtualBaseClassCastToDerived.ql create mode 100644 cpp/misra/test/rules/RULE-8-2-1/VirtualBaseClassCastToDerived.testref diff --git a/cpp/misra/src/rules/RULE-8-2-1/VirtualBaseClassCastToDerived.ql b/cpp/misra/src/rules/RULE-8-2-1/VirtualBaseClassCastToDerived.ql new file mode 100644 index 0000000000..cd9cb150a8 --- /dev/null +++ b/cpp/misra/src/rules/RULE-8-2-1/VirtualBaseClassCastToDerived.ql @@ -0,0 +1,23 @@ +/** + * @id cpp/misra/virtual-base-class-cast-to-derived + * @name RULE-8-2-1: A virtual base class shall only be cast to a derived class by means of dynamic_cast + * @description Casting from a virtual base class to a derived class using anything other than + * dynamic_cast causes undefined behavior. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-2-1 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.rules.pointertoavirtualbaseclasscasttoapointer.PointerToAVirtualBaseClassCastToAPointer + +class VirtualBaseClassCastToDerivedQuery extends PointerToAVirtualBaseClassCastToAPointerSharedQuery { + VirtualBaseClassCastToDerivedQuery() { + this = Conversions2Package::virtualBaseClassCastToDerivedQuery() + } +} diff --git a/cpp/misra/test/rules/RULE-8-2-1/VirtualBaseClassCastToDerived.testref b/cpp/misra/test/rules/RULE-8-2-1/VirtualBaseClassCastToDerived.testref new file mode 100644 index 0000000000..475d3fd8be --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-2-1/VirtualBaseClassCastToDerived.testref @@ -0,0 +1 @@ +cpp/common/test/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.ql \ No newline at end of file From 75837e2b8e7a71657bee90066cf918576e9fc458 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Fri, 15 Aug 2025 12:01:05 +0100 Subject: [PATCH 04/23] PointerToAVirtualBaseClass: Handle reference types and type defs --- .../2025-08-15-m5-2-2-virtual-base.md | 3 + ...interToAVirtualBaseClassCastToAPointer.qll | 12 +++- ...ToAVirtualBaseClassCastToAPointer.expected | 3 + .../test.cpp | 63 ++++++++++++++++++- 4 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 change_notes/2025-08-15-m5-2-2-virtual-base.md diff --git a/change_notes/2025-08-15-m5-2-2-virtual-base.md b/change_notes/2025-08-15-m5-2-2-virtual-base.md new file mode 100644 index 0000000000..072ad41ea6 --- /dev/null +++ b/change_notes/2025-08-15-m5-2-2-virtual-base.md @@ -0,0 +1,3 @@ + - `M5-2-2` - `PointerToAVirtualBaseClassCastToAPointer.ql`: + - Report casts where the from or to types are typedefs to virtual base classes or derived classes. + - Report casts to a reference type which is a derived type. \ No newline at end of file diff --git a/cpp/common/src/codingstandards/cpp/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.qll b/cpp/common/src/codingstandards/cpp/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.qll index 37425e0dd2..207b6da415 100644 --- a/cpp/common/src/codingstandards/cpp/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.qll +++ b/cpp/common/src/codingstandards/cpp/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.qll @@ -16,11 +16,19 @@ query predicate problems(Cast cast, string message) { exists(VirtualBaseClass castFrom, Class castTo | not isExcluded(cast, getQuery()) and not cast instanceof DynamicCast and - castFrom = cast.getExpr().getType().(PointerType).getBaseType() and - cast.getType().(PointerType).getBaseType() = castTo and castTo = castFrom.getADerivedClass+() and message = "A pointer to virtual base class " + castFrom.getName() + " is not cast to a pointer of derived class " + castTo.getName() + " using a dynamic_cast." + | + // Pointer cast + castFrom = cast.getExpr().getType().stripTopLevelSpecifiers().(PointerType).getBaseType() and + cast.getType().stripTopLevelSpecifiers().(PointerType).getBaseType() = castTo + or + // Reference type cast + castFrom = cast.getExpr().getType().stripTopLevelSpecifiers() and + // Not actually represented as a reference type in our model - instead as the + // type itself + cast.getType().stripTopLevelSpecifiers() = castTo ) } diff --git a/cpp/common/test/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.expected b/cpp/common/test/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.expected index e69de29bb2..7f64ab5d1c 100644 --- a/cpp/common/test/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.expected +++ b/cpp/common/test/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.expected @@ -0,0 +1,3 @@ +| test.cpp:36:12:36:37 | reinterpret_cast... | A pointer to virtual base class C1 is not cast to a pointer of derived class C2 using a dynamic_cast. | +| test.cpp:42:12:42:38 | reinterpret_cast... | A pointer to virtual base class C1 is not cast to a pointer of derived class C2 using a dynamic_cast. | +| test.cpp:48:17:48:48 | reinterpret_cast... | A pointer to virtual base class C1 is not cast to a pointer of derived class C2 using a dynamic_cast. | diff --git a/cpp/common/test/rules/pointertoavirtualbaseclasscasttoapointer/test.cpp b/cpp/common/test/rules/pointertoavirtualbaseclasscasttoapointer/test.cpp index 59c0cb4b37..419bc0764e 100644 --- a/cpp/common/test/rules/pointertoavirtualbaseclasscasttoapointer/test.cpp +++ b/cpp/common/test/rules/pointertoavirtualbaseclasscasttoapointer/test.cpp @@ -2,17 +2,74 @@ class C1 { public: virtual ~C1() {} }; + class C2 : public virtual C1 { public: C2() {} ~C2() {} }; -void f1() { +typedef C1 Base; +typedef C2 Derived; + +void test_dynamic_cast_pointer_compliant() { C2 l1; C1 *p1 = &l1; + C2 *p2 = dynamic_cast(p1); // COMPLIANT +} - // C2 *p2 = static_cast(p1); // NON_COMPLIANT, prohibited by compiler - C2 *p3 = dynamic_cast(p1); // COMPLIANT +void test_dynamic_cast_reference_compliant() { + C2 l1; + C1 *p1 = &l1; C2 &l2 = dynamic_cast(*p1); // COMPLIANT } + +void test_dynamic_cast_reference_compliant_typedefs() { + Derived l1; + Base *p1 = &l1; + Derived &l2 = dynamic_cast(*p1); // COMPLIANT +} + +void test_reinterpret_cast_pointer_non_compliant() { + C2 l1; + C1 *p1 = &l1; + C2 *p2 = reinterpret_cast(p1); // NON_COMPLIANT +} + +void test_reinterpret_cast_reference_non_compliant() { + C2 l1; + C1 *p1 = &l1; + C2 &l2 = reinterpret_cast(*p1); // NON_COMPLIANT +} + +void test_reinterpret_cast_typedefs_non_compliant() { + Derived l1; + Base *p1 = &l1; + Derived &l2 = reinterpret_cast(*p1); // NON_COMPLIANT +} + +void test_c_style_cast_pointer_non_compliant() { + C2 l1; + C1 *p1 = &l1; + // C2 *p2 = (C2 *)p1; // NON_COMPLIANT - prohibited by the compiler +} + +void test_c_style_cast_reference_non_compliant() { + C2 l1; + C1 *p1 = &l1; + // C2 &l2 = (C2 &)*p1; // NON_COMPLIANT - prohibited by the compiler +} + +void test_static_cast_pointer_non_compliant() { + C2 l1; + C1 *p1 = &l1; + // C2 *p2 = static_cast(p1); // NON_COMPLIANT - prohibited by the + // compiler +} + +void test_static_cast_reference_non_compliant() { + C2 l1; + C1 *p1 = &l1; + // C2 &l2 = static_cast(*p1); // NON_COMPLIANT - prohibited by the + // compiler +} \ No newline at end of file From 1792e8806cdfa9e589cbced7dc5c985984b4c409 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Fri, 15 Aug 2025 15:19:34 +0100 Subject: [PATCH 05/23] A5-2-2: Refactor query to extract shared components MISRA C++ Rule 8.2.2 is similar, but not identical. --- .../rules/A5-2-2/TraditionalCStyleCastsUsed.ql | 13 +++---------- cpp/autosar/test/rules/A5-2-2/test.cpp | 2 +- .../src/codingstandards/cpp/CStyleCasts.qll | 18 ++++++++++++++++++ 3 files changed, 22 insertions(+), 11 deletions(-) create mode 100644 cpp/common/src/codingstandards/cpp/CStyleCasts.qll diff --git a/cpp/autosar/src/rules/A5-2-2/TraditionalCStyleCastsUsed.ql b/cpp/autosar/src/rules/A5-2-2/TraditionalCStyleCastsUsed.ql index 66a289dfe0..7e2d19d336 100644 --- a/cpp/autosar/src/rules/A5-2-2/TraditionalCStyleCastsUsed.ql +++ b/cpp/autosar/src/rules/A5-2-2/TraditionalCStyleCastsUsed.ql @@ -16,6 +16,7 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.Macro +import codingstandards.cpp.CStyleCasts /** * Gets the macro (if any) that generated the given `CStyleCast`. @@ -61,18 +62,10 @@ Macro getGeneratedFrom(CStyleCast c) { * argument type is compatible with a single-argument constructor. */ -from CStyleCast c, string extraMessage, Locatable l, string supplementary +from ExplicitUserDefinedCStyleCast c, string extraMessage, Locatable l, string supplementary where not isExcluded(c, BannedSyntaxPackage::traditionalCStyleCastsUsedQuery()) and - not c.isImplicit() and - not c.getType() instanceof UnknownType and - // For casts in templates that occur on types related to a template parameter, the copy of th - // cast in the uninstantiated template is represented as a `CStyleCast` even if in practice all - // the instantiations represent it as a `ConstructorCall`. To avoid the common false positive case - // of using the functional cast notation to call a constructor we exclude all `CStyleCast`s on - // uninstantiated templates, and instead rely on reporting results within instantiations. - not c.isFromUninstantiatedTemplate(_) and - // Exclude casts created from macro invocations of macros defined by third parties + // Not generated from a library macro not getGeneratedFrom(c) instanceof LibraryMacro and // If the cast was generated from a user-provided macro, then report the macro that generated the // cast, as the macro itself may have generated the cast diff --git a/cpp/autosar/test/rules/A5-2-2/test.cpp b/cpp/autosar/test/rules/A5-2-2/test.cpp index fb39868560..d585efc37b 100644 --- a/cpp/autosar/test/rules/A5-2-2/test.cpp +++ b/cpp/autosar/test/rules/A5-2-2/test.cpp @@ -2,7 +2,7 @@ #include #include int foo() { return 1; } - +// A copy of 8.2,2 test.cpp, but with different cases compliant/non-compliant void test_c_style_cast() { double f = 3.14; std::uint32_t n1 = (std::uint32_t)f; // NON_COMPLIANT - C-style cast diff --git a/cpp/common/src/codingstandards/cpp/CStyleCasts.qll b/cpp/common/src/codingstandards/cpp/CStyleCasts.qll new file mode 100644 index 0000000000..b387f9898e --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/CStyleCasts.qll @@ -0,0 +1,18 @@ +import cpp +import codingstandards.cpp.Macro + +/** + * A C-style cast that is explicitly user written and has a known target type. + */ +class ExplicitUserDefinedCStyleCast extends CStyleCast { + ExplicitUserDefinedCStyleCast() { + not this.isImplicit() and + not this.getType() instanceof UnknownType and + // For casts in templates that occur on types related to a template parameter, the copy of th + // cast in the uninstantiated template is represented as a `CStyleCast` even if in practice all + // the instantiations represent it as a `ConstructorCall`. To avoid the common false positive case + // of using the functional cast notation to call a constructor we exclude all `CStyleCast`s on + // uninstantiated templates, and instead rely on reporting results within instantiations. + not this.isFromUninstantiatedTemplate(_) + } +} From 9b9a68c570409b2f1bb48b6180b82623c4727115 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Sun, 17 Aug 2025 23:18:36 +0100 Subject: [PATCH 06/23] Fix AlertReporting for nested macros Nested macro invocations do not have getExpandedElements(), but do have getAffectedElements(). --- cpp/common/src/codingstandards/cpp/AlertReporting.qll | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cpp/common/src/codingstandards/cpp/AlertReporting.qll b/cpp/common/src/codingstandards/cpp/AlertReporting.qll index 3ef5315906..97c46c0629 100644 --- a/cpp/common/src/codingstandards/cpp/AlertReporting.qll +++ b/cpp/common/src/codingstandards/cpp/AlertReporting.qll @@ -14,15 +14,21 @@ module MacroUnwrapper { * Gets a macro invocation that applies to the result element. */ private MacroInvocation getAMacroInvocation(ResultElement re) { - result.getAnExpandedElement() = re + result.getAnAffectedElement() = re } /** * Gets the primary macro invocation that generated the result element. + * + * Does not hold for cases where the result element is located at a macro argument site. */ MacroInvocation getPrimaryMacroInvocation(ResultElement re) { exists(MacroInvocation mi | mi = getAMacroInvocation(re) and + // Only report cases where the element is not located at the macro expansion site + // This means we'll report results in macro arguments in the macro argument + // location, not within the macro itself + mi.getLocation().getStartColumn() = re.getLocation().getStartColumn() and // No other more specific macro that expands to element not exists(MacroInvocation otherMi | otherMi = getAMacroInvocation(re) and otherMi.getParentInvocation() = mi From 9aa5e214cd87a8d583ac0fc11c9608806a449711 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Sun, 17 Aug 2025 23:19:58 +0100 Subject: [PATCH 07/23] RULE-8-2-2 - NoCStyleOrFunctionalCasts Detects C-style casts and functional notation casts that should be replaced with explicit cast operators. Prevents unsafe type conversions that lack clear intent and proper type checking constraints. [a] --- .../RULE-8-2-2/NoCStyleOrFunctionalCasts.ql | 33 ++++ .../NoCStyleOrFunctionalCasts.expected | 7 + .../NoCStyleOrFunctionalCasts.qlref | 1 + cpp/misra/test/rules/RULE-8-2-2/test.cpp | 158 ++++++++++++++++++ 4 files changed, 199 insertions(+) create mode 100644 cpp/misra/src/rules/RULE-8-2-2/NoCStyleOrFunctionalCasts.ql create mode 100644 cpp/misra/test/rules/RULE-8-2-2/NoCStyleOrFunctionalCasts.expected create mode 100644 cpp/misra/test/rules/RULE-8-2-2/NoCStyleOrFunctionalCasts.qlref create mode 100644 cpp/misra/test/rules/RULE-8-2-2/test.cpp diff --git a/cpp/misra/src/rules/RULE-8-2-2/NoCStyleOrFunctionalCasts.ql b/cpp/misra/src/rules/RULE-8-2-2/NoCStyleOrFunctionalCasts.ql new file mode 100644 index 0000000000..3515d0ec1b --- /dev/null +++ b/cpp/misra/src/rules/RULE-8-2-2/NoCStyleOrFunctionalCasts.ql @@ -0,0 +1,33 @@ +/** + * @id cpp/misra/no-c-style-or-functional-casts + * @name RULE-8-2-2: C-style casts and functional notation casts shall not be used + * @description Using C-style casts or functional notation casts allows unsafe type conversions and + * makes code harder to maintain compared to using named casts like const_cast, + * dynamic_cast, static_cast and reinterpret_cast. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-2-2 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.CStyleCasts +import codingstandards.cpp.AlertReporting + +class MISRACPPProhibitedCStyleCasts extends ExplicitUserDefinedCStyleCast { + MISRACPPProhibitedCStyleCasts() { + // MISRA C++ permits casts to void types + not getType() instanceof VoidType + } +} + +from Element reportingElement, MISRACPPProhibitedCStyleCasts c +where + not isExcluded(c, Conversions2Package::noCStyleOrFunctionalCastsQuery()) and + reportingElement = MacroUnwrapper::unwrapElement(c) and + exists(reportingElement.getFile().getRelativePath()) // within user code - validated during testing +select reportingElement, "Use of explicit c-style cast to " + c.getType().getName() + "." diff --git a/cpp/misra/test/rules/RULE-8-2-2/NoCStyleOrFunctionalCasts.expected b/cpp/misra/test/rules/RULE-8-2-2/NoCStyleOrFunctionalCasts.expected new file mode 100644 index 0000000000..a2b3b522cc --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-2-2/NoCStyleOrFunctionalCasts.expected @@ -0,0 +1,7 @@ +| test.cpp:8:22:8:37 | (uint32_t)... | Use of explicit c-style cast to uint32_t. | +| test.cpp:9:22:9:32 | (unsigned int)... | Use of explicit c-style cast to unsigned int. | +| test.cpp:70:1:70:31 | #define ADD_ONE(x) ((int)x) + 1 | Use of explicit c-style cast to int. | +| test.cpp:83:19:83:26 | (int)... | Use of explicit c-style cast to int. | +| test.cpp:84:27:84:34 | (int)... | Use of explicit c-style cast to int. | +| test.cpp:112:10:112:13 | (int)... | Use of explicit c-style cast to int. | +| test.cpp:147:12:147:26 | (unsigned int)... | Use of explicit c-style cast to unsigned int. | diff --git a/cpp/misra/test/rules/RULE-8-2-2/NoCStyleOrFunctionalCasts.qlref b/cpp/misra/test/rules/RULE-8-2-2/NoCStyleOrFunctionalCasts.qlref new file mode 100644 index 0000000000..cbd07ad124 --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-2-2/NoCStyleOrFunctionalCasts.qlref @@ -0,0 +1 @@ +rules/RULE-8-2-2/NoCStyleOrFunctionalCasts.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-8-2-2/test.cpp b/cpp/misra/test/rules/RULE-8-2-2/test.cpp new file mode 100644 index 0000000000..3d57d7f3c8 --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-2-2/test.cpp @@ -0,0 +1,158 @@ +#include +#include +#include +int foo() { return 1; } +// A copy of A5-2-2 test.cpp, but with different cases compliant/non-compliant +void test_c_style_cast() { + double f = 3.14; + std::uint32_t n1 = (std::uint32_t)f; // NON_COMPLIANT - C-style cast + std::uint32_t n2 = unsigned(f); // NON_COMPLIANT - functional notation + + std::uint8_t n3 = 1; + std::uint8_t n4 = 1; + std::uint8_t n5 = n3 + n4; // ignored, implicit casts + + (void)foo(); // COMPLIANT - permitted by MISRA C++ +} + +class A { +public: + virtual void f1() {} +}; + +class B : A { +public: + virtual void f1() {} +}; + +class C { + void f1() {} +}; + +void test_cpp_style_cast() { + // These cases may contravene other rules, but are marked as COMPLIANT for + // this rule + A a1; + const A *a2 = &a1; + A *a3 = const_cast(a2); // COMPLIANT + B *b = dynamic_cast(a3); // COMPLIANT + C *c = reinterpret_cast(a3); // COMPLIANT + std::int16_t n8 = 0; + std::int32_t n9 = static_cast(n8); // COMPLIANT + static_cast(foo()); // COMPLIANT +} + +class A5_2_2a { +public: + template + static void Foo(const std::string &name, As &&...rest) { + Fun(Log(std::forward( + rest)...)); // COMPLIANT - previously reported as a false positive + } + + template static std::string Log(As &&...tail) { + return std::string(); + } + + static void Fun(const std::string &message) {} +}; + +class A5_2_2 final { +public: + void f(const std::string &s) const { A5_2_2a::Foo("name", "x", "y", "z"); } +}; + +void a5_2_2_test() { + A5_2_2 a; + a.f(""); +} + +#define ADD_ONE(x) ((int)x) + 1 // NON_COMPLIANT +#define NESTED_ADD_ONE(x) ADD_ONE(x) // Not reported - reported at ADD_ONE +#define NO_CAST_ADD_ONE(x) x + 1 // COMPLIANT + +#include "macro_c_style_casts.h" + +void test_macro_cast() { + ADD_ONE(1); // Reported at macro definition site + NESTED_ADD_ONE(1); // reported at macro definition site + LIBRARY_ADD_TWO(1); // COMPLIANT - macro generating the cast is defined in a + // library, and is not modifiable by the user + LIBRARY_NESTED_ADD_TWO(1); // COMPLIANT - macro generating the cast is defined + // in a library, and is not modifiable by the user + NO_CAST_ADD_ONE((int)1.0); // NON_COMPLIANT - cast in argument to macro + LIBRARY_NO_CAST_ADD_TWO((int)1.0); // NON_COMPLIANT - library macro with + // c-style cast in argument, written by + // user so should be reported +} + +class D { +public: + D(int x) : fx(x), fy(0) {} + D(int x, int y) : fx(x), fy(y) {} + +private: + int fx; + int fy; +}; + +D testNonFunctionalCast() { + return (D)1; // COMPLIANT +} + +D testFunctionalCast() { + return D(1); // COMPLIANT +} + +D testFunctionalCastMulti() { + return D(1, 2); // COMPLIANT +} + +template T testFunctionalCastTemplate() { + return T(1); // NON_COMPLIANT - used with an int +} + +template T testFunctionalCastTemplateMulti() { + return T(1, 2); // COMPLIANT +} + +void testFunctionalCastTemplateUse() { + testFunctionalCastTemplate(); + testFunctionalCastTemplate(); + testFunctionalCastTemplateMulti(); +} + +template class E { +public: + class F { + public: + F(int x) : fx(x), fy(0) {} + F(int x, int y) : fx(x), fy(y) {} + + private: + int fx; + int fy; + }; + + F f() { + return F(1); // COMPLIANT + } + + D d() { + return D(1); // COMPLIANT + } + + int i() { + double f = 3.14; + return (unsigned int)f; // NON_COMPLIANT + } +}; + +class G {}; + +void testE() { + E e; + e.f(); + e.d(); + e.i(); +} From 9118b81101baca05ec21e5dac71b89b8f07381c9 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Sun, 17 Aug 2025 23:39:30 +0100 Subject: [PATCH 08/23] RULE-8-2-6 - IntToPointerCastProhibited Detects casts from integral, enumerated, or void pointer types to object pointer types that may lead to unspecified behavior. [a] --- .../RULE-8-2-6/IntToPointerCastProhibited.ql | 43 +++++++++ .../IntToPointerCastProhibited.expected | 11 +++ .../IntToPointerCastProhibited.qlref | 1 + cpp/misra/test/rules/RULE-8-2-6/test.cpp | 90 +++++++++++++++++++ 4 files changed, 145 insertions(+) create mode 100644 cpp/misra/src/rules/RULE-8-2-6/IntToPointerCastProhibited.ql create mode 100644 cpp/misra/test/rules/RULE-8-2-6/IntToPointerCastProhibited.expected create mode 100644 cpp/misra/test/rules/RULE-8-2-6/IntToPointerCastProhibited.qlref create mode 100644 cpp/misra/test/rules/RULE-8-2-6/test.cpp diff --git a/cpp/misra/src/rules/RULE-8-2-6/IntToPointerCastProhibited.ql b/cpp/misra/src/rules/RULE-8-2-6/IntToPointerCastProhibited.ql new file mode 100644 index 0000000000..dd1c7d15a4 --- /dev/null +++ b/cpp/misra/src/rules/RULE-8-2-6/IntToPointerCastProhibited.ql @@ -0,0 +1,43 @@ +/** + * @id cpp/misra/int-to-pointer-cast-prohibited + * @name RULE-8-2-6: An object with integral, enumerated, or pointer to void type shall not be cast to a pointer type + * @description Casting from an integral type, enumerated type, or pointer to void type to a pointer + * type leads to unspecified behavior and is error prone. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-2-6 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra + +from Cast cast, Type sourceType, Type targetType +where + not isExcluded(cast, Conversions2Package::intToPointerCastProhibitedQuery()) and + sourceType = cast.getExpr().getType().stripTopLevelSpecifiers() and + targetType = cast.getType().stripTopLevelSpecifiers() and + targetType instanceof PointerType and + not targetType instanceof FunctionPointerType and + not ( + // Exception: casts between void pointers are allowed + targetType.(PointerType).getBaseType().stripTopLevelSpecifiers() instanceof VoidType and + sourceType instanceof PointerType and + sourceType.(PointerType).getBaseType().stripTopLevelSpecifiers() instanceof VoidType + ) and + ( + // Integral types + sourceType instanceof IntegralType + or + // Enumerated types + sourceType instanceof Enum + or + // Pointer to void type + sourceType instanceof PointerType and + sourceType.(PointerType).getBaseType().stripTopLevelSpecifiers() instanceof VoidType + ) +select cast, + "Cast from '" + sourceType.toString() + "' to '" + targetType.toString() + "' is prohibited." diff --git a/cpp/misra/test/rules/RULE-8-2-6/IntToPointerCastProhibited.expected b/cpp/misra/test/rules/RULE-8-2-6/IntToPointerCastProhibited.expected new file mode 100644 index 0000000000..806c70a661 --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-2-6/IntToPointerCastProhibited.expected @@ -0,0 +1,11 @@ +| test.cpp:15:20:15:53 | reinterpret_cast... | Cast from 'signed int' to 'TestStruct *' is prohibited. | +| test.cpp:16:20:16:53 | reinterpret_cast... | Cast from 'signed long' to 'TestStruct *' is prohibited. | +| test.cpp:17:22:17:57 | reinterpret_cast... | Cast from 'signed long' to 'int32_t *' is prohibited. | +| test.cpp:24:20:24:53 | reinterpret_cast... | Cast from 'TestEnum' to 'TestStruct *' is prohibited. | +| test.cpp:25:22:25:57 | reinterpret_cast... | Cast from 'TestEnum' to 'int32_t *' is prohibited. | +| test.cpp:32:20:32:48 | static_cast... | Cast from 'void *' to 'TestStruct *' is prohibited. | +| test.cpp:33:20:33:53 | reinterpret_cast... | Cast from 'void *' to 'TestStruct *' is prohibited. | +| test.cpp:34:22:34:52 | static_cast... | Cast from 'void *' to 'int32_t *' is prohibited. | +| test.cpp:35:22:35:57 | reinterpret_cast... | Cast from 'void *' to 'int32_t *' is prohibited. | +| test.cpp:43:14:43:41 | reinterpret_cast... | Cast from 'signed int' to 'void *' is prohibited. | +| test.cpp:44:14:44:41 | reinterpret_cast... | Cast from 'signed long' to 'void *' is prohibited. | diff --git a/cpp/misra/test/rules/RULE-8-2-6/IntToPointerCastProhibited.qlref b/cpp/misra/test/rules/RULE-8-2-6/IntToPointerCastProhibited.qlref new file mode 100644 index 0000000000..c6f30b00f6 --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-2-6/IntToPointerCastProhibited.qlref @@ -0,0 +1 @@ +rules/RULE-8-2-6/IntToPointerCastProhibited.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-8-2-6/test.cpp b/cpp/misra/test/rules/RULE-8-2-6/test.cpp new file mode 100644 index 0000000000..5abd52242e --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-2-6/test.cpp @@ -0,0 +1,90 @@ +#include + +struct TestStruct { + std::int32_t m1; + std::int32_t m2; +}; + +enum TestEnum { VALUE1, VALUE2 }; + +void test_integral_to_pointer_cast() { + std::int32_t l1 = 42; + std::int64_t l2 = 0x1000; + + // Casting integral types to pointer types + TestStruct *l4 = reinterpret_cast(l1); // NON_COMPLIANT + TestStruct *l5 = reinterpret_cast(l2); // NON_COMPLIANT + std::int32_t *l7 = reinterpret_cast(l2); // NON_COMPLIANT +} + +void test_enumerated_to_pointer_cast() { + TestEnum l1 = VALUE1; + + // Casting enumerated types to pointer types + TestStruct *l3 = reinterpret_cast(l1); // NON_COMPLIANT + std::int32_t *l5 = reinterpret_cast(l1); // NON_COMPLIANT +} + +void test_void_pointer_to_object_pointer_cast() { + void *l1 = nullptr; + + // Casting void pointer to object pointer types + TestStruct *l2 = static_cast(l1); // NON_COMPLIANT + TestStruct *l3 = reinterpret_cast(l1); // NON_COMPLIANT + std::int32_t *l4 = static_cast(l1); // NON_COMPLIANT + std::int32_t *l5 = reinterpret_cast(l1); // NON_COMPLIANT +} + +void test_integral_to_void_pointer_cast() { + std::int32_t l1 = 42; + std::int64_t l2 = 0x1000; + + // Casting integral types to void pointer + void *l3 = reinterpret_cast(l1); // NON_COMPLIANT + void *l4 = reinterpret_cast(l2); // NON_COMPLIANT +} + +void test_compliant_void_pointer_casts() { + void *l1 = nullptr; + const void *l2 = nullptr; + + // Casts between void pointers are allowed + const void *l3 = const_cast(l1); // COMPLIANT + void *l4 = const_cast(l2); // COMPLIANT + volatile void *l5 = static_cast(l1); // COMPLIANT +} + +void f1() {} +void f2(int) {} + +void test_compliant_function_pointer_exceptions() { + std::int64_t l1 = 0x1000; + + // Function pointer casts are exceptions to this rule + void (*l2)() = reinterpret_cast(l1); // COMPLIANT + void (*l3)(int) = reinterpret_cast(l1); // COMPLIANT +} + +struct TestClass { + void memberFunc(); + std::int32_t m1; +}; + +void test_compliant_member_function_pointer_exceptions() { + void *l1 = nullptr; + + // Member function pointer casts are technically exceptions to this rule, but + // are prohibited by the compiler. + // void (TestClass::*l2)() = + // reinterpret_cast(l1); // COMPLIANT +} + +void test_compliant_regular_pointer_operations() { + TestStruct l1; + TestStruct *l2 = &l1; + std::int32_t *l3 = &l1.m1; + + // Regular pointer operations that don't involve forbidden casts + TestStruct *l4 = l2; // COMPLIANT + std::int32_t *l5 = l3; // COMPLIANT +} \ No newline at end of file From 13e184e5fc65c4fef3f77093a08a7257608c4dc9 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Sun, 17 Aug 2025 23:57:07 +0100 Subject: [PATCH 09/23] Add intptr_t and uintptr_t stubs --- cpp/common/test/includes/standard-library/cstdint.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpp/common/test/includes/standard-library/cstdint.h b/cpp/common/test/includes/standard-library/cstdint.h index 6a691637ff..888162dc36 100644 --- a/cpp/common/test/includes/standard-library/cstdint.h +++ b/cpp/common/test/includes/standard-library/cstdint.h @@ -15,5 +15,7 @@ typedef uint8_t uint_fast8_t; typedef uint16_t uint_fast16_t; typedef uint32_t uint_fast32_t; typedef uint64_t uint_fast64_t; +typedef long intptr_t; +typedef unsigned long uintptr_t; } // namespace std #endif // _GHLIBCPP_CSTDINT \ No newline at end of file From 72d24a152418b08c854aa28d71c8b78cd883c738 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Sun, 17 Aug 2025 23:57:30 +0100 Subject: [PATCH 10/23] RULE-8-2-7 - NoPointerToIntegralCast Detects casts that convert pointer types to integral types, which can make code harder to understand and may break pointer tracking in analysis tools. [a] --- .../RULE-8-2-7/NoPointerToIntegralCast.ql | 25 ++++++ .../NoPointerToIntegralCast.expected | 17 +++++ .../RULE-8-2-7/NoPointerToIntegralCast.qlref | 1 + cpp/misra/test/rules/RULE-8-2-7/test.cpp | 76 +++++++++++++++++++ 4 files changed, 119 insertions(+) create mode 100644 cpp/misra/src/rules/RULE-8-2-7/NoPointerToIntegralCast.ql create mode 100644 cpp/misra/test/rules/RULE-8-2-7/NoPointerToIntegralCast.expected create mode 100644 cpp/misra/test/rules/RULE-8-2-7/NoPointerToIntegralCast.qlref create mode 100644 cpp/misra/test/rules/RULE-8-2-7/test.cpp diff --git a/cpp/misra/src/rules/RULE-8-2-7/NoPointerToIntegralCast.ql b/cpp/misra/src/rules/RULE-8-2-7/NoPointerToIntegralCast.ql new file mode 100644 index 0000000000..e38467bd40 --- /dev/null +++ b/cpp/misra/src/rules/RULE-8-2-7/NoPointerToIntegralCast.ql @@ -0,0 +1,25 @@ +/** + * @id cpp/misra/no-pointer-to-integral-cast + * @name RULE-8-2-7: A cast should not convert a pointer type to an integral type + * @description Casting between pointer types and integral types makes code behavior harder to + * understand and may cause pointer tracking tools to become unreliable. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-2-7 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra + +from Cast cast, Type sourceType, Type targetType +where + not isExcluded(cast, Conversions2Package::noPointerToIntegralCastQuery()) and + sourceType = cast.getExpr().getType().getUnspecifiedType() and + targetType = cast.getType().getUnspecifiedType() and + (sourceType instanceof PointerType or sourceType instanceof FunctionPointerType) and + targetType instanceof IntegralType +select cast, "Cast converts pointer type to integral type '" + targetType.toString() + "'." diff --git a/cpp/misra/test/rules/RULE-8-2-7/NoPointerToIntegralCast.expected b/cpp/misra/test/rules/RULE-8-2-7/NoPointerToIntegralCast.expected new file mode 100644 index 0000000000..7a5f729206 --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-2-7/NoPointerToIntegralCast.expected @@ -0,0 +1,17 @@ +| test.cpp:22:22:22:59 | reinterpret_cast... | Cast converts pointer type to integral type 'long'. | +| test.cpp:23:23:23:61 | reinterpret_cast... | Cast converts pointer type to integral type 'unsigned long'. | +| test.cpp:24:20:24:55 | reinterpret_cast... | Cast converts pointer type to integral type 'signed char'. | +| test.cpp:25:21:25:57 | reinterpret_cast... | Cast converts pointer type to integral type 'unsigned char'. | +| test.cpp:26:21:26:57 | reinterpret_cast... | Cast converts pointer type to integral type 'signed short'. | +| test.cpp:27:22:27:59 | reinterpret_cast... | Cast converts pointer type to integral type 'unsigned short'. | +| test.cpp:28:21:28:59 | reinterpret_cast... | Cast converts pointer type to integral type 'signed int'. | +| test.cpp:29:22:29:61 | reinterpret_cast... | Cast converts pointer type to integral type 'unsigned int'. | +| test.cpp:30:22:30:61 | reinterpret_cast... | Cast converts pointer type to integral type 'signed long'. | +| test.cpp:32:7:32:47 | reinterpret_cast... | Cast converts pointer type to integral type 'unsigned long'. | +| test.cpp:33:14:33:42 | reinterpret_cast... | Cast converts pointer type to integral type 'long'. | +| test.cpp:34:23:34:60 | reinterpret_cast... | Cast converts pointer type to integral type 'unsigned long'. | +| test.cpp:35:13:35:40 | reinterpret_cast... | Cast converts pointer type to integral type 'int'. | +| test.cpp:36:22:36:58 | reinterpret_cast... | Cast converts pointer type to integral type 'unsigned int'. | +| test.cpp:64:7:64:47 | reinterpret_cast... | Cast converts pointer type to integral type 'long'. | +| test.cpp:66:7:66:48 | reinterpret_cast... | Cast converts pointer type to integral type 'unsigned long'. | +| test.cpp:67:14:67:45 | reinterpret_cast... | Cast converts pointer type to integral type 'long'. | diff --git a/cpp/misra/test/rules/RULE-8-2-7/NoPointerToIntegralCast.qlref b/cpp/misra/test/rules/RULE-8-2-7/NoPointerToIntegralCast.qlref new file mode 100644 index 0000000000..c46a48360b --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-2-7/NoPointerToIntegralCast.qlref @@ -0,0 +1 @@ +rules/RULE-8-2-7/NoPointerToIntegralCast.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-8-2-7/test.cpp b/cpp/misra/test/rules/RULE-8-2-7/test.cpp new file mode 100644 index 0000000000..6f89118c21 --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-2-7/test.cpp @@ -0,0 +1,76 @@ +#include + +struct S { + int m1; +}; + +class C { +public: + int m1; +}; + +void test_pointer_to_integral_casts() { + S s; + S *s_ptr = &s; + C c; + C *c_ptr = &c; + int l1 = 42; + int *int_ptr = &l1; + void *void_ptr = &l1; + + // Non-compliant cases - pointer to integral type casts + std::intptr_t l2 = reinterpret_cast(s_ptr); // NON_COMPLIANT + std::uintptr_t l3 = reinterpret_cast(s_ptr); // NON_COMPLIANT + std::int8_t l4 = reinterpret_cast(s_ptr); // NON_COMPLIANT + std::uint8_t l5 = reinterpret_cast(s_ptr); // NON_COMPLIANT + std::int16_t l6 = reinterpret_cast(c_ptr); // NON_COMPLIANT + std::uint16_t l7 = reinterpret_cast(c_ptr); // NON_COMPLIANT + std::int32_t l8 = reinterpret_cast(int_ptr); // NON_COMPLIANT + std::uint32_t l9 = reinterpret_cast(int_ptr); // NON_COMPLIANT + std::int64_t l10 = reinterpret_cast(void_ptr); // NON_COMPLIANT + std::uint64_t l11 = + reinterpret_cast(void_ptr); // NON_COMPLIANT + long l12 = reinterpret_cast(s_ptr); // NON_COMPLIANT + unsigned long l13 = reinterpret_cast(s_ptr); // NON_COMPLIANT + int l14 = reinterpret_cast(s_ptr); // NON_COMPLIANT + unsigned int l15 = reinterpret_cast(s_ptr); // NON_COMPLIANT +} + +void test_compliant_pointer_operations() { + S s; + S *s_ptr = &s; + C c; + C *c_ptr = &c; + int l1 = 42; + int *int_ptr = &l1; + + // Compliant cases - pointer to pointer casts + void *l16 = static_cast(s_ptr); // COMPLIANT + S *l17 = static_cast(static_cast(s_ptr)); // COMPLIANT + C *l18 = reinterpret_cast(s_ptr); // COMPLIANT + + // Compliant cases - using pointers without casting to integral + S *l19 = s_ptr; // COMPLIANT + if (s_ptr != nullptr) { // COMPLIANT + s_ptr->m1 = 10; // COMPLIANT + } +} + +void test_function_pointer_to_integral() { + void (*func_ptr)() = nullptr; + + // Non-compliant cases - function pointer to integral type casts + std::intptr_t l20 = + reinterpret_cast(func_ptr); // NON_COMPLIANT + std::uintptr_t l21 = + reinterpret_cast(func_ptr); // NON_COMPLIANT + long l22 = reinterpret_cast(func_ptr); // NON_COMPLIANT +} + +void test_member_pointer_to_integral() { + // Member pointer to integral type casts are forbidden by the rule, but also + // prohibited by the compiler, e.g. + // int S::*member_ptr = &S::m1; + // std::intptr_t l23 = reinterpret_cast(member_ptr); + // std::uintptr_t l24 = reinterpret_cast(member_ptr); +} \ No newline at end of file From 8804fecce236fed51212d34a2ea69133f4f59e40 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Mon, 18 Aug 2025 00:11:39 +0100 Subject: [PATCH 11/23] RULE-8-2-8 - PointerToIntegralCast Detects pointer-to-integral casts that use types other than std::uintptr_t or std::intptr_t, which may not guarantee representation of all pointer values. [a] --- .../rules/RULE-8-2-8/PointerToIntegralCast.ql | 32 ++++++++ .../RULE-8-2-8/PointerToIntegralCast.expected | 9 ++ .../RULE-8-2-8/PointerToIntegralCast.qlref | 1 + cpp/misra/test/rules/RULE-8-2-8/test.cpp | 82 +++++++++++++++++++ 4 files changed, 124 insertions(+) create mode 100644 cpp/misra/src/rules/RULE-8-2-8/PointerToIntegralCast.ql create mode 100644 cpp/misra/test/rules/RULE-8-2-8/PointerToIntegralCast.expected create mode 100644 cpp/misra/test/rules/RULE-8-2-8/PointerToIntegralCast.qlref create mode 100644 cpp/misra/test/rules/RULE-8-2-8/test.cpp diff --git a/cpp/misra/src/rules/RULE-8-2-8/PointerToIntegralCast.ql b/cpp/misra/src/rules/RULE-8-2-8/PointerToIntegralCast.ql new file mode 100644 index 0000000000..f7c2c4ffbd --- /dev/null +++ b/cpp/misra/src/rules/RULE-8-2-8/PointerToIntegralCast.ql @@ -0,0 +1,32 @@ +/** + * @id cpp/misra/pointer-to-integral-cast + * @name RULE-8-2-8: An object pointer type shall not be cast to an integral type other than std::uintptr_t or + * @description Casting object pointers to integral types other than std::uintptr_t or std::intptr_t + * can lead to implementation-defined behavior and potential loss of pointer + * information. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-2-8 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra + +from ReinterpretCast cast, Type targetType, Type sourceType +where + not isExcluded(cast, Conversions2Package::pointerToIntegralCastQuery()) and + sourceType = cast.getExpr().getType().getUnspecifiedType() and + sourceType instanceof PointerType and + targetType = cast.getType() and + targetType.getUnspecifiedType() instanceof IntegralType and + not ( + targetType.(UserType).hasGlobalOrStdName("uintptr_t") or + targetType.(UserType).hasGlobalOrStdName("intptr_t") + ) +select cast, + "Cast of object pointer type to integral type '" + targetType.toString() + + "' instead of 'std::uintptr_t' or 'std::intptr_t'." diff --git a/cpp/misra/test/rules/RULE-8-2-8/PointerToIntegralCast.expected b/cpp/misra/test/rules/RULE-8-2-8/PointerToIntegralCast.expected new file mode 100644 index 0000000000..7a541ff406 --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-2-8/PointerToIntegralCast.expected @@ -0,0 +1,9 @@ +| test.cpp:37:13:37:47 | reinterpret_cast... | Cast of object pointer type to integral type 'unsigned long' instead of 'std::uintptr_t' or 'std::intptr_t'. | +| test.cpp:42:13:42:46 | reinterpret_cast... | Cast of object pointer type to integral type 'unsigned int' instead of 'std::uintptr_t' or 'std::intptr_t'. | +| test.cpp:47:13:47:38 | reinterpret_cast... | Cast of object pointer type to integral type 'long' instead of 'std::uintptr_t' or 'std::intptr_t'. | +| test.cpp:52:13:52:45 | reinterpret_cast... | Cast of object pointer type to integral type 'size_t' instead of 'std::uintptr_t' or 'std::intptr_t'. | +| test.cpp:57:13:57:43 | reinterpret_cast... | Cast of object pointer type to integral type 'hashPtr_t' instead of 'std::uintptr_t' or 'std::intptr_t'. | +| test.cpp:62:13:62:42 | reinterpret_cast... | Cast of object pointer type to integral type 'MyIntPtr' instead of 'std::uintptr_t' or 'std::intptr_t'. | +| test.cpp:67:13:67:35 | reinterpret_cast... | Cast of object pointer type to integral type 'unsigned long' instead of 'std::uintptr_t' or 'std::intptr_t'. | +| test.cpp:72:13:72:47 | reinterpret_cast... | Cast of object pointer type to integral type 'uint64_t' instead of 'std::uintptr_t' or 'std::intptr_t'. | +| test.cpp:77:13:77:46 | reinterpret_cast... | Cast of object pointer type to integral type 'int64_t' instead of 'std::uintptr_t' or 'std::intptr_t'. | diff --git a/cpp/misra/test/rules/RULE-8-2-8/PointerToIntegralCast.qlref b/cpp/misra/test/rules/RULE-8-2-8/PointerToIntegralCast.qlref new file mode 100644 index 0000000000..ed46d08aeb --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-2-8/PointerToIntegralCast.qlref @@ -0,0 +1 @@ +rules/RULE-8-2-8/PointerToIntegralCast.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-8-2-8/test.cpp b/cpp/misra/test/rules/RULE-8-2-8/test.cpp new file mode 100644 index 0000000000..75b3d7cb88 --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-2-8/test.cpp @@ -0,0 +1,82 @@ +#include +#include +struct S {}; +class C {}; + +void *g1 = nullptr; +S *g2 = nullptr; +const int *g3 = nullptr; + +using hashPtr_t = std::uintptr_t; +using MyIntPtr = std::intptr_t; + +void test_compliant_uintptr_t_cast() { + S *l1 = nullptr; + auto l2 = reinterpret_cast(l1); // COMPLIANT +} + +void test_compliant_intptr_t_cast() { + C *l1 = nullptr; + auto l2 = reinterpret_cast(l1); // COMPLIANT +} + +void test_compliant_void_pointer_cast() { + void *l1 = nullptr; + auto l2 = reinterpret_cast(l1); // COMPLIANT + auto l3 = reinterpret_cast(l1); // COMPLIANT +} + +void test_compliant_const_pointer_cast() { + const int *l1 = nullptr; + auto l2 = reinterpret_cast(l1); // COMPLIANT + auto l3 = reinterpret_cast(l1); // COMPLIANT +} + +void test_non_compliant_unsigned_long_cast() { + S *l1 = nullptr; + auto l2 = reinterpret_cast(l1); // NON_COMPLIANT +} + +void test_non_compliant_unsigned_int_cast() { + S *l1 = nullptr; + auto l2 = reinterpret_cast(l1); // NON_COMPLIANT +} + +void test_non_compliant_long_cast() { + C *l1 = nullptr; + auto l2 = reinterpret_cast(l1); // NON_COMPLIANT +} + +void test_non_compliant_size_t_cast() { + void *l1 = nullptr; + auto l2 = reinterpret_cast(l1); // NON_COMPLIANT +} + +void test_non_compliant_typedef_cast() { + S *l1 = nullptr; + auto l2 = reinterpret_cast(l1); // NON_COMPLIANT +} + +void test_non_compliant_using_alias_cast() { + C *l1 = nullptr; + auto l2 = reinterpret_cast(l1); // NON_COMPLIANT +} + +template void test_non_compliant_template_cast() { + S *l1 = nullptr; + auto l2 = reinterpret_cast(l1); // NON_COMPLIANT +} + +void test_non_compliant_uint64_t_cast() { + S *l1 = nullptr; + auto l2 = reinterpret_cast(l1); // NON_COMPLIANT +} + +void test_non_compliant_int64_t_cast() { + void *l1 = nullptr; + auto l2 = reinterpret_cast(l1); // NON_COMPLIANT +} + +void test_instantiate_template() { + test_non_compliant_template_cast(); +} \ No newline at end of file From 29f1bbdc87e523c9d4f44681d23f95a4b802628a Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Mon, 18 Aug 2025 14:33:07 +0100 Subject: [PATCH 12/23] RULE-9-2-1 - NoStandaloneTypeCastExpression Detects explicit type conversions using functional notation as standalone expression statements that create immediately-destroyed temporary objects. [a] --- .../NoStandaloneTypeCastExpression.ql | 36 +++++++++ .../NoStandaloneTypeCastExpression.expected | 7 ++ .../NoStandaloneTypeCastExpression.qlref | 1 + cpp/misra/test/rules/RULE-9-2-1/test.cpp | 74 +++++++++++++++++++ 4 files changed, 118 insertions(+) create mode 100644 cpp/misra/src/rules/RULE-9-2-1/NoStandaloneTypeCastExpression.ql create mode 100644 cpp/misra/test/rules/RULE-9-2-1/NoStandaloneTypeCastExpression.expected create mode 100644 cpp/misra/test/rules/RULE-9-2-1/NoStandaloneTypeCastExpression.qlref create mode 100644 cpp/misra/test/rules/RULE-9-2-1/test.cpp diff --git a/cpp/misra/src/rules/RULE-9-2-1/NoStandaloneTypeCastExpression.ql b/cpp/misra/src/rules/RULE-9-2-1/NoStandaloneTypeCastExpression.ql new file mode 100644 index 0000000000..ce1cde0b59 --- /dev/null +++ b/cpp/misra/src/rules/RULE-9-2-1/NoStandaloneTypeCastExpression.ql @@ -0,0 +1,36 @@ +/** + * @id cpp/misra/no-standalone-type-cast-expression + * @name RULE-9-2-1: An explicit type conversion shall not be an expression statement + * @description Using an explicit type conversion as an expression statement creates a temporary + * object that is immediately discarded, which can lead to unintended premature + * resource cleanup. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-9-2-1 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra + +from ExprStmt stmt, Expr expr +where + not isExcluded(stmt, Conversions2Package::noStandaloneTypeCastExpressionQuery()) and + expr = stmt.getExpr() and + ( + // Explicit conversions which call a constructor + expr instanceof ConstructorCall + or + // Cast expressions using functional notation + expr instanceof Cast + ) and + // Exclude init-statements in if/for statements + // This is because the extractor has a bug as of 2.20.7 which means it does not parse + // these two cases separately. We choose to ignore if statements, which can cause false + // negatives, but will prevent false positives + not exists(IfStmt ifStmt | ifStmt.getInitialization() = stmt) +select stmt, + "Explicit type conversion used as expression statement creates temporary object that is immediately discarded." diff --git a/cpp/misra/test/rules/RULE-9-2-1/NoStandaloneTypeCastExpression.expected b/cpp/misra/test/rules/RULE-9-2-1/NoStandaloneTypeCastExpression.expected new file mode 100644 index 0000000000..eb58109070 --- /dev/null +++ b/cpp/misra/test/rules/RULE-9-2-1/NoStandaloneTypeCastExpression.expected @@ -0,0 +1,7 @@ +| test.cpp:20:3:20:35 | ExprStmt | Explicit type conversion used as expression statement creates temporary object that is immediately discarded. | +| test.cpp:21:3:21:23 | ExprStmt | Explicit type conversion used as expression statement creates temporary object that is immediately discarded. | +| test.cpp:22:3:22:27 | ExprStmt | Explicit type conversion used as expression statement creates temporary object that is immediately discarded. | +| test.cpp:23:3:23:34 | ExprStmt | Explicit type conversion used as expression statement creates temporary object that is immediately discarded. | +| test.cpp:42:8:42:40 | ExprStmt | Explicit type conversion used as expression statement creates temporary object that is immediately discarded. | +| test.cpp:66:3:66:17 | ExprStmt | Explicit type conversion used as expression statement creates temporary object that is immediately discarded. | +| test.cpp:67:3:67:17 | ExprStmt | Explicit type conversion used as expression statement creates temporary object that is immediately discarded. | diff --git a/cpp/misra/test/rules/RULE-9-2-1/NoStandaloneTypeCastExpression.qlref b/cpp/misra/test/rules/RULE-9-2-1/NoStandaloneTypeCastExpression.qlref new file mode 100644 index 0000000000..5aec9cdada --- /dev/null +++ b/cpp/misra/test/rules/RULE-9-2-1/NoStandaloneTypeCastExpression.qlref @@ -0,0 +1 @@ +rules/RULE-9-2-1/NoStandaloneTypeCastExpression.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-9-2-1/test.cpp b/cpp/misra/test/rules/RULE-9-2-1/test.cpp new file mode 100644 index 0000000000..75aed0a90e --- /dev/null +++ b/cpp/misra/test/rules/RULE-9-2-1/test.cpp @@ -0,0 +1,74 @@ +#include +#include +#include + +std::mutex g1; +std::mutex g2; + +void f1(std::unique_lock l1) { + // Function parameter for testing compliant cases +} + +void test_explicit_type_conversion_expression_statement() { + // Compliant cases - declarations + std::unique_lock l1(g1); // COMPLIANT + std::unique_lock l2{g1}; // COMPLIANT + std::unique_lock(l3); // COMPLIANT - declaration with redundant + // parentheses around variable name + + // Non-compliant cases - explicit type conversions as expression statements + std::unique_lock{g1}; // NON_COMPLIANT + std::scoped_lock{g1}; // NON_COMPLIANT + std::scoped_lock(g1, g2); // NON_COMPLIANT + std::lock_guard{g1}; // NON_COMPLIANT + + // Compliant cases - type conversions not as expression statements + f1(std::unique_lock(g1)); // COMPLIANT + f1(std::unique_lock{g1}); // COMPLIANT + auto l4 = std::unique_lock(g1); // COMPLIANT + auto l5 = std::unique_lock{g1}; // COMPLIANT + + // The extractor has a bug as of 2.20.7 which means it does not parse + // these two cases separately. We choose to ignore if statements, which + // can cause false negatives, but will prevent false positives + if (std::unique_lock(g1); true) { // COMPLIANT - init-statement + } + if (std::unique_lock{g1}; true) { // NON_COMPLIANT[FALSE_NEGATIVE] + } + + for (std::unique_lock(g1);;) { // COMPLIANT - init-statement + break; + } + for (std::unique_lock{g1};;) { // NON_COMPLIANT - init-statement + break; + } +} + +void test_primitive_type_conversions() { + // Non-compliant cases with primitive types + std::int32_t(42); // NON_COMPLIANT + float(3.14); // NON_COMPLIANT + double(2.71); // NON_COMPLIANT + bool(true); // NON_COMPLIANT + + // Compliant cases + auto l1 = std::int32_t(42); // COMPLIANT + auto l2 = float(3.14); // COMPLIANT + std::int32_t l3(42); // COMPLIANT - declaration + std::int32_t l4{42}; // COMPLIANT - declaration +} + +struct CustomType { + CustomType(std::int32_t) {} +}; +void test_custom_types() { + // Non-compliant cases + CustomType(42); // NON_COMPLIANT + CustomType{42}; // NON_COMPLIANT + + // Compliant cases + CustomType l1(42); // COMPLIANT - declaration + CustomType l2{42}; // COMPLIANT - declaration + auto l3 = CustomType(42); // COMPLIANT + auto l4 = CustomType{42}; // COMPLIANT +} \ No newline at end of file From d6456703581e2120543ebf693721557ab842b1e6 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Mon, 18 Aug 2025 14:49:16 +0100 Subject: [PATCH 13/23] Update mutex to include scoped_lock stubs --- cpp/common/test/includes/standard-library/mutex.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cpp/common/test/includes/standard-library/mutex.h b/cpp/common/test/includes/standard-library/mutex.h index 4c49819ddd..d21042dcf7 100644 --- a/cpp/common/test/includes/standard-library/mutex.h +++ b/cpp/common/test/includes/standard-library/mutex.h @@ -1,6 +1,6 @@ #ifndef _GHLIBCPP_MUTEX #define _GHLIBCPP_MUTEX -#include "chrono.h" +#include namespace std { @@ -77,6 +77,14 @@ template class lock_guard { mutex_type &_m; }; +template class scoped_lock { +public: + explicit scoped_lock(MutexTypes &...m); + scoped_lock(const scoped_lock &) = delete; + scoped_lock &operator=(const scoped_lock &) = delete; + ~scoped_lock(); +}; + } // namespace std #endif // _GHLIBCPP_MUTEX From 5efc380eddfdb6781872a648c3c8fffc6e9a1315 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Mon, 18 Aug 2025 14:49:29 +0100 Subject: [PATCH 14/23] Rule 8.2.6: Query improvements - Simplify the query implementation - Improve query message --- .../RULE-8-2-6/IntToPointerCastProhibited.ql | 29 +++++++++---------- .../IntToPointerCastProhibited.expected | 22 +++++++------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/cpp/misra/src/rules/RULE-8-2-6/IntToPointerCastProhibited.ql b/cpp/misra/src/rules/RULE-8-2-6/IntToPointerCastProhibited.ql index dd1c7d15a4..0ab4c96f2d 100644 --- a/cpp/misra/src/rules/RULE-8-2-6/IntToPointerCastProhibited.ql +++ b/cpp/misra/src/rules/RULE-8-2-6/IntToPointerCastProhibited.ql @@ -15,29 +15,26 @@ import cpp import codingstandards.cpp.misra -from Cast cast, Type sourceType, Type targetType +from Cast cast, Type sourceType, PointerType targetType, string typeKind where not isExcluded(cast, Conversions2Package::intToPointerCastProhibitedQuery()) and - sourceType = cast.getExpr().getType().stripTopLevelSpecifiers() and - targetType = cast.getType().stripTopLevelSpecifiers() and - targetType instanceof PointerType and - not targetType instanceof FunctionPointerType and - not ( - // Exception: casts between void pointers are allowed - targetType.(PointerType).getBaseType().stripTopLevelSpecifiers() instanceof VoidType and - sourceType instanceof PointerType and - sourceType.(PointerType).getBaseType().stripTopLevelSpecifiers() instanceof VoidType - ) and + sourceType = cast.getExpr().getType().getUnspecifiedType() and + targetType = cast.getType().getUnspecifiedType() and ( // Integral types - sourceType instanceof IntegralType + sourceType instanceof IntegralType and + typeKind = "integral" or // Enumerated types - sourceType instanceof Enum + sourceType instanceof Enum and + typeKind = "enumerated" or // Pointer to void type - sourceType instanceof PointerType and - sourceType.(PointerType).getBaseType().stripTopLevelSpecifiers() instanceof VoidType + sourceType.(PointerType).getBaseType() instanceof VoidType and + typeKind = "pointer to void" and + // Exception: casts between void pointers are allowed + not targetType.getBaseType() instanceof VoidType ) select cast, - "Cast from '" + sourceType.toString() + "' to '" + targetType.toString() + "' is prohibited." + "Cast from " + typeKind + " type '" + cast.getExpr().getType() + "' to pointer type '" + + cast.getType() + "'." diff --git a/cpp/misra/test/rules/RULE-8-2-6/IntToPointerCastProhibited.expected b/cpp/misra/test/rules/RULE-8-2-6/IntToPointerCastProhibited.expected index 806c70a661..18accdc95e 100644 --- a/cpp/misra/test/rules/RULE-8-2-6/IntToPointerCastProhibited.expected +++ b/cpp/misra/test/rules/RULE-8-2-6/IntToPointerCastProhibited.expected @@ -1,11 +1,11 @@ -| test.cpp:15:20:15:53 | reinterpret_cast... | Cast from 'signed int' to 'TestStruct *' is prohibited. | -| test.cpp:16:20:16:53 | reinterpret_cast... | Cast from 'signed long' to 'TestStruct *' is prohibited. | -| test.cpp:17:22:17:57 | reinterpret_cast... | Cast from 'signed long' to 'int32_t *' is prohibited. | -| test.cpp:24:20:24:53 | reinterpret_cast... | Cast from 'TestEnum' to 'TestStruct *' is prohibited. | -| test.cpp:25:22:25:57 | reinterpret_cast... | Cast from 'TestEnum' to 'int32_t *' is prohibited. | -| test.cpp:32:20:32:48 | static_cast... | Cast from 'void *' to 'TestStruct *' is prohibited. | -| test.cpp:33:20:33:53 | reinterpret_cast... | Cast from 'void *' to 'TestStruct *' is prohibited. | -| test.cpp:34:22:34:52 | static_cast... | Cast from 'void *' to 'int32_t *' is prohibited. | -| test.cpp:35:22:35:57 | reinterpret_cast... | Cast from 'void *' to 'int32_t *' is prohibited. | -| test.cpp:43:14:43:41 | reinterpret_cast... | Cast from 'signed int' to 'void *' is prohibited. | -| test.cpp:44:14:44:41 | reinterpret_cast... | Cast from 'signed long' to 'void *' is prohibited. | +| test.cpp:15:20:15:53 | reinterpret_cast... | Cast from integral type 'int32_t' to pointer type 'TestStruct *'. | +| test.cpp:16:20:16:53 | reinterpret_cast... | Cast from integral type 'int64_t' to pointer type 'TestStruct *'. | +| test.cpp:17:22:17:57 | reinterpret_cast... | Cast from integral type 'int64_t' to pointer type 'int32_t *'. | +| test.cpp:24:20:24:53 | reinterpret_cast... | Cast from enumerated type 'TestEnum' to pointer type 'TestStruct *'. | +| test.cpp:25:22:25:57 | reinterpret_cast... | Cast from enumerated type 'TestEnum' to pointer type 'int32_t *'. | +| test.cpp:32:20:32:48 | static_cast... | Cast from pointer to void type 'void *' to pointer type 'TestStruct *'. | +| test.cpp:33:20:33:53 | reinterpret_cast... | Cast from pointer to void type 'void *' to pointer type 'TestStruct *'. | +| test.cpp:34:22:34:52 | static_cast... | Cast from pointer to void type 'void *' to pointer type 'int32_t *'. | +| test.cpp:35:22:35:57 | reinterpret_cast... | Cast from pointer to void type 'void *' to pointer type 'int32_t *'. | +| test.cpp:43:14:43:41 | reinterpret_cast... | Cast from integral type 'int32_t' to pointer type 'void *'. | +| test.cpp:44:14:44:41 | reinterpret_cast... | Cast from integral type 'int64_t' to pointer type 'void *'. | From b0d18ceaf99594620012e7ffbe48a603aaf0d56a Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Mon, 18 Aug 2025 14:52:11 +0100 Subject: [PATCH 15/23] Rule 8.2.7: Simplify CodeQL --- cpp/misra/src/rules/RULE-8-2-7/NoPointerToIntegralCast.ql | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cpp/misra/src/rules/RULE-8-2-7/NoPointerToIntegralCast.ql b/cpp/misra/src/rules/RULE-8-2-7/NoPointerToIntegralCast.ql index e38467bd40..3b477cce66 100644 --- a/cpp/misra/src/rules/RULE-8-2-7/NoPointerToIntegralCast.ql +++ b/cpp/misra/src/rules/RULE-8-2-7/NoPointerToIntegralCast.ql @@ -15,11 +15,10 @@ import cpp import codingstandards.cpp.misra -from Cast cast, Type sourceType, Type targetType +from Cast cast, Type sourceType, IntegralType targetType where not isExcluded(cast, Conversions2Package::noPointerToIntegralCastQuery()) and sourceType = cast.getExpr().getType().getUnspecifiedType() and targetType = cast.getType().getUnspecifiedType() and - (sourceType instanceof PointerType or sourceType instanceof FunctionPointerType) and - targetType instanceof IntegralType -select cast, "Cast converts pointer type to integral type '" + targetType.toString() + "'." + (sourceType instanceof PointerType or sourceType instanceof FunctionPointerType) +select cast, "Cast converts pointer type to integral type '" + targetType + "'." From 51ef7bd7426013723d6dfcda525cfb9408578c8d Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Mon, 18 Aug 2025 14:56:55 +0100 Subject: [PATCH 16/23] Rule 8.2.8: Improve CodeQL query - Remove redundant code - Use stripSpecifiers(..) to check for use of (u)intptr_t. --- .../src/rules/RULE-8-2-8/PointerToIntegralCast.ql | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/cpp/misra/src/rules/RULE-8-2-8/PointerToIntegralCast.ql b/cpp/misra/src/rules/RULE-8-2-8/PointerToIntegralCast.ql index f7c2c4ffbd..668b3103d6 100644 --- a/cpp/misra/src/rules/RULE-8-2-8/PointerToIntegralCast.ql +++ b/cpp/misra/src/rules/RULE-8-2-8/PointerToIntegralCast.ql @@ -15,18 +15,15 @@ import cpp import codingstandards.cpp.misra +import codingstandards.cpp.types.Type -from ReinterpretCast cast, Type targetType, Type sourceType +from ReinterpretCast cast, PointerType sourceType, Type targetType where not isExcluded(cast, Conversions2Package::pointerToIntegralCastQuery()) and sourceType = cast.getExpr().getType().getUnspecifiedType() and - sourceType instanceof PointerType and targetType = cast.getType() and targetType.getUnspecifiedType() instanceof IntegralType and - not ( - targetType.(UserType).hasGlobalOrStdName("uintptr_t") or - targetType.(UserType).hasGlobalOrStdName("intptr_t") - ) + not stripSpecifiers(targetType).(UserType).hasGlobalOrStdName(["uintptr_t", "intptr_t"]) select cast, - "Cast of object pointer type to integral type '" + targetType.toString() + + "Cast of object pointer type to integral type '" + targetType + "' instead of 'std::uintptr_t' or 'std::intptr_t'." From 550f38c366ce21b00392859eb5575736c751b576 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Mon, 18 Aug 2025 14:58:53 +0100 Subject: [PATCH 17/23] Rule 9.2.1: Add proviso for if statement initializers --- rule_packages/cpp/Conversions2.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rule_packages/cpp/Conversions2.json b/rule_packages/cpp/Conversions2.json index 6c178effc6..4258fd16bf 100644 --- a/rule_packages/cpp/Conversions2.json +++ b/rule_packages/cpp/Conversions2.json @@ -116,7 +116,10 @@ "short_name": "NoStandaloneTypeCastExpression", "tags": [ "scope/single-translation-unit" - ] + ], + "implementation_scope": { + "description": "Expression statements in if statement initializers are not currently supported." + } } ], "title": "An explicit type conversion shall not be an expression statement" From 3ab9104e4a4d6325454a8561d89d18b451c8169a Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Mon, 18 Aug 2025 15:06:34 +0100 Subject: [PATCH 18/23] Add change note for AlertReporting behaviour --- change_notes/2025-08-18-fix-alert-reporting.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 change_notes/2025-08-18-fix-alert-reporting.md diff --git a/change_notes/2025-08-18-fix-alert-reporting.md b/change_notes/2025-08-18-fix-alert-reporting.md new file mode 100644 index 0000000000..fc1883ed7a --- /dev/null +++ b/change_notes/2025-08-18-fix-alert-reporting.md @@ -0,0 +1,3 @@ + - `RULE-1-2`, `RULE-23-3`, `RULE-23-5`, `RULE-23-6`: + - Results that occur in nested macro invocations are now reported in the macro that defines the contravening code, rather than the macro which is first expanded. + - Results the occur in arguments to macro invocations are now reported in at the macro invocation site, instead of the macro definition site. \ No newline at end of file From 1d0736eddf82f8296a980c7f3193ea00f19e8791 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Thu, 21 Aug 2025 17:15:45 +0100 Subject: [PATCH 19/23] PointerToAVirtualBaseClass: Support casts from parents of virtual base classes. --- .../2025-08-15-m5-2-2-virtual-base.md | 4 +++- ...interToAVirtualBaseClassCastToAPointer.qll | 22 +++++++++++++------ ...ToAVirtualBaseClassCastToAPointer.expected | 7 +++--- .../test.cpp | 14 +++++++++++- 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/change_notes/2025-08-15-m5-2-2-virtual-base.md b/change_notes/2025-08-15-m5-2-2-virtual-base.md index 072ad41ea6..f57b630ad9 100644 --- a/change_notes/2025-08-15-m5-2-2-virtual-base.md +++ b/change_notes/2025-08-15-m5-2-2-virtual-base.md @@ -1,3 +1,5 @@ - `M5-2-2` - `PointerToAVirtualBaseClassCastToAPointer.ql`: - Report casts where the from or to types are typedefs to virtual base classes or derived classes. - - Report casts to a reference type which is a derived type. \ No newline at end of file + - Report casts to a reference type which is a derived type. + - Report casts where the base class is the parent of a virtual base class. + - The alert message has been updated to refer to the virtual base class derivation. \ No newline at end of file diff --git a/cpp/common/src/codingstandards/cpp/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.qll b/cpp/common/src/codingstandards/cpp/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.qll index 207b6da415..6993778a4d 100644 --- a/cpp/common/src/codingstandards/cpp/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.qll +++ b/cpp/common/src/codingstandards/cpp/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.qll @@ -12,23 +12,31 @@ abstract class PointerToAVirtualBaseClassCastToAPointerSharedQuery extends Query Query getQuery() { result instanceof PointerToAVirtualBaseClassCastToAPointerSharedQuery } -query predicate problems(Cast cast, string message) { - exists(VirtualBaseClass castFrom, Class castTo | +query predicate problems( + Cast cast, string message, VirtualClassDerivation derivation, string derivationDescription +) { + exists(Class castFrom, VirtualBaseClass virtualBaseClass, Class castTo, string type | not isExcluded(cast, getQuery()) and not cast instanceof DynamicCast and - castTo = castFrom.getADerivedClass+() and + castTo = virtualBaseClass.getADerivedClass+() and + virtualBaseClass = castFrom.getADerivedClass*() and + derivation = virtualBaseClass.getAVirtualDerivation() and + derivation.getDerivedClass().getADerivedClass*() = castTo and + derivationDescription = "derived through virtual base class " + virtualBaseClass.getName() and message = - "A pointer to virtual base class " + castFrom.getName() + - " is not cast to a pointer of derived class " + castTo.getName() + " using a dynamic_cast." + "dynamic_cast not used for cast from " + type + " to base class " + castFrom.getName() + + " to derived class " + castTo.getName() + " which is $@." | // Pointer cast castFrom = cast.getExpr().getType().stripTopLevelSpecifiers().(PointerType).getBaseType() and - cast.getType().stripTopLevelSpecifiers().(PointerType).getBaseType() = castTo + cast.getType().stripTopLevelSpecifiers().(PointerType).getBaseType() = castTo and + type = "pointer" or // Reference type cast castFrom = cast.getExpr().getType().stripTopLevelSpecifiers() and // Not actually represented as a reference type in our model - instead as the // type itself - cast.getType().stripTopLevelSpecifiers() = castTo + cast.getType().stripTopLevelSpecifiers() = castTo and + type = "reference" ) } diff --git a/cpp/common/test/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.expected b/cpp/common/test/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.expected index 7f64ab5d1c..37e2557b81 100644 --- a/cpp/common/test/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.expected +++ b/cpp/common/test/rules/pointertoavirtualbaseclasscasttoapointer/PointerToAVirtualBaseClassCastToAPointer.expected @@ -1,3 +1,4 @@ -| test.cpp:36:12:36:37 | reinterpret_cast... | A pointer to virtual base class C1 is not cast to a pointer of derived class C2 using a dynamic_cast. | -| test.cpp:42:12:42:38 | reinterpret_cast... | A pointer to virtual base class C1 is not cast to a pointer of derived class C2 using a dynamic_cast. | -| test.cpp:48:17:48:48 | reinterpret_cast... | A pointer to virtual base class C1 is not cast to a pointer of derived class C2 using a dynamic_cast. | +| test.cpp:41:12:41:37 | reinterpret_cast... | dynamic_cast not used for cast from pointer to base class C1 to derived class C2 which is $@. | test.cpp:11:12:11:28 | derivation | derived through virtual base class C1 | +| test.cpp:47:12:47:38 | reinterpret_cast... | dynamic_cast not used for cast from reference to base class C1 to derived class C2 which is $@. | test.cpp:11:12:11:28 | derivation | derived through virtual base class C1 | +| test.cpp:53:17:53:48 | reinterpret_cast... | dynamic_cast not used for cast from reference to base class C1 to derived class C2 which is $@. | test.cpp:11:12:11:28 | derivation | derived through virtual base class C1 | +| test.cpp:86:12:86:37 | reinterpret_cast... | dynamic_cast not used for cast from pointer to base class C0 to derived class C2 which is $@. | test.cpp:11:12:11:28 | derivation | derived through virtual base class C1 | diff --git a/cpp/common/test/rules/pointertoavirtualbaseclasscasttoapointer/test.cpp b/cpp/common/test/rules/pointertoavirtualbaseclasscasttoapointer/test.cpp index 419bc0764e..ab4666069e 100644 --- a/cpp/common/test/rules/pointertoavirtualbaseclasscasttoapointer/test.cpp +++ b/cpp/common/test/rules/pointertoavirtualbaseclasscasttoapointer/test.cpp @@ -1,4 +1,9 @@ -class C1 { +class C0 { +public: + virtual ~C0() {} +}; + +class C1 : public C0 { public: virtual ~C1() {} }; @@ -72,4 +77,11 @@ void test_static_cast_reference_non_compliant() { C1 *p1 = &l1; // C2 &l2 = static_cast(*p1); // NON_COMPLIANT - prohibited by the // compiler +} + +void test_skipped_base_class() { + C2 l1; + C0 *p1 = &l1; + C2 *l2 = dynamic_cast(p1); // COMPLIANT + C2 *p2 = reinterpret_cast(p1); // NON_COMPLIANT } \ No newline at end of file From 6bce7f8538fa50a72851392a869da2596dd24405 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Thu, 21 Aug 2025 23:22:16 +0100 Subject: [PATCH 20/23] Rule 8.2.8: Handle results in templates per rule --- .../rules/RULE-8-2-8/PointerToIntegralCast.ql | 58 ++++++++++++++++--- .../RULE-8-2-8/PointerToIntegralCast.expected | 22 ++++--- cpp/misra/test/rules/RULE-8-2-8/test.cpp | 23 ++++++-- 3 files changed, 82 insertions(+), 21 deletions(-) diff --git a/cpp/misra/src/rules/RULE-8-2-8/PointerToIntegralCast.ql b/cpp/misra/src/rules/RULE-8-2-8/PointerToIntegralCast.ql index 668b3103d6..8a39c6bea4 100644 --- a/cpp/misra/src/rules/RULE-8-2-8/PointerToIntegralCast.ql +++ b/cpp/misra/src/rules/RULE-8-2-8/PointerToIntegralCast.ql @@ -17,13 +17,55 @@ import cpp import codingstandards.cpp.misra import codingstandards.cpp.types.Type -from ReinterpretCast cast, PointerType sourceType, Type targetType +class InvalidReinterpretCast extends ReinterpretCast { + InvalidReinterpretCast() { + this.getExpr().getType().stripTopLevelSpecifiers() instanceof PointerType and + this.getType().getUnspecifiedType() instanceof IntegralType and + not stripSpecifiers(this.getType()).(UserType).hasGlobalOrStdName(["uintptr_t", "intptr_t"]) + } +} + +from + InvalidReinterpretCast cast, string message, Element optionalTemplateUseSite, + string optionalTemplateUseSiteString where not isExcluded(cast, Conversions2Package::pointerToIntegralCastQuery()) and - sourceType = cast.getExpr().getType().getUnspecifiedType() and - targetType = cast.getType() and - targetType.getUnspecifiedType() instanceof IntegralType and - not stripSpecifiers(targetType).(UserType).hasGlobalOrStdName(["uintptr_t", "intptr_t"]) -select cast, - "Cast of object pointer type to integral type '" + targetType + - "' instead of 'std::uintptr_t' or 'std::intptr_t'." + // Where a result occurs in both the instantiated and uninstantiated template, + // prefer the uninstantiated version. + not exists(InvalidReinterpretCast otherCast | + otherCast.getLocation() = cast.getLocation() and + not otherCast = cast and + otherCast.isFromUninstantiatedTemplate(_) + ) and + if not cast.isFromTemplateInstantiation(_) + then + // Either not in a template, or appears in the uninstantiated template and is + // therefore not argument dependent. + message = + "Cast of object pointer type to integral type '" + cast.getType() + + "' instead of 'std::uintptr_t' or 'std::intptr_t'." and + optionalTemplateUseSite = cast and + optionalTemplateUseSiteString = "" + else + // This is from a template instantiation and is dependent on a template argument, + // so attempt to identify the locations which instantiate the template. + exists(Element instantiation | + cast.isFromTemplateInstantiation(instantiation) and + message = "Cast of object pointer type to integral type inside $@." + | + optionalTemplateUseSite = instantiation.(ClassTemplateInstantiation).getATypeNameUse() and + optionalTemplateUseSiteString = + "instantiation of class " + instantiation.(ClassTemplateInstantiation).getName() + or + optionalTemplateUseSite = + instantiation.(FunctionTemplateInstantiation).getACallToThisFunction() and + optionalTemplateUseSiteString = + "call to instantiated template f of " + + instantiation.(FunctionTemplateInstantiation).getName() + or + optionalTemplateUseSite = instantiation.(VariableTemplateInstantiation).getAnAccess() and + optionalTemplateUseSiteString = + "reference to instantiated template variable " + + instantiation.(VariableTemplateInstantiation).getName() + ) +select cast, message, optionalTemplateUseSite, optionalTemplateUseSiteString diff --git a/cpp/misra/test/rules/RULE-8-2-8/PointerToIntegralCast.expected b/cpp/misra/test/rules/RULE-8-2-8/PointerToIntegralCast.expected index 7a541ff406..dd85d003c2 100644 --- a/cpp/misra/test/rules/RULE-8-2-8/PointerToIntegralCast.expected +++ b/cpp/misra/test/rules/RULE-8-2-8/PointerToIntegralCast.expected @@ -1,9 +1,13 @@ -| test.cpp:37:13:37:47 | reinterpret_cast... | Cast of object pointer type to integral type 'unsigned long' instead of 'std::uintptr_t' or 'std::intptr_t'. | -| test.cpp:42:13:42:46 | reinterpret_cast... | Cast of object pointer type to integral type 'unsigned int' instead of 'std::uintptr_t' or 'std::intptr_t'. | -| test.cpp:47:13:47:38 | reinterpret_cast... | Cast of object pointer type to integral type 'long' instead of 'std::uintptr_t' or 'std::intptr_t'. | -| test.cpp:52:13:52:45 | reinterpret_cast... | Cast of object pointer type to integral type 'size_t' instead of 'std::uintptr_t' or 'std::intptr_t'. | -| test.cpp:57:13:57:43 | reinterpret_cast... | Cast of object pointer type to integral type 'hashPtr_t' instead of 'std::uintptr_t' or 'std::intptr_t'. | -| test.cpp:62:13:62:42 | reinterpret_cast... | Cast of object pointer type to integral type 'MyIntPtr' instead of 'std::uintptr_t' or 'std::intptr_t'. | -| test.cpp:67:13:67:35 | reinterpret_cast... | Cast of object pointer type to integral type 'unsigned long' instead of 'std::uintptr_t' or 'std::intptr_t'. | -| test.cpp:72:13:72:47 | reinterpret_cast... | Cast of object pointer type to integral type 'uint64_t' instead of 'std::uintptr_t' or 'std::intptr_t'. | -| test.cpp:77:13:77:46 | reinterpret_cast... | Cast of object pointer type to integral type 'int64_t' instead of 'std::uintptr_t' or 'std::intptr_t'. | +| test.cpp:35:13:35:47 | reinterpret_cast... | Cast of object pointer type to integral type 'unsigned long' instead of 'std::uintptr_t' or 'std::intptr_t'. | test.cpp:35:13:35:47 | reinterpret_cast... | | +| test.cpp:40:13:40:46 | reinterpret_cast... | Cast of object pointer type to integral type 'unsigned int' instead of 'std::uintptr_t' or 'std::intptr_t'. | test.cpp:40:13:40:46 | reinterpret_cast... | | +| test.cpp:45:13:45:38 | reinterpret_cast... | Cast of object pointer type to integral type 'long' instead of 'std::uintptr_t' or 'std::intptr_t'. | test.cpp:45:13:45:38 | reinterpret_cast... | | +| test.cpp:50:13:50:45 | reinterpret_cast... | Cast of object pointer type to integral type 'size_t' instead of 'std::uintptr_t' or 'std::intptr_t'. | test.cpp:50:13:50:45 | reinterpret_cast... | | +| test.cpp:55:13:55:43 | reinterpret_cast... | Cast of object pointer type to integral type 'hashPtr_t' instead of 'std::uintptr_t' or 'std::intptr_t'. | test.cpp:55:13:55:43 | reinterpret_cast... | | +| test.cpp:60:13:60:42 | reinterpret_cast... | Cast of object pointer type to integral type 'MyIntPtr' instead of 'std::uintptr_t' or 'std::intptr_t'. | test.cpp:60:13:60:42 | reinterpret_cast... | | +| test.cpp:65:13:65:35 | reinterpret_cast... | Cast of object pointer type to integral type inside $@. | test.cpp:94:3:94:50 | call to test_non_compliant_template_cast | call to instantiated template f of test_non_compliant_template_cast | +| test.cpp:67:13:67:47 | reinterpret_cast... | Cast of object pointer type to integral type 'uint64_t' instead of 'std::uintptr_t' or 'std::intptr_t'. | test.cpp:67:13:67:47 | reinterpret_cast... | | +| test.cpp:72:13:72:47 | reinterpret_cast... | Cast of object pointer type to integral type 'uint64_t' instead of 'std::uintptr_t' or 'std::intptr_t'. | test.cpp:72:13:72:47 | reinterpret_cast... | | +| test.cpp:77:13:77:46 | reinterpret_cast... | Cast of object pointer type to integral type 'int64_t' instead of 'std::uintptr_t' or 'std::intptr_t'. | test.cpp:77:13:77:46 | reinterpret_cast... | | +| test.cpp:84:15:84:37 | reinterpret_cast... | Cast of object pointer type to integral type inside $@. | test.cpp:95:48:95:48 | definition of x | instantiation of class TestNonCompliantTemplateCast | +| test.cpp:86:15:86:49 | reinterpret_cast... | Cast of object pointer type to integral type 'uint64_t' instead of 'std::uintptr_t' or 'std::intptr_t'. | test.cpp:86:15:86:49 | reinterpret_cast... | | +| test.cpp:91:23:91:45 | reinterpret_cast... | Cast of object pointer type to integral type inside $@. | test.cpp:96:3:96:35 | variable_template | reference to instantiated template variable variable_template | diff --git a/cpp/misra/test/rules/RULE-8-2-8/test.cpp b/cpp/misra/test/rules/RULE-8-2-8/test.cpp index 75b3d7cb88..6b3dbd7685 100644 --- a/cpp/misra/test/rules/RULE-8-2-8/test.cpp +++ b/cpp/misra/test/rules/RULE-8-2-8/test.cpp @@ -3,9 +3,7 @@ struct S {}; class C {}; -void *g1 = nullptr; -S *g2 = nullptr; -const int *g3 = nullptr; +S *g1 = nullptr; using hashPtr_t = std::uintptr_t; using MyIntPtr = std::intptr_t; @@ -64,7 +62,9 @@ void test_non_compliant_using_alias_cast() { template void test_non_compliant_template_cast() { S *l1 = nullptr; - auto l2 = reinterpret_cast(l1); // NON_COMPLIANT + auto l2 = reinterpret_cast(l1); // NON_COMPLIANT + auto l3 = reinterpret_cast(l1); // COMPLIANT + auto l4 = reinterpret_cast(l1); // NON_COMPLIANT } void test_non_compliant_uint64_t_cast() { @@ -77,6 +77,21 @@ void test_non_compliant_int64_t_cast() { auto l2 = reinterpret_cast(l1); // NON_COMPLIANT } +template class TestNonCompliantTemplateCast { +public: + TestNonCompliantTemplateCast() { + S *l1 = nullptr; + auto l2 = reinterpret_cast(l1); // NON_COMPLIANT + auto l3 = reinterpret_cast(l1); // COMPLIANT + auto l4 = reinterpret_cast(l1); // NON_COMPLIANT + } +}; + +template +T variable_template = reinterpret_cast(g1); // NON_COMPLIANT + void test_instantiate_template() { test_non_compliant_template_cast(); + TestNonCompliantTemplateCast x{}; + variable_template; } \ No newline at end of file From 6e9ee92c521585412aa13f5fb4a346ef209d33b1 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Tue, 26 Aug 2025 21:45:29 +0100 Subject: [PATCH 21/23] Conversions2: Add rule tags --- .../VirtualBaseClassCastToDerived.ql | 1 + .../RULE-8-2-2/NoCStyleOrFunctionalCasts.ql | 2 ++ .../RULE-8-2-6/IntToPointerCastProhibited.ql | 2 ++ .../RULE-8-2-7/NoPointerToIntegralCast.ql | 2 ++ .../rules/RULE-8-2-8/PointerToIntegralCast.ql | 1 + .../NoStandaloneTypeCastExpression.ql | 1 + rule_packages/cpp/Conversions2.json | 21 +++++++++++++------ 7 files changed, 24 insertions(+), 6 deletions(-) diff --git a/cpp/misra/src/rules/RULE-8-2-1/VirtualBaseClassCastToDerived.ql b/cpp/misra/src/rules/RULE-8-2-1/VirtualBaseClassCastToDerived.ql index cd9cb150a8..6803483c95 100644 --- a/cpp/misra/src/rules/RULE-8-2-1/VirtualBaseClassCastToDerived.ql +++ b/cpp/misra/src/rules/RULE-8-2-1/VirtualBaseClassCastToDerived.ql @@ -8,6 +8,7 @@ * @problem.severity error * @tags external/misra/id/rule-8-2-1 * scope/single-translation-unit + * correctness * external/misra/enforcement/decidable * external/misra/obligation/required */ diff --git a/cpp/misra/src/rules/RULE-8-2-2/NoCStyleOrFunctionalCasts.ql b/cpp/misra/src/rules/RULE-8-2-2/NoCStyleOrFunctionalCasts.ql index 3515d0ec1b..441720c5e4 100644 --- a/cpp/misra/src/rules/RULE-8-2-2/NoCStyleOrFunctionalCasts.ql +++ b/cpp/misra/src/rules/RULE-8-2-2/NoCStyleOrFunctionalCasts.ql @@ -9,6 +9,8 @@ * @problem.severity error * @tags external/misra/id/rule-8-2-2 * scope/single-translation-unit + * readability + * correctness * external/misra/enforcement/decidable * external/misra/obligation/required */ diff --git a/cpp/misra/src/rules/RULE-8-2-6/IntToPointerCastProhibited.ql b/cpp/misra/src/rules/RULE-8-2-6/IntToPointerCastProhibited.ql index 0ab4c96f2d..0a97dc5359 100644 --- a/cpp/misra/src/rules/RULE-8-2-6/IntToPointerCastProhibited.ql +++ b/cpp/misra/src/rules/RULE-8-2-6/IntToPointerCastProhibited.ql @@ -8,6 +8,8 @@ * @problem.severity error * @tags external/misra/id/rule-8-2-6 * scope/single-translation-unit + * correctness + * maintainability * external/misra/enforcement/decidable * external/misra/obligation/required */ diff --git a/cpp/misra/src/rules/RULE-8-2-7/NoPointerToIntegralCast.ql b/cpp/misra/src/rules/RULE-8-2-7/NoPointerToIntegralCast.ql index 3b477cce66..dde10b0e40 100644 --- a/cpp/misra/src/rules/RULE-8-2-7/NoPointerToIntegralCast.ql +++ b/cpp/misra/src/rules/RULE-8-2-7/NoPointerToIntegralCast.ql @@ -8,6 +8,8 @@ * @problem.severity error * @tags external/misra/id/rule-8-2-7 * scope/single-translation-unit + * correctness + * maintainability * external/misra/enforcement/decidable * external/misra/obligation/advisory */ diff --git a/cpp/misra/src/rules/RULE-8-2-8/PointerToIntegralCast.ql b/cpp/misra/src/rules/RULE-8-2-8/PointerToIntegralCast.ql index 8a39c6bea4..357513305b 100644 --- a/cpp/misra/src/rules/RULE-8-2-8/PointerToIntegralCast.ql +++ b/cpp/misra/src/rules/RULE-8-2-8/PointerToIntegralCast.ql @@ -9,6 +9,7 @@ * @problem.severity error * @tags external/misra/id/rule-8-2-8 * scope/single-translation-unit + * correctness * external/misra/enforcement/decidable * external/misra/obligation/required */ diff --git a/cpp/misra/src/rules/RULE-9-2-1/NoStandaloneTypeCastExpression.ql b/cpp/misra/src/rules/RULE-9-2-1/NoStandaloneTypeCastExpression.ql index ce1cde0b59..e3be797b9d 100644 --- a/cpp/misra/src/rules/RULE-9-2-1/NoStandaloneTypeCastExpression.ql +++ b/cpp/misra/src/rules/RULE-9-2-1/NoStandaloneTypeCastExpression.ql @@ -9,6 +9,7 @@ * @problem.severity error * @tags external/misra/id/rule-9-2-1 * scope/single-translation-unit + * correctness * external/misra/enforcement/decidable * external/misra/obligation/required */ diff --git a/rule_packages/cpp/Conversions2.json b/rule_packages/cpp/Conversions2.json index 4258fd16bf..a4fc4f565d 100644 --- a/rule_packages/cpp/Conversions2.json +++ b/rule_packages/cpp/Conversions2.json @@ -14,7 +14,8 @@ "severity": "error", "short_name": "VirtualBaseClassCastToDerived", "tags": [ - "scope/single-translation-unit" + "scope/single-translation-unit", + "correctness" ], "shared_implementation_short_name": "PointerToAVirtualBaseClassCastToAPointer" } @@ -35,7 +36,9 @@ "severity": "error", "short_name": "NoCStyleOrFunctionalCasts", "tags": [ - "scope/single-translation-unit" + "scope/single-translation-unit", + "readability", + "correctness" ] } ], @@ -55,7 +58,9 @@ "severity": "error", "short_name": "IntToPointerCastProhibited", "tags": [ - "scope/single-translation-unit" + "scope/single-translation-unit", + "correctness", + "maintainability" ] } ], @@ -75,7 +80,9 @@ "severity": "error", "short_name": "NoPointerToIntegralCast", "tags": [ - "scope/single-translation-unit" + "scope/single-translation-unit", + "correctness", + "maintainability" ] } ], @@ -95,7 +102,8 @@ "severity": "error", "short_name": "PointerToIntegralCast", "tags": [ - "scope/single-translation-unit" + "scope/single-translation-unit", + "correctness" ] } ], @@ -115,7 +123,8 @@ "severity": "error", "short_name": "NoStandaloneTypeCastExpression", "tags": [ - "scope/single-translation-unit" + "scope/single-translation-unit", + "correctness" ], "implementation_scope": { "description": "Expression statements in if statement initializers are not currently supported." From 3477dcf1a2d9df92e1c767fdfd926df806052610 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Tue, 26 Aug 2025 21:49:10 +0100 Subject: [PATCH 22/23] Query formatting --- .../rules/M5-2-2/PointerToAVirtualBaseClassCastToAPointer.ql | 3 ++- .../src/rules/RULE-8-2-1/VirtualBaseClassCastToDerived.ql | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cpp/autosar/src/rules/M5-2-2/PointerToAVirtualBaseClassCastToAPointer.ql b/cpp/autosar/src/rules/M5-2-2/PointerToAVirtualBaseClassCastToAPointer.ql index 57eda7f146..ad49d9e151 100644 --- a/cpp/autosar/src/rules/M5-2-2/PointerToAVirtualBaseClassCastToAPointer.ql +++ b/cpp/autosar/src/rules/M5-2-2/PointerToAVirtualBaseClassCastToAPointer.ql @@ -17,7 +17,8 @@ import cpp import codingstandards.cpp.autosar import codingstandards.cpp.rules.pointertoavirtualbaseclasscasttoapointer.PointerToAVirtualBaseClassCastToAPointer -class PointerToAVirtualBaseClassCastToAPointerQuery extends PointerToAVirtualBaseClassCastToAPointerSharedQuery { +class PointerToAVirtualBaseClassCastToAPointerQuery extends PointerToAVirtualBaseClassCastToAPointerSharedQuery +{ PointerToAVirtualBaseClassCastToAPointerQuery() { this = PointersPackage::pointerToAVirtualBaseClassCastToAPointerQuery() } diff --git a/cpp/misra/src/rules/RULE-8-2-1/VirtualBaseClassCastToDerived.ql b/cpp/misra/src/rules/RULE-8-2-1/VirtualBaseClassCastToDerived.ql index 6803483c95..858aaec42c 100644 --- a/cpp/misra/src/rules/RULE-8-2-1/VirtualBaseClassCastToDerived.ql +++ b/cpp/misra/src/rules/RULE-8-2-1/VirtualBaseClassCastToDerived.ql @@ -17,7 +17,8 @@ import cpp import codingstandards.cpp.misra import codingstandards.cpp.rules.pointertoavirtualbaseclasscasttoapointer.PointerToAVirtualBaseClassCastToAPointer -class VirtualBaseClassCastToDerivedQuery extends PointerToAVirtualBaseClassCastToAPointerSharedQuery { +class VirtualBaseClassCastToDerivedQuery extends PointerToAVirtualBaseClassCastToAPointerSharedQuery +{ VirtualBaseClassCastToDerivedQuery() { this = Conversions2Package::virtualBaseClassCastToDerivedQuery() } From 0124f5e9a8e3a9e35f211c2d4a5428d87df49e7e Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Fri, 29 Aug 2025 09:55:25 -0700 Subject: [PATCH 23/23] Fix performance issue from join on start column. --- .../src/codingstandards/cpp/AlertReporting.qll | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/cpp/common/src/codingstandards/cpp/AlertReporting.qll b/cpp/common/src/codingstandards/cpp/AlertReporting.qll index 97c46c0629..a85ae4097b 100644 --- a/cpp/common/src/codingstandards/cpp/AlertReporting.qll +++ b/cpp/common/src/codingstandards/cpp/AlertReporting.qll @@ -17,6 +17,17 @@ module MacroUnwrapper { result.getAnAffectedElement() = re } + private MacroInvocation getASubsumedMacroInvocation(ResultElement re) { + result = getAMacroInvocation(re) and + // Only report cases where the element is not located at the macro expansion site + // This means we'll report results in macro arguments in the macro argument + // location, not within the macro itself. + // + // Do not join start column values. + pragma[only_bind_out](result.getLocation().getStartColumn()) = + pragma[only_bind_out](re.getLocation().getStartColumn()) + } + /** * Gets the primary macro invocation that generated the result element. * @@ -24,11 +35,7 @@ module MacroUnwrapper { */ MacroInvocation getPrimaryMacroInvocation(ResultElement re) { exists(MacroInvocation mi | - mi = getAMacroInvocation(re) and - // Only report cases where the element is not located at the macro expansion site - // This means we'll report results in macro arguments in the macro argument - // location, not within the macro itself - mi.getLocation().getStartColumn() = re.getLocation().getStartColumn() and + mi = getASubsumedMacroInvocation(re) and // No other more specific macro that expands to element not exists(MacroInvocation otherMi | otherMi = getAMacroInvocation(re) and otherMi.getParentInvocation() = mi