parser 2.4.0.2 → 2.5.0.0

Sign up to get free protection for your applications and to get access to all the features.
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