diff --git a/c/cert/src/codeql-pack.lock.yml b/c/cert/src/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/c/cert/src/codeql-pack.lock.yml +++ b/c/cert/src/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/c/cert/src/rules/INT36-C/ConvertingAPointerToIntegerOrIntegerToPointer.ql b/c/cert/src/rules/INT36-C/ConvertingAPointerToIntegerOrIntegerToPointer.ql index 1cbdcc4e12..a209afca6d 100644 --- a/c/cert/src/rules/INT36-C/ConvertingAPointerToIntegerOrIntegerToPointer.ql +++ b/c/cert/src/rules/INT36-C/ConvertingAPointerToIntegerOrIntegerToPointer.ql @@ -17,6 +17,7 @@ import cpp import codingstandards.c.cert +import codingstandards.cpp.types.Resolve class LiteralZero extends Literal { LiteralZero() { this.getValue() = "0" } @@ -37,21 +38,30 @@ class StdIntIntPtrType extends Type { } } +class ResolvesToStdIntIntPtrType = ResolvesTo::IgnoringSpecifiers; + +class ResolvesToVoidPointerType = ResolvesTo::IgnoringSpecifiers; + /** * Casting a pointer value to integer, excluding literal 0. * Includes implicit conversions made during declarations or assignments. */ predicate conversionBetweenPointerAndInteger(Cast cast, string message) { /* Ensure that `int` has different size than that of pointers */ - exists(IntType intType, PointerType ptrType | intType.getSize() < ptrType.getSize() | - cast.getExpr().getUnderlyingType() = intType and - cast.getUnderlyingType() = ptrType and + exists( + ResolvesTo::IgnoringSpecifiers intType, + ResolvesTo::IgnoringSpecifiers ptrType + | + intType.getSize() < ptrType.getSize() + | + cast.getExpr().getType() = intType and + cast.getType() = ptrType and if cast.isCompilerGenerated() then message = "Integer expression " + cast.getExpr() + " is implicitly cast to a pointer type." else message = "Integer expression " + cast.getExpr() + " is cast to a pointer type." or - cast.getExpr().getUnderlyingType() = ptrType and - cast.getUnderlyingType() = intType and + cast.getExpr().getType() = ptrType and + cast.getType() = intType and if cast.isCompilerGenerated() then message = "Pointer expression " + cast.getExpr() + " is implicitly cast to an integer type." @@ -61,11 +71,11 @@ predicate conversionBetweenPointerAndInteger(Cast cast, string message) { not cast.getExpr() instanceof LiteralZero and /* Compliant exception 2: variable's declared type is (u)intptr_t */ not ( - cast.getType() instanceof StdIntIntPtrType and - cast.getExpr().getType() instanceof VoidPointerType + cast.getType() instanceof ResolvesToStdIntIntPtrType and + cast.getExpr().getType() instanceof ResolvesToVoidPointerType or - cast.getType() instanceof VoidPointerType and - cast.getExpr().getType() instanceof StdIntIntPtrType + cast.getType() instanceof ResolvesToVoidPointerType and + cast.getExpr().getType() instanceof ResolvesToStdIntIntPtrType ) } diff --git a/c/cert/test/codeql-pack.lock.yml b/c/cert/test/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/c/cert/test/codeql-pack.lock.yml +++ b/c/cert/test/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/c/common/src/codeql-pack.lock.yml b/c/common/src/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/c/common/src/codeql-pack.lock.yml +++ b/c/common/src/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/c/common/test/codeql-pack.lock.yml b/c/common/test/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/c/common/test/codeql-pack.lock.yml +++ b/c/common/test/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/c/misra/src/codeql-pack.lock.yml b/c/misra/src/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/c/misra/src/codeql-pack.lock.yml +++ b/c/misra/src/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/c/misra/src/rules/RULE-22-12/NonstandardUseOfThreadingObject.ql b/c/misra/src/rules/RULE-22-12/NonstandardUseOfThreadingObject.ql index d92b4ccea6..85bcba272a 100644 --- a/c/misra/src/rules/RULE-22-12/NonstandardUseOfThreadingObject.ql +++ b/c/misra/src/rules/RULE-22-12/NonstandardUseOfThreadingObject.ql @@ -16,9 +16,11 @@ import cpp import codingstandards.c.misra import codingstandards.cpp.Concurrency -import codingstandards.cpp.Type +import codingstandards.cpp.types.Resolve -predicate isThreadingObject(Type t) { t instanceof PossiblySpecified::Type } +predicate isThreadingObject(Type t) { + t instanceof ResolvesTo::IgnoringSpecifiers +} predicate validUseOfStdThreadObject(Expr e) { e.getParent() instanceof AddressOfExpr diff --git a/c/misra/src/rules/RULE-22-13/ThreadingObjectWithInvalidStorageDuration.ql b/c/misra/src/rules/RULE-22-13/ThreadingObjectWithInvalidStorageDuration.ql index 066cf3c295..5afd9f4547 100644 --- a/c/misra/src/rules/RULE-22-13/ThreadingObjectWithInvalidStorageDuration.ql +++ b/c/misra/src/rules/RULE-22-13/ThreadingObjectWithInvalidStorageDuration.ql @@ -17,7 +17,7 @@ import cpp import codingstandards.c.misra import codingstandards.c.Objects import codingstandards.cpp.Concurrency -import codingstandards.cpp.Type +import codingstandards.cpp.types.Resolve from ObjectIdentity obj, StorageDuration storageDuration, Type type where @@ -25,7 +25,7 @@ where storageDuration = obj.getStorageDuration() and not storageDuration.isStatic() and type = obj.getASubObjectType() and - type instanceof PossiblySpecified::Type + type instanceof ResolvesTo::IgnoringSpecifiers select obj, "Object of type '" + obj.getType().getName() + "' has invalid storage duration type '" + storageDuration.getStorageTypeName() + "'." diff --git a/c/misra/src/rules/RULE-22-14/MutexNotInitializedBeforeUse.ql b/c/misra/src/rules/RULE-22-14/MutexNotInitializedBeforeUse.ql index f78c25f981..08f4374b62 100644 --- a/c/misra/src/rules/RULE-22-14/MutexNotInitializedBeforeUse.ql +++ b/c/misra/src/rules/RULE-22-14/MutexNotInitializedBeforeUse.ql @@ -17,7 +17,7 @@ import cpp import codingstandards.c.misra import codingstandards.c.Objects import codingstandards.cpp.Concurrency -import codingstandards.cpp.Type +import codingstandards.cpp.types.Resolve import codingstandards.c.initialization.GlobalInitializationAnalysis module MutexInitializationConfig implements GlobalInitializationAnalysisConfigSig { @@ -68,8 +68,8 @@ where ) and ( if - obj.getType() instanceof PossiblySpecified::Type or - obj.getType() instanceof PossiblySpecified::Type + obj.getType() instanceof ResolvesTo::IgnoringSpecifiers or + obj.getType() instanceof ResolvesTo::IgnoringSpecifiers then description = typeString else description = typeString + " in object" ) diff --git a/c/misra/test/codeql-pack.lock.yml b/c/misra/test/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/c/misra/test/codeql-pack.lock.yml +++ b/c/misra/test/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/change_notes/2025-12-03-type-resolution-tracking-changes.md b/change_notes/2025-12-03-type-resolution-tracking-changes.md new file mode 100644 index 0000000000..5e5e9e4f1d --- /dev/null +++ b/change_notes/2025-12-03-type-resolution-tracking-changes.md @@ -0,0 +1,6 @@ + - `INT36-C` - `ConvertingAPointerToIntegerOrIntegerToPointer.ql`: + - Integrated new type resolution modules to fully handle typedefs and ignore cv-qualifiers during type comparisons, such as in detecting int types, pointer types, (u)intptr_t types, and void pointer types. + - `RULE-22-12`, `RULE-22-13`, `RULE-22-14` - `NonstandardUseOfThreadingObject.ql`, `ThreadingObjectWithInvalidStorageDuration.ql`, `MutexNotInitializedBeforeUse.ql`: + - Integrated new type resolution modules to handle typedefs when identifying threading object types. + - `RULE-9-5-1` - `LegacyForStatementsShouldBeSimple.ql`: + - Refactor to integrate new type resolution, no change in functionality expected. \ No newline at end of file diff --git a/cpp/autosar/src/codeql-pack.lock.yml b/cpp/autosar/src/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/cpp/autosar/src/codeql-pack.lock.yml +++ b/cpp/autosar/src/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/cpp/autosar/test/codeql-pack.lock.yml b/cpp/autosar/test/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/cpp/autosar/test/codeql-pack.lock.yml +++ b/cpp/autosar/test/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/cpp/cert/src/codeql-pack.lock.yml b/cpp/cert/src/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/cpp/cert/src/codeql-pack.lock.yml +++ b/cpp/cert/src/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/cpp/cert/test/codeql-pack.lock.yml b/cpp/cert/test/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/cpp/cert/test/codeql-pack.lock.yml +++ b/cpp/cert/test/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/cpp/common/src/codeql-pack.lock.yml b/cpp/common/src/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/cpp/common/src/codeql-pack.lock.yml +++ b/cpp/common/src/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/cpp/common/src/codingstandards/cpp/ast/ValueCategory.qll b/cpp/common/src/codingstandards/cpp/ast/ValueCategory.qll new file mode 100644 index 0000000000..bd1d35dfaf --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/ast/ValueCategory.qll @@ -0,0 +1,60 @@ +import cpp + +/** + * Get an expression's value category as a ValueCategory object. + * + * Note that the standard cpp library exposes `is{_}ValueCategory` predicates, but they do not + * correctly work with conversions. This function is intended to give the correct answer in the + * presence of conversions such as lvalue-to-rvalue conversion. + */ +ValueCategory getValueCategory(Expr e) { + not exists(e.getConversion()) and + result = getDirectValueCategory(e) + or + if e.getConversion() instanceof ReferenceToExpr + then result = getDirectValueCategory(e) + else result = getDirectValueCategory(e.getConversion()) +} + +private ValueCategory getDirectValueCategory(Expr e) { + if e.isLValueCategory() + then result = LValue(e.getValueCategoryString()) + else + if e.isPRValueCategory() + then result = PRValue(e.getValueCategoryString()) + else + if e.isXValueCategory() + then result = XValue(e.getValueCategoryString()) + else none() +} + +newtype TValueCategory = + LValue(string descr) { + exists(Expr e | e.isLValueCategory() and descr = e.getValueCategoryString()) + } or + PRValue(string descr) { + exists(Expr e | e.isPRValueCategory() and descr = e.getValueCategoryString()) + } or + XValue(string descr) { + exists(Expr e | e.isXValueCategory() and descr = e.getValueCategoryString()) + } + +class ValueCategory extends TValueCategory { + string description; + + ValueCategory() { + this = LValue(description) or this = PRValue(description) or this = XValue(description) + } + + predicate isLValue() { this instanceof LValue } + + predicate isPRValue() { this instanceof PRValue } + + predicate isXValue() { this instanceof XValue } + + predicate isRValue() { this instanceof PRValue or this instanceof XValue } + + predicate isGlvalue() { this instanceof LValue or this instanceof XValue } + + string toString() { result = description } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Preconditions5.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Preconditions5.qll new file mode 100644 index 0000000000..8256a4ecc7 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Preconditions5.qll @@ -0,0 +1,26 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Preconditions5Query = TStdMoveWithNonConstLvalueQuery() + +predicate isPreconditions5QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `stdMoveWithNonConstLvalue` query + Preconditions5Package::stdMoveWithNonConstLvalueQuery() and + queryId = + // `@id` for the `stdMoveWithNonConstLvalue` query + "cpp/misra/std-move-with-non-const-lvalue" and + ruleId = "RULE-28-6-1" and + category = "required" +} + +module Preconditions5Package { + Query stdMoveWithNonConstLvalueQuery() { + //autogenerate `Query` type + result = + // `Query` type for `stdMoveWithNonConstLvalue` query + TQueryCPP(TPreconditions5PackageQuery(TStdMoveWithNonConstLvalueQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll index 2e2403165f..e7aabb62e4 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll @@ -44,6 +44,7 @@ import Operators import OrderOfEvaluation import OutOfBounds import Pointers +import Preconditions5 import Representation import Scope import SideEffects1 @@ -103,6 +104,7 @@ newtype TCPPQuery = TOrderOfEvaluationPackageQuery(OrderOfEvaluationQuery q) or TOutOfBoundsPackageQuery(OutOfBoundsQuery q) or TPointersPackageQuery(PointersQuery q) or + TPreconditions5PackageQuery(Preconditions5Query q) or TRepresentationPackageQuery(RepresentationQuery q) or TScopePackageQuery(ScopeQuery q) or TSideEffects1PackageQuery(SideEffects1Query q) or @@ -162,6 +164,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat isOrderOfEvaluationQueryMetadata(query, queryId, ruleId, category) or isOutOfBoundsQueryMetadata(query, queryId, ruleId, category) or isPointersQueryMetadata(query, queryId, ruleId, category) or + isPreconditions5QueryMetadata(query, queryId, ruleId, category) or isRepresentationQueryMetadata(query, queryId, ruleId, category) or isScopeQueryMetadata(query, queryId, ruleId, category) or isSideEffects1QueryMetadata(query, queryId, ruleId, category) or diff --git a/cpp/common/src/codingstandards/cpp/types/Resolve.qll b/cpp/common/src/codingstandards/cpp/types/Resolve.qll new file mode 100644 index 0000000000..3fd2f7db78 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/types/Resolve.qll @@ -0,0 +1,271 @@ +private import cpp +private import qtil.Qtil +private import codingstandards.cpp.types.Specifiers +private import codeql.util.Boolean + +Type typeResolvesToTypeStep(Type type) { + result = type.(Decltype).getBaseType() + or + result = type.(TypedefType).getBaseType() +} + +module PointerTo::Type PointeeType> { + /** + * A pointer type that points to a type that resolves to the module's `PointeeType` type parameter. + */ + class Type extends Qtil::Final::Type { + Type() { getBaseType() instanceof PointeeType } + } +} + +module ReferenceOf::Type ReferencedType> { + /** + * A reference type that refers to a type that resolves to the module's `ReferencedType` type + * parameter. + */ + class Type extends Qtil::Final::Type { + Type() { getBaseType() instanceof ReferencedType } + } +} + +/** + * A module for handling complex type resolution in c++, such as a decltype of a const typedef + * of a struct type, which resolves to a const struct type. + * + * Basic usage: + * - `ResolvesTo::Exactly` is the set of class types, and typedefs/decltypes that resolve + * to class types exactly (without specifiers). + * - `resolvedType.resolve()` gets the fully resolved class type for the above. + * - `ResolvesTo::Specified` is the set of types that resolve to a specified class type. + * - `ResolvesTo::Ref` is the set of types that resolve to a reference to a class type. + * - `ResolvesTo::CvConst` is the set of types that resolve to a const class type. + * - `ResolvesTo::IgnoringSpecifiers` is the set of types that resolve to a class type + * ignoring specifiers. + * - `ResolvesTo::ExactlyOrRef` is the set of types that resolve to a class type and + * unwraps references. + * + * These module classes are preferred to the member predicates on `Type` such as `resolveTypedefs`, + * `stripSpecifiers`, `getUnderlyingType()`, etc, for the following reasons: + * - Hopefully the API is clearer and easier to use correctly. + * - Unlike `resolveTypedefs`, these classes can find types that resolve to other typedefs (via + * `ResolvesType::Exactly` etc.), instead of always resolving all typedefs. + * - The member predicates on `Type` have cases with no result. For instance, if there's a const + * typedef `const T = F`, but `const F` is never explicitly written in the code, then there is no + * matching extracted `Type` for `resolveTypedefs` to return, and it will have no result. + */ +module ResolvesTo::Type ResolvedType> { + private import cpp as cpp + + final class CppType = cpp::Type; + + /** + * A type that resolves exactly to the module's `ResolvedType` type parameter. + * + * For example, `ResolvesTo::Type` is the set of all `FooType`s and types that resolve + * (through typedefs * and/or decltypes) to `FooType`s. This does _not_ include types that resolve + * to a const `FooType` (though `FooType` itself may be const). To perform type resolution and + * check or strip specifiers, see module classes `IgnoringSpecifiers`, `Specified`, `Const`, etc. + * + * ``` + * // Example `ResolvesTo::Exactly` types: + * FooType f; // matches (a FooType) + * decltype(f); // matches (a decltype of FooType) + * typedef FooType FT; // matches (a typedef of FooType) + * FT f2; // matches (a typedef of FooType) + * decltype(f2); // matches (a decltype of typedef of FooType) + * typedef FT FT2; // matches (a typedef of typedef of FooType) + * + * // Examples types that are not `ResolvesTo::Exactly` types: + * const FooType cf; // does not match (specified FooTypes) + * FooType& rf = f; // does not match (references to FooTypes) + * NotFooType nf; // does not match (non FooTypes) + * ``` + */ + class Exactly extends CppType { + ResolvedType resolvedType; + + Exactly() { resolvedType = typeResolvesToTypeStep*(this) } + + ResolvedType resolve() { result = resolvedType } + } + + /** + * A type that resolves to a const type that in turn resolves to the module's `ResolvedType` type. + * + * For example, `ResolvesTo::CvConst` is the set of all const `FooType`s and types that + * resolve (through typedefs and/or decltypes) to const `FooType`s, including cases involving + * `const` typedefs, etc. + * + * Volatile specifiers are ignored, since const volatile types are still const. + * + * For matching both const and non-const types that resolve to `FooType`, see + * `IgnoringSpecifiers`. + * + * ``` + * // Note that this does NOT match `FooType`s that are not const. + * FooType f; // does not match (non-const) + * decltype(f) df; // does not match (non-const) + * typedef TF = FooType; // does not match (non-const) + * + * // Example `ResolvesTo::Const` types: + * const FooType cf; // matches (a const FooType) + * const volatile FooType cf; // matches (a const FooType, volatile is allowed and ignored) + * decltype(cf); // matches (a decltype of a const FooType) + * const decltype(f); // matches (a const decltype of FooType) + * const decltype(cf); // matches (a const decltype of a const FooType) + * typedef const FooType CFT; // matches (a typedef of const FooType) + * const TF ctf; // matches (a const typedef of FooType) + * + * // Additional examples types that are not `ResolvesTo::Const` types: + * const FooType &f; // does not match (reference to const FooType) + * ``` + */ + class CvConst extends Specified { + CvConst() { getASpecifier() instanceof ConstSpecifier } + } + + /** + * A type that resolves to a cv-qualified (or otherwise specified) type that in turn resolves to + * the module's `ResolvedType` type parameter. + * + * For example, `ResolvesTo::Specified` is the set of all specified `FooType`s and types + * that resolve (through typedefs and/or decltypes) to specified `FooType`s, including cases + * involving `const` and `volatile` typedefs, etc. + * + * ``` + * // Note that this does NOT match `FooType`s that are not specified. + * FooType f; // does not match (not specified) + * decltype(f) df; // does not match (not specified) + * typedef TF = FooType; // does not match (not specified) + * + * // Example `ResolvesTo::Specified` types: + * const FooType cf; // matches (a const FooType) + * volatile FooType vf; // matches (a volatile FooType) + * const volatile FooType cvf; // matches (a const volatile FooType) + * decltype(cf); // matches (a decltype of a const FooType) + * volatile decltype(f); // matches (a volatile decltype of FooType) + * const decltype(vf); // matches (a const decltype of volatile FooType) + * const decltype(cf); // matches (a const decltype of const FooType) + * typedef const FooType CFT; // matches (a typedef of const FooType) + * const TF ctf; // matches (a const typedef of FooType) + * volatile TF ctf; // matches (a volatile typedef of FooType) + * + * // Additional examples types that are not `ResolvesTo::Specified` types: + * const FooType &f; // does not match (reference to const FooType) + * ``` + */ + class Specified extends CppType { + ResolvedType resolved; + + Specified() { + resolved = typeResolvesToTypeStep*(this).(SpecifiedType).getBaseType().(Exactly).resolve() + } + + ResolvedType resolve() { result = resolved } + } + + /** + * A class that resolves to the module's `ResolvedType` type parameter, ignoring specifiers. + */ + class IgnoringSpecifiers extends CppType { + ResolvedType resolved; + + IgnoringSpecifiers() { + resolved = this.(Specified).resolve() + or + resolved = this.(Exactly).resolve() + } + + ResolvedType resolve() { result = resolved } + } + + /** + * A type that resolves to a reference that resolves to the module's `ResolvedType` type + * parameter. + * + * For example, `ResolvesTo::Ref` is the set of all references to `FooType`s and types + * that resolve (through typedefs and/or decltypes) to references to `FooType`s. + * + * ``` + * // Example `ResolvesTo::Ref` types: + * FooType &f; // matches (a reference to FooType) + * decltype(f); // matches (a decltype of reference to FooType) + * typedef FooType &FT; // matches (a typedef of ref to FooType) + * FT f2; // matches (a typedef of ref to FooType) + * decltype(f2); // matches (a decltype of typedef of ref to FooType) + * typedef FT FT2; // matches (a typedef of typedef of ref to FooType) + * + * // Examples types that are not `ResolvesTo::Ref` types: + * const FooType &cf; // does not match (specified references to FooTypes) + * FooType rf = f; // does not match (non-rerefence FooTypes) + * NotFooType &nf; // does not match (non FooTypes) + * ``` + */ + class Ref extends CppType { + ResolvedType resolved; + + Ref() { + // Note that the extractor appears to perform reference collapsing, so cases like + // `const T& &` are treated as `const T&`. + resolved = typeResolvesToTypeStep*(this).(ReferenceType).getBaseType().(Exactly).resolve() + } + + ResolvedType resolve() { result = resolved } + } + + /** + * A type that resolves to a const reference of (or reference to const of) the module's + * `ResolvedType` type parameter. + * + * For example, `ResolvesTo::CvConstRef` is the set of all const references to `FooType`s + * and types that resolve (through typedefs and/or decltypes) to const references to `FooType`s. + * + * Volatile specifiers are ignored, since const volatile types are still const. + * + * ``` + * FooType &f; // does not match (not const) + * const FooType f; // does not match (not a reference) + * + * // Example `ResolvesTo::CvConstRef` types: + * const FooType &cf; // matches (a const reference to FooType) + * const volatile FooType &cf; // matches (a const reference to FooType, volatile is ignored) + * const decltype(f) cdf; // matches (a const decltype of reference to FooType) + * decltype(f)& dcf; // matches (a decltype of const reference to FooType) + * typedef const FooType &CFT; // matches (a typedef of const ref to FooType) + * const CFT ctf; // matches due to const collapse + * CFT &ctf; // matches due to reference collapse + * ``` + */ + class CvConstRef extends CppType { + ResolvedType resolved; + + CvConstRef() { + exists(ReferenceType refType | + // A type can be a reference to const, but a const type cannot contain a reference. + // Therefore, we only need to find reference types that resolve to const types. + // Note that the extractor appears to perform reference collapsing, so cases like + // `const T& &` are treated as `const T&`. + refType = typeResolvesToTypeStep*(this) and + resolved = refType.getBaseType().(CvConst).resolve() + ) + } + + ResolvedType resolve() { result = resolved } + } + + /** + * A type that resolves to either a reference that resolves to the module's `ResolvedType` type + * parameter, or exactly to the `ResolvedType`. + */ + class ExactlyOrRef extends CppType { + ResolvedType resolved; + + ExactlyOrRef() { + resolved = this.(Ref).resolve() + or + resolved = this.(Exactly).resolve() + } + + ResolvedType resolve() { result = resolved } + } +} diff --git a/cpp/common/src/codingstandards/cpp/types/Specifiers.qll b/cpp/common/src/codingstandards/cpp/types/Specifiers.qll new file mode 100644 index 0000000000..187709f8c1 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/types/Specifiers.qll @@ -0,0 +1,28 @@ +import cpp + +class ConstSpecifier extends Specifier { + ConstSpecifier() { this.hasName("const") } +} + +/** + * A SpecifiedType with a `const` specifier. + * + * Note that this does *not* find all const types, as it does not resolve typedefs etc. + */ +class RawConstType extends SpecifiedType { + RawConstType() { this.getASpecifier() instanceof ConstSpecifier } +} + +/** + * Any type that is const, using the `.isConst()` member predicate. + */ +class ConstType extends Type { + ConstType() { this.isConst() } +} + +/** + * Any type that is not const, using the `.isConst()` member predicate. + */ +class NonConstType extends Type { + NonConstType() { not this.isConst() } +} diff --git a/cpp/common/src/codingstandards/cpp/types/Type.qll b/cpp/common/src/codingstandards/cpp/types/Type.qll index 0e55b3ad58..137a01c239 100644 --- a/cpp/common/src/codingstandards/cpp/types/Type.qll +++ b/cpp/common/src/codingstandards/cpp/types/Type.qll @@ -60,30 +60,6 @@ Type stripSpecifiers(Type type) { else result = type } -signature class PossiblySpecifiedBaseType extends Type; - -/** - * This module defines a class `Type` which holds for types `T` and `const/volatile T` etc. - * - * Similar to `getUnspecifiedType()`, but does not resolve typedefs. Useful for matching - * potentially qualified versions of standard typedef types, such as `const mtx_t`. - * - * Example usage: `someType.(PossiblySpecified::Type).strip()` - */ -module PossiblySpecified { - import cpp as cpp - - final class CppType = cpp::Type; - - class Type extends CppType { - BaseType baseType; - - Type() { baseType = stripSpecifiers(this) } - - BaseType strip() { result = baseType } - } -} - /** * Get the precision of an integral type, where precision is defined as the number of bits * that can be used to represent the numeric value. diff --git a/cpp/common/src/qlpack.yml b/cpp/common/src/qlpack.yml index 21663a5186..222a096067 100644 --- a/cpp/common/src/qlpack.yml +++ b/cpp/common/src/qlpack.yml @@ -3,5 +3,6 @@ version: 2.52.0-dev license: MIT dependencies: codeql/cpp-all: 4.0.3 + advanced-security/qtil: "0.0.3" dataExtensions: - ext/*.model.yml diff --git a/cpp/common/test/codeql-pack.lock.yml b/cpp/common/test/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/cpp/common/test/codeql-pack.lock.yml +++ b/cpp/common/test/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/cpp/common/test/library/codingstandards/cpp/types/Resolve/ResolveTest.expected b/cpp/common/test/library/codingstandards/cpp/types/Resolve/ResolveTest.expected new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cpp/common/test/library/codingstandards/cpp/types/Resolve/ResolveTest.ql b/cpp/common/test/library/codingstandards/cpp/types/Resolve/ResolveTest.ql new file mode 100644 index 0000000000..ab8953f7b2 --- /dev/null +++ b/cpp/common/test/library/codingstandards/cpp/types/Resolve/ResolveTest.ql @@ -0,0 +1,68 @@ +import cpp +import utils.test.InlineExpectationsTest +import codingstandards.cpp.types.Resolve + +class FooType extends Class { + FooType() { this.hasName("Foo") } +} + +class TDFoo extends TypedefType { + TDFoo() { this.hasName("TDFoo") } +} + +class DeclFoo extends Decltype { + DeclFoo() { this.getBaseType() instanceof FooType } +} + +class ExactFoo = ResolvesTo::Exactly; + +class ConstFoo = ResolvesTo::CvConst; + +class SpecFoo = ResolvesTo::Specified; + +class MaybeSpecFoo = ResolvesTo::IgnoringSpecifiers; + +class RefFoo = ResolvesTo::Ref; + +class ConstRefFoo = ResolvesTo::CvConstRef; + +class ExactTDFoo = ResolvesTo::Exactly; + +class ExactDeclFoo = ResolvesTo::Exactly; + +predicate hasRelevantType(Type type, string s) { + type instanceof ExactFoo and s = "Foo" + or + type instanceof ConstFoo and s = "ConstFoo" + or + type instanceof SpecFoo and s = "SpecFoo" + or + type instanceof MaybeSpecFoo and s = "MaybeSpecFoo" + or + type instanceof RefFoo and s = "RefFoo" + or + type instanceof ConstRefFoo and s = "ConstRefFoo" + or + type instanceof ExactTDFoo and s = "TDFoo" + or + type instanceof ExactDeclFoo and s = "DeclFoo" +} + +module MyTest implements TestSig { + string getARelevantTag() { result = "type" } + + predicate hasActualResult(Location l, string element, string tag, string value) { + exists(Variable var, Type type | + var.getLocation().getFile().getBaseName() = "test.cpp" and + not var.isFromTemplateInstantiation(_) and + not var.isFromUninstantiatedTemplate(_) and + var.getLocation() = l and + element = var.toString() and + tag = getARelevantTag() and + type = var.getType() and + value = concat(string typestr | hasRelevantType(type, typestr) | typestr, ",") + ) + } +} + +import MakeTest diff --git a/cpp/common/test/library/codingstandards/cpp/types/Resolve/test.cpp b/cpp/common/test/library/codingstandards/cpp/types/Resolve/test.cpp new file mode 100644 index 0000000000..c0bd914ad2 --- /dev/null +++ b/cpp/common/test/library/codingstandards/cpp/types/Resolve/test.cpp @@ -0,0 +1,72 @@ +#include + +class Foo {}; + +Foo l1; // $ type=Foo,MaybeSpecFoo +Foo &l2 = l1; // $ type=RefFoo +const Foo l3; // $ type=ConstFoo,MaybeSpecFoo,SpecFoo +const Foo &l4 = l3; // $ type=ConstRefFoo +volatile Foo l5; // $ type=MaybeSpecFoo,SpecFoo +volatile Foo &l6 = l5; // $ type= +const volatile Foo l7; // $ type=ConstFoo,MaybeSpecFoo,SpecFoo +const volatile Foo &l8 = l7; // $ type=ConstRefFoo + +typedef Foo TDFoo; +typedef Foo &TDFooRef; +typedef const Foo TDFooConst; +typedef const Foo &TDFooConstRef; +typedef volatile Foo TDFooVol; +typedef const volatile Foo TDFooVolConst; + +// Plain typedefs +TDFoo l9; // $ type=Foo,MaybeSpecFoo,TDFoo +TDFooConst l10; // $ type=ConstFoo,MaybeSpecFoo,SpecFoo +TDFooVol l11; // $ type=MaybeSpecFoo,SpecFoo +TDFooVolConst l12; // $ type=ConstFoo,MaybeSpecFoo,SpecFoo +const TDFoo l13; // $ type=ConstFoo,MaybeSpecFoo,SpecFoo +volatile TDFoo l15; // $ type=MaybeSpecFoo,SpecFoo + +// Ref types +TDFoo &l17 = l1; // $ type=RefFoo +TDFooRef l18 = l1; // $ type=RefFoo +TDFooConstRef l19 = l3; // $ type=ConstRefFoo + +// const collapse +const TDFooConstRef l21 = l3; // $ type=ConstRefFoo +const TDFooConst l14; // $ type=ConstFoo,MaybeSpecFoo,SpecFoo +const volatile TDFoo l16; // $ type=ConstFoo,MaybeSpecFoo,SpecFoo + +// Ref collapse +TDFooRef &l22 = l1; // $ type=RefFoo +const TDFooRef &l23 = l1; // $ type=RefFoo +TDFooConstRef &l24 = l3; // $ type=ConstRefFoo + +// Cannot const qualify a ref type, const is ignored [8.3.2/1] +TDFooRef const l20 = l1; // $ type=RefFoo + +template class Declval { +public: + T type; +}; + +decltype(l1) l25 = l1; // $ type=DeclFoo,Foo,MaybeSpecFoo +decltype(Declval::type) l26; // $ type=DeclFoo,Foo,MaybeSpecFoo +decltype(Declval::type) l27 = l1; // $ type=RefFoo +decltype(Declval::type) l28; // $ type=ConstFoo,MaybeSpecFoo,SpecFoo +decltype(Declval::type) l29 = l1; // $ type=ConstRefFoo +decltype(l25) l30; // $ type=DeclFoo,Foo,MaybeSpecFoo +decltype(Declval::type) l31; // $ type=DeclFoo,Foo,MaybeSpecFoo +decltype(Declval::type) l32 = l1; // $ type=RefFoo +decltype(Declval::type) l33 = l1; // $ type=ConstRefFoo +decltype(Declval::type) l34; // $ type=ConstFoo,MaybeSpecFoo,SpecFoo + +const decltype(Declval::type) l35; // $ type=ConstFoo,MaybeSpecFoo,SpecFoo +volatile decltype(Declval::type) l36; // $ type=MaybeSpecFoo,SpecFoo +const volatile decltype(Declval::type) + l37; // $ type=ConstFoo,MaybeSpecFoo,SpecFoo +decltype(Declval::type) &l38 = l1; // $ type=RefFoo +decltype(Declval::type) &l39 = l1; // $ type=RefFoo +decltype(Declval::type) &l40 = l1; // $ type=ConstRefFoo +decltype(Declval::type) &l41 = l1; // $ type=ConstRefFoo +const decltype(Declval::type) + l42; // $ type=ConstFoo,MaybeSpecFoo,SpecFoo \ No newline at end of file diff --git a/cpp/common/test/library/codingstandards/cpp/types/options b/cpp/common/test/library/codingstandards/cpp/types/options new file mode 100644 index 0000000000..17bcdd919b --- /dev/null +++ b/cpp/common/test/library/codingstandards/cpp/types/options @@ -0,0 +1 @@ +semmle-extractor-options:--clang -std=c++17 -nostdinc++ -I../../../../../../../common/test/includes/standard-library -I../../../../common/test/includes/custom-library \ No newline at end of file diff --git a/cpp/misra/src/codeql-pack.lock.yml b/cpp/misra/src/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/cpp/misra/src/codeql-pack.lock.yml +++ b/cpp/misra/src/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/cpp/misra/src/rules/RULE-28-6-1/StdMoveWithNonConstLvalue.ql b/cpp/misra/src/rules/RULE-28-6-1/StdMoveWithNonConstLvalue.ql new file mode 100644 index 0000000000..5287126009 --- /dev/null +++ b/cpp/misra/src/rules/RULE-28-6-1/StdMoveWithNonConstLvalue.ql @@ -0,0 +1,48 @@ +/** + * @id cpp/misra/std-move-with-non-const-lvalue + * @name RULE-28-6-1: The argument to std::move shall be a non-const lvalue + * @description Calling std::move on a const lvalue will not result in a move, and calling std::move + * on an rvalue is redundant. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-28-6-1 + * scope/single-translation-unit + * correctness + * maintainability + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.standardlibrary.Utility +import codingstandards.cpp.ast.ValueCategory +import codingstandards.cpp.types.Resolve +import codingstandards.cpp.types.Specifiers + +predicate isConstLvalue(Expr arg) { + getValueCategory(arg).isLValue() and + arg.getType() instanceof ResolvesTo::ExactlyOrRef +} + +Type typeOfArgument(Expr e) { + // An xvalue may be a constructor, which has no return type. However, these xvalues as act values + // of the constructed type. + if e instanceof ConstructorCall + then result = e.(ConstructorCall).getTargetType() + else result = e.getType() +} + +from StdMoveCall call, Expr arg, string description +where + arg = call.getArgument(0) and + ( + isConstLvalue(arg) and + description = "const " + getValueCategory(arg).toString() + or + getValueCategory(arg).isRValue() and + description = getValueCategory(arg).toString() + ) +select call, + "Call to 'std::move' with " + description + " argument of type '" + typeOfArgument(arg).toString() + + "'." diff --git a/cpp/misra/src/rules/RULE-9-5-1/LegacyForStatementsShouldBeSimple.ql b/cpp/misra/src/rules/RULE-9-5-1/LegacyForStatementsShouldBeSimple.ql index 9525d1f5f8..eb7759b1d8 100644 --- a/cpp/misra/src/rules/RULE-9-5-1/LegacyForStatementsShouldBeSimple.ql +++ b/cpp/misra/src/rules/RULE-9-5-1/LegacyForStatementsShouldBeSimple.ql @@ -22,6 +22,8 @@ import codingstandards.cpp.Call import codingstandards.cpp.Loops import codingstandards.cpp.ast.Increment import codingstandards.cpp.misra.BuiltInTypeRules::MisraCpp23BuiltInTypes +import codingstandards.cpp.types.Resolve +import codingstandards.cpp.types.Specifiers Variable getDeclaredVariableInForLoop(ForStmt forLoop) { result = forLoop.getADeclaration().getADeclarationEntry().(VariableDeclarationEntry).getVariable() @@ -85,13 +87,11 @@ Expr getLoopStepOfForStmt(ForStmt forLoop) { predicate loopVariableAssignedToNonConstPointerOrReferenceType( ForStmt forLoop, VariableAccess loopVariableAccessInCondition, Expr assignmentRhs ) { - exists(Type targetType, DerivedType strippedType | + exists(Type targetType | isAssignment(assignmentRhs, targetType, _) and - strippedType = targetType.stripTopLevelSpecifiers() and - not strippedType.getBaseType().isConst() and ( - strippedType instanceof PointerType or - strippedType instanceof ReferenceType + targetType instanceof ResolvesTo::Type>::IgnoringSpecifiers or + targetType instanceof ResolvesTo::Type>::IgnoringSpecifiers ) | assignmentRhs.getEnclosingStmt().getParent*() = forLoop.getStmt() and diff --git a/cpp/misra/test/codeql-pack.lock.yml b/cpp/misra/test/codeql-pack.lock.yml index a45ea8f438..9a7d19bc0a 100644 --- a/cpp/misra/test/codeql-pack.lock.yml +++ b/cpp/misra/test/codeql-pack.lock.yml @@ -1,6 +1,8 @@ --- lockVersion: 1.0.0 dependencies: + advanced-security/qtil: + version: 0.0.3 codeql/cpp-all: version: 4.0.3 codeql/dataflow: diff --git a/cpp/misra/test/rules/RULE-28-6-1/StdMoveWithNonConstLvalue.expected b/cpp/misra/test/rules/RULE-28-6-1/StdMoveWithNonConstLvalue.expected new file mode 100644 index 0000000000..763b9671e5 --- /dev/null +++ b/cpp/misra/test/rules/RULE-28-6-1/StdMoveWithNonConstLvalue.expected @@ -0,0 +1,21 @@ +| test.cpp:18:8:18:16 | call to move | Call to 'std::move' with const lvalue argument of type 'const string &'. | +| test.cpp:19:8:19:16 | call to move | Call to 'std::move' with const lvalue argument of type 'const string'. | +| test.cpp:21:8:21:16 | call to move | Call to 'std::move' with const lvalue argument of type 'const string &&'. | +| test.cpp:23:8:23:16 | call to move | Call to 'std::move' with xvalue argument of type 'basic_string, allocator>'. | +| test.cpp:24:8:24:16 | call to move | Call to 'std::move' with xvalue argument of type 'basic_string, allocator>'. | +| test.cpp:25:8:25:16 | call to move | Call to 'std::move' with xvalue argument of type 'string'. | +| test.cpp:35:5:35:13 | call to move | Call to 'std::move' with const lvalue argument of type 'const string'. | +| test.cpp:40:56:40:64 | call to move | Call to 'std::move' with const lvalue argument of type 'const basic_string, allocator> &'. | +| test.cpp:42:56:42:64 | call to move | Call to 'std::move' with const lvalue argument of type 'const basic_string, allocator> &'. | +| test.cpp:46:56:46:64 | call to move | Call to 'std::move' with const lvalue argument of type 'const basic_string, allocator> &'. | +| test.cpp:50:56:50:64 | call to move | Call to 'std::move' with const lvalue argument of type 'const basic_string, allocator> &'. | +| test.cpp:55:49:55:57 | call to move | Call to 'std::move' with const lvalue argument of type 'const basic_string, allocator> &'. | +| test.cpp:59:49:59:57 | call to move | Call to 'std::move' with const lvalue argument of type 'const basic_string, allocator> &'. | +| test.cpp:63:49:63:57 | call to move | Call to 'std::move' with const lvalue argument of type 'const basic_string, allocator> &'. | +| test.cpp:68:48:68:56 | call to move | Call to 'std::move' with const lvalue argument of type 'const basic_string, allocator>'. | +| test.cpp:72:48:72:56 | call to move | Call to 'std::move' with const lvalue argument of type 'const basic_string, allocator> &'. | +| test.cpp:76:48:76:56 | call to move | Call to 'std::move' with const lvalue argument of type 'const basic_string, allocator> &&'. | +| test.cpp:81:55:81:63 | call to move | Call to 'std::move' with const lvalue argument of type 'const basic_string, allocator> &&'. | +| test.cpp:85:55:85:63 | call to move | Call to 'std::move' with const lvalue argument of type 'const basic_string, allocator> &'. | +| test.cpp:89:55:89:63 | call to move | Call to 'std::move' with const lvalue argument of type 'const basic_string, allocator> &&'. | +| test.cpp:129:3:129:11 | call to move | Call to 'std::move' with const lvalue argument of type 'const iterator &'. | diff --git a/cpp/misra/test/rules/RULE-28-6-1/StdMoveWithNonConstLvalue.qlref b/cpp/misra/test/rules/RULE-28-6-1/StdMoveWithNonConstLvalue.qlref new file mode 100644 index 0000000000..c1906c448e --- /dev/null +++ b/cpp/misra/test/rules/RULE-28-6-1/StdMoveWithNonConstLvalue.qlref @@ -0,0 +1 @@ +rules/RULE-28-6-1/StdMoveWithNonConstLvalue.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-28-6-1/test.cpp b/cpp/misra/test/rules/RULE-28-6-1/test.cpp new file mode 100644 index 0000000000..e77e43c278 --- /dev/null +++ b/cpp/misra/test/rules/RULE-28-6-1/test.cpp @@ -0,0 +1,132 @@ +#include +#include + +template T func(T param); + +std::string f2(); + +void f3() { + std::string l1{"hello"}; + std::string &l2 = l1; + std::string const &l3 = l1; + std::string const l4{"hello"}; + std::string &&l5 = std::string{"hello"}; + const std::string &&l6 = std::string{"hello"}; + + func(std::move(l1)); // COMPLIANT - non-const lvalue + func(std::move(l2)); // COMPLIANT - non-const lvalue + func(std::move(l3)); // NON_COMPLIANT - const lvalue + func(std::move(l4)); // NON_COMPLIANT - const lvalue + func(std::move(l5)); // COMPLIANT - non-const glvalue + func(std::move(l6)); // NON_COMPLIANT - const lvalue + + func(std::move(std::string("hello"))); // NON_COMPLIANT - rvalue argument + func(std::move(l1 + "!")); // NON_COMPLIANT - temporary rvalue + func(std::move(f2())); // NON_COMPLIANT - rvalue from function call +} + +class TestClass { +public: + std::string m1; + std::string const m2{"constant"}; + + void test_member_variables() { + std::move(m1); // COMPLIANT - non-const member + std::move(m2); // NON_COMPLIANT - const member + } +}; + +// NON_COMPLIANT -- always moving const value +template void cref_tpl1(const T ¶m) { std::move(param); } +// NON_COMPLIANT -- always moving const value +template void cref_tpl2(const T ¶m) { std::move(param); } +// NON_COMPLIANT -- always moving const value +template void cref_tpl3(const T ¶m) { std::move(param); } +// NON_COMPLIANT -- always moving const value +template void cref_tpl4(const T ¶m) { std::move(param); } +// NON_COMPLIANT -- always moving const value +template void cref_tpl5(const T ¶m) { std::move(param); } +// NON_COMPLIANT -- always moving const value +template void cref_tpl6(const T ¶m) { std::move(param); } + +// T=std::string -- COMPLIANT +template void ref_tpl1(T ¶m) { std::move(param); } +// T=const std::string -- NON_COMPLIANT, move const lvalue ref +template void ref_tpl2(T ¶m) { std::move(param); } +// T=std::string& -- COMPLIANT +template void ref_tpl3(T ¶m) { std::move(param); } +// T=const std::string& -- NON_COMPLIANT, move const lvalue ref +template void ref_tpl4(T ¶m) { std::move(param); } +// T=std::string&& -- COMPLIANT +template void ref_tpl5(T ¶m) { std::move(param); } +// T=const std::string&& -- NON_COMPLIANT, move const rvalue ref +template void ref_tpl6(T ¶m) { std::move(param); } + +// T=std::string -- COMPLIANT +template void val_tpl1(T param) { std::move(param); } +// T=const std::string -- NON_COMPLIANT, move const lvalue +template void val_tpl2(T param) { std::move(param); } +// T=std::string& -- COMPLIANT +template void val_tpl3(T param) { std::move(param); } +// T=const std::string& -- NON_COMPLIANT -- move const lvalue ref +template void val_tpl4(T param) { std::move(param); } +// T=std::string&& -- COMPLIANT +template void val_tpl5(T param) { std::move(param); } +// T=const std::string&& -- NON_COMPLIANT -- move const rvalue ref +template void val_tpl6(T param) { std::move(param); } + +// T=std::string -- COMPLIANT +template void univ_ref_tpl1(T &¶m) { std::move(param); } +// T=const std::string -- NON_COMPLIANT -- moving const rvalue ref +template void univ_ref_tpl2(T &¶m) { std::move(param); } +// T=std::string& -- COMPLIANT +template void univ_ref_tpl3(T &¶m) { std::move(param); } +// T=const std::string& -- NON_COMPLIANT -- moving const lvalue ref +template void univ_ref_tpl4(T &¶m) { std::move(param); } +// T=std::string&& -- COMPLIANT +template void univ_ref_tpl5(T &¶m) { std::move(param); } +// T=const std::string&& -- NON_COMPLIANT +template void univ_ref_tpl6(T &¶m) { std::move(param); } + +void f4() { + std::string l1{"test"}; + const std::string l2{"test"}; + std::string &l3 = l1; + const std::string &l4 = l1; + + val_tpl1(l1); + val_tpl2(l2); + val_tpl3(l3); + val_tpl4(l4); + val_tpl5(""); + val_tpl6(""); + ref_tpl1(l1); + ref_tpl2(l2); + ref_tpl3(l3); + ref_tpl4(l4); + ref_tpl5(l1); + ref_tpl6(l1); + cref_tpl1(l1); + cref_tpl2(l2); + cref_tpl3(l3); + cref_tpl4(l4); + cref_tpl5(l1); + cref_tpl6(l1); + univ_ref_tpl1(""); + univ_ref_tpl2(""); + univ_ref_tpl3(l3); + univ_ref_tpl4(l4); + univ_ref_tpl5(""); + univ_ref_tpl6(""); +} + +#include +#include + +void algorithm() { + std::vector v; + const std::vector::iterator &it = v.begin(); + std::move(it); // NON_COMPLIANT -- from + std::move::iterator &>( + it, it, std::back_inserter(v)); // COMPLIANT -- from +} \ No newline at end of file diff --git a/rule_packages/cpp/Preconditions5.json b/rule_packages/cpp/Preconditions5.json new file mode 100644 index 0000000000..5a758c2fb0 --- /dev/null +++ b/rule_packages/cpp/Preconditions5.json @@ -0,0 +1,26 @@ +{ + "MISRA-C++-2023": { + "RULE-28-6-1": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Calling std::move on a const lvalue will not result in a move, and calling std::move on an rvalue is redundant.", + "kind": "problem", + "name": "The argument to std::move shall be a non-const lvalue", + "precision": "very-high", + "severity": "error", + "short_name": "StdMoveWithNonConstLvalue", + "tags": [ + "scope/single-translation-unit", + "correctness", + "maintainability" + ] + } + ], + "title": "The argument to std::move shall be a non-const lvalue" + } + } +} \ No newline at end of file diff --git a/rules.csv b/rules.csv index 3cb64a0bac..f9952f8fb1 100644 --- a/rules.csv +++ b/rules.csv @@ -996,7 +996,7 @@ cpp,MISRA-C++-2023,RULE-25-5-2,Yes,Mandatory,Decidable,Single Translation Unit," cpp,MISRA-C++-2023,RULE-25-5-3,Yes,Mandatory,Undecidable,System,"The pointer returned by the C++ Standard Library functions asctime, ctime, gmtime, localtime, localeconv, getenv, setlocale or strerror must not be used following a subsequent call to the same function",RULE-21-20,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-26-3-1,Yes,Advisory,Decidable,Single Translation Unit,std::vector should not be specialized with bool,A18-1-2,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-28-3-1,Yes,Required,Undecidable,System,Predicates shall not have persistent side effects,A25-1-1,SideEffects3,Easy, -cpp,MISRA-C++-2023,RULE-28-6-1,Yes,Required,Decidable,Single Translation Unit,The argument to std::move shall be a non-const lvalue,A18-9-3,Preconditions,Easy, +cpp,MISRA-C++-2023,RULE-28-6-1,Yes,Required,Decidable,Single Translation Unit,The argument to std::move shall be a non-const lvalue,A18-9-3,Preconditions5,Easy, cpp,MISRA-C++-2023,RULE-28-6-2,Yes,Required,Decidable,Single Translation Unit,Forwarding references and std::forward shall be used together,A18-9-2,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-28-6-3,Yes,Required,Decidable,Single Translation Unit,An object shall not be used while in a potentially moved-from state,A12-8-3,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-28-6-4,Yes,Required,Decidable,Single Translation Unit,"The result of std::remove, std::remove_if, std::unique and empty shall be used",,DeadCode2,Easy,