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..393f0cbdf5 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Conversions.qll @@ -0,0 +1,129 @@ +//** 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 + TInappropriateBitwiseOrShiftOperandsQuery() or + TNoSignednessChangeFromPromotionQuery() or + TNumericAssignmentTypeMismatchQuery() or + TFunctionPointerConversionContextQuery() + +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 `inappropriateBitwiseOrShiftOperands` query + ConversionsPackage::inappropriateBitwiseOrShiftOperandsQuery() and + queryId = + // `@id` for the `inappropriateBitwiseOrShiftOperands` query + "cpp/misra/inappropriate-bitwise-or-shift-operands" and + ruleId = "RULE-7-0-4" 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" +} + +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 inappropriateBitwiseOrShiftOperandsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `inappropriateBitwiseOrShiftOperands` query + TQueryCPP(TConversionsPackageQuery(TInappropriateBitwiseOrShiftOperandsQuery())) + } + + 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())) + } +} 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..dba3af7a4a 100644 --- a/cpp/common/src/codingstandards/cpp/types/Type.qll +++ b/cpp/common/src/codingstandards/cpp/types/Type.qll @@ -94,3 +94,51 @@ 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() + ) + ) +} + +/** + * The size of the smallest `int` type in the database in bytes. + */ +int sizeOfInt() { + // The size of int is implementation-defined + result = + min(int size | + size = any(IntType i | i.isSigned()).getCanonicalArithmeticType().getSize() + | + size + ) +} + +/** + * 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/common/test/includes/standard-library/clocale b/cpp/common/test/includes/standard-library/clocale index 430c36daa0..8ec16234b8 100644 --- a/cpp/common/test/includes/standard-library/clocale +++ b/cpp/common/test/includes/standard-library/clocale @@ -1,4 +1,5 @@ -#pragma once +#ifndef _GHLIBCPP_CLOCALE +#define _GHLIBCPP_CLOCALE #define NULL 0 #define LC_ALL 0 @@ -15,3 +16,5 @@ using ::lconv; using ::localeconv; using ::setlocale; } // namespace std + +#endif // _GHLIBCPP_CLOCALE \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/cwchar b/cpp/common/test/includes/standard-library/cwchar index e69de29bb2..cba1ea7248 100644 --- a/cpp/common/test/includes/standard-library/cwchar +++ b/cpp/common/test/includes/standard-library/cwchar @@ -0,0 +1,23 @@ +#ifndef _GHLIBCPP_CWCHAR +#define _GHLIBCPP_CWCHAR + +#include "stddef.h" + +namespace std { +// Character classification and conversion types +typedef struct { + int __count; + union { + unsigned int __wch; + char __wchb[4]; + } __value; +} mbstate_t; + +typedef unsigned int wint_t; + +// Wide character constants +static const wint_t WEOF = static_cast(-1); + +} // namespace std + +#endif // _GHLIBCPP_CWCHAR \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/ios.h b/cpp/common/test/includes/standard-library/ios.h index 1a78db28aa..08d32a6c36 100644 --- a/cpp/common/test/includes/standard-library/ios.h +++ b/cpp/common/test/includes/standard-library/ios.h @@ -5,6 +5,9 @@ namespace std { typedef size_t streamsize; typedef int pos_type; +typedef long long streamoff; +typedef pos_type streampos; +typedef pos_type wstreampos; // Bitmask type as specified by [bitmask.types] // Operators omitted as not required for our test cases @@ -67,5 +70,8 @@ template class basic_ios : public std::ios_base { ios_base &hex(ios_base &str); +std::ios_base &boolalpha(std::ios_base &str); +std::ios_base &noboolalpha(std::ios_base &str); + } // namespace std #endif // _GHLIBCPP_IOS \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/locale b/cpp/common/test/includes/standard-library/locale new file mode 100644 index 0000000000..755c5f6ee1 --- /dev/null +++ b/cpp/common/test/includes/standard-library/locale @@ -0,0 +1,61 @@ + +#ifndef _GHLIBCPP_LOCALE +#define _GHLIBCPP_LOCALE + +#include + +namespace std { + +class locale { +public: + class facet; + class id; + typedef int category; + + static const category none = 0, collate = 0x010, ctype = 0x020, + monetary = 0x040, numeric = 0x080, time = 0x100, + messages = 0x200, + all = collate | ctype | monetary | numeric | time | + messages; + + locale() noexcept; + locale(const locale &other) noexcept; + explicit locale(const char *std_name); + explicit locale(const string &std_name); + locale(const locale &other, const char *std_name, category); + locale(const locale &other, const string &std_name, category); + template locale(const locale &other, Facet *f); + locale(const locale &other, const locale &one, category); + ~locale(); + const locale &operator=(const locale &other) noexcept; + template locale combine(const locale &other) const; + + basic_string name() const; + + bool operator==(const locale &other) const; + bool operator!=(const locale &other) const; + template + bool operator()(const basic_string &s1, + const basic_string &s2) const; + + static locale global(const locale &); + static const locale &classic(); +}; + +template bool isspace(charT c, const locale &loc); +template bool isprint(charT c, const locale &loc); +template bool iscntrl(charT c, const locale &loc); +template bool isupper(charT c, const locale &loc); +template bool islower(charT c, const locale &loc); +template bool isalpha(charT c, const locale &loc); +template bool isdigit(charT c, const locale &loc); +template bool ispunct(charT c, const locale &loc); +template bool isxdigit(charT c, const locale &loc); +template bool isalnum(charT c, const locale &loc); +template bool isgraph(charT c, const locale &loc); +template bool isblank(charT c, const locale &loc); +template charT toupper(charT c, const locale &loc); +template charT tolower(charT c, const locale &loc); +} // namespace std + +#endif // _GHLIBCPP_LOCALE \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/locale.h b/cpp/common/test/includes/standard-library/locale.h index 19a8905531..2501c9c5d5 100644 --- a/cpp/common/test/includes/standard-library/locale.h +++ b/cpp/common/test/includes/standard-library/locale.h @@ -1,38 +1,37 @@ -#ifndef _GHLIBCPP_LOCALE -#define _GHLIBCPP_LOCALE +#ifndef _GHLIBCPP_LOCALE_H +#define _GHLIBCPP_LOCALE_H -#define LC_ALL 6 +#define LC_ALL 6 struct lconv { - char *decimal_point; - char *thousands_sep; - char *grouping; + char *decimal_point; + char *thousands_sep; + char *grouping; - char *int_curr_symbol; - char *currency_symbol; - char *mon_decimal_point; - char *mon_thousands_sep; - char *mon_grouping; - char *positive_sign; - char *negative_sign; - char int_frac_digits; - char frac_digits; - char p_cs_precedes; - char p_sep_by_space; - char n_cs_precedes; - char n_sep_by_space; - char p_sign_posn; - char n_sign_posn; - char int_p_cs_precedes; - char int_p_sep_by_space; - char int_n_cs_precedes; - char int_n_sep_by_space; - char int_p_sign_posn; - char int_n_sign_posn; + char *int_curr_symbol; + char *currency_symbol; + char *mon_decimal_point; + char *mon_thousands_sep; + char *mon_grouping; + char *positive_sign; + char *negative_sign; + char int_frac_digits; + char frac_digits; + char p_cs_precedes; + char p_sep_by_space; + char n_cs_precedes; + char n_sep_by_space; + char p_sign_posn; + char n_sign_posn; + char int_p_cs_precedes; + char int_p_sep_by_space; + char int_n_cs_precedes; + char int_n_sep_by_space; + char int_p_sign_posn; + char int_n_sign_posn; }; - -char *setlocale (int, const char *); +char *setlocale(int, const char *); struct lconv *localeconv(void); -#endif // _GHLIBCPP_LOCALE \ No newline at end of file +#endif // _GHLIBCPP_LOCALE_H \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/optional b/cpp/common/test/includes/standard-library/optional new file mode 100644 index 0000000000..8b129a36cb --- /dev/null +++ b/cpp/common/test/includes/standard-library/optional @@ -0,0 +1,165 @@ +#ifndef _GHLIBCPP_OPTIONAL +#define _GHLIBCPP_OPTIONAL + +#include "initializer_list" +#include "stddef.h" + +namespace std { + +// Forward declarations and helper types +struct in_place_t { + explicit in_place_t() = default; +}; +constexpr in_place_t in_place{}; + +// Type trait helper +template struct decay { + typedef T type; +}; + +// nullopt_t type and nullopt constant +struct nullopt_t { + explicit constexpr nullopt_t(int) {} +}; +constexpr nullopt_t nullopt{0}; + +// bad_optional_access exception +class bad_optional_access {}; + +// optional template class +template class optional { +public: + typedef T value_type; + + // Constructors + constexpr optional() noexcept; + constexpr optional(nullopt_t) noexcept; + constexpr optional(const optional &other); + constexpr optional(optional &&other) noexcept; + template constexpr explicit optional(const optional &other); + template constexpr explicit optional(optional &&other); + template + constexpr explicit optional(in_place_t, Args &&...args); + template + constexpr explicit optional(in_place_t, initializer_list ilist, + Args &&...args); + template constexpr explicit optional(U &&value); + + // Destructor + ~optional(); + + // Assignment operators + optional &operator=(nullopt_t) noexcept; + constexpr optional &operator=(const optional &other); + constexpr optional &operator=(optional &&other) noexcept; + template optional &operator=(U &&value); + template optional &operator=(const optional &other); + template optional &operator=(optional &&other); + + // Observers + constexpr const T *operator->() const; + constexpr T *operator->(); + constexpr const T &operator*() const &; + constexpr T &operator*() &; + constexpr const T &&operator*() const &&; + constexpr T &&operator*() &&; + constexpr explicit operator bool() const noexcept; + constexpr bool has_value() const noexcept; + constexpr const T &value() const &; + constexpr T &value() &; + constexpr const T &&value() const &&; + constexpr T &&value() &&; + template constexpr T value_or(U &&default_value) const &; + template constexpr T value_or(U &&default_value) &&; + + // Modifiers + void swap(optional &other) noexcept; + void reset() noexcept; + template T &emplace(Args &&...args); + template + T &emplace(initializer_list ilist, Args &&...args); +}; + +// Deduction guides +template optional(T) -> optional; + +// Comparison operators +template +constexpr bool operator==(const optional &lhs, const optional &rhs); +template +constexpr bool operator!=(const optional &lhs, const optional &rhs); +template +constexpr bool operator<(const optional &lhs, const optional &rhs); +template +constexpr bool operator<=(const optional &lhs, const optional &rhs); +template +constexpr bool operator>(const optional &lhs, const optional &rhs); +template +constexpr bool operator>=(const optional &lhs, const optional &rhs); + +// Comparison with nullopt +template +constexpr bool operator==(const optional &opt, nullopt_t) noexcept; +template +constexpr bool operator==(nullopt_t, const optional &opt) noexcept; +template +constexpr bool operator!=(const optional &opt, nullopt_t) noexcept; +template +constexpr bool operator!=(nullopt_t, const optional &opt) noexcept; +template +constexpr bool operator<(const optional &opt, nullopt_t) noexcept; +template +constexpr bool operator<(nullopt_t, const optional &opt) noexcept; +template +constexpr bool operator<=(const optional &opt, nullopt_t) noexcept; +template +constexpr bool operator<=(nullopt_t, const optional &opt) noexcept; +template +constexpr bool operator>(const optional &opt, nullopt_t) noexcept; +template +constexpr bool operator>(nullopt_t, const optional &opt) noexcept; +template +constexpr bool operator>=(const optional &opt, nullopt_t) noexcept; +template +constexpr bool operator>=(nullopt_t, const optional &opt) noexcept; + +// Comparison with T +template +constexpr bool operator==(const optional &opt, const U &value); +template +constexpr bool operator==(const T &value, const optional &opt); +template +constexpr bool operator!=(const optional &opt, const U &value); +template +constexpr bool operator!=(const T &value, const optional &opt); +template +constexpr bool operator<(const optional &opt, const U &value); +template +constexpr bool operator<(const T &value, const optional &opt); +template +constexpr bool operator<=(const optional &opt, const U &value); +template +constexpr bool operator<=(const T &value, const optional &opt); +template +constexpr bool operator>(const optional &opt, const U &value); +template +constexpr bool operator>(const T &value, const optional &opt); +template +constexpr bool operator>=(const optional &opt, const U &value); +template +constexpr bool operator>=(const T &value, const optional &opt); + +// Specialized algorithms +template void swap(optional &lhs, optional &rhs) noexcept; + +// Factory functions +template +constexpr optional::type> make_optional(T &&value); +template +constexpr optional make_optional(Args &&...args); +template +constexpr optional make_optional(initializer_list ilist, Args &&...args); + +} // namespace std + +#endif // _GHLIBCPP_OPTIONAL \ No newline at end of file diff --git a/cpp/common/test/includes/standard-library/ostream.h b/cpp/common/test/includes/standard-library/ostream.h index 9f2c6d9069..23117a93d2 100644 --- a/cpp/common/test/includes/standard-library/ostream.h +++ b/cpp/common/test/includes/standard-library/ostream.h @@ -10,6 +10,18 @@ class basic_ostream : virtual public basic_ios { typedef charT char_type; basic_ostream &operator<<(int n); + basic_ostream &operator<<(bool n); + basic_ostream &operator<<(short n); + basic_ostream &operator<<(unsigned short n); + basic_ostream &operator<<(unsigned int n); + basic_ostream &operator<<(long n); + basic_ostream &operator<<(unsigned long n); + basic_ostream &operator<<(long long n); + basic_ostream &operator<<(unsigned long long n); + basic_ostream &operator<<(float f); + basic_ostream &operator<<(double f); + basic_ostream &operator<<(long double f); + basic_ostream &operator<<(const void *p); basic_ostream &put(char_type c); basic_ostream &write(const char_type *s, streamsize n); @@ -25,6 +37,20 @@ template basic_ostream &operator<<( basic_ostream &, basic_ostream &(*func)(basic_ostream &)); + +template +basic_ostream & +operator<<(basic_ostream &, + std::ios_base &(*func)(std::ios_base &)); +template +basic_ostream &operator<<( + basic_ostream &, + std::basic_ios &(*func)(std::basic_ios &)); + +template +basic_ostream &operator<<(basic_ostream &os, + const void *p); + template basic_ostream &endl(basic_ostream &); diff --git a/cpp/common/test/includes/standard-library/string b/cpp/common/test/includes/standard-library/string index a3f22f5e80..cb76a7742d 100644 --- a/cpp/common/test/includes/standard-library/string +++ b/cpp/common/test/includes/standard-library/string @@ -1,12 +1,92 @@ #ifndef _GHLIBCPP_STRING #define _GHLIBCPP_STRING +#include "cwchar" #include "initializer_list" +#include "ios.h" #include "iosfwd.h" #include "iterator.h" #include "stddef.h" namespace std { -template struct char_traits; +template struct char_traits { + typedef charT char_type; + typedef int int_type; + typedef streamoff off_type; + typedef streampos pos_type; + typedef mbstate_t state_type; + + static void assign(char_type &c1, const char_type &c2); + static bool eq(const char_type &c1, const char_type &c2); + static bool lt(const char_type &c1, const char_type &c2); + + static int compare(const char_type *s1, const char_type *s2, size_t n); + static size_t length(const char_type *s); + static const char_type *find(const char_type *s, size_t n, + const char_type &a); + static char_type *move(char_type *s1, const char_type *s2, size_t n); + static char_type *copy(char_type *s1, const char_type *s2, size_t n); + static char_type *assign(char_type *s, size_t n, char_type a); + + static int_type not_eof(const int_type &c); + static char_type to_char_type(const int_type &c); + static int_type to_int_type(const char_type &c); + static bool eq_int_type(const int_type &c1, const int_type &c2); + static int_type eof(); +}; + +// Specialization for char +template <> struct char_traits { + typedef char char_type; + typedef int int_type; + typedef streamoff off_type; + typedef streampos pos_type; + typedef mbstate_t state_type; + + static void assign(char_type &c1, const char_type &c2); + static bool eq(const char_type &c1, const char_type &c2); + static bool lt(const char_type &c1, const char_type &c2); + + static int compare(const char_type *s1, const char_type *s2, size_t n); + static size_t length(const char_type *s); + static const char_type *find(const char_type *s, size_t n, + const char_type &a); + static char_type *move(char_type *s1, const char_type *s2, size_t n); + static char_type *copy(char_type *s1, const char_type *s2, size_t n); + static char_type *assign(char_type *s, size_t n, char_type a); + + static int_type not_eof(const int_type &c); + static char_type to_char_type(const int_type &c); + static int_type to_int_type(const char_type &c); + static bool eq_int_type(const int_type &c1, const int_type &c2); + static int_type eof(); +}; + +// Specialization for wchar_t +template <> struct char_traits { + typedef wchar_t char_type; + typedef wint_t int_type; + typedef streamoff off_type; + typedef wstreampos pos_type; + typedef mbstate_t state_type; + + static void assign(char_type &c1, const char_type &c2); + static bool eq(const char_type &c1, const char_type &c2); + static bool lt(const char_type &c1, const char_type &c2); + + static int compare(const char_type *s1, const char_type *s2, size_t n); + static size_t length(const char_type *s); + static const char_type *find(const char_type *s, size_t n, + const char_type &a); + static char_type *move(char_type *s1, const char_type *s2, size_t n); + static char_type *copy(char_type *s1, const char_type *s2, size_t n); + static char_type *assign(char_type *s, size_t n, char_type a); + + static int_type not_eof(const int_type &c); + static char_type to_char_type(const int_type &c); + static int_type to_int_type(const char_type &c); + static bool eq_int_type(const int_type &c1, const int_type &c2); + static int_type eof(); +}; template class allocator { public: @@ -114,14 +194,15 @@ public: basic_string &replace(size_type pos, size_type n1, size_type n2, charT c); basic_string &replace(__const_iterator i1, __const_iterator i2, const basic_string &str); - basic_string &replace(__const_iterator i1, __const_iterator i2, const charT *s, - size_type n); - basic_string &replace(__const_iterator i1, __const_iterator i2, const charT *s); + basic_string &replace(__const_iterator i1, __const_iterator i2, + const charT *s, size_type n); + basic_string &replace(__const_iterator i1, __const_iterator i2, + const charT *s); basic_string &replace(__const_iterator i1, __const_iterator i2, size_type n, charT c); template - basic_string &replace(__const_iterator i1, __const_iterator i2, InputIterator j1, - InputIterator j2); + basic_string &replace(__const_iterator i1, __const_iterator i2, + InputIterator j1, InputIterator j2); basic_string &replace(__const_iterator, __const_iterator, initializer_list); 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..0ce4bfd42c --- /dev/null +++ b/cpp/misra/src/codingstandards/cpp/misra/BuiltInTypeRules.qll @@ -0,0 +1,290 @@ +/** + * 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 PlainCharType 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() +} + +/** + * Gets the built-in type of a type, if it is a built-in type. + * + * This function will strip specifiers and typedefs to get the underlying built-in type. + */ +BuiltInType getBuiltInType(Type t) { + // Get the built-in type of a type, if it is a built-in type + result = t + or + // Strip specifiers and typedefs to get the built-in type + result = getBuiltInType(t.getUnspecifiedType()) + or + // For reference types, get the base type and then the built-in type + result = getBuiltInType(t.(ReferenceType).getBaseType()) + or + // For enum types, get the explicit underlying type and then the built-in type + result = getBuiltInType(t.(Enum).getExplicitUnderlyingType()) +} + +/** + * The signedness of a MISRA C++ 2023 numeric type. + */ +newtype Signedness = + Signed() or + Unsigned() + +class CharacterType extends Type { + // The actual character type, which is either a plain char or a wide char + BuiltInType realType; + + CharacterType() { + // A type whose type category is character + getTypeCategory(realType) = Character() and + realType = getBuiltInType(this) + } + + Type getRealType() { result = realType } +} + +/** + * 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 whose type category is either integral or a floating-point + getTypeCategory(realType) = [Integral().(TypeCategory), FloatingPoint()] and + realType = getBuiltInType(this) + } + + 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, Type targetType, string context) { + exists(Expr preConversionAssignment | + isPreConversionAssignment(preConversionAssignment, targetType, context) and + preConversionAssignment.getExplicitlyConverted() = source + ) +} + +predicate isPreConversionAssignment(Expr source, Type 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-3/NoCharacterNumericalValue.ql b/cpp/misra/src/rules/RULE-7-0-3/NoCharacterNumericalValue.ql new file mode 100644 index 0000000000..c4d294a0b8 --- /dev/null +++ b/cpp/misra/src/rules/RULE-7-0-3/NoCharacterNumericalValue.ql @@ -0,0 +1,61 @@ +/** + * @id cpp/misra/no-character-numerical-value + * @name RULE-7-0-3: The numerical value of a character shall not be used + * @description Using the numerical value of a character type may lead to inconsistent behavior due + * to encoding dependencies and should be avoided in favor of safer C++ Standard + * Library functions. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-7-0-3 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra.BuiltInTypeRules + +from Conversion c, Expr expr, Type sourceType, Type targetType +where + expr = c.getExpr() and + sourceType = expr.getType() and + targetType = c.getType() and + ( + // Conversion from character type to non-character type + sourceType instanceof CharacterType and + not targetType instanceof CharacterType + or + // Conversion from non-character type to character type + not sourceType instanceof CharacterType and + targetType instanceof CharacterType + // or + // // Conversion between different character types + // getTypeCategory(sourceType) instanceof Character and + // getTypeCategory(targetType) instanceof Character and + // not sourceType = targetType + ) and + // Exclude conversions where both operands have the same character type in equality operations + not exists(EqualityOperation eq, CharacterType leftType, CharacterType rightType | + eq.getAnOperand() = expr and + leftType = eq.getLeftOperand().getType() and + rightType = eq.getRightOperand().getType() and + leftType.getRealType() = rightType.getRealType() + ) and + // Exclude unevaluated operands + not ( + expr.getParent*() instanceof SizeofExprOperator or + expr.getParent*() instanceof SizeofTypeOperator or + expr.getParent*() instanceof TypeidOperator or + expr.getParent*() = any(Decltype dt).getExpr() or + expr.getParent*() instanceof StaticAssert + ) and + // Exclude optional comparisons that don't involve conversion + not exists(FunctionCall fc | + fc.getTarget().hasName("operator==") and + fc.getAnArgument() = expr and + fc.getQualifier().getType().hasName("optional") + ) +select expr, + "Conversion of character type '" + sourceType.getName() + "' to '" + targetType.getName() + + "' uses numerical value of character." diff --git a/cpp/misra/src/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.ql b/cpp/misra/src/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.ql new file mode 100644 index 0000000000..b256beaf71 --- /dev/null +++ b/cpp/misra/src/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.ql @@ -0,0 +1,144 @@ +/** + * @id cpp/misra/inappropriate-bitwise-or-shift-operands + * @name RULE-7-0-4: The operands of bitwise operators and shift operators shall be appropriate + * @description Bitwise and shift operators should only be applied to operands of appropriate types + * and values to avoid implementation-defined or undefined behavior. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-7-0-4 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.misra.BuiltInTypeRules + +predicate isSignedType(NumericType t) { t.getSignedness() = Signed() } + +predicate isUnsignedType(NumericType t) { t.getSignedness() = Unsigned() } + +predicate isConstantExpression(Expr e) { + e instanceof Literal or + e.isConstant() +} + +predicate isValidShiftConstantRange(Expr right, Type leftType) { + exists(int value | + value = right.getValue().toInt() and + value >= 0 and + value < leftType.getSize() * 8 + ) +} + +predicate isSignedConstantLeftShiftException(LShiftExpr shift) { + exists( + Expr left, Expr right, NumericType leftType, QlBuiltins::BigInt leftVal, int rightVal, + int maxBit + | + left = shift.getLeftOperand() and + right = shift.getRightOperand() and + leftType = left.getType() and + isConstantExpression(left) and + isConstantExpression(right) and + isSignedType(leftType) and + isValidShiftConstantRange(right, leftType) and + leftVal = left.getValue().toBigInt() and + rightVal = right.getValue().toInt() and + leftVal >= 0.toBigInt() and + maxBit = leftType.getSize() * 8 - 1 and + // Check that no set bit is shifted into or beyond the sign bit + leftVal * 2.toBigInt().pow(rightVal) < 2.toBigInt().pow(maxBit) + ) +} + +class BinaryShiftOperation extends BinaryOperation { + BinaryShiftOperation() { + this instanceof LShiftExpr or + this instanceof RShiftExpr + } +} + +class AssignShiftOperation extends AssignOperation { + AssignShiftOperation() { + this instanceof AssignLShiftExpr or + this instanceof AssignRShiftExpr + } +} + +from Expr x, string message +where + not isExcluded(x, ConversionsPackage::inappropriateBitwiseOrShiftOperandsQuery()) and + ( + // Binary bitwise operators (excluding shift operations) - both operands must be unsigned + exists(BinaryBitwiseOperation op | + not op instanceof BinaryShiftOperation and + op = x and + not ( + isUnsignedType(op.getLeftOperand().getExplicitlyConverted().getType()) and + isUnsignedType(op.getRightOperand().getExplicitlyConverted().getType()) + ) and + message = + "Binary bitwise operator '" + op.getOperator() + "' requires both operands to be unsigned." + ) + or + // Compound assignment bitwise operators - both operands must be unsigned + exists(AssignBitwiseOperation op | + not op instanceof AssignShiftOperation and + op = x and + not ( + isUnsignedType(op.getLValue().getExplicitlyConverted().getType()) and + isUnsignedType(op.getRValue().getExplicitlyConverted().getType()) + ) and + message = + "Compound assignment bitwise operator '" + op.getOperator() + + "' requires both operands to be unsigned." + ) + or + // Bit complement operator - operand must be unsigned + exists(ComplementExpr comp | + comp = x and + not isUnsignedType(comp.getOperand().getExplicitlyConverted().getType()) and + message = "Bit complement operator '~' requires unsigned operand." + ) + or + // Shift operators - left operand must be unsigned + exists(BinaryShiftOperation shift | + shift = x and + not isUnsignedType(shift.getLeftOperand().getExplicitlyConverted().getType()) and + not isSignedConstantLeftShiftException(shift) and + message = "Shift operator '" + shift.getOperator() + "' requires unsigned left operand." + ) + or + // Compound assignment shift operators - left operand must be unsigned + exists(AssignShiftOperation shift | + shift = x and + not isUnsignedType(shift.getLValue().getExplicitlyConverted().getType()) and + message = "Shift operator '" + shift.getOperator() + "' requires unsigned left operand." + ) + or + // Shift operators - right operand must be unsigned or constant in valid range + exists(BinaryShiftOperation shift, Expr right | + shift = x and + right = shift.getRightOperand() and + not isUnsignedType(right.getExplicitlyConverted().getType()) and + not isValidShiftConstantRange(right, shift.getLeftOperand().getExplicitlyConverted().getType()) and + message = + "Shift operator '" + shift.getOperator() + + "' requires unsigned right operand or constant in valid range." + ) + or + // Compound assignment shift operators - right operand must be unsigned or constant in valid range + exists(AssignShiftOperation shift, Expr right | + shift = x and + right = shift.getRValue() and + not isUnsignedType(right.getExplicitlyConverted().getType()) and + not isValidShiftConstantRange(right, shift.getLValue().getExplicitlyConverted().getType()) and + message = + "Shift operator '" + shift.getOperator() + + "' requires unsigned right operand or constant in valid range." + ) + ) +select x, message diff --git a/cpp/misra/src/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.ql b/cpp/misra/src/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.ql new file mode 100644 index 0000000000..30d75e3dee --- /dev/null +++ b/cpp/misra/src/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.ql @@ -0,0 +1,215 @@ +/** + * @id cpp/misra/no-signedness-change-from-promotion + * @name RULE-7-0-5: Integral promotion and the usual arithmetic conversions shall not change the signedness or the type + * @description Integral promotion and usual arithmetic conversions that change operand signedness + * or type category may cause unexpected behavior or undefined behavior when operations + * overflow. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-7-0-5 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.misra.BuiltInTypeRules + +abstract class RelevantConversion extends Expr { + abstract NumericType getFromType(); + + abstract NumericType getToType(); + + abstract Expr getConvertedExpr(); + + abstract string getKindOfConversion(); +} + +/** + * A `Conversion` that is relevant for the rule. + */ +abstract class RelevantRealConversion extends RelevantConversion, Conversion { + NumericType fromType; + NumericType toType; + + RelevantRealConversion() { + fromType = this.getExpr().getType() and + toType = this.getType() and + this.isImplicit() + } + + override NumericType getFromType() { result = fromType } + + override NumericType getToType() { result = toType } + + override Expr getConvertedExpr() { result = this.getExpr() } +} + +class UsualArithmeticConversion extends RelevantRealConversion { + UsualArithmeticConversion() { + ( + // Most binary operations from and to numeric types participate in usual arithmetic conversions + exists(BinaryOperation op | + // Shifts do not participate in usual arithmetic conversions + not op instanceof LShiftExpr and + not op instanceof RShiftExpr and + op.getAnOperand().getFullyConverted() = this + ) + or + // Most binary assignment operations from and to numeric types participate in usual arithmetic + // conversions + exists(AssignOperation ao | + // Shifts do not participate in usual arithmetic conversions + not ao instanceof AssignLShiftExpr and + not ao instanceof AssignRShiftExpr and + ao.getRValue().getFullyConverted() = this + ) + ) + } + + override string getKindOfConversion() { result = "Usual arithmetic conversion" } +} + +class IntegerPromotion extends RelevantRealConversion { + IntegerPromotion() { + // Exclude integer promotions combined with usual arithmetic conversions, which are handled separately + not this instanceof UsualArithmeticConversion and + // Only consider cases where the integer promotion is the last conversion applied + exists(Expr e | e.getFullyConverted() = this) and + // Integer promotion occurs where the from type is smaller than int + fromType.getRealSize() < sizeOfInt() and + // To type is bigger than or equal to int + toType.getRealSize() >= sizeOfInt() and + // An integer promotion is a conversion from an integral type to an integral type + // + // This deliberately excludes integer promotions from `bool` and unscoped enums which do not + // have a fixed underlying type, because neither of these are considered integral types in the + // MISRA C++ rules. + fromType.getTypeCategory() = Integral() and + toType.getTypeCategory() = Integral() + } + + override string getKindOfConversion() { result = "Integer promotion" } +} + +class ImpliedUsualArithmeticConversion extends RelevantConversion { + NumericType fromType; + NumericType toType; + + ImpliedUsualArithmeticConversion() { + // The lvalue of an assignment operation does not have a `Conversion` in our model, but + // it is still subject to usual arithmetic conversions (excepting shifts). + // + // rvalues are handled separately in the `UsualArithmeticConversion` class. + exists(AssignOperation aop | + not aop instanceof AssignLShiftExpr and + not aop instanceof AssignRShiftExpr and + // lvalue subject to usual arithmetic conversions + aop.getLValue() = this and + // From type is the type of the lvalue, which should be a numeric type under the MISRA rule + fromType = this.getType() and + // Under usual arithmetic conversions, the converted types of both arguments will be the same, + // so even though we don't have an explicit conversion, we can still deduce that the target + // type will be the same as the converted type of the rvalue. + toType = aop.getRValue().getFullyConverted().getType() and + // Only consider cases where the conversion is not a no-op, for consistency with the `Conversion` class + not fromType.getRealType() = toType.getRealType() + ) + } + + override NumericType getFromType() { result = fromType } + + override NumericType getToType() { result = toType } + + override Expr getConvertedExpr() { result = this } + + override string getKindOfConversion() { result = "Usual arithmetic conversion" } +} + +class ImpliedIntegerPromotion extends RelevantConversion { + NumericType fromType; + + ImpliedIntegerPromotion() { + ( + exists(AssignLShiftExpr aop | aop.getLValue() = this) or + exists(AssignRShiftExpr aop | aop.getLValue() = this) + ) and + // Only consider integer promotions from MISRA C++ "numeric types" as per the rule + fromType = this.getType() and + fromType.getTypeCategory() = Integral() and + // If the size is less than int, then it is an implied integer promotion + fromType.getRealSize() < sizeOfInt() + } + + override NumericType getFromType() { result = fromType } + + override NumericType getToType() { + // Only report the canonical type - e.g. `int` not `signed int` + result = result.(IntegralType).getCanonicalArithmeticType() and + if result instanceof Char16Type or result instanceof Char32Type or result instanceof Wchar_t + then + // Smallest type that can hold the value of the `fromType` + result = + min(NumericType candidateType | + ( + candidateType instanceof IntType or + candidateType instanceof LongType or + candidateType instanceof LongLongType + ) and + fromType.getIntegralUpperBound() <= candidateType.getIntegralUpperBound() + | + candidateType order by candidateType.getIntegralUpperBound() + ) + else ( + if + // If the `fromType` is signed, the result must be signed + fromType.getSignedness() = Signed() + or + // If the `fromType` is unsigned, but the result can fit into the signed int type, then the + // result must be signed as well. + fromType.getIntegralUpperBound() <= + any(IntType t | t.isSigned()).(NumericType).getIntegralUpperBound() + then + // `int` is returned + result.(IntType).isSigned() + else + // Otherwise `unsigned int` is returned + result.(IntType).isUnsigned() + ) + } + + override Expr getConvertedExpr() { result = this } + + override string getKindOfConversion() { result = "Integer promotion" } +} + +from Expr e, RelevantConversion c, NumericType fromType, NumericType toType, string changeType +where + not isExcluded(e, ConversionsPackage::noSignednessChangeFromPromotionQuery()) and + c.getConvertedExpr() = e and + fromType = c.getFromType() and + toType = c.getToType() and + ( + fromType.getSignedness() != toType.getSignedness() and changeType = "signedness" + or + fromType.getTypeCategory() != toType.getTypeCategory() and changeType = "type category" + ) and + // Ignore crement operations + not exists(CrementOperation cop | cop.getAnOperand() = e) and + // Exception 1: allow safe constant conversions + not ( + e.getValue().toInt() >= 0 and + fromType.(IntegralType).isSigned() and + toType.(IntegralType).isUnsigned() + ) and + // Exception 2: allow safe conversions from integral to floating-point types + not ( + e.isConstant() and + fromType.getTypeCategory() = Integral() and + toType.getTypeCategory() = FloatingPoint() + ) +select e, + c.getKindOfConversion() + " from '" + fromType.getName() + "' to '" + toType.getName() + + "' changes " + changeType + "." 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/src/rules/RULE-7-11-3/FunctionPointerConversionContext.ql b/cpp/misra/src/rules/RULE-7-11-3/FunctionPointerConversionContext.ql new file mode 100644 index 0000000000..7dadbb734d --- /dev/null +++ b/cpp/misra/src/rules/RULE-7-11-3/FunctionPointerConversionContext.ql @@ -0,0 +1,63 @@ +/** + * @id cpp/misra/function-pointer-conversion-context + * @name RULE-7-11-3: A conversion from function type to pointer-to-function type shall only occur in appropriate contexts + * @description Converting a function type to a pointer-to-function type outside of static_cast or + * assignment to a pointer-to-function object creates ambiguous behavior and potential + * unintended effects. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-7-11-3 + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.misra.BuiltInTypeRules +import codingstandards.cpp.Type + +/** + * An `Expr` representing an implicit conversion from a function type to a pointer-to-function type. + * + * Despite the name, these are not `Conversion`s in our model. Instead, they are expressions + * representing functions that have been implicilty converted to function pointers. + */ +abstract class FunctionToFunctionPointerConversion extends Expr { } + +/** + * A `FunctionAccess` that has been implicitly converted to a function pointer type. + */ +class FunctionAccessConversionToFunctionPointer extends FunctionAccess, + FunctionToFunctionPointerConversion +{ + FunctionAccessConversionToFunctionPointer() { + this.getType().getUnspecifiedType() instanceof FunctionPointerIshType + } +} + +/** + * A `Call` to a `ConversionOperator` that converts a lambda to a function pointer type. + */ +class LambdaFunctionPointerConversion extends Call, FunctionToFunctionPointerConversion { + LambdaFunctionPointerConversion() { + this.getTarget().(ConversionOperator).getDestType() instanceof FunctionPointerIshType and + this.getQualifier().getType().getUnspecifiedType() instanceof Closure + } +} + +from FunctionToFunctionPointerConversion f +where + not isExcluded(f, ConversionsPackage::functionPointerConversionContextQuery()) and + // Not converted by an explicit static cast + not exists(Conversion c | + c.getExpr() = f and + not c.isImplicit() and + c.getType().getUnspecifiedType() instanceof FunctionPointerIshType + ) and + // Not a MISRA compliant assignment to a function pointer type + not exists(FunctionPointerIshType targetType | isAssignment(f, targetType, _)) +select f, + "Inappropriate conversion from function type to pointer-to-function type in '" + f.toString() + + "'." 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-3/NoCharacterNumericalValue.expected b/cpp/misra/test/rules/RULE-7-0-3/NoCharacterNumericalValue.expected new file mode 100644 index 0000000000..0b6088d34e --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-3/NoCharacterNumericalValue.expected @@ -0,0 +1,27 @@ +| test.cpp:17:13:17:14 | 10 | Conversion of character type 'int' to 'char' uses numerical value of character. | +| test.cpp:18:13:18:14 | 65 | Conversion of character type 'int' to 'char' uses numerical value of character. | +| test.cpp:19:13:19:13 | 0 | Conversion of character type 'int' to 'char' uses numerical value of character. | +| test.cpp:24:20:24:22 | 97 | Conversion of character type 'char' to 'int8_t' uses numerical value of character. | +| test.cpp:25:21:25:24 | 13 | Conversion of character type 'char' to 'uint8_t' uses numerical value of character. | +| test.cpp:26:12:26:14 | 98 | Conversion of character type 'char' to 'int' uses numerical value of character. | +| test.cpp:43:13:43:14 | l1 | Conversion of character type 'char' to 'int' uses numerical value of character. | +| test.cpp:43:13:43:20 | ... - ... | Conversion of character type 'int' to 'char' uses numerical value of character. | +| test.cpp:43:18:43:20 | 48 | Conversion of character type 'char' to 'int' uses numerical value of character. | +| test.cpp:44:13:44:14 | l1 | Conversion of character type 'char' to 'int' uses numerical value of character. | +| test.cpp:44:13:44:18 | ... + ... | Conversion of character type 'int' to 'char' uses numerical value of character. | +| test.cpp:45:13:45:14 | l1 | Conversion of character type 'char' to 'int' uses numerical value of character. | +| test.cpp:45:13:45:18 | ... * ... | Conversion of character type 'int' to 'char' uses numerical value of character. | +| test.cpp:51:7:51:8 | l1 | Conversion of character type 'char' to 'bool' uses numerical value of character. | +| test.cpp:51:13:51:14 | l2 | Conversion of character type 'char' to 'bool' uses numerical value of character. | +| test.cpp:53:7:53:8 | l1 | Conversion of character type 'char' to 'bool' uses numerical value of character. | +| test.cpp:55:8:55:9 | l2 | Conversion of character type 'char' to 'bool' uses numerical value of character. | +| test.cpp:73:39:73:41 | 97 | Conversion of character type 'char' to 'int_type' uses numerical value of character. | +| test.cpp:89:8:89:9 | l1 | Conversion of character type 'char' to 'int' uses numerical value of character. | +| test.cpp:89:14:89:16 | 48 | Conversion of character type 'char' to 'int' uses numerical value of character. | +| test.cpp:89:23:89:24 | l1 | Conversion of character type 'char' to 'int' uses numerical value of character. | +| test.cpp:89:29:89:31 | 57 | Conversion of character type 'char' to 'int' uses numerical value of character. | +| test.cpp:102:25:102:26 | l1 | Conversion of character type 'char' to 'int' uses numerical value of character. | +| test.cpp:117:15:117:16 | l2 | Conversion of character type 'int_type' to 'char' uses numerical value of character. | +| test.cpp:123:31:123:32 | 65 | Conversion of character type 'int' to 'char' uses numerical value of character. | +| test.cpp:124:29:124:31 | 65 | Conversion of character type 'char' to 'int' uses numerical value of character. | +| test.cpp:130:6:130:7 | l2 | Conversion of character type 'char' to 'int' uses numerical value of character. | diff --git a/cpp/misra/test/rules/RULE-7-0-3/NoCharacterNumericalValue.qlref b/cpp/misra/test/rules/RULE-7-0-3/NoCharacterNumericalValue.qlref new file mode 100644 index 0000000000..b1254bda14 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-3/NoCharacterNumericalValue.qlref @@ -0,0 +1 @@ +rules/RULE-7-0-3/NoCharacterNumericalValue.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-0-3/test.cpp b/cpp/misra/test/rules/RULE-7-0-3/test.cpp new file mode 100644 index 0000000000..205712b1ff --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-3/test.cpp @@ -0,0 +1,131 @@ +#include +#include +#include +#include +#include +#include +#include + +void test_character_literal_assignment() { + char l1 = 'a'; // COMPLIANT + char l2 = '\r'; // COMPLIANT + char l3 = '\n'; // COMPLIANT + char l4 = '\0'; // COMPLIANT +} + +void test_implicit_conversion_from_int() { + char l1 = 10; // NON_COMPLIANT + char l2 = 65; // NON_COMPLIANT + char l3 = 0; // NON_COMPLIANT +} + +void test_implicit_conversion_to_int() { + char l1 = 'a'; + std::int8_t l2 = 'a'; // NON_COMPLIANT + std::uint8_t l3 = '\r'; // NON_COMPLIANT + int l4 = 'b'; // NON_COMPLIANT +} + +void test_signed_char_assignment() { + signed char l1 = 11; // COMPLIANT + signed char l2 = 65; // COMPLIANT +} + +void test_conversion_between_character_types() { + char l1 = L'A'; // COMPLIANT + wchar_t l2 = 'B'; // COMPLIANT + char16_t l3 = 'C'; // COMPLIANT + char32_t l4 = 'D'; // COMPLIANT +} + +void test_arithmetic_operations() { + char l1 = 'a'; + char l2 = l1 - '0'; // NON_COMPLIANT + char l3 = l1 + 1; // NON_COMPLIANT + char l4 = l1 * 2; // NON_COMPLIANT +} + +void test_boolean_conversion() { + char l1 = 'a'; + char l2 = '\0'; + if (l1 && l2) { // NON_COMPLIANT + } + if (l1) { // NON_COMPLIANT + } + if (!l2) { // NON_COMPLIANT + } +} + +void test_same_type_comparison() { + char l1 = 'a'; + if (l1 != 'q') { // COMPLIANT + } + if (l1 == 'b') { // COMPLIANT + } +} + +void test_char_traits_usage() { + using CT = std::char_traits; + char l1 = 'a'; + if (CT::eq(l1, 'q')) { // COMPLIANT + } + auto l2 = CT::to_int_type('a'); // COMPLIANT + auto l3 = static_cast('a'); // NON_COMPLIANT +} + +void test_optional_comparison() { + std::optional l1; + if (l1 == 'r') { // COMPLIANT + } +} + +void test_unevaluated_operand() { + decltype('s' + 't') l1; // COMPLIANT + static_assert(sizeof('x') > 0); // COMPLIANT +} + +void test_range_check_non_compliant() { + char l1 = 'a'; + if ((l1 >= '0') && (l1 <= '9')) { // NON_COMPLIANT + } +} + +void test_range_check_compliant() { + using CT = std::char_traits; + char l1 = 'a'; + if (!CT::lt(l1, '0') && !CT::lt('9', l1)) { // COMPLIANT + } +} + +void test_isdigit_non_compliant() { + char l1 = 'a'; + if (0 == std::isdigit(l1)) { // NON_COMPLIANT + } +} + +void test_isdigit_compliant() { + char l1 = 'a'; + if (std::isdigit(l1, std::locale{})) { // COMPLIANT + } +} + +void test_stream_conversion() { + std::istream &l1 = std::cin; + auto l2 = l1.get(); + using CT = std::char_traits; + if (CT::not_eof(l2)) { + char l3 = l2; // NON_COMPLIANT + char l4 = CT::to_char_type(l2); // COMPLIANT + } +} + +void test_explicit_cast() { + char l1 = static_cast(65); // NON_COMPLIANT + int l2 = static_cast('A'); // NON_COMPLIANT +} + +void test_function_parameter_conversion() { + auto f1 = [](int l1) {}; + char l2 = 'x'; + f1(l2); // NON_COMPLIANT +} \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.expected b/cpp/misra/test/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.expected new file mode 100644 index 0000000000..0c9144d018 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.expected @@ -0,0 +1,43 @@ +| test.cpp:14:3:14:13 | ... & ... | Binary bitwise operator '&' requires both operands to be unsigned. | +| test.cpp:15:3:15:13 | ... \| ... | Binary bitwise operator '\|' requires both operands to be unsigned. | +| test.cpp:16:3:16:13 | ... ^ ... | Binary bitwise operator '^' requires both operands to be unsigned. | +| test.cpp:18:3:18:13 | ... & ... | Binary bitwise operator '&' requires both operands to be unsigned. | +| test.cpp:19:3:19:13 | ... \| ... | Binary bitwise operator '\|' requires both operands to be unsigned. | +| test.cpp:30:3:30:15 | ... &= ... | Compound assignment bitwise operator '&=' requires both operands to be unsigned. | +| test.cpp:31:3:31:15 | ... \|= ... | Compound assignment bitwise operator '\|=' requires both operands to be unsigned. | +| test.cpp:32:3:32:15 | ... ^= ... | Compound assignment bitwise operator '^=' requires both operands to be unsigned. | +| test.cpp:42:3:42:6 | ~ ... | Bit complement operator '~' requires unsigned operand. | +| test.cpp:54:3:54:9 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:55:3:55:11 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:63:3:63:18 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:74:3:74:11 | ... << ... | Shift operator '<<' requires unsigned right operand or constant in valid range. | +| test.cpp:75:3:75:11 | ... >> ... | Shift operator '>>' requires unsigned right operand or constant in valid range. | +| test.cpp:85:3:85:11 | ... << ... | Shift operator '<<' requires unsigned right operand or constant in valid range. | +| test.cpp:86:3:86:11 | ... << ... | Shift operator '<<' requires unsigned right operand or constant in valid range. | +| test.cpp:87:3:87:11 | ... >> ... | Shift operator '>>' requires unsigned right operand or constant in valid range. | +| test.cpp:93:3:93:11 | ... << ... | Shift operator '<<' requires unsigned right operand or constant in valid range. | +| test.cpp:94:3:94:11 | ... << ... | Shift operator '<<' requires unsigned right operand or constant in valid range. | +| test.cpp:95:3:95:11 | ... >> ... | Shift operator '>>' requires unsigned right operand or constant in valid range. | +| test.cpp:96:3:96:11 | ... >> ... | Shift operator '>>' requires unsigned right operand or constant in valid range. | +| test.cpp:115:3:115:12 | ... <<= ... | Shift operator '<<=' requires unsigned left operand. | +| test.cpp:116:3:116:12 | ... >>= ... | Shift operator '>>=' requires unsigned left operand. | +| test.cpp:117:3:117:11 | ... <<= ... | Shift operator '<<=' requires unsigned left operand. | +| test.cpp:118:3:118:11 | ... >>= ... | Shift operator '>>=' requires unsigned left operand. | +| test.cpp:121:3:121:12 | ... <<= ... | Shift operator '<<=' requires unsigned right operand or constant in valid range. | +| test.cpp:122:3:122:12 | ... >>= ... | Shift operator '>>=' requires unsigned right operand or constant in valid range. | +| test.cpp:125:3:125:12 | ... <<= ... | Shift operator '<<=' requires unsigned right operand or constant in valid range. | +| test.cpp:126:3:126:12 | ... <<= ... | Shift operator '<<=' requires unsigned right operand or constant in valid range. | +| test.cpp:127:3:127:12 | ... >>= ... | Shift operator '>>=' requires unsigned right operand or constant in valid range. | +| test.cpp:130:3:130:12 | ... <<= ... | Shift operator '<<=' requires unsigned right operand or constant in valid range. | +| test.cpp:131:3:131:12 | ... <<= ... | Shift operator '<<=' requires unsigned right operand or constant in valid range. | +| test.cpp:132:3:132:12 | ... >>= ... | Shift operator '>>=' requires unsigned right operand or constant in valid range. | +| test.cpp:133:3:133:12 | ... >>= ... | Shift operator '>>=' requires unsigned right operand or constant in valid range. | +| test.cpp:143:3:143:9 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:144:3:144:9 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:145:3:145:9 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:146:3:146:9 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:150:3:150:11 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:154:3:154:30 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:156:3:156:17 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:162:3:162:10 | ... << ... | Shift operator '<<' requires unsigned left operand. | +| test.cpp:170:3:170:11 | ... >> ... | Shift operator '>>' requires unsigned left operand. | diff --git a/cpp/misra/test/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.qlref b/cpp/misra/test/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.qlref new file mode 100644 index 0000000000..df3c14d752 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.qlref @@ -0,0 +1 @@ +rules/RULE-7-0-4/InappropriateBitwiseOrShiftOperands.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-0-4/test.cpp b/cpp/misra/test/rules/RULE-7-0-4/test.cpp new file mode 100644 index 0000000000..1735d59e58 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-4/test.cpp @@ -0,0 +1,171 @@ +#include +#include + +void test_binary_bitwise_operators_unsigned_operands() { + std::uint32_t u32a = 0x12345678U; + std::uint32_t u32b = 0x87654321U; + std::int32_t s32a = 0x12345678; + std::int32_t s32b = 0x87654321; + + u32a & u32b; // COMPLIANT + u32a | u32b; // COMPLIANT + u32a ^ u32b; // COMPLIANT + + s32a & s32b; // NON_COMPLIANT + s32a | s32b; // NON_COMPLIANT + s32a ^ s32b; // NON_COMPLIANT + + s32a & u32b; // NON_COMPLIANT + u32a | s32b; // NON_COMPLIANT +} + +void test_compound_assignment_bitwise_operators() { + std::uint32_t u32 = 0x12345678U; + std::int32_t s32 = 0x12345678; + + u32 &= 0xFFFFU; // COMPLIANT + u32 |= 0x1000U; // COMPLIANT + u32 ^= 0x5555U; // COMPLIANT + + s32 &= 0xFFFF; // NON_COMPLIANT + s32 |= 0x1000; // NON_COMPLIANT + s32 ^= 0x5555; // NON_COMPLIANT +} + +void test_bit_complement_operator() { + std::uint32_t u32 = 0x12345678U; + std::uint8_t u8 = 0x55U; + std::int32_t s32 = 0x12345678; + + ~u32; // COMPLIANT + ~u8; // COMPLIANT + ~s32; // NON_COMPLIANT +} + +void test_shift_operators_left_operand_type() { + std::uint32_t u32 = 0x12345678U; + std::uint8_t u8 = 2U; + std::int32_t s32 = 0x12345678; + + 1U << u8; // COMPLIANT + 1U << 31; // COMPLIANT + u32 << 2U; // COMPLIANT + + 1 << u8; // NON_COMPLIANT + s32 << 2U; // NON_COMPLIANT +} + +void test_shift_operators_with_promotion() { + std::uint8_t u8 = 1U; + std::uint16_t u16 = 2U; + + static_cast(u8 + u16) << 2U; // COMPLIANT + (u8 + u16) << 2U; // NON_COMPLIANT +} + +void test_shift_operators_right_operand_unsigned() { + std::uint32_t u32 = 0x12345678U; + std::uint8_t u8 = 2U; + std::int8_t s8 = 2; + + u32 << u8; // COMPLIANT + u32 >> u8; // COMPLIANT + + u32 << s8; // NON_COMPLIANT + u32 >> s8; // NON_COMPLIANT +} + +void test_shift_operators_right_operand_constant_range() { + std::uint32_t u32 = 0x12345678U; + + u32 << 0; // COMPLIANT + u32 << 31; // COMPLIANT + u32 >> 15; // COMPLIANT + + u32 << 32; // NON_COMPLIANT + u32 << 64; // NON_COMPLIANT + u32 >> 32; // NON_COMPLIANT +} + +void test_shift_operators_negative_right_operand() { + std::uint32_t u32 = 0x12345678U; + + u32 << -1; // NON_COMPLIANT + u32 << -5; // NON_COMPLIANT + u32 >> -1; // NON_COMPLIANT + u32 >> -3; // NON_COMPLIANT +} + +void test_compound_assignment_shift_operators() { + std::uint32_t u32 = 0x12345678U; + std::uint8_t u8 = 2U; + std::int32_t s32 = 0x12345678; + std::int8_t s8 = 2; + + // Unsigned left operand with unsigned right operand + u32 <<= u8; // COMPLIANT + u32 >>= u8; // COMPLIANT + + // Unsigned left operand with constant right operand in valid range + u32 <<= 0; // COMPLIANT + u32 <<= 31; // COMPLIANT + u32 >>= 15; // COMPLIANT + + // Signed left operand + s32 <<= u8; // NON_COMPLIANT + s32 >>= u8; // NON_COMPLIANT + s32 <<= 2; // NON_COMPLIANT + s32 >>= 2; // NON_COMPLIANT + + // Unsigned left operand with signed right operand + u32 <<= s8; // NON_COMPLIANT + u32 >>= s8; // NON_COMPLIANT + + // Right operand out of range + u32 <<= 32; // NON_COMPLIANT + u32 <<= 64; // NON_COMPLIANT + u32 >>= 32; // NON_COMPLIANT + + // Negative right operand + u32 <<= -1; // NON_COMPLIANT + u32 <<= -5; // NON_COMPLIANT + u32 >>= -1; // NON_COMPLIANT + u32 >>= -3; // NON_COMPLIANT +} + +void test_exception_signed_constant_left_operand_exception() { + // Exception cases for signed constant expressions + 1 << 30; // COMPLIANT + 2 << 29; // COMPLIANT + 4 << 28; // COMPLIANT + 8 << 27; // COMPLIANT + + 1 << 31; // NON_COMPLIANT + 2 << 30; // NON_COMPLIANT + 4 << 29; // NON_COMPLIANT + 8 << 28; // NON_COMPLIANT + + 1LL << 31; // COMPLIANT - 64 bit type + 1LL << 62; // COMPLIANT - 64 bit type + 1LL << 63; // NON_COMPLIANT - 64 bit type + 0x1000'0000'0000'0000LL << 2; // COMPLIANT + 0x2000'0000'0000'0000LL << 1; // COMPLIANT + + 0x4000'0000'0000'0000LL << 1; // NON_COMPLIANT + + 0x40000000 << 1; // NON_COMPLIANT +} + +void test_exception_non_constant_signed_operand() { + std::int32_t s32 = 1; + + s32 << 2; // NON_COMPLIANT +} + +void test_right_shift_signed_operands() { + std::uint32_t u32 = 0x80000000U; + std::int32_t s32 = -1; + + u32 >> 1U; // COMPLIANT + s32 >> 1U; // NON_COMPLIANT +} \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected b/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected new file mode 100644 index 0000000000..a9f1f37c63 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.expected @@ -0,0 +1,110 @@ +| test.cpp:22:3:22:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:22:8:22:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:23:3:23:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:23:7:23:8 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:24:3:24:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:24:8:24:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:25:3:25:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:25:8:25:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:26:3:26:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:26:8:26:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:27:3:27:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:27:8:27:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:28:3:28:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:28:8:28:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:29:3:29:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:29:8:29:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:43:3:43:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:43:9:43:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:44:3:44:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:44:9:44:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:45:3:45:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:45:9:45:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:46:3:46:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:46:9:46:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:47:3:47:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:47:9:47:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:48:3:48:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:48:9:48:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:49:3:49:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:49:9:49:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:50:3:50:4 | l1 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:50:9:50:10 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:62:3:62:4 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:63:3:63:4 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:64:3:64:4 | l2 | Integer promotion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:65:3:65:4 | l2 | Integer promotion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:68:9:68:10 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:69:9:69:10 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:73:3:73:4 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:74:3:74:4 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:75:3:75:4 | l2 | Integer promotion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:76:3:76:4 | l2 | Integer promotion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:79:10:79:11 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:80:10:80:11 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:91:3:91:4 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | +| test.cpp:92:3:92:4 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | +| test.cpp:93:3:93:4 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | +| test.cpp:94:3:94:4 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | +| test.cpp:95:3:95:4 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | +| test.cpp:96:3:96:4 | l1 | Usual arithmetic conversion from 'int32_t' to 'unsigned int' changes signedness. | +| test.cpp:98:3:98:4 | l3 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:98:8:98:9 | l4 | Usual arithmetic conversion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:99:3:99:4 | l3 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:99:8:99:9 | l4 | Usual arithmetic conversion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:109:8:109:9 | l2 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:109:13:109:14 | l4 | Integer promotion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:117:4:117:5 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:119:4:119:5 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:121:4:121:5 | l1 | Integer promotion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:164:8:164:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:165:8:165:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:166:7:166:8 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:167:8:167:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:168:8:168:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:169:8:169:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:170:8:170:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:171:8:171:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:173:8:173:9 | l4 | Usual arithmetic conversion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:174:8:174:9 | l4 | Usual arithmetic conversion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:175:7:175:8 | l4 | Usual arithmetic conversion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:177:8:177:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:178:8:178:9 | l2 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:179:9:179:10 | l4 | Usual arithmetic conversion from 'uint16_t' to 'int' changes signedness. | +| test.cpp:199:9:199:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes signedness. | +| test.cpp:199:9:199:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes type category. | +| test.cpp:200:9:200:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes signedness. | +| test.cpp:200:9:200:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes type category. | +| test.cpp:201:9:201:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes signedness. | +| test.cpp:201:9:201:10 | l2 | Usual arithmetic conversion from 'uint32_t' to 'float' changes type category. | +| test.cpp:270:3:270:4 | l3 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:270:8:270:9 | l4 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:271:3:271:4 | l3 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:271:7:271:8 | l4 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:272:3:272:4 | l3 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:272:8:272:9 | l4 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:273:3:273:4 | l3 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:273:8:273:9 | l4 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:274:3:274:4 | l3 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:274:8:274:9 | l4 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:275:3:275:4 | l3 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:275:8:275:9 | l4 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:278:3:278:4 | l3 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:278:8:278:9 | l7 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:279:3:279:4 | l3 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:279:7:279:8 | l7 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:280:3:280:4 | l7 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:280:8:280:9 | l3 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:286:4:286:5 | l3 | Integer promotion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:287:4:287:5 | l3 | Integer promotion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:288:4:288:5 | l3 | Integer promotion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:294:3:294:31 | static_cast... | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:295:7:296:46 | static_cast... | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:299:3:299:4 | l3 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:299:8:299:9 | l4 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:300:3:300:4 | l3 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:300:9:300:10 | l4 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:301:3:301:4 | l3 | Usual arithmetic conversion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:301:9:301:10 | l7 | Usual arithmetic conversion from 'uint8_t' to 'int' changes signedness. | +| test.cpp:304:3:304:4 | l3 | Integer promotion from 'UnscopedEnumExplicit' to 'int' changes signedness. | +| test.cpp:305:3:305:4 | l3 | Integer promotion from 'UnscopedEnumExplicit' to 'int' changes signedness. | diff --git a/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.qlref b/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.qlref new file mode 100644 index 0000000000..ae020269b9 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-5/NoSignednessChangeFromPromotion.qlref @@ -0,0 +1 @@ +rules/RULE-7-0-5/NoSignednessChangeFromPromotion.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-0-5/test.cpp b/cpp/misra/test/rules/RULE-7-0-5/test.cpp new file mode 100644 index 0000000000..6e36d80d68 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-0-5/test.cpp @@ -0,0 +1,330 @@ +#include + +// Global variables for testing +std::uint8_t g1 = 5; +std::uint8_t g2 = 10; +std::uint16_t g3 = 100; +std::uint32_t g4 = 1000; +std::int8_t g5 = -5; +std::int32_t g6 = -1000; +float g7 = 3.14f; + +constexpr std::int32_t f1(std::int32_t i) { return i * i; } + +void test_binary_arithmetic_operations() { + std::uint8_t l1 = 5; + std::uint8_t l2 = 10; + std::uint16_t l3 = 100; + std::uint32_t l4 = 1000; + std::int8_t l5 = -5; + std::int32_t l6 = -1000; + + l1 + l2; // NON_COMPLIANT - u8 + u8 -> signed int + l1 *l2; // NON_COMPLIANT - u8 * u8 -> signed int + l1 - l2; // NON_COMPLIANT - u8 - u8 -> signed int + l1 / l2; // NON_COMPLIANT - u8 / u8 -> signed int + l1 % l2; // NON_COMPLIANT - u8 % u8 -> signed int + l1 & l2; // NON_COMPLIANT - u8 & u8 -> signed int + l1 | l2; // NON_COMPLIANT - u8 | u8 -> signed int + l1 ^ l2; // NON_COMPLIANT - u8 ^ u8 -> signed int + + static_cast(l1) + l2; // COMPLIANT - l2 -> unsigned int + l1 + static_cast(l2); // COMPLIANT - l1 -> unsigned int + + l6 *l5; // COMPLIANT - l5 -> signed int + l4 / l1; // COMPLIANT - l1 -> unsigned int +} + +void test_assignment_operations() { + std::uint8_t l1 = 5; + std::uint8_t l2 = 10; + std::uint32_t l3 = 1000; + + l1 += l2; // NON_COMPLIANT - same as l1 + l2 + l1 -= l2; // NON_COMPLIANT - same as l1 - l2 + l1 *= l2; // NON_COMPLIANT - same as l1 * l2 + l1 /= l2; // NON_COMPLIANT - same as l1 / l2 + l1 %= l2; // NON_COMPLIANT - same as l1 % l2 + l1 &= l2; // NON_COMPLIANT - same as l1 & l2 + l1 |= l2; // NON_COMPLIANT - same as l1 | l2 + l1 ^= l2; // NON_COMPLIANT - same as l1 ^ l2 + + l1 += static_cast(l2); // COMPLIANT - l1 -> unsigned int + l1 += l3; // COMPLIANT - l1 -> unsigned int +} + +void test_shift_operations() { + std::uint8_t l1 = 5; + std::uint16_t l2 = 100; + std::uint32_t l3 = 1000; + std::int16_t l4; + + l1 << 2; // NON_COMPLIANT - l1 -> signed int + l1 >> 1; // NON_COMPLIANT - l1 -> signed int + l2 << 2; // NON_COMPLIANT - l2 -> signed int + l2 >> 1; // NON_COMPLIANT - l2 -> signed int + l3 << 2; // COMPLIANT + l3 >> 1; // COMPLIANT + l3 << l1; // NON_COMPLIANT - l1 -> signed int + l3 >> l1; // NON_COMPLIANT - l1 -> signed int + l3 << l4; // COMPLIANT + l3 >> l4; // COMPLIANT + + l1 <<= 2; // NON_COMPLIANT - l1 -> signed int + l1 >>= 1; // NON_COMPLIANT - l1 -> signed int + l2 <<= 2; // NON_COMPLIANT - l2 -> signed int + l2 >>= 1; // NON_COMPLIANT - l2 -> signed int + l3 <<= 2; // COMPLIANT + l3 >>= 1; // COMPLIANT + l3 <<= l1; // NON_COMPLIANT - l1 -> signed int + l3 >>= l1; // NON_COMPLIANT - l1 -> signed int + l3 <<= l4; // COMPLIANT + l3 >>= l4; // COMPLIANT +} + +void test_comparison_operations() { + std::int32_t l1 = -1000; + std::uint32_t l2 = 1000; + std::uint8_t l3 = 5; + std::uint16_t l4 = 100; + + l1 > l2; // NON_COMPLIANT - l1 -> unsigned int + l1 < l2; // NON_COMPLIANT - l1 -> unsigned int + l1 >= l2; // NON_COMPLIANT - l1 -> unsigned int + l1 <= l2; // NON_COMPLIANT - l1 -> unsigned int + l1 == l2; // NON_COMPLIANT - l1 -> unsigned int + l1 != l2; // NON_COMPLIANT - l1 -> unsigned int + + l3 > l4; // NON_COMPLIANT - l3 and l4 -> signed int + l3 < l4; // NON_COMPLIANT - l3 and l4 -> signed int +} + +void test_conditional_operator() { + bool l1 = true; + std::uint8_t l2 = 5; + std::uint8_t l3 = 10; + std::uint16_t l4 = 100; + + l1 ? l2 : l3; // COMPLIANT - no conversion + l1 ? l2 : l4; // NON_COMPLIANT - l2 and l4 -> signed int +} + +void test_unary_operations() { + std::uint8_t l1 = 5; + std::uint32_t l2 = 1000; + std::int8_t l3 = -5; + + ~l1; // NON_COMPLIANT - l1 -> signed int + ~l2; // COMPLIANT + -l1; // NON_COMPLIANT - l1 -> signed int + -l3; // COMPLIANT - l3 is signed + +l1; // NON_COMPLIANT - l1 -> signed int +} + +void test_increment_decrement() { + std::uint8_t l1 = 5; + std::uint16_t l2 = 100; + + l1++; // COMPLIANT - rule does not apply + ++l1; // COMPLIANT - rule does not apply + l1--; // COMPLIANT - rule does not apply + --l1; // COMPLIANT - rule does not apply + l2++; // COMPLIANT - rule does not apply + ++l2; // COMPLIANT - rule does not apply +} + +void test_array_subscript() { + int l1[10]; + std::uint8_t l2 = 5; + + l1[l2]; // COMPLIANT - rule does not apply +} + +void test_logical_operators() { + std::uint8_t l1 = 5; + std::uint8_t l2 = 10; + std::uint16_t l3 = 100; + std::int8_t l4 = -5; + bool l5 = true; + + l1 &&l2; // COMPLIANT - rule does not apply to logical operators + l1 || l2; // COMPLIANT - rule does not apply to logical operators + l1 &&l3; // COMPLIANT - rule does not apply to logical operators + l4 &&l1; // COMPLIANT - rule does not apply to logical operators + l5 &&l1; // COMPLIANT - rule does not apply to logical operators + l1 &&l5; // COMPLIANT - rule does not apply to logical operators +} + +void test_mixed_signed_unsigned_arithmetic() { + std::int8_t l1 = -5; + std::uint8_t l2 = 10; + std::int16_t l3 = -100; + std::uint16_t l4 = 200; + + l1 + l2; // NON_COMPLIANT - l1 and l2 -> signed int + l1 - l2; // NON_COMPLIANT - l1 and l2 -> signed int + l1 *l2; // NON_COMPLIANT - l1 and l2 -> signed int + l1 / l2; // NON_COMPLIANT - l1 and l2 -> signed int + l1 % l2; // NON_COMPLIANT - l1 and l2 -> signed int + l1 & l2; // NON_COMPLIANT - l1 and l2 -> signed int + l1 | l2; // NON_COMPLIANT - l1 and l2 -> signed int + l1 ^ l2; // NON_COMPLIANT - l1 and l2 -> signed int + + l3 + l4; // NON_COMPLIANT - l3 and l4 -> signed int + l3 - l4; // NON_COMPLIANT - l3 and l4 -> signed int + l3 *l4; // NON_COMPLIANT - l3 and l4 -> signed int + + l1 < l2; // NON_COMPLIANT - l1 and l2 -> signed int + l1 > l2; // NON_COMPLIANT - l1 and l2 -> signed int + l3 == l4; // NON_COMPLIANT - l3 and l4 -> signed int +} + +void test_exception_compile_time_constants() { + std::uint32_t l1 = 1000; + float l2 = 3.14f; + std::int32_t l3 = 5; + + l1 - 1; // COMPLIANT - exception #1 + l1 + 42; // COMPLIANT - exception #1 + l2 += 1; // COMPLIANT - exception #2 + l2 += 0x10001; // COMPLIANT - exception #2 + l3 + f1(10); // COMPLIANT - exception #1 + l2 + f1(10); // COMPLIANT - exception #2 +} + +void test_floating_point_conversions() { + float l1; + std::uint32_t l2; + + l1 += l2; // NON_COMPLIANT - l2 -> floating + l1 *= l2; // NON_COMPLIANT - l2 -> floating + l1 /= l2; // NON_COMPLIANT - l2 -> floating +} + +void test_pointer_arithmetic() { + int l1[10]; + std::uint8_t l2 = 5; + std::uint16_t l3 = 2; + std::int8_t l4 = 3; + std::int32_t l5 = 1; + int *l6 = l1; + + l1 + l2; // COMPLIANT - rule does not apply to pointer arithmetic + l1 + l3; // COMPLIANT - rule does not apply to pointer arithmetic + l1 + l4; // COMPLIANT - rule does not apply to pointer arithmetic + l1 + l5; // COMPLIANT - rule does not apply to pointer arithmetic + l6 + l2; // COMPLIANT - rule does not apply to pointer arithmetic + l6 + l3; // COMPLIANT - rule does not apply to pointer arithmetic + + l1 - l2; // COMPLIANT - rule does not apply to pointer arithmetic + l6 - l3; // COMPLIANT - rule does not apply to pointer arithmetic + + l6 - l1; // COMPLIANT - rule does not apply to pointer arithmetic +} + +void test_pointer_assignment_arithmetic() { + int l1[10]; + std::uint8_t l2 = 5; + std::uint16_t l3 = 2; + std::int8_t l4 = 3; + int *l5 = l1; + + l5 += l2; // COMPLIANT - rule does not apply to pointer arithmetic + l5 += l3; // COMPLIANT - rule does not apply to pointer arithmetic + l5 += l4; // COMPLIANT - rule does not apply to pointer arithmetic + + l5 -= l2; // COMPLIANT - rule does not apply to pointer arithmetic + l5 -= l3; // COMPLIANT - rule does not apply to pointer arithmetic + l5 -= l4; // COMPLIANT - rule does not apply to pointer arithmetic +} + +// Enum types for testing +enum UnscopedEnum { VALUE1, VALUE2, VALUE3 }; +enum class ScopedEnum { VALUE1, VALUE2, VALUE3 }; +enum UnscopedEnumExplicit : std::uint8_t { + EXPLICIT_VALUE1 = 1, + EXPLICIT_VALUE2 = 2 +}; +enum class ScopedEnumExplicit : std::uint8_t { + EXPLICIT_VALUE1 = 1, + EXPLICIT_VALUE2 = 2 +}; + +void test_enum_types() { + UnscopedEnum l1 = VALUE1; + UnscopedEnum l2 = VALUE2; + UnscopedEnumExplicit l3 = EXPLICIT_VALUE1; + UnscopedEnumExplicit l4 = EXPLICIT_VALUE2; + ScopedEnum l5 = ScopedEnum::VALUE1; + ScopedEnumExplicit l6 = ScopedEnumExplicit::EXPLICIT_VALUE1; + std::uint8_t l7 = 5; + std::uint32_t l8 = 10; + + // Unscoped enum without explicit underlying type - not considered numeric + // type + l1 + l2; // COMPLIANT - rule does not apply + l1 *l2; // COMPLIANT - rule does not apply + l1 & l2; // COMPLIANT - rule does not apply + + // Unscoped enum with explicit underlying type - considered numeric type + l3 + l4; // NON_COMPLIANT - uint8_t + uint8_t -> signed int + l3 *l4; // NON_COMPLIANT - uint8_t * uint8_t -> signed int + l3 & l4; // NON_COMPLIANT - uint8_t & uint8_t -> signed int + l3 - l4; // NON_COMPLIANT - uint8_t - uint8_t -> signed int + l3 | l4; // NON_COMPLIANT - uint8_t | uint8_t -> signed int + l3 ^ l4; // NON_COMPLIANT - uint8_t ^ uint8_t -> signed int + + // Mixed enum and integer arithmetic + l3 + l7; // NON_COMPLIANT - uint8_t + uint8_t -> signed int + l3 *l7; // NON_COMPLIANT - uint8_t * uint8_t -> signed int + l7 - l3; // NON_COMPLIANT - uint8_t - uint8_t -> signed int + + l3 + l8; // COMPLIANT - uint8_t -> signed int (matches l8) + l8 *l3; // COMPLIANT - uint8_t -> signed int (matches l8) + + // Unary operations on enum with explicit underlying type + ~l3; // NON_COMPLIANT - uint8_t -> signed int + -l3; // NON_COMPLIANT - uint8_t -> signed int + +l3; // NON_COMPLIANT - uint8_t -> signed int + + // Scoped enums - not considered numeric type regardless of underlying type + static_cast(l5) + + static_cast(ScopedEnum::VALUE2); // COMPLIANT - rule does not apply + // to explicit casts + static_cast(l6) + // NON_COMPLIANT + static_cast( // NON_COMPLIANT + ScopedEnumExplicit::EXPLICIT_VALUE2); + + // Comparison operations with enum + l3 > l4; // NON_COMPLIANT - uint8_t > uint8_t -> signed int + l3 == l4; // NON_COMPLIANT - uint8_t == uint8_t -> signed int + l3 != l7; // NON_COMPLIANT - uint8_t != uint8_t -> signed int + + // Shift operations with enum + l3 << 1; // NON_COMPLIANT - uint8_t -> signed int + l3 >> 1; // NON_COMPLIANT - uint8_t -> signed int + + // Conditional operator with enum + true ? l3 : l4; // COMPLIANT - same types, no conversion + true ? l3 : l8; // COMPLIANT - same underlying types, no conversion +} + +#define A 100LL // intmax_t +#define B 200LL // intmax_t +#define C 300ULL // uintmax_t +#define D 400ULL // uintmax_t + +#if A + B > 250 // COMPLIANT - both intmax_t, no conversion +; +#elif C + D < 800 // COMPLIANT - both uintmax_t, no conversion +; +#endif + +#define SIGNED_MAX 9223372036854775807LL // intmax_t +#define UNSIGNED_VAL 1ULL // uintmax_t + +#if SIGNED_MAX + UNSIGNED_VAL > 0 // NON_COMPLIANT[FALSE_NEGATIVE] +// intmax_t + uintmax_t → both converted to uintmax_t +// This changes SIGNED_MAX from signed to unsigned +; +#endif 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 diff --git a/cpp/misra/test/rules/RULE-7-11-3/FunctionPointerConversionContext.expected b/cpp/misra/test/rules/RULE-7-11-3/FunctionPointerConversionContext.expected new file mode 100644 index 0000000000..be07c4b103 --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-11-3/FunctionPointerConversionContext.expected @@ -0,0 +1,16 @@ +| test.cpp:11:7:11:7 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. | +| test.cpp:14:7:14:7 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. | +| test.cpp:18:16:18:16 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. | +| test.cpp:21:14:21:14 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. | +| test.cpp:25:7:25:7 | call to operator void (*)() | Inappropriate conversion from function type to pointer-to-function type in 'call to operator void (*)()'. | +| test.cpp:33:14:33:14 | call to operator void (*)() | Inappropriate conversion from function type to pointer-to-function type in 'call to operator void (*)()'. | +| test.cpp:61:13:61:13 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. | +| test.cpp:64:13:64:13 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. | +| test.cpp:67:7:67:7 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. | +| test.cpp:71:7:71:7 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. | +| test.cpp:74:7:74:7 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. | +| test.cpp:77:7:77:7 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. | +| test.cpp:83:7:83:7 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. | +| test.cpp:87:7:87:7 | f | Inappropriate conversion from function type to pointer-to-function type in 'f'. | +| test.cpp:92:7:92:7 | call to operator void (*)() | Inappropriate conversion from function type to pointer-to-function type in 'call to operator void (*)()'. | +| test.cpp:96:7:96:7 | call to operator void (*)() | Inappropriate conversion from function type to pointer-to-function type in 'call to operator void (*)()'. | diff --git a/cpp/misra/test/rules/RULE-7-11-3/FunctionPointerConversionContext.qlref b/cpp/misra/test/rules/RULE-7-11-3/FunctionPointerConversionContext.qlref new file mode 100644 index 0000000000..d05d05586b --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-11-3/FunctionPointerConversionContext.qlref @@ -0,0 +1 @@ +rules/RULE-7-11-3/FunctionPointerConversionContext.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-7-11-3/test.cpp b/cpp/misra/test/rules/RULE-7-11-3/test.cpp new file mode 100644 index 0000000000..0522a703fc --- /dev/null +++ b/cpp/misra/test/rules/RULE-7-11-3/test.cpp @@ -0,0 +1,98 @@ +#include +#include + +// Test functions +extern int *f(); +void f1(double); +void f1(std::uint32_t); +void simple_function(); + +void test_function_to_pointer_conversion() { + if (f) { // NON_COMPLIANT + } + + if (f == nullptr) { // NON_COMPLIANT + } + + std::cout << std::boolalpha // COMPLIANT - considered assignment + << f; // NON_COMPLIANT - not assignment + + // Non-compliant: Unary plus operator causing pointer decay + auto l1 = +f; // NON_COMPLIANT + + auto lam = []() {}; + // Lambda used in boolean context + if (lam) { // NON_COMPLIANT + } + + // Lambda used in address-of operator + if (&lam) { // COMPLIANT + } + + // Unary plus on lambda + auto l2 = +lam; // NON_COMPLIANT + + // Using address-of operator + if (&f != nullptr) { // COMPLIANT + } + + // Function call + (f)(); // COMPLIANT + lam(); // COMPLIANT + + // static_cast conversion + auto selected = static_cast(f1); // COMPLIANT + + // Assignment to pointer-to-function type + void (*p)() = lam; // COMPLIANT + + // Assignment to pointer-to-function type + int *(*func_ptr)() = f; // COMPLIANT + + // Using address-of operator + void (*simple_ptr)() = &simple_function; // COMPLIANT + + // Direct assignment without conversion + void (*simple_ptr2)() = simple_function; // COMPLIANT +} + +void test_arithmetic_expressions() { + // Function used in arithmetic expression (triggers pointer decay) + auto l3 = f + 0; // NON_COMPLIANT + + // Function used in arithmetic subtraction expression + auto l4 = f - 0; // NON_COMPLIANT + + // Function used in comparison with non-null pointer + if (f > nullptr) { // NON_COMPLIANT + } + + // Function used in relational operators + if (f < nullptr) { // NON_COMPLIANT + } + + if (f <= nullptr) { // NON_COMPLIANT + } + + if (f >= nullptr) { // NON_COMPLIANT + } +} + +void test_logical_expressions() { + // Function used in logical AND expression + if (f && true) { // NON_COMPLIANT + } + + // Function used in logical OR expression + if (f || false) { // NON_COMPLIANT + } + + // Lambda used in logical AND expression + auto lam = []() {}; + if (lam && true) { // NON_COMPLIANT + } + + // Lambda used in logical OR expression + if (lam || false) { // NON_COMPLIANT + } +} \ No newline at end of file diff --git a/rule_packages/cpp/Conversions.json b/rule_packages/cpp/Conversions.json new file mode 100644 index 0000000000..441b5aa85f --- /dev/null +++ b/rule_packages/cpp/Conversions.json @@ -0,0 +1,147 @@ +{ + "MISRA-C++-2023": { + "RULE-7-0-1": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "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", + "name": "There shall be no conversion from type bool", + "precision": "very-high", + "severity": "error", + "short_name": "NoConversionFromBool", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "There shall be no conversion from type bool" + }, + "RULE-7-0-2": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "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", + "name": "There shall be no conversion to type bool", + "precision": "very-high", + "severity": "error", + "short_name": "NoImplicitBoolConversion", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "There shall be no conversion to type bool" + }, + "RULE-7-0-3": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Using the numerical value of a character type may lead to inconsistent behavior due to encoding dependencies and should be avoided in favor of safer C++ Standard Library functions.", + "kind": "problem", + "name": "The numerical value of a character shall not be used", + "precision": "very-high", + "severity": "error", + "short_name": "NoCharacterNumericalValue", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "The numerical value of a character shall not be used" + }, + "RULE-7-0-4": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Bitwise and shift operators should only be applied to operands of appropriate types and values to avoid implementation-defined or undefined behavior.", + "kind": "problem", + "name": "The operands of bitwise operators and shift operators shall be appropriate", + "precision": "very-high", + "severity": "error", + "short_name": "InappropriateBitwiseOrShiftOperands", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "The operands of bitwise operators and shift operators shall be appropriate" + }, + "RULE-7-0-5": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Integral promotion and usual arithmetic conversions that change operand signedness or type category may cause unexpected behavior or undefined behavior when operations overflow.", + "kind": "problem", + "name": "Integral promotion and the usual arithmetic conversions shall not change the signedness or the type", + "precision": "very-high", + "severity": "error", + "short_name": "NoSignednessChangeFromPromotion", + "tags": [ + "scope/single-translation-unit" + ], + "implementation_scope": { + "description": "Arithmetic conversions in preprocessor directives are not supported." + } + } + ], + "title": "Integral promotion and the usual arithmetic conversions shall not change the signedness or the type category of an operand" + }, + "RULE-7-0-6": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "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", + "name": "Assignment between numeric types shall be appropriate", + "precision": "high", + "severity": "error", + "short_name": "NumericAssignmentTypeMismatch", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "Assignment between numeric types shall be appropriate" + }, + "RULE-7-11-3": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Converting a function type to a pointer-to-function type outside of static_cast or assignment to a pointer-to-function object creates ambiguous behavior and potential unintended effects.", + "kind": "problem", + "name": "A conversion from function type to pointer-to-function type shall only occur in appropriate contexts", + "precision": "very-high", + "severity": "error", + "short_name": "FunctionPointerConversionContext", + "tags": [ + "scope/single-translation-unit" + ] + } + ], + "title": "A conversion from function type to pointer-to-function type shall only occur in appropriate contexts" + } + } +} \ No newline at end of file diff --git a/rules.csv b/rules.csv index 68049625e6..3cb64a0bac 100644 --- a/rules.csv +++ b/rules.csv @@ -873,7 +873,7 @@ cpp,MISRA-C++-2023,RULE-6-9-2,Yes,Advisory,Decidable,Single Translation Unit,The cpp,MISRA-C++-2023,RULE-7-0-1,Yes,Required,Decidable,Single Translation Unit,There shall be no conversion from type bool,,Conversions,Easy, cpp,MISRA-C++-2023,RULE-7-0-2,Yes,Required,Decidable,Single Translation Unit,There shall be no conversion to type bool,,Conversions,Easy, cpp,MISRA-C++-2023,RULE-7-0-3,Yes,Required,Decidable,Single Translation Unit,The numerical value of a character shall not be used,M5-0-11,Conversions,Medium, -cpp,MISRA-C++-2023,RULE-7-0-4,Yes,Required,Decidable,Single Translation Unit,The operands of bitwise operators and shift operators shall be appropriate,RULE-10-1,Preconditions,Medium, +cpp,MISRA-C++-2023,RULE-7-0-4,Yes,Required,Decidable,Single Translation Unit,The operands of bitwise operators and shift operators shall be appropriate,RULE-10-1,Conversions,Medium, cpp,MISRA-C++-2023,RULE-7-0-5,Yes,Required,Decidable,Single Translation Unit,Integral promotion and the usual arithmetic conversions shall not change the signedness or the type category of an operand,"M5-0-4,M5-0-9,INT31-C",Conversions,Medium, cpp,MISRA-C++-2023,RULE-7-0-6,Yes,Required,Decidable,Single Translation Unit,Assignment between numeric types shall be appropriate,,Conversions,Hard, cpp,MISRA-C++-2023,RULE-7-11-1,Yes,Required,Decidable,Single Translation Unit,nullptr shall be the only form of the null-pointer-constant,A4-10-1,ImportMisra23,Import, @@ -882,14 +882,14 @@ cpp,MISRA-C++-2023,RULE-7-11-3,Yes,Required,Decidable,Single Translation Unit,A cpp,MISRA-C++-2023,RULE-8-0-1,Yes,Advisory,Decidable,Single Translation Unit,Parentheses should be used to make the meaning of an expression appropriately explicit,M5-0-2,Expressions2,Medium, cpp,MISRA-C++-2023,RULE-8-1-1,Yes,Required,Decidable,Single Translation Unit,A non-transient lambda shall not implicitly capture this,,Expressions2,Easy, cpp,MISRA-C++-2023,RULE-8-1-2,Yes,Advisory,Decidable,Single Translation Unit,Variables should be captured explicitly in a non-transient lambda,A5-1-2,Expressions2,Easy, -cpp,MISRA-C++-2023,RULE-8-2-1,Yes,Required,Decidable,Single Translation Unit,A virtual base class shall only be cast to a derived class by means of dynamic_cast,,Conversions,Easy, -cpp,MISRA-C++-2023,RULE-8-2-2,Yes,Required,Decidable,Single Translation Unit,C-style casts and functional notation casts shall not be used,A5-2-2,Conversions,Easy, +cpp,MISRA-C++-2023,RULE-8-2-1,Yes,Required,Decidable,Single Translation Unit,A virtual base class shall only be cast to a derived class by means of dynamic_cast,,Conversions2,Easy, +cpp,MISRA-C++-2023,RULE-8-2-2,Yes,Required,Decidable,Single Translation Unit,C-style casts and functional notation casts shall not be used,A5-2-2,Conversions2,Easy, cpp,MISRA-C++-2023,RULE-8-2-3,Yes,Required,Decidable,Single Translation Unit,A cast shall not remove any const or volatile qualification from the type accessed via a pointer or by reference,A5-2-3,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-8-2-4,Yes,Required,Decidable,Single Translation Unit,Casts shall not be performed between a pointer to function and any other type,M5-2-6,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-8-2-5,Yes,Required,Decidable,Single Translation Unit,reinterpret_cast shall not be used,A5-2-4,ImportMisra23,Import, -cpp,MISRA-C++-2023,RULE-8-2-6,Yes,Required,Decidable,Single Translation Unit,"An object with integral, enumerated, or pointer to void type shall not be cast to a pointer type","RULE-11-6, INT36-C",Conversions,Easy, -cpp,MISRA-C++-2023,RULE-8-2-7,Yes,Advisory,Decidable,Single Translation Unit,A cast should not convert a pointer type to an integral type,"RULE-11-6, INT36-C",Conversions,Easy, -cpp,MISRA-C++-2023,RULE-8-2-8,Yes,Required,Decidable,Single Translation Unit,An object pointer type shall not be cast to an integral type other than std::uintptr_t or std::intptr_t,"RULE-11-6, INT36-C",Conversions,Easy, +cpp,MISRA-C++-2023,RULE-8-2-6,Yes,Required,Decidable,Single Translation Unit,"An object with integral, enumerated, or pointer to void type shall not be cast to a pointer type","RULE-11-6, INT36-C",Conversions2,Easy, +cpp,MISRA-C++-2023,RULE-8-2-7,Yes,Advisory,Decidable,Single Translation Unit,A cast should not convert a pointer type to an integral type,"RULE-11-6, INT36-C",Conversions2,Easy, +cpp,MISRA-C++-2023,RULE-8-2-8,Yes,Required,Decidable,Single Translation Unit,An object pointer type shall not be cast to an integral type other than std::uintptr_t or std::intptr_t,"RULE-11-6, INT36-C",Conversions2,Easy, cpp,MISRA-C++-2023,RULE-8-2-9,Yes,Required,Decidable,Single Translation Unit,The operand to typeid shall not be an expression of polymorphic class type,,Preconditions,Easy, cpp,MISRA-C++-2023,RULE-8-2-10,Yes,Required,Undecidable,System,"Functions shall not call themselves, either directly or indirectly",A7-5-2,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-8-2-11,Yes,Required,Decidable,Single Translation Unit,An argument passed via ellipsis shall have an appropriate type,,Preconditions,Easy, @@ -903,7 +903,7 @@ cpp,MISRA-C++-2023,RULE-8-18-1,Yes,Mandatory,Undecidable,System,An object or sub cpp,MISRA-C++-2023,RULE-8-18-2,Yes,Advisory,Decidable,Single Translation Unit,The result of an assignment operator should not be used,RULE-13-4,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-8-19-1,Yes,Advisory,Decidable,Single Translation Unit,The comma operator should not be used,M5-18-1,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-8-20-1,Yes,Advisory,Decidable,Single Translation Unit,An unsigned arithmetic operation with constant operands should not wrap,INT30-C,ImportMisra23,Import, -cpp,MISRA-C++-2023,RULE-9-2-1,Yes,Required,Decidable,Single Translation Unit,An explicit type conversion shall not be an expression statement,DCL53-CPP,Conversions,Easy, +cpp,MISRA-C++-2023,RULE-9-2-1,Yes,Required,Decidable,Single Translation Unit,An explicit type conversion shall not be an expression statement,DCL53-CPP,Conversions2,Easy, cpp,MISRA-C++-2023,RULE-9-3-1,Yes,Required,Decidable,Single Translation Unit,The body of an iteration-statement or a selection-statement shall be a compound-statement,RULE-15-6,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-9-4-1,Yes,Required,Decidable,Single Translation Unit,All if ... else if constructs shall be terminated with an else statement,RULE-15-7,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-9-4-2,Yes,Required,Decidable,Single Translation Unit,The structure of a switch statement shall be appropriate,"RULE-16-1, RULE-16-2,RULE-16-3,RULE-16-4,RULE-16-5,RULE-16-6,RULE-16-7",Statements,Medium, pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy