Skip to content

Commit d933bf6

Browse files
committed
QL: Add ql/use-set-literal query.
1 parent 4b73c99 commit d933bf6

1 file changed

Lines changed: 89 additions & 0 deletions

File tree

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/**
2+
* @name Use a set literal in place of `or`
3+
* @description A chain of `or`s can be replaced with a set literal, improving readability.
4+
* @kind problem
5+
* @problem.severity recommendation
6+
* @id ql/use-set-literal
7+
* @tags maintainability
8+
* @precision high
9+
*/
10+
11+
import ql
12+
import codeql_ql.ast.internal.Predicate // TODO: for PredicateOrBuiltin
13+
14+
/**
15+
* A chain of disjunctions treated as one object. For example the following is
16+
* a chain of disjunctions with three operands:
17+
* ```
18+
* a or b or c
19+
* ```
20+
*/
21+
class DisjunctionChain extends Disjunction {
22+
DisjunctionChain() { not exists(Disjunction parent | parent.getAnOperand() = this) }
23+
24+
/**
25+
* Gets any operand of the chain.
26+
*/
27+
Formula getAnOperandRec() {
28+
result = getAnOperand*() and
29+
not result instanceof Disjunction
30+
}
31+
}
32+
33+
/**
34+
* An equality comparison with a `Literal`. For example:
35+
* ```
36+
* x = 4
37+
* ```
38+
*/
39+
class EqualsLiteral extends ComparisonFormula {
40+
EqualsLiteral() {
41+
getSymbol() = "=" and
42+
getAnOperand() instanceof Literal
43+
}
44+
}
45+
46+
/**
47+
* A chain of disjunctions where each operand is an equality comparison between
48+
* the same thing and various `Literal`s. For example:
49+
* ```
50+
* x = 4 or
51+
* x = 5 or
52+
* x = 6
53+
* ```
54+
*/
55+
class DisjunctionEqualsLiteral extends DisjunctionChain {
56+
DisjunctionEqualsLiteral() {
57+
// VarAccess on the same variable
58+
exists(VarDef v |
59+
forex(Formula f | f = getAnOperandRec() |
60+
f.(EqualsLiteral).getAnOperand().(VarAccess).getDeclaration() = v
61+
)
62+
)
63+
or
64+
// FieldAccess on the same variable
65+
exists(VarDecl v |
66+
forex(Formula f | f = getAnOperandRec() |
67+
f.(EqualsLiteral).getAnOperand().(FieldAccess).getDeclaration() = v
68+
)
69+
)
70+
or
71+
// ThisAccess
72+
forex(Formula f | f = getAnOperandRec() |
73+
f.(EqualsLiteral).getAnOperand() instanceof ThisAccess
74+
)
75+
or
76+
// ResultAccess
77+
forex(Formula f | f = getAnOperandRec() |
78+
f.(EqualsLiteral).getAnOperand() instanceof ResultAccess
79+
)
80+
// (in principle something like GlobalValueNumbering could be used to generalize this)
81+
}
82+
}
83+
84+
from DisjunctionChain d, int c
85+
where
86+
d instanceof DisjunctionEqualsLiteral and
87+
c = count(d.getAnOperandRec()) and
88+
c >= 4
89+
select d, "This formula can be replaced with equality on a set literal, improving readability."

0 commit comments

Comments
 (0)