From 6dc64be10e40c6b9128f1a5c10744826f10edd2f Mon Sep 17 00:00:00 2001 From: Jeongsoo Lee Date: Thu, 12 Mar 2026 20:09:14 -0700 Subject: [PATCH 1/2] Add rule package of Banned2 --- rule_packages/cpp/Banned2.json | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 rule_packages/cpp/Banned2.json diff --git a/rule_packages/cpp/Banned2.json b/rule_packages/cpp/Banned2.json new file mode 100644 index 000000000..c5f4caaaa --- /dev/null +++ b/rule_packages/cpp/Banned2.json @@ -0,0 +1,25 @@ +{ + "MISRA-C++-2023": { + "RULE-10-2-2": { + "properties": { + "enforcement": "decidable", + "obligation": "advisory" + }, + "queries": [ + { + "description": "An unscoped enumeration should not be used outside of a class/struct scope; use 'enum class' instead to prevent name clashes and implicit conversions to integral types.", + "kind": "problem", + "name": "Unscoped enumerations should not be declared", + "precision": "very-high", + "severity": "error", + "short_name": "UnscopedEnumerationsShouldNotBeDeclared", + "tags": [ + "scope/single-translation-unit", + "correctness" + ] + } + ], + "title": "Unscoped enumerations should not be declared" + } + } +} \ No newline at end of file From 21af40d903bee7a533c7a4474a3c8598778f8dd4 Mon Sep 17 00:00:00 2001 From: Jeongsoo Lee Date: Thu, 12 Mar 2026 21:21:33 -0700 Subject: [PATCH 2/2] Add implementation of Banned2 --- .../cpp/exclusions/cpp/Banned2.qll | 26 +++ .../cpp/exclusions/cpp/RuleMetadata.qll | 3 + ...UnscopedEnumerationsShouldNotBeDeclared.ql | 31 ++++ ...edEnumerationsShouldNotBeDeclared.expected | 1 + ...copedEnumerationsShouldNotBeDeclared.qlref | 1 + cpp/misra/test/rules/RULE-10-2-2/test.cpp | 152 ++++++++++++++++++ 6 files changed, 214 insertions(+) create mode 100644 cpp/common/src/codingstandards/cpp/exclusions/cpp/Banned2.qll create mode 100644 cpp/misra/src/rules/RULE-10-2-2/UnscopedEnumerationsShouldNotBeDeclared.ql create mode 100644 cpp/misra/test/rules/RULE-10-2-2/UnscopedEnumerationsShouldNotBeDeclared.expected create mode 100644 cpp/misra/test/rules/RULE-10-2-2/UnscopedEnumerationsShouldNotBeDeclared.qlref create mode 100644 cpp/misra/test/rules/RULE-10-2-2/test.cpp diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Banned2.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Banned2.qll new file mode 100644 index 000000000..849571304 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Banned2.qll @@ -0,0 +1,26 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Banned2Query = TUnscopedEnumerationsShouldNotBeDeclaredQuery() + +predicate isBanned2QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `unscopedEnumerationsShouldNotBeDeclared` query + Banned2Package::unscopedEnumerationsShouldNotBeDeclaredQuery() and + queryId = + // `@id` for the `unscopedEnumerationsShouldNotBeDeclared` query + "cpp/misra/unscoped-enumerations-should-not-be-declared" and + ruleId = "RULE-10-2-2" and + category = "advisory" +} + +module Banned2Package { + Query unscopedEnumerationsShouldNotBeDeclaredQuery() { + //autogenerate `Query` type + result = + // `Query` type for `unscopedEnumerationsShouldNotBeDeclared` query + TQueryCPP(TBanned2PackageQuery(TUnscopedEnumerationsShouldNotBeDeclaredQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll index 88537493d..32475b5fd 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll @@ -4,6 +4,7 @@ import codingstandards.cpp.exclusions.RuleMetadata //** Import packages for this language **/ import Allocations import Banned1 +import Banned2 import BannedAPIs import BannedFunctions import BannedLibraries @@ -92,6 +93,7 @@ import VirtualFunctions newtype TCPPQuery = TAllocationsPackageQuery(AllocationsQuery q) or TBanned1PackageQuery(Banned1Query q) or + TBanned2PackageQuery(Banned2Query q) or TBannedAPIsPackageQuery(BannedAPIsQuery q) or TBannedFunctionsPackageQuery(BannedFunctionsQuery q) or TBannedLibrariesPackageQuery(BannedLibrariesQuery q) or @@ -180,6 +182,7 @@ newtype TCPPQuery = predicate isQueryMetadata(Query query, string queryId, string ruleId, string category) { isAllocationsQueryMetadata(query, queryId, ruleId, category) or isBanned1QueryMetadata(query, queryId, ruleId, category) or + isBanned2QueryMetadata(query, queryId, ruleId, category) or isBannedAPIsQueryMetadata(query, queryId, ruleId, category) or isBannedFunctionsQueryMetadata(query, queryId, ruleId, category) or isBannedLibrariesQueryMetadata(query, queryId, ruleId, category) or diff --git a/cpp/misra/src/rules/RULE-10-2-2/UnscopedEnumerationsShouldNotBeDeclared.ql b/cpp/misra/src/rules/RULE-10-2-2/UnscopedEnumerationsShouldNotBeDeclared.ql new file mode 100644 index 000000000..6815dacec --- /dev/null +++ b/cpp/misra/src/rules/RULE-10-2-2/UnscopedEnumerationsShouldNotBeDeclared.ql @@ -0,0 +1,31 @@ +/** + * @id cpp/misra/unscoped-enumerations-should-not-be-declared + * @name RULE-10-2-2: Unscoped enumerations should not be declared + * @description An unscoped enumeration should not be used outside of a class/struct scope; use + * 'enum class' instead to prevent name clashes and implicit conversions to integral + * types. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-10-2-2 + * scope/single-translation-unit + * correctness + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra + +class MemberUnscopedEnum extends Enum { + MemberUnscopedEnum() { + not this instanceof ScopedEnum and + exists(Class klass | klass = this.getEnclosingElement()) + } +} + +from Enum enum +where + not isExcluded(enum, Banned2Package::unscopedEnumerationsShouldNotBeDeclaredQuery()) and + not (enum instanceof ScopedEnum or enum instanceof MemberUnscopedEnum) +select enum, "This enumeration is an unscoped enum not enclosed in a class or a struct." diff --git a/cpp/misra/test/rules/RULE-10-2-2/UnscopedEnumerationsShouldNotBeDeclared.expected b/cpp/misra/test/rules/RULE-10-2-2/UnscopedEnumerationsShouldNotBeDeclared.expected new file mode 100644 index 000000000..2ec1a0ac6 --- /dev/null +++ b/cpp/misra/test/rules/RULE-10-2-2/UnscopedEnumerationsShouldNotBeDeclared.expected @@ -0,0 +1 @@ +No expected results have yet been specified \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-10-2-2/UnscopedEnumerationsShouldNotBeDeclared.qlref b/cpp/misra/test/rules/RULE-10-2-2/UnscopedEnumerationsShouldNotBeDeclared.qlref new file mode 100644 index 000000000..672d297ba --- /dev/null +++ b/cpp/misra/test/rules/RULE-10-2-2/UnscopedEnumerationsShouldNotBeDeclared.qlref @@ -0,0 +1 @@ +rules/RULE-10-2-2/UnscopedEnumerationsShouldNotBeDeclared.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-10-2-2/test.cpp b/cpp/misra/test/rules/RULE-10-2-2/test.cpp new file mode 100644 index 000000000..89cf1263d --- /dev/null +++ b/cpp/misra/test/rules/RULE-10-2-2/test.cpp @@ -0,0 +1,152 @@ +#include + +/* ========== 1. Global scope fixtures ========== */ +static int32_t G1 = 1; +static int32_t G2 = 2; + +/* ========== 2. Global scope enums ========== */ +enum E_Global1 : int32_t { V1, V2, V3 }; // NON_COMPLIANT: unscoped at global scope +enum { GlobalAnon1, GlobalAnon2 }; // NON_COMPLIANT: unscoped anonymous at global scope +enum class E_Global2 : int32_t { V1, V2 }; // COMPLIANT: scoped enum + +/* ========== 3. Nested namespaces ========== */ +namespace N1 { + static int32_t N1_V1 = 1; + + enum E1 : int32_t { G1 }; // NON_COMPLIANT: unscoped in namespace + hides ::G1 + enum { N1_Anon1, N1_Anon2 }; // NON_COMPLIANT: unscoped anonymous in namespace + enum class E2 : int32_t { G1 }; // COMPLIANT: scoped enum + + namespace N2 { + static int32_t N2_V1 = 1; + + enum E3 : int32_t { N1_V1 }; // NON_COMPLIANT: unscoped in namespace + hides N1::N1_V1 + enum E4 : int32_t { G2 }; // NON_COMPLIANT: unscoped in namespace + hides ::G2 + enum class E5 : int32_t { N1_V1, G2 }; // COMPLIANT: scoped enum + } +} + +/* ========== 4. Anonymous namespace ========== */ +namespace { + enum E_Anon1 : int32_t { V1, V2 }; // NON_COMPLIANT: unscoped in anonymous namespace + enum { AnonAnon1, AnonAnon2 }; // NON_COMPLIANT: unscoped anonymous in anonymous namespace + enum class E_Anon2 : int32_t { V1 }; // COMPLIANT: scoped enum +} + +/* ========== 5. Anonymous namespace inside named namespace ========== */ +namespace N3 { + static int32_t N3_V1 = 1; + + namespace { + enum E1 : int32_t { N3_V1 }; // NON_COMPLIANT: unscoped + hides N3::N3_V1 + enum class E2 : int32_t { N3_V1 }; // COMPLIANT: scoped enum + } +} + +/* ========== 6. Nested classes ========== */ +class C1 { + static int32_t C1_V1; + + enum E1 { G1 }; // COMPLIANT: unscoped in class (exception) + hides ::G1 + enum { C1_Anon1, C1_Anon2 }; // COMPLIANT: unscoped anonymous in class (exception) + enum class E2 { G1 }; // COMPLIANT: scoped enum + + class C2 { + enum E3 { C1_V1 }; // COMPLIANT: unscoped in nested class (exception) + enum E4 { G2 }; // COMPLIANT: unscoped in nested class (exception) + hides ::G2 + + struct S1 { + enum E5 { C1_V1 }; // COMPLIANT: unscoped in struct (exception) + enum class E6 { C1_V1 }; // COMPLIANT: scoped enum + }; + }; +}; + +/* ========== 7. Struct at global scope ========== */ +struct S_Global { + enum E1 { G1 }; // COMPLIANT: unscoped in struct (exception) + enum { S_Anon1, S_Anon2 }; // COMPLIANT: unscoped anonymous in struct (exception) + enum class E2 { G1 }; // COMPLIANT: scoped enum +}; + +/* ========== 8. Class inside namespace ========== */ +namespace N4 { + static int32_t N4_V1 = 1; + + enum E1 : int32_t { G1 }; // NON_COMPLIANT: unscoped in namespace + + class C1 { + enum E2 { N4_V1 }; // COMPLIANT: unscoped in class (exception) + hides N4::N4_V1 + enum E3 { G2 }; // COMPLIANT: unscoped in class (exception) + hides ::G2 + enum class E4 { N4_V1, G2 }; // COMPLIANT: scoped enum + }; +} + +/* ========== 9. Function body ========== */ +void f1() { + int F1_V1 = 1; + + enum E1 : int32_t { G1 }; // NON_COMPLIANT: unscoped in function + hides ::G1 + enum { F1_Anon1, F1_Anon2 }; // NON_COMPLIANT: unscoped anonymous in function + enum class E2 : int32_t { G1 }; // COMPLIANT: scoped enum +} + +/* ========== 10. Nested blocks ========== */ +void f2() { + int F2_V1 = 1; + + { + int F2_V2 = 2; + + enum E1 : int32_t { F2_V1 }; // NON_COMPLIANT: unscoped in block + hides outer F2_V1 + enum E2 : int32_t { G2 }; // NON_COMPLIANT: unscoped in block + hides ::G2 + + { + enum E3 : int32_t { F2_V2 }; // NON_COMPLIANT: unscoped in nested block + hides outer F2_V2 + enum class E4 : int32_t { F2_V1, F2_V2, G1 }; // COMPLIANT: scoped enum + } + } +} + +/* ========== 11. Local class in function ========== */ +void f3() { + int F3_V1 = 1; + + class LocalC1 { + enum E1 { F3_V1 }; // COMPLIANT: unscoped in local class (exception) + enum E2 { G1 }; // COMPLIANT: unscoped in local class (exception) + hides ::G1 + enum { Local_Anon1 }; // COMPLIANT: unscoped anonymous in local class (exception) + enum class E3 { F3_V1, G1 }; // COMPLIANT: scoped enum + }; +} + +/* ========== 12. Lambda body ========== */ +auto lambda1 = []() { + enum E1 : int32_t { G1 }; // NON_COMPLIANT: unscoped in lambda body + enum { Lambda_Anon1 }; // NON_COMPLIANT: unscoped anonymous in lambda body + enum class E2 : int32_t { G1 }; // COMPLIANT: scoped enum +}; + +/* ========== 13. Nested lambdas ========== */ +namespace N5 { + auto lambda2 = []() { + enum E1 : int32_t { G1 }; // NON_COMPLIANT: unscoped in lambda body + + auto nested_lambda = []() { + enum E2 : int32_t { G2 }; // NON_COMPLIANT: unscoped in nested lambda body + enum class E3 : int32_t { G1, G2 }; // COMPLIANT: scoped enum + }; + }; +} + +/* ========== 14. Lambda inside class ========== */ +class C3 { + static inline auto member_lambda = []() { + enum E1 : int32_t { G1 }; // NON_COMPLIANT: unscoped in lambda body (not class scope!) + enum class E2 : int32_t { G1 }; // COMPLIANT: scoped enum + }; +}; + +int main() { + return 0; +}