Skip to content

Commit 9d81013

Browse files
committed
ruby: simplify and document
1 parent b3eaac0 commit 9d81013

File tree

1 file changed

+10
-13
lines changed

1 file changed

+10
-13
lines changed

ruby/ql/src/queries/performance/DatabaseQueryInLoop.ql

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,28 +23,26 @@ string getALoopMethodName() {
2323
]
2424
}
2525

26-
/** A call to a loop operation. */
26+
/** A loop, represented by a call to a loop operation. */
2727
class LoopingCall extends DataFlow::CallNode {
28-
DataFlow::CallableNode loopBlock;
28+
Callable loopScope;
2929

3030
LoopingCall() {
31-
this.getMethodName() = getALoopMethodName() and loopBlock = this.getBlock().asCallable()
31+
this.getMethodName() = getALoopMethodName() and
32+
loopScope = this.getBlock().asCallable().asCallableAstNode()
3233
}
3334

34-
DataFlow::CallableNode getLoopBlock() { result = loopBlock }
35+
/** Holds if `c` is executed as part of the body of this loop. */
36+
predicate executesCall(DataFlow::CallNode c) { c.asExpr().getScope() = loopScope }
3537
}
3638

37-
predicate happensInLoop(LoopingCall loop, DataFlow::CallNode e) {
38-
loop.getLoopBlock().asCallableAstNode() = e.asExpr().getScope()
39-
}
40-
41-
// The ActiveRecord instance is used to potentially control the loop
39+
/** Holds if `ar` influences `guard`, which may control the execution of a loop. */
4240
predicate usedInLoopControlGuard(ActiveRecordInstance ar, DataFlow::Node guard) {
4341
TaintTracking::localTaint(ar, guard) and
4442
guard = guardForLoopControl(_, _)
4543
}
4644

47-
// A guard for controlling the loop
45+
/** Gets a dataflow node that is used to decide whether to break a loop. */
4846
DataFlow::Node guardForLoopControl(ConditionalExpr cond, Stmt control) {
4947
result.asExpr().getAstNode() = cond.getCondition().getAChild*() and
5048
(
@@ -55,15 +53,14 @@ DataFlow::Node guardForLoopControl(ConditionalExpr cond, Stmt control) {
5553
control = cond.getBranch(_).getAChild()
5654
}
5755

58-
from LoopingCall loop, DataFlow::CallNode call
56+
from LoopingCall loop, ActiveRecordModelFinderCall call
5957
where
58+
loop.executesCall(call) and
6059
// Disregard loops over constants
6160
not isArrayConstant(loop.getReceiver().asExpr(), _) and
6261
// Disregard cases where the looping is influenced by the query result
6362
not usedInLoopControlGuard(call, _) and
64-
happensInLoop(loop, call) and
6563
// Only report calls that are likely to be expensive
66-
call instanceof ActiveRecordModelFinderCall and
6764
not call.getMethodName() in ["new", "create"]
6865
select call,
6966
"This call to a database query operation happens inside $@, and could be hoisted to a single call outside the loop.",

0 commit comments

Comments
 (0)