Feature #4965

The problem of "print line if line = DATA.gets"

Added by yimutang (Joey Zhou) over 5 years ago. Updated almost 5 years ago.

Target version:


Right now, I find the following code will raise an exception:

print line if line = DATA.gets

undefined local variable or method `line' for main:Object (NameError)

"print line while line = DATA.gets" also doesn't work, maybe "unless" "until" modifiers either.

It seems that the Ruby interpreter is too impatient, once seeing unassigned variable "line", it croaks.

However, the expression after "if" or "while" modifier should be evaluated first. Logically, I think the code above should work properly, there's no need to write "line = nil" before it.

Related issues

Duplicates Ruby trunk - Feature #1141: assignment of variable in "right" if statement fails Rejected 02/11/2009
Duplicated by Ruby trunk - Feature #6224: Make variables in if/unless conditions available to conditional statements Rejected 03/29/2012
Duplicated by Ruby trunk - Feature #8600: Recognize variables declared in if modifier at end of line Rejected 07/04/2013


#1 Updated by adgar (Michael Edgar) over 5 years ago

Some technical background: the current issue stems from how the parser must differentiate between local variables and method calls. Presently, local variables are created by the parser upon their introduction (assignment, rescue handlers, etc.), and they are available lexically after that in the given scope. This approach allows the parser to create the AST as each grammar rule is parsed. However, as you note, the if-guard and until-guard constructs, which are parsed after the guarded body, are executed before the guarded body. When not using begin...end with while-mod and until-mod, the condition is executed first as well.

Fixing this would involve post-processing in the parser, because the parser will discover the assignment after it has parsed reference and created the relevant AST node(s). One strategy is, when a guarded expression is parsed, to go back and examine the guarded body for local variable references that were instead considered method calls, replacing them with lvar references. It'd be a bit messy, but should work. It would break backwards compatibility, but not really in a meaningful way; it'd probably end up targeted for 2.0 though, regardless.

I'll whip up a patch implementing this today.

#2 [ruby-core:43644] Updated by mame (Yusuke Endoh) about 5 years ago

  • Status changed from Open to Assigned
  • Assignee set to matz (Yukihiro Matsumoto)

#3 [ruby-core:43648] Updated by duerst (Martin Dürst) about 5 years ago

Just for completeness, at today's developers' meeting in Akihabara, there were several people who were supportive of this proposal. Arguments given were that humans read code top-down, but not necessarily left-to-right character-by-character, and that if we would want to claim that Ruby works strictly top-down and per-line-left-to-right, then postfix if (and friends) would be impossible in the first place.

#4 [ruby-core:43926] Updated by matz (Yukihiro Matsumoto) almost 5 years ago

  • Status changed from Assigned to Rejected

The reason behind has been explained in #1141. If someone want to change the rule, he need to understand the rational first, then should try persuade me.


#5 [ruby-core:43937] Updated by rosenfeld (Rodrigo Rosenfeld Rosas) almost 5 years ago

Hi Matz, some people tried to with the argument that if you're reading a post-if/unless and they seem natural, using a variable assignment alongside with the post-if/unless would be just as natural.

Also available in: Atom PDF