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/Banned3.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Banned3.qll new file mode 100644 index 000000000..44fc76965 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Banned3.qll @@ -0,0 +1,26 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Banned3Query = TUnscopedEnumWithoutFixedUnderlyingTypeUsedQuery() + +predicate isBanned3QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `unscopedEnumWithoutFixedUnderlyingTypeUsed` query + Banned3Package::unscopedEnumWithoutFixedUnderlyingTypeUsedQuery() and + queryId = + // `@id` for the `unscopedEnumWithoutFixedUnderlyingTypeUsed` query + "cpp/misra/unscoped-enum-without-fixed-underlying-type-used" and + ruleId = "RULE-10-2-3" and + category = "required" +} + +module Banned3Package { + Query unscopedEnumWithoutFixedUnderlyingTypeUsedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `unscopedEnumWithoutFixedUnderlyingTypeUsed` query + TQueryCPP(TBanned3PackageQuery(TUnscopedEnumWithoutFixedUnderlyingTypeUsedQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll index 88537493d..64b493588 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll @@ -4,6 +4,8 @@ import codingstandards.cpp.exclusions.RuleMetadata //** Import packages for this language **/ import Allocations import Banned1 +import Banned2 +import Banned3 import BannedAPIs import BannedFunctions import BannedLibraries @@ -92,6 +94,8 @@ import VirtualFunctions newtype TCPPQuery = TAllocationsPackageQuery(AllocationsQuery q) or TBanned1PackageQuery(Banned1Query q) or + TBanned2PackageQuery(Banned2Query q) or + TBanned3PackageQuery(Banned3Query q) or TBannedAPIsPackageQuery(BannedAPIsQuery q) or TBannedFunctionsPackageQuery(BannedFunctionsQuery q) or TBannedLibrariesPackageQuery(BannedLibrariesQuery q) or @@ -180,6 +184,8 @@ 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 + isBanned3QueryMetadata(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/src/rules/RULE-10-2-3/UnscopedEnumWithoutFixedUnderlyingTypeUsed.ql b/cpp/misra/src/rules/RULE-10-2-3/UnscopedEnumWithoutFixedUnderlyingTypeUsed.ql new file mode 100644 index 000000000..6852473e0 --- /dev/null +++ b/cpp/misra/src/rules/RULE-10-2-3/UnscopedEnumWithoutFixedUnderlyingTypeUsed.ql @@ -0,0 +1,125 @@ +/** + * @id cpp/misra/unscoped-enum-without-fixed-underlying-type-used + * @name RULE-10-2-3: The numeric value of an unscoped enumeration with no fixed underlying type shall not be used + * @description Treating unscoped enumeration without a fixed underlying type as an integral type is + * not portable and might cause unintended behaviors. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-10-2-3 + * scope/single-translation-unit + * correctness + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra + +private predicate isUnscopedEnum(Enum enum) { not enum instanceof ScopedEnum } + +private predicate withoutFixedUnderlyingType(Enum enum) { not enum.hasExplicitUnderlyingType() } + +private predicate isUnscopedEnumWithoutFixedUnderlyingType(Enum enum) { + isUnscopedEnum(enum) and withoutFixedUnderlyingType(enum) +} + +class ArithmeticBitwiseLogicalBinaryOperation extends BinaryOperation { + ArithmeticBitwiseLogicalBinaryOperation() { + this instanceof BinaryArithmeticOperation or + this instanceof BinaryBitwiseOperation or + this instanceof BinaryLogicalOperation + } +} + +/** + * ``` C++ + * static_cast(u) == static_cast(s); // COMPLIANT: comparing ints + * ``` + * ^^^ To solve this, we use `getExplicitlyConverted`: + * `binOp.getLeftOperand().getExplicitlyConverted()` gives `int`. + */ +predicate arithmeticBitwiseLogicalOperationUsesUnscopedUnfixedEnum( + ArithmeticBitwiseLogicalBinaryOperation binOp +) { + /* + * We want to strip explicit casts and not implicit ones. Without the + * stripping of explicit casts, our query would raise a false alarm on + * cases such as below. + * + * ``` C++ + * static_cast(u) + 1 // COMPLIANT + * ``` + */ + + isUnscopedEnumWithoutFixedUnderlyingType(binOp + .getLeftOperand() + .getExplicitlyConverted() + .getUnderlyingType()) or + isUnscopedEnumWithoutFixedUnderlyingType(binOp + .getRightOperand() + .getExplicitlyConverted() + .getUnderlyingType()) +} + +class RelationalEqualityBinaryOperation extends BinaryOperation { + RelationalEqualityBinaryOperation() { + this instanceof RelationalOperation or + this instanceof EqualityOperation + } +} + +predicate relationalEqualityOperationUsesUnscopedUnfixedEnum(RelationalEqualityBinaryOperation binOp) { + exists(Type leftOperandType, Type rightOperandType | + /* + * We want to strip explicit casts and not implicit ones. Without the + * stripping of explicit casts, our query would raise a false alarm on + * cases such as below. + * + * ``` C++ + * static_cast(u) == 1 // COMPLIANT + * ``` + */ + + leftOperandType = binOp.getLeftOperand().getExplicitlyConverted().getUnderlyingType() and + rightOperandType = binOp.getRightOperand().getExplicitlyConverted().getUnderlyingType() and + ( + isUnscopedEnumWithoutFixedUnderlyingType(leftOperandType) + or + isUnscopedEnumWithoutFixedUnderlyingType(rightOperandType) + ) and + leftOperandType != rightOperandType + ) +} + +class ArithmeticBitwiseCompoundAssignment extends AssignOperation { + ArithmeticBitwiseCompoundAssignment() { + this instanceof AssignArithmeticOperation or + this instanceof AssignBitwiseOperation + } +} + +predicate compoundAssignmentUsesUnscopedUnfixedEnum( + ArithmeticBitwiseCompoundAssignment compoundAssignment +) { + isUnscopedEnumWithoutFixedUnderlyingType(compoundAssignment.getAnOperand().getUnderlyingType()) +} + +predicate assignmentSourceIsUnscopedUnfixedEnum(AssignExpr assign) { none() } + +predicate staticCastSourceIsUnscopedUnfixedEnumVariant(StaticCast cast) { none() } + +predicate switchCaseIsAnUnfixedEnumVariant(SwitchCase switchCase) { none() } + +from Element x +where + not isExcluded(x, Banned3Package::unscopedEnumWithoutFixedUnderlyingTypeUsedQuery()) and + ( + arithmeticBitwiseLogicalOperationUsesUnscopedUnfixedEnum(x) or + relationalEqualityOperationUsesUnscopedUnfixedEnum(x) or + compoundAssignmentUsesUnscopedUnfixedEnum(x) or + assignmentSourceIsUnscopedUnfixedEnum(x) or + staticCastSourceIsUnscopedUnfixedEnumVariant(x) or + switchCaseIsAnUnfixedEnumVariant(x) + ) +select x, "TODO" 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; +} diff --git a/cpp/misra/test/rules/RULE-10-2-3/UnscopedEnumWithoutFixedUnderlyingTypeUsed.expected b/cpp/misra/test/rules/RULE-10-2-3/UnscopedEnumWithoutFixedUnderlyingTypeUsed.expected new file mode 100644 index 000000000..2ec1a0ac6 --- /dev/null +++ b/cpp/misra/test/rules/RULE-10-2-3/UnscopedEnumWithoutFixedUnderlyingTypeUsed.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-3/UnscopedEnumWithoutFixedUnderlyingTypeUsed.qlref b/cpp/misra/test/rules/RULE-10-2-3/UnscopedEnumWithoutFixedUnderlyingTypeUsed.qlref new file mode 100644 index 000000000..5d4ae9985 --- /dev/null +++ b/cpp/misra/test/rules/RULE-10-2-3/UnscopedEnumWithoutFixedUnderlyingTypeUsed.qlref @@ -0,0 +1 @@ +rules/RULE-10-2-3/UnscopedEnumWithoutFixedUnderlyingTypeUsed.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-10-2-3/test.cpp b/cpp/misra/test/rules/RULE-10-2-3/test.cpp new file mode 100644 index 000000000..8d8c102e3 --- /dev/null +++ b/cpp/misra/test/rules/RULE-10-2-3/test.cpp @@ -0,0 +1,414 @@ +#include + +/* ========== 1. Fixtures ========== */ + +// Unscoped, no fixed type +enum Unfixed { U0, U1, U2 }; +enum Unfixed2 { V0 = 10, V1, V2 }; + +// Unscoped, with fixed type +enum Fixed : std::int32_t { F0 = 20, F1, F2 }; +enum Fixed2 : std::int32_t { G0 = 30, G1, G2 }; + +// Scoped enum (defaults to int) +enum class Scoped { S0, S1, S2 }; + +// Scoped, with fixed type +enum class ScopedFixed : std::int32_t { SF0, SF1, SF2 }; + +// Unscoped, no fixed type, large values +enum UnfixedLarge { UL0 = 0x7FFFFFFF, UL1 }; + +/* ========== 2. Arithmetic operators ========== */ + +void arithmetic() { + Unfixed u = U0; + Fixed f = F0; + + u + 1; // NON_COMPLIANT: arithmetic on unfixed unscoped enum + u - 1; // NON_COMPLIANT: arithmetic on unfixed unscoped enum + u * 2; // NON_COMPLIANT: arithmetic on unfixed unscoped enum + u / 2; // NON_COMPLIANT: arithmetic on unfixed unscoped enum + u % 2; // NON_COMPLIANT: arithmetic on unfixed unscoped enum + + f + 1; // COMPLIANT: fixed underlying type + f - 1; // COMPLIANT: fixed underlying type +} + +/* ========== 3. Bitwise operators ========== */ + +void bitwise() { + Unfixed u = U0; + Fixed f = F0; + + u | U1; // NON_COMPLIANT: bitwise on unfixed unscoped enum + u & U1; // NON_COMPLIANT: bitwise on unfixed unscoped enum + u ^ U1; // NON_COMPLIANT: bitwise on unfixed unscoped enum + + f | F1; // COMPLIANT: fixed underlying type + f & F1; // COMPLIANT: fixed underlying type +} + +/* ========== 4. Shift operators ========== */ + +void shift() { + Unfixed u = U0; + Fixed f = F0; + + u << 1; // NON_COMPLIANT: shift on unfixed unscoped enum + u >> 1; // NON_COMPLIANT: shift on unfixed unscoped enum + + f << 1; // COMPLIANT: fixed underlying type + f >> 1; // COMPLIANT: fixed underlying type +} + +/* ========== 5. Logical operators ========== */ + +void logical() { + Unfixed u = U0; + Fixed f = F0; + + u &&U1; // NON_COMPLIANT: logical on unfixed unscoped enum + u || U1; // NON_COMPLIANT: logical on unfixed unscoped enum + + f &&F1; // COMPLIANT: fixed underlying type +} + +/* ========== 6. Compound assignment operators ========== */ + +void compound_assignment() { + Unfixed u = U0; + Fixed f = F0; + int i = 0; + + i += u; // NON_COMPLIANT: compound assignment with unfixed unscoped enum + i -= u; // NON_COMPLIANT: compound assignment with unfixed unscoped enum + i *= u; // NON_COMPLIANT: compound assignment with unfixed unscoped enum + i /= u; // NON_COMPLIANT: compound assignment with unfixed unscoped enum + i |= u; // NON_COMPLIANT: compound assignment with unfixed unscoped enum + i &= u; // NON_COMPLIANT: compound assignment with unfixed unscoped enum + i ^= u; // NON_COMPLIANT: compound assignment with unfixed unscoped enum + i <<= u; // NON_COMPLIANT: compound assignment with unfixed unscoped enum + i >>= u; // NON_COMPLIANT: compound assignment with unfixed unscoped enum + + i += f; // COMPLIANT: fixed underlying type + i |= f; // COMPLIANT: fixed underlying type +} + +/* ========== 7. Relational and equality operators ========== */ + +void relational() { + Unfixed u = U0; + Unfixed u2 = U1; + Fixed f = F0; + Fixed f2 = F1; + + u == u2; // COMPLIANT: same enum type + u != u2; // COMPLIANT: same enum type + u < u2; // COMPLIANT: same enum type + u > u2; // COMPLIANT: same enum type + u <= u2; // COMPLIANT: same enum type + u >= u2; // COMPLIANT: same enum type + + u == 0; // NON_COMPLIANT: comparing unfixed enum to int + u < 5; // NON_COMPLIANT: comparing unfixed enum to int + u != 1; // NON_COMPLIANT: comparing unfixed enum to int + + f == 0; // COMPLIANT: fixed underlying type + f < 5; // COMPLIANT: fixed underlying type +} + +/* ========== 8. Assignment ========== */ + +void assignment() { + Unfixed u = U0; + Fixed f = F0; + + std::int32_t i32; + std::int8_t i8; + std::int64_t i64; + + i32 = u; // COMPLIANT: std::int32_t large enough + i64 = u; // COMPLIANT: std::int64_t large enough + i8 = u; // NON_COMPLIANT: std::int8_t might not hold all values + + i32 = f; // COMPLIANT: fixed underlying type + i8 = f; // COMPLIANT: fixed underlying type + + Unfixed u2; + u2 = u; // COMPLIANT: same enum type +} + +/* ========== 9. Switch statements ========== */ + +void switch_statements() { + Unfixed u = U0; + Fixed f = F0; + + switch (u) { + case U0: + break; + case U1: + break; + case U2: + break; // COMPLIANT: all cases are enumerators of same type + } + + switch (u) { + case U0: + break; + case 5: + break; // NON_COMPLIANT: mixed enumerator and integer literal + } + + switch (u) { + case U0: + break; + case F0: + break; // NON_COMPLIANT: mixed enumerators from different enums + } + + switch (f) { + case F0: + break; + case 5: + break; // COMPLIANT: fixed underlying type + } +} + +/* ========== 10. static_cast FROM unfixed enum ========== */ + +void static_cast_from_unfixed() { + Unfixed u = U0; + + // Target is same enumeration type + static_cast(u); // COMPLIANT: same enum type + + // Target is integer type large enough + static_cast(u); // COMPLIANT: int guaranteed large enough + static_cast(u); // COMPLIANT: long >= int + static_cast(u); // COMPLIANT: std::int64_t >= int + static_cast(u); // COMPLIANT: unsigned int large enough + + // Target is integer type that might not be large enough + static_cast( + u); // NON_COMPLIANT: std::int8_t might not hold all values + static_cast( + u); // NON_COMPLIANT: std::uint8_t might not hold all values + static_cast( + u); // NON_COMPLIANT: std::int16_t might not hold all values + static_cast(u); // NON_COMPLIANT: char might not hold all values + + // Target is different enumeration type + static_cast(u); // NON_COMPLIANT: different enum type + static_cast(u); // NON_COMPLIANT: different enum type + static_cast(u); // NON_COMPLIANT: different enum type +} + +/* ========== 11. static_cast FROM fixed enum ========== */ + +void static_cast_from_fixed() { + Fixed f = F0; + + // All compliant because source has fixed underlying type + static_cast(f); // COMPLIANT: fixed underlying type + static_cast(f); // COMPLIANT: fixed underlying type + static_cast(f); // COMPLIANT: fixed underlying type + static_cast(f); // NON_COMPLIANT: target is unfixed enum (TO rule) +} + +/* ========== 12. static_cast TO unfixed enum ========== */ + +void static_cast_to_unfixed() { + int i = 0; + std::int8_t i8 = 0; + std::int64_t i64 = 0; + + // Any static_cast from non-enum to unfixed enum is non-compliant + static_cast(0); // NON_COMPLIANT: unfixed enum as target + static_cast(i); // NON_COMPLIANT: unfixed enum as target + static_cast(i8); // NON_COMPLIANT: unfixed enum as target + static_cast(i64); // NON_COMPLIANT: unfixed enum as target +} + +/* ========== 13. static_cast TO fixed enum ========== */ + +void static_cast_to_fixed() { + int i = 0; + Unfixed u = U0; + + // Fixed enum as target is compliant (for the TO rule) + static_cast(0); // COMPLIANT: fixed enum as target + static_cast(i); // COMPLIANT: fixed enum as target + static_cast(F0); // COMPLIANT: fixed enum as target + static_cast(u); // NON_COMPLIANT: source is unfixed enum, target is + // different type (FROM rule) +} + +/* ========== 14. static_cast TO scoped enum ========== */ + +void static_cast_to_scoped() { + int i = 0; + Unfixed u = U0; + + // Scoped enums have fixed underlying type (defaults to int) + static_cast(0); // COMPLIANT: scoped enum has fixed type + static_cast(i); // COMPLIANT: scoped enum has fixed type + static_cast(u); // NON_COMPLIANT: source is unfixed enum, target is + // different type (FROM rule) +} + +/* ========== 15. static_cast FROM scoped enum ========== */ + +void static_cast_from_scoped() { + Scoped s = Scoped::S0; + + // Scoped enums have fixed underlying type + static_cast(s); // COMPLIANT: scoped enum has fixed type + static_cast(s); // COMPLIANT: scoped enum has fixed type + static_cast(s); // COMPLIANT: same type, fixed + static_cast(s); // NON_COMPLIANT: target is unfixed enum (TO rule) +} + +/* ========== 16. Cross-enum relational operators ========== */ + +void cross_enum_relational() { + Unfixed u = U0; + Fixed f = F0; + Scoped s = Scoped::S0; + + // Unfixed vs Fixed (both unscoped, different types) + u == f; // NON_COMPLIANT: different enum types, unfixed operand + u < f; // NON_COMPLIANT: different enum types, unfixed operand + u != f; // NON_COMPLIANT: different enum types, unfixed operand + + // Fixed vs Fixed (same type) + f == F1; // COMPLIANT: same enum type, fixed + + // Unfixed vs Scoped - with cast + u == static_cast(s); // NON_COMPLIANT: unfixed enum compared to int + + static_cast(u) == static_cast(s); // COMPLIANT: comparing ints + + // Fixed vs Scoped - with cast + f == static_cast(s); // COMPLIANT: fixed enum compared to int +} + +/* ========== 17. Cross-enum arithmetic operators ========== */ + +void cross_enum_arithmetic() { + Unfixed u = U0; + Fixed f = F0; + Scoped s = Scoped::S0; + + u + f; // NON_COMPLIANT: arithmetic with unfixed operand + f + u; // NON_COMPLIANT: arithmetic with unfixed operand + u - f; // NON_COMPLIANT: arithmetic with unfixed operand + + f + static_cast(s); // COMPLIANT: fixed enum + int + + u + U1; // NON_COMPLIANT: arithmetic on unfixed +} + +/* ========== 18. Cross-enum bitwise operators ========== */ + +void cross_enum_bitwise() { + Unfixed u = U0; + Fixed f = F0; + + u | f; // NON_COMPLIANT: bitwise with unfixed operand + u & f; // NON_COMPLIANT: bitwise with unfixed operand + u ^ f; // NON_COMPLIANT: bitwise with unfixed operand + f | u; // NON_COMPLIANT: bitwise with unfixed operand + + f | F1; // COMPLIANT: both fixed, same type +} + +/* ========== 19. Two different unfixed enums ========== */ + +void two_unfixed() { + Unfixed u = U0; + Unfixed2 v = V0; + + u == v; // NON_COMPLIANT: different enum types, both unfixed + u < v; // NON_COMPLIANT: different enum types, both unfixed + u + v; // NON_COMPLIANT: arithmetic on unfixed + u | v; // NON_COMPLIANT: bitwise on unfixed + + switch (u) { + case U0: + break; + case V0: + break; // NON_COMPLIANT: case from different unfixed enum + } +} + +/* ========== 20. Two different fixed enums ========== */ + +void two_fixed() { + Fixed f = F0; + Fixed2 g = G0; + + f == g; // COMPLIANT: both have fixed underlying type + f < g; // COMPLIANT: both have fixed underlying type + f + g; // COMPLIANT: both have fixed underlying type + f | g; // COMPLIANT: both have fixed underlying type + + switch (f) { + case F0: + break; + case G0: + break; // COMPLIANT: both have fixed underlying type + } +} + +/* ========== 21. Mixed fixed and unfixed ========== */ + +void mixed_fixed_unfixed() { + Unfixed u = U0; + Fixed f = F0; + Unfixed2 v = V0; + Fixed2 g = G0; + + // The presence of ANY unfixed operand makes it non-compliant + u + f; // NON_COMPLIANT: u is unfixed + f + u; // NON_COMPLIANT: u is unfixed + u + g; // NON_COMPLIANT: u is unfixed + v + f; // NON_COMPLIANT: v is unfixed + v + g; // NON_COMPLIANT: v is unfixed + + u == f; // NON_COMPLIANT: u is unfixed, different types + f == u; // NON_COMPLIANT: u is unfixed, different types + + f + g; // COMPLIANT: both fixed + f == g; // COMPLIANT: both fixed +} + +/* ========== 22. Scoped enum operations ========== */ + +void scoped_enum_ops() { + Scoped s = Scoped::S0; + + // These don't compile without explicit cast, so they're safe by design + // s + 1; // Would not compile + // s == 0; // Would not compile + + static_cast(s); // COMPLIANT: scoped enum + static_cast(1); // COMPLIANT: scoped enum +} + +/* ========== 23. Member unfixed enum ========== */ + +class C1 { + enum MemberUnfixed { M0, M1, M2 }; + + void member_ops() { + MemberUnfixed m = M0; + + m + 1; // NON_COMPLIANT: arithmetic on unfixed enum (even if member) + m == 0; // NON_COMPLIANT: comparing unfixed enum to int (even if member) + m == M1; // COMPLIANT: same enum type + } +}; + +int main() { return 0; } \ No newline at end of file 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 diff --git a/rule_packages/cpp/Banned3.json b/rule_packages/cpp/Banned3.json new file mode 100644 index 000000000..5e281da93 --- /dev/null +++ b/rule_packages/cpp/Banned3.json @@ -0,0 +1,25 @@ +{ + "MISRA-C++-2023": { + "RULE-10-2-3": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Treating unscoped enumeration without a fixed underlying type as an integral type is not portable and might cause unintended behaviors.", + "kind": "problem", + "name": "The numeric value of an unscoped enumeration with no fixed underlying type shall not be used", + "precision": "very-high", + "severity": "error", + "short_name": "UnscopedEnumWithoutFixedUnderlyingTypeUsed", + "tags": [ + "scope/single-translation-unit", + "correctness" + ] + } + ], + "title": "The numeric value of an unscoped enumeration with no fixed underlying type shall not be used" + } + } +} diff --git a/rules.csv b/rules.csv index 6f2f20478..1a3910ab9 100644 --- a/rules.csv +++ b/rules.csv @@ -918,9 +918,9 @@ cpp,MISRA-C++-2023,RULE-10-0-1,Yes,Advisory,Decidable,Single Translation Unit,A cpp,MISRA-C++-2023,RULE-10-1-1,Yes,Advisory,Decidable,Single Translation Unit,The target type of a pointer or lvalue reference parameter should be const-qualified appropriately,RULE-8-13,Declarations2,Hard, cpp,MISRA-C++-2023,RULE-10-1-2,Yes,Required,Decidable,Single Translation Unit,The volatile qualifier shall be used appropriately,,Declarations2,Easy, cpp,MISRA-C++-2023,RULE-10-2-1,Yes,Required,Decidable,Single Translation Unit,An enumeration shall be defined with an explicit underlying type,A7-2-2,ImportMisra23,Import, -cpp,MISRA-C++-2023,RULE-10-2-2,Yes,Advisory,Decidable,Single Translation Unit,Unscoped enumerations should not be declared,A7-2-3,Banned,Easy, -cpp,MISRA-C++-2023,RULE-10-2-3,Yes,Required,Decidable,Single Translation Unit,The numeric value of an unscoped enumeration with no fixed underlying type shall not be used,A4-5-1,Banned,Easy, -cpp,MISRA-C++-2023,RULE-10-3-1,Yes,Advisory,Decidable,Single Translation Unit,There should be no unnamed namespaces in header files,"DCL59-CPP, M7-3-3",Banned,Easy, +cpp,MISRA-C++-2023,RULE-10-2-2,Yes,Advisory,Decidable,Single Translation Unit,Unscoped enumerations should not be declared,A7-2-3,Banned2,Easy, +cpp,MISRA-C++-2023,RULE-10-2-3,Yes,Required,Decidable,Single Translation Unit,The numeric value of an unscoped enumeration with no fixed underlying type shall not be used,A4-5-1,Banned3,Easy, +cpp,MISRA-C++-2023,RULE-10-3-1,Yes,Advisory,Decidable,Single Translation Unit,There should be no unnamed namespaces in header files,"DCL59-CPP, M7-3-3",Banned4,Easy, cpp,MISRA-C++-2023,RULE-10-4-1,Yes,Required,Decidable,Single Translation Unit,The asm declaration shall not be used,A7-4-1,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-11-3-1,Yes,Advisory,Decidable,Single Translation Unit,Variables of array type should not be declared,,Declarations2,Easy, cpp,MISRA-C++-2023,RULE-11-3-2,Yes,Advisory,Decidable,Single Translation Unit,The declaration of an object should contain no more than two levels of pointer indirection,A5-0-3,ImportMisra23,Import,