Skip to content

Commit 9373651

Browse files
committed
QL: add redundant-assignment query
1 parent e28f1ff commit 9373651

File tree

2 files changed

+111
-1
lines changed

2 files changed

+111
-1
lines changed

ql/ql/src/codeql/GlobalValueNumbering.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ private TValueNumber nonUniqueValueNumber(Expr e) {
237237

238238
/** Gets the value number of an expression `e`. */
239239
cached
240-
TValueNumber valueNumber(Expr e) {
240+
ValueNumber valueNumber(Expr e) {
241241
result = nonUniqueValueNumber(e)
242242
or
243243
uniqueValueNumber(e) and
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/**
2+
* @name Redundant assignment.
3+
* @description Assigning the same value twice is redundant.
4+
* @kind problem
5+
* @problem.severity warning
6+
* @precision high
7+
* @id ql/redunant-assignment
8+
* @tags maintainability
9+
*/
10+
11+
import ql
12+
13+
/**
14+
* A variable that is set equal to (assigned) a value one or more times.
15+
*/
16+
class AssignedVariable extends VarDecl {
17+
AssignedVariable() {
18+
exists(VarAccess access, ComparisonFormula comp | comp.getOperator() = "=" |
19+
access.getDeclaration() = this and
20+
comp.getAnOperand() = access
21+
)
22+
}
23+
24+
/**
25+
* Gets an expression that is assigned to this variable.
26+
*/
27+
Expr getAnAssignedExpr() {
28+
exists(VarAccess access, ComparisonFormula comp, Expr operand |
29+
comp.getOperator() = "=" and
30+
access.getDeclaration() = this and
31+
comp.getAnOperand() = access and
32+
operand = comp.getAnOperand() and
33+
not operand.(VarAccess).getDeclaration() = this
34+
|
35+
result = operand and
36+
not result instanceof Set
37+
or
38+
result = operand.(Set).getAnElement()
39+
)
40+
}
41+
}
42+
43+
import codeql.GlobalValueNumbering as GVN
44+
45+
/**
46+
* Holds if `assigned1` and `assigned2` assigns the same value to `var`.
47+
* The assignments may be on different branches of a disjunction.
48+
*/
49+
predicate candidateRedundantAssignment(AssignedVariable var, Expr assigned1, Expr assigned2) {
50+
assigned1 = var.getAnAssignedExpr() and
51+
assigned2 = var.getAnAssignedExpr() and
52+
(
53+
GVN::valueNumber(assigned1) = GVN::valueNumber(assigned2)
54+
or
55+
// because GVN skips large strings, we need to check for equality manually
56+
assigned1.(String).getValue() = assigned2.(String).getValue()
57+
) and
58+
assigned1 != assigned2
59+
}
60+
61+
/**
62+
* Gets a (transitive) parent of `p`, where the parent is not a disjunction, and `p` is a candidate assignment from `candidateRedundantAssignment`.
63+
*/
64+
AstNode getConjunctionParentRec(AstNode p) {
65+
candidateRedundantAssignment(_, p, _) and
66+
result = p
67+
or
68+
result = getConjunctionParentRec(p).getParent() and
69+
not result instanceof Disjunction and
70+
not result instanceof IfFormula and
71+
not result instanceof Implication and
72+
not result instanceof Negation and
73+
not result instanceof Predicate
74+
}
75+
76+
/**
77+
* Gets which level in the AST `p` is at.
78+
* E.g. the top-level is 0, the next level is 1, etc.
79+
*/
80+
int level(AstNode p) {
81+
p instanceof TopLevel and result = 0
82+
or
83+
result = level(p.getParent()) + 1
84+
}
85+
86+
/**
87+
* Gets the top-most parent of `p` that is not a disjunction.
88+
*/
89+
AstNode getConjunctionParent(AstNode p) {
90+
result =
91+
min(int level, AstNode parent |
92+
parent = getConjunctionParentRec(p) and level = level(parent)
93+
|
94+
parent order by level
95+
)
96+
}
97+
98+
from AssignedVariable var, Expr assigned1, Expr assigned2
99+
where
100+
candidateRedundantAssignment(var, assigned1, assigned2) and
101+
getConjunctionParent(assigned1) = getConjunctionParent(assigned2) and
102+
// de-duplcation:
103+
(
104+
assigned1.getLocation().getStartLine() < assigned2.getLocation().getStartLine()
105+
or
106+
assigned1.getLocation().getStartLine() = assigned2.getLocation().getStartLine() and
107+
assigned1.getLocation().getStartColumn() < assigned2.getLocation().getStartColumn()
108+
)
109+
select assigned2, "$@ has previously been assigned $@.", var, "The variable " + var.getName(),
110+
assigned1, "the same value"

0 commit comments

Comments
 (0)