forked from github/codeql-coding-standards
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathVariableMissingConstexpr.ql
More file actions
137 lines (129 loc) · 4.76 KB
/
VariableMissingConstexpr.ql
File metadata and controls
137 lines (129 loc) · 4.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/**
* @id cpp/autosar/variable-missing-constexpr
* @name A7-1-2: The constexpr specifier shall be used for variables that can be determined at compile time
* @description Using 'constexpr' makes it clear that a variable is intended to be compile time
* constant.
* @kind problem
* @precision high
* @problem.severity recommendation
* @tags external/autosar/id/a7-1-2
* maintainability
* external/autosar/allocated-target/implementation
* external/autosar/enforcement/automated
* external/autosar/obligation/required
*/
import cpp
import codingstandards.cpp.autosar
import codingstandards.cpp.TrivialType
import codingstandards.cpp.SideEffect
import semmle.code.cpp.controlflow.SSA
predicate isZeroInitializable(Variable v) {
not exists(v.getInitializer().getExpr()) and
(
v instanceof StaticStorageDurationVariable
or
isTypeZeroInitializable(v.getType())
)
}
predicate isTypeZeroInitializable(Type t) {
t.getUnspecifiedType() instanceof Class
or
t.getUnderlyingType() instanceof ArrayType
}
/**
* An optimized set of expressions used to determine the flow through constexpr variables.
*/
class VariableAccessOrCallOrLiteral extends Expr {
VariableAccessOrCallOrLiteral() {
this instanceof VariableAccess or
this instanceof Call or
this instanceof Literal
}
}
/**
* Holds if the value of source flows through compile time evaluated variables to target.
*/
predicate flowsThroughConstExprVariables(
VariableAccessOrCallOrLiteral source, VariableAccessOrCallOrLiteral target
) {
(
source = target
or
source != target and
exists(SsaDefinition intermediateDef, StackVariable intermediate |
intermediateDef.getAVariable().getFunction() = source.getEnclosingFunction() and
intermediateDef.getAVariable().getFunction() = target.getEnclosingFunction() and
intermediateDef.getAVariable() = intermediate and
intermediate.isConstexpr()
|
DataFlow::localExprFlow(source, intermediateDef.getDefiningValue(intermediate)) and
flowsThroughConstExprVariables(intermediateDef.getAUse(intermediate), target)
)
)
}
/*
* Returns true if the given call may be evaluated at compile time and is compile time evaluated because
* all its arguments are compile time evaluated and its default values are compile time evaluated.
*/
predicate isCompileTimeEvaluated(Call call) {
// 1. The call may be evaluated at compile time, because it is constexpr, and
call.getTarget().isConstexpr() and
// 2. all its arguments are compile time evaluated, and
forall(DataFlow::Node ultimateArgSource, DataFlow::Node argSource |
argSource = DataFlow::exprNode(call.getAnArgument()) and
DataFlow::localFlow(ultimateArgSource, argSource) and
not DataFlow::localFlowStep(_, ultimateArgSource)
|
(
ultimateArgSource.asExpr() instanceof Literal
or
any(Call c | isCompileTimeEvaluated(c)) = ultimateArgSource.asExpr()
) and
// If the ultimate argument source is not the same as the argument source, then it must flow through
// constexpr variables.
(
ultimateArgSource != argSource
implies
flowsThroughConstExprVariables(ultimateArgSource.asExpr(), argSource.asExpr())
)
) and
// 3. all the default values used are compile time evaluated.
forall(Expr defaultValue, Parameter parameterUsingDefaultValue, int idx |
parameterUsingDefaultValue = call.getTarget().getParameter(idx) and
not exists(call.getArgument(idx)) and
parameterUsingDefaultValue.getAnAssignedValue() = defaultValue
|
defaultValue instanceof Literal
or
any(Call c | isCompileTimeEvaluated(c)) = defaultValue
)
}
from Variable v
where
not isExcluded(v, ConstPackage::variableMissingConstexprQuery()) and
v.hasDefinition() and
not v.isConstexpr() and
not v instanceof Parameter and
not v.isAffectedByMacro() and
isLiteralType(v.getType()) and
// Unfortunately, `isConstant` is not sufficient here because it doesn't include calls to
// constexpr constructors, and does not take into account zero initialization
(
v.getInitializer().getExpr().isConstant()
or
any(Call call | isCompileTimeEvaluated(call)) = v.getInitializer().getExpr()
or
isZeroInitializable(v)
or
// Value initialized
v.getInitializer().getExpr().(AggregateLiteral).getNumChild() = 0
) and
// Not modified directly
not exists(VariableEffect e | e.getTarget() = v) and
// No address taken in a non-const way
not v.getAnAccess().isAddressOfAccessNonConst() and
// Not assigned by a user in a constructor
not exists(ConstructorFieldInit cfi | cfi.getTarget() = v and not cfi.isCompilerGenerated()) and
// Ignore union members
not v.getDeclaringType() instanceof Union
select v, "Variable " + v.getName() + " could be marked 'constexpr'."