Skip to content

Commit 3e2fb5a

Browse files
authored
Merge pull request github#105 from github/aschackmull/transitive-step
New performance query: Transitive step in recursion.
2 parents 78caab4 + ec292db commit 3e2fb5a

File tree

1 file changed

+165
-0
lines changed

1 file changed

+165
-0
lines changed
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/**
2+
* @name Transitively closed recursive delta
3+
* @description Using a transitively closed relation as the step in a recursive
4+
* delta can perform poorly as it is inherently quadratic and may
5+
* force materialization of a fastTC. The transitively closed delta
6+
* can usually just be replaced by the underlying step relation as
7+
* the recursive context will provide transitive closure.
8+
* @kind problem
9+
* @problem.severity error
10+
* @id ql/transitive-step
11+
* @tags performance
12+
* @precision high
13+
*/
14+
15+
import ql
16+
17+
Expr getArg(Call c, int i) {
18+
result = c.getArgument(i)
19+
or
20+
result = c.(MemberCall).getBase() and i = -1
21+
or
22+
exists(c.getType()) and result = c and i = -2
23+
}
24+
25+
newtype TParameter =
26+
TThisParam(ClassPredicate p) or
27+
TResultParam(Predicate p) { exists(p.getReturnType()) } or
28+
TVarParam(VarDecl v) { any(Predicate p).getParameter(_) = v }
29+
30+
class Parameter extends TParameter {
31+
string toString() {
32+
this instanceof TThisParam and result = "this"
33+
or
34+
this instanceof TResultParam and result = "result"
35+
or
36+
exists(VarDecl v | this = TVarParam(v) and result = v.toString())
37+
}
38+
39+
Expr getAnAccess() {
40+
result instanceof ThisAccess and this = TThisParam(result.getEnclosingPredicate())
41+
or
42+
result instanceof ResultAccess and this = TResultParam(result.getEnclosingPredicate())
43+
or
44+
this = TVarParam(result.(VarAccess).getDeclaration())
45+
}
46+
47+
predicate isParameterOf(Predicate p, int i) {
48+
this = TThisParam(p) and i = -1
49+
or
50+
this = TResultParam(p) and i = -2
51+
or
52+
this = TVarParam(p.getParameter(i))
53+
}
54+
}
55+
56+
predicate hasTwoArgs(Call c, Expr arg1, Expr arg2) {
57+
exists(int i1, int i2 |
58+
arg1 = getArg(c, i1) and
59+
arg2 = getArg(c, i2) and
60+
i1 != i2 and
61+
strictcount(getArg(c, _)) = 2
62+
)
63+
}
64+
65+
predicate transitivePred(Predicate p, AstNode tc) {
66+
exists(PredicateExpr pe |
67+
p.(ClasslessPredicate).getAlias() = pe and
68+
transitivePred(pe.getResolvedPredicate(), tc)
69+
)
70+
or
71+
p.(ClasslessPredicate).getAlias().(HigherOrderFormula).getName() = "fastTC" and
72+
tc = p
73+
or
74+
strictcount(Parameter par | par.isParameterOf(p, _)) = 2 and
75+
exists(Formula body | p.getBody() = body |
76+
transitiveCall(body, tc) and
77+
hasTwoArgs(body, any(Identifier i1), any(Identifier i2))
78+
or
79+
exists(ComparisonFormula eq, Call c |
80+
body = eq and
81+
eq.getSymbol() = "=" and
82+
transitiveCall(c, tc) and
83+
getArg(c, _) instanceof Identifier and
84+
eq.getAnOperand() = c and
85+
eq.getAnOperand() instanceof Identifier
86+
)
87+
)
88+
}
89+
90+
predicate transitiveCall(Call c, AstNode tc) {
91+
c.isClosure(_) and tc = c
92+
or
93+
transitivePred(c.getTarget(), tc)
94+
}
95+
96+
class TransitivelyClosedCall extends Call {
97+
TransitivelyClosedCall() { transitiveCall(this, _) }
98+
99+
predicate hasArgs(Expr arg1, Expr arg2) { hasTwoArgs(this, arg1, arg2) }
100+
101+
AstNode getReason() { transitiveCall(this, result) }
102+
}
103+
104+
AstNode getParentOfExpr(Expr e) { result = e.getParent() }
105+
106+
Formula getEnclosing(Expr e) { result = getParentOfExpr+(e) }
107+
108+
Formula enlargeScopeStep(Formula f) { result.(Conjunction).getAnOperand() = f }
109+
110+
Formula enlargeScope(Formula f) {
111+
result = enlargeScopeStep*(f) and not exists(enlargeScopeStep(result))
112+
}
113+
114+
predicate varaccesValue(VarAccess va, VarDecl v, Formula scope) {
115+
va.getDeclaration() = v and
116+
scope = enlargeScope(getEnclosing(va))
117+
}
118+
119+
predicate thisValue(ThisAccess ta, Formula scope) { scope = enlargeScope(getEnclosing(ta)) }
120+
121+
predicate resultValue(ResultAccess ra, Formula scope) { scope = enlargeScope(getEnclosing(ra)) }
122+
123+
predicate valueStep(Expr e1, Expr e2) {
124+
exists(VarDecl v, Formula scope |
125+
varaccesValue(e1, v, scope) and
126+
varaccesValue(e2, v, scope)
127+
)
128+
or
129+
exists(Formula scope |
130+
thisValue(e1, scope) and
131+
thisValue(e2, scope)
132+
or
133+
resultValue(e1, scope) and
134+
resultValue(e2, scope)
135+
)
136+
or
137+
exists(InlineCast c |
138+
e1 = c and e2 = c.getBase()
139+
or
140+
e2 = c and e1 = c.getBase()
141+
)
142+
or
143+
exists(ComparisonFormula eq |
144+
eq.getSymbol() = "=" and
145+
eq.getAnOperand() = e1 and
146+
eq.getAnOperand() = e2 and
147+
e1 != e2
148+
)
149+
}
150+
151+
predicate transitiveDelta(Call rec, TransitivelyClosedCall tc) {
152+
exists(Expr recarg, int i, Expr tcarg, Predicate pred, Parameter p |
153+
rec.getTarget() = pred and
154+
pred = rec.getEnclosingPredicate() and
155+
recarg = getArg(rec, i) and
156+
valueStep*(recarg, tcarg) and
157+
tc.hasArgs(tcarg, p.getAnAccess()) and
158+
p.isParameterOf(pred, i)
159+
)
160+
}
161+
162+
from Call rec, TransitivelyClosedCall tc, AstNode reason
163+
where transitiveDelta(rec, tc) and reason = tc.getReason()
164+
select tc, "This recursive delta is transively closed $@, which may be a performance problem.",
165+
reason, "here"

0 commit comments

Comments
 (0)