Skip to content

Commit 14d2f5f

Browse files
committed
QL: add a new ql/could-be-cast query
1 parent a1f4c85 commit 14d2f5f

File tree

5 files changed

+99
-0
lines changed

5 files changed

+99
-0
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import ql
2+
3+
/**
4+
* Holds if `aggr` is of one of the following forms:
5+
* `exists(var | range)` or `any(var | range)` or `any(var | | range)` or `any(type x | .. | x)`
6+
*/
7+
private predicate castAggregate(AstNode aggr, Formula range, VarDecl var, string kind) {
8+
kind = "exists" and
9+
exists(Exists ex | aggr = ex |
10+
ex.getRange() = range and
11+
not exists(ex.getFormula()) and
12+
count(ex.getArgument(_)) = 1 and
13+
ex.getArgument(0) = var
14+
)
15+
or
16+
kind = "any" and
17+
exists(Any anyy | aggr = anyy |
18+
not exists(anyy.getRange()) and
19+
anyy.getExpr(0) = range and
20+
count(anyy.getExpr(_)) = 1 and
21+
count(anyy.getArgument(_)) = 1 and
22+
anyy.getArgument(0) = var
23+
)
24+
or
25+
kind = "any" and
26+
exists(Any anyy | aggr = anyy |
27+
range = anyy.getRange() and
28+
count(anyy.getArgument(_)) = 1 and
29+
anyy.getArgument(0) = var and
30+
not exists(anyy.getExpr(0))
31+
)
32+
or
33+
kind = "any" and
34+
exists(Any anyy | aggr = anyy |
35+
range = anyy.getRange() and
36+
count(anyy.getExpr(_)) = 1 and
37+
count(anyy.getArgument(_)) = 1 and
38+
anyy.getExpr(_).(VarAccess).getDeclaration() = var and
39+
anyy.getArgument(0) = var
40+
)
41+
}
42+
43+
/** Holds if `aggr` is an aggregate that could be replaced with an instanceof expression or an inline cast. */
44+
predicate aggregateCouldBeCast(
45+
AstNode aggr, ComparisonFormula comp, string kind, VarDecl var, AstNode operand
46+
) {
47+
castAggregate(aggr, comp, var, kind) and
48+
comp.getOperator() = "=" and
49+
count(VarAccess access | access.getDeclaration() = var and access.getParent() != aggr) = 1 and
50+
comp.getAnOperand().(VarAccess).getDeclaration() = var and
51+
operand = comp.getAnOperand() and
52+
not operand.(VarAccess).getDeclaration() = var
53+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* @name Expression can be replaced with a cast
3+
* @description An exists/any that is only used to cast a value to a type
4+
* can be replaced with a cast.
5+
* @kind problem
6+
* @problem.severity warning
7+
* @id ql/could-be-cast
8+
* @precision very-high
9+
*/
10+
11+
import ql
12+
import codeql_ql.style.CouldBeCastQuery
13+
14+
from AstNode aggr, VarDecl var, string msg
15+
where
16+
exists(string kind | aggregateCouldBeCast(aggr, _, kind, var, _) |
17+
kind = "exists" and
18+
msg = "The assignment to $@ in the exists(..) can replaced with an instanceof expression."
19+
or
20+
kind = "any" and
21+
msg = "The assignment to $@ in this any(..) can be replaced with an inline cast."
22+
)
23+
select aggr, msg, var, var.getName()
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
| Foo.qll:3:3:3:24 | Exists | The assignment to $@ in the exists(..) can replaced with an instanceof expression. | Foo.qll:3:10:3:15 | j | j |
2+
| Foo.qll:7:3:7:21 | Any | The assignment to $@ in this any(..) can be replaced with an inline cast. | Foo.qll:7:7:7:12 | j | j |
3+
| Foo.qll:9:3:9:25 | Any | The assignment to $@ in this any(..) can be replaced with an inline cast. | Foo.qll:9:7:9:12 | j | j |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
queries/style/CouldBeCast.ql
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
bindingset[i]
2+
predicate foo(int i) {
3+
exists(Even j | j = i) // NOT OK
4+
or
5+
exists(Even j | j = i | j % 4 = 0) // OK
6+
or
7+
any(Even j | j = i) = 2 // NOT OK
8+
or
9+
any(Even j | j = i | j) = 2 // NOT OK
10+
or
11+
any(Even j | j = i | j * 2) = 4 // OK
12+
or
13+
any(Even j | j = i and j % 4 = 0 | j) = 4 // OK
14+
}
15+
16+
class Even extends int {
17+
bindingset[this]
18+
Even() { this % 2 = 0 }
19+
}

0 commit comments

Comments
 (0)