diff --git a/ruby/ql/lib/change-notes/2025-05-13-captured-variables-live-more-often.md b/ruby/ql/lib/change-notes/2025-05-13-captured-variables-live-more-often.md new file mode 100644 index 000000000000..3a0878e6553c --- /dev/null +++ b/ruby/ql/lib/change-notes/2025-05-13-captured-variables-live-more-often.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Captured variables are currently considered live when the capturing function exits normally. Now they are also considered live when the capturing function exits via an exception. \ No newline at end of file diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll index 3c1da6f30138..b4ba8e3ffadd 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll @@ -106,7 +106,6 @@ private predicate writesCapturedVariable(Cfg::BasicBlock bb, LocalVariable v) { * at index `i` in exit block `bb`. */ private predicate capturedExitRead(Cfg::AnnotatedExitBasicBlock bb, int i, LocalVariable v) { - bb.isNormal() and writesCapturedVariable(bb.getAPredecessor*(), v) and i = bb.length() } diff --git a/ruby/ql/test/query-tests/variables/DeadStoreOfLocal/DeadStoreOfLocal.rb b/ruby/ql/test/query-tests/variables/DeadStoreOfLocal/DeadStoreOfLocal.rb index 7f3252a58fdc..ae40573c5c18 100644 --- a/ruby/ql/test/query-tests/variables/DeadStoreOfLocal/DeadStoreOfLocal.rb +++ b/ruby/ql/test/query-tests/variables/DeadStoreOfLocal/DeadStoreOfLocal.rb @@ -33,4 +33,34 @@ def m(y) y = 3 # OK - the call to `super` sees the value of `y`` super end +end + +def do_twice + yield + yield +end + +def get_done_twice x + do_twice do + print x + x += 1 # OK - the block is executed twice + end +end + +def retry_once + yield +rescue + yield +end + +def get_retried x + retry_once do + print x + if x < 1 + begin + x += 1 # OK - the block may be executed again + raise StandardError + end + end + end end \ No newline at end of file