Skip to content

Commit 3b4206c

Browse files
authored
Merge pull request #8517 from hmac/hmac/lambda-captured-var
Ruby: fix bug with captured variable reads in lambdas
2 parents 61c9442 + 99b5c58 commit 3b4206c

File tree

5 files changed

+56
-2
lines changed

5 files changed

+56
-2
lines changed

ruby/ql/lib/codeql/ruby/ast/internal/Variable.qll

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,11 @@ import Cached
406406

407407
/** Holds if this scope inherits `name` from an outer scope `outer`. */
408408
private predicate inherits(Scope::Range scope, string name, Scope::Range outer) {
409-
(scope instanceof Ruby::Block or scope instanceof Ruby::DoBlock) and
409+
(
410+
scope instanceof Ruby::Block or
411+
scope instanceof Ruby::DoBlock or
412+
scope instanceof Ruby::Lambda
413+
) and
410414
not scopeDefinesParameterVariable(scope, name, _) and
411415
(
412416
outer = scope.getOuterScope() and

ruby/ql/test/library-tests/ast/Ast.expected

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,39 @@ calls/calls.rb:
706706
# 347| getKey: [SymbolLiteral] :X
707707
# 347| getComponent: [StringTextComponent] X
708708
# 347| getValue: [ConstantReadAccess] X
709+
# 350| getStmt: [AssignExpr] ... = ...
710+
# 350| getAnOperand/getLeftOperand: [LocalVariableAccess] y
711+
# 350| getAnOperand/getRightOperand: [IntegerLiteral] 1
712+
# 351| getStmt: [AssignExpr] ... = ...
713+
# 351| getAnOperand/getLeftOperand: [LocalVariableAccess] id
714+
# 351| getAnOperand/getRightOperand: [Lambda] -> { ... }
715+
# 351| getParameter: [SimpleParameter] x
716+
# 351| getDefiningAccess: [LocalVariableAccess] x
717+
# 351| getStmt: [LocalVariableAccess] y
718+
# 352| getStmt: [AssignExpr] ... = ...
719+
# 352| getAnOperand/getLeftOperand: [LocalVariableAccess] f
720+
# 352| getAnOperand/getRightOperand: [Lambda] -> { ... }
721+
# 352| getParameter: [SimpleParameter] x
722+
# 352| getDefiningAccess: [LocalVariableAccess] x
723+
# 352| getStmt: [MethodCall] call to foo
724+
# 352| getReceiver: [SelfVariableAccess] self
725+
# 352| getArgument: [LocalVariableAccess] x
726+
# 353| getStmt: [AssignExpr] ... = ...
727+
# 353| getAnOperand/getLeftOperand: [LocalVariableAccess] g
728+
# 353| getAnOperand/getRightOperand: [Lambda] -> { ... }
729+
# 353| getParameter: [SimpleParameter] x
730+
# 353| getDefiningAccess: [LocalVariableAccess] x
731+
# 353| getStmt: [MethodCall] call to unknown_call
732+
# 353| getReceiver: [SelfVariableAccess] self
733+
# 354| getStmt: [AssignExpr] ... = ...
734+
# 354| getAnOperand/getLeftOperand: [LocalVariableAccess] h
735+
# 354| getAnOperand/getRightOperand: [Lambda] -> { ... }
736+
# 354| getParameter: [SimpleParameter] x
737+
# 354| getDefiningAccess: [LocalVariableAccess] x
738+
# 355| getStmt: [LocalVariableAccess] x
739+
# 356| getStmt: [LocalVariableAccess] y
740+
# 357| getStmt: [MethodCall] call to unknown_call
741+
# 357| getReceiver: [SelfVariableAccess] self
709742
control/cases.rb:
710743
# 1| [Toplevel] cases.rb
711744
# 2| getStmt: [AssignExpr] ... = ...

ruby/ql/test/library-tests/ast/ValueText.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ exprValue
7373
| calls/calls.rb:346:5:346:5 | :X | :X |
7474
| calls/calls.rb:346:8:346:9 | 42 | 42 |
7575
| calls/calls.rb:347:5:347:5 | :X | :X |
76+
| calls/calls.rb:350:5:350:5 | 1 | 1 |
7677
| constants/constants.rb:3:19:3:27 | "const_a" | const_a |
7778
| constants/constants.rb:6:15:6:23 | "const_b" | const_b |
7879
| constants/constants.rb:17:12:17:18 | "Hello" | Hello |
@@ -927,6 +928,7 @@ exprCfgNodeValue
927928
| calls/calls.rb:346:5:346:5 | :X | :X |
928929
| calls/calls.rb:346:8:346:9 | 42 | 42 |
929930
| calls/calls.rb:347:5:347:5 | :X | :X |
931+
| calls/calls.rb:350:5:350:5 | 1 | 1 |
930932
| constants/constants.rb:3:19:3:27 | "const_a" | const_a |
931933
| constants/constants.rb:6:15:6:23 | "const_b" | const_b |
932934
| constants/constants.rb:17:12:17:18 | "Hello" | Hello |

ruby/ql/test/library-tests/ast/calls/calls.expected

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ callsWithArguments
113113
| calls.rb:345:1:345:15 | call to foo | foo | 1 | calls.rb:345:9:345:14 | Pair |
114114
| calls.rb:346:1:346:10 | call to foo | foo | 0 | calls.rb:346:5:346:9 | Pair |
115115
| calls.rb:347:1:347:7 | call to foo | foo | 0 | calls.rb:347:5:347:6 | Pair |
116+
| calls.rb:352:13:352:17 | call to foo | foo | 0 | calls.rb:352:17:352:17 | x |
116117
callsWithReceiver
117118
| calls.rb:2:1:2:5 | call to foo | calls.rb:2:1:2:5 | self |
118119
| calls.rb:5:1:5:10 | call to bar | calls.rb:5:1:5:3 | Foo |
@@ -368,6 +369,9 @@ callsWithReceiver
368369
| calls.rb:345:1:345:15 | call to foo | calls.rb:345:1:345:15 | self |
369370
| calls.rb:346:1:346:10 | call to foo | calls.rb:346:1:346:10 | self |
370371
| calls.rb:347:1:347:7 | call to foo | calls.rb:347:1:347:7 | self |
372+
| calls.rb:352:13:352:17 | call to foo | calls.rb:352:13:352:17 | self |
373+
| calls.rb:353:13:353:24 | call to unknown_call | calls.rb:353:13:353:24 | self |
374+
| calls.rb:357:3:357:14 | call to unknown_call | calls.rb:357:3:357:14 | self |
371375
callsWithBlock
372376
| calls.rb:17:1:17:17 | call to foo | calls.rb:17:5:17:17 | { ... } |
373377
| calls.rb:20:1:22:3 | call to foo | calls.rb:20:5:22:3 | do ... end |

ruby/ql/test/library-tests/ast/calls/calls.rb

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,4 +344,15 @@ def foo(a, b, ...)
344344
foo(x: 42)
345345
foo(x:, novar:)
346346
foo(X: 42)
347-
foo(X:)
347+
foo(X:)
348+
349+
# calls inside lambdas
350+
y = 1
351+
id = ->(x) { y }
352+
f = ->(x) { foo x }
353+
g = ->(x) { unknown_call }
354+
h = -> (x) do
355+
x
356+
y
357+
unknown_call
358+
end

0 commit comments

Comments
 (0)