You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
elsif node incorrectly included inside IfNode#statements instead of represented as nested IfNode in consequent #3946
When parsing an if / elsif / else construct using Prism (Rust bindings),
the elsif branch appears to be included inside the parent IfNode#statements
instead of being represented as a nested IfNode in the consequent field.
This causes structural ambiguity in the AST and makes it impossible to
distinguish between:
a normal statement inside the if body
an elsif branch
From the Ruby grammar perspective, elsif should be modeled as a nested IfNode inside the consequent of the previous IfNode.
However, what I observe is that the elsif appears inside the statements
list of the parent IfNode.
Example
Input
ifafooelsifbbarelsebazend
Expected AST Structure (Simplified)
IfNode
predicate: a
statements:
- foo
consequent:
IfNode
predicate: b
statements:
- bar
consequent:
Statements:
- baz
Actual Behavior
The elsif branch appears inside the parent IfNode#statements
instead of being represented as a nested IfNode in consequent.
This makes elsif indistinguishable from a regular statement
when traversing statements.
Hi @Earlopain , I understand that consequent has been removed. However, from my understanding, subsequent is meant to represent the else branch, and it doesn’t seem to fully address the elsif case. Perhaps it might make sense to keep both.
For consequent vs subsequent, there is not much difference between them, they both represent the same thing. They were just renamed to be more descriptive. It contains either IfNode or ElseNode. If it is IfNode, it must be elsif since that is the only syntax-valid option.
In the OP, you're saying that the statements contain the elsif branch. I was pointing out your use of consequent because it tells me that you might be using a somewhat old version. It's entirely possible that it misparses that code. This works as expected for me:
Yeah, that is being interpreted as a plain method call instead so it makes sense that it becomes part of the body. You also need to call the base implementation, as otherwise it will not visit the nested child nodes.
In your specific code example the numbers were switched so it only executed the else branch => you didn't get a runtime error. I switched it above to demonstrate only.
So, ruby is too flexible. I've been exploring to develop a formatting tool with prism's api recently. What do you think of this idea? To be honest, I knew very little about ruby before.
Sorry, I know very little about that yet. It's something I want to explore but for now I haven't spend much time one it.
You may check out ruby-syntax-tree/syntax_tree#543. It's in ruby but the overall idea should cary over to rust. lib/syntax_tree/format.rb in particular might be of interest to you.
Activity
Earlopain commentedon Feb 27, 2026
Hi there, are you sure you're using 1.9.0?
consequentwas deprecated and renamed in the bindings more than a year ago, the name issubsequentnow.tu6ge commentedon Feb 27, 2026
Hi @Earlopain , I understand that
consequenthas been removed. However, from my understanding,subsequentis meant to represent the else branch, and it doesn’t seem to fully address theelsifcase. Perhaps it might make sense to keep both.Earlopain commentedon Feb 27, 2026
For
consequentvssubsequent, there is not much difference between them, they both represent the same thing. They were just renamed to be more descriptive. It contains eitherIfNodeorElseNode. If it isIfNode, it must beelsifsince that is the only syntax-valid option.In the OP, you're saying that the
statementscontain theelsifbranch. I was pointing out your use ofconsequentbecause it tells me that you might be using a somewhat old version. It's entirely possible that it misparses that code. This works as expected for me:tu6ge commentedon Feb 27, 2026
Hmm, I ran your test case locally and it does pass. But why does the output I print include elseif?
print content:
tu6ge commentedon Feb 27, 2026
I think I know the reason. I wrote the wrong keyword. elseif elsif
Earlopain commentedon Feb 27, 2026
Yeah, that is being interpreted as a plain method call instead so it makes sense that it becomes part of the body. You also need to call the base implementation, as otherwise it will not visit the nested child nodes.
I'm not familiar with rust, but it should be something along those lines. That should give you the output you are expecting.
tu6ge commentedon Feb 27, 2026
Yes, strangely enough, I put this wrong code in ruby's playground and ran it without an error.
Earlopain commentedon Feb 27, 2026
elseifbecomes a method callSyntactically there's nothing wrong with this code but obviously not quite the intended outcome.
tu6ge commentedon Feb 27, 2026
But I not defined elseif function
Earlopain commentedon Feb 27, 2026
In your specific code example the numbers were switched so it only executed the
elsebranch => you didn't get a runtime error. I switched it above to demonstrate only.tu6ge commentedon Feb 27, 2026
So, ruby is too flexible. I've been exploring to develop a formatting tool with prism's api recently. What do you think of this idea? To be honest, I knew very little about ruby before.
Earlopain commentedon Feb 27, 2026
Sorry, I know very little about that yet. It's something I want to explore but for now I haven't spend much time one it.
You may check out ruby-syntax-tree/syntax_tree#543. It's in ruby but the overall idea should cary over to rust.
lib/syntax_tree/format.rbin particular might be of interest to you.tu6ge commentedon Feb 28, 2026
Thank you. Let me have a look