Skip to content

Commit 4c95a2e

Browse files
committed
Combine Elsif node into IfNode
Many other Ruby ASTs do not have a separate node representing `elsif` conditionals; instead, they use the same node as `if` but make it a consequent of other `if` nodes. This commit removes the `Elsif` node and combines its functionality with `IfNode`, ensuring that both the parsing and formatting logic are still functional.
1 parent e0be579 commit 4c95a2e

File tree

3 files changed

+31
-107
lines changed

3 files changed

+31
-107
lines changed

lib/syntax_tree/node.rb

+22-101
Original file line numberDiff line numberDiff line change
@@ -4795,95 +4795,6 @@ def ===(other)
47954795
end
47964796
end
47974797

4798-
# Elsif represents another clause in an +if+ or +unless+ chain.
4799-
#
4800-
# if variable
4801-
# elsif other_variable
4802-
# end
4803-
#
4804-
class Elsif < Node
4805-
# [Node] the expression to be checked
4806-
attr_reader :predicate
4807-
4808-
# [Statements] the expressions to be executed
4809-
attr_reader :statements
4810-
4811-
# [nil | Elsif | Else] the next clause in the chain
4812-
attr_reader :consequent
4813-
4814-
# [Array[ Comment | EmbDoc ]] the comments attached to this node
4815-
attr_reader :comments
4816-
4817-
def initialize(predicate:, statements:, consequent:, location:)
4818-
@predicate = predicate
4819-
@statements = statements
4820-
@consequent = consequent
4821-
@location = location
4822-
@comments = []
4823-
end
4824-
4825-
def accept(visitor)
4826-
visitor.visit_elsif(self)
4827-
end
4828-
4829-
def child_nodes
4830-
[predicate, statements, consequent]
4831-
end
4832-
4833-
def copy(predicate: nil, statements: nil, consequent: nil, location: nil)
4834-
node =
4835-
Elsif.new(
4836-
predicate: predicate || self.predicate,
4837-
statements: statements || self.statements,
4838-
consequent: consequent || self.consequent,
4839-
location: location || self.location
4840-
)
4841-
4842-
node.comments.concat(comments.map(&:copy))
4843-
node
4844-
end
4845-
4846-
alias deconstruct child_nodes
4847-
4848-
def deconstruct_keys(_keys)
4849-
{
4850-
predicate: predicate,
4851-
statements: statements,
4852-
consequent: consequent,
4853-
location: location,
4854-
comments: comments
4855-
}
4856-
end
4857-
4858-
def format(q)
4859-
q.group do
4860-
q.group do
4861-
q.text("elsif ")
4862-
q.nest("elsif".length - 1) { q.format(predicate) }
4863-
end
4864-
4865-
unless statements.empty?
4866-
q.indent do
4867-
q.breakable_force
4868-
q.format(statements)
4869-
end
4870-
end
4871-
4872-
if consequent
4873-
q.group do
4874-
q.breakable_force
4875-
q.format(consequent)
4876-
end
4877-
end
4878-
end
4879-
end
4880-
4881-
def ===(other)
4882-
other.is_a?(Elsif) && predicate === other.predicate &&
4883-
statements === other.statements && consequent === other.consequent
4884-
end
4885-
end
4886-
48874798
# EmbDoc represents a multi-line comment.
48884799
#
48894800
# =begin
@@ -6279,7 +6190,7 @@ def format(q)
62796190
# If we can transform this node into a ternary, then we're going to
62806191
# print a special version that uses the ternary operator if it fits on
62816192
# one line.
6282-
if Ternaryable.call(q, node)
6193+
if Ternaryable.call(q, node) && keyword != "elsif"
62836194
format_ternary(q)
62846195
return
62856196
end
@@ -6288,7 +6199,7 @@ def format(q)
62886199
# case we can't know for certain that that assignment doesn't impact the
62896200
# statements inside the conditional) then we can't use the modifier form
62906201
# and we must use the block form.
6291-
if ContainsAssignment.call(node.predicate)
6202+
if keyword == "elsif" || ContainsAssignment.call(node.predicate)
62926203
format_break(q, force: true)
62936204
return
62946205
end
@@ -6337,8 +6248,10 @@ def format_break(q, force:)
63376248
q.format(node.consequent)
63386249
end
63396250

6340-
force ? q.breakable_force : q.breakable_space
6341-
q.text("end")
6251+
unless keyword == "elsif"
6252+
force ? q.breakable_force : q.breakable_space
6253+
q.text("end")
6254+
end
63426255
end
63436256

63446257
def format_ternary(q)
@@ -6399,7 +6312,7 @@ def contains_conditional?
63996312
end
64006313
end
64016314

6402-
# If represents the first clause in an +if+ chain.
6315+
# If an +if+ or +elsif+ clause in an +if+ chain.
64036316
#
64046317
# if predicate
64056318
# end
@@ -6411,17 +6324,21 @@ class IfNode < Node
64116324
# [Statements] the expressions to be executed
64126325
attr_reader :statements
64136326

6414-
# [nil | Elsif | Else] the next clause in the chain
6327+
# [nil | IfNode | Else] the next clause in the chain
64156328
attr_reader :consequent
64166329

64176330
# [Array[ Comment | EmbDoc ]] the comments attached to this node
64186331
attr_reader :comments
64196332

6420-
def initialize(predicate:, statements:, consequent:, location:)
6333+
# [ String ] the opening of the conditional statement
6334+
attr_reader :beginning
6335+
6336+
def initialize(predicate:, statements:, consequent:, location:, beginning:)
64216337
@predicate = predicate
64226338
@statements = statements
64236339
@consequent = consequent
64246340
@location = location
6341+
@beginning = beginning
64256342
@comments = []
64266343
end
64276344

@@ -6433,13 +6350,15 @@ def child_nodes
64336350
[predicate, statements, consequent]
64346351
end
64356352

6436-
def copy(predicate: nil, statements: nil, consequent: nil, location: nil)
6353+
def copy(predicate: nil, statements: nil, consequent: nil, location: nil,
6354+
beginning: nil)
64376355
node =
64386356
IfNode.new(
64396357
predicate: predicate || self.predicate,
64406358
statements: statements || self.statements,
64416359
consequent: consequent || self.consequent,
6442-
location: location || self.location
6360+
location: location || self.location,
6361+
beginning: beginning || self.beginning,
64436362
)
64446363

64456364
node.comments.concat(comments.map(&:copy))
@@ -6454,17 +6373,19 @@ def deconstruct_keys(_keys)
64546373
statements: statements,
64556374
consequent: consequent,
64566375
location: location,
6376+
beginning: beginning,
64576377
comments: comments
64586378
}
64596379
end
64606380

64616381
def format(q)
6462-
ConditionalFormatter.new("if", self).format(q)
6382+
ConditionalFormatter.new(beginning, self).format(q)
64636383
end
64646384

64656385
def ===(other)
64666386
other.is_a?(IfNode) && predicate === other.predicate &&
6467-
statements === other.statements && consequent === other.consequent
6387+
statements === other.statements && consequent === other.consequent &&
6388+
beginning === other.beginning
64686389
end
64696390

64706391
# Checks if the node was originally found in the modifier form.
@@ -11260,7 +11181,7 @@ class UnlessNode < Node
1126011181
# [Statements] the expressions to be executed
1126111182
attr_reader :statements
1126211183

11263-
# [nil | Elsif | Else] the next clause in the chain
11184+
# [nil | IfNode | Else] the next clause in the chain
1126411185
attr_reader :consequent
1126511186

1126611187
# [Array[ Comment | EmbDoc ]] the comments attached to this node

lib/syntax_tree/parser.rb

+8-5
Original file line numberDiff line numberDiff line change
@@ -1513,11 +1513,12 @@ def on_elsif(predicate, statements, consequent)
15131513
ending.location.start_column
15141514
)
15151515

1516-
Elsif.new(
1516+
IfNode.new(
15171517
predicate: predicate,
15181518
statements: statements,
15191519
consequent: consequent,
1520-
location: beginning.location.to(ending.location)
1520+
location: beginning.location.to(ending.location),
1521+
beginning: beginning.value
15211522
)
15221523
end
15231524

@@ -1996,7 +1997,8 @@ def on_if(predicate, statements, consequent)
19961997
predicate: predicate,
19971998
statements: statements,
19981999
consequent: consequent,
1999-
location: beginning.location.to(ending.location)
2000+
location: beginning.location.to(ending.location),
2001+
beginning: beginning.value
20002002
)
20012003
end
20022004

@@ -2014,14 +2016,15 @@ def on_ifop(predicate, truthy, falsy)
20142016
# :call-seq:
20152017
# on_if_mod: (untyped predicate, untyped statement) -> IfNode
20162018
def on_if_mod(predicate, statement)
2017-
consume_keyword(:if)
2019+
beginning = consume_keyword(:if)
20182020

20192021
IfNode.new(
20202022
predicate: predicate,
20212023
statements:
20222024
Statements.new(self, body: [statement], location: statement.location),
20232025
consequent: nil,
2024-
location: statement.location.to(predicate.location)
2026+
location: statement.location.to(predicate.location),
2027+
beginning: beginning.value
20252028
)
20262029
end
20272030

test/node_test.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ def test_elsif
452452
SOURCE
453453

454454
at = location(lines: 2..4, chars: 9..30)
455-
assert_node(Elsif, source, at: at, &:consequent)
455+
assert_node(IfNode, source, at: at, &:consequent)
456456
end
457457

458458
def test_embdoc

0 commit comments

Comments
 (0)