diff --git a/cpp/common/src/codingstandards/cpp/Call.qll b/cpp/common/src/codingstandards/cpp/Call.qll new file mode 100644 index 0000000000..706d66e01c --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/Call.qll @@ -0,0 +1,18 @@ +import cpp +import codingstandards.cpp.types.Type + +/** + * Gets the `FunctionType` of an expression call. + */ +FunctionType getExprCallFunctionType(ExprCall call) { + // A standard expression call + // Returns a FunctionPointerIshType + result = call.(ExprCall).getExpr().getType() + or + // An expression call using the pointer to member operator (.* or ->*) + // This special handling is required because we don't have a CodeQL class representing the call + // to a pointer to member function, but the right hand side is extracted as the -1 child of the + // call. + // Returns a RoutineType + result = call.(ExprCall).getChild(-1).getType().(PointerToMemberType).getBaseType() +} diff --git a/cpp/common/src/codingstandards/cpp/ConstantExpressions.qll b/cpp/common/src/codingstandards/cpp/ConstantExpressions.qll new file mode 100644 index 0000000000..c75df942db --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/ConstantExpressions.qll @@ -0,0 +1,97 @@ +import cpp + +final private class FinalExpr = Expr; + +/** + * An integer constant expression as defined by the C++17 standard. + */ +class IntegerConstantExpr extends FinalExpr { + IntegerConstantExpr() { + // An integer constant expression is a constant expression that has an + // integral type. + this.isConstant() and + exists(Type unspecifiedType | unspecifiedType = this.getUnspecifiedType() | + unspecifiedType instanceof IntegralType + or + // Unscoped enum type + unspecifiedType instanceof Enum and + not unspecifiedType instanceof ScopedEnum + ) + } + + /** + * Gets the value of this integer constant expression. + * + * This is only defined for expressions that are constant expressions, and + * that have a value that can be represented as a `BigInt`. + */ + QlBuiltins::BigInt getConstantValue() { + if exists(getPreConversionConstantValue()) + then result = getPreConversionConstantValue() + else result = this.getValue().toBigInt() + } + + /** + * Gets the pre-conversion constant value of this integer constant expression, if it is different + * from `getValue()`. + * + * This is required because `Expr.getValue()` returns the _converted constant expression value_ + * for non-literal constant expressions, which is the expression value after conversions have been + * applied, but for validating conversions we need the _pre-conversion constant expression value_. + */ + private QlBuiltins::BigInt getPreConversionConstantValue() { + // Access of a variable that has a constant initializer + result = + this.(VariableAccess) + .getTarget() + .getInitializer() + .getExpr() + .getFullyConverted() + .getValue() + .toBigInt() + or + result = this.(EnumConstantAccess).getTarget().getValue().toBigInt() + or + result = -this.(UnaryMinusExpr).getOperand().getFullyConverted().getValue().toBigInt() + or + result = this.(UnaryPlusExpr).getOperand().getFullyConverted().getValue().toBigInt() + or + result = this.(NotExpr).getOperand().getFullyConverted().getValue().toBigInt().bitNot() + or + exists(BinaryOperation op, QlBuiltins::BigInt left, QlBuiltins::BigInt right | + op = this and + left = op.getLeftOperand().getFullyConverted().getValue().toBigInt() and + right = op.getRightOperand().getFullyConverted().getValue().toBigInt() + | + op instanceof AddExpr and + result = left + right + or + op instanceof SubExpr and + result = left - right + or + op instanceof MulExpr and + result = left * right + or + op instanceof DivExpr and + result = left / right + or + op instanceof RemExpr and + result = left % right + or + op instanceof BitwiseAndExpr and + result = left.bitAnd(right) + or + op instanceof BitwiseOrExpr and + result = left.bitOr(right) + or + op instanceof BitwiseXorExpr and + result = left.bitXor(right) + or + op instanceof RShiftExpr and + result = left.bitShiftRightSigned(right.toInt()) + or + op instanceof LShiftExpr and + result = left.bitShiftLeft(right.toInt()) + ) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Conversions.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Conversions.qll new file mode 100644 index 0000000000..f151565d11 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Conversions.qll @@ -0,0 +1,214 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype ConversionsQuery = + TNoConversionFromBoolQuery() or + TNoImplicitBoolConversionQuery() or + TNoCharacterNumericalValueQuery() or + TNoSignednessChangeFromPromotionQuery() or + TNumericAssignmentTypeMismatchQuery() or + TFunctionPointerConversionContextQuery() or + TVirtualBaseClassCastToDerivedQuery() or + TNoCStyleOrFunctionalCastsQuery() or + TIntToPointerCastProhibitedQuery() or + TNoPointerToIntegralCastQuery() or + TPointerToIntegralCastQuery() or + TNoStandaloneTypeCastExpressionQuery() + +predicate isConversionsQueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `noConversionFromBool` query + ConversionsPackage::noConversionFromBoolQuery() and + queryId = + // `@id` for the `noConversionFromBool` query + "cpp/misra/no-conversion-from-bool" and + ruleId = "RULE-7-0-1" and + category = "required" + or + query = + // `Query` instance for the `noImplicitBoolConversion` query + ConversionsPackage::noImplicitBoolConversionQuery() and + queryId = + // `@id` for the `noImplicitBoolConversion` query + "cpp/misra/no-implicit-bool-conversion" and + ruleId = "RULE-7-0-2" and + category = "required" + or + query = + // `Query` instance for the `noCharacterNumericalValue` query + ConversionsPackage::noCharacterNumericalValueQuery() and + queryId = + // `@id` for the `noCharacterNumericalValue` query + "cpp/misra/no-character-numerical-value" and + ruleId = "RULE-7-0-3" and + category = "required" + or + query = + // `Query` instance for the `noSignednessChangeFromPromotion` query + ConversionsPackage::noSignednessChangeFromPromotionQuery() and + queryId = + // `@id` for the `noSignednessChangeFromPromotion` query + "cpp/misra/no-signedness-change-from-promotion" and + ruleId = "RULE-7-0-5" and + category = "required" + or + query = + // `Query` instance for the `numericAssignmentTypeMismatch` query + ConversionsPackage::numericAssignmentTypeMismatchQuery() and + queryId = + // `@id` for the `numericAssignmentTypeMismatch` query + "cpp/misra/numeric-assignment-type-mismatch" and + ruleId = "RULE-7-0-6" and + category = "required" + or + query = + // `Query` instance for the `functionPointerConversionContext` query + ConversionsPackage::functionPointerConversionContextQuery() and + queryId = + // `@id` for the `functionPointerConversionContext` query + "cpp/misra/function-pointer-conversion-context" and + ruleId = "RULE-7-11-3" and + category = "required" + or + query = + // `Query` instance for the `virtualBaseClassCastToDerived` query + ConversionsPackage::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 + ConversionsPackage::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 + ConversionsPackage::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 + ConversionsPackage::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 + ConversionsPackage::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 + ConversionsPackage::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 ConversionsPackage { + Query noConversionFromBoolQuery() { + //autogenerate `Query` type + result = + // `Query` type for `noConversionFromBool` query + TQueryCPP(TConversionsPackageQuery(TNoConversionFromBoolQuery())) + } + + Query noImplicitBoolConversionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `noImplicitBoolConversion` query + TQueryCPP(TConversionsPackageQuery(TNoImplicitBoolConversionQuery())) + } + + Query noCharacterNumericalValueQuery() { + //autogenerate `Query` type + result = + // `Query` type for `noCharacterNumericalValue` query + TQueryCPP(TConversionsPackageQuery(TNoCharacterNumericalValueQuery())) + } + + Query noSignednessChangeFromPromotionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `noSignednessChangeFromPromotion` query + TQueryCPP(TConversionsPackageQuery(TNoSignednessChangeFromPromotionQuery())) + } + + Query numericAssignmentTypeMismatchQuery() { + //autogenerate `Query` type + result = + // `Query` type for `numericAssignmentTypeMismatch` query + TQueryCPP(TConversionsPackageQuery(TNumericAssignmentTypeMismatchQuery())) + } + + Query functionPointerConversionContextQuery() { + //autogenerate `Query` type + result = + // `Query` type for `functionPointerConversionContext` query + TQueryCPP(TConversionsPackageQuery(TFunctionPointerConversionContextQuery())) + } + + Query virtualBaseClassCastToDerivedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `virtualBaseClassCastToDerived` query + TQueryCPP(TConversionsPackageQuery(TVirtualBaseClassCastToDerivedQuery())) + } + + Query noCStyleOrFunctionalCastsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `noCStyleOrFunctionalCasts` query + TQueryCPP(TConversionsPackageQuery(TNoCStyleOrFunctionalCastsQuery())) + } + + Query intToPointerCastProhibitedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `intToPointerCastProhibited` query + TQueryCPP(TConversionsPackageQuery(TIntToPointerCastProhibitedQuery())) + } + + Query noPointerToIntegralCastQuery() { + //autogenerate `Query` type + result = + // `Query` type for `noPointerToIntegralCast` query + TQueryCPP(TConversionsPackageQuery(TNoPointerToIntegralCastQuery())) + } + + Query pointerToIntegralCastQuery() { + //autogenerate `Query` type + result = + // `Query` type for `pointerToIntegralCast` query + TQueryCPP(TConversionsPackageQuery(TPointerToIntegralCastQuery())) + } + + Query noStandaloneTypeCastExpressionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `noStandaloneTypeCastExpression` query + TQueryCPP(TConversionsPackageQuery(TNoStandaloneTypeCastExpressionQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll index abd6aeff96..88e4d55358 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll @@ -12,6 +12,7 @@ import Comments import Concurrency import Conditionals import Const +import Conversions import DeadCode import Declarations import ExceptionSafety @@ -67,6 +68,7 @@ newtype TCPPQuery = TConcurrencyPackageQuery(ConcurrencyQuery q) or TConditionalsPackageQuery(ConditionalsQuery q) or TConstPackageQuery(ConstQuery q) or + TConversionsPackageQuery(ConversionsQuery q) or TDeadCodePackageQuery(DeadCodeQuery q) or TDeclarationsPackageQuery(DeclarationsQuery q) or TExceptionSafetyPackageQuery(ExceptionSafetyQuery q) or @@ -122,6 +124,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat isConcurrencyQueryMetadata(query, queryId, ruleId, category) or isConditionalsQueryMetadata(query, queryId, ruleId, category) or isConstQueryMetadata(query, queryId, ruleId, category) or + isConversionsQueryMetadata(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/cpp/common/src/codingstandards/cpp/types/Compatible.qll b/cpp/common/src/codingstandards/cpp/types/Compatible.qll index c4ee9a22e3..399ba80629 100644 --- a/cpp/common/src/codingstandards/cpp/types/Compatible.qll +++ b/cpp/common/src/codingstandards/cpp/types/Compatible.qll @@ -1,6 +1,7 @@ import cpp import codeql.util.Boolean import codingstandards.cpp.types.Graph +import codingstandards.cpp.types.Type module TypeNamesMatchConfig implements TypeEquivalenceSig { predicate resolveTypedefs() { @@ -522,24 +523,6 @@ module FunctionDeclarationTypeEquivalence< } } -/** - * Convenience class to reduce the awkwardness of how `RoutineType` and `FunctionPointerIshType` - * don't have a common ancestor. - */ -private class FunctionType extends Type { - FunctionType() { this instanceof RoutineType or this instanceof FunctionPointerIshType } - - Type getReturnType() { - result = this.(RoutineType).getReturnType() or - result = this.(FunctionPointerIshType).getReturnType() - } - - Type getParameterType(int i) { - result = this.(RoutineType).getParameterType(i) or - result = this.(FunctionPointerIshType).getParameterType(i) - } -} - private class LeafType extends Type { LeafType() { not this instanceof DerivedType and diff --git a/cpp/common/src/codingstandards/cpp/types/Type.qll b/cpp/common/src/codingstandards/cpp/types/Type.qll index 42d77b8055..b1b0b7aba8 100644 --- a/cpp/common/src/codingstandards/cpp/types/Type.qll +++ b/cpp/common/src/codingstandards/cpp/types/Type.qll @@ -94,3 +94,38 @@ int getPrecision(IntegralType type) { or type.isExplicitlySigned() and result = type.getSize() * 8 - 1 } + +/** + * Determines the lower and upper bounds of an integral type. + */ +predicate integralTypeBounds(IntegralType integralType, QlBuiltins::BigInt lb, QlBuiltins::BigInt ub) { + exists(QlBuiltins::BigInt limit | limit = 2.toBigInt().pow(8 * integralType.getSize()) | + if integralType instanceof BoolType + then lb = 0.toBigInt() and ub = 1.toBigInt() + else + if integralType.isSigned() + then ( + lb = -(limit / 2.toBigInt()) and ub = (limit / 2.toBigInt()) - 1.toBigInt() + ) else ( + lb = 0.toBigInt() and ub = limit - 1.toBigInt() + ) + ) +} + +/** + * Convenience class to reduce the awkwardness of how `RoutineType` and `FunctionPointerIshType` + * don't have a common ancestor. + */ +class FunctionType extends Type { + FunctionType() { this instanceof RoutineType or this instanceof FunctionPointerIshType } + + Type getReturnType() { + result = this.(RoutineType).getReturnType() or + result = this.(FunctionPointerIshType).getReturnType() + } + + Type getParameterType(int i) { + result = this.(RoutineType).getParameterType(i) or + result = this.(FunctionPointerIshType).getParameterType(i) + } +} diff --git a/cpp/misra/src/codingstandards/cpp/misra/BuiltInTypeRules.qll b/cpp/misra/src/codingstandards/cpp/misra/BuiltInTypeRules.qll new file mode 100644 index 0000000000..17a3e257a2 --- /dev/null +++ b/cpp/misra/src/codingstandards/cpp/misra/BuiltInTypeRules.qll @@ -0,0 +1,267 @@ +/** + * A library for utility classes related to the built-in type rules in MISRA C++ 2023 (Section 4.7.0). + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.Call +import codingstandards.cpp.Type + +/** + * A MISRA C++ 2023 type category. + */ +newtype TypeCategory = + Integral() or + FloatingPoint() or + Character() or + Other() + +/** + * Gets the type category of a built-in type. + * + * This does not apply the rules related to stripping specifiers or typedefs, or references. + */ +TypeCategory getTypeCategory(BuiltInType t) { + ( + t instanceof CharType or + t instanceof WideCharType or + t instanceof Char16Type or + t instanceof Char32Type or + t instanceof Char8Type + ) and + result = Character() + or + ( + // The 5 standard integral types, covering both signed/unsigned variants + // Explicitly list the signed/unsigned `char` to avoid capturing plain `char`, which is of character type category + t instanceof SignedCharType or + t instanceof UnsignedCharType or + t instanceof ShortType or + t instanceof IntType or + t instanceof LongType or + t instanceof LongLongType + ) and + result = Integral() + or + ( + t instanceof FloatType or + t instanceof DoubleType or + t instanceof LongDoubleType + ) and + result = FloatingPoint() + or + ( + t instanceof BoolType or + t instanceof VoidType or + t instanceof NullPointerType + ) and + result = Other() +} + +/** + * The signedness of a MISRA C++ 2023 numeric type. + */ +newtype Signedness = + Signed() or + Unsigned() + +/** + * A MISRA C++ 2023 numeric type is a type that represents a number, either an integral or a floating-point. + * + * In addition to the basic integral and floating-point types, it includes: + * - Enum types with an explicit underlying type that is a numeric type. + * - Typedef'd types that are numeric types. + * - Numeric types with specifiers (e.g., `const`, `volatile`, `restrict`). + */ +class NumericType extends Type { + // The actual numeric type, which is either an integral or a floating-point type. + Type realType; + + NumericType() { + // A type which is either an integral or a floating-point type category + getTypeCategory(this) = [Integral().(TypeCategory), FloatingPoint()] and + realType = this + or + // Any type which, after stripping specifiers and typedefs, is a numeric type + realType = this.getUnspecifiedType().(NumericType).getRealType() + or + // Any reference type where the base type is a numeric type + realType = this.(ReferenceType).getBaseType().(NumericType).getRealType() + or + // Any Enum type with an explicit underlying type that is a numeric type + realType = this.(Enum).getExplicitUnderlyingType().(NumericType).getRealType() + } + + Signedness getSignedness() { + if realType.(IntegralType).isUnsigned() then result = Unsigned() else result = Signed() + } + + /** Gets the size of the actual numeric type. */ + int getRealSize() { result = realType.getSize() } + + TypeCategory getTypeCategory() { result = getTypeCategory(realType) } + + /** + * Gets the integeral upper bound of the numeric type, if it represents an integer type. + */ + QlBuiltins::BigInt getIntegralUpperBound() { integralTypeBounds(realType, _, result) } + + /** + * Gets the integeral lower bound of the numeric type, if it represents an integer type. + */ + QlBuiltins::BigInt getIntegralLowerBound() { integralTypeBounds(realType, result, _) } + + Type getRealType() { result = realType } +} + +/** + * One of the 10 canonical integer types, which are the standard integer types. + */ +class CanonicalIntegerTypes extends NumericType, IntegralType { + CanonicalIntegerTypes() { this = this.getCanonicalArithmeticType() } +} + +predicate isAssignment(Expr source, NumericType targetType, string context) { + exists(Expr preConversionAssignment | + isPreConversionAssignment(preConversionAssignment, targetType, context) and + preConversionAssignment.getExplicitlyConverted() = source + ) +} + +predicate isPreConversionAssignment(Expr source, NumericType targetType, string context) { + // Assignment expression (which excludes compound assignments) + exists(AssignExpr assign | + assign.getRValue() = source and + context = "assignment" + | + if isAssignedToBitfield(source, _) + then + // For the MISRA type rules we treat bit fields as a special case + exists(BitField bf | + isAssignedToBitfield(source, bf) and + targetType = getBitFieldType(bf) + ) + else + exists(Type t | t = assign.getLValue().getType() | + // Unwrap PointerToMemberType e.g `l1.*l2 = x;` + if t instanceof PointerToMemberType + then targetType = t.(PointerToMemberType).getBaseType() + else targetType = t + ) + ) + or + // Variable initialization + exists(Variable v, Initializer init | + init.getExpr() = source and + v.getInitializer() = init and + context = "initialization" + | + // For the MISRA type rules we treat bit fields as a special case + if v instanceof BitField + then targetType = getBitFieldType(v) + else + // Regular variable initialization + targetType = v.getType() + ) + or + exists(ConstructorFieldInit fi | + fi.getExpr() = source and + context = "constructor field initialization" + | + // For the MISRA type rules we treat bit fields as a special case + if fi.getTarget() instanceof BitField + then targetType = getBitFieldType(fi.getTarget()) + else + // Regular variable initialization + targetType = fi.getTarget().getType() + ) + or + // Passing a function parameter by value + exists(Call call, int i | + call.getArgument(i) = source and + not targetType.stripTopLevelSpecifiers() instanceof ReferenceType and + context = "function argument" + | + // A regular function call + targetType = call.getTarget().getParameter(i).getType() + or + // A function call where the argument is passed as varargs + call.getTarget().getNumberOfParameters() <= i and + // The rule states that the type should match the "adjusted" type of the argument + targetType = source.getFullyConverted().getType() + or + // An expression call - get the function type, then the parameter type + targetType = getExprCallFunctionType(call).getParameterType(i) + ) + or + // Return statement + exists(ReturnStmt ret, Function f | + ret.getExpr() = source and + ret.getEnclosingFunction() = f and + targetType = f.getType() and + not targetType.stripTopLevelSpecifiers() instanceof ReferenceType and + context = "return" + ) + or + // Switch case + exists(SwitchCase case, SwitchStmt switch | + case.getExpr() = source and + case.getSwitchStmt() = switch and + targetType = switch.getExpr().getFullyConverted().getType() and + context = "switch case" + ) + or + // Class aggregate literal initialization + exists(ClassAggregateLiteral al, Field f | + source = al.getAFieldExpr(f) and + context = "class aggregate literal" + | + // For the MISRA type rules we treat bit fields as a special case + if f instanceof BitField + then targetType = getBitFieldType(f) + else + // Regular variable initialization + targetType = f.getType() + ) + or + // Array or vector aggregate literal initialization + exists(ArrayOrVectorAggregateLiteral vl | + source = vl.getAnElementExpr(_) and + targetType = vl.getElementType() and + context = "array or vector aggregate literal" + ) +} + +/** + * Gets the smallest integral type that can hold the value of a bit field. + * + * The type is determined by the signedness of the bit field and the number of bits. + */ +CanonicalIntegerTypes getBitFieldType(BitField bf) { + exists(NumericType bitfieldActualType | + bitfieldActualType = bf.getType() and + // Integral type with the same signedness as the bit field, and big enough to hold the bit field value + result.getSignedness() = bitfieldActualType.getSignedness() and + result.getSize() * 8 >= bf.getNumBits() and + // No smaller integral type can hold the bit field value + not exists(CanonicalIntegerTypes other | + other.getSize() * 8 >= bf.getNumBits() and + other.getSignedness() = result.getSignedness() + | + other.getSize() < result.getRealSize() + or + // Where multiple types exist with the same size and signedness, prefer shorter names - mainly + // to disambiguate between `unsigned long` and `unsigned long long` on platforms where they + // are the same size + other.getSize() = result.getRealSize() and + other.getName().length() < result.getName().length() + ) + ) +} + +/** + * Holds if the `source` expression is assigned to a bit field. + */ +predicate isAssignedToBitfield(Expr source, BitField bf) { + source = bf.getAnAssignedValue().getExplicitlyConverted() +} diff --git a/cpp/misra/src/rules/RULE-7-0-1/NoConversionFromBool.ql b/cpp/misra/src/rules/RULE-7-0-1/NoConversionFromBool.ql new file mode 100644 index 0000000000..600d454863 --- /dev/null +++ b/cpp/misra/src/rules/RULE-7-0-1/NoConversionFromBool.ql @@ -0,0 +1,43 @@ +/** + * @id cpp/misra/no-conversion-from-bool + * @name RULE-7-0-1: There shall be no conversion from type bool + * @description Converting a bool type (implicitly or explicitly) to another type can lead to + * unintended behavior and code obfuscation, particularly when using bitwise operators + * instead of logical operators. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-7-0-1 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra + +from Expr e, Conversion conv +where + not isExcluded(e, ConversionsPackage::noConversionFromBoolQuery()) and + conv = e.getConversion() and + conv.getExpr().getType().stripTopLevelSpecifiers() instanceof BoolType and + not conv.getType().stripTopLevelSpecifiers() instanceof BoolType and + // Exclude cases that are explicitly allowed + not ( + // Exception: equality operators with both bool operands + exists(EqualityOperation eq | + eq.getAnOperand() = e and + eq.getLeftOperand().getType().stripTopLevelSpecifiers() instanceof BoolType and + eq.getRightOperand().getType().stripTopLevelSpecifiers() instanceof BoolType + ) + or + // Exception: explicit constructor calls + exists(ConstructorCall cc | cc.getAnArgument() = e) + or + // Exception: assignment to bit-field of length 1 + exists(AssignExpr assign | + assign.getRValue() = e and + assign.getLValue().(ValueFieldAccess).getTarget().(BitField).getNumBits() = 1 + ) + ) +select e, "Conversion from 'bool' to '" + conv.getType().toString() + "'." diff --git a/cpp/misra/src/rules/RULE-7-0-2/NoImplicitBoolConversion.ql b/cpp/misra/src/rules/RULE-7-0-2/NoImplicitBoolConversion.ql new file mode 100644 index 0000000000..308f879f1d --- /dev/null +++ b/cpp/misra/src/rules/RULE-7-0-2/NoImplicitBoolConversion.ql @@ -0,0 +1,98 @@ +/** + * @id cpp/misra/no-implicit-bool-conversion + * @name RULE-7-0-2: There shall be no conversion to type bool + * @description Implicit and contextual conversions to bool from fundamental types, unscoped enums, + * or pointers may lead to unintended behavior, except for specific cases like pointer + * checks and explicit operator bool conversions. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-7-0-2 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra + +predicate isInContextualBoolContext(Expr expr) { + exists(IfStmt ifStmt | ifStmt.getCondition() = expr) or + exists(WhileStmt whileStmt | whileStmt.getCondition() = expr) or + exists(ForStmt forStmt | forStmt.getCondition() = expr) or + exists(DoStmt doStmt | doStmt.getCondition() = expr) or + exists(ConditionalExpr condExpr | condExpr.getCondition() = expr) or + exists(LogicalAndExpr logicalAnd | logicalAnd.getAnOperand() = expr) or + exists(LogicalOrExpr logicalOr | logicalOr.getAnOperand() = expr) or + exists(NotExpr notExpr | notExpr.getOperand() = expr) +} + +predicate isInWhileConditionDeclaration(Expr expr) { + exists(WhileStmt whileStmt, ConditionDeclExpr condDecl | + whileStmt.getCondition() = condDecl and + condDecl.getExpr() = expr + ) +} + +predicate isBitFieldOfSizeOne(Expr expr) { + exists(BitField bf | + expr = bf.getAnAccess() and + bf.getNumBits() = 1 + ) +} + +predicate isPointerType(Type t) { + t.getUnspecifiedType() instanceof PointerType or + t.getUnspecifiedType() instanceof ArrayType or + t.getUnspecifiedType() instanceof PointerToMemberType +} + +from Element e, string reason +where + not isExcluded(e, ConversionsPackage::noImplicitBoolConversionQuery()) and + ( + // Conversions to bool + exists(Conversion conv | + e = conv and + conv.getType().getUnspecifiedType() instanceof BoolType and + not conv.getExpr().getType().getUnspecifiedType() instanceof BoolType and + // Exception 2: Contextual conversion from pointer + not ( + isPointerType(conv.getExpr().getType()) and + isInContextualBoolContext(conv.getExpr()) + ) and + // Exception 3: Bit-field of size 1 + not isBitFieldOfSizeOne(conv.getExpr()) and + // Exception 4: While condition declaration + not isInWhileConditionDeclaration(conv.getExpr()) and + reason = "Conversion from '" + conv.getExpr().getType().toString() + "' to 'bool'" + ) + or + // Calls to conversion operators to bool + // + // Note: we flag these separately because: + // 1. If the conversion via the operator is implicit, there is no `Conversion` - only a call to + // the `ConversionOperator`. + // 2. If the conversion is explicit, the `Conversion` is from `bool` to `bool`, which is not + // flagged in the previous `Conversion` case above. + exists(Call conversionCall, ConversionOperator op | + e = conversionCall and + conversionCall.getTarget() = op and + op.getType().getUnspecifiedType() instanceof BoolType and + // Exception 1: Static cast to bool from class with explicit operator bool + not exists(StaticCast conv | + op.isExplicit() and + conv.getExpr() = conversionCall and + conv.getType().getUnspecifiedType() instanceof BoolType + ) and + // Exception 2: Contextual conversion from class with explicit operator bool is allowed + not ( + op.isExplicit() and + isInContextualBoolContext(conversionCall) + ) and + reason = + "Conversion operator call from '" + conversionCall.getQualifier().getType().toString() + + "' to 'bool'" + ) + ) +select e, reason + "." diff --git a/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql new file mode 100644 index 0000000000..94f5210c8d --- /dev/null +++ b/cpp/misra/src/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql @@ -0,0 +1,211 @@ +/** + * @id cpp/misra/numeric-assignment-type-mismatch + * @name RULE-7-0-6: Assignment between numeric types shall be appropriate + * @description Assignment between numeric types with different sizes, signedness, or type + * categories can lead to unexpected information loss, undefined behavior, or silent + * overload resolution changes. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/misra/id/rule-7-0-6 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.ConstantExpressions +import codingstandards.cpp.misra.BuiltInTypeRules +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +predicate isValidConstantAssignment(IntegerConstantExpr source, NumericType targetType) { + isAssignment(source, targetType, _) and + exists(QlBuiltins::BigInt val | val = source.getConstantValue() | + // Bit field assignment: check if the value fits in the bit field + exists(BitField bf, int numBits | + isAssignedToBitfield(source, bf) and + numBits = bf.getNumBits() and + if targetType.getSignedness() = Signed() + then + // Signed bit field: value must be in the range of signed bit field + val >= -2.toBigInt().pow(numBits - 1) and + val < 2.toBigInt().pow(numBits - 1) + else ( + // Unsigned bit field: value must be in the range of unsigned bit field + val >= 0.toBigInt() and + val < 2.toBigInt().pow(numBits) + ) + ) + or + // Regular assignment: check if the value fits in the target type range + not isAssignedToBitfield(source, _) and + ( + // Integer types: check if the value fits in the target type range + targetType.getIntegralLowerBound() <= val and + val <= targetType.getIntegralUpperBound() + or + // All floating point types can represent all integer values + targetType.getTypeCategory() = FloatingPoint() + ) + ) +} + +bindingset[sourceType, targetType] +pragma[inline_late] +predicate isValidTypeMatch(NumericType sourceType, NumericType targetType) { + // Same type category, signedness and size + sourceType.getTypeCategory() = targetType.getTypeCategory() and + sourceType.getSignedness() = targetType.getSignedness() and + sourceType.getRealSize() = targetType.getRealSize() +} + +predicate hasConstructorException(FunctionCall call) { + exists(Constructor ctor, Class c | + call.getTarget() = ctor and + c = ctor.getDeclaringType() and + // Constructor callable with single numeric argument + ctor.getNumberOfParameters() = 1 and + ctor.getParameter(0).getType() instanceof NumericType and + // No other single-argument constructors except copy/move + not exists(Constructor other | + other.getDeclaringType() = c and + other != ctor and + other.getNumberOfParameters() = 1 and + not other instanceof CopyConstructor and + not other instanceof MoveConstructor + ) + ) +} + +/** + * An id-expression that has a numeric type. + * + * This is restricted to variable accesses, that are not explicitly qualified in any way. + */ +class IdExpression extends VariableAccess { + IdExpression() { + // Not a member variable access (no dot or arrow) + ( + not exists(this.getQualifier()) + or + // Member variable, but the qualifier is not explicit + this.getQualifier().isCompilerGenerated() + ) and + // Not an id-expression if it's an explicit conversion + not this.hasExplicitConversion() + } +} + +predicate isValidWidening(Expr source, NumericType sourceType, NumericType targetType) { + isAssignment(source, targetType, _) and + source.getType() = sourceType and + // Same type category and signedness, source size smaller, source is id-expression or has constructor exception + ( + source instanceof IdExpression or + hasConstructorException(any(Call call | call.getAnArgument().getExplicitlyConverted() = source)) + ) and + sourceType.getTypeCategory() = targetType.getTypeCategory() and + sourceType.getSignedness() = targetType.getSignedness() and + sourceType.getRealSize() < targetType.getRealSize() +} + +/** + * A non-extensible call is a call that cannot be extended by adding new overloads. + */ +predicate isNonExtensible(Call c) { + exists(NameQualifier qual | qual.getExpr() = c and c.getTarget() instanceof MemberFunction) + or + exists(c.getQualifier()) and not c.getQualifier().isCompilerGenerated() + or + c.getTarget() instanceof Operator +} + +int getMinimumNumberOfParameters(Function f) { + result = count(Parameter p | p = f.getAParameter() and not p.hasInitializer() | p) +} + +/** Get an overload of the function f, excluding deleted overloads. */ +Function getAnOverload(Function f) { + ( + result = f.getAnOverload() + or + // Instantiated function templates don't directly participate in overload resolution + // so check the templates overloads + result = f.(FunctionTemplateInstantiation).getTemplate().getAnOverload() + ) and + // Exclude deleted overloads + not result.isDeleted() +} + +predicate isOverloadIndependent(Call call, Expr arg) { + exists(int i | arg = call.getArgument(i) | + // Call through function pointer + call instanceof ExprCall + or + isNonExtensible(call) and + exists(Function target | target = call.getTarget() | + forall(Function overload | + overload = getAnOverload(target) and + // Check that the overload accepts the number of arguments provided by this call, + // considering parameters with default values may be omitted in the call + overload.getNumberOfParameters() >= call.getNumberOfArguments() and + getMinimumNumberOfParameters(overload) <= call.getNumberOfArguments() + | + // Check that the parameter types match + overload.getParameter(i).getType().getUnspecifiedType() = + target.getParameter(i).getType().getUnspecifiedType() + ) + ) + ) +} + +/** + * Check if the source expression should have the same type as the target type. + */ +predicate shouldHaveSameType(Expr source) { + exists(Call call | + call.getAnArgument().getExplicitlyConverted() = source and + isAssignment(source, _, _) and + not hasConstructorException(call) + | + not isOverloadIndependent(call, source) + or + // Passed as a varargs parameter + exists(int i | + call.getTarget().isVarargs() and + call.getArgument(i).getExplicitlyConverted() = source and + // Argument is greater than the number of parameters + call.getTarget().getNumberOfParameters() <= i + ) + ) +} + +predicate isValidAssignment(Expr source, NumericType targetType, string context) { + isAssignment(source, targetType, context) and + exists(NumericType sourceType | sourceType = source.getType() | + if shouldHaveSameType(source) + then sourceType.getRealType() = targetType.getRealType() + else ( + // Valid type match + isValidTypeMatch(sourceType, targetType) + or + // Valid widening assignment + isValidWidening(source, sourceType, targetType) + or + // Valid constant assignment (integer constants) + isValidConstantAssignment(source, targetType) + ) + ) +} + +from Expr source, NumericType sourceType, NumericType targetType, string context +where + not isExcluded(source, ConversionsPackage::numericAssignmentTypeMismatchQuery()) and + isAssignment(source, targetType, context) and + // The assignment must be between numeric types + sourceType = source.getType() and + not isValidAssignment(source, targetType, context) +select source, + "Assignment between incompatible numeric types from '" + sourceType.getName() + "' to '" + + targetType.getName() + "'." diff --git a/cpp/misra/test/rules/RULE-7-0-1/NoConversionFromBool.expected b/cpp/misra/test/rules/RULE-7-0-1/NoConversionFromBool.expected new file mode 100644 index 0000000000..8e0795fc6a --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-1/NoConversionFromBool.expected @@ -0,0 +1,29 @@ +| test.cpp:23:7:23:8 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:23:12:23:13 | b2 | Conversion from 'bool' to 'int'. | +| test.cpp:25:7:25:8 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:25:12:25:13 | b2 | Conversion from 'bool' to 'int'. | +| test.cpp:27:7:27:8 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:27:12:27:13 | b2 | Conversion from 'bool' to 'int'. | +| test.cpp:29:8:29:9 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:33:7:33:8 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:33:12:33:13 | b2 | Conversion from 'bool' to 'int'. | +| test.cpp:35:7:35:8 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:35:12:35:13 | b2 | Conversion from 'bool' to 'int'. | +| test.cpp:37:7:37:8 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:37:13:37:14 | b2 | Conversion from 'bool' to 'int'. | +| test.cpp:39:7:39:8 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:39:13:39:14 | b2 | Conversion from 'bool' to 'int'. | +| test.cpp:43:7:43:8 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:45:7:45:8 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:47:7:47:8 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:51:20:51:21 | b1 | Conversion from 'bool' to 'double'. | +| test.cpp:52:20:52:21 | b1 | Conversion from 'bool' to 'double'. | +| test.cpp:53:28:53:29 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:56:34:56:35 | b1 | Conversion from 'bool' to 'int8_t'. | +| test.cpp:57:36:57:37 | b1 | Conversion from 'bool' to 'int32_t'. | +| test.cpp:60:6:60:7 | b1 | Conversion from 'bool' to 'int32_t'. | +| test.cpp:61:6:61:7 | b1 | Conversion from 'bool' to 'double'. | +| test.cpp:64:11:64:12 | b1 | Conversion from 'bool' to 'int'. | +| test.cpp:72:9:72:10 | b1 | Conversion from 'bool' to 'int8_t'. | +| test.cpp:73:10:73:11 | b1 | Conversion from 'bool' to 'int32_t'. | +| test.cpp:74:8:74:9 | b1 | Conversion from 'bool' to 'double'. | diff --git a/cpp/misra/test/rules/RULE-7-0-1/NoConversionFromBool.qlref b/cpp/misra/test/rules/RULE-7-0-1/NoConversionFromBool.qlref new file mode 100644 index 0000000000..6d66f51484 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-1/NoConversionFromBool.qlref @@ -0,0 +1 @@ +rules/RULE-7-0-1/NoConversionFromBool.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-0-1/test.cpp b/cpp/misra/test/rules/RULE-7-0-1/test.cpp new file mode 100644 index 0000000000..57243e1c9a --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-1/test.cpp @@ -0,0 +1,117 @@ +#include + +struct A { + explicit A(bool) {} +}; + +struct BitField { + std::uint8_t bit : 1; +}; + +void f1(std::int32_t n) {} +void f2(double d) {} + +void test_bool_conversion_violations() { + bool b1 = true; + bool b2 = false; + double d1 = 1.0; + std::int8_t s8a = 0; + std::int32_t s32a = 0; + BitField bf; + + // Bitwise operations - non-compliant + if (b1 & b2) { // NON_COMPLIANT + } + if (b1 | b2) { // NON_COMPLIANT + } + if (b1 ^ b2) { // NON_COMPLIANT + } + if (~b1) { // NON_COMPLIANT + } + + // Relational operations - non-compliant + if (b1 < b2) { // NON_COMPLIANT + } + if (b1 > b2) { // NON_COMPLIANT + } + if (b1 <= b2) { // NON_COMPLIANT + } + if (b1 >= b2) { // NON_COMPLIANT + } + + // Comparison with integer literals - non-compliant + if (b1 == 0) { // NON_COMPLIANT + } + if (b1 == 1) { // NON_COMPLIANT + } + if (b1 != 0) { // NON_COMPLIANT + } + + // Arithmetic operations - non-compliant + double l1 = d1 * b1; // NON_COMPLIANT + double l2 = d1 + b1; // NON_COMPLIANT + std::int32_t l3 = s32a + b1; // NON_COMPLIANT + + // Explicit casts to integral types - non-compliant + s8a = static_cast(b1); // NON_COMPLIANT + s32a = static_cast(b1); // NON_COMPLIANT + + // Function parameter conversion - non-compliant + f1(b1); // NON_COMPLIANT + f2(b1); // NON_COMPLIANT + + // Switch statement - non-compliant + switch (b1) { // NON_COMPLIANT + case 0: + break; + case 1: + break; + } + + // Assignment to integral types - non-compliant + s8a = b1; // NON_COMPLIANT + s32a = b1; // NON_COMPLIANT + d1 = b1; // NON_COMPLIANT +} + +void test_bool_conversion_compliant() { + bool b1 = true; + bool b2 = false; + std::int8_t s8a = 0; + BitField bf; + + // Boolean equality operations - compliant + if (b1 == false) { // COMPLIANT + } + if (b1 == true) { // COMPLIANT + } + if (b1 == b2) { // COMPLIANT + } + if (b1 != b2) { // COMPLIANT + } + + // Logical operations - compliant + if (b1 && b2) { // COMPLIANT + } + if (b1 || b2) { // COMPLIANT + } + if (!b1) { // COMPLIANT + } + + // Conditional operator without conversion - compliant + s8a = b1 ? 3 : 7; // COMPLIANT + + // Function parameter without conversion - compliant + f1(b1 ? 1 : 0); // COMPLIANT + + // Explicit constructor calls - compliant + A l1{true}; // COMPLIANT + A l2(false); // COMPLIANT + A l3 = static_cast(true); // COMPLIANT + + // Assignment to constructor - compliant + A l4 = A{false}; // COMPLIANT + + // Bit-field assignment exception - compliant + bf.bit = b1; // COMPLIANT +} \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-0-2/NoImplicitBoolConversion.expected b/cpp/misra/test/rules/RULE-7-0-2/NoImplicitBoolConversion.expected new file mode 100644 index 0000000000..c000a23dd2 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-2/NoImplicitBoolConversion.expected @@ -0,0 +1,28 @@ +| test.cpp:53:20:53:28 | (bool)... | Conversion from 'int' to 'bool'. | +| test.cpp:57:7:57:7 | (bool)... | Conversion from 'int' to 'bool'. | +| test.cpp:59:7:59:8 | (bool)... | Conversion from 'uint8_t' to 'bool'. | +| test.cpp:61:8:61:8 | (bool)... | Conversion from 'int' to 'bool'. | +| test.cpp:65:7:65:8 | (bool)... | Conversion from 'uint8_t' to 'bool'. | +| test.cpp:76:8:76:9 | (bool)... | Conversion from 'int16_t' to 'bool'. | +| test.cpp:85:20:85:21 | (bool)... | Conversion from 'int32_t' to 'bool'. | +| test.cpp:91:31:91:32 | (bool)... | Conversion from 'int32_t' to 'bool'. | +| test.cpp:99:30:99:31 | (bool)... | Conversion from 'int32_t' to 'bool'. | +| test.cpp:112:12:112:13 | (bool)... | Conversion from 'int32_t' to 'bool'. | +| test.cpp:127:13:127:16 | (bool)... | Conversion from 'int32_t *' to 'bool'. | +| test.cpp:130:7:130:13 | (bool)... | Conversion from 'decltype(nullptr)' to 'bool'. | +| test.cpp:135:13:135:32 | static_cast... | Conversion from 'int' to 'bool'. | +| test.cpp:136:13:136:14 | (bool)... | Conversion from 'uint8_t' to 'bool'. | +| test.cpp:148:13:148:13 | call to operator bool | Conversion operator call from 'TestClassImplicit' to 'bool'. | +| test.cpp:161:13:161:14 | (bool)... | Conversion from 'Color' to 'bool'. | +| test.cpp:162:7:162:8 | (bool)... | Conversion from 'Color' to 'bool'. | +| test.cpp:179:13:179:14 | (bool)... | Conversion from 'float' to 'bool'. | +| test.cpp:180:13:180:14 | (bool)... | Conversion from 'double' to 'bool'. | +| test.cpp:181:13:181:14 | (bool)... | Conversion from 'long double' to 'bool'. | +| test.cpp:182:7:182:8 | (bool)... | Conversion from 'float' to 'bool'. | +| test.cpp:184:7:184:8 | (bool)... | Conversion from 'double' to 'bool'. | +| test.cpp:186:7:186:8 | (bool)... | Conversion from 'long double' to 'bool'. | +| test.cpp:195:7:195:8 | (bool)... | Conversion from 'int32_t *' to 'bool'. | +| test.cpp:197:7:197:8 | (bool)... | Conversion from 'int32_t *' to 'bool'. | +| test.cpp:199:13:199:14 | (bool)... | Conversion from 'int32_t *' to 'bool'. | +| test.cpp:211:13:211:14 | (bool)... | Conversion from '..:: *' to 'bool'. | +| test.cpp:212:13:212:14 | (bool)... | Conversion from '..:: *' to 'bool'. | diff --git a/cpp/misra/test/rules/RULE-7-0-2/NoImplicitBoolConversion.qlref b/cpp/misra/test/rules/RULE-7-0-2/NoImplicitBoolConversion.qlref new file mode 100644 index 0000000000..a7b86d71f8 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-2/NoImplicitBoolConversion.qlref @@ -0,0 +1 @@ +rules/RULE-7-0-2/NoImplicitBoolConversion.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-0-2/test.cpp b/cpp/misra/test/rules/RULE-7-0-2/test.cpp new file mode 100644 index 0000000000..568980dea3 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-2/test.cpp @@ -0,0 +1,214 @@ +#include +#include + +// Global variables for testing +std::uint8_t g1 = 5; +std::uint8_t g2 = 10; +std::uint8_t g3 = 15; +std::uint8_t g4 = 20; +std::int16_t g5 = 100; +std::int32_t g6 = 200; +bool g7 = true; +std::int32_t g8[5] = {1, 2, 3, 4, 5}; + +// Function declarations +std::int32_t f1(); +bool f2(); +std::int32_t *f3(); + +// Class with explicit operator bool +class TestClassExplicit { + std::int32_t m1; + +public: + explicit operator bool() const { return m1 < 0; } +}; + +class TestClassImplicit { + std::int32_t m1; + +public: + operator bool() const { return m1 < 0; } // Implicit conversion +}; + +// Class with member function pointer +class TestClassMemberFunc { +public: + void memberFunc() {} +}; + +// Bit-field struct for exception #3 +struct BitFieldStruct { + unsigned int m1 : 1; +}; + +void test_logical_operators() { + std::uint8_t l1 = 5; + std::uint8_t l2 = 10; + std::uint8_t l3 = 15; + std::uint8_t l4 = 20; + + if ((l1 < l2) && (l3 < l4)) { // COMPLIANT + } + if ((l1 < l2) && (l3 + l4)) { // NON_COMPLIANT + } + if (true && (l3 < l4)) { // COMPLIANT + } + if (1 && (l3 < l4)) { // NON_COMPLIANT + } + if (l1 && (l3 < l4)) { // NON_COMPLIANT + } + if (!0) { // NON_COMPLIANT + } + if (!false) { // COMPLIANT + } + if (l1) { // NON_COMPLIANT + } +} + +void test_conditional_operator() { + std::int32_t l1 = 100; + std::int32_t l2 = 200; + std::int32_t l3 = 300; + std::int16_t l4 = 50; + bool l5 = true; + + l1 = l4 ? l2 : l3; // NON_COMPLIANT + l1 = l5 ? l2 : l3; // COMPLIANT + l1 = (l4 < 5) ? l2 : l3; // COMPLIANT +} + +void test_if_statements() { + std::int32_t l1; + bool l2 = f2(); + + if (std::int32_t l3 = f1()) { // NON_COMPLIANT + } + if (std::int32_t l4 = f1(); l4 != 0) { // COMPLIANT + } + if (bool l5 = f2()) { // COMPLIANT + } + if (std::int32_t l6 = f1(); l6) { // NON_COMPLIANT + } +} + +void test_while_loops() { + while (std::int32_t l1 = f1()) { // COMPLIANT - exception #4 + } + + for (std::int32_t l2 = 10; l2; --l2) { // NON_COMPLIANT + } + + while (std::cin) { // COMPLIANT - exception #2 + } +} + +void test_do_while_loops() { + std::int32_t l1 = 5; + bool l2 = true; + + do { + --l1; + } while (l1); // NON_COMPLIANT + + do { + --l1; + } while (l2); // COMPLIANT + + do { + --l1; + } while (l1 > 0); // COMPLIANT +} + +void test_pointer_conversions() { + if (f3()) { // COMPLIANT - exception #2 + } + + bool l1 = f3(); // NON_COMPLIANT + bool l2 = f3() != nullptr; // COMPLIANT + + if (nullptr) { // NON_COMPLIANT + } +} + +void test_assignment_to_bool() { + bool l1 = static_cast(4); // NON_COMPLIANT + bool l2 = g1; // NON_COMPLIANT + bool l3 = (g1 < g2); // COMPLIANT + bool l4 = g7; // COMPLIANT +} + +void test_classes_with_bool_operators() { + TestClassExplicit l1; + + bool l2 = static_cast(l1); // COMPLIANT - exception #1 + if (l1) { // COMPLIANT - exception #2 + } + TestClassImplicit l3; + bool l4 = l3; // NON_COMPLIANT +} + +void test_bitfield_conversion() { + BitFieldStruct l1; + + bool l2 = l1.m1; // COMPLIANT - exception #3 +} + +void test_unscoped_enum_conversion() { + enum Color { RED, GREEN, BLUE }; + Color l1 = RED; + + bool l2 = l1; // NON_COMPLIANT + if (l1) { // NON_COMPLIANT + } + bool l3 = (l1 == RED); // COMPLIANT +} + +void test_scoped_enum_conversion() { + enum class Status { ACTIVE, INACTIVE }; + Status l1 = Status::ACTIVE; + + bool l2 = (l1 == Status::ACTIVE); // COMPLIANT +} + +void test_floating_point_conversion() { + float l1 = 3.14f; + double l2 = 2.71; + long double l3 = 1.41L; + + bool l4 = l1; // NON_COMPLIANT + bool l5 = l2; // NON_COMPLIANT + bool l6 = l3; // NON_COMPLIANT + if (l1) { // NON_COMPLIANT + } + if (l2) { // NON_COMPLIANT + } + if (l3) { // NON_COMPLIANT + } + bool l7 = (l1 > 0.0f); // COMPLIANT + bool l8 = (l2 != 0.0); // COMPLIANT +} + +void test_array_conversion() { + std::int32_t l1[5] = {1, 2, 3, 4, 5}; + + if (l1) { // NON_COMPLIANT + } + if (g8) { // NON_COMPLIANT + } + bool l2 = l1; // NON_COMPLIANT + bool l3 = (l1 != nullptr); // COMPLIANT +} + +void test_member_function_pointer_conversion() { + void (TestClassMemberFunc::*l1)() = &TestClassMemberFunc::memberFunc; + void (TestClassMemberFunc::*l2)() = nullptr; + + if (l1) { // COMPLIANT - exception #2 + } + if (l2) { // COMPLIANT - exception #2 + } + bool l3 = l1; // NON_COMPLIANT + bool l4 = l2; // NON_COMPLIANT + bool l5 = (l1 != nullptr); // COMPLIANT +} \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected new file mode 100644 index 0000000000..a6473c6df0 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.expected @@ -0,0 +1,322 @@ +| test.cpp:36:8:36:11 | 300 | Assignment between incompatible numeric types from 'unsigned int' to 'uint8_t'. | +| test.cpp:39:7:39:10 | 0.0 | Assignment between incompatible numeric types from 'float' to 'double'. | +| test.cpp:45:8:45:9 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'uint8_t'. | +| test.cpp:46:8:46:9 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'int8_t'. | +| test.cpp:51:8:51:10 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test.cpp:52:9:52:11 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'uint16_t'. | +| test.cpp:57:7:57:9 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'float'. | +| test.cpp:58:9:58:9 | f | Assignment between incompatible numeric types from 'float' to 'int32_t'. | +| test.cpp:95:12:95:13 | m1 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | +| test.cpp:96:12:96:13 | m2 | Assignment between incompatible numeric types from 'uint16_t' to 'uint64_t'. | +| test.cpp:97:13:97:14 | m1 | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | +| test.cpp:98:13:98:14 | m2 | Assignment between incompatible numeric types from 'uint16_t' to 'uint64_t'. | +| test.cpp:103:9:103:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint32_t'. | +| test.cpp:104:9:104:12 | (...) | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | +| test.cpp:105:9:105:37 | static_cast... | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | +| test.cpp:110:8:110:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test.cpp:111:21:111:27 | ... + ... | Assignment between incompatible numeric types from 'int' to 'int16_t'. | +| test.cpp:134:11:134:11 | 4 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | +| test.cpp:137:11:137:13 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test.cpp:141:11:141:13 | 256 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | +| test.cpp:143:11:143:13 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test.cpp:144:11:144:13 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned char'. | +| test.cpp:148:11:148:13 | 512 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | +| test.cpp:149:11:149:15 | 65535 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | +| test.cpp:150:11:150:15 | 65536 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | +| test.cpp:153:11:153:13 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned short'. | +| test.cpp:157:11:157:15 | 65536 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | +| test.cpp:160:11:160:13 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned short'. | +| test.cpp:164:11:164:16 | 131072 | Assignment between incompatible numeric types from 'int' to 'unsigned int'. | +| test.cpp:168:11:168:13 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'unsigned int'. | +| test.cpp:172:11:172:22 | 4294967296 | Assignment between incompatible numeric types from 'unsigned long' to 'unsigned int'. | +| test.cpp:176:11:176:13 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'unsigned int'. | +| test.cpp:180:11:180:21 | 8589934592 | Assignment between incompatible numeric types from 'long' to 'unsigned long'. | +| test.cpp:193:11:193:12 | 16 | Assignment between incompatible numeric types from 'int32_t' to 'signed char'. | +| test.cpp:194:11:194:13 | - ... | Assignment between incompatible numeric types from 'int' to 'signed char'. | +| test.cpp:196:11:196:13 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'signed char'. | +| test.cpp:200:11:200:14 | 2048 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | +| test.cpp:201:11:201:15 | - ... | Assignment between incompatible numeric types from 'int' to 'short'. | +| test.cpp:204:11:204:13 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | +| test.cpp:208:12:208:22 | 134217728 | Assignment between incompatible numeric types from 'long long' to 'int'. | +| test.cpp:209:12:209:23 | - ... | Assignment between incompatible numeric types from 'long long' to 'int'. | +| test.cpp:224:8:224:9 | l1 | Assignment between incompatible numeric types from 'Colour' to 'uint8_t'. | +| test.cpp:234:6:234:8 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'int64_t'. | +| test.cpp:237:6:237:8 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test.cpp:239:6:239:7 | l1 | Assignment between incompatible numeric types from 'int' to 'int32_t'. | +| test.cpp:252:6:252:7 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'int32_t'. | +| test.cpp:261:6:261:6 | 2 | Assignment between incompatible numeric types from 'int' to 'long'. | +| test.cpp:269:14:269:15 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'int'. | +| test.cpp:289:6:289:8 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'size_t'. | +| test.cpp:294:12:294:14 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | +| test.cpp:313:9:313:10 | 42 | Assignment between incompatible numeric types from 'int' to 'long'. | +| test.cpp:346:25:346:27 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned long'. | +| test.cpp:358:19:358:25 | ... + ... | Assignment between incompatible numeric types from 'int' to 'int16_t'. | +| test.cpp:363:10:363:12 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test.cpp:396:8:396:9 | l6 | Assignment between incompatible numeric types from 'uint32_t &' to 'uint8_t'. | +| test.cpp:397:8:397:9 | l7 | Assignment between incompatible numeric types from 'int8_t &' to 'uint8_t'. | +| test.cpp:398:9:398:10 | l8 | Assignment between incompatible numeric types from 'float &' to 'int32_t'. | +| test.cpp:409:6:409:7 | l3 | Assignment between incompatible numeric types from 'uint8_t &' to 'int64_t'. | +| test.cpp:410:6:410:7 | l4 | Assignment between incompatible numeric types from 'uint16_t &' to 'int32_t'. | +| test.cpp:421:8:421:9 | l3 | Assignment between incompatible numeric types from 'uint8_t &' to 'int8_t'. | +| test.cpp:422:8:422:9 | l4 | Assignment between incompatible numeric types from 'int8_t &' to 'uint8_t'. | +| test.cpp:435:9:435:10 | l4 | Assignment between incompatible numeric types from 'float &' to 'int32_t'. | +| test.cpp:436:7:436:8 | l5 | Assignment between incompatible numeric types from 'double &' to 'float'. | +| test.cpp:437:7:437:8 | l6 | Assignment between incompatible numeric types from 'int32_t &' to 'float'. | +| test.cpp:448:8:448:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test.cpp:466:7:466:8 | l3 | Assignment between incompatible numeric types from 'uint16_t &' to 'uint32_t'. | +| test.cpp:469:7:469:8 | l5 | Assignment between incompatible numeric types from 'uint64_t &' to 'uint32_t'. | +| test.cpp:518:12:518:13 | l1 | Assignment between incompatible numeric types from 'uint32_t' to 'uint8_t'. | +| test.cpp:519:12:519:13 | l1 | Assignment between incompatible numeric types from 'uint32_t' to 'uint16_t'. | +| test.cpp:521:12:521:13 | l2 | Assignment between incompatible numeric types from 'int32_t' to 'int8_t'. | +| test.cpp:522:12:522:13 | l2 | Assignment between incompatible numeric types from 'int32_t' to 'int16_t'. | +| test.cpp:542:12:542:14 | 300 | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test.cpp:543:12:543:16 | 70000 | Assignment between incompatible numeric types from 'int' to 'uint16_t'. | +| test.cpp:544:12:544:27 | 4294967296 | Assignment between incompatible numeric types from 'unsigned long long' to 'uint32_t'. | +| test.cpp:545:12:545:14 | 200 | Assignment between incompatible numeric types from 'int' to 'int8_t'. | +| test.cpp:546:12:546:16 | 40000 | Assignment between incompatible numeric types from 'int' to 'int16_t'. | +| test.cpp:547:12:547:26 | 4294967296 | Assignment between incompatible numeric types from 'long long' to 'int32_t'. | +| test.cpp:548:12:548:14 | 1.0 | Assignment between incompatible numeric types from 'double' to 'float'. | +| test.cpp:549:12:549:15 | 1.0 | Assignment between incompatible numeric types from 'float' to 'double'. | +| test.cpp:554:12:554:18 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test.cpp:555:12:555:18 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint16_t'. | +| test.cpp:556:12:556:18 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint32_t'. | +| test.cpp:558:12:558:13 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'int16_t'. | +| test.cpp:559:12:559:13 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'int32_t'. | +| test.cpp:560:12:560:13 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'float'. | +| test.cpp:561:12:561:13 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'double'. | +| test.cpp:591:9:591:37 | static_cast... | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | +| test_aggregate.cpp:29:22:29:24 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test_aggregate.cpp:31:26:31:28 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'uint16_t'. | +| test_aggregate.cpp:33:31:33:33 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_aggregate.cpp:35:22:35:24 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'float'. | +| test_aggregate.cpp:38:22:38:24 | 256 | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test_aggregate.cpp:38:27:38:31 | 65536 | Assignment between incompatible numeric types from 'int' to 'uint16_t'. | +| test_aggregate.cpp:39:22:39:33 | 2147483648 | Assignment between incompatible numeric types from 'long long' to 'int32_t'. | +| test_aggregate.cpp:51:22:51:24 | 300 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | +| test_aggregate.cpp:51:27:51:29 | 400 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | +| test_aggregate.cpp:51:32:51:34 | 500 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | +| test_aggregate.cpp:52:22:52:23 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'unsigned char'. | +| test_aggregate.cpp:52:26:52:27 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'unsigned char'. | +| test_aggregate.cpp:52:30:52:31 | s8 | Assignment between incompatible numeric types from 'int8_t' to 'unsigned char'. | +| test_aggregate.cpp:53:22:53:24 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test_aggregate.cpp:53:27:53:29 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test_aggregate.cpp:53:32:53:34 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test_aggregate.cpp:58:26:58:28 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | +| test_aggregate.cpp:58:31:58:33 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | +| test_aggregate.cpp:58:38:58:40 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | +| test_aggregate.cpp:58:43:58:45 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'signed short'. | +| test_aggregate.cpp:59:26:59:27 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'signed short'. | +| test_aggregate.cpp:59:30:59:31 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'signed short'. | +| test_aggregate.cpp:60:26:60:27 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'signed short'. | +| test_aggregate.cpp:60:30:60:31 | u8 | Assignment between incompatible numeric types from 'uint8_t' to 'signed short'. | +| test_aggregate.cpp:76:8:76:10 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test_aggregate.cpp:80:7:80:9 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'uint32_t'. | +| test_aggregate.cpp:96:24:96:26 | 300 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | +| test_aggregate.cpp:96:29:96:33 | 70000 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | +| test_aggregate.cpp:96:36:96:39 | 5000 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | +| test_aggregate.cpp:97:24:97:26 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test_aggregate.cpp:97:29:97:31 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned short'. | +| test_aggregate.cpp:97:34:97:36 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | +| test_aggregate.cpp:107:28:107:30 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test_member_pointers.cpp:25:12:25:14 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_member_pointers.cpp:26:12:26:14 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:27:12:27:14 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_member_pointers.cpp:28:12:28:14 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. | +| test_member_pointers.cpp:32:13:32:15 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_member_pointers.cpp:33:13:33:15 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:34:13:34:15 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_member_pointers.cpp:35:13:35:15 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. | +| test_member_pointers.cpp:42:12:42:14 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_member_pointers.cpp:43:12:43:14 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:44:12:44:14 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_member_pointers.cpp:45:12:45:14 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. | +| test_member_pointers.cpp:49:13:49:15 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_member_pointers.cpp:50:13:50:15 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:51:13:51:15 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_member_pointers.cpp:52:13:52:15 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. | +| test_member_pointers.cpp:58:11:58:13 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | +| test_member_pointers.cpp:60:11:60:13 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:66:11:66:13 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:67:11:67:13 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_member_pointers.cpp:82:6:82:8 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_member_pointers.cpp:83:6:83:8 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:84:6:84:8 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_member_pointers.cpp:85:6:85:8 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. | +| test_member_pointers.cpp:90:6:90:8 | s64 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_member_pointers.cpp:91:6:91:8 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:92:6:92:8 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_member_pointers.cpp:93:6:93:8 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int32_t'. | +| test_member_pointers.cpp:97:7:97:9 | s16 | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | +| test_member_pointers.cpp:100:7:100:9 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:106:41:106:43 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int32_t'. | +| test_member_pointers.cpp:107:41:107:43 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_member_pointers.cpp:125:12:125:14 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'int64_t'. | +| test_member_pointers.cpp:126:12:126:14 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'int64_t'. | +| test_member_pointers.cpp:127:12:127:14 | u64 | Assignment between incompatible numeric types from 'uint64_t' to 'int64_t'. | +| test_operators.cpp:99:8:99:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:100:8:100:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:104:8:104:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:105:8:105:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:109:7:109:8 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:110:7:110:8 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:114:8:114:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:115:8:115:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:119:8:119:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:120:8:120:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:124:8:124:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:125:8:125:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:129:8:129:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:130:8:130:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:134:8:134:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:135:8:135:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:139:9:139:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:140:9:140:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:144:9:144:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:145:9:145:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:150:9:150:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:151:9:151:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:155:9:155:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:156:9:156:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:160:8:160:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:161:8:161:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:165:9:165:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:166:9:166:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:170:8:170:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:171:8:171:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:175:9:175:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:176:9:176:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:181:6:181:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:182:6:182:7 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:187:6:187:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:188:6:188:7 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:190:10:190:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:191:6:191:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:192:6:192:7 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:192:10:192:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:197:8:197:9 | l3 | Assignment between incompatible numeric types from 'int16_t' to 'int32_t'. | +| test_operators.cpp:198:8:198:9 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:199:8:199:9 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:203:9:203:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:204:9:204:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:208:9:208:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:209:9:209:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:213:9:213:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:214:9:214:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:218:9:218:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:219:9:219:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:223:9:223:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:224:9:224:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:228:9:228:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:229:9:229:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:233:9:233:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:234:9:234:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:238:9:238:10 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:239:9:239:10 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:243:10:243:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:244:10:244:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:248:10:248:11 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:249:10:249:11 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:254:3:254:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:255:3:255:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:259:3:259:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:260:3:260:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:264:3:264:4 | l4 | Assignment between incompatible numeric types from 'int64_t' to 'int32_t'. | +| test_operators.cpp:265:3:265:4 | l5 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_operators.cpp:277:8:277:12 | 42.0 | Assignment between incompatible numeric types from 'float' to 'int32_t'. | +| test_operators.cpp:292:8:292:10 | 42 | Assignment between incompatible numeric types from 'long' to 'int32_t'. | +| test_operators.cpp:293:8:293:11 | 42 | Assignment between incompatible numeric types from 'long long' to 'int32_t'. | +| test_operators.cpp:294:8:294:10 | 42 | Assignment between incompatible numeric types from 'unsigned int' to 'int32_t'. | +| test_specified.cpp:37:8:37:9 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test_specified.cpp:38:8:38:9 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'uint8_t'. | +| test_specified.cpp:41:8:41:9 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'int8_t'. | +| test_specified.cpp:42:8:42:9 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'int8_t'. | +| test_specified.cpp:43:9:43:10 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'int16_t'. | +| test_specified.cpp:48:8:48:9 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'uint8_t'. | +| test_specified.cpp:49:9:49:10 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'uint16_t'. | +| test_specified.cpp:50:9:50:10 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'uint32_t'. | +| test_specified.cpp:58:9:58:10 | l7 | Assignment between incompatible numeric types from 'float' to 'int32_t'. | +| test_specified.cpp:59:9:59:10 | l8 | Assignment between incompatible numeric types from 'double' to 'int32_t'. | +| test_specified.cpp:71:7:71:8 | l8 | Assignment between incompatible numeric types from 'double' to 'float'. | +| test_specified.cpp:94:8:94:9 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test_specified.cpp:95:8:95:9 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'uint8_t'. | +| test_specified.cpp:96:9:96:10 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'uint16_t'. | +| test_specified.cpp:97:8:97:9 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'int8_t'. | +| test_specified.cpp:98:8:98:9 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'int8_t'. | +| test_specified.cpp:99:9:99:10 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'int16_t'. | +| test_specified.cpp:102:8:102:9 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'uint8_t'. | +| test_specified.cpp:103:9:103:10 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'uint16_t'. | +| test_specified.cpp:104:9:104:10 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'uint32_t'. | +| test_specified.cpp:105:8:105:9 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'int8_t'. | +| test_specified.cpp:106:9:106:10 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'int16_t'. | +| test_specified.cpp:107:9:107:10 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_specified.cpp:110:9:110:10 | l7 | Assignment between incompatible numeric types from 'float' to 'int32_t'. | +| test_specified.cpp:111:9:111:10 | l8 | Assignment between incompatible numeric types from 'double' to 'int32_t'. | +| test_specified.cpp:112:7:112:8 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'float'. | +| test_specified.cpp:113:7:113:8 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'float'. | +| test_specified.cpp:114:7:114:8 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'float'. | +| test_specified.cpp:115:7:115:8 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'double'. | +| test_specified.cpp:116:7:116:8 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'double'. | +| test_specified.cpp:117:7:117:8 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'double'. | +| test_specified.cpp:120:7:120:8 | l8 | Assignment between incompatible numeric types from 'double' to 'float'. | +| test_specified.cpp:143:8:143:9 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test_specified.cpp:144:8:144:9 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'uint8_t'. | +| test_specified.cpp:145:9:145:10 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'uint16_t'. | +| test_specified.cpp:146:8:146:9 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'int8_t'. | +| test_specified.cpp:147:8:147:9 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'int8_t'. | +| test_specified.cpp:148:9:148:10 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'int16_t'. | +| test_specified.cpp:151:8:151:9 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'uint8_t'. | +| test_specified.cpp:152:9:152:10 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'uint16_t'. | +| test_specified.cpp:153:9:153:10 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'uint32_t'. | +| test_specified.cpp:154:8:154:9 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'int8_t'. | +| test_specified.cpp:155:9:155:10 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'int16_t'. | +| test_specified.cpp:156:9:156:10 | l3 | Assignment between incompatible numeric types from 'uint32_t' to 'int32_t'. | +| test_specified.cpp:159:9:159:10 | l7 | Assignment between incompatible numeric types from 'float' to 'int32_t'. | +| test_specified.cpp:160:9:160:10 | l8 | Assignment between incompatible numeric types from 'double' to 'int32_t'. | +| test_specified.cpp:161:7:161:8 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'float'. | +| test_specified.cpp:162:7:162:8 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'float'. | +| test_specified.cpp:163:7:163:8 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'float'. | +| test_specified.cpp:164:7:164:8 | l4 | Assignment between incompatible numeric types from 'int8_t' to 'double'. | +| test_specified.cpp:165:7:165:8 | l5 | Assignment between incompatible numeric types from 'int16_t' to 'double'. | +| test_specified.cpp:166:7:166:8 | l6 | Assignment between incompatible numeric types from 'int32_t' to 'double'. | +| test_specified.cpp:169:7:169:8 | l8 | Assignment between incompatible numeric types from 'double' to 'float'. | +| test_specified.cpp:213:8:213:9 | l6 | Assignment between incompatible numeric types from 'const uint16_t &' to 'uint8_t'. | +| test_specified.cpp:214:8:214:10 | l10 | Assignment between incompatible numeric types from 'volatile uint16_t &' to 'uint8_t'. | +| test_specified.cpp:215:8:215:10 | l14 | Assignment between incompatible numeric types from 'const volatile uint16_t &' to 'uint8_t'. | +| test_specified.cpp:218:8:218:9 | l5 | Assignment between incompatible numeric types from 'const uint8_t &' to 'int8_t'. | +| test_specified.cpp:219:8:219:9 | l7 | Assignment between incompatible numeric types from 'const int8_t &' to 'uint8_t'. | +| test_specified.cpp:220:8:220:9 | l9 | Assignment between incompatible numeric types from 'volatile uint8_t &' to 'int8_t'. | +| test_specified.cpp:221:8:221:10 | l11 | Assignment between incompatible numeric types from 'volatile int8_t &' to 'uint8_t'. | +| test_specified.cpp:222:8:222:10 | l13 | Assignment between incompatible numeric types from 'const volatile uint8_t &' to 'int8_t'. | +| test_specified.cpp:223:8:223:10 | l15 | Assignment between incompatible numeric types from 'const volatile int8_t &' to 'uint8_t'. | +| test_specified.cpp:226:9:226:10 | l8 | Assignment between incompatible numeric types from 'const float &' to 'int32_t'. | +| test_specified.cpp:227:9:227:11 | l12 | Assignment between incompatible numeric types from 'volatile float &' to 'int32_t'. | +| test_specified.cpp:228:9:228:11 | l16 | Assignment between incompatible numeric types from 'const volatile float &' to 'int32_t'. | +| test_specified.cpp:229:7:229:8 | l3 | Assignment between incompatible numeric types from 'int8_t' to 'float'. | +| test_specified.cpp:230:7:230:8 | l7 | Assignment between incompatible numeric types from 'const int8_t &' to 'float'. | +| test_specified.cpp:231:7:231:9 | l11 | Assignment between incompatible numeric types from 'volatile int8_t &' to 'float'. | +| test_specified.cpp:232:7:232:9 | l15 | Assignment between incompatible numeric types from 'const volatile int8_t &' to 'float'. | +| test_specified.cpp:245:7:245:8 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'const uint32_t'. | +| test_specified.cpp:246:7:246:8 | l2 | Assignment between incompatible numeric types from 'uint16_t' to 'const uint32_t'. | +| test_specified.cpp:247:7:247:8 | l3 | Assignment between incompatible numeric types from 'int8_t' to 'volatile int64_t'. | +| test_specified.cpp:248:7:248:8 | l1 | Assignment between incompatible numeric types from 'uint8_t' to 'const volatile uint16_t'. | +| test_specified.cpp:250:7:250:8 | l3 | Assignment between incompatible numeric types from 'int8_t' to 'const volatile uint16_t'. | +| test_specified.cpp:285:8:285:22 | g4 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test_specified.cpp:287:8:287:19 | s4 | Assignment between incompatible numeric types from 'uint16_t' to 'uint8_t'. | +| test_specified.cpp:292:9:292:23 | g5 | Assignment between incompatible numeric types from 'int8_t' to 'uint16_t'. | +| test_specified.cpp:294:9:294:20 | s5 | Assignment between incompatible numeric types from 'int8_t' to 'uint16_t'. | +| test_specified.cpp:308:23:308:25 | 300 | Assignment between incompatible numeric types from 'int' to 'unsigned char'. | +| test_specified.cpp:308:28:308:32 | 70000 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | +| test_specified.cpp:308:35:308:38 | 3000 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | +| test_specified.cpp:309:12:309:16 | 70000 | Assignment between incompatible numeric types from 'int' to 'unsigned short'. | +| test_specified.cpp:314:23:314:25 | u16 | Assignment between incompatible numeric types from 'uint16_t' to 'unsigned char'. | +| test_specified.cpp:314:28:314:30 | u32 | Assignment between incompatible numeric types from 'uint32_t' to 'unsigned short'. | +| test_specified.cpp:314:33:314:35 | s32 | Assignment between incompatible numeric types from 'int32_t' to 'short'. | +| test_specified.cpp:329:8:329:9 | l2 | Assignment between incompatible numeric types from 'CVColour' to 'uint8_t'. | +| test_specified.cpp:331:8:331:9 | l3 | Assignment between incompatible numeric types from 'CVColour' to 'uint8_t'. | +| test_specified.cpp:342:8:342:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test_specified.cpp:343:8:343:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test_specified.cpp:344:8:344:14 | ... + ... | Assignment between incompatible numeric types from 'int' to 'uint8_t'. | +| test_specified.cpp:351:9:351:12 | (...) | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | +| test_specified.cpp:352:9:352:12 | (...) | Assignment between incompatible numeric types from 'uint8_t' to 'uint32_t'. | +| test_specified.cpp:370:18:370:20 | 300 | Assignment between incompatible numeric types from 'int' to 'const uint8_t'. | +| test_specified.cpp:370:23:370:27 | 70000 | Assignment between incompatible numeric types from 'int' to 'volatile uint16_t'. | diff --git a/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.qlref b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.qlref new file mode 100644 index 0000000000..967340a41e --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-6/NumericAssignmentTypeMismatch.qlref @@ -0,0 +1 @@ +rules/RULE-7-0-6/NumericAssignmentTypeMismatch.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-0-6/test.cpp b/cpp/misra/test/rules/RULE-7-0-6/test.cpp new file mode 100644 index 0000000000..ae7310bc3d --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-6/test.cpp @@ -0,0 +1,627 @@ +#include +#include + +// Global variables for testing +std::uint32_t u32; +std::int32_t s32; +std::uint8_t u8; +std::int8_t s8; +std::uint16_t u16; +std::int16_t s16; +std::uint64_t u64; +std::int64_t s64; +float f; +double d; + +namespace TestNamespace { +std::uint8_t g1; +std::uint16_t g2; +} // namespace TestNamespace + +struct TestStruct { + std::uint8_t m1; + std::uint16_t m2; + static std::uint8_t s1; + static std::uint16_t s2; +}; + +std::uint8_t TestStruct::s1; +std::uint16_t TestStruct::s2; + +// Test basic constant assignments +void test_constant_assignments() { + u32 = 1; // COMPLIANT + s32 = 4u * 2u; // COMPLIANT + u8 = 3u; // COMPLIANT + u8 = 300u; // NON_COMPLIANT + f = 1; // COMPLIANT + f = 9999999999; // COMPLIANT + d = 0.0f; // NON_COMPLIANT + f = 0.0f; // COMPLIANT +} + +// Test signedness violations +void test_signedness_violations() { + u8 = s8; // NON_COMPLIANT + s8 = u8; // NON_COMPLIANT +} + +// Test size violations +void test_size_violations() { + u8 = u16; // NON_COMPLIANT + u16 = u64; // NON_COMPLIANT +} + +// Test type category violations +void test_type_category_violations() { + f = s32; // NON_COMPLIANT + s32 = f; // NON_COMPLIANT +} + +// Test widening of id-expressions +void test_widening_id_expressions() { + u32 = u8; // COMPLIANT - widening of id-expression + s64 = s8; // COMPLIANT - widening of id-expression + u64 = u16; // COMPLIANT - widening of id-expression +} + +// Test widening with namespace qualifiers (allowed) +void test_widening_namespace_qualified() { + u32 = TestNamespace::g1; // COMPLIANT - namespace qualified id-expression + u64 = TestNamespace::g2; // COMPLIANT - namespace qualified id-expression +} + +// Test widening with type qualifiers (allowed) +void test_widening_type_qualified() { + u32 = TestStruct::s1; // COMPLIANT - type qualified id-expression + u64 = TestStruct::s2; // COMPLIANT - type qualified id-expression +} + +// Test widening with decltype (allowed) +void test_widening_decltype_qualified() { + std::uint8_t l1 = 42; + std::uint16_t l2 = 42; + u32 = decltype(l1){}; // COMPLIANT - treated as a constant + u64 = decltype(l2){}; // COMPLIANT - treated as a constant + TestStruct l3; + u32 = decltype(l3)::s1; // COMPLIANT - decltype qualified + u64 = decltype(l3)::s2; // COMPLIANT - decltype qualified +} + +// Test widening with object member access (not allowed) +void test_widening_object_member_access() { + TestStruct l1; + TestStruct *l2 = &l1; + u32 = l1.m1; // NON_COMPLIANT - object member access, not id-expression + u64 = l1.m2; // NON_COMPLIANT - object member access, not id-expression + u32 = l2->m1; // NON_COMPLIANT - object member access, not id-expression + u64 = l2->m2; // NON_COMPLIANT - object member access, not id-expression +} + +// Test widening with expressions (not allowed) +void test_widening_expressions() { + u32 = u8 + 0; // NON_COMPLIANT - not id-expression + u32 = (u8); // NON_COMPLIANT - not id-expression (parenthesized) + u32 = static_cast(u8); // NON_COMPLIANT - not id-expression +} + +// Test expression results +void test_expression_results() { + u8 = u8 + u8; // NON_COMPLIANT + std::int16_t l1 = s8 + s8; // NON_COMPLIANT +} + +// Test bit-fields with various sizes around boundaries +struct S { + std::uint32_t m1 : 2; // 2-bit field + std::uint32_t m2 : 8; // 8-bit field (boundary) + std::uint32_t m3 : 9; // 9-bit field (boundary + 1) + std::uint32_t m4 : 16; // 16-bit field (boundary) + std::uint32_t m5 : 17; // 17-bit field (boundary + 1) + std::uint32_t m6 : 32; // 32-bit field (boundary) + std::uint64_t m7 : 33; // 33-bit field (boundary + 1) + std::int32_t m8 : 5; // Signed 5-bit field + std::int32_t m9 : 12; // Signed 12-bit field + std::int32_t m10 : 28; // Signed 28-bit field +}; + +void test_bitfields() { + S l1; + + // 2-bit field tests + l1.m1 = 2; // COMPLIANT - value fits in 2 bits + l1.m1 = 3; // COMPLIANT - value fits in 2 bits + l1.m1 = 4; // NON_COMPLIANT - constant does not fit in 2 bits + + l1.m1 = u8; // COMPLIANT - u8 is fine, not integer constant + l1.m1 = u16; // NON_COMPLIANT - narrowing from uint16_t + + // 8-bit boundary field tests + l1.m2 = 255; // COMPLIANT - value fits in uint8_t + l1.m2 = 256; // NON_COMPLIANT - value does not fit in unint8_t + l1.m2 = u8; // COMPLIANT - same width as uint8_t + l1.m2 = u16; // NON_COMPLIANT - narrowing from uint16_t + l1.m2 = u32; // NON_COMPLIANT - narrowing from uint32_t + + // 9-bit boundary + 1 field tests + l1.m3 = 511; // COMPLIANT + l1.m3 = 512; // NON_COMPLIANT - value does not fit in 9 bits + l1.m3 = 65535; // NON_COMPLIANT - value does not fit in 9 bits + l1.m3 = 65536; // NON_COMPLIANT - value does not fit in 9 bits + l1.m3 = u8; // COMPLIANT - widening from uint8_t to uint16_t + l1.m3 = u16; // COMPLIANT + l1.m3 = u32; // NON_COMPLIANT - narrowing from uint32_t + + // 16-bit boundary field tests + l1.m4 = 65535; // COMPLIANT - value fits in 16 bits + l1.m4 = 65536; // NON_COMPLIANT - value does not fit in 16 bits + l1.m4 = u8; // COMPLIANT - widening from uint8_t + l1.m4 = u16; // COMPLIANT - same width as uint16_t + l1.m4 = u32; // NON_COMPLIANT - narrowing from uint32_t + + // 17-bit boundary + 1 field tests + l1.m5 = 131071; // COMPLIANT - value fits in 17 bits + l1.m5 = 131072; // NON_COMPLIANT - value does not fit in 17 bits + l1.m5 = u8; // COMPLIANT - widening from uint8_t + l1.m5 = u16; // COMPLIANT - widening from uint16_t + l1.m5 = u32; // COMPLIANT + l1.m5 = u64; // NON_COMPLIANT - narrowing from uint64_t + + // 32-bit boundary field tests + l1.m6 = 4294967295U; // COMPLIANT - value fits in 32 bits + l1.m6 = 4294967296UL; // NON_COMPLIANT - value does not fit in 32 bits + l1.m6 = u8; // COMPLIANT - widening from uint8_t + l1.m6 = u16; // COMPLIANT - widening from uint16_t + l1.m6 = u32; // COMPLIANT - same width as uint32_t + l1.m6 = u64; // NON_COMPLIANT - narrowing from uint64_t + + // 33-bit boundary + 1 field tests + l1.m7 = 8589934591ULL; // COMPLIANT + l1.m7 = 8589934592L; // NON_COMPLIANT - value does not fit in 33 bits + l1.m7 = 8589934592ULL; // COMPLIANT - integer constant does not satisfy + // conditions, but the type matches the deduced type of + // the bitfield (unsigned long long), so is considered + // compliant by the rule(!) + l1.m7 = u8; // COMPLIANT - widening from uint8_t + l1.m7 = u16; // COMPLIANT - widening from uint16_t + l1.m7 = u32; // COMPLIANT - widening from uint32_t + l1.m7 = u64; // COMPLIANT - narrowing from uint64_t + + // Signed bitfield tests + l1.m8 = 15; // COMPLIANT + l1.m8 = -16; // COMPLIANT + l1.m8 = 16; // NON_COMPLIANT - value does not fit in signed 5-bit type + l1.m8 = -17; // NON_COMPLIANT - value does not fit in signed 5-bit type + l1.m8 = s8; // COMPLIANT - same width as int8_t + l1.m8 = s16; // NON_COMPLIANT - narrowing from int16_t + + l1.m9 = 2047; // COMPLIANT - value fits in signed 12-bit type + l1.m9 = -2048; // COMPLIANT - value fits in signed 12-bit type + l1.m9 = 2048; // NON_COMPLIANT - value does not fit in signed 12-bit type + l1.m9 = -2049; // NON_COMPLIANT - value does not fit in signed 12-bit type + l1.m9 = s8; // COMPLIANT - widening from int8_t + l1.m9 = s16; // COMPLIANT - same width as int16_t + l1.m9 = s32; // NON_COMPLIANT - narrowing from int32_t + + l1.m10 = 134217727; // COMPLIANT - value fits in signed 28-bit type + l1.m10 = -134217728; // COMPLIANT - value fits in signed 28-bit type + l1.m10 = 134217728LL; // NON_COMPLIANT - does not fit in signed 28-bit type + l1.m10 = -134217729LL; // NON_COMPLIANT - does not fit in signed 28-bit type + l1.m10 = s8; // COMPLIANT - widening from int8_t + l1.m10 = s16; // COMPLIANT - widening from int16_t + l1.m10 = s32; // COMPLIANT +} + +// Test enums +enum Colour : std::uint16_t { red, green, blue }; + +enum States { enabled, disabled }; + +void test_enums() { + Colour l1 = red; + u8 = red; // COMPLIANT + u32 = red; // COMPLIANT + u8 = l1; // NON_COMPLIANT + u32 = l1; // COMPLIANT + u8 = enabled; // COMPLIANT - enabled is not numeric +} + +// Test function parameters - non-overload-independent +void f1(std::int64_t l1) {} +void f2(std::int32_t l1) {} + +void test_function_parameters_non_overload_independent() { + f1(s32); // NON_COMPLIANT + f1(s64); // COMPLIANT + f2(s32); // COMPLIANT + f2(s64); // NON_COMPLIANT + int l1 = 42; + f2(l1); // NON_COMPLIANT - needs to be the same type as the parameter + signed int l2 = 42; + f2(l2); // COMPLIANT - int32_t is defined as `signed int` in this database +} + +// Test overloaded functions, but still non-overload-independent +// because they are "extensible" (i.e., they can be extended with new +// overloads). +void f3(std::int64_t l1) {} +void f3(std::int32_t l1) {} + +void test_overloaded_functions() { + f3(s32); // COMPLIANT + f3(s8); // NON_COMPLIANT + f3(s64); // COMPLIANT +} + +// Test function pointers - always "overload-independent" +void f4(long l1) {} + +void test_function_pointers() { + void (*l1)(long) = &f4; + f4(2); // NON_COMPLIANT + l1(2); // COMPLIANT +} + +// Test variadic functions +void f5(const char *l1, ...) {} + +void test_variadic_functions() { + f5("test", u8); // NON_COMPLIANT - will be promoted to `int` + f5("test", s32); // COMPLIANT - already `int`, no promotion needed +} + +struct A { + // first parameter to f6 with two arguments is overload-independent + void f6(std::size_t l1, int l2) {} + void f6(std::size_t l1, std::string l2) {} + // Different overload, so does not conflict with f6 above + void f6(std::int8_t l1, std::string l2, int x) {} + // Deleted function, ignored for overload independence calculations + void f6(float l1, float l2) = delete; + // Not overload-independent when called with one parameter + // Overload-independent when called with two parameters + void f7(float l1) {} + void f7(std::int32_t l1, int x = 1) {} + void f8(); +}; + +void A::f8() { + f6(u32, "answer"); // NON_COMPLIANT - extensible, could call a global + // function instead - e.g. `void f6(std::uint32_t l1, + // std::string l2)` + this->f6(u32, "answer"); // COMPLIANT + this->f6(u32, 42); // COMPLIANT + this->f7(s16); // NON_COMPLIANT - no widening as not overload-independent + this->f7(s16, 2); // COMPLIANT - overload-independent, only one target +} + +void test_member_function_overload_independent() { + A l1; + l1.f6(42, "answer"); // COMPLIANT + A *l2 = &l1; + l2->f6(42, "answer"); // COMPLIANT +} + +// Test member function calls - not overload-independent +struct B { + void f8(int l1, int l2) {} + void f8(long l1, std::string l2) {} +}; + +void test_member_function_not_overload_independent() { + B l1; + l1.f8(42, "answer"); // NON_COMPLIANT +} + +// Test constructor exception +struct MyInt { + explicit MyInt(std::int32_t l1) {} + MyInt(std::int32_t l1, std::int32_t l2) {} +}; + +void f9(MyInt l1) {} + +void test_constructor_exception() { + f9(MyInt{s8}); // COMPLIANT + MyInt l1{s8}; // COMPLIANT +} + +template struct D { + // Overload-independent - f10 parameters are always the same type + void f10(T l1, int l2) {} + void f10(T l1, std::string l2) {} + // Not overload-independent + template void f11(S1 l1, int l2) {} + template void f11(S2 l1, std::string l2) {} + void f11(std::int32_t l1, float f) {} +}; + +void test_template_functions() { + D l1; + l1.f10(u32, "X"); // COMPLIANT - can widen, because always same type + l1.f10(u32, 1); // COMPLIANT - can widen, because always same type + D l2; + l2.f10(u16, "X"); // COMPLIANT - can widen, because always same type + l2.f10(u16, 1); // COMPLIANT - can widen, because always same type + l1.f11(u32, "X"); // NON_COMPLIANT - not overload-independent + // and not the same type as the parameter + // so cannot widen - must be the same type + l1.f11(s32, 1); // COMPLIANT - same as specialized type + l1.f11(s32, 0.0f); // COMPLIANT - matches parameter type +} + +// Test initialization forms +std::int32_t f12(std::int8_t l1) { + std::int16_t l2 = l1; // COMPLIANT + std::int16_t l3{l1}; // COMPLIANT + std::int16_t l4(l1); // COMPLIANT + std::int16_t l5{l1 + l1}; // NON_COMPLIANT + return l1; // COMPLIANT +} + +std::int32_t test_return() { + return u32; // NON_COMPLIANT - wrong signedness +} + +// Test switch cases +void test_switch_cases() { + switch (s8) { + case 1: // COMPLIANT + break; + case 0x7F: // COMPLIANT + break; + case 0x80: // COMPLIANT - condition subject to promotion + break; + // Our extractor and supported compilers prohibit the below + // narrowing conversion. + // case 0xFFFF'FFFF'FFFF: + // break; + } +} + +// Test reference types - references to numeric types are considered numeric +void test_reference_types_basic() { + std::uint8_t l1 = 42; + std::uint32_t l2 = 100; + std::int8_t l3 = -5; + float l4 = 3.14f; + + std::uint8_t &l5 = l1; // COMPLIANT + std::uint32_t &l6 = l2; // COMPLIANT + std::int8_t &l7 = l3; // COMPLIANT + float &l8 = l4; // COMPLIANT + + // Reference types follow same rules as their referred types + u32 = l5; // COMPLIANT - widening of id-expression (reference) + u8 = l6; // NON_COMPLIANT - narrowing from reference + u8 = l7; // NON_COMPLIANT - different signedness from reference + s32 = l8; // NON_COMPLIANT - different type category from reference +} + +void test_reference_types_function_parameters() { + std::uint8_t l1 = 42; + std::uint16_t l2 = 1000; + + std::uint8_t &l3 = l1; + std::uint16_t &l4 = l2; + + // Function calls with reference arguments + f1(l3); // NON_COMPLIANT - widening conversion through reference + f2(l4); // NON_COMPLIANT - narrowing conversion through reference +} + +void test_reference_types_signedness() { + std::uint8_t l1 = 42; + std::int8_t l2 = -5; + + std::uint8_t &l3 = l1; + std::int8_t &l4 = l2; + + // Signedness violations through references + s8 = l3; // NON_COMPLIANT - different signedness through reference + u8 = l4; // NON_COMPLIANT - different signedness through reference +} + +void test_reference_types_floating_point() { + float l1 = 3.14f; + double l2 = 2.718; + std::int32_t l3 = 42; + + float &l4 = l1; + double &l5 = l2; + std::int32_t &l6 = l3; + + // Type category violations through references + s32 = l4; // NON_COMPLIANT - different type category through reference + f = l5; // NON_COMPLIANT - different size through reference + f = l6; // NON_COMPLIANT - different type category through reference +} + +void test_reference_types_expressions() { + std::uint8_t l1 = 42; + std::uint8_t l2 = 24; + + std::uint8_t &l3 = l1; + std::uint8_t &l4 = l2; + + // Expression results with references still follow expression rules + u8 = l3 + l4; // NON_COMPLIANT - addition promotes to int + s32 = l3 + l4; // COMPLIANT - promotion to int +} + +// Test reference parameters in functions +void f13(std::uint8_t &l1) {} +void f13(std::uint16_t &l1) {} + +void f14(std::uint32_t l1) {} + +void test_references_to_parameters() { + std::uint8_t l1 = 42; + std::uint16_t l2 = 1000; + + f13(l1); // COMPLIANT - not covered by rule, as pass-by-ref + f13(l2); // COMPLIANT - not covered by rule, as pass-by-ref + + std::uint16_t &l3 = l2; + f14(l3); // NON_COMPLIANT - must be the same type, as non-overload-independent + std::uint64_t l4 = 1000; + std::uint64_t &l5 = l4; + f14(l5); // NON_COMPLIANT - narrowing conversion through reference +} + +// Test compound assignments - rule does not apply to compound assignments +void test_compound_assignments() { + std::uint8_t l1 = 10; + std::uint16_t l2 = 100; + std::int8_t l3 = 5; + float l4 = 1.5f; + + l1 += l2; // COMPLIANT - compound assignment, rule does not apply + l1 -= l3; // COMPLIANT - compound assignment, rule does not apply + l2 *= l1; // COMPLIANT - compound assignment, rule does not apply + l2 /= l3; // COMPLIANT - compound assignment, rule does not apply + l1 %= l3; // COMPLIANT - compound assignment, rule does not apply + l2 &= l1; // COMPLIANT - compound assignment, rule does not apply + l2 |= l3; // COMPLIANT - compound assignment, rule does not apply + l2 ^= l1; // COMPLIANT - compound assignment, rule does not apply + l2 <<= 2; // COMPLIANT - compound assignment, rule does not apply + l2 >>= 1; // COMPLIANT - compound assignment, rule does not apply + l4 += l1; // COMPLIANT - compound assignment, rule does not apply + l4 -= s32; // COMPLIANT - compound assignment, rule does not apply +} + +// Test constructor field initializers +struct ConstructorTest { + std::uint8_t m1; + std::uint16_t m2; + std::uint32_t m3; + std::int8_t m4; + std::int16_t m5; + std::int32_t m6; + float m7; + double m8; + + // Constructor with various member initializer scenarios + ConstructorTest(std::uint8_t l1, std::uint16_t l2, std::int8_t l3) + : m1(l1), // COMPLIANT - same type + m2(l2), // COMPLIANT - same type + m3(l1), // COMPLIANT - widening of id-expression + m4(l3), // COMPLIANT - same type + m5(l3), // COMPLIANT - widening of id-expression + m6(l3), // COMPLIANT - widening of id-expression + m7(1.0f), // COMPLIANT - same type + m8(1.0) { // COMPLIANT - same type + } + + // Constructor with non-compliant initializers + ConstructorTest(std::uint32_t l1, std::int32_t l2, float l3) + : m1(l1), // NON_COMPLIANT - narrowing + m2(l1), // NON_COMPLIANT - narrowing + m3(l1), // COMPLIANT - same type + m4(l2), // NON_COMPLIANT - narrowing and different signedness + m5(l2), // NON_COMPLIANT - narrowing and different signedness + m6(l2), // COMPLIANT - same type + m7(l3), // COMPLIANT - same type + m8(l3) { // COMPLIANT - allowed to use float to initialize double + } + + // Constructor with constant initializers + ConstructorTest() + : m1(100), // COMPLIANT - constant fits + m2(65535), // COMPLIANT - constant fits + m3(4294967295U), // COMPLIANT - constant fits + m4(127), // COMPLIANT - constant fits + m5(32767), // COMPLIANT - constant fits + m6(2147483647), // COMPLIANT - constant fits + m7(3.14f), // COMPLIANT - same type constant + m8(2.718) { // COMPLIANT - same type constant + } + + // Constructor with non-compliant constant initializers + ConstructorTest(int) + : m1(300), // NON_COMPLIANT - constant too large + m2(70000), // NON_COMPLIANT - constant too large + m3(0x1'0000'0000ULL), // NON_COMPLIANT - constant too large + m4(200), // NON_COMPLIANT - constant too large + m5(40000), // NON_COMPLIANT - constant too large + m6(0x1'0000'0000LL), // NON_COMPLIANT - constant too large + m7(1.0), // NON_COMPLIANT - different size + m8(1.0f) { // NON_COMPLIANT - different size + } + + // Constructor with expression initializers + ConstructorTest(std::uint8_t l1, std::uint8_t l2, std::int8_t l3) + : m1(l1 + l2), // NON_COMPLIANT - expression result is int + m2(l1 + l2), // NON_COMPLIANT - expression result is int + m3(l1 + l2), // NON_COMPLIANT - expression result is int + m4(l3), // COMPLIANT - widening of id-expression + m5(l1), // NON_COMPLIANT - different signedness + m6(l1), // NON_COMPLIANT - different signedness + m7(l1), // NON_COMPLIANT - different type category + m8(l1) { // NON_COMPLIANT - different type category + } +}; + +void test_constructor_field_initializers() { + std::uint8_t l1 = 42; + std::uint16_t l2 = 1000; + std::int8_t l3 = 10; + + ConstructorTest l4(l1, l2, l3); // Test first constructor + ConstructorTest l5(u32, s32, f); // Test second constructor + ConstructorTest l6; // Test third constructor + ConstructorTest l7(0); // Test fourth constructor + ConstructorTest l8(l1, l1, l3); // Test fifth constructor +} + +// Test explicit casts +void test_explicit_casts() { + std::uint8_t l1 = 42; + std::uint16_t l2 = 1000; + std::int8_t l3 = -10; + std::int32_t l4 = -100; + float l5 = 3.14f; + double l6 = 2.718; + + // Explicit cast expressions are treated as expressions, not id-expressions + u8 = static_cast(l2); // COMPLIANT + u16 = static_cast(l1); // COMPLIANT + s8 = static_cast(l4); // COMPLIANT + s32 = static_cast(l3); // COMPLIANT + s32 = static_cast(l3); // NON_COMPLIANT + + // Type category conversions with explicit casts + f = static_cast(l4); // COMPLIANT + s32 = static_cast(l5); // COMPLIANT + + // Size conversions with explicit casts + d = static_cast(l5); // COMPLIANT + l5 = static_cast(l6); // COMPLIANT + + // C-style casts (also expressions) + u8 = (std::uint8_t)l2; // COMPLIANT + s8 = (std::int8_t)l4; // COMPLIANT + f = (float)l4; // COMPLIANT + + // Functional style casts (also expressions) + u8 = std::uint8_t(l2); // COMPLIANT + s8 = std::int8_t(l4); // COMPLIANT + f = float(l4); // COMPLIANT + + // Const_cast (creates expressions) + const std::uint8_t l7 = 100; + u8 = const_cast(l7); // COMPLIANT + + // Reinterpret_cast (creates expressions) + u32 = reinterpret_cast(l4); // COMPLIANT + + // Assignment to variables through explicit casts + std::uint32_t l8; + std::uint16_t l9; + l8 = static_cast(l9); // COMPLIANT + l9 = static_cast(l8); // COMPLIANT + + // Function calls with explicit casts + f1(static_cast(l4)); // COMPLIANT + f2(static_cast(l4)); // COMPLIANT +} \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-0-6/test_aggregate.cpp b/cpp/misra/test/rules/RULE-7-0-6/test_aggregate.cpp new file mode 100644 index 0000000000..7e215bc602 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-6/test_aggregate.cpp @@ -0,0 +1,111 @@ +#include + +std::uint32_t u32; +std::int32_t s32; +std::uint8_t u8; +std::int8_t s8; +std::uint16_t u16; +std::int16_t s16; +std::uint64_t u64; +std::int64_t s64; +float f; +double d; + +// Test aggregate initialization - struct with multiple members +struct SimpleAggregate { + std::uint8_t m1; + std::uint16_t m2; + std::int32_t m3; + float m4; +}; + +void test_aggregate_initialization_basic() { + // Compliant cases - exact types or constants that fit + SimpleAggregate l1{42, 1000, -50, 3.14f}; // COMPLIANT + SimpleAggregate l2{u8, u16, s32, f}; // COMPLIANT + SimpleAggregate l3{255, 65535, 2147483647, 0.0f}; // COMPLIANT + + // Non-compliant cases - type violations + SimpleAggregate l4{u16, u8, s32, // NON_COMPLIANT - narrowing u16 to uint8_t + f}; + SimpleAggregate l5{u8, u32, s32, // NON_COMPLIANT - narrowing u32 to uint16_t + f}; + SimpleAggregate l6{u8, u16, u32, f}; // NON_COMPLIANT - different signedness + SimpleAggregate l7{u8, u16, s32, + s32}; // NON_COMPLIANT - different type category + + // Constants that don't fit + SimpleAggregate l8{256, 65536, // NON_COMPLIANT - constants don't fit + 2147483648LL, // NON_COMPLIANT - constants don't fit + 0.0f}; + + // Widening of id-expressions is allowed + SimpleAggregate l9{u8, u8, s8, f}; // COMPLIANT - widening allowed +} + +// Test aggregate initialization - arrays +void test_aggregate_initialization_arrays() { + // Basic arrays + std::uint8_t l1[3]{10, 20, 30}; // COMPLIANT + std::uint8_t l2[3]{u8, u8, u8}; // COMPLIANT + std::uint8_t l3[3]{300, 400, 500}; // NON_COMPLIANT - constants don't fit + std::uint8_t l4[3]{s8, s8, s8}; // NON_COMPLIANT - signedness mismatch + std::uint8_t l5[3]{u16, u16, u16}; // NON_COMPLIANT - narrowing + + // Multi-dimensional arrays + std::int16_t l6[2][2]{{1, 2}, {3, 4}}; // COMPLIANT + std::int16_t l7[2][2]{{s8, s8}, {s8, s8}}; // COMPLIANT - widening allowed + std::int16_t l8[2][2]{{s32, s32}, {s32, s32}}; // NON_COMPLIANT - narrowing + std::int16_t l9[2][2]{{u8, u8}, // NON_COMPLIANT - signedness mismatch + {u8, u8}}; // NON_COMPLIANT - signedness mismatch +} + +// Test aggregate initialization - nested structs +struct NestedAggregate { + SimpleAggregate m1; + std::uint32_t m2; +}; + +void test_aggregate_initialization_nested() { + // Compliant nested initialization + NestedAggregate l1{{10, 100, -5, 1.0f}, 500}; // COMPLIANT + NestedAggregate l2{{u8, u16, s32, f}, u32}; // COMPLIANT + + // Non-compliant nested initialization + NestedAggregate l3{ + {u16, u8, s32, f}, // NON_COMPLIANT - narrowing in nested struct + u32}; + NestedAggregate l4{ + {u8, u16, s32, f}, + s32}; // NON_COMPLIANT - signedness mismatch in outer member +} + +// Test aggregate initialization - struct with bit-fields +struct BitfieldAggregate { + std::uint32_t m1 : 8; + std::uint32_t m2 : 16; + std::int32_t m3 : 12; +}; + +void test_aggregate_initialization_bitfields() { + // Compliant cases + BitfieldAggregate l1{100, 30000, -500}; // COMPLIANT + BitfieldAggregate l2{u8, u16, s16}; // COMPLIANT - appropriate sizes + + // Non-compliant cases + BitfieldAggregate l3{300, 70000, 5000}; // NON_COMPLIANT - constants don't fit + BitfieldAggregate l4{u16, u32, s32}; // NON_COMPLIANT - narrowing +} + +// Test aggregate initialization with designated initializers (C++20 feature, +// but test for basic cases) +void test_aggregate_initialization_designated() { + // Note: Designated initializers are C++20, but we can test basic aggregate + // init patterns + SimpleAggregate l1{.m1 = 10, .m2 = 100, .m3 = -5, .m4 = 1.0f}; // COMPLIANT + SimpleAggregate l2{.m1 = u8, .m2 = u16, .m3 = s32, .m4 = f}; // COMPLIANT + SimpleAggregate l3{.m1 = u16, // NON_COMPLIANT - type violation + .m2 = u8, + .m3 = s32, + .m4 = f}; +} \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-0-6/test_member_pointers.cpp b/cpp/misra/test/rules/RULE-7-0-6/test_member_pointers.cpp new file mode 100644 index 0000000000..d6f3de7eb8 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-6/test_member_pointers.cpp @@ -0,0 +1,128 @@ +#include + +std::int16_t s16; +std::uint16_t u16; +std::int32_t s32; +std::uint32_t u32; +std::int64_t s64; +std::uint64_t u64; + +struct MemberFunctionPointerTest { + void mf15(std::int32_t l1) {} + void mf15(std::uint32_t l1) {} + void mf16(std::int32_t l1) {} +}; + +void test_pointer_to_member_functions() { + MemberFunctionPointerTest l1; + MemberFunctionPointerTest *l6 = &l1; + + // mf15 is overload independent when used as a member function pointer + void (MemberFunctionPointerTest::*l2)(std::int32_t) = + &MemberFunctionPointerTest::mf15; + (l1.*l2)(s16); // COMPLIANT - widening of id-expression + (l1.*l2)(s32); // COMPLIANT - type match + (l1.*l2)(s64); // NON_COMPLIANT - narrowing + (l1.*l2)(u16); // NON_COMPLIANT - wrong sign + (l1.*l2)(u32); // NON_COMPLIANT - wrong sign + (l1.*l2)(u64); // NON_COMPLIANT - wrong sign and narrowing + + (l6->*l2)(s16); // COMPLIANT - is widening of id-expression + (l6->*l2)(s32); // COMPLIANT - type match + (l6->*l2)(s64); // NON_COMPLIANT - narrowing + (l6->*l2)(u16); // NON_COMPLIANT - wrong sign + (l6->*l2)(u32); // NON_COMPLIANT - wrong sign + (l6->*l2)(u64); // NON_COMPLIANT - wrong sign and narrowing + + // mf16 is overload independent when used as a member function pointer + void (MemberFunctionPointerTest::*l3)(std::int32_t) = + &MemberFunctionPointerTest::mf16; + (l1.*l3)(s16); // COMPLIANT - widening of id-expression + (l1.*l3)(s32); // COMPLIANT - type match + (l1.*l3)(s64); // NON_COMPLIANT - narrowing + (l1.*l3)(u16); // NON_COMPLIANT - wrong sign + (l1.*l3)(u32); // NON_COMPLIANT - wrong sign + (l1.*l3)(u64); // NON_COMPLIANT - wrong sign and narrowing + + (l6->*l3)(s16); // COMPLIANT - widening of id-expression + (l6->*l3)(s32); // COMPLIANT - type match + (l6->*l3)(s64); // NON_COMPLIANT - narrowing + (l6->*l3)(u16); // NON_COMPLIANT - wrong sign + (l6->*l3)(u32); // NON_COMPLIANT - wrong sign + (l6->*l3)(u64); // NON_COMPLIANT - wrong sign and narrowing + + // Direct calls for comparison + + // mf15 is not overload-independent, so it should only be compliant + // where an exact type of an overload is used + l1.mf15(s16); // NON_COMPLIANT - widening not allowed + l1.mf15(s32); // COMPLIANT - exact type match + l1.mf15(u16); // NON_COMPLIANT - widening not allowed + l1.mf15(u32); // COMPLIANT - exact type match + + // A qualified call to mf16 is overload-independent + l1.mf16(s16); // COMPLIANT - widening of id-expression + l1.mf16(s32); // COMPLIANT - exact type match + l1.mf16(u16); // NON_COMPLIANT + l1.mf16(u32); // NON_COMPLIANT +} + +// Test static member function pointers - should be overload-independent +struct StaticMemberFunctionPointerTest { + static void mf19(std::int32_t l1) {} + static void mf19(std::uint32_t l1) {} + static void mf20(std::int32_t l1) {} +}; + +void test_static_member_function_pointers() { + // Static member function pointers - overload-independent + void (*l1)(std::int32_t) = &StaticMemberFunctionPointerTest::mf19; + l1(s16); // COMPLIANT - widening of id-expression + l1(s32); // COMPLIANT - type match + l1(s64); // NON_COMPLIANT - narrowing + l1(u16); // NON_COMPLIANT - wrong sign + l1(u32); // NON_COMPLIANT - wrong sign + l1(u64); // NON_COMPLIANT - wrong sign and narrowing + + void (*l2)(std::int32_t) = &StaticMemberFunctionPointerTest::mf20; + l2(s16); // COMPLIANT - widening of id-expression + l2(s32); // COMPLIANT - type match + l2(s64); // NON_COMPLIANT - narrowing + l2(u16); // NON_COMPLIANT - wrong sign + l2(u32); // NON_COMPLIANT - wrong sign + l2(u64); // NON_COMPLIANT - wrong sign and narrowing + + // Direct calls for comparison - not overload-independent + StaticMemberFunctionPointerTest::mf19( + s16); // NON_COMPLIANT - widening not allowed + StaticMemberFunctionPointerTest::mf19(s32); // COMPLIANT - exact type match + StaticMemberFunctionPointerTest::mf19( + u16); // NON_COMPLIANT - widening not allowed + StaticMemberFunctionPointerTest::mf19(u32); // COMPLIANT - exact type match + + StaticMemberFunctionPointerTest::mf20( + s16); // COMPLIANT - widening of id-expression + StaticMemberFunctionPointerTest::mf20(s32); // COMPLIANT - exact type match + StaticMemberFunctionPointerTest::mf20(u16); // NON_COMPLIANT + StaticMemberFunctionPointerTest::mf20(u32); // NON_COMPLIANT +} + +// Test member data pointers - not function calls, but test assignment to them +struct MemberDataPointerTest { + std::int64_t m1; + std::int64_t m2 : 10; +}; + +void test_member_data_pointers() { + MemberDataPointerTest l1; + + // Member data pointer assignments - follow normal assignment rules + std::int64_t MemberDataPointerTest::*l2 = &MemberDataPointerTest::m1; + + l1.*l2 = s16; // COMPLIANT - widening conversion allowed + l1.*l2 = s32; // COMPLIANT - widening conversion allowed + l1.*l2 = s64; // COMPLIANT + l1.*l2 = u16; // NON_COMPLIANT - signedness violation + l1.*l2 = u32; // NON_COMPLIANT - different signedness/size + l1.*l2 = u64; // NON_COMPLIANT - different signedness +} \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-0-6/test_non_numeric.cpp b/cpp/misra/test/rules/RULE-7-0-6/test_non_numeric.cpp new file mode 100644 index 0000000000..3da5c21c81 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-6/test_non_numeric.cpp @@ -0,0 +1,138 @@ +#include +#include + +// Test non-numeric type categories - these should not trigger rule violations +void test_non_numeric_type_categories() { + // Character category types + char l1 = 'a'; + wchar_t l2 = L'b'; + char16_t l3 = u'c'; + char32_t l4 = U'd'; + + // Other category types + bool l5 = true; + void *l6 = nullptr; + std::nullptr_t l7 = nullptr; + + // Assignments between character types and numeric types + // Rule should not apply since source/target involve non-numeric types + std::uint8_t l8 = 42; + std::int32_t l9 = 100; + float l10 = 3.14f; + + // Character to numeric - rule does not apply + l8 = l1; // COMPLIANT - char is character category, not numeric + l9 = l2; // COMPLIANT - wchar_t is character category, not numeric + l8 = l3; // COMPLIANT - char16_t is character category, not numeric + l9 = l4; // COMPLIANT - char32_t is character category, not numeric + l10 = l1; // COMPLIANT - char is character category, not numeric + + // Numeric to character - rule does not apply + l1 = l8; // COMPLIANT - char is character category, not numeric + l2 = l9; // COMPLIANT - wchar_t is character category, not numeric + l3 = l8; // COMPLIANT - char16_t is character category, not numeric + l4 = l9; // COMPLIANT - char32_t is character category, not numeric + l1 = l10; // COMPLIANT - char is character category, not numeric + + // Other category to numeric - rule does not apply + l8 = l5; // COMPLIANT - bool is other category, not numeric + l9 = l5; // COMPLIANT - bool is other category, not numeric + l10 = l5; // COMPLIANT - bool is other category, not numeric + + // Numeric to other category - rule does not apply + l5 = l8; // COMPLIANT - bool is other category, not numeric + l5 = l9; // COMPLIANT - bool is other category, not numeric + l5 = l10; // COMPLIANT - bool is other category, not numeric + + // Character to character - rule does not apply + l1 = l2; // COMPLIANT - both character category, not numeric + l3 = l4; // COMPLIANT - both character category, not numeric + l1 = l3; // COMPLIANT - both character category, not numeric + + // Other to other - rule does not apply + std::nullptr_t l11 = l7; // COMPLIANT - both other category, not numeric + l6 = l7; // COMPLIANT - both other category, not numeric + + // Character to other - rule does not apply + l5 = l1; // COMPLIANT - neither is numeric category + l6 = nullptr; // COMPLIANT - neither is numeric category + + // Other to character - rule does not apply + l1 = l5; // COMPLIANT - neither is numeric category +} + +// Test function parameters with non-numeric types +void f15(char l1) {} +void f16(bool l1) {} +void f17(wchar_t l1) {} + +void test_non_numeric_function_parameters() { + std::uint8_t l1 = 42; + std::int32_t l2 = 100; + char l3 = 'x'; + bool l4 = true; + wchar_t l5 = L'y'; + + // Function calls with non-numeric parameters - rule does not apply + f15(l1); // COMPLIANT - parameter is character category, not numeric + f15(l2); // COMPLIANT - parameter is character category, not numeric + f15(l3); // COMPLIANT - parameter is character category, not numeric + + f16(l1); // COMPLIANT - parameter is other category, not numeric + f16(l2); // COMPLIANT - parameter is other category, not numeric + f16(l4); // COMPLIANT - parameter is other category, not numeric + + f17(l1); // COMPLIANT - parameter is character category, not numeric + f17(l2); // COMPLIANT - parameter is character category, not numeric + f17(l5); // COMPLIANT - parameter is character category, not numeric +} + +// Test references to non-numeric types +void test_non_numeric_references() { + char l1 = 'a'; + bool l2 = true; + wchar_t l3 = L'b'; + std::uint8_t l4 = 42; + std::int32_t l5 = 100; + + char &l6 = l1; + bool &l7 = l2; + wchar_t &l8 = l3; + + // Assignments involving references to non-numeric types - rule does not apply + l4 = l6; // COMPLIANT - reference to character category, not numeric + l5 = l7; // COMPLIANT - reference to other category, not numeric + l4 = l8; // COMPLIANT - reference to character category, not numeric + + l6 = l4; // COMPLIANT - reference to character category, not numeric + l7 = l5; // COMPLIANT - reference to other category, not numeric + l8 = l4; // COMPLIANT - reference to character category, not numeric +} + +// Test bit-fields with non-numeric types (though these are rare in practice) +struct NonNumericBitFields { + bool m1 : 1; // Other category + char m2 : 7; // Character category + wchar_t m3 : 16; // Character category +}; + +void test_non_numeric_bitfields() { + NonNumericBitFields l1; + std::uint8_t l2 = 42; + std::int32_t l3 = 100; + bool l4 = true; + char l5 = 'x'; + + // Assignments to/from non-numeric bit-fields - rule does not apply + l1.m1 = l2; // COMPLIANT - bit-field is other category, not numeric + l1.m1 = l4; // COMPLIANT - bit-field is other category, not numeric + l1.m2 = l2; // COMPLIANT - bit-field is character category, not numeric + l1.m2 = l5; // COMPLIANT - bit-field is character category, not numeric + l1.m3 = l3; // COMPLIANT - bit-field is character category, not numeric + + l2 = l1.m1; // COMPLIANT - bit-field is other category, not numeric + l4 = l1.m1; // COMPLIANT - bit-field is other category, not numeric + l2 = l1.m2; // COMPLIANT - bit-field is character category, not numeric + l5 = l1.m2; // COMPLIANT - bit-field is character category, not numeric + l3 = l1.m3; // COMPLIANT - bit-field is character category, not numeric +} \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-0-6/test_operators.cpp b/cpp/misra/test/rules/RULE-7-0-6/test_operators.cpp new file mode 100644 index 0000000000..f8823bfa03 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-6/test_operators.cpp @@ -0,0 +1,295 @@ +#include + +// Test user-defined operators - always non-extensible +struct UserDefinedOperators { + UserDefinedOperators(std::int32_t l1) {} + + // Binary operators + UserDefinedOperators operator+(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator-(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator*(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator/(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator%(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator&(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator|(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator^(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator<<(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + UserDefinedOperators operator>>(std::int32_t l1) const { + return UserDefinedOperators{0}; + } + + // Comparison operators + bool operator==(std::int32_t l1) const { return true; } + bool operator!=(std::int32_t l1) const { return false; } + bool operator<(std::int32_t l1) const { return false; } + bool operator<=(std::int32_t l1) const { return false; } + bool operator>(std::int32_t l1) const { return false; } + bool operator>=(std::int32_t l1) const { return false; } + + // Subscript operator + std::int32_t operator[](std::int32_t l1) const { return 0; } + + // Function call operator + std::int32_t operator()(std::int32_t l1) const { return 0; } + std::int32_t operator()(std::int32_t l1, std::int32_t l2) const { return 0; } + + // Assignment operators + UserDefinedOperators &operator=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator+=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator-=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator*=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator/=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator%=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator&=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator|=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator^=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator<<=(std::int32_t l1) { return *this; } + UserDefinedOperators &operator>>=(std::int32_t l1) { return *this; } + + // Increment/decrement operators + UserDefinedOperators &operator++() { return *this; } + UserDefinedOperators operator++(int) { return UserDefinedOperators{0}; } + UserDefinedOperators &operator--() { return *this; } + UserDefinedOperators operator--(int) { return UserDefinedOperators{0}; } +}; + +// Global user-defined operators +UserDefinedOperators operator+(std::int32_t l1, + const UserDefinedOperators &l2) { + return UserDefinedOperators{0}; +} + +UserDefinedOperators operator-(std::int32_t l1, + const UserDefinedOperators &l2) { + return UserDefinedOperators{0}; +} + +bool operator==(std::int32_t l1, const UserDefinedOperators &l2) { + return true; +} + +void test_user_defined_operators() { + UserDefinedOperators l1{42}; + std::int32_t l2 = 10; + std::int16_t l3 = 5; + std::int64_t l4 = 100; + std::uint32_t l5 = 20; + + // Member operators - non-extensible, exact type match required + l1 + l2; // COMPLIANT - exact type match + l1 + l3; // COMPLIANT - widening conversion is allowed + l1 + l4; // NON_COMPLIANT - different type + l1 + l5; // NON_COMPLIANT - different signedness + + l1 - l2; // COMPLIANT - exact type match + l1 - l3; // COMPLIANT - widening conversion is allowed + l1 - l4; // NON_COMPLIANT - different type + l1 - l5; // NON_COMPLIANT - different signedness + + l1 *l2; // COMPLIANT - exact type match + l1 *l3; // COMPLIANT - widening conversion is allowed + l1 *l4; // NON_COMPLIANT - different type + l1 *l5; // NON_COMPLIANT - different signedness + + l1 / l2; // COMPLIANT - exact type match + l1 / l3; // COMPLIANT - widening conversion is allowed + l1 / l4; // NON_COMPLIANT - different type + l1 / l5; // NON_COMPLIANT - different signedness + + l1 % l2; // COMPLIANT - exact type match + l1 % l3; // COMPLIANT - widening conversion is allowed + l1 % l4; // NON_COMPLIANT - different type + l1 % l5; // NON_COMPLIANT - different signedness + + l1 & l2; // COMPLIANT - exact type match + l1 & l3; // COMPLIANT - widening conversion is allowed + l1 & l4; // NON_COMPLIANT - different type + l1 & l5; // NON_COMPLIANT - different signedness + + l1 | l2; // COMPLIANT - exact type match + l1 | l3; // COMPLIANT - widening conversion is allowed + l1 | l4; // NON_COMPLIANT - different type + l1 | l5; // NON_COMPLIANT - different signedness + + l1 ^ l2; // COMPLIANT - exact type match + l1 ^ l3; // COMPLIANT - widening conversion is allowed + l1 ^ l4; // NON_COMPLIANT - different type + l1 ^ l5; // NON_COMPLIANT - different signedness + + l1 << l2; // COMPLIANT - exact type match + l1 << l3; // COMPLIANT - widening conversion is allowed + l1 << l4; // NON_COMPLIANT - different type + l1 << l5; // NON_COMPLIANT - different signedness + + l1 >> l2; // COMPLIANT - exact type match + l1 >> l3; // COMPLIANT - widening conversion is allowed + l1 >> l4; // NON_COMPLIANT - different type + l1 >> l5; // NON_COMPLIANT - different signedness + + // Comparison operators + l1 == l2; // COMPLIANT - exact type match + l1 == l3; // COMPLIANT - widening conversion is allowed + l1 == l4; // NON_COMPLIANT - different type + l1 == l5; // NON_COMPLIANT - different signedness + + l1 != l2; // COMPLIANT - exact type match + l1 != l3; // COMPLIANT - widening conversion is allowed + l1 != l4; // NON_COMPLIANT - different type + l1 != l5; // NON_COMPLIANT - different signedness + + l1 < l2; // COMPLIANT - exact type match + l1 < l3; // COMPLIANT - widening conversion is allowed + l1 < l4; // NON_COMPLIANT - different type + l1 < l5; // NON_COMPLIANT - different signedness + + l1 <= l2; // COMPLIANT - exact type match + l1 <= l3; // COMPLIANT - widening conversion is allowed + l1 <= l4; // NON_COMPLIANT - different type + l1 <= l5; // NON_COMPLIANT + + l1 > l2; // COMPLIANT - exact type match + l1 > l3; // COMPLIANT - widening conversion is allowed + l1 > l4; // NON_COMPLIANT + l1 > l5; // NON_COMPLIANT - different signedness + + l1 >= l2; // COMPLIANT - exact type match + l1 >= l3; // COMPLIANT - widening conversion is allowed + l1 >= l4; // NON_COMPLIANT + l1 >= l5; // NON_COMPLIANT - different signedness + + // Subscript operator + l1[l2]; // COMPLIANT - exact type match + l1[l3]; // COMPLIANT - widening conversion is allowed + l1[l4]; // NON_COMPLIANT - different type + l1[l5]; // NON_COMPLIANT - different signedness + + // Function call operator + l1(l2); // COMPLIANT - exact type match + l1(l3); // COMPLIANT - widening conversion is allowed + l1(l4); // NON_COMPLIANT - different type + l1(l5); // NON_COMPLIANT - different signedness + l1(l2, l2); // COMPLIANT - both exact type match + l1(l2, l4); // NON_COMPLIANT - second parameter different type + l1(l4, l2); // NON_COMPLIANT - first parameter different type + l1(l4, l5); // NON_COMPLIANT - both parameters different type + + // The presence of a default copy constructor for UserDefinedOperators means + // that assignments through operator= must be exact type matches. + l1 = l2; // COMPLIANT - exact type match + l1 = l3; // NON_COMPLIANT + l1 = l4; // NON_COMPLIANT + l1 = l5; // NON_COMPLIANT + + l1 += l2; // COMPLIANT - exact type match + l1 += l3; // COMPLIANT - widening conversion is allowed + l1 += l4; // NON_COMPLIANT - different type + l1 += l5; // NON_COMPLIANT - different signedness + + l1 -= l2; // COMPLIANT - exact type match + l1 -= l3; // COMPLIANT - widening conversion is allowed + l1 -= l4; // NON_COMPLIANT - different type + l1 -= l5; // NON_COMPLIANT - different signedness + + l1 *= l2; // COMPLIANT - exact type match + l1 *= l3; // COMPLIANT - widening conversion is allowed + l1 *= l4; // NON_COMPLIANT - different type + l1 *= l5; // NON_COMPLIANT - different signedness + + l1 /= l2; // COMPLIANT - exact type match + l1 /= l3; // COMPLIANT - widening conversion is allowed + l1 /= l4; // NON_COMPLIANT - different type + l1 /= l5; // NON_COMPLIANT - different signedness + + l1 %= l2; // COMPLIANT - exact type match + l1 %= l3; // COMPLIANT - widening conversion is allowed + l1 %= l4; // NON_COMPLIANT - different type + l1 %= l5; // NON_COMPLIANT - different signedness + + l1 &= l2; // COMPLIANT - exact type match + l1 &= l3; // COMPLIANT - widening conversion is allowed + l1 &= l4; // NON_COMPLIANT - different type + l1 &= l5; // NON_COMPLIANT - different signedness + + l1 |= l2; // COMPLIANT - exact type match + l1 |= l3; // COMPLIANT - widening conversion is allowed + l1 |= l4; // NON_COMPLIANT - different type + l1 |= l5; // NON_COMPLIANT - different signedness + + l1 ^= l2; // COMPLIANT - exact type match + l1 ^= l3; // COMPLIANT - widening conversion is allowed + l1 ^= l4; // NON_COMPLIANT - different type + l1 ^= l5; // NON_COMPLIANT - different signedness + + l1 <<= l2; // COMPLIANT - exact type match + l1 <<= l3; // COMPLIANT - widening conversion is allowed + l1 <<= l4; // NON_COMPLIANT - different type + l1 <<= l5; // NON_COMPLIANT - different signedness + + l1 >>= l2; // COMPLIANT - exact type match + l1 >>= l3; // COMPLIANT - widening conversion is allowed + l1 >>= l4; // NON_COMPLIANT - different type + l1 >>= l5; // NON_COMPLIANT - different signedness + + // Global operators + l2 + l1; // COMPLIANT - exact type match + l3 + l1; // COMPLIANT - widening conversion is allowed + l4 + l1; // NON_COMPLIANT - different type + l5 + l1; // NON_COMPLIANT - different signedness + + l2 - l1; // COMPLIANT - exact type match + l3 - l1; // COMPLIANT - widening conversion is allowed + l4 - l1; // NON_COMPLIANT - different type + l5 - l1; // NON_COMPLIANT - different signedness + + l2 == l1; // COMPLIANT - exact type match + l3 == l1; // COMPLIANT - widening conversion is allowed + l4 == l1; // NON_COMPLIANT - different type + l5 == l1; // NON_COMPLIANT - different signedness +} + +// Test user-defined operators with constants +void test_user_defined_operators_constants() { + UserDefinedOperators l1{42}; + + // Constants with exact type match + l1 + 42; // COMPLIANT + l1 + 42L; // COMPLIANT + l1 + 42LL; // COMPLIANT + l1 + 42U; // COMPLIANT + l1 + 42.0f; // NON_COMPLIANT - float constant + + l1 == 42; // COMPLIANT - integer constant is int/int32_t + l1 == 42L; // COMPLIANT - long constant + l1 == 42LL; // COMPLIANT - long long constant + l1 == 42U; // COMPLIANT - unsigned constant + + l1[42]; // COMPLIANT - integer constant is int/int32_t + l1[42L]; // COMPLIANT - long constant + l1[42LL]; // COMPLIANT - long long constant + l1[42U]; // COMPLIANT - unsigned constant + + // The presence of a default copy constructor for UserDefinedOperators means + // that assignments through operator= must be exact type matches. + l1 = 42; // COMPLIANT - integer constant is int/int32_t + l1 = 42L; // NON_COMPLIANT + l1 = 42LL; // NON_COMPLIANT + l1 = 42U; // NON_COMPLIANT +} \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-0-6/test_specified.cpp b/cpp/misra/test/rules/RULE-7-0-6/test_specified.cpp new file mode 100644 index 0000000000..dc58ade311 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-6/test_specified.cpp @@ -0,0 +1,371 @@ +#include +#include + +// Global variables for testing +std::uint32_t u32; +std::int32_t s32; +std::uint8_t u8; +std::int8_t s8; +std::uint16_t u16; +std::int16_t s16; +std::uint64_t u64; +std::int64_t s64; +float f; +double d; + +// Test cv-qualified types +void test_cv_qualified_const() { + const std::uint8_t l1 = 42; // COMPLIANT + const std::uint16_t l2 = 1000; // COMPLIANT + const std::uint32_t l3 = 50000; // COMPLIANT + const std::int8_t l4 = -5; // COMPLIANT + const std::int16_t l5 = -1000; // COMPLIANT + const std::int32_t l6 = -50000; // COMPLIANT + const float l7 = 3.14f; // COMPLIANT + const double l8 = 2.718; // COMPLIANT + + // Widening of const id-expressions - allowed + // Also allowed because the integer constant expressions are within the range + u16 = l1; // COMPLIANT + u32 = l1; // COMPLIANT + u32 = l2; // COMPLIANT + s16 = l4; // COMPLIANT + s32 = l4; // COMPLIANT + s32 = l5; // COMPLIANT + + // Narrowing of const id-expressions - not allowed + u8 = l2; // NON_COMPLIANT + u8 = l3; // NON_COMPLIANT + // Permitted because the integer constant expression is within the range + u16 = l3; // COMPLIANT + s8 = l5; // NON_COMPLIANT + s8 = l6; // NON_COMPLIANT + s16 = l6; // NON_COMPLIANT + + // Incorrect signedness conversions, and the integer constant + // expressions are not in the range of the target type (as they are all + // negative) + u8 = l4; // NON_COMPLIANT + u16 = l5; // NON_COMPLIANT + u32 = l6; // NON_COMPLIANT + // These are signedness violations, but as they are integer constant + // expressions within range they are allowed + s8 = l1; // COMPLIANT + s16 = l2; // COMPLIANT + s32 = l3; // COMPLIANT + + // Type category errors (int to float) + s32 = l7; // NON_COMPLIANT + s32 = l8; // NON_COMPLIANT + // These are not type category errors, as they are integer constant + // expressions whose value fits within both floating point types, as the range + // is infinite + f = l4; // COMPLIANT + f = l5; // COMPLIANT + f = l6; // COMPLIANT + d = l4; // COMPLIANT + d = l5; // COMPLIANT + d = l6; // COMPLIANT + + // Size violations within same type category with const + f = l8; // NON_COMPLIANT + d = l7; // COMPLIANT +} + +void test_cv_qualified_volatile() { + volatile std::uint8_t l1 = 42; + volatile std::uint16_t l2 = 1000; + volatile std::uint32_t l3 = 50000; + volatile std::int8_t l4 = -5; + volatile std::int16_t l5 = -1000; + volatile std::int32_t l6 = -50000; + volatile float l7 = 3.14f; + volatile double l8 = 2.718; + + // Widening of volatile id-expressions - allowed + u16 = l1; // COMPLIANT + u32 = l1; // COMPLIANT + u32 = l2; // COMPLIANT + s16 = l4; // COMPLIANT + s32 = l4; // COMPLIANT + s32 = l5; // COMPLIANT + + // Narrowing of volatile id-expressions - not allowed + u8 = l2; // NON_COMPLIANT + u8 = l3; // NON_COMPLIANT + u16 = l3; // NON_COMPLIANT + s8 = l5; // NON_COMPLIANT + s8 = l6; // NON_COMPLIANT + s16 = l6; // NON_COMPLIANT + + // Signedness violations with volatile + u8 = l4; // NON_COMPLIANT + u16 = l5; // NON_COMPLIANT + u32 = l6; // NON_COMPLIANT + s8 = l1; // NON_COMPLIANT + s16 = l2; // NON_COMPLIANT + s32 = l3; // NON_COMPLIANT + + // Type category violations with volatile + s32 = l7; // NON_COMPLIANT + s32 = l8; // NON_COMPLIANT + f = l4; // NON_COMPLIANT + f = l5; // NON_COMPLIANT + f = l6; // NON_COMPLIANT + d = l4; // NON_COMPLIANT + d = l5; // NON_COMPLIANT + d = l6; // NON_COMPLIANT + + // Size violations within same type category with volatile + f = l8; // NON_COMPLIANT + d = l7; // COMPLIANT +} + +void test_cv_qualified_const_volatile() { + const volatile std::uint8_t l1 = 42; + const volatile std::uint16_t l2 = 1000; + const volatile std::uint32_t l3 = 50000; + const volatile std::int8_t l4 = -5; + const volatile std::int16_t l5 = -1000; + const volatile std::int32_t l6 = -50000; + const volatile float l7 = 3.14f; + const volatile double l8 = 2.718; + + // Widening of const volatile id-expressions - allowed + u16 = l1; // COMPLIANT + u32 = l1; // COMPLIANT + u32 = l2; // COMPLIANT + s16 = l4; // COMPLIANT + s32 = l4; // COMPLIANT + s32 = l5; // COMPLIANT + + // Narrowing of const volatile id-expressions - not allowed + u8 = l2; // NON_COMPLIANT + u8 = l3; // NON_COMPLIANT + u16 = l3; // NON_COMPLIANT + s8 = l5; // NON_COMPLIANT + s8 = l6; // NON_COMPLIANT + s16 = l6; // NON_COMPLIANT + + // Signedness violations with const volatile + u8 = l4; // NON_COMPLIANT + u16 = l5; // NON_COMPLIANT + u32 = l6; // NON_COMPLIANT + s8 = l1; // NON_COMPLIANT + s16 = l2; // NON_COMPLIANT + s32 = l3; // NON_COMPLIANT + + // Type category violations with const volatile + s32 = l7; // NON_COMPLIANT + s32 = l8; // NON_COMPLIANT + f = l4; // NON_COMPLIANT + f = l5; // NON_COMPLIANT + f = l6; // NON_COMPLIANT + d = l4; // NON_COMPLIANT + d = l5; // NON_COMPLIANT + d = l6; // NON_COMPLIANT + + // Size violations within same type category with const volatile + f = l8; // NON_COMPLIANT + d = l7; // COMPLIANT +} + +// Test cv-qualified references +void test_cv_qualified_references() { + std::uint8_t l1 = 42; + std::uint16_t l2 = 1000; + std::int8_t l3 = -5; + float l4 = 3.14f; + + const std::uint8_t &l5 = l1; + const std::uint16_t &l6 = l2; + const std::int8_t &l7 = l3; + const float &l8 = l4; + + volatile std::uint8_t &l9 = l1; + volatile std::uint16_t &l10 = l2; + volatile std::int8_t &l11 = l3; + volatile float &l12 = l4; + + const volatile std::uint8_t &l13 = l1; + const volatile std::uint16_t &l14 = l2; + const volatile std::int8_t &l15 = l3; + const volatile float &l16 = l4; + + // Widening through cv-qualified references - allowed + u16 = l5; // COMPLIANT + u32 = l5; // COMPLIANT + u32 = l6; // COMPLIANT + s16 = l7; // COMPLIANT + s32 = l7; // COMPLIANT + u16 = l9; // COMPLIANT + u32 = l9; // COMPLIANT + u32 = l10; // COMPLIANT + s16 = l11; // COMPLIANT + s32 = l11; // COMPLIANT + u16 = l13; // COMPLIANT + u32 = l13; // COMPLIANT + u32 = l14; // COMPLIANT + s16 = l15; // COMPLIANT + s32 = l15; // COMPLIANT + + // Narrowing through cv-qualified references - not allowed + u8 = l6; // NON_COMPLIANT + u8 = l10; // NON_COMPLIANT + u8 = l14; // NON_COMPLIANT + + // Signedness violations through cv-qualified references + s8 = l5; // NON_COMPLIANT + u8 = l7; // NON_COMPLIANT + s8 = l9; // NON_COMPLIANT + u8 = l11; // NON_COMPLIANT + s8 = l13; // NON_COMPLIANT + u8 = l15; // NON_COMPLIANT + + // Type category violations through cv-qualified references + s32 = l8; // NON_COMPLIANT + s32 = l12; // NON_COMPLIANT + s32 = l16; // NON_COMPLIANT + f = l3; // NON_COMPLIANT + f = l7; // NON_COMPLIANT + f = l11; // NON_COMPLIANT + f = l15; // NON_COMPLIANT +} + +// Test cv-qualified function parameters +void f15(const std::uint32_t l1) {} +void f16(volatile std::int64_t l1) {} +void f17(const volatile std::uint16_t l1) {} + +void test_cv_qualified_function_parameters() { + const std::uint8_t l1 = 42; + volatile std::uint16_t l2 = 1000; + const volatile std::int8_t l3 = -5; + + f15(l1); // NON_COMPLIANT + f15(l2); // NON_COMPLIANT + f16(l3); // NON_COMPLIANT + f17(l1); // NON_COMPLIANT + f17(l2); // COMPLIANT + f17(l3); // NON_COMPLIANT +} + +// Test cv-qualified static and namespace variables +namespace CVNamespace { +const std::uint8_t g3 = 42; +volatile std::uint16_t g4 = 1000; +const volatile std::int8_t g5 = -5; +} // namespace CVNamespace + +struct CVStruct { + static const std::uint8_t s3; + static volatile std::uint16_t s4; + static const volatile std::int8_t s5; +}; + +const std::uint8_t CVStruct::s3 = 42; +volatile std::uint16_t CVStruct::s4 = 1000; +const volatile std::int8_t CVStruct::s5 = -5; + +void test_cv_qualified_static_and_namespace() { + // Widening of cv-qualified namespace and static variables - allowed + u16 = CVNamespace::g3; // COMPLIANT + u32 = CVNamespace::g3; // COMPLIANT + u32 = CVNamespace::g4; // COMPLIANT + s16 = CVNamespace::g5; // COMPLIANT + s32 = CVNamespace::g5; // COMPLIANT + + u16 = CVStruct::s3; // COMPLIANT + u32 = CVStruct::s3; // COMPLIANT + u32 = CVStruct::s4; // COMPLIANT + s16 = CVStruct::s5; // COMPLIANT + s32 = CVStruct::s5; // COMPLIANT + + // Narrowing of cv-qualified namespace and static variables - not allowed + u8 = CVNamespace::g4; // NON_COMPLIANT + s8 = CVNamespace::g5; // COMPLIANT - constant fits + u8 = CVStruct::s4; // NON_COMPLIANT + s8 = CVStruct::s5; // COMPLIANT - constant fits + + // Signedness violations with cv-qualified namespace and static variables + s8 = CVNamespace::g3; // COMPLIANT - constant expression + u16 = CVNamespace::g5; // NON_COMPLIANT + s8 = CVStruct::s3; // COMPLIANT - constant expression + u16 = CVStruct::s5; // NON_COMPLIANT +} + +// Test cv-qualified bitfields +struct CVBitfieldStruct { + const std::uint32_t m11 : 8; + volatile std::uint32_t m12 : 16; + const volatile std::int32_t m13 : 12; +}; + +void test_cv_qualified_bitfields() { + CVBitfieldStruct l1{100, 30000, -500}; // COMPLIANT + + // CV-qualified bitfields follow same rules as regular bitfields + CVBitfieldStruct l2{300, 70000, 3000}; // NON_COMPLIANT + l2.m12 = 70000; // NON_COMPLIANT + + CVBitfieldStruct l3{u8, u16, s16}; // COMPLIANT + l1.m12 = u16; // COMPLIANT + + CVBitfieldStruct l4{u16, u32, s32}; // NON_COMPLIANT +} + +// Test cv-qualified enums +enum CVColour : std::uint16_t { cv_red, cv_green, cv_blue }; + +void test_cv_qualified_enums() { + const CVColour l1 = cv_red; + volatile CVColour l2 = cv_green; + const volatile CVColour l3 = cv_blue; + + u8 = cv_red; // COMPLIANT + u32 = cv_red; // COMPLIANT + u8 = l1; // COMPLIANT - constant fits in uint8_t + u32 = l1; // COMPLIANT + u8 = l2; // NON_COMPLIANT + u32 = l2; // COMPLIANT + u8 = l3; // NON_COMPLIANT + u32 = l3; // COMPLIANT +} + +// Test cv-qualified expressions with operators +void test_cv_qualified_expressions() { + const std::uint8_t l1 = 10; + volatile std::uint8_t l2 = 20; + const volatile std::uint8_t l3 = 30; + + // Expressions with cv-qualified operands still follow expression rules + u8 = l1 + l2; // NON_COMPLIANT + u8 = l1 + l3; // NON_COMPLIANT + u8 = l2 + l3; // NON_COMPLIANT + s32 = l1 + l2; // COMPLIANT + s32 = l1 + l3; // COMPLIANT + s32 = l2 + l3; // COMPLIANT + + // Parenthesized cv-qualified expressions are not id-expressions + u32 = (l1); // COMPLIANT - constant expression fits + u32 = (l2); // NON_COMPLIANT + u32 = (l3); // NON_COMPLIANT +} + +// Test cv-qualified aggregate initialization +struct CVAggregate { + const std::uint8_t m1; + volatile std::uint16_t m2; + const volatile std::int32_t m3; +}; + +void test_cv_qualified_aggregate_initialization() { + const std::uint8_t l1 = 42; + volatile std::uint16_t l2 = 1000; + const volatile std::int8_t l3 = -5; + + // CV-qualified aggregate members follow same rules + CVAggregate l4{10, 100, -50}; // COMPLIANT + CVAggregate l5{l1, l2, l3}; // COMPLIANT + CVAggregate l6{300, 70000, l3}; // NON_COMPLIANT +} \ No newline at end of file