From 8dc773756ad3c3674adc8e15fd7ffbab88c93461 Mon Sep 17 00:00:00 2001 From: Oleh Fedorenko Date: Tue, 12 May 2026 14:23:55 +0000 Subject: [PATCH] Fix pin operator incorrectly applied to variables in pattern match body Bug: process_in processes both the pattern and the body while the SexpProcessor context stack contains :in. The __var method checks context[1] for :in or /_pat$/ and prepends ^ (pin operator) to all lvar references. This causes local variables in the body (after "then") to be incorrectly rendered with ^, producing invalid Ruby. For example, `case x; in [a, b]; a; end` was rendered as: case x in [a, b] then ^a # <-- wrong: ^ should only appear in patterns end Fix: wrap body processing in `in_context :in_body` so that __var sees :in_body instead of :in on the context stack and skips the pin prefix. Pattern variables inside the pattern itself continue to be pinned correctly since the pattern is still processed in :in context. Co-Authored-By: Claude Opus 4.6 --- lib/ruby2ruby.rb | 6 +++++- test/test_ruby2ruby.rb | 10 ++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/ruby2ruby.rb b/lib/ruby2ruby.rb index a3fb5af..d68d828 100755 --- a/lib/ruby2ruby.rb +++ b/lib/ruby2ruby.rb @@ -609,7 +609,11 @@ def process_in exp # :nodoc: _, lhs, *rhs = exp cond = process lhs - body = rhs.compact.map { |sexp| indent process sexp } + body = rhs.compact.map { |sexp| + in_context :in_body do + indent process sexp + end + } body << indent("# do nothing") if body.empty? body = body.join "\n" diff --git a/test/test_ruby2ruby.rb b/test/test_ruby2ruby.rb index 2def801..b57a14d 100644 --- a/test/test_ruby2ruby.rb +++ b/test/test_ruby2ruby.rb @@ -1002,6 +1002,16 @@ def test_case_in__hash_pat_14 assert_case_in "Object[b: 1]", s(:hash_pat, s(:const, :Object), s(:lit, :b), s(:lit, 1)) end + def test_case_in_body_does_not_pin_variables + inn = s(:case, s(:call, nil, :x), + s(:in, + s(:array_pat, nil, s(:lasgn, :a), s(:lasgn, :b)), + s(:lvar, :a)), + nil) + out = "case x\nin [a, b] then\n a\nend" + assert_parse inn, out + end + def test_interpolation_and_escapes # log_entry = " \e[#{message_color}m#{message}\e[0m " inn = s(:lasgn, :log_entry,