diff --git a/cpp/autosar/src/rules/M5-0-12/SignedCharAndUnsignedCharTypeShallOnlyBeUsedForTheStorageAndUseOfNumericValues.ql b/cpp/autosar/src/rules/M5-0-12/SignedCharAndUnsignedCharTypeShallOnlyBeUsedForTheStorageAndUseOfNumericValues.ql index 3b6e436c56..7ffc47e50f 100644 --- a/cpp/autosar/src/rules/M5-0-12/SignedCharAndUnsignedCharTypeShallOnlyBeUsedForTheStorageAndUseOfNumericValues.ql +++ b/cpp/autosar/src/rules/M5-0-12/SignedCharAndUnsignedCharTypeShallOnlyBeUsedForTheStorageAndUseOfNumericValues.ql @@ -16,23 +16,194 @@ import cpp import codingstandards.cpp.autosar -from Variable v, Expr aexp +newtype TTemplatedElement = + TClassTemplate(TemplateClass c) or + TFunctionTemplate(TemplateFunction f) or + TVariableTemplate(TemplateVariable v) + +class TemplatedElement extends TTemplatedElement { + TemplateClass asTemplateClass() { this = TClassTemplate(result) } + + TemplateFunction asTemplateFunction() { this = TFunctionTemplate(result) } + + TemplateVariable asTemplateVariable() { this = TVariableTemplate(result) } + + string toString() { + result = this.asTemplateClass().toString() or + result = this.asTemplateFunction().toString() or + result = this.asTemplateVariable().toString() + } + + Location getLocation() { + result = this.asTemplateClass().getLocation() or + result = this.asTemplateFunction().getLocation() or + result = this.asTemplateVariable().getLocation() + } + + string getName() { + result = this.asTemplateClass().getName() or + result = this.asTemplateFunction().getName() or + result = this.asTemplateVariable().getName() + } +} + +newtype TTemplateInstantiation = + TClassTemplateInstantiation(ClassTemplateInstantiation c) or + TFunctionTemplateInstantiation(FunctionTemplateInstantiation f) or + TVariableTemplateInstantiation(VariableTemplateInstantiation v) + +class TemplateInstantiation extends TTemplateInstantiation { + ClassTemplateInstantiation asClassTemplateInstantiation() { + this = TClassTemplateInstantiation(result) + } + + FunctionTemplateInstantiation asFunctionTemplateInstantiation() { + this = TFunctionTemplateInstantiation(result) + } + + VariableTemplateInstantiation asVariableTemplateInstantiation() { + this = TVariableTemplateInstantiation(result) + } + + string toString() { + result = this.asClassTemplateInstantiation().toString() or + result = this.asFunctionTemplateInstantiation().toString() or + result = this.asVariableTemplateInstantiation().toString() + } + + Location getLocation() { + result = this.asClassTemplateInstantiation().getLocation() or + result = this.asFunctionTemplateInstantiation().getLocation() or + result = this.asVariableTemplateInstantiation().getLocation() + } + + Element asElement() { + result = this.asClassTemplateInstantiation() or + result = this.asFunctionTemplateInstantiation() or + result = this.asVariableTemplateInstantiation() + } + + TemplatedElement getTemplate() { + result.asTemplateClass() = this.asClassTemplateInstantiation().getTemplate() or + result.asTemplateFunction() = this.asFunctionTemplateInstantiation().getTemplate() or + result.asTemplateVariable() = this.asVariableTemplateInstantiation().getTemplate() + } + + /** + * Gets a use of an instantiation of this template. i.e. + * 1. For a class template, it's where the instantiated type is used by the name. + * 2. For a function template, it's where the instantiated function is called. + * 3. For a variable template, it's where the instantiated variable is initialized. + */ + Element getAUse() { + result = this.asClassTemplateInstantiation().getATypeNameUse() or + result = this.asFunctionTemplateInstantiation().getACallToThisFunction() or + result = this.asVariableTemplateInstantiation() + } +} + +class ImplicitConversionFromPlainCharType extends Conversion { + ImplicitConversionFromPlainCharType() { + this.isImplicit() and + this.getExpr().getUnspecifiedType() instanceof PlainCharType and + ( + this.getUnspecifiedType() instanceof SignedCharType or + this.getUnspecifiedType() instanceof UnsignedCharType + ) + } +} + +newtype TImplicitConversionElement = + TImplicitConversionOutsideTemplate(ImplicitConversionFromPlainCharType implicitConversion) { + not exists(TemplateInstantiation instantiation | + implicitConversion.isFromTemplateInstantiation(instantiation.asElement()) + ) + } or + TInstantiationOfImplicitConversionTemplate( + TemplateInstantiation templateInstantiation, + ImplicitConversionFromPlainCharType implicitConversion + ) { + implicitConversion.getEnclosingElement+() = templateInstantiation.asElement() + } + +class ImplicitConversionLocation extends TImplicitConversionElement { + ImplicitConversionFromPlainCharType asImplicitConversionOutsideTemplate() { + this = TImplicitConversionOutsideTemplate(result) + } + + TemplateInstantiation asInstantiationOfImplicitConversionTemplate( + ImplicitConversionFromPlainCharType implicitConversion + ) { + this = TInstantiationOfImplicitConversionTemplate(result, implicitConversion) + } + + predicate isImplicitConversionOutsideTemplate() { + exists(this.asImplicitConversionOutsideTemplate()) + } + + predicate isInstantiationOfImplicitConversionTemplate() { + exists( + TemplateInstantiation templateInstantiation, + ImplicitConversionFromPlainCharType implicitConversion + | + templateInstantiation = this.asInstantiationOfImplicitConversionTemplate(implicitConversion) + ) + } + + ImplicitConversionFromPlainCharType getImplicitConversion() { + result = this.asImplicitConversionOutsideTemplate() or + exists(TemplateInstantiation templateInstantiation | + this = TInstantiationOfImplicitConversionTemplate(templateInstantiation, result) + ) + } + + string toString() { + result = this.asImplicitConversionOutsideTemplate().toString() or + exists(ImplicitConversionFromPlainCharType implicitConversion | + result = this.asInstantiationOfImplicitConversionTemplate(implicitConversion).toString() + ) + } + + Location getLocation() { + result = this.asImplicitConversionOutsideTemplate().getLocation() or + exists(ImplicitConversionFromPlainCharType implicitConversion | + result = this.asInstantiationOfImplicitConversionTemplate(implicitConversion).getLocation() + ) + } + + Element asElement() { + result = this.asImplicitConversionOutsideTemplate() or + exists(ImplicitConversionFromPlainCharType implicitConversion | + result = this.asInstantiationOfImplicitConversionTemplate(implicitConversion).getAUse() + ) + } +} + +string getMessageTemplate(ImplicitConversionLocation implicitConversionLocation) { + exists(ImplicitConversionFromPlainCharType implicitConversion | + implicitConversion = implicitConversionLocation.getImplicitConversion() + | + implicitConversionLocation.isImplicitConversionOutsideTemplate() and + result = + "Implicit conversion of plain char $@ to " + implicitConversion.getType().getName() + "." + or + implicitConversionLocation.isInstantiationOfImplicitConversionTemplate() and + result = + "Implicit conversion of plain char $@ to " + implicitConversion.getType().getName() + + " from instantiating template '" + + implicitConversionLocation + .asInstantiationOfImplicitConversionTemplate(implicitConversion) + .getTemplate() + .getName() + "'." + ) +} + +from + ImplicitConversionLocation implicitConversionLocation, + ImplicitConversionFromPlainCharType implicitConversion where - not isExcluded(v, + not isExcluded(implicitConversionLocation.asElement(), StringsPackage::signedCharAndUnsignedCharTypeShallOnlyBeUsedForTheStorageAndUseOfNumericValuesQuery()) and - // We find cases where it is an explicitly signed char type with an assignment - // to a non-numeric type. NOTE: This rule addresses cases where the char type - // is used character data only, the rule does not explicitly cover this. - // Please see M5-0-11 for explicit handling of this case. Get types that are - // char, except for ones that are 'plain', meaning the sign is explicit. - ( - v.getUnspecifiedType() instanceof SignedCharType or - v.getUnspecifiedType() instanceof UnsignedCharType - ) and - // Identify places where these explicitly signed types are being assigned to a - // non-numeric type. - aexp = v.getAnAssignedValue() and - aexp.getUnspecifiedType() instanceof CharType -select aexp, - "Assignment of an non-integer type to variable $@ which is a variable with an explicitly signed char type", - v, v.getName() + implicitConversion = implicitConversionLocation.getImplicitConversion() +select implicitConversionLocation.asElement(), getMessageTemplate(implicitConversionLocation), + implicitConversion.getExpr(), "expression" diff --git a/cpp/autosar/test/rules/M5-0-12/test.cpp b/cpp/autosar/test/rules/M5-0-12/test.cpp index 453c37bf1e..99cb995854 100644 --- a/cpp/autosar/test/rules/M5-0-12/test.cpp +++ b/cpp/autosar/test/rules/M5-0-12/test.cpp @@ -1,16 +1,359 @@ #include -void f1() { - unsigned char a1 = 'c'; // NON_COMPLIANT - unsigned char a2 = 10; - signed char a3 = 'c'; // NON_COMPLIANT - signed char a4 = 10; +template class C1 { +public: + C1() : x(y) {} - std::int8_t a5 = 'c'; // NON_COMPLIANT - std::int8_t a6 = 10; +private: + unsigned char x; +}; - std::uint8_t a7 = 'c'; // NON_COMPLIANT - std::uint8_t a8 = 10; +template class C2 { +public: + C2() : x(y) {} - char a9 = 'c'; +private: + signed char x; +}; + +template class C3 { +public: + C3() : x(y) {} + +private: + unsigned char x; +}; + +template class C4 { +public: + C4() : x(y) {} + +private: + signed char x; +}; + +/* Twin templates for std::uint8_t and std::int8_t */ +template class C5 { +public: + C5() : x(y) {} + +private: + std::uint8_t x; +}; + +template class C6 { +public: + C6() : x(y) {} + +private: + std::int8_t x; +}; + +template class C7 { +public: + C7() : x(y) {} + +private: + std::uint8_t x; +}; + +template class C8 { +public: + C8() : x(y) {} + +private: + std::int8_t x; +}; + +void f1(unsigned char x) {} +void f2(signed char x) {} +void f3(unsigned char x) {} +void f4(signed char x) {} + +/* Twin functions for std::uint8_t and std::int8_t */ +void f9(std::uint8_t x) {} +void f10(std::int8_t x) {} +void f11(std::uint8_t x) {} +void f12(std::int8_t x) {} + +template void f5(T x) { unsigned char y = x; } +template void f6(T x) { signed char y = x; } +template void f7(T x) { unsigned char y = x; } +template void f8(T x) { signed char y = x; } + +/* Twin template functions for std::uint8_t and std::int8_t */ +template void f13(T x) { std::uint8_t y = x; } +template void f14(T x) { std::int8_t y = x; } +template void f15(T x) { std::uint8_t y = x; } +template void f16(T x) { std::int8_t y = x; } + +template class C9 { +public: + C9(T y) : x(y) {} + +private: + unsigned char x; +}; + +template class C10 { +public: + C10(T y) : x(y) {} + +private: + signed char x; +}; + +template class C11 { +public: + C11(T y) : x(y) {} + +private: + signed char x; +}; + +template class C12 { +public: + C12(T y) : x(y) {} + +private: + signed char x; +}; + +/* Twin template classes for std::uint8_t and std::int8_t */ +template class C13 { +public: + C13(T y) : x(y) {} + +private: + std::uint8_t x; +}; + +template class C14 { +public: + C14(T y) : x(y) {} + +private: + std::int8_t x; +}; + +template class C15 { +public: + C15(T y) : x(y) {} + +private: + std::int8_t x; +}; + +template class C16 { +public: + C16(T y) : x(y) {} + +private: + std::int8_t x; +}; + +int main() { + + /* ========== 1. Assigning a char to another char ========== */ + + /* ===== 1-1. Assigning a char to a char variable ===== */ + + unsigned char x1 = 1; + unsigned char y1 = + x1; // COMPLIANT: unsigned char assigned to an unsigned char + + signed char x2 = 1; + signed char y2 = x2; // COMPLIANT: signed char assigned to a signed char + + char x3 = 'x'; + unsigned char y3 = x3; // NON-COMPLIANT: plain char assigned to a unsigned char + + char x4 = 'x'; + signed char y4 = x4; // NON-COMPLIANT: plain char assigned to a signed char + + /* Twin cases with std::uint8_t and std::int8_t */ + std::uint8_t x5 = 1; + std::uint8_t y5 = + x5; // COMPLIANT: std::uint8_t assigned to a std::uint8_t + + std::int8_t x6 = 1; + std::int8_t y6 = x6; // COMPLIANT: std::int8_t assigned to a std::int8_t + + char x7 = 'x'; + std::uint8_t y7 = x7; // NON-COMPLIANT: plain char assigned to a std::uint8_t + + char x8 = 'x'; + std::int8_t y8 = x8; // NON-COMPLIANT: plain char assigned to a std::int8_t + + /* ===== 1-2. Assigning a char to a char member ===== */ + + C1 c1; // COMPLIANT: unsigned char arg passed to an unsigned + // char member through a template + + C2 c2; // COMPLIANT: signed char arg passed to a signed char + // member through a template + + C3 c3; // NON-COMPLIANT: plain char arg passed to a unsigned char + // member through a template + + C4 c4; // NON-COMPLIANT: plain char arg passed to a signed char + // member through a template + + /* Twin cases with std::uint8_t and std::int8_t */ + C5 c5; // COMPLIANT: std::uint8_t arg passed to a std::uint8_t + // member through a template + + C6 c6; // COMPLIANT: std::int8_t arg passed to a std::int8_t + // member through a template + + C7 c7; // NON-COMPLIANT: plain char arg passed to a std::uint8_t + // member through a template + + C8 c8; // NON-COMPLIANT: plain char arg passed to a std::int8_t + // member through a template + + /* ========== 1-3. Assigning a char to a char through a pointer ========== */ + + unsigned char x9 = 1; + unsigned char *y9 = &x9; + unsigned char z1 = + *y9; // COMPLIANT: unsigned char assigned to a *&unsigned char + + signed char x10 = 1; + signed char *y10 = &x10; + signed char z2 = *y10; // COMPLIANT: signed char assigned to an *&signed char + + char x11 = 1; + char *y11 = &x11; + unsigned char z3 = + *y11; // NON-COMPLIANT: plain char assigned to an *&unsigned char + + char x12 = 1; + char *y12 = &x12; + signed char z4 = + *y12; // NON-COMPLIANT: plain char assigned to an *&signed char + + /* Twin cases with std::uint8_t and std::int8_t */ + std::uint8_t x13 = 1; + std::uint8_t *y13 = &x13; + std::uint8_t z5 = + *y13; // COMPLIANT: std::uint8_t assigned to a *&std::uint8_t + + std::int8_t x14 = 1; + std::int8_t *y14 = &x14; + std::int8_t z6 = *y14; // COMPLIANT: std::int8_t assigned to an *&std::int8_t + + char x15 = 1; + char *y15 = &x15; + std::uint8_t z7 = + *y15; // NON-COMPLIANT: plain char assigned to an *&std::uint8_t + + char x16 = 1; + char *y16 = &x16; + std::int8_t z8 = + *y16; // NON-COMPLIANT: plain char assigned to an *&std::int8_t + + /* ========== 2. Passing a char argument to a char parameter ========== */ + + /* ===== 2-1. Passing char argument to a char parameter of a regular function + * ===== */ + + unsigned char a1 = 1; + f1(a1); // COMPLIANT: unsigned char arg passed to an unsigned char parameter + + signed char a2 = 1; + f2(a2); // COMPLIANT: signed char arg passed to a signed char parameter + + char a3 = 'a'; + f3(a3); // NON-COMPLIANT: plain char arg passed to a unsigned char parameter + + char a4 = 'a'; + f4(a4); // NON-COMPLIANT: plain char arg passed to a signed char parameter + + /* Twin cases with std::uint8_t and std::int8_t */ + std::uint8_t a5 = 1; + f9(a5); // COMPLIANT: std::uint8_t arg passed to a std::uint8_t parameter + + std::int8_t a6 = 1; + f10(a6); // COMPLIANT: std::int8_t arg passed to a std::int8_t parameter + + char a7 = 'a'; + f11(a7); // NON-COMPLIANT: plain char arg passed to a std::uint8_t parameter + + char a8 = 'a'; + f12(a8); // NON-COMPLIANT: plain char arg passed to a std::int8_t parameter + + /* ===== 2-2. Passing char argument to a char parameter through a template + * ===== */ + + unsigned char a9 = 1; + f5(a9); // COMPLIANT: unsigned char arg passed to an unsigned char parameter + // through a template + + signed char a10 = 1; + f6(a10); // COMPLIANT: signed char arg passed to a signed char parameter + // through a template + + char a11 = 'a'; + f7(a11); // NON-COMPLIANT: plain char arg passed to an unsigned char parameter + // through a template + + char a12 = 'a'; + f8(a12); // NON-COMPLIANT: plain char arg passed to a signed char parameter through + // a template + + /* Twin cases with std::uint8_t and std::int8_t */ + std::uint8_t a13 = 1; + f13(a13); // COMPLIANT: std::uint8_t arg passed to a std::uint8_t parameter + // through a template + + std::int8_t a14 = 1; + f14(a14); // COMPLIANT: std::int8_t arg passed to a std::int8_t parameter + // through a template + + char a15 = 'a'; + f15(a15); // NON-COMPLIANT: plain char arg passed to a std::uint8_t parameter + // through a template + + char a16 = 'a'; + f16(a16); // NON-COMPLIANT: plain char arg passed to a std::int8_t parameter through + // a template + + /* ========== 2-3. Passing a char argument to a char parameter through a + * template ========== */ + + unsigned char a17 = 1; + C9 c9( + a17); // COMPLIANT: unsigned char arg passed to an unsigned char parameter + // of a constructor through a template + + signed char a18 = 1; + C10 c10(a18); // COMPLIANT: signed char arg passed to an signed + // char parameter of a constructor through a template + + char a19 = 'a'; + C11 c11(a19); // NON-COMPLIANT: plain char arg passed to a signed char + // parameter of a constructor through a template + + char a20 = 'a'; + C12 c12(a20); // NON-COMPLIANT: plain char arg passed to an signed char + // parameter of a constructor through a template + + /* Twin cases with std::uint8_t and std::int8_t */ + std::uint8_t a21 = 1; + C13 c13( + a21); // COMPLIANT: std::uint8_t arg passed to a std::uint8_t parameter + // of a constructor through a template + + std::int8_t a22 = 1; + C14 c14(a22); // COMPLIANT: std::int8_t arg passed to a std::int8_t + // parameter of a constructor through a template + + char a23 = 'a'; + C15 c15(a23); // NON-COMPLIANT: plain char arg passed to a std::int8_t + // parameter of a constructor through a template + + char a24 = 'a'; + C16 c16(a24); // NON-COMPLIANT: plain char arg passed to a std::int8_t + // parameter of a constructor through a template } \ No newline at end of file