Skip to content

Commit 802d647

Browse files
committedDec 9, 2021
Force the maintaining of the modifier forms of conditionals and loops if the statement includes an assignment. Also, for the maintaining of the block form of conditionals and loops if the predicate includes an assignment.
1 parent 5bf43b2 commit 802d647

File tree

10 files changed

+96
-28
lines changed

10 files changed

+96
-28
lines changed
 

‎CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
1010

1111
- [#7](https://github.com/kddnewton/syntax_tree/issues/7) Better formatting for hashes and arrays that are values in hashes.
1212
- [#9](https://github.com/kddnewton/syntax_tree/issues/9) Special handling for RSpec matchers when nesting `CommandCall` nodes.
13+
- [#10](https://github.com/kddnewton/syntax_tree/issues/10) Force the maintaining of the modifier forms of conditionals and loops if the statement includes an assignment. Also, for the maintaining of the block form of conditionals and loops if the predicate includes an assignment.
1314

1415
## [1.1.0] - 2021-12-08
1516

‎lib/syntax_tree.rb

+59-28
Original file line numberDiff line numberDiff line change
@@ -1654,7 +1654,7 @@ def initialize(
16541654
end
16551655

16561656
def child_nodes
1657-
[constant, *required, rest, *posts]
1657+
[constant, *requireds, rest, *posts]
16581658
end
16591659

16601660
def format(q)
@@ -6784,6 +6784,23 @@ def on_ident(value)
67846784
)
67856785
end
67866786

6787+
# If the predicate of a conditional or loop contains an assignment (in which
6788+
# case we can't know for certain that that assignment doesn't impact the
6789+
# statements inside the conditional) then we can't use the modifier form
6790+
# and we must use the block form.
6791+
module ContainsAssignment
6792+
def self.call(parent)
6793+
queue = [parent]
6794+
6795+
while node = queue.shift
6796+
return true if [Assign, MAssign, OpAssign].include?(node.class)
6797+
queue += node.child_nodes
6798+
end
6799+
6800+
false
6801+
end
6802+
end
6803+
67876804
# Formats an If or Unless node.
67886805
class ConditionalFormatter
67896806
# [String] the keyword associated with this conditional
@@ -6802,7 +6819,7 @@ def format(q)
68026819
# case we can't know for certain that that assignment doesn't impact the
68036820
# statements inside the conditional) then we can't use the modifier form
68046821
# and we must use the block form.
6805-
if [Assign, MAssign, OpAssign].include?(node.predicate.class)
6822+
if ContainsAssignment.call(node.predicate)
68066823
format_break(q, force: true)
68076824
return
68086825
end
@@ -7078,23 +7095,31 @@ def initialize(keyword, node)
70787095
end
70797096

70807097
def format(q)
7081-
q.group do
7082-
q.if_break do
7083-
q.text("#{keyword} ")
7084-
q.nest(keyword.length + 1) { q.format(node.predicate) }
7085-
q.indent do
7086-
q.breakable
7087-
q.format(node.statement)
7088-
end
7089-
q.breakable
7090-
q.text("end")
7091-
end.if_flat do
7092-
Parentheses.flat(q) do
7093-
q.format(node.statement)
7094-
q.text(" #{keyword} ")
7095-
q.format(node.predicate)
7096-
end
7097-
end
7098+
if ContainsAssignment.call(node.statement)
7099+
q.group { format_flat(q) }
7100+
else
7101+
q.group { q.if_break { format_break(q) }.if_flat { format_flat(q) } }
7102+
end
7103+
end
7104+
7105+
private
7106+
7107+
def format_break(q)
7108+
q.text("#{keyword} ")
7109+
q.nest(keyword.length + 1) { q.format(node.predicate) }
7110+
q.indent do
7111+
q.breakable
7112+
q.format(node.statement)
7113+
end
7114+
q.breakable
7115+
q.text("end")
7116+
end
7117+
7118+
def format_flat(q)
7119+
Parentheses.flat(q) do
7120+
q.format(node.statement)
7121+
q.text(" #{keyword} ")
7122+
q.format(node.predicate)
70987123
end
70997124
end
71007125
end
@@ -12436,11 +12461,7 @@ def initialize(keyword, node, statements)
1243612461
end
1243712462

1243812463
def format(q)
12439-
# If the predicate of the loop contains an assignment (in which case we
12440-
# can't know for certain that that assignment doesn't impact the
12441-
# statements inside the loop) then we can't use the modifier form and we
12442-
# must use the block form.
12443-
if [Assign, MAssign, OpAssign].include?(node.predicate.class)
12464+
if ContainsAssignment.call(node.predicate)
1244412465
format_break(q)
1244512466
q.break_parent
1244612467
return
@@ -12601,8 +12622,13 @@ def format(q)
1260112622
# foo
1260212623
# end until bar
1260312624
#
12604-
# The above is effectively a `do...until` loop.
12605-
if statement.is_a?(Begin)
12625+
# Also, if the statement of the modifier includes an assignment, then we
12626+
# can't know for certain that it won't impact the predicate, so we need to
12627+
# force it to stay as it is. This looks like:
12628+
#
12629+
# foo = bar until foo
12630+
#
12631+
if statement.is_a?(Begin) || ContainsAssignment.call(statement)
1260612632
q.format(statement)
1260712633
q.text(" until ")
1260812634
q.format(predicate)
@@ -13189,8 +13215,13 @@ def format(q)
1318913215
# foo
1319013216
# end while bar
1319113217
#
13192-
# The above is effectively a `do...while` loop.
13193-
if statement.is_a?(Begin)
13218+
# Also, if the statement of the modifier includes an assignment, then we
13219+
# can't know for certain that it won't impact the predicate, so we need to
13220+
# force it to stay as it is. This looks like:
13221+
#
13222+
# foo = bar while foo
13223+
#
13224+
if statement.is_a?(Begin) || ContainsAssignment.call(statement)
1319413225
q.format(statement)
1319513226
q.text(" while ")
1319613227
q.format(predicate)

‎test/fixtures/if.rb

+4
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,7 @@
2424
if foo += 1
2525
foo
2626
end
27+
%
28+
if (foo += 1)
29+
foo
30+
end

‎test/fixtures/if_mod.rb

+5
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,8 @@
88
end
99
%
1010
bar if foo # comment
11+
%
12+
foo = barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr if foo
13+
-
14+
foo =
15+
barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr if foo

‎test/fixtures/unless.rb

+4
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,7 @@
2424
unless foo += 1
2525
foo
2626
end
27+
%
28+
unless (foo += 1)
29+
foo
30+
end

‎test/fixtures/unless_mod.rb

+5
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,8 @@
88
end
99
%
1010
bar unless foo # comment
11+
%
12+
foo = barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr unless foo
13+
-
14+
foo =
15+
barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr unless foo

‎test/fixtures/until.rb

+4
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,7 @@
1919
until foo += 1
2020
foo
2121
end
22+
%
23+
until (foo += 1)
24+
foo
25+
end

‎test/fixtures/until_mod.rb

+5
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,8 @@
88
end
99
%
1010
bar until foo # comment
11+
%
12+
foo = barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr until foo
13+
-
14+
foo =
15+
barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr until foo

‎test/fixtures/while.rb

+4
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,7 @@
1919
while foo += 1
2020
foo
2121
end
22+
%
23+
while (foo += 1)
24+
foo
25+
end

‎test/fixtures/while_mod.rb

+5
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,8 @@
88
end
99
%
1010
bar while foo # comment
11+
%
12+
foo = barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr while foo
13+
-
14+
foo =
15+
barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr while foo

0 commit comments

Comments
 (0)