diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Classes2.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Classes2.qll new file mode 100644 index 000000000..780acb1ca --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Classes2.qll @@ -0,0 +1,129 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Classes2Query = + TVirtualInheritanceNotAllowedQuery() or + TMemberSpecifiersNotUsedAppropriatelyQuery() or + TPrivateAndPublicDataMembersMixedQuery() or + TInvalidSignatureForSpecialMemberFunctionQuery() or + TNonExplicitConversionMemberQuery() or + TLogicalAndAndLogicalOrOperatorsOverloadedQuery() or + TInvalidOperatorOverloadedAsMemberFunctionQuery() + +predicate isClasses2QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `virtualInheritanceNotAllowed` query + Classes2Package::virtualInheritanceNotAllowedQuery() and + queryId = + // `@id` for the `virtualInheritanceNotAllowed` query + "cpp/misra/virtual-inheritance-not-allowed" and + ruleId = "RULE-13-1-1" and + category = "advisory" + or + query = + // `Query` instance for the `memberSpecifiersNotUsedAppropriately` query + Classes2Package::memberSpecifiersNotUsedAppropriatelyQuery() and + queryId = + // `@id` for the `memberSpecifiersNotUsedAppropriately` query + "cpp/misra/member-specifiers-not-used-appropriately" and + ruleId = "RULE-13-3-1" and + category = "required" + or + query = + // `Query` instance for the `privateAndPublicDataMembersMixed` query + Classes2Package::privateAndPublicDataMembersMixedQuery() and + queryId = + // `@id` for the `privateAndPublicDataMembersMixed` query + "cpp/misra/private-and-public-data-members-mixed" and + ruleId = "RULE-14-1-1" and + category = "advisory" + or + query = + // `Query` instance for the `invalidSignatureForSpecialMemberFunction` query + Classes2Package::invalidSignatureForSpecialMemberFunctionQuery() and + queryId = + // `@id` for the `invalidSignatureForSpecialMemberFunction` query + "cpp/misra/invalid-signature-for-special-member-function" and + ruleId = "RULE-15-0-2" and + category = "advisory" + or + query = + // `Query` instance for the `nonExplicitConversionMember` query + Classes2Package::nonExplicitConversionMemberQuery() and + queryId = + // `@id` for the `nonExplicitConversionMember` query + "cpp/misra/non-explicit-conversion-member" and + ruleId = "RULE-15-1-3" and + category = "required" + or + query = + // `Query` instance for the `logicalAndAndLogicalOrOperatorsOverloaded` query + Classes2Package::logicalAndAndLogicalOrOperatorsOverloadedQuery() and + queryId = + // `@id` for the `logicalAndAndLogicalOrOperatorsOverloaded` query + "cpp/misra/logical-and-and-logical-or-operators-overloaded" and + ruleId = "RULE-16-5-1" and + category = "required" + or + query = + // `Query` instance for the `invalidOperatorOverloadedAsMemberFunction` query + Classes2Package::invalidOperatorOverloadedAsMemberFunctionQuery() and + queryId = + // `@id` for the `invalidOperatorOverloadedAsMemberFunction` query + "cpp/misra/invalid-operator-overloaded-as-member-function" and + ruleId = "RULE-16-6-1" and + category = "advisory" +} + +module Classes2Package { + Query virtualInheritanceNotAllowedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `virtualInheritanceNotAllowed` query + TQueryCPP(TClasses2PackageQuery(TVirtualInheritanceNotAllowedQuery())) + } + + Query memberSpecifiersNotUsedAppropriatelyQuery() { + //autogenerate `Query` type + result = + // `Query` type for `memberSpecifiersNotUsedAppropriately` query + TQueryCPP(TClasses2PackageQuery(TMemberSpecifiersNotUsedAppropriatelyQuery())) + } + + Query privateAndPublicDataMembersMixedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `privateAndPublicDataMembersMixed` query + TQueryCPP(TClasses2PackageQuery(TPrivateAndPublicDataMembersMixedQuery())) + } + + Query invalidSignatureForSpecialMemberFunctionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `invalidSignatureForSpecialMemberFunction` query + TQueryCPP(TClasses2PackageQuery(TInvalidSignatureForSpecialMemberFunctionQuery())) + } + + Query nonExplicitConversionMemberQuery() { + //autogenerate `Query` type + result = + // `Query` type for `nonExplicitConversionMember` query + TQueryCPP(TClasses2PackageQuery(TNonExplicitConversionMemberQuery())) + } + + Query logicalAndAndLogicalOrOperatorsOverloadedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `logicalAndAndLogicalOrOperatorsOverloaded` query + TQueryCPP(TClasses2PackageQuery(TLogicalAndAndLogicalOrOperatorsOverloadedQuery())) + } + + Query invalidOperatorOverloadedAsMemberFunctionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `invalidOperatorOverloadedAsMemberFunction` query + TQueryCPP(TClasses2PackageQuery(TInvalidOperatorOverloadedAsMemberFunctionQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll index abd6aeff9..57a8407c7 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll @@ -8,6 +8,7 @@ import BannedLibraries import BannedSyntax import BannedTypes import Classes +import Classes2 import Comments import Concurrency import Conditionals @@ -63,6 +64,7 @@ newtype TCPPQuery = TBannedSyntaxPackageQuery(BannedSyntaxQuery q) or TBannedTypesPackageQuery(BannedTypesQuery q) or TClassesPackageQuery(ClassesQuery q) or + TClasses2PackageQuery(Classes2Query q) or TCommentsPackageQuery(CommentsQuery q) or TConcurrencyPackageQuery(ConcurrencyQuery q) or TConditionalsPackageQuery(ConditionalsQuery q) or @@ -118,6 +120,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat isBannedSyntaxQueryMetadata(query, queryId, ruleId, category) or isBannedTypesQueryMetadata(query, queryId, ruleId, category) or isClassesQueryMetadata(query, queryId, ruleId, category) or + isClasses2QueryMetadata(query, queryId, ruleId, category) or isCommentsQueryMetadata(query, queryId, ruleId, category) or isConcurrencyQueryMetadata(query, queryId, ruleId, category) or isConditionalsQueryMetadata(query, queryId, ruleId, category) or diff --git a/cpp/misra/src/rules/RULE-13-1-1/VirtualInheritanceNotAllowed.ql b/cpp/misra/src/rules/RULE-13-1-1/VirtualInheritanceNotAllowed.ql new file mode 100644 index 000000000..f73d4b782 --- /dev/null +++ b/cpp/misra/src/rules/RULE-13-1-1/VirtualInheritanceNotAllowed.ql @@ -0,0 +1,23 @@ +/** + * @id cpp/misra/virtual-inheritance-not-allowed + * @name RULE-13-1-1: Classes should not be inherited virtually + * @description Virtual inheritance should not be used as it may lead to unexpected behavior. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-13-1-1 + * scope/single-translation-unit + * correctness + * readability + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra + +from VirtualClassDerivation vcd +where not isExcluded(vcd, Classes2Package::virtualInheritanceNotAllowedQuery()) +select vcd, + "Class '" + vcd.getDerivedClass().getName() + "' inherits virtually from '" + + vcd.getBaseClass().getName() + "'." diff --git a/cpp/misra/src/rules/RULE-13-3-1/MemberSpecifiersNotUsedAppropriately.ql b/cpp/misra/src/rules/RULE-13-3-1/MemberSpecifiersNotUsedAppropriately.ql new file mode 100644 index 000000000..464aa9f73 --- /dev/null +++ b/cpp/misra/src/rules/RULE-13-3-1/MemberSpecifiersNotUsedAppropriately.ql @@ -0,0 +1,59 @@ +/** + * @id cpp/misra/member-specifiers-not-used-appropriately + * @name RULE-13-3-1: User-declared member functions shall use the virtual, override and final specifiers appropriately + * @description Appropriate use of specifiers on member functions more clearly indicate the + * intention of the function. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-13-3-1 + * scope/single-translation-unit + * readability + * maintainability + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra + +from MemberFunction f, string message +where + not isExcluded(f, Classes2Package::memberSpecifiersNotUsedAppropriatelyQuery()) and + ( + // Case 1: Specifiers incompatible with explicitly virtual + f.isDeclaredVirtual() and + exists(Specifier s | + s = f.getASpecifier() and + s.getName() = ["final", "override"] + ) and + message = + "Member function '" + f.getName() + + "' uses redundant 'virtual' and 'final' specifiers together." + or + // Case 2: Redundant 'virtual' specifier + f.overrides(_) and + f.isDeclaredVirtual() and + message = + "Member function '" + f.getName() + + "' overrides a base function but uses 'virtual' specifier." + or + // Case 3: both 'override' and 'final' specifiers on an overridden virtual function + f.isVirtual() and + not f.isDeclaredVirtual() and + f.isOverride() and + f.isFinal() and + message = + "Member function '" + f.getName() + + "' uses redundant 'override' and 'final' specifiers together." + or + // Case 5: overrides a virtual function but has no override or final specifier + f.overrides(_) and + not f.isDeclaredVirtual() and + not f.isOverride() and + not f.isFinal() and + message = + "Member function '" + f.getName() + + "' overrides a base function but lacks 'override' or 'final' specifier." + ) +select f, message diff --git a/cpp/misra/src/rules/RULE-14-1-1/PrivateAndPublicDataMembersMixed.ql b/cpp/misra/src/rules/RULE-14-1-1/PrivateAndPublicDataMembersMixed.ql new file mode 100644 index 000000000..3ed02db0d --- /dev/null +++ b/cpp/misra/src/rules/RULE-14-1-1/PrivateAndPublicDataMembersMixed.ql @@ -0,0 +1,48 @@ +/** + * @id cpp/misra/private-and-public-data-members-mixed + * @name RULE-14-1-1: Non-static data members should be either all private or all public + * @description Mixing public and private data members in a class obfuscates the range of valid + * states allowable for that class. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-14-1-1 + * scope/single-translation-unit + * maintainability + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra + +from Class c, Field f +where + not isExcluded(f, Classes2Package::privateAndPublicDataMembersMixedQuery()) and + f = c.getAMember() and + not f.isStatic() and + ( + // Protected data members is not allowed. + f.isProtected() + or + // Public and private data members cannot be mixed. + exists(Field other | + other = c.getAMember() and + not other.isStatic() and + other != f and + ( + f.isPublic() and other.isPrivate() + or + f.isPrivate() and other.isPublic() + or + f.isProtected() and + (other.isPublic() or other.isPrivate()) + or + (f.isPublic() or f.isPrivate()) and + other.isProtected() + ) + ) + ) +select f, + "Non-static data member '" + f.getName() + + "' has mixed access level with other data members in class '" + c.getName() + "'." diff --git a/cpp/misra/src/rules/RULE-15-0-2/InvalidSignatureForSpecialMemberFunction.ql b/cpp/misra/src/rules/RULE-15-0-2/InvalidSignatureForSpecialMemberFunction.ql new file mode 100644 index 000000000..d39cea267 --- /dev/null +++ b/cpp/misra/src/rules/RULE-15-0-2/InvalidSignatureForSpecialMemberFunction.ql @@ -0,0 +1,103 @@ +/** + * @id cpp/misra/invalid-signature-for-special-member-function + * @name RULE-15-0-2: User-provided copy and move member functions of a class should have appropriate signatures + * @description Proper implementations of copy and move constructors and assignment operators ensure + * that resources are managed correctly. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-15-0-2 + * scope/single-translation-unit + * correctness + * maintainability + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra +import codeql.util.Boolean + +pragma[inline] +predicate checkSignature( + MemberFunction f, Boolean noexcept, Boolean lvalueQualified, Boolean rvalueRef, string err +) { + f.getNumberOfParameters() > 1 and + err = "too many parameters" + or + noexcept = true and + not f.isNoExcept() and + err = "should be noexcept" + or + lvalueQualified = true and + not f.isLValueRefQualified() and + err = "should be lvalue-qualified" + or + lvalueQualified = false and + f.isLValueRefQualified() and + err = "should not be lvalue-qualified" + or + rvalueRef = true and + not isRvalueRefX(f.getParameter(0).getType(), f.getDeclaringType()) and + err = "should take rvalue reference to class or struct type" + or + rvalueRef = false and + not isConstRefX(f.getParameter(0).getType(), f.getDeclaringType()) and + err = "should take const reference to class or struct type" + or + f.isVirtual() and + err = "should not be virtual" + or + not f instanceof Constructor and + not f.getType().(LValueReferenceType).getBaseType() = f.getDeclaringType() and + not f.getType() instanceof VoidType and + err = "should return void or lvalue reference to class or struct type" + or + not f instanceof Constructor and + f.isExplicit() and + err = "should not be explicit" +} + +predicate isRvalueRefX(Type t, Class x) { + // X && + t.(RValueReferenceType).getBaseType() = x +} + +predicate isConstRefX(Type t, Class x) { + exists(SpecifiedType st | + // X const & + st = t.(LValueReferenceType).getBaseType() and + st.getSpecifierString() = "const" and + st.getBaseType() = x + or + // const X & + st = t and + st.getSpecifierString() = "const" and + st.getBaseType().(LValueReferenceType).getBaseType() = x + ) +} + +from MemberFunction f, string err, string prefix +where + not isExcluded(f, Classes2Package::invalidSignatureForSpecialMemberFunctionQuery()) and + not f.isDeleted() and + not f.isDefaulted() and + not f.isCompilerGenerated() and + ( + f instanceof CopyConstructor and + prefix = "Copy constructor" and + checkSignature(f, false, false, false, err) + or + f instanceof MoveConstructor and + prefix = "Move constructor" and + checkSignature(f, true, false, true, err) + or + f instanceof CopyAssignmentOperator and + prefix = "Copy assignment operator" and + checkSignature(f, false, true, false, err) + or + f instanceof MoveAssignmentOperator and + prefix = "Move assignment operator" and + checkSignature(f, true, true, true, err) + ) +select f, prefix + " on type $@ " + err + ".", f.getDeclaringType(), f.getDeclaringType().getName() diff --git a/cpp/misra/src/rules/RULE-15-1-3/NonExplicitConversionMember.ql b/cpp/misra/src/rules/RULE-15-1-3/NonExplicitConversionMember.ql new file mode 100644 index 000000000..c85e0d259 --- /dev/null +++ b/cpp/misra/src/rules/RULE-15-1-3/NonExplicitConversionMember.ql @@ -0,0 +1,48 @@ +/** + * @id cpp/misra/non-explicit-conversion-member + * @name RULE-15-1-3: Conversion operators and constructors that are callable with a single argument shall be explicit + * @description Implicit conversions can lead to unexpected behavior that is preventable by + * declaring conversion operators and constructors as explicit. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-15-1-3 + * scope/single-translation-unit + * correctness + * readability + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra + +predicate isCallableWithOneArgument(Constructor c) { + // At least one parameter + c.getNumberOfParameters() > 0 and + // At most one parameter without an initializer + count(Parameter p | p = c.getAParameter() and not p.hasInitializer()) <= 1 +} + +from Element x, string message +where + not isExcluded(x, Classes2Package::nonExplicitConversionMemberQuery()) and + ( + exists(Constructor c | + x = c and + not c.isExplicit() and + not c instanceof CopyConstructor and + not c instanceof MoveConstructor and + isCallableWithOneArgument(c) and + message = + "Constructor '" + c.getName() + + "' that is callable with a single argument shall be explicit." + ) + or + exists(ConversionOperator co | + x = co and + not co.isExplicit() and + message = "Conversion operator shall be explicit." + ) + ) +select x, message diff --git a/cpp/misra/src/rules/RULE-16-5-1/LogicalAndAndLogicalOrOperatorsOverloaded.ql b/cpp/misra/src/rules/RULE-16-5-1/LogicalAndAndLogicalOrOperatorsOverloaded.ql new file mode 100644 index 000000000..5eaa1aa14 --- /dev/null +++ b/cpp/misra/src/rules/RULE-16-5-1/LogicalAndAndLogicalOrOperatorsOverloaded.ql @@ -0,0 +1,32 @@ +/** + * @id cpp/misra/logical-and-and-logical-or-operators-overloaded + * @name RULE-16-5-1: The logical AND and logical OR operators shall not be overloaded + * @description Overloaded logical AND and logical OR operators change short-circuiting behavior and + * can lead to unexpected results. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-16-5-1 + * scope/single-translation-unit + * correctness + * readability + * maintainability + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra + +from Element element, string message +where + not isExcluded(element, Classes2Package::logicalAndAndLogicalOrOperatorsOverloadedQuery()) and + // Case 1: Overloaded logical operator definitions + exists(Operator op | + op = element and + (op.getName() = "operator&&" or op.getName() = "operator||") and + message = + "Overloaded logical operator '" + op.getName() + + "' changes short-circuit evaluation behavior." + ) +select element, message diff --git a/cpp/misra/src/rules/RULE-16-6-1/InvalidOperatorOverloadedAsMemberFunction.ql b/cpp/misra/src/rules/RULE-16-6-1/InvalidOperatorOverloadedAsMemberFunction.ql new file mode 100644 index 000000000..7bf5dc703 --- /dev/null +++ b/cpp/misra/src/rules/RULE-16-6-1/InvalidOperatorOverloadedAsMemberFunction.ql @@ -0,0 +1,30 @@ +/** + * @id cpp/misra/invalid-operator-overloaded-as-member-function + * @name RULE-16-6-1: Symmetrical operators should only be implemented as non-member functions + * @description Function resolution of operators overloaded as member functions is dependent on + * ordering and available conversions, which can lead to unexpected behavior. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-16-6-1 + * scope/single-translation-unit + * correctness + * maintainability + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra + +from MemberFunction f +where + not isExcluded(f, Classes2Package::invalidOperatorOverloadedAsMemberFunctionQuery()) and + f.hasName([ + "operator+", "operator-", "operator*", "operator/", "operator%", "operator==", "operator!=", + "operator<", "operator<=", "operator>=", "operator>", "operator^", "operator&", "operator|", + "operator&&", "operator||" + ]) and + f.getNumberOfParameters() = 1 +select f, + "Symmetrical operator '" + f.getName() + "' should be implemented as a non-member function." diff --git a/cpp/misra/test/rules/RULE-13-1-1/VirtualInheritanceNotAllowed.expected b/cpp/misra/test/rules/RULE-13-1-1/VirtualInheritanceNotAllowed.expected new file mode 100644 index 000000000..6bd423bcf --- /dev/null +++ b/cpp/misra/test/rules/RULE-13-1-1/VirtualInheritanceNotAllowed.expected @@ -0,0 +1,10 @@ +| test.cpp:15:26:15:44 | derivation | Class 'VirtualDerived1' inherits virtually from 'Base'. | +| test.cpp:19:26:19:44 | derivation | Class 'VirtualDerived2' inherits virtually from 'Base'. | +| test.cpp:23:26:23:44 | derivation | Class 'VirtualMultiple' inherits virtually from 'Base'. | +| test.cpp:24:26:24:51 | derivation | Class 'VirtualMultiple' inherits virtually from 'AnotherBase'. | +| test.cpp:29:32:29:51 | derivation | Class 'PrivateVirtualDerived' inherits virtually from 'Base'. | +| test.cpp:33:34:33:55 | derivation | Class 'ProtectedVirtualDerived' inherits virtually from 'Base'. | +| test.cpp:59:22:59:40 | derivation | Class 'DiamondLeft' inherits virtually from 'Base'. | +| test.cpp:63:23:63:41 | derivation | Class 'DiamondRight' inherits virtually from 'Base'. | +| test.cpp:75:19:75:37 | derivation | Class 'Level1' inherits virtually from 'Base'. | +| test.cpp:79:19:79:39 | derivation | Class 'Level2' inherits virtually from 'Level1'. | diff --git a/cpp/misra/test/rules/RULE-13-1-1/VirtualInheritanceNotAllowed.qlref b/cpp/misra/test/rules/RULE-13-1-1/VirtualInheritanceNotAllowed.qlref new file mode 100644 index 000000000..3c3eb1db3 --- /dev/null +++ b/cpp/misra/test/rules/RULE-13-1-1/VirtualInheritanceNotAllowed.qlref @@ -0,0 +1 @@ +rules/RULE-13-1-1/VirtualInheritanceNotAllowed.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-13-1-1/test.cpp b/cpp/misra/test/rules/RULE-13-1-1/test.cpp new file mode 100644 index 000000000..aec227c12 --- /dev/null +++ b/cpp/misra/test/rules/RULE-13-1-1/test.cpp @@ -0,0 +1,82 @@ +#include + +// Base class for testing virtual inheritance +struct Base { + virtual std::int32_t getValue() { return 1; } + std::int32_t m1; +}; + +// Another base class +struct AnotherBase { + std::int32_t m2; +}; + +// Test cases for virtual inheritance (non-compliant) +struct VirtualDerived1 : public virtual Base { // NON_COMPLIANT + std::int32_t getDoubleValue() { return getValue() * 2; } +}; + +struct VirtualDerived2 : public virtual Base { // NON_COMPLIANT + std::int32_t getValue() override { return 3; } +}; + +struct VirtualMultiple : public virtual Base, + public virtual AnotherBase { // NON_COMPLIANT + std::int32_t m3; +}; + +// Test cases for private virtual inheritance (non-compliant) +struct PrivateVirtualDerived : private virtual Base { // NON_COMPLIANT + std::int32_t m4; +}; + +struct ProtectedVirtualDerived : protected virtual Base { // NON_COMPLIANT + std::int32_t m5; +}; + +// Test cases for regular inheritance (compliant) +struct RegularDerived1 : public Base { // COMPLIANT + std::int32_t getTripleValue() { return getValue() * 3; } +}; + +struct RegularDerived2 : public Base { // COMPLIANT + std::int32_t getValue() override { return 4; } +}; + +struct RegularMultiple : public Base, public AnotherBase { // COMPLIANT + std::int32_t m6; +}; + +struct PrivateRegularDerived : private Base { // COMPLIANT + std::int32_t m7; +}; + +struct ProtectedRegularDerived : protected Base { // COMPLIANT + std::int32_t m8; +}; + +// Diamond hierarchy with virtual inheritance (non-compliant) +struct DiamondLeft : public virtual Base { // NON_COMPLIANT + std::int32_t leftMethod() { return getValue() + 10; } +}; + +struct DiamondRight : public virtual Base { // NON_COMPLIANT + std::int32_t getValue() override { return 5; } +}; + +struct DiamondBottom : public DiamondLeft, public DiamondRight { // COMPLIANT + std::int32_t bottomMethod() { + return leftMethod(); // Calls DiamondRight::getValue() due to dominance + } +}; + +void test_edge_cases() { + // Multiple levels of virtual inheritance + struct Level1 : public virtual Base { // NON_COMPLIANT + std::int32_t m9; + }; + + struct Level2 : public virtual Level1 { // NON_COMPLIANT + std::int32_t m10; + }; +} \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-13-3-1/MemberSpecifiersNotUsedAppropriately.expected b/cpp/misra/test/rules/RULE-13-3-1/MemberSpecifiersNotUsedAppropriately.expected new file mode 100644 index 000000000..1c432077f --- /dev/null +++ b/cpp/misra/test/rules/RULE-13-3-1/MemberSpecifiersNotUsedAppropriately.expected @@ -0,0 +1,13 @@ +| test.cpp:14:16:14:17 | f1 | Member function 'f1' uses redundant 'virtual' and 'final' specifiers together. | +| test.cpp:15:16:15:17 | f2 | Member function 'f2' uses redundant 'virtual' and 'final' specifiers together. | +| test.cpp:33:16:33:25 | m1_virtual | Member function 'm1_virtual' overrides a base function but uses 'virtual' specifier. | +| test.cpp:38:16:38:25 | m1_virtual | Member function 'm1_virtual' overrides a base function but uses 'virtual' specifier. | +| test.cpp:38:16:38:25 | m1_virtual | Member function 'm1_virtual' uses redundant 'virtual' and 'final' specifiers together. | +| test.cpp:43:16:43:25 | m1_virtual | Member function 'm1_virtual' overrides a base function but uses 'virtual' specifier. | +| test.cpp:43:16:43:25 | m1_virtual | Member function 'm1_virtual' uses redundant 'virtual' and 'final' specifiers together. | +| test.cpp:48:8:48:17 | m1_virtual | Member function 'm1_virtual' uses redundant 'override' and 'final' specifiers together. | +| test.cpp:53:8:53:17 | m1_virtual | Member function 'm1_virtual' overrides a base function but lacks 'override' or 'final' specifier. | +| test.cpp:69:16:69:28 | m1_nonvirtual | Member function 'm1_nonvirtual' uses redundant 'virtual' and 'final' specifiers together. | +| test.cpp:90:3:90:6 | ~C12 | Member function '~C12' overrides a base function but lacks 'override' or 'final' specifier. | +| test.cpp:95:11:95:14 | ~C13 | Member function '~C13' overrides a base function but uses 'virtual' specifier. | +| test.cpp:95:11:95:14 | ~C13 | Member function '~C13' uses redundant 'virtual' and 'final' specifiers together. | diff --git a/cpp/misra/test/rules/RULE-13-3-1/MemberSpecifiersNotUsedAppropriately.qlref b/cpp/misra/test/rules/RULE-13-3-1/MemberSpecifiersNotUsedAppropriately.qlref new file mode 100644 index 000000000..a48b329a1 --- /dev/null +++ b/cpp/misra/test/rules/RULE-13-3-1/MemberSpecifiersNotUsedAppropriately.qlref @@ -0,0 +1 @@ +rules/RULE-13-3-1/MemberSpecifiersNotUsedAppropriately.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-13-3-1/test.cpp b/cpp/misra/test/rules/RULE-13-3-1/test.cpp new file mode 100644 index 000000000..5474d5e6d --- /dev/null +++ b/cpp/misra/test/rules/RULE-13-3-1/test.cpp @@ -0,0 +1,107 @@ +class VirtualMemberBase { +public: + virtual void m1_virtual() {} // COMPLIANT +}; + +class NonvirtualMemberBase { +public: + void m1_nonvirtual() {} // COMPLIANT +}; + +// Invalid base class declarations +class InvalidBaseClass { +public: + virtual void f1() final = 0; // NON_COMPLIANT + virtual void f2() final {} // NON_COMPLIANT + + // Does not compile: final nonvirtual function + // void f3() final {} // NON_COMPLIANT +}; + +class C1 : public VirtualMemberBase { +public: + void m1_virtual() override {} // COMPLIANT +}; + +class C2 : public VirtualMemberBase { +public: + void m1_virtual() final {} // COMPLIANT +}; + +class C3 : public VirtualMemberBase { +public: + virtual void m1_virtual() {} // NON_COMPLIANT +}; + +class C4 : public VirtualMemberBase { +public: + virtual void m1_virtual() override {} // NON_COMPLIANT +}; + +class C5 : public VirtualMemberBase { +public: + virtual void m1_virtual() final {} // NON_COMPLIANT +}; + +class C6 : public VirtualMemberBase { +public: + void m1_virtual() final override {} // NON_COMPLIANT +}; + +class C7 : public VirtualMemberBase { +public: + void m1_virtual() {} // NON_COMPLIANT +}; + +class C8 : public NonvirtualMemberBase { +public: + void m1_nonvirtual() {} // COMPLIANT +}; + +class C9 : public NonvirtualMemberBase { +public: + virtual void m1_nonvirtual() { + } // COMPLIANT: Note that this violates rule 6.4.2 +}; + +class C10 : public NonvirtualMemberBase { +public: + virtual void m1_nonvirtual() final { + } // NON_COMPLIANT: Note that this also violates rule 6.4.2 +}; + +class VirtualDestructorBase { +public: + virtual ~VirtualDestructorBase() = default; // COMPLIANT +}; + +class NonvirtualDestructorBase { +public: + ~NonvirtualDestructorBase() = default; // COMPLIANT +}; + +class C11 : public VirtualDestructorBase { +public: + ~C11() override = default; // COMPLIANT +}; + +class C12 : public VirtualDestructorBase { +public: + ~C12(); // NON_COMPLIANT +}; + +class C13 : public VirtualDestructorBase { +public: + virtual ~C13() override = default; // NON_COMPLIANT +}; + +class C14 : public NonvirtualDestructorBase { +public: + ~C14() = default; // COMPLIANT +}; + +class C15 : public NonvirtualDestructorBase { +public: + virtual ~C15() = + default; // COMPLIANT: Note that this does not violate rule 6.4.2 +}; \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-14-1-1/PrivateAndPublicDataMembersMixed.expected b/cpp/misra/test/rules/RULE-14-1-1/PrivateAndPublicDataMembersMixed.expected new file mode 100644 index 000000000..e0f0cf141 --- /dev/null +++ b/cpp/misra/test/rules/RULE-14-1-1/PrivateAndPublicDataMembersMixed.expected @@ -0,0 +1,12 @@ +| test.cpp:6:16:6:17 | m1 | Non-static data member 'm1' has mixed access level with other data members in class 'MixedAccessClass'. | +| test.cpp:8:16:8:17 | m2 | Non-static data member 'm2' has mixed access level with other data members in class 'MixedAccessClass'. | +| test.cpp:45:16:45:17 | m1 | Non-static data member 'm1' has mixed access level with other data members in class 'ProtectedAndPrivateClass'. | +| test.cpp:47:16:47:17 | m2 | Non-static data member 'm2' has mixed access level with other data members in class 'ProtectedAndPrivateClass'. | +| test.cpp:53:16:53:17 | m1 | Non-static data member 'm1' has mixed access level with other data members in class 'PublicAndProtectedClass'. | +| test.cpp:55:16:55:17 | m2 | Non-static data member 'm2' has mixed access level with other data members in class 'PublicAndProtectedClass'. | +| test.cpp:61:16:61:17 | m1 | Non-static data member 'm1' has mixed access level with other data members in class 'AllThreeAccessLevels'. | +| test.cpp:63:16:63:17 | m2 | Non-static data member 'm2' has mixed access level with other data members in class 'AllThreeAccessLevels'. | +| test.cpp:65:16:65:17 | m3 | Non-static data member 'm3' has mixed access level with other data members in class 'AllThreeAccessLevels'. | +| test.cpp:71:16:71:17 | m1 | Non-static data member 'm1' has mixed access level with other data members in class 'OnlyProtected'. | +| test.cpp:72:16:72:17 | m2 | Non-static data member 'm2' has mixed access level with other data members in class 'OnlyProtected'. | +| test.cpp:73:16:73:17 | m3 | Non-static data member 'm3' has mixed access level with other data members in class 'OnlyProtected'. | diff --git a/cpp/misra/test/rules/RULE-14-1-1/PrivateAndPublicDataMembersMixed.qlref b/cpp/misra/test/rules/RULE-14-1-1/PrivateAndPublicDataMembersMixed.qlref new file mode 100644 index 000000000..e9ac2aba8 --- /dev/null +++ b/cpp/misra/test/rules/RULE-14-1-1/PrivateAndPublicDataMembersMixed.qlref @@ -0,0 +1 @@ +rules/RULE-14-1-1/PrivateAndPublicDataMembersMixed.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-14-1-1/test.cpp b/cpp/misra/test/rules/RULE-14-1-1/test.cpp new file mode 100644 index 000000000..1c5320f23 --- /dev/null +++ b/cpp/misra/test/rules/RULE-14-1-1/test.cpp @@ -0,0 +1,95 @@ +#include + +// Test case: Class with mixed public and private non-static data members +class MixedAccessClass { +public: + std::int32_t m1; // NON_COMPLIANT +private: + std::int32_t m2; // NON_COMPLIANT +}; + +// Test case: Class with all public non-static data members +class AllPublicClass { +public: + std::int32_t m1; // COMPLIANT + std::int32_t m2; // COMPLIANT + std::int32_t m3; // COMPLIANT +}; + +// Test case: Class with all private non-static data members +class AllPrivateClass { +private: + std::int32_t m1; // COMPLIANT + std::int32_t m2; // COMPLIANT + std::int32_t m3; // COMPLIANT +}; + +// Test case: Struct with all public members (default access) +struct AllPublicStruct { + std::int32_t m1; // COMPLIANT + std::int32_t m2; // COMPLIANT +}; + +// Test case: Mixed access with static members should be compliant +class MixedWithStaticMembers { +public: + static std::int32_t s1; // COMPLIANT +private: + std::int32_t m1; // COMPLIANT + std::int32_t m2; // COMPLIANT +}; + +// Test case: Class with protected and private members +class ProtectedAndPrivateClass { +protected: + std::int32_t m1; // NON_COMPLIANT +private: + std::int32_t m2; // NON_COMPLIANT +}; + +// Test case: Class with public and protected members +class PublicAndProtectedClass { +public: + std::int32_t m1; // NON_COMPLIANT +protected: + std::int32_t m2; // NON_COMPLIANT +}; + +// Test case: Class with all three access levels +class AllThreeAccessLevels { +public: + std::int32_t m1; // NON_COMPLIANT +protected: + std::int32_t m2; // NON_COMPLIANT +private: + std::int32_t m3; // NON_COMPLIANT +}; + +// Test case: Class only protected members +class OnlyProtected { +protected: + std::int32_t m1; // NON_COMPLIANT + std::int32_t m2; // NON_COMPLIANT + std::int32_t m3; // NON_COMPLIANT +}; + +// Test case: Empty class +class EmptyClass { + // COMPLIANT - no data members +}; + +// Test case: Class with only static members +class OnlyStaticMembers { +public: + static std::int32_t s1; // COMPLIANT +private: + static std::int32_t s2; // COMPLIANT +}; + +// Test case: Class with only member functions +class OnlyMemberFunctions { +public: + void f1() {} // COMPLIANT +private: + void f2() {} // COMPLIANT +}; diff --git a/cpp/misra/test/rules/RULE-15-0-2/InvalidSignatureForSpecialMemberFunction.expected b/cpp/misra/test/rules/RULE-15-0-2/InvalidSignatureForSpecialMemberFunction.expected new file mode 100644 index 000000000..0ce0d3cc6 --- /dev/null +++ b/cpp/misra/test/rules/RULE-15-0-2/InvalidSignatureForSpecialMemberFunction.expected @@ -0,0 +1,11 @@ +| test.cpp:45:3:45:29 | NonCompliantCopyConstructor | Copy constructor on type $@ should take const reference to class or struct type. | test.cpp:42:7:42:33 | NonCompliantCopyConstructor | NonCompliantCopyConstructor | +| test.cpp:52:3:52:37 | NonCompliantMoveConstructorNoExcept | Move constructor on type $@ should be noexcept. | test.cpp:49:7:49:41 | NonCompliantMoveConstructorNoExcept | NonCompliantMoveConstructorNoExcept | +| test.cpp:61:3:61:11 | operator= | Copy assignment operator on type $@ should be lvalue-qualified. | test.cpp:57:7:57:46 | NonCompliantCopyAssignmentNoRefQualifier | NonCompliantCopyAssignmentNoRefQualifier | +| test.cpp:69:3:69:11 | operator= | Move assignment operator on type $@ should be lvalue-qualified. | test.cpp:65:7:65:46 | NonCompliantMoveAssignmentNoRefQualifier | NonCompliantMoveAssignmentNoRefQualifier | +| test.cpp:77:3:77:11 | operator= | Move assignment operator on type $@ should be noexcept. | test.cpp:73:7:73:40 | NonCompliantMoveAssignmentNoExcept | NonCompliantMoveAssignmentNoExcept | +| test.cpp:85:3:85:11 | operator= | Copy assignment operator on type $@ should take const reference to class or struct type. | test.cpp:81:7:81:43 | NonCompliantCopyAssignmentNonConstRef | NonCompliantCopyAssignmentNonConstRef | +| test.cpp:92:3:92:22 | NonCompliantVolatile | Copy constructor on type $@ should take const reference to class or struct type. | test.cpp:89:7:89:26 | NonCompliantVolatile | NonCompliantVolatile | +| test.cpp:100:3:100:11 | operator= | Copy assignment operator on type $@ should not be virtual. | test.cpp:96:7:96:25 | NonCompliantVirtual | NonCompliantVirtual | +| test.cpp:107:3:107:11 | operator= | Copy assignment operator on type $@ should take const reference to class or struct type. | test.cpp:103:7:103:29 | NonCompliantCopyByValue | NonCompliantCopyByValue | +| test.cpp:113:3:113:30 | NonCompliantDefaultArguments | Move constructor on type $@ too many parameters. | test.cpp:110:7:110:34 | NonCompliantDefaultArguments | NonCompliantDefaultArguments | +| test.cpp:158:3:158:20 | NonCompliantStruct | Copy constructor on type $@ should take const reference to class or struct type. | test.cpp:155:8:155:25 | NonCompliantStruct | NonCompliantStruct | diff --git a/cpp/misra/test/rules/RULE-15-0-2/InvalidSignatureForSpecialMemberFunction.qlref b/cpp/misra/test/rules/RULE-15-0-2/InvalidSignatureForSpecialMemberFunction.qlref new file mode 100644 index 000000000..b337b84fb --- /dev/null +++ b/cpp/misra/test/rules/RULE-15-0-2/InvalidSignatureForSpecialMemberFunction.qlref @@ -0,0 +1 @@ +rules/RULE-15-0-2/InvalidSignatureForSpecialMemberFunction.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-15-0-2/test.cpp b/cpp/misra/test/rules/RULE-15-0-2/test.cpp new file mode 100644 index 000000000..d3a3924b4 --- /dev/null +++ b/cpp/misra/test/rules/RULE-15-0-2/test.cpp @@ -0,0 +1,160 @@ +#include +#include + +// Test cases for Rule 15.0.2: User-provided copy and move member functions +// should have appropriate signatures + +// Compliant examples +class CompliantClass { +public: + CompliantClass() = default; + CompliantClass(CompliantClass const &); // COMPLIANT + CompliantClass(CompliantClass &&) noexcept; // COMPLIANT + CompliantClass &operator=(CompliantClass const &) &; // COMPLIANT + CompliantClass &operator=(CompliantClass &&) &noexcept; // COMPLIANT +}; + +class CompliantWithAlternatives { +public: + CompliantWithAlternatives() = default; + constexpr CompliantWithAlternatives( + const CompliantWithAlternatives &); // COMPLIANT + explicit constexpr CompliantWithAlternatives( + CompliantWithAlternatives &&) noexcept; // COMPLIANT + constexpr CompliantWithAlternatives & + operator=(const CompliantWithAlternatives &) &noexcept; // COMPLIANT + constexpr CompliantWithAlternatives & + operator=(CompliantWithAlternatives &&) &noexcept; // COMPLIANT +}; + +class CompliantVoidReturn { +public: + CompliantVoidReturn() = default; + CompliantVoidReturn(CompliantVoidReturn const &); + CompliantVoidReturn(CompliantVoidReturn &&) noexcept; + void + operator=(CompliantVoidReturn const &) &; // COMPLIANT - void return allowed + void operator=( + CompliantVoidReturn &&) &noexcept; // COMPLIANT - void return allowed +}; + +// Non-compliant examples +class NonCompliantCopyConstructor { +public: + NonCompliantCopyConstructor() = default; + NonCompliantCopyConstructor( + NonCompliantCopyConstructor &); // NON_COMPLIANT - non-const reference +}; + +class NonCompliantMoveConstructorNoExcept { +public: + NonCompliantMoveConstructorNoExcept() = default; + NonCompliantMoveConstructorNoExcept( + NonCompliantMoveConstructorNoExcept + &&); // NON_COMPLIANT - missing noexcept +}; + +class NonCompliantCopyAssignmentNoRefQualifier { +public: + NonCompliantCopyAssignmentNoRefQualifier() = default; + NonCompliantCopyAssignmentNoRefQualifier & + operator=(NonCompliantCopyAssignmentNoRefQualifier const + &); // NON_COMPLIANT - missing & qualifier +}; + +class NonCompliantMoveAssignmentNoRefQualifier { +public: + NonCompliantMoveAssignmentNoRefQualifier() = default; + NonCompliantMoveAssignmentNoRefQualifier & + operator=(NonCompliantMoveAssignmentNoRefQualifier + &&) noexcept; // NON_COMPLIANT - missing & qualifier +}; + +class NonCompliantMoveAssignmentNoExcept { +public: + NonCompliantMoveAssignmentNoExcept() = default; + NonCompliantMoveAssignmentNoExcept & + operator=(NonCompliantMoveAssignmentNoExcept &&) + &; // NON_COMPLIANT - missing noexcept +}; + +class NonCompliantCopyAssignmentNonConstRef { +public: + NonCompliantCopyAssignmentNonConstRef() = default; + NonCompliantCopyAssignmentNonConstRef & + operator=(NonCompliantCopyAssignmentNonConstRef &) + &; // NON_COMPLIANT - non-const reference +}; + +class NonCompliantVolatile { +public: + NonCompliantVolatile() = default; + NonCompliantVolatile(NonCompliantVolatile volatile const + &); // NON_COMPLIANT - volatile qualifier +}; + +class NonCompliantVirtual { +public: + NonCompliantVirtual() = default; + virtual NonCompliantVirtual & + operator=(NonCompliantVirtual const &) &; // NON_COMPLIANT - virtual +}; + +class NonCompliantCopyByValue { +public: + NonCompliantCopyByValue() = default; + NonCompliantCopyByValue & + operator=(NonCompliantCopyByValue) &; // NON_COMPLIANT - pass by value +}; + +class NonCompliantDefaultArguments { +public: + NonCompliantDefaultArguments() = default; + NonCompliantDefaultArguments( + NonCompliantDefaultArguments &&, + char c = + 'x') noexcept; // NON_COMPLIANT - default argument in move constructor +}; + +// Edge cases that are not copy/move constructors/operators +class NotCopyMoveOperations { +public: + NotCopyMoveOperations() = default; + NotCopyMoveOperations( + NotCopyMoveOperations const &, + char c); // COMPLIANT - not a copy constructor due to additional parameter + NotCopyMoveOperations( + NotCopyMoveOperations &&, + int i); // COMPLIANT - not a move constructor due to additional parameter +}; + +// Deleted operations - rule does not apply +class DeletedOperations { +public: + DeletedOperations() = default; + DeletedOperations(DeletedOperations const &) = + delete; // COMPLIANT - rule does not apply to deleted + DeletedOperations(DeletedOperations &&) = + delete; // COMPLIANT - rule does not apply to deleted + DeletedOperations &operator=(DeletedOperations const &) = + delete; // COMPLIANT - rule does not apply to deleted + DeletedOperations &operator=(DeletedOperations &&) = + delete; // COMPLIANT - rule does not apply to deleted +}; + +// Structs +struct CompliantStruct { +public: + CompliantStruct() = default; + CompliantStruct(CompliantStruct const &); // COMPLIANT + CompliantStruct(CompliantStruct &&) noexcept; // COMPLIANT + CompliantStruct &operator=(CompliantStruct const &) &; // COMPLIANT + CompliantStruct &operator=(CompliantStruct &&) &noexcept; // COMPLIANT +}; + +struct NonCompliantStruct { +public: + NonCompliantStruct() = default; + NonCompliantStruct( + NonCompliantStruct &); // NON_COMPLIANT - non-const reference +}; \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-15-1-3/NonExplicitConversionMember.expected b/cpp/misra/test/rules/RULE-15-1-3/NonExplicitConversionMember.expected new file mode 100644 index 000000000..43a5fc607 --- /dev/null +++ b/cpp/misra/test/rules/RULE-15-1-3/NonExplicitConversionMember.expected @@ -0,0 +1,6 @@ +| test.cpp:5:3:5:12 | TestClass1 | Constructor 'TestClass1' that is callable with a single argument shall be explicit. | +| test.cpp:6:3:6:23 | operator std::int32_t | Conversion operator shall be explicit. | +| test.cpp:23:3:23:12 | TestClass4 | Constructor 'TestClass4' that is callable with a single argument shall be explicit. | +| test.cpp:25:3:25:12 | TestClass4 | Constructor 'TestClass4' that is callable with a single argument shall be explicit. | +| test.cpp:44:3:44:17 | operator double | Conversion operator shall be explicit. | +| test.cpp:45:3:45:23 | operator const char * | Conversion operator shall be explicit. | diff --git a/cpp/misra/test/rules/RULE-15-1-3/NonExplicitConversionMember.qlref b/cpp/misra/test/rules/RULE-15-1-3/NonExplicitConversionMember.qlref new file mode 100644 index 000000000..35de2525e --- /dev/null +++ b/cpp/misra/test/rules/RULE-15-1-3/NonExplicitConversionMember.qlref @@ -0,0 +1 @@ +rules/RULE-15-1-3/NonExplicitConversionMember.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-15-1-3/test.cpp b/cpp/misra/test/rules/RULE-15-1-3/test.cpp new file mode 100644 index 000000000..c1470de11 --- /dev/null +++ b/cpp/misra/test/rules/RULE-15-1-3/test.cpp @@ -0,0 +1,47 @@ +#include + +class TestClass1 { +public: + TestClass1(std::int32_t a); // NON_COMPLIANT + operator std::int32_t() const; // NON_COMPLIANT +}; + +class TestClass2 { +public: + explicit TestClass2(std::int32_t a); // COMPLIANT + explicit operator bool() const; // COMPLIANT +}; + +class TestClass3 { +public: + TestClass3(const TestClass3 &other); // COMPLIANT - copy constructor + TestClass3(TestClass3 &&other); // COMPLIANT - move constructor +}; + +class TestClass4 { +public: + TestClass4(std::int32_t a, + std::int32_t b = 0); // NON_COMPLIANT - callable with one argument + TestClass4(char a = 'a', + std::int32_t b = 0); // NON_COMPLIANT - callable with one argument + TestClass4(char a, char b); // COMPLIANT - requires two arguments +}; + +class TestClass5 { +public: + explicit TestClass5(std::int32_t a, std::int32_t b = 0); // COMPLIANT + explicit TestClass5(char a = 'a', std::int32_t b = 0); // COMPLIANT +}; + +class TestClass6 { +public: + TestClass6(std::int32_t a, std::int32_t b, + std::int32_t c = 0); // COMPLIANT - requires at least two arguments +}; + +class TestClass7 { +public: + operator double() const; // NON_COMPLIANT + operator const char *() const; // NON_COMPLIANT + explicit operator void *() const; // COMPLIANT +}; \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-16-5-1/LogicalAndAndLogicalOrOperatorsOverloaded.expected b/cpp/misra/test/rules/RULE-16-5-1/LogicalAndAndLogicalOrOperatorsOverloaded.expected new file mode 100644 index 000000000..e45b6917c --- /dev/null +++ b/cpp/misra/test/rules/RULE-16-5-1/LogicalAndAndLogicalOrOperatorsOverloaded.expected @@ -0,0 +1,4 @@ +| test.cpp:13:6:13:15 | operator&& | Overloaded logical operator 'operator&&' changes short-circuit evaluation behavior. | +| test.cpp:17:6:17:15 | operator\|\| | Overloaded logical operator 'operator\|\|' changes short-circuit evaluation behavior. | +| test.cpp:21:10:21:19 | operator&& | Overloaded logical operator 'operator&&' changes short-circuit evaluation behavior. | +| test.cpp:25:10:25:19 | operator\|\| | Overloaded logical operator 'operator\|\|' changes short-circuit evaluation behavior. | diff --git a/cpp/misra/test/rules/RULE-16-5-1/LogicalAndAndLogicalOrOperatorsOverloaded.qlref b/cpp/misra/test/rules/RULE-16-5-1/LogicalAndAndLogicalOrOperatorsOverloaded.qlref new file mode 100644 index 000000000..4b04def6d --- /dev/null +++ b/cpp/misra/test/rules/RULE-16-5-1/LogicalAndAndLogicalOrOperatorsOverloaded.qlref @@ -0,0 +1 @@ +rules/RULE-16-5-1/LogicalAndAndLogicalOrOperatorsOverloaded.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-16-5-1/test.cpp b/cpp/misra/test/rules/RULE-16-5-1/test.cpp new file mode 100644 index 000000000..aab76e7ff --- /dev/null +++ b/cpp/misra/test/rules/RULE-16-5-1/test.cpp @@ -0,0 +1,27 @@ +#include + +class TestBool { +public: + TestBool(bool value) : m1(value) {} + bool getValue() const { return m1; } + +private: + bool m1; +}; + +// Non-compliant overloads +bool operator&&(const TestBool &l1, bool l2) { // NON_COMPLIANT + return l1.getValue() && l2; +} + +bool operator||(const TestBool &l1, bool l2) { // NON_COMPLIANT + return l1.getValue() || l2; +} + +TestBool operator&&(const TestBool &l1, const TestBool &l2) { // NON_COMPLIANT + return TestBool(l1.getValue() && l2.getValue()); +} + +TestBool operator||(const TestBool &l1, const TestBool &l2) { // NON_COMPLIANT + return TestBool(l1.getValue() || l2.getValue()); +} \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-16-6-1/InvalidOperatorOverloadedAsMemberFunction.expected b/cpp/misra/test/rules/RULE-16-6-1/InvalidOperatorOverloadedAsMemberFunction.expected new file mode 100644 index 000000000..4574aa480 --- /dev/null +++ b/cpp/misra/test/rules/RULE-16-6-1/InvalidOperatorOverloadedAsMemberFunction.expected @@ -0,0 +1,18 @@ +| test.cpp:11:13:11:21 | operator+ | Symmetrical operator 'operator+' should be implemented as a non-member function. | +| test.cpp:12:13:12:21 | operator- | Symmetrical operator 'operator-' should be implemented as a non-member function. | +| test.cpp:13:13:13:21 | operator* | Symmetrical operator 'operator*' should be implemented as a non-member function. | +| test.cpp:14:13:14:21 | operator/ | Symmetrical operator 'operator/' should be implemented as a non-member function. | +| test.cpp:15:13:15:21 | operator% | Symmetrical operator 'operator%' should be implemented as a non-member function. | +| test.cpp:17:8:17:17 | operator== | Symmetrical operator 'operator==' should be implemented as a non-member function. | +| test.cpp:18:8:18:17 | operator!= | Symmetrical operator 'operator!=' should be implemented as a non-member function. | +| test.cpp:19:8:19:16 | operator< | Symmetrical operator 'operator<' should be implemented as a non-member function. | +| test.cpp:20:8:20:17 | operator<= | Symmetrical operator 'operator<=' should be implemented as a non-member function. | +| test.cpp:21:8:21:17 | operator>= | Symmetrical operator 'operator>=' should be implemented as a non-member function. | +| test.cpp:22:8:22:16 | operator> | Symmetrical operator 'operator>' should be implemented as a non-member function. | +| test.cpp:24:13:24:21 | operator^ | Symmetrical operator 'operator^' should be implemented as a non-member function. | +| test.cpp:25:13:25:21 | operator& | Symmetrical operator 'operator&' should be implemented as a non-member function. | +| test.cpp:26:13:26:21 | operator\| | Symmetrical operator 'operator\|' should be implemented as a non-member function. | +| test.cpp:28:8:28:17 | operator&& | Symmetrical operator 'operator&&' should be implemented as a non-member function. | +| test.cpp:29:8:29:17 | operator\|\| | Symmetrical operator 'operator\|\|' should be implemented as a non-member function. | +| test.cpp:32:13:32:21 | operator+ | Symmetrical operator 'operator+' should be implemented as a non-member function. | +| test.cpp:33:8:33:17 | operator== | Symmetrical operator 'operator==' should be implemented as a non-member function. | diff --git a/cpp/misra/test/rules/RULE-16-6-1/InvalidOperatorOverloadedAsMemberFunction.qlref b/cpp/misra/test/rules/RULE-16-6-1/InvalidOperatorOverloadedAsMemberFunction.qlref new file mode 100644 index 000000000..5323989ff --- /dev/null +++ b/cpp/misra/test/rules/RULE-16-6-1/InvalidOperatorOverloadedAsMemberFunction.qlref @@ -0,0 +1 @@ +rules/RULE-16-6-1/InvalidOperatorOverloadedAsMemberFunction.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-16-6-1/test.cpp b/cpp/misra/test/rules/RULE-16-6-1/test.cpp new file mode 100644 index 000000000..9fca95db4 --- /dev/null +++ b/cpp/misra/test/rules/RULE-16-6-1/test.cpp @@ -0,0 +1,94 @@ +#include +#include + +class TestClass { + std::int32_t value; + +public: + TestClass(std::int32_t v = 0) : value(v) {} + + // NON-COMPLIANT: Symmetrical operators as member functions + TestClass operator+(const TestClass &rhs) const; // NON_COMPLIANT + TestClass operator-(const TestClass &rhs) const; // NON_COMPLIANT + TestClass operator*(const TestClass &rhs) const; // NON_COMPLIANT + TestClass operator/(const TestClass &rhs) const; // NON_COMPLIANT + TestClass operator%(const TestClass &rhs) const; // NON_COMPLIANT + + bool operator==(const TestClass &rhs) const; // NON_COMPLIANT + bool operator!=(const TestClass &rhs) const; // NON_COMPLIANT + bool operator<(const TestClass &rhs) const; // NON_COMPLIANT + bool operator<=(const TestClass &rhs) const; // NON_COMPLIANT + bool operator>=(const TestClass &rhs) const; // NON_COMPLIANT + bool operator>(const TestClass &rhs) const; // NON_COMPLIANT + + TestClass operator^(const TestClass &rhs) const; // NON_COMPLIANT + TestClass operator&(const TestClass &rhs) const; // NON_COMPLIANT + TestClass operator|(const TestClass &rhs) const; // NON_COMPLIANT + + bool operator&&(const TestClass &rhs) const; // NON_COMPLIANT + bool operator||(const TestClass &rhs) const; // NON_COMPLIANT + + // NON-COMPLIANT: Mixed type symmetrical operators as members + TestClass operator+(std::int32_t rhs) const; // NON_COMPLIANT + bool operator==(std::int32_t rhs) const; // NON_COMPLIANT + + // COMPLIANT: Hidden friend operators (non-member but defined in class) + friend TestClass operator+(const TestClass &lhs, + const TestClass &rhs) { // COMPLIANT + return TestClass(lhs.value + rhs.value); + } + + friend bool operator==(const TestClass &lhs, + const TestClass &rhs) { // COMPLIANT + return lhs.value == rhs.value; + } + + // COMPLIANT: Non-symmetrical operators as members are allowed + TestClass &operator+=(const TestClass &rhs); // COMPLIANT + TestClass &operator-=(const TestClass &rhs); // COMPLIANT + TestClass &operator*=(const TestClass &rhs); // COMPLIANT + TestClass &operator/=(const TestClass &rhs); // COMPLIANT + TestClass &operator%=(const TestClass &rhs); // COMPLIANT + + TestClass &operator^=(const TestClass &rhs); // COMPLIANT + TestClass &operator&=(const TestClass &rhs); // COMPLIANT + TestClass &operator|=(const TestClass &rhs); // COMPLIANT + + TestClass &operator++(); // COMPLIANT + TestClass operator++(int); // COMPLIANT + TestClass &operator--(); // COMPLIANT + TestClass operator--(int); // COMPLIANT + + TestClass operator~() const; // COMPLIANT + bool operator!() const; // COMPLIANT + + TestClass &operator=(const TestClass &rhs); // COMPLIANT + + // COMPLIANT: Stream operators are not symmetrical + friend std::ostream &operator<<(std::ostream &os, + const TestClass &tc); // COMPLIANT + friend std::istream &operator>>(std::istream &is, TestClass &tc); // COMPLIANT +}; + +// COMPLIANT: Non-member symmetrical operators +TestClass operator-(const TestClass &lhs, const TestClass &rhs); // COMPLIANT +TestClass operator*(const TestClass &lhs, const TestClass &rhs); // COMPLIANT +TestClass operator/(const TestClass &lhs, const TestClass &rhs); // COMPLIANT +TestClass operator%(const TestClass &lhs, const TestClass &rhs); // COMPLIANT + +bool operator!=(const TestClass &lhs, const TestClass &rhs); // COMPLIANT +bool operator<(const TestClass &lhs, const TestClass &rhs); // COMPLIANT +bool operator<=(const TestClass &lhs, const TestClass &rhs); // COMPLIANT +bool operator>=(const TestClass &lhs, const TestClass &rhs); // COMPLIANT +bool operator>(const TestClass &lhs, const TestClass &rhs); // COMPLIANT + +TestClass operator^(const TestClass &lhs, const TestClass &rhs); // COMPLIANT +TestClass operator&(const TestClass &lhs, const TestClass &rhs); // COMPLIANT +TestClass operator|(const TestClass &lhs, const TestClass &rhs); // COMPLIANT + +bool operator&&(const TestClass &lhs, const TestClass &rhs); // COMPLIANT +bool operator||(const TestClass &lhs, const TestClass &rhs); // COMPLIANT + +// COMPLIANT: Mixed type non-member symmetrical operators +TestClass operator+(const TestClass &lhs, std::int32_t rhs); // COMPLIANT +TestClass operator+(std::int32_t lhs, const TestClass &rhs); // COMPLIANT \ No newline at end of file diff --git a/rule_packages/cpp/Classes2.json b/rule_packages/cpp/Classes2.json new file mode 100644 index 000000000..2018163a9 --- /dev/null +++ b/rule_packages/cpp/Classes2.json @@ -0,0 +1,158 @@ +{ + "MISRA-C++-2023": { + "RULE-13-1-1": { + "properties": { + "enforcement": "decidable", + "obligation": "advisory" + }, + "queries": [ + { + "description": "Virtual inheritance should not be used as it may lead to unexpected behavior.", + "kind": "problem", + "name": "Classes should not be inherited virtually", + "precision": "very-high", + "severity": "warning", + "short_name": "VirtualInheritanceNotAllowed", + "tags": [ + "scope/single-translation-unit", + "correctness", + "readability" + ] + } + ], + "title": "Classes should not be inherited virtually" + }, + "RULE-13-3-1": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Appropriate use of specifiers on member functions more clearly indicate the intention of the function.", + "kind": "problem", + "name": "User-declared member functions shall use the virtual, override and final specifiers appropriately", + "precision": "very-high", + "severity": "warning", + "short_name": "MemberSpecifiersNotUsedAppropriately", + "tags": [ + "scope/single-translation-unit", + "readability", + "maintainability" + ] + } + ], + "title": "User-declared member functions shall use the virtual, override and final specifiers appropriately" + }, + "RULE-14-1-1": { + "properties": { + "enforcement": "decidable", + "obligation": "advisory" + }, + "queries": [ + { + "description": "Mixing public and private data members in a class obfuscates the range of valid states allowable for that class.", + "kind": "problem", + "name": "Non-static data members should be either all private or all public", + "precision": "very-high", + "severity": "warning", + "short_name": "PrivateAndPublicDataMembersMixed", + "tags": [ + "scope/single-translation-unit", + "maintainability" + ] + } + ], + "title": "Non-static data members should be either all private or all public" + }, + "RULE-15-0-2": { + "properties": { + "enforcement": "decidable", + "obligation": "advisory" + }, + "queries": [ + { + "description": "Proper implementations of copy and move constructors and assignment operators ensure that resources are managed correctly.", + "kind": "problem", + "name": "User-provided copy and move member functions of a class should have appropriate signatures", + "precision": "very-high", + "severity": "warning", + "short_name": "InvalidSignatureForSpecialMemberFunction", + "tags": [ + "scope/single-translation-unit", + "correctness", + "maintainability" + ] + } + ], + "title": "User-provided copy and move member functions of a class should have appropriate signatures" + }, + "RULE-15-1-3": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Implicit conversions can lead to unexpected behavior that is preventable by declaring conversion operators and constructors as explicit.", + "kind": "problem", + "name": "Conversion operators and constructors that are callable with a single argument shall be explicit", + "precision": "very-high", + "severity": "warning", + "short_name": "NonExplicitConversionMember", + "tags": [ + "scope/single-translation-unit", + "correctness", + "readability" + ] + } + ], + "title": "Conversion operators and constructors that are callable with a single argument shall be explicit" + }, + "RULE-16-5-1": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Overloaded logical AND and logical OR operators change short-circuiting behavior and can lead to unexpected results.", + "kind": "problem", + "name": "The logical AND and logical OR operators shall not be overloaded", + "precision": "very-high", + "severity": "warning", + "short_name": "LogicalAndAndLogicalOrOperatorsOverloaded", + "tags": [ + "scope/single-translation-unit", + "correctness", + "readability", + "maintainability" + ] + } + ], + "title": "The logical AND and logical OR operators shall not be overloaded" + }, + "RULE-16-6-1": { + "properties": { + "enforcement": "decidable", + "obligation": "advisory" + }, + "queries": [ + { + "description": "Function resolution of operators overloaded as member functions is dependent on ordering and available conversions, which can lead to unexpected behavior.", + "kind": "problem", + "name": "Symmetrical operators should only be implemented as non-member functions", + "precision": "very-high", + "severity": "warning", + "short_name": "InvalidOperatorOverloadedAsMemberFunction", + "tags": [ + "scope/single-translation-unit", + "correctness", + "maintainability" + ] + } + ], + "title": "Symmetrical operators should only be implemented as non-member functions" + } + } +} \ No newline at end of file diff --git a/rules.csv b/rules.csv index 68049625e..aba41e91c 100644 --- a/rules.csv +++ b/rules.csv @@ -938,12 +938,12 @@ cpp,MISRA-C++-2023,RULE-13-3-2,Yes,Required,Decidable,Single Translation Unit,Pa cpp,MISRA-C++-2023,RULE-13-3-3,Yes,Required,Decidable,System,The parameters in all declarations or overrides of a function shall either be unnamed or have identical names,RULE-8-3,Declarations2,Easy, cpp,MISRA-C++-2023,RULE-13-3-4,Yes,Required,Decidable,Single Translation Unit,A comparison of a potentially virtual pointer to member function shall only be with nullptr,A5-10-1,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-14-1-1,Yes,Advisory,Decidable,Single Translation Unit,Non-static data members should be either all private or all public,,Classes2,Easy, -cpp,MISRA-C++-2023,RULE-15-0-1,Yes,Required,Decidable,Single Translation Unit,Special member functions shall be provided appropriately,A12-0-1,Classes2,Medium, +cpp,MISRA-C++-2023,RULE-15-0-1,Yes,Required,Decidable,Single Translation Unit,Special member functions shall be provided appropriately,A12-0-1,Classes3,Medium, cpp,MISRA-C++-2023,RULE-15-0-2,Yes,Advisory,Decidable,Single Translation Unit,User-provided copy and move member functions of a class should have appropriate signatures,,Classes2,Easy, cpp,MISRA-C++-2023,RULE-15-1-1,Yes,Required,Undecidable,System,An object’s dynamic type shall not be used from within its constructor or destructor,M12-1-1,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-15-1-2,Yes,Advisory,Decidable,Single Translation Unit,All constructors of a class should explicitly initialize all of its virtual base classes and immediate base classes,A12-1-1,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-15-1-3,Yes,Required,Decidable,Single Translation Unit,Conversion operators and constructors that are callable with a single argument shall be explicit,"A12-1-4,A13-5-2",Classes2,Easy, -cpp,MISRA-C++-2023,RULE-15-1-4,Yes,Advisory,Decidable,Single Translation Unit,"All direct, non-static data members of a class should be initialized before the class object is accessible",,Classes2,Hard, +cpp,MISRA-C++-2023,RULE-15-1-4,Yes,Advisory,Decidable,Single Translation Unit,"All direct, non-static data members of a class should be initialized before the class object is accessible",,Classes3,Hard, cpp,MISRA-C++-2023,RULE-15-1-5,Yes,Required,Decidable,Single Translation Unit,A class shall only define an initializer-list constructor when it is the only constructor,A8-5-4,ImportMisra23,Import, cpp,MISRA-C++-2023,DIR-15-8-1,Yes,Required,Decidable,Implementation,User-provided copy assignment operators and move assignment operators shall handle self-assignment,A12-8-5,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-16-5-1,Yes,Required,Decidable,Single Translation Unit,The logical AND and logical OR operators shall not be overloaded,M5-2-11,Classes2,Easy,