[ruby-core:122878] [Ruby Bug#21139] Prism and parse.y parses `it = it` differently
From:
"jeremyevans0 (Jeremy Evans) via ruby-core" <ruby-core@...>
Date:
2025-07-28 20:21:13 UTC
List:
ruby-core #122878
Issue #21139 has been updated by jeremyevans0 (Jeremy Evans).
vinistock (Vinicius Stock) wrote in #note-18:
> Thanks for the context, I understand your point about consistency. So this case
>
> ```ruby
> 42.tap { |x| x = x; p x }
> ```
>
> Works because by the time we reach `x = x`, the block parameter already declared `x` as a local and so it doesn't end up setting it equal to `nil`. Would it make sense to automatically declare `it` as a local inside the block?
>
> Or another question, do we agree that these two should return the same result?
>
> ```ruby
> 42.tap { it = it; p it }
> 42.tap { |x| x = x; p x }
> ```
No. `it` is not a local variable until it is referenced, so the first example should always print `nil` (unless `it` was already a local variable in higher scope). Everywhere else in Ruby:
```ruby
lvar = lvar
```
Will result in `lvar` being nil if was not already a local variable in scope.
To think that `42.tap { it = it; p it }` should print 42 is to believe that `it` is a local variable before it is referenced. However, if `it` is never referenced, then `it` is never a local variable, and the block does not accept an argument:
```ruby
proc{}.arity # => 0
proc{it}.arity # => 1
```
You could argue that `it` is a local variable from the start of the block instead of where `it` is first referenced in the block. However, that's not how local variables work in general. Every other place in Ruby where a local variable is declared (referenced in the LHS of an assignment), it is only considered declared after the reference, not before the reference:
```
def x = 1
def y
x # => 1
x = x
x # => nil
end
```
If `it` should be considered a local variable from the start of the block if it is referenced anywhere in the block, then in the above example, the first reference to `x` inside `y` should be a local variable reference instead of a method call, even though the `x` local variable is not declared until afterward.
In general, you shouldn't consider `it` to be a local variable. If `it` was a local variable, this would work:
```ruby
proc{eval("it")}.call(1) # NameError
```
But it doesn't work, even if `it` is already referenced:
```ruby
proc{it; eval("it")}.call(1) # NameError
```
If at the point of reference, `it` is already a local variable, Ruby treats it as it would any local variable. If `it` is not already a local variable, then Ruby treats it as the implicit block parameter. Inside a block, for `it = it`, the RHS `it` was previously declared as a local variable by the LHS, so it is treated as a local variable instead of as the implicit block parameter.
AMomchilov (Alexander Momchilov) wrote in #note-19:
> @jeremyevans0 I see your point about how ivars take precedence over methods of the same name, but I think `it` is more like a block argument (well, a local variable) than a method, so I find this inconsistently really surprising:
>
> ```
> ruby --parser=parse.y -e "42.tap { it = it; p it }" # => nil
> ruby --parser=parse.y -e "42.tap { |it| it = it; p it }" # => 42
> ```
>
> Since `it` is just a shorthand for "the first block argument," then I think people would expect that it works _as if_ you declared `|it|` yourself.
They may. However, doing so is inconsistent with how local variables are designed in Ruby. Personally, I think consistency is much more important in this case. I cannot think of a good reason to define an `it` local variable while also using `it` as the implicit block parameter. If you are only using `it` in the same block scope, no local variable is necessary. If you are setting `it` to a local variable so it is usable inside a nested block, you should pick a different variable name, as using `it` is more likely to cause confusion.
----------------------------------------
Bug #21139: Prism and parse.y parses `it = it` differently
https://bugs.ruby-lang.org/issues/21139#change-114181
* Author: tompng (tomoya ishida)
* Status: Feedback
* Assignee: prism
* ruby -v: ruby 3.5.0dev (2025-02-14T16:49:52Z master ee181d1bb7) +PRISM [x86_64-linux]
* Backport: 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN
----------------------------------------
~~~
# ruby --parser=parse.y -e "42.tap { it = it; p it }"
nil
# ruby --parser=prism -e "42.tap { it = it; p it }"
42
~~~
---Files--------------------------------
clipboard-202503081702-idzz2.png (22.6 KB)
--
https://bugs.ruby-lang.org/
______________________________________________
ruby-core mailing list -- ruby-core@ml.ruby-lang.org
To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/