diff --git a/cpp/common/src/codingstandards/cpp/Call.qll b/cpp/common/src/codingstandards/cpp/Call.qll index 4edbb454a..22fde8c29 100644 --- a/cpp/common/src/codingstandards/cpp/Call.qll +++ b/cpp/common/src/codingstandards/cpp/Call.qll @@ -17,3 +17,48 @@ FunctionType getExprCallFunctionType(ExprCall call) { // Returns a RoutineType result = call.(ExprCall).getChild(-1).getType().(PointerToMemberType).getBaseType() } + +/** + * An `Expr` that is used as an argument to a `Call`, and has helpers to handle with the differences + * between `ExprCall` and `FunctionCall` cases. + */ +class CallArgumentExpr extends Expr { + Call call; + Type paramType; + int argIndex; + + CallArgumentExpr() { + this = call.getArgument(argIndex) and + ( + paramType = call.getTarget().getParameter(argIndex).getType() + or + paramType = getExprCallFunctionType(call).getParameterType(argIndex) + ) + } + + /** + * Gets the `FunctionExpr` or `FunctionCall` that this argument appears in. + */ + Call getCall() { result = call } + + /** + * Gets the `Type` of the parameter corresponding to this argument, whether its based on the + * target function or the function pointer type. + */ + Type getParamType() { result = paramType } + + /** + * Gets the argument index of this argument in the call. + */ + int getArgIndex() { result = argIndex } + + /** + * Gets the target `Function` if this is an argument to a `FunctionCall`. + */ + Function getKnownFunction() { result = call.getTarget() } + + /** + * Gets the target `Parameter` if this is an argument to a `FunctionCall`. + */ + Parameter getKnownParameter() { result = call.getTarget().getParameter(argIndex) } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Declarations6.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Declarations6.qll new file mode 100644 index 000000000..cc2ffb53c --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Declarations6.qll @@ -0,0 +1,26 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Declarations6Query = TPointerOrRefParamNotConstQuery() + +predicate isDeclarations6QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `pointerOrRefParamNotConst` query + Declarations6Package::pointerOrRefParamNotConstQuery() and + queryId = + // `@id` for the `pointerOrRefParamNotConst` query + "cpp/misra/pointer-or-ref-param-not-const" and + ruleId = "RULE-10-1-1" and + category = "advisory" +} + +module Declarations6Package { + Query pointerOrRefParamNotConstQuery() { + //autogenerate `Query` type + result = + // `Query` type for `pointerOrRefParamNotConst` query + TQueryCPP(TDeclarations6PackageQuery(TPointerOrRefParamNotConstQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll index 3b8175042..d87fb23e4 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll @@ -34,6 +34,7 @@ import DeadCode9 import Declarations import Declarations1 import Declarations2 +import Declarations6 import ExceptionSafety import Exceptions1 import Exceptions2 @@ -131,6 +132,7 @@ newtype TCPPQuery = TDeclarationsPackageQuery(DeclarationsQuery q) or TDeclarations1PackageQuery(Declarations1Query q) or TDeclarations2PackageQuery(Declarations2Query q) or + TDeclarations6PackageQuery(Declarations6Query q) or TExceptionSafetyPackageQuery(ExceptionSafetyQuery q) or TExceptions1PackageQuery(Exceptions1Query q) or TExceptions2PackageQuery(Exceptions2Query q) or @@ -228,6 +230,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat isDeclarationsQueryMetadata(query, queryId, ruleId, category) or isDeclarations1QueryMetadata(query, queryId, ruleId, category) or isDeclarations2QueryMetadata(query, queryId, ruleId, category) or + isDeclarations6QueryMetadata(query, queryId, ruleId, category) or isExceptionSafetyQueryMetadata(query, queryId, ruleId, category) or isExceptions1QueryMetadata(query, queryId, ruleId, category) or isExceptions2QueryMetadata(query, queryId, ruleId, category) or diff --git a/cpp/common/test/includes/standard-library/vector.h b/cpp/common/test/includes/standard-library/vector.h index 543d80eca..e4d0a75a5 100644 --- a/cpp/common/test/includes/standard-library/vector.h +++ b/cpp/common/test/includes/standard-library/vector.h @@ -20,8 +20,8 @@ template > class vector { iterator begin(); iterator end(); - const_iterator cbegin(); - const_iterator cend(); + const_iterator cbegin() const; + const_iterator cend() const; size_type size() const noexcept; void resize(size_type sz); void resize(size_type sz, const T &c); diff --git a/cpp/misra/src/rules/RULE-10-1-1/PointerOrRefParamNotConst.ql b/cpp/misra/src/rules/RULE-10-1-1/PointerOrRefParamNotConst.ql new file mode 100644 index 000000000..8db001492 --- /dev/null +++ b/cpp/misra/src/rules/RULE-10-1-1/PointerOrRefParamNotConst.ql @@ -0,0 +1,254 @@ +/** + * @id cpp/misra/pointer-or-ref-param-not-const + * @name RULE-10-1-1: The target type of a pointer or lvalue reference parameter should be const-qualified appropriately + * @description Pointer or lvalue reference parameters that do not modify the target object should + * be const-qualified to accurately reflect function behavior and prevent unintended + * modifications. + * @kind problem + * @precision high + * @problem.severity warning + * @tags external/misra/id/rule-10-1-1 + * correctness + * readability + * performance + * scope/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra +import codingstandards.cpp.types.Pointers +import codingstandards.cpp.Call +import codingstandards.cpp.SideEffect + +/** + * Holds if the function is in a template scope and should be excluded. + */ +predicate isInTemplateScope(Function f) { + f.isFromTemplateInstantiation(_) + or + f.isFromUninstantiatedTemplate(_) +} + +/** + * A `Type` that may be a pointer, array, or reference, to a const or a non-const type. + * + * For example, `const int*`, `int* const`, `const int* const`, `int*`, `int&`, `const int&` are all + * `PointerLikeType`s, while `int`, `int&&`, and `const int` are not. + * + * To check if a `PointerLikeType` points/refers to a const-qualified type, use the `pointsToConst()` + * predicate. + */ +class PointerLikeType extends Type { + Type innerType; + Type outerType; + + PointerLikeType() { + innerType = this.(UnspecifiedPointerOrArrayType).getBaseType() and + outerType = this + or + innerType = this.(LValueReferenceType).getBaseType() and + outerType = this + or + exists(PointerLikeType stripped | + stripped = this.stripTopLevelSpecifiers() and not stripped = this + | + innerType = stripped.getInnerType() and + outerType = stripped.getOuterType() + ) + } + + /** + * Gets the pointed to or referred to type, for instance `int` for `int*` or `const int&`. + */ + Type getInnerType() { result = innerType } + + /** + * Gets the resolved pointer, array, or reference type itself, for instance `int*` in `int* const`. + * + * Removes cv-qualification and resolves typedefs and decltypes and specifiers via + * `stripTopLevelSpecifiers()`. + */ + Type getOuterType() { result = outerType } + + /** + * Holds when this type points to const -- for example, `const int*` and `const int&` point to + * const, while `int*`, `int *const` and `int&` do not. + */ + predicate pointsToConst() { innerType.isConst() } + + /** + * Holds when this type points to non-const -- for example, `int*` and `int&` and `int *const` + * point to non-const, while `const int*`, `const int&` do not. + */ + predicate pointsToNonConst() { not innerType.isConst() } +} + +/** + * A `Parameter` whose type is a `PointerLikeType` such as a pointer or reference. + */ +class PointerLikeParam extends Parameter { + PointerLikeType pointerLikeType; + + PointerLikeParam() { + pointerLikeType = this.getType() and + not pointerLikeType.pointsToConst() and + // Exclude pointers to non-object types + not pointerLikeType.getInnerType() instanceof RoutineType + } + + /** + * Gets the pointer like type of this parameter. + */ + PointerLikeType getPointerLikeType() { result = pointerLikeType } + + /** + * Gets usages of this parameter that maintain pointer-like semantics -- typically this means + * either a normal access, or switching between pointers and reference semantics. + * + * Examples of accesses with pointer-like semantics include: + * - `ref` in `int &x = ref`, or `&ref` in `int *x = &ref`; + * - `ptr` in `int *x = ptr`, or `*ptr` in `int &x = *ptr`; + * + * In the above examples, we can still access the value pointed to by `ref` or `ptr` through the + * expression. + * + * Examples of non-pointer-like semantics include: + * - `ref` in `int x = ref` and `*ptr` in `int x = *ptr`; + * + * In the above examples, the value pointed to by `ref` or `ptr` is copied and the expression + * refers to a new/different object. + */ + Expr getAPointerLikeAccess() { + result = this.getAnAccess() + or + // For reference parameters, also consider accesses to the parameter itself as accesses to the referent + pointerLikeType.getOuterType() instanceof ReferenceType and + result.(AddressOfExpr).getOperand() = this.getAnAccess() + or + // A pointer is dereferenced, but the result is not copied + pointerLikeType.getOuterType() instanceof PointerType and + result.(PointerDereferenceExpr).getOperand() = this.getAnAccess() and + not any(ReferenceDereferenceExpr rde).getExpr() = result.getConversion+() + } +} + +/** + * A `VariableEffect` whose target variable is a `PointerLikeParam`. + * + * Examples of pointer-like effects on a pointer-like parameter `p` would include `p = ...`, `++p`, + * `*p = ...`, and `++*p`, etc. + */ +class PointerLikeEffect extends VariableEffect { + PointerLikeParam param; + + PointerLikeEffect() { param = this.getTarget() } + + /** + * Holds if this effect modifies the pointed-to or referred-to object. + * + * For example, `*p = 0` modifies the inner type if `p` is a pointer, and `p = 0` affects the + * inner type if `p` is a reference. + */ + predicate affectsInnerType() { + if param.getPointerLikeType() instanceof ReferenceType + then affectsOuterType() + else not affectsOuterType() + } + + /** + * Holds if this effect modifies the pointer or reference itself. + * + * For example, `p = ...` and `++p` modify the outer type, whether that type is a pointer or + * reference, while `*p = 0` does not modify the outer type. + */ + predicate affectsOuterType() { + this.(Assignment).getLValue() = param.getAnAccess() + or + this.(CrementOperation).getOperand() = param.getAnAccess() + } +} + +/** + * A candidate parameter that could have its target type const-qualified. + */ +class NonConstParam extends PointerLikeParam { + NonConstParam() { + not pointerLikeType.pointsToConst() and + // Ignore parameters in functions without bodies + exists(this.getFunction().getBlock()) and + // Ignore unnamed parameters + this.isNamed() and + // Ignore functions that use ASM statements + not exists(AsmStmt a | a.getEnclosingFunction() = this.getFunction()) and + // Must have a pointer, array, or lvalue reference type with non-const target + // Exclude pointers to non-object types + not pointerLikeType.getInnerType() instanceof RoutineType and + not pointerLikeType.getInnerType() instanceof VoidType and + // Exclude virtual functions + not this.getFunction().isVirtual() and + // Exclude functions in template scope + not isInTemplateScope(this.getFunction()) and + // Exclude main + not this.getFunction().hasGlobalName("main") and + // Exclude deleted functions + not this.getFunction().isDeleted() and + // Exclude any parameter whose underlying data is modified + not exists(PointerLikeEffect effect | + effect.getTarget() = this and + effect.affectsInnerType() + ) and + // Exclude parameters passed as arguments to non-const pointer/ref params + not exists(CallArgumentExpr arg | + arg = this.getAPointerLikeAccess() and + arg.getParamType().(PointerLikeType).pointsToNonConst() + ) and + // Exclude parameters used as qualifier for a non-const member function + not exists(FunctionCall fc | + fc.getQualifier() = [this.getAnAccess(), this.getAPointerLikeAccess()] and + not fc.getTarget().hasSpecifier("const") and + not fc.getTarget().isStatic() + ) and + // Exclude parameters assigned to a non-const pointer/reference alias + not exists(Variable v | + v.getAnAssignedValue() = this.getAPointerLikeAccess() and + v.getType().(PointerLikeType).pointsToNonConst() + ) and + // Exclude parameters returned as non-const pointer/reference + not exists(ReturnStmt ret | + ret.getExpr() = this.getAPointerLikeAccess() and + ret.getEnclosingFunction().getType().(PointerLikeType).pointsToNonConst() + ) and + not exists(FieldAccess fa | + fa.getQualifier() = [this.getAPointerLikeAccess(), this.getAnAccess()] and + fa.isLValueCategory() + ) and + not exists(AddressOfExpr addrOf | + // exclude pointer to pointer and reference to pointer cases. + addrOf.getOperand() = this.getAPointerLikeAccess() and + addrOf.getType().(PointerLikeType).getInnerType() instanceof PointerLikeType + ) and + not exists(PointerArithmeticOperation pointerManip | + pointerManip.getAnOperand() = this.getAPointerLikeAccess() and + pointerManip.getType().(PointerLikeType).pointsToNonConst() + ) + } +} + +from NonConstParam param, Type innerType +where + not isExcluded(param, Declarations6Package::pointerOrRefParamNotConstQuery()) and + innerType = param.getPointerLikeType().getInnerType() and + not param.isAffectedByMacro() and + // There are some odd database patterns where a function has multiple parameters with the same + // index and different names, due to strange extraction+linker scenarios. These give wrong + // results, and should be excluded. + count(Parameter p | + p.getFunction() = param.getFunction() and + p.getIndex() = param.getIndex() + ) = 1 +select param, + "Parameter '" + param.getName() + "' points/refers to a non-const type '" + innerType.toString() + + "' but does not modify the target object in the $@.", param.getFunction().getDefinition(), + "function definition" diff --git a/cpp/misra/test/rules/RULE-10-1-1/PointerOrRefParamNotConst.expected b/cpp/misra/test/rules/RULE-10-1-1/PointerOrRefParamNotConst.expected new file mode 100644 index 000000000..bd3262e6c --- /dev/null +++ b/cpp/misra/test/rules/RULE-10-1-1/PointerOrRefParamNotConst.expected @@ -0,0 +1,48 @@ +| test.cpp:4:22:4:23 | p1 | Parameter 'p1' points/refers to a non-const type 'int8_t' but does not modify the target object in the $@. | test.cpp:4:6:4:7 | definition of f1 | function definition | +| test.cpp:7:28:7:29 | p4 | Parameter 'p4' points/refers to a non-const type 'int8_t' but does not modify the target object in the $@. | test.cpp:4:6:4:7 | definition of f1 | function definition | +| test.cpp:8:21:8:22 | p5 | Parameter 'p5' points/refers to a non-const type 'int8_t' but does not modify the target object in the $@. | test.cpp:4:6:4:7 | definition of f1 | function definition | +| test.cpp:19:36:19:37 | l2 | Parameter 'l2' points/refers to a non-const type 'vector>' but does not modify the target object in the $@. | test.cpp:18:6:18:7 | definition of f3 | function definition | +| test.cpp:40:20:40:21 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:40:5:40:5 | definition of operator() | function definition | +| test.cpp:41:20:41:21 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:41:5:41:5 | definition of operator() | function definition | +| test.cpp:42:20:42:21 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:42:5:42:5 | definition of operator() | function definition | +| test.cpp:45:20:45:21 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:45:5:45:5 | definition of operator() | function definition | +| test.cpp:46:20:46:21 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:46:5:46:5 | definition of operator() | function definition | +| test.cpp:50:20:50:21 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:50:5:50:5 | definition of operator() | function definition | +| test.cpp:51:20:51:21 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:51:5:51:5 | definition of operator() | function definition | +| test.cpp:54:20:54:21 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:54:5:54:5 | definition of operator() | function definition | +| test.cpp:55:20:55:21 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:55:5:55:5 | definition of operator() | function definition | +| test.cpp:60:20:60:21 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:60:5:60:5 | definition of operator() | function definition | +| test.cpp:61:20:61:21 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:61:5:61:5 | definition of operator() | function definition | +| test.cpp:62:20:62:21 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:62:5:62:5 | definition of operator() | function definition | +| test.cpp:63:20:63:21 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:63:5:63:5 | definition of operator() | function definition | +| test.cpp:70:20:70:21 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:70:5:70:5 | definition of operator() | function definition | +| test.cpp:71:20:71:21 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:71:5:71:5 | definition of operator() | function definition | +| test.cpp:72:20:72:21 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:72:5:72:5 | definition of operator() | function definition | +| test.cpp:73:20:73:21 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:73:5:73:5 | definition of operator() | function definition | +| test.cpp:78:20:78:21 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:78:5:78:5 | definition of operator() | function definition | +| test.cpp:80:20:80:21 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:80:5:80:5 | definition of operator() | function definition | +| test.cpp:82:20:82:21 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:82:5:82:5 | definition of operator() | function definition | +| test.cpp:84:20:84:21 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:84:5:84:5 | definition of operator() | function definition | +| test.cpp:89:20:89:21 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:89:5:89:5 | definition of operator() | function definition | +| test.cpp:91:20:91:21 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:91:5:91:5 | definition of operator() | function definition | +| test.cpp:93:20:93:21 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:93:5:93:5 | definition of operator() | function definition | +| test.cpp:95:20:95:21 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:95:5:95:5 | definition of operator() | function definition | +| test.cpp:131:26:131:27 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:131:5:131:5 | definition of operator() | function definition | +| test.cpp:132:26:132:27 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:132:5:132:5 | definition of operator() | function definition | +| test.cpp:133:26:133:27 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:133:5:133:5 | definition of operator() | function definition | +| test.cpp:134:26:134:27 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:134:5:134:5 | definition of operator() | function definition | +| test.cpp:135:26:135:27 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:135:5:135:5 | definition of operator() | function definition | +| test.cpp:136:26:136:27 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:136:5:136:5 | definition of operator() | function definition | +| test.cpp:137:12:137:13 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:137:5:137:5 | definition of operator() | function definition | +| test.cpp:138:12:138:13 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:138:5:138:5 | definition of operator() | function definition | +| test.cpp:139:12:139:13 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:139:5:139:5 | definition of operator() | function definition | +| test.cpp:140:12:140:13 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:140:5:140:5 | definition of operator() | function definition | +| test.cpp:141:12:141:13 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:141:5:141:5 | definition of operator() | function definition | +| test.cpp:142:12:142:13 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:142:5:142:5 | definition of operator() | function definition | +| test.cpp:145:23:145:24 | p1 | Parameter 'p1' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:145:6:145:7 | definition of f5 | function definition | +| test.cpp:148:23:148:24 | p4 | Parameter 'p4' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:145:6:145:7 | definition of f5 | function definition | +| test.cpp:153:23:153:24 | p9 | Parameter 'p9' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:145:6:145:7 | definition of f5 | function definition | +| test.cpp:154:23:154:25 | p10 | Parameter 'p10' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:145:6:145:7 | definition of f5 | function definition | +| test.cpp:156:23:156:25 | p12 | Parameter 'p12' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:145:6:145:7 | definition of f5 | function definition | +| test.cpp:187:12:187:13 | p2 | Parameter 'p2' points/refers to a non-const type 'S' but does not modify the target object in the $@. | test.cpp:186:6:186:7 | definition of f6 | function definition | +| test.cpp:198:23:198:24 | p4 | Parameter 'p4' points/refers to a non-const type 'int32_t' but does not modify the target object in the $@. | test.cpp:195:6:195:7 | definition of f7 | function definition | diff --git a/cpp/misra/test/rules/RULE-10-1-1/PointerOrRefParamNotConst.qlref b/cpp/misra/test/rules/RULE-10-1-1/PointerOrRefParamNotConst.qlref new file mode 100644 index 000000000..f72df6a5b --- /dev/null +++ b/cpp/misra/test/rules/RULE-10-1-1/PointerOrRefParamNotConst.qlref @@ -0,0 +1 @@ +rules/RULE-10-1-1/PointerOrRefParamNotConst.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-10-1-1/test.cpp b/cpp/misra/test/rules/RULE-10-1-1/test.cpp new file mode 100644 index 000000000..abdd87910 --- /dev/null +++ b/cpp/misra/test/rules/RULE-10-1-1/test.cpp @@ -0,0 +1,236 @@ +#include +#include + +void f1(std::int8_t *p1, // NON_COMPLIANT - *p1 not modified + const std::int8_t *p2, // COMPLIANT - already const + std::int8_t *p3, // COMPLIANT - *p3 modified + std::int8_t *const p4, // NON_COMPLIANT - *p4 not modified + std::int8_t p5[3]) { // NON_COMPLIANT - array decays to pointer + *p3 = *p1 + *p2 + *p4 + p5[2]; +} + +void f2(std::int32_t &l1, // COMPLIANT - modified + std::int32_t &&l2, // COMPLIANT - rvalue reference, rule N/A + std::int32_t &) { // COMPLIANT - unnamed, rule N/A + l1 = 0; +} + +auto f3(std::vector &l1, // COMPLIANT - x.begin() is non-const + std::vector &l2 // NON_COMPLIANT - cbegin is const +) { + l1.begin(); + l2.cbegin(); +} + +int32_t i32; +int32_t *ptr; +const int32_t *cptr; +int32_t &ref = i32; +void take_i32(std::int32_t l1); +void take_ptr(std::int32_t *l1); +void take_cptr(const std::int32_t *l1); +void take_cptrc(const std::int32_t *const l1); +void take_ptrc(std::int32_t *const l1); +void take_ref(std::int32_t &l1); +void take_cref(const std::int32_t &l1); + +void f4() { + // Pointers used as values + [](std::int32_t *p1) { *p1 = 0; }; // COMPLIANT + [](std::int32_t *p1) { p1 = nullptr; }; // NON_COMPLIANT + [](std::int32_t *p1) { i32 = *p1; }; // NON_COMPLIANT + [](std::int32_t *p1) { take_i32(*p1); }; // NON_COMPLIANT + [](std::int32_t *p1) { ref = *p1; }; // COMPLIANT + [](std::int32_t *p1) { take_ref(*p1); }; // COMPLIANT + [](std::int32_t *p1) { const int &l1 = *p1; }; // NON_COMPLIANT + [](std::int32_t *p1) { take_cref(*p1); }; // NON_COMPLIANT + + // References used as values + [](std::int32_t &p1) { p1 = 0; }; // COMPLIANT + [](std::int32_t &p1) { i32 = p1; }; // NON_COMPLIANT + [](std::int32_t &p1) { take_i32(p1); }; // NON_COMPLIANT + [](std::int32_t &p1) { ref = p1; }; // COMPLIANT + [](std::int32_t &p1) { take_ref(p1); }; // COMPLIANT + [](std::int32_t &p1) { const int &l1 = p1; }; // NON_COMPLIANT + [](std::int32_t &p1) { take_cref(p1); }; // NON_COMPLIANT + + // Pointers used as pointers + [](std::int32_t *p1) { ptr = p1; }; // COMPLIANT + [](std::int32_t *p1) { take_ptr(p1); }; // COMPLIANT + [](std::int32_t *p1) { cptr = p1; }; // NON_COMPLIANT + [](std::int32_t *p1) { take_cptr(p1); }; // NON_COMPLIANT + [](std::int32_t *p1) { const std::int32_t *const l1 = p1; }; // NON_COMPLIANT + [](std::int32_t *p1) { take_cptrc(p1); }; // NON_COMPLIANT + [](std::int32_t *p1) { std::int32_t *const l1 = p1; }; // COMPLIANT + [](std::int32_t *p1) { take_ptrc(p1); }; // COMPLIANT + + // Taking addresses of references parameter + [](std::int32_t &p1) { ptr = &p1; }; // COMPLIANT + [](std::int32_t &p1) { take_ptr(&p1); }; // COMPLIANT + [](std::int32_t &p1) { cptr = &p1; }; // NON_COMPLIANT + [](std::int32_t &p1) { take_cptr(&p1); }; // NON_COMPLIANT + [](std::int32_t &p1) { const std::int32_t *const l1 = &p1; }; // NON_COMPLIANT + [](std::int32_t &p1) { take_cptrc(&p1); }; // NON_COMPLIANT + [](std::int32_t &p1) { std::int32_t *const l1 = &p1; }; // COMPLIANT + [](std::int32_t &p1) { take_ptrc(&p1); }; // COMPLIANT + + // Returning from pointer parameters + [](std::int32_t *p1) -> std::int32_t { return *p1; }; // NON_COMPLIANT + [](std::int32_t *p1) -> std::int32_t & { return *p1; }; // COMPLIANT + [](std::int32_t *p1) -> const std::int32_t & { return *p1; }; // NON_COMPLIANT + [](std::int32_t *p1) -> std::int32_t * { return p1; }; // COMPLIANT + [](std::int32_t *p1) -> const std::int32_t * { return p1; }; // NON_COMPLIANT + [](std::int32_t *p1) -> std::int32_t *const { return p1; }; // COMPLIANT + [](std::int32_t *p1) -> const std::int32_t *const { + return p1; + }; // NON_COMPLIANT + + // Returning from reference parameters + [](std::int32_t &p1) -> std::int32_t { return p1; }; // NON_COMPLIANT + [](std::int32_t &p1) -> std::int32_t & { return p1; }; // COMPLIANT + [](std::int32_t &p1) -> const std::int32_t & { return p1; }; // NON_COMPLIANT + [](std::int32_t &p1) -> std::int32_t * { return &p1; }; // COMPLIANT + [](std::int32_t &p1) -> const std::int32_t * { return &p1; }; // NON_COMPLIANT + [](std::int32_t &p1) -> std::int32_t *const { return &p1; }; // COMPLIANT + [](std::int32_t &p1) -> const std::int32_t *const { + return &p1; + }; // NON_COMPLIANT + + // Non compliant cases are compliant when const + using cpi32 = const std::int32_t *; + using cri32 = const std::int32_t &; + [](const std::int32_t *p1) { p1 = nullptr; }; // COMPLIANT + [](const std::int32_t *p1) { i32 = *p1; }; // COMPLIANT + [](const std::int32_t *p1) { take_i32(*p1); }; // COMPLIANT + [](const std::int32_t *p1) { const int &l1 = *p1; }; // COMPLIANT + [](const std::int32_t *p1) { take_cref(*p1); }; // COMPLIANT + [](const std::int32_t &p1) { i32 = p1; }; // COMPLIANT + [](const std::int32_t &p1) { take_i32(p1); }; // COMPLIANT + [](const std::int32_t &p1) { const int &l1 = p1; }; // COMPLIANT + [](const std::int32_t &p1) { take_cref(p1); }; // COMPLIANT + [](const std::int32_t *p1) { cptr = p1; }; // COMPLIANT + [](const std::int32_t *p1) { take_cptr(p1); }; // COMPLIANT + [](cpi32 p1) { const std::int32_t *const l1 = p1; }; // COMPLIANT + [](cpi32 p1) { take_cptrc(p1); }; // COMPLIANT + [](cri32 p1) { cptr = &p1; }; // COMPLIANT + [](cri32 p1) { take_cptr(&p1); }; // COMPLIANT + [](cri32 p1) { const std::int32_t *const l1 = &p1; }; // COMPLIANT + [](cri32 p1) { take_cptrc(&p1); }; // COMPLIANT + [](cpi32 p1) -> std::int32_t { return *p1; }; // COMPLIANT + [](cpi32 p1) -> const std::int32_t & { return *p1; }; // COMPLIANT + [](cpi32 p1) -> const std::int32_t * { return p1; }; // COMPLIANT + [](cpi32 p1) -> const std::int32_t *const { return p1; }; // COMPLIANT + [](cri32 p1) -> std::int32_t { return p1; }; // COMPLIANT + [](cri32 p1) -> const std::int32_t & { return p1; }; // COMPLIANT + [](cri32 p1) -> const std::int32_t * { return &p1; }; // COMPLIANT + [](cri32 p1) -> const std::int32_t *const { return &p1; }; // COMPLIANT + + // Non pointer compliant cases are not compliant for `int32_t *const`. + using pi32c = std::int32_t *const; + + [](std::int32_t *const p1) { i32 = *p1; }; // NON_COMPLIANT + [](std::int32_t *const p1) { take_i32(*p1); }; // NON_COMPLIANT + [](std::int32_t *const p1) { const int &l1 = *p1; }; // NON_COMPLIANT + [](std::int32_t *const p1) { take_cref(*p1); }; // NON_COMPLIANT + [](std::int32_t *const p1) { cptr = p1; }; // NON_COMPLIANT + [](std::int32_t *const p1) { take_cptr(p1); }; // NON_COMPLIANT + [](pi32c p1) { const std::int32_t *const l1 = p1; }; // NON_COMPLIANT + [](pi32c p1) { take_cptrc(p1); }; // NON_COMPLIANT + [](pi32c p1) -> std::int32_t { return *p1; }; // NON_COMPLIANT + [](pi32c p1) -> const std::int32_t & { return *p1; }; // NON_COMPLIANT + [](pi32c p1) -> const std::int32_t * { return p1; }; // NON_COMPLIANT + [](pi32c p1) -> const std::int32_t *const { return p1; }; // NON_COMPLIANT +} + +void f5(std::int32_t *p1, // NON_COMPLIANT -- pointer modified + std::int32_t *p2, // COMPLIANT -- pointee modified + std::int32_t &p3, // COMPLIANT -- reference modified + std::int32_t *p4, // NON_COMPLIANT -- pointer assigned + std::int32_t *p5, // COMPLIANT -- pointee assigned + std::int32_t &p6, // COMPLIANT -- assigned + const std::int32_t *p7, // COMPLIANT + const std::int32_t &p8, // COMPLIANT + std::int32_t *p9, // NON_COMPLIANT + std::int32_t &p10, // NON_COMPLIANT + std::int32_t *p11, // COMPLIANT + std::int32_t *p12, // NON_COMPLIANT -- unused + std::int32_t **p13, // COMPLIANT + std::int32_t **p14, // COMPLIANT + std::int32_t **p15, // COMPLIANT + std::int32_t **p16 // COMPLIANT +) { + p1++; + (*p2)++; + p3++; + p4 += 0; + *p5 *= 0; + p6 /= 0; + p7++; + if (p8) { + } + p9 ? true : false; + while (p10) { + } + p11[0] = 0; + // p12 + p13[0] = p1; + p14[0][0] = 0; + (*p15)[0] = 0; + *(p16[0]) = 0; +} + +struct S { + std::int32_t m1; +}; + +void f6(S *p1, // COMPLIANT + S *p2, // NON_COMPLIANT + S *p3 // COMPLIANT +) { + p1->m1 = 1; + std::int32_t l1 = p2->m1; + std::int32_t &l2 = p3->m1; +} + +void f7(std::int32_t *p1, // COMPLIANT + std::int32_t *p2, // COMPLIANT + std::int32_t *p3, // NON_COMPLIANT[False negative] + std::int32_t *p4 // NON_COMPLIANT +) { + using pi32 = std::int32_t *; + using cpi32 = const std::int32_t *; + using ci32 = const std::int32_t; + + pi32 *l1 = &p1; // pointer to pointer to non-const + pi32 &l2 = p2; // reference to pointer to non-const + cpi32 *l3 = &p3; // pointer to pointer to const + const cpi32 &l4 = p4; // (const) reference to pointer to const + // cpi32 &l4 = p4; -- does not compile, non-const lvalues cannot involve a + // const cast from *i32 to const i32* +} + +int main(int argc, char *argv[]); // COMPLIANT - main is excluded +void f8(std::int32_t *p1, std::int32_t &p2); // COMPLIANT - no body to analyze +void f9(std::int32_t *p1, std::int32_t &p2) = delete; // COMPLIANT - deleted +void f10(void *l1) {} // COMPLIANT - void pointer excluded + +class Base { +public: + virtual void f(std::int32_t *p1, std::int32_t &p2); // COMPLIANT +}; + +class Derived : public Base { +public: + void f(std::int32_t *p1, std::int32_t &p2) override; // COMPLIANT +}; + +template struct A { + void m1(T &p1, T *p2) {} // COMPLIANT - in template scope + + void m2() { + auto lambda = [](std::int32_t &l2) { + }; // COMPLIANT - lambda in template scope + } +}; + +template void f11(T &l1) {} // COMPLIANT - function template \ No newline at end of file diff --git a/rule_packages/cpp/Declarations6.json b/rule_packages/cpp/Declarations6.json new file mode 100644 index 000000000..05d39eb33 --- /dev/null +++ b/rule_packages/cpp/Declarations6.json @@ -0,0 +1,27 @@ +{ + "MISRA-C++-2023": { + "RULE-10-1-1": { + "properties": { + "enforcement": "decidable", + "obligation": "advisory" + }, + "queries": [ + { + "description": "Pointer or lvalue reference parameters that do not modify the target object should be const-qualified to accurately reflect function behavior and prevent unintended modifications.", + "kind": "problem", + "name": "The target type of a pointer or lvalue reference parameter should be const-qualified appropriately", + "precision": "high", + "severity": "warning", + "short_name": "PointerOrRefParamNotConst", + "tags": [ + "correctness", + "readability", + "performance", + "scope/single-translation-unit" + ] + } + ], + "title": "The target type of a pointer or lvalue reference parameter should be const-qualified appropriately" + } + } +} \ No newline at end of file diff --git a/rules.csv b/rules.csv index bbbb47f38..06b97a585 100644 --- a/rules.csv +++ b/rules.csv @@ -915,7 +915,7 @@ cpp,MISRA-C++-2023,RULE-9-6-3,Yes,Required,Decidable,Single Translation Unit,The cpp,MISRA-C++-2023,RULE-9-6-4,Yes,Required,Undecidable,System,A function declared with the [[noreturn]] attribute shall not return,MSC53-CPP,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-9-6-5,Yes,Required,Decidable,Single Translation Unit,A function with non-void return type shall return a value on all paths,MSC52-CPP,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-10-0-1,Yes,Advisory,Decidable,Single Translation Unit,A declaration should not declare more than one variable or member variable,M8-0-1,ImportMisra23,Import, -cpp,MISRA-C++-2023,RULE-10-1-1,Yes,Advisory,Decidable,Single Translation Unit,The target type of a pointer or lvalue reference parameter should be const-qualified appropriately,RULE-8-13,Declarations3,Hard, +cpp,MISRA-C++-2023,RULE-10-1-1,Yes,Advisory,Decidable,Single Translation Unit,The target type of a pointer or lvalue reference parameter should be const-qualified appropriately,RULE-8-13,Declarations6,Hard, cpp,MISRA-C++-2023,RULE-10-1-2,Yes,Required,Decidable,Single Translation Unit,The volatile qualifier shall be used appropriately,,Declarations3,Easy, cpp,MISRA-C++-2023,RULE-10-2-1,Yes,Required,Decidable,Single Translation Unit,An enumeration shall be defined with an explicit underlying type,A7-2-2,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-10-2-2,Yes,Advisory,Decidable,Single Translation Unit,Unscoped enumerations should not be declared,A7-2-3,Banned2,Easy,