|
| 1 | +/** |
| 2 | + * @id cpp/misra/pointer-or-ref-param-not-const |
| 3 | + * @name RULE-10-1-1: The target type of a pointer or lvalue reference parameter should be const-qualified appropriately |
| 4 | + * @description Pointer or lvalue reference parameters that do not modify the target object should |
| 5 | + * be const-qualified to accurately reflect function behavior and prevent unintended |
| 6 | + * modifications. |
| 7 | + * @kind problem |
| 8 | + * @precision high |
| 9 | + * @problem.severity warning |
| 10 | + * @tags external/misra/id/rule-10-1-1 |
| 11 | + * correctness |
| 12 | + * readability |
| 13 | + * performance |
| 14 | + * scope/single-translation-unit |
| 15 | + * external/misra/enforcement/decidable |
| 16 | + * external/misra/obligation/advisory |
| 17 | + */ |
| 18 | + |
| 19 | +import cpp |
| 20 | +import codingstandards.cpp.misra |
| 21 | +import codingstandards.cpp.types.Pointers |
| 22 | +import codingstandards.cpp.Call |
| 23 | +import codingstandards.cpp.SideEffect |
| 24 | + |
| 25 | +/** |
| 26 | + * Holds if `f` is a `Function` in a template scope and should be excluded. |
| 27 | + */ |
| 28 | +predicate isInTemplateScope(Function f) { |
| 29 | + f.isFromTemplateInstantiation(_) |
| 30 | + or |
| 31 | + f.isFromUninstantiatedTemplate(_) |
| 32 | +} |
| 33 | + |
| 34 | +/** |
| 35 | + * A `Parameter` whose type is a `PointerLikeType` such as a pointer or reference. |
| 36 | + */ |
| 37 | +class PointerLikeParam extends Parameter { |
| 38 | + PointerLikeType pointerLikeType; |
| 39 | + |
| 40 | + PointerLikeParam() { pointerLikeType = this.getType() } |
| 41 | + |
| 42 | + /** |
| 43 | + * Gets the pointer like type of this parameter. |
| 44 | + */ |
| 45 | + PointerLikeType getPointerLikeType() { result = pointerLikeType } |
| 46 | + |
| 47 | + /** |
| 48 | + * Gets usages of this parameter that maintain pointer-like semantics -- typically this means |
| 49 | + * either a normal access, or switching between pointers and reference semantics. |
| 50 | + * |
| 51 | + * Examples of accesses with pointer-like semantics include: |
| 52 | + * - `ref` in `int &x = ref`, or `&ref` in `int *x = &ref`; |
| 53 | + * - `ptr` in `int *x = ptr`, or `*ptr` in `int &x = *ptr`; |
| 54 | + * |
| 55 | + * In the above examples, we can still access the value pointed to by `ref` or `ptr` through the |
| 56 | + * expression. |
| 57 | + * |
| 58 | + * Examples of non-pointer-like semantics include: |
| 59 | + * - `ref` in `int x = ref` and `*ptr` in `int x = *ptr`; |
| 60 | + * |
| 61 | + * In the above examples, the value pointed to by `ref` or `ptr` is copied and the expression |
| 62 | + * refers to a new/different object. |
| 63 | + */ |
| 64 | + Expr getAPointerLikeAccess() { result = getAPointerLikeAccessOf(this.getAnAccess()) } |
| 65 | +} |
| 66 | + |
| 67 | +/** |
| 68 | + * A candidate parameter that could have its target type const-qualified. |
| 69 | + */ |
| 70 | +class NonConstParam extends PointerLikeParam { |
| 71 | + NonConstParam() { |
| 72 | + not pointerLikeType.pointsToConst() and |
| 73 | + // Ignore parameters in functions without bodies |
| 74 | + exists(this.getFunction().getBlock()) and |
| 75 | + // Ignore unnamed parameters |
| 76 | + this.isNamed() and |
| 77 | + // Ignore functions that use ASM statements |
| 78 | + not exists(AsmStmt a | a.getEnclosingFunction() = this.getFunction()) and |
| 79 | + // Must have a pointer, array, or lvalue reference type with non-const target |
| 80 | + // Exclude pointers to non-object types |
| 81 | + not pointerLikeType.getInnerType+().getUnderlyingType() instanceof RoutineType and |
| 82 | + not pointerLikeType.getInnerType+().getUnderlyingType() instanceof VoidType and |
| 83 | + // Exclude virtual functions |
| 84 | + not this.getFunction().isVirtual() and |
| 85 | + // Exclude functions in template scope |
| 86 | + not isInTemplateScope(this.getFunction()) and |
| 87 | + // Exclude main |
| 88 | + not this.getFunction().hasGlobalName("main") and |
| 89 | + // Exclude deleted functions |
| 90 | + not this.getFunction().isDeleted() and |
| 91 | + // Exclude any parameter whose underlying data is modified |
| 92 | + not exists(AliasParameter alias | alias = this | alias.isModified()) and |
| 93 | + // Exclude parameters passed as arguments to non-const pointer/ref params |
| 94 | + not exists(CallArgumentExpr arg | |
| 95 | + arg = this.getAPointerLikeAccess() and |
| 96 | + arg.getParamType().(PointerLikeType).pointsToNonConst() |
| 97 | + ) and |
| 98 | + // Exclude parameters used as qualifier for a non-const member function |
| 99 | + not exists(FunctionCall fc | |
| 100 | + fc.getQualifier() = [this.getAnAccess(), this.getAPointerLikeAccess()] and |
| 101 | + not fc.getTarget().hasSpecifier("const") and |
| 102 | + not fc.getTarget().isStatic() |
| 103 | + ) and |
| 104 | + // Exclude parameters assigned to a non-const pointer/reference alias |
| 105 | + not exists(Variable v | |
| 106 | + v.getAnAssignedValue() = this.getAPointerLikeAccess() and |
| 107 | + v.getType().(PointerLikeType).pointsToNonConst() |
| 108 | + ) and |
| 109 | + // Exclude parameters returned as non-const pointer/reference |
| 110 | + not exists(ReturnStmt ret | |
| 111 | + ret.getExpr() = this.getAPointerLikeAccess() and |
| 112 | + ret.getEnclosingFunction().getType().(PointerLikeType).pointsToNonConst() |
| 113 | + ) and |
| 114 | + not exists(FieldAccess fa | |
| 115 | + fa.getQualifier() = [this.getAPointerLikeAccess(), this.getAnAccess()] and |
| 116 | + fa.isLValueCategory() |
| 117 | + ) and |
| 118 | + not exists(AddressOfExpr addrOf | |
| 119 | + // exclude pointer to pointer and reference to pointer cases. |
| 120 | + addrOf.getOperand() = this.getAPointerLikeAccess() and |
| 121 | + addrOf.getType().(PointerLikeType).getInnerType() instanceof PointerLikeType |
| 122 | + ) and |
| 123 | + not exists(PointerArithmeticOperation pointerManip | |
| 124 | + pointerManip.getAnOperand() = this.getAPointerLikeAccess() and |
| 125 | + pointerManip.getType().(PointerLikeType).pointsToNonConst() |
| 126 | + ) |
| 127 | + } |
| 128 | +} |
| 129 | + |
| 130 | +from NonConstParam param, Type innerType |
| 131 | +where |
| 132 | + not isExcluded(param, Declarations6Package::pointerOrRefParamNotConstQuery()) and |
| 133 | + innerType = param.getPointerLikeType().getInnerType() and |
| 134 | + not param.isAffectedByMacro() and |
| 135 | + // There are some odd database patterns where a function has multiple parameters with the same |
| 136 | + // index and different names, due to strange extraction+linker scenarios. These give wrong |
| 137 | + // results, and should be excluded. |
| 138 | + count(Parameter p | |
| 139 | + p.getFunction() = param.getFunction() and |
| 140 | + p.getIndex() = param.getIndex() |
| 141 | + ) = 1 |
| 142 | +select param, |
| 143 | + "Parameter '" + param.getName() + "' points/refers to a non-const type '" + innerType.toString() + |
| 144 | + "' but does not modify the target object in the $@.", param.getFunction().getDefinition(), |
| 145 | + "function definition" |
0 commit comments