parser 2.4.0.2 → 2.5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -6
  3. data/CHANGELOG.md +35 -1
  4. data/Gemfile +2 -0
  5. data/README.md +1 -2
  6. data/Rakefile +2 -1
  7. data/bin/ruby-parse +2 -1
  8. data/bin/ruby-rewrite +2 -1
  9. data/lib/gauntlet_parser.rb +2 -0
  10. data/lib/parser.rb +16 -17
  11. data/lib/parser/all.rb +2 -0
  12. data/lib/parser/ast/node.rb +2 -0
  13. data/lib/parser/ast/processor.rb +2 -0
  14. data/lib/parser/base.rb +6 -12
  15. data/lib/parser/builders/default.rb +28 -47
  16. data/lib/parser/clobbering_error.rb +2 -0
  17. data/lib/parser/context.rb +42 -0
  18. data/lib/parser/current.rb +4 -20
  19. data/lib/parser/deprecation.rb +13 -0
  20. data/lib/parser/diagnostic.rb +3 -3
  21. data/lib/parser/diagnostic/engine.rb +2 -0
  22. data/lib/parser/lexer.rl +122 -60
  23. data/lib/parser/lexer/dedenter.rb +2 -0
  24. data/lib/parser/lexer/explanation.rb +2 -0
  25. data/lib/parser/lexer/literal.rb +4 -9
  26. data/lib/parser/lexer/stack_state.rb +4 -1
  27. data/lib/parser/macruby.y +32 -17
  28. data/lib/parser/messages.rb +14 -0
  29. data/lib/parser/meta.rb +2 -0
  30. data/lib/parser/rewriter.rb +30 -44
  31. data/lib/parser/ruby18.y +20 -13
  32. data/lib/parser/ruby19.y +32 -17
  33. data/lib/parser/ruby20.y +33 -18
  34. data/lib/parser/ruby21.y +32 -17
  35. data/lib/parser/ruby22.y +32 -17
  36. data/lib/parser/ruby23.y +32 -17
  37. data/lib/parser/ruby24.y +63 -46
  38. data/lib/parser/ruby25.y +72 -48
  39. data/lib/parser/rubymotion.y +33 -18
  40. data/lib/parser/runner.rb +4 -7
  41. data/lib/parser/runner/ruby_parse.rb +10 -0
  42. data/lib/parser/runner/ruby_rewrite.rb +2 -0
  43. data/lib/parser/source/buffer.rb +19 -24
  44. data/lib/parser/source/comment.rb +2 -0
  45. data/lib/parser/source/comment/associator.rb +2 -0
  46. data/lib/parser/source/map.rb +2 -0
  47. data/lib/parser/source/map/collection.rb +2 -0
  48. data/lib/parser/source/map/condition.rb +2 -0
  49. data/lib/parser/source/map/constant.rb +2 -0
  50. data/lib/parser/source/map/definition.rb +2 -0
  51. data/lib/parser/source/map/for.rb +2 -0
  52. data/lib/parser/source/map/heredoc.rb +2 -0
  53. data/lib/parser/source/map/keyword.rb +2 -0
  54. data/lib/parser/source/map/objc_kwarg.rb +2 -0
  55. data/lib/parser/source/map/operator.rb +2 -0
  56. data/lib/parser/source/map/rescue_body.rb +2 -0
  57. data/lib/parser/source/map/send.rb +2 -0
  58. data/lib/parser/source/map/ternary.rb +2 -0
  59. data/lib/parser/source/map/variable.rb +2 -0
  60. data/lib/parser/source/range.rb +81 -13
  61. data/lib/parser/source/rewriter.rb +48 -10
  62. data/lib/parser/source/rewriter/action.rb +2 -0
  63. data/lib/parser/source/tree_rewriter.rb +301 -0
  64. data/lib/parser/source/tree_rewriter/action.rb +133 -0
  65. data/lib/parser/static_environment.rb +2 -0
  66. data/lib/parser/syntax_error.rb +2 -0
  67. data/lib/parser/tree_rewriter.rb +133 -0
  68. data/lib/parser/version.rb +3 -1
  69. data/parser.gemspec +4 -1
  70. data/test/bug_163/fixtures/input.rb +2 -0
  71. data/test/bug_163/fixtures/output.rb +2 -0
  72. data/test/bug_163/rewriter.rb +2 -0
  73. data/test/helper.rb +7 -7
  74. data/test/parse_helper.rb +57 -10
  75. data/test/racc_coverage_helper.rb +2 -0
  76. data/test/test_base.rb +2 -0
  77. data/test/test_current.rb +2 -4
  78. data/test/test_diagnostic.rb +3 -1
  79. data/test/test_diagnostic_engine.rb +2 -0
  80. data/test/test_encoding.rb +61 -49
  81. data/test/test_lexer.rb +164 -77
  82. data/test/test_lexer_stack_state.rb +2 -0
  83. data/test/test_parse_helper.rb +8 -8
  84. data/test/test_parser.rb +613 -51
  85. data/test/test_runner_rewrite.rb +47 -0
  86. data/test/test_source_buffer.rb +22 -10
  87. data/test/test_source_comment.rb +2 -0
  88. data/test/test_source_comment_associator.rb +2 -0
  89. data/test/test_source_map.rb +2 -0
  90. data/test/test_source_range.rb +92 -45
  91. data/test/test_source_rewriter.rb +3 -1
  92. data/test/test_source_rewriter_action.rb +2 -0
  93. data/test/test_source_tree_rewriter.rb +177 -0
  94. data/test/test_static_environment.rb +2 -0
  95. data/test/using_tree_rewriter/fixtures/input.rb +3 -0
  96. data/test/using_tree_rewriter/fixtures/output.rb +3 -0
  97. data/test/using_tree_rewriter/using_tree_rewriter.rb +9 -0
  98. metadata +21 -10
  99. data/lib/parser/compatibility/ruby1_8.rb +0 -20
  100. data/lib/parser/compatibility/ruby1_9.rb +0 -32
  101. data/test/bug_163/test_runner_rewrite.rb +0 -35
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+ require 'fileutils'
5
+ require 'shellwords'
6
+ require 'open3'
7
+
8
+ BASE_DIR = Pathname.new(__FILE__) + '..'
9
+ require (BASE_DIR + 'helper').expand_path
10
+
11
+ class TestRunnerRewrite < Minitest::Test
12
+ def assert_rewriter_output(path, args, input: 'input.rb', output: 'output.rb', expected_output: '', expected_error: '')
13
+ @ruby_rewrite = BASE_DIR.expand_path + '../bin/ruby-rewrite'
14
+ @test_dir = BASE_DIR + path
15
+ @fixtures_dir = @test_dir + 'fixtures'
16
+
17
+ Dir.mktmpdir("parser", BASE_DIR.expand_path.to_s) do |tmp_dir|
18
+ tmp_dir = Pathname.new(tmp_dir)
19
+ sample_file = tmp_dir + "#{path}.rb"
20
+ sample_file_expanded = sample_file.expand_path
21
+ expected_file = @fixtures_dir + output
22
+
23
+ FileUtils.cp(@fixtures_dir + input, sample_file_expanded)
24
+ stdout, stderr, exit_code = Dir.chdir @test_dir do
25
+ Open3.capture3 %Q{
26
+ #{Shellwords.escape(@ruby_rewrite.to_s)} #{args} \
27
+ #{Shellwords.escape(sample_file_expanded.to_s)}
28
+ }
29
+ end
30
+
31
+ assert_equal expected_output.chomp, stdout.chomp
32
+ assert_match expected_error.chomp, stderr.chomp
33
+ assert_equal File.read(expected_file.expand_path), File.read(sample_file)
34
+ end
35
+ end
36
+
37
+ def test_rewriter_bug_163
38
+ assert_rewriter_output('bug_163',
39
+ '--modify -l rewriter.rb',
40
+ expected_error: Parser::Rewriter::DEPRECATION_WARNING
41
+ )
42
+ end
43
+
44
+ def test_tree_rewriter
45
+ assert_rewriter_output('using_tree_rewriter', '-l using_tree_rewriter.rb --modify')
46
+ end
47
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'helper'
2
4
 
3
5
  class TestSourceBuffer < Minitest::Test
@@ -29,17 +31,15 @@ class TestSourceBuffer < Minitest::Test
29
31
  end
30
32
  end
31
33
 
32
- if defined?(Encoding)
33
- def test_source_setter_encoding_error
34
- error = assert_raises EncodingError do
35
- @buffer.source = [
36
- '# encoding: utf-8',
37
- "# \xf9"
38
- ].join("\n")
39
- end
40
-
41
- assert_match /invalid byte sequence in UTF\-8/, error.message
34
+ def test_source_setter_encoding_error
35
+ error = assert_raises EncodingError do
36
+ @buffer.source = [
37
+ '# encoding: utf-8',
38
+ "# \xf9"
39
+ ].join("\n")
42
40
  end
41
+
42
+ assert_match /invalid byte sequence in UTF\-8/, error.message
43
43
  end
44
44
 
45
45
  def test_read
@@ -117,6 +117,18 @@ class TestSourceBuffer < Minitest::Test
117
117
  end
118
118
  end
119
119
 
120
+ def test_source_range
121
+ @buffer = Parser::Source::Buffer.new('(string)', 5)
122
+
123
+ assert_raises RuntimeError do
124
+ @buffer.source_range
125
+ end
126
+
127
+ @buffer.source = "abc\ndef\nghi\n"
128
+
129
+ assert_equal Parser::Source::Range.new(@buffer, 0, @buffer.source.size), @buffer.source_range
130
+ end
131
+
120
132
  def test_last_line
121
133
  @buffer.source = "1\nfoo\nbar"
122
134
  assert_equal 3, @buffer.last_line
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'helper'
2
4
 
3
5
  class TestSourceComment < Minitest::Test
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'helper'
2
4
  require 'parser/ruby18'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'helper'
2
4
  require 'parse_helper'
3
5
 
@@ -1,21 +1,28 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'helper'
2
4
 
3
5
  class TestSourceRange < Minitest::Test
4
6
  def setup
5
7
  @buf = Parser::Source::Buffer.new('(string)')
6
8
  @buf.source = "foobar\nbaz"
9
+ @sr1_3 = Parser::Source::Range.new(@buf, 1, 3)
10
+ @sr2_2 = Parser::Source::Range.new(@buf, 2, 2)
11
+ @sr3_3 = Parser::Source::Range.new(@buf, 3, 3)
12
+ @sr2_6 = Parser::Source::Range.new(@buf, 2, 6)
13
+ @sr5_8 = Parser::Source::Range.new(@buf, 5, 8)
14
+ @sr5_7 = Parser::Source::Range.new(@buf, 5, 7)
15
+ @sr6_7 = Parser::Source::Range.new(@buf, 6, 7)
7
16
  end
8
17
 
9
18
  def test_initialize
10
- sr = Parser::Source::Range.new(@buf, 1, 2)
11
- assert_equal 1, sr.begin_pos
12
- assert_equal 2, sr.end_pos
13
- assert sr.frozen?
19
+ assert_equal 1, @sr1_3.begin_pos
20
+ assert_equal 3, @sr1_3.end_pos
21
+ assert @sr1_3.frozen?
14
22
  end
15
23
 
16
24
  def test_size
17
- sr = Parser::Source::Range.new(@buf, 1, 3)
18
- assert_equal 2, sr.size
25
+ assert_equal 4, @sr2_6.size
19
26
  end
20
27
 
21
28
  def test_bad_size
@@ -25,54 +32,86 @@ class TestSourceRange < Minitest::Test
25
32
  end
26
33
 
27
34
  def test_join
28
- sr1 = Parser::Source::Range.new(@buf, 1, 2)
29
- sr2 = Parser::Source::Range.new(@buf, 5, 8)
30
- sr = sr1.join(sr2)
35
+ sr = @sr1_3.join(@sr5_8)
31
36
 
32
37
  assert_equal 1, sr.begin_pos
33
38
  assert_equal 8, sr.end_pos
34
39
  end
35
40
 
36
41
  def test_intersect
37
- sr1 = Parser::Source::Range.new(@buf, 1, 3)
38
- sr2 = Parser::Source::Range.new(@buf, 2, 6)
39
- sr3 = Parser::Source::Range.new(@buf, 5, 8)
42
+ assert_equal 2, @sr1_3.intersect(@sr2_6).begin_pos
43
+ assert_equal 3, @sr1_3.intersect(@sr2_6).end_pos
44
+ assert_equal 5, @sr2_6.intersect(@sr5_8).begin_pos
45
+ assert_equal 6, @sr2_6.intersect(@sr5_8).end_pos
46
+ assert @sr1_3.intersect(@sr5_8) == nil
47
+ assert_equal 2, @sr1_3.intersect(@sr2_2).begin_pos
48
+ assert_equal 2, @sr1_3.intersect(@sr2_2).end_pos
49
+ assert_equal 2, @sr2_2.intersect(@sr2_2).begin_pos
50
+ assert_equal 2, @sr2_2.intersect(@sr2_2).end_pos
51
+ end
40
52
 
41
- assert_equal 2, sr1.intersect(sr2).begin_pos
42
- assert_equal 3, sr1.intersect(sr2).end_pos
43
- assert_equal 5, sr2.intersect(sr3).begin_pos
44
- assert_equal 6, sr2.intersect(sr3).end_pos
45
- assert sr1.intersect(sr3) == nil
53
+ def test_overlaps
54
+ assert !@sr1_3.overlaps?(@sr5_8)
55
+ assert @sr1_3.overlaps?(@sr2_6)
56
+ assert @sr2_6.overlaps?(@sr5_8)
57
+ assert @sr1_3.overlaps?(@sr2_2)
58
+ assert !@sr2_6.overlaps?(@sr2_2)
59
+ assert @sr2_2.overlaps?(@sr2_2)
60
+ end
61
+
62
+ def check_relationship(relationship, sr1, sr2, reflexive_relationship = relationship)
63
+ # Double check equality
64
+ assert_equal true, sr1 == sr1.dup
65
+ assert_equal true, sr1 != sr2
66
+ # Check relationships and reflexivity
67
+ assert_equal true, sr1.send(relationship, sr2)
68
+ assert_equal true, sr2.send(reflexive_relationship, sr1)
69
+ # Check it's not true for itself
70
+ assert_equal false, sr1.send(relationship, sr1)
71
+ # Check other relationships return false
72
+ others = %i[disjoint? crossing? contains? contained?] - [relationship, reflexive_relationship]
73
+ others.each do |other_rel|
74
+ assert_equal false, sr1.send(other_rel, sr2), other_rel
75
+ assert_equal false, sr2.send(other_rel, sr1), other_rel
76
+ end
46
77
  end
47
78
 
48
79
  def test_disjoint
49
- sr1 = Parser::Source::Range.new(@buf, 1, 3)
50
- sr2 = Parser::Source::Range.new(@buf, 2, 6)
51
- sr3 = Parser::Source::Range.new(@buf, 5, 8)
80
+ check_relationship(:disjoint?, @sr1_3, @sr5_8)
81
+ check_relationship(:disjoint?, @sr2_2, @sr2_6)
82
+ check_relationship(:disjoint?, @sr2_2, @sr3_3)
83
+ check_relationship(:disjoint?, @sr2_6, @sr6_7)
84
+ end
52
85
 
53
- assert sr1.disjoint?(sr3)
54
- assert !sr1.disjoint?(sr2)
55
- assert !sr2.disjoint?(sr3)
86
+ def test_crossing
87
+ check_relationship(:crossing?, @sr1_3, @sr2_6)
56
88
  end
57
89
 
58
- def test_overlaps
59
- sr1 = Parser::Source::Range.new(@buf, 1, 3)
60
- sr2 = Parser::Source::Range.new(@buf, 2, 6)
61
- sr3 = Parser::Source::Range.new(@buf, 5, 8)
90
+ def test_containment
91
+ check_relationship(:contained?, @sr2_2, @sr1_3, :contains?)
92
+ check_relationship(:contained?, @sr5_7, @sr5_8, :contains?)
93
+ check_relationship(:contained?, @sr6_7, @sr5_8, :contains?)
94
+ end
62
95
 
63
- assert !sr1.overlaps?(sr3)
64
- assert sr1.overlaps?(sr2)
65
- assert sr2.overlaps?(sr3)
96
+ def test_order
97
+ assert_equal 0, @sr1_3 <=> @sr1_3
98
+ assert_equal -1, @sr1_3 <=> @sr5_8
99
+ assert_equal -1, @sr2_2 <=> @sr2_6
100
+ assert_equal +1, @sr2_6 <=> @sr2_2
101
+
102
+ assert_equal -1, @sr1_3 <=> @sr2_6
103
+
104
+ assert_equal +1, @sr2_2 <=> @sr1_3
105
+ assert_equal -1, @sr1_3 <=> @sr2_2
106
+ assert_equal -1, @sr5_7 <=> @sr5_8
107
+
108
+ assert_nil @sr1_3 <=> Parser::Source::Range.new(@buf.dup, 1, 3)
109
+ assert_nil @sr1_3 <=> 4
66
110
  end
67
111
 
68
112
  def test_empty
69
- sr1 = Parser::Source::Range.new(@buf, 1, 3)
70
- sr2 = Parser::Source::Range.new(@buf, 2, 2)
71
- sr3 = Parser::Source::Range.new(@buf, 7, 8)
72
-
73
- assert !sr1.empty?
74
- assert sr2.empty?
75
- assert !sr3.empty?
113
+ assert !@sr1_3.empty?
114
+ assert @sr2_2.empty?
76
115
  end
77
116
 
78
117
  def test_line
@@ -93,15 +132,13 @@ class TestSourceRange < Minitest::Test
93
132
  end
94
133
 
95
134
  def test_begin_end
96
- sr = Parser::Source::Range.new(@buf, 1, 5)
135
+ sr_beg = @sr2_6.begin
136
+ assert_equal 2, sr_beg.begin_pos
137
+ assert_equal 2, sr_beg.end_pos
97
138
 
98
- sr_beg = sr.begin
99
- assert_equal 1, sr_beg.begin_pos
100
- assert_equal 1, sr_beg.end_pos
101
-
102
- sr_end = sr.end
103
- assert_equal 5, sr_end.begin_pos
104
- assert_equal 5, sr_end.end_pos
139
+ sr_end = @sr2_6.end
140
+ assert_equal 6, sr_end.begin_pos
141
+ assert_equal 6, sr_end.end_pos
105
142
  end
106
143
 
107
144
  def test_source
@@ -122,4 +159,14 @@ class TestSourceRange < Minitest::Test
122
159
  sr = Parser::Source::Range.new(@buf, 8, 9)
123
160
  assert_equal '(string):2:2', sr.to_s
124
161
  end
162
+
163
+ def test_with
164
+ sr2 = @sr1_3.with(begin_pos: 2)
165
+ sr3 = @sr1_3.with(end_pos: 4)
166
+
167
+ assert_equal 2, sr2.begin_pos
168
+ assert_equal 3, sr2.end_pos
169
+ assert_equal 1, sr3.begin_pos
170
+ assert_equal 4, sr3.end_pos
171
+ end
125
172
  end
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'helper'
2
4
 
3
5
  class TestSourceRewriter < Minitest::Test
4
6
  def setup
5
7
  @buf = Parser::Source::Buffer.new('(rewriter)')
6
8
  @buf.source = 'foo bar baz'
7
-
9
+ Parser::Source::Rewriter.warned_of_deprecation = true
8
10
  @rewriter = Parser::Source::Rewriter.new(@buf)
9
11
  end
10
12
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'helper'
2
4
 
3
5
  class TestSourceRewriterAction < Minitest::Test
@@ -0,0 +1,177 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'helper'
4
+
5
+ class TestSourceTreeRewriter < Minitest::Test
6
+ def setup
7
+ @buf = Parser::Source::Buffer.new('(rewriter)')
8
+ @buf.source = 'puts(:hello, :world)'
9
+
10
+ @hello = range(5, 6)
11
+ @comma_space = range(11,2)
12
+ @world = range(13,6)
13
+ end
14
+
15
+ def range(from, len)
16
+ Parser::Source::Range.new(@buf, from, from + len)
17
+ end
18
+
19
+ # Returns either:
20
+ # - String (Normal operation)
21
+ # - [diagnostic, ...] (Diagnostics)
22
+ # - Parser::ClobberingError
23
+ #
24
+ def apply(actions, **policy)
25
+ diagnostics = []
26
+ diags = -> { diagnostics.flatten.map(&:strip).join("\n") }
27
+ rewriter = Parser::Source::TreeRewriter.new(@buf, **policy)
28
+ rewriter.diagnostics.consumer = -> diag { diagnostics << diag.render }
29
+ actions.each do |action, range, *args|
30
+ rewriter.public_send(action, range, *args)
31
+ end
32
+ if diagnostics.empty?
33
+ rewriter.process
34
+ else
35
+ diags.call
36
+ end
37
+ rescue ::Parser::ClobberingError => e
38
+ [::Parser::ClobberingError, diags.call]
39
+ end
40
+
41
+ # Expects ordered actions to be grouped together
42
+ def check_actions(expected, grouped_actions, **policy)
43
+ grouped_actions.permutation do |sequence|
44
+ # [action, [action, action]] => [action, action, action]
45
+ # except we can't use flatten because "action" are arrays themselves
46
+ actions = sequence.flat_map { |group| group.first.is_a?(Array) ? group : [group] }
47
+ assert_equal(expected, apply(actions, **policy))
48
+ end
49
+ end
50
+
51
+ def assert_actions_result(expected, *actions, **rest)
52
+ if expected == :raise
53
+ diagnostic = rest.values.first
54
+ check_actions([::Parser::ClobberingError, diagnostic], actions)
55
+ elsif rest.empty?
56
+ check_actions(expected, actions)
57
+ else
58
+ policy, diagnostic = rest.first
59
+ check_actions(expected, actions, policy => :accept)
60
+ check_actions(diagnostic, actions, policy => :warn)
61
+ diagnostic.gsub!(/warning: /, 'error: ')
62
+ check_actions([::Parser::ClobberingError, diagnostic], actions, policy => :raise)
63
+ end
64
+ end
65
+
66
+ ### Simple cases
67
+
68
+ def test_remove
69
+ assert_actions_result 'puts(, :world)', [:remove, @hello]
70
+ end
71
+
72
+ def test_insert_before
73
+ assert_actions_result 'puts(:hello, 42, :world)', [:insert_before, @world, '42, ']
74
+ end
75
+
76
+ def test_insert_after
77
+ assert_actions_result 'puts(:hello, 42, :world)', [:insert_after, @hello, ', 42']
78
+ end
79
+
80
+ def test_wrap
81
+ assert_actions_result 'puts([:hello], :world)', [:wrap, @hello, '[', ']']
82
+ end
83
+
84
+ def test_replace
85
+ assert_actions_result 'puts(:hi, :world)', [:replace, @hello, ':hi']
86
+ end
87
+
88
+ #
89
+ # All other cases, as per doc
90
+ #
91
+
92
+ def test_crossing_non_deletions
93
+ check = [
94
+ [:wrap, '(', ')'],
95
+ [:remove],
96
+ [:replace, 'xx'],
97
+ ]
98
+ check.combination(2) do |(action, *args), (action_b, *args_b)|
99
+ next if action == :remove && action_b == :remove
100
+ assert_actions_result :raise,
101
+ [[action, @hello.join(@comma_space), *args],
102
+ [action_b, @world.join(@comma_space), *args_b]],
103
+ diagnostic: <<-DIAGNOSTIC.chomp
104
+ (rewriter):1:12: error: the rewriting action on:
105
+ (rewriter):1: puts(:hello, :world)
106
+ (rewriter):1: ^~~~~~~~
107
+ (rewriter):1:6: error: is crossing that on:
108
+ (rewriter):1: puts(:hello, :world)
109
+ (rewriter):1: ^~~~~~~~
110
+ DIAGNOSTIC
111
+ end
112
+ end
113
+
114
+
115
+ def test_crossing_deletions
116
+ assert_actions_result 'puts()',
117
+ [[:remove, @hello.join(@comma_space)],
118
+ [:remove, @world.join(@comma_space)]],
119
+ crossing_deletions: <<-DIAGNOSTIC.chomp
120
+ (rewriter):1:12: warning: the deletion of:
121
+ (rewriter):1: puts(:hello, :world)
122
+ (rewriter):1: ^~~~~~~~
123
+ (rewriter):1:6: warning: is crossing:
124
+ (rewriter):1: puts(:hello, :world)
125
+ (rewriter):1: ^~~~~~~~
126
+ DIAGNOSTIC
127
+ end
128
+
129
+ def test_multiple_actions
130
+ assert_actions_result 'puts({:hello => [:everybody]})',
131
+ [:replace, @comma_space, ' => '],
132
+ [:wrap, @hello.join(@world), '{', '}'],
133
+ [:replace, @world, ':everybody'],
134
+ [:wrap, @world, '[', ']']
135
+ end
136
+
137
+
138
+ def test_multiple_actions
139
+ assert_actions_result 'puts({:hello => [:everybody]})',
140
+ [:replace, @comma_space, ' => '],
141
+ [:wrap, @hello.join(@world), '{', '}'],
142
+ [:replace, @world, ':everybody'],
143
+ [:wrap, @world, '[', ']']
144
+ end
145
+
146
+ def test_wraps_same_range
147
+ assert_actions_result 'puts([(:hello)], :world)',
148
+ [[:wrap, @hello, '(', ')'],
149
+ [:wrap, @hello, '[', ']']]
150
+ end
151
+
152
+ def test_replace_same_range
153
+ assert_actions_result 'puts(:hey, :world)',
154
+ [[:replace, @hello, ':hi'],
155
+ [:replace, @hello, ':hey']],
156
+ different_replacements: <<-DIAGNOSTIC.chomp
157
+ (rewriter):1:6: warning: different replacements: :hey vs :hi
158
+ (rewriter):1: puts(:hello, :world)
159
+ (rewriter):1: ^~~~~~
160
+ DIAGNOSTIC
161
+ end
162
+
163
+ def test_swallowed_insertions
164
+ assert_actions_result 'puts(:hi)',
165
+ [[:wrap, @hello.adjust(begin_pos: 1), '__', '__'],
166
+ [:replace, @world.adjust(end_pos: -2), 'xx'],
167
+ [:replace, @hello.join(@world), ':hi']],
168
+ swallowed_insertions: <<-DIAGNOSTIC.chomp
169
+ (rewriter):1:6: warning: this replacement:
170
+ (rewriter):1: puts(:hello, :world)
171
+ (rewriter):1: ^~~~~~~~~~~~~~
172
+ (rewriter):1:7: warning: swallows some inner rewriting actions:
173
+ (rewriter):1: puts(:hello, :world)
174
+ (rewriter):1: ^~~~~ ~~~~
175
+ DIAGNOSTIC
176
+ end
177
+ end