parser 2.7.1.2 → 2.7.1.3

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -6
  3. data/CHANGELOG.md +17 -1
  4. data/README.md +1 -2
  5. data/Rakefile +1 -1
  6. data/doc/AST_FORMAT.md +22 -0
  7. data/lib/parser/ast/processor.rb +3 -0
  8. data/lib/parser/builders/default.rb +9 -0
  9. data/lib/parser/diagnostic.rb +1 -1
  10. data/lib/parser/diagnostic/engine.rb +1 -2
  11. data/lib/parser/lexer.rl +7 -0
  12. data/lib/parser/messages.rb +15 -0
  13. data/lib/parser/meta.rb +1 -1
  14. data/lib/parser/ruby28.y +62 -20
  15. data/lib/parser/runner.rb +21 -2
  16. data/lib/parser/runner/ruby_rewrite.rb +2 -2
  17. data/lib/parser/source/buffer.rb +3 -1
  18. data/lib/parser/source/comment/associator.rb +14 -4
  19. data/lib/parser/source/range.rb +7 -0
  20. data/lib/parser/source/tree_rewriter.rb +1 -1
  21. data/lib/parser/tree_rewriter.rb +1 -2
  22. data/lib/parser/version.rb +1 -1
  23. data/parser.gemspec +1 -1
  24. data/test/helper.rb +24 -6
  25. data/test/parse_helper.rb +9 -16
  26. data/test/test_ast_processor.rb +32 -0
  27. data/test/test_base.rb +1 -1
  28. data/test/test_diagnostic.rb +6 -7
  29. data/test/test_diagnostic_engine.rb +5 -8
  30. data/test/test_lexer.rb +5 -8
  31. data/test/test_meta.rb +12 -0
  32. data/test/test_parser.rb +84 -13
  33. data/test/test_runner_parse.rb +22 -1
  34. data/test/test_runner_rewrite.rb +1 -1
  35. data/test/test_source_buffer.rb +4 -1
  36. data/test/test_source_comment.rb +2 -2
  37. data/test/test_source_comment_associator.rb +47 -15
  38. data/test/test_source_map.rb +1 -2
  39. data/test/test_source_range.rb +16 -11
  40. data/test/test_source_rewriter.rb +4 -4
  41. data/test/test_source_rewriter_action.rb +2 -2
  42. data/test/test_source_tree_rewriter.rb +3 -3
  43. metadata +11 -7
@@ -149,6 +149,13 @@ module Parser
149
149
  (@begin_pos...@end_pos).to_a
150
150
  end
151
151
 
152
+ ##
153
+ # @return [Range] a Ruby range with the same `begin_pos` and `end_pos`
154
+ #
155
+ def to_range
156
+ self.begin_pos...self.end_pos
157
+ end
158
+
152
159
  ##
153
160
  # Composes a GNU/Clang-style string representation of the beginning of this
154
161
  # range.
@@ -221,7 +221,7 @@ module Parser
221
221
  #
222
222
  # @return [String]
223
223
  #
224
- def process
224
+ def process
225
225
  source = @source_buffer.source
226
226
 
227
227
  chunks = []
@@ -28,8 +28,7 @@ module Parser
28
28
  # EOF
29
29
  #
30
30
  # ast = Parser::CurrentRuby.parse code
31
- # buffer = Parser::Source::Buffer.new('(example)')
32
- # buffer.source = code
31
+ # buffer = Parser::Source::Buffer.new('(example)', source: code)
33
32
  # rewriter = RemoveDo.new
34
33
  #
35
34
  # # Rewrite the AST, returns a String with the new form.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Parser
4
- VERSION = '2.7.1.2'
4
+ VERSION = '2.7.1.3'
5
5
  end
@@ -45,7 +45,7 @@ Gem::Specification.new do |spec|
45
45
  spec.add_dependency 'ast', '~> 2.4.0'
46
46
 
47
47
  spec.add_development_dependency 'bundler', '>= 1.15', '< 3.0.0'
48
- spec.add_development_dependency 'rake', '~> 10.0'
48
+ spec.add_development_dependency 'rake', '~> 13.0.1'
49
49
  spec.add_development_dependency 'racc', '= 1.4.15'
50
50
  spec.add_development_dependency 'cliver', '~> 0.3.2'
51
51
 
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'tempfile'
4
- require 'minitest/test'
5
-
6
4
  require 'simplecov'
7
5
 
8
6
  if ENV.include?('COVERAGE') && SimpleCov.usable?
@@ -28,9 +26,9 @@ if ENV.include?('COVERAGE') && SimpleCov.usable?
28
26
  at_exit { RaccCoverage.stop }
29
27
 
30
28
  SimpleCov.start do
31
- self.formatter = SimpleCov::Formatter::MultiFormatter[
32
- SimpleCov::Formatter::HTMLFormatter,
33
- ]
29
+ self.formatter = SimpleCov::Formatter::MultiFormatter.new(
30
+ SimpleCov::Formatter::HTMLFormatter
31
+ )
34
32
 
35
33
  add_group 'Grammars' do |source_file|
36
34
  source_file.filename =~ %r{\.y$}
@@ -53,9 +51,29 @@ require 'minitest/autorun'
53
51
  $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
54
52
  require 'parser'
55
53
 
54
+ module NodeCollector
55
+ extend self
56
+ attr_accessor :callbacks, :nodes
57
+ self.callbacks = []
58
+ self.nodes = []
59
+
60
+ def check
61
+ @callbacks.each do |callback|
62
+ @nodes.each { |node| callback.call(node) }
63
+ end
64
+ puts "#{callbacks.size} additional tests on #{nodes.size} nodes ran successfully"
65
+ end
66
+
67
+ Minitest.after_run { check }
68
+ end
69
+
70
+ def for_each_node(&block)
71
+ NodeCollector.callbacks << block
72
+ end
73
+
56
74
  class Parser::AST::Node
57
75
  def initialize(type, *)
58
- raise "Type #{type} missing from Parser::Meta::NODE_TYPES" unless Parser::Meta::NODE_TYPES.include?(type)
76
+ NodeCollector.nodes << self
59
77
  super
60
78
  end
61
79
  end
@@ -85,8 +85,7 @@ module ParseHelper
85
85
  end
86
86
 
87
87
  def try_parsing(ast, code, parser, source_maps, version)
88
- source_file = Parser::Source::Buffer.new('(assert_parses)')
89
- source_file.source = code
88
+ source_file = Parser::Source::Buffer.new('(assert_parses)', source: code)
90
89
 
91
90
  begin
92
91
  parsed_ast = parser.parse(source_file)
@@ -106,8 +105,7 @@ module ParseHelper
106
105
  assert_equal ast, parsed_ast,
107
106
  "(#{version}) AST equality"
108
107
 
109
- parse_source_map_descriptions(source_maps) \
110
- do |begin_pos, end_pos, map_field, ast_path, line|
108
+ parse_source_map_descriptions(source_maps) do |begin_pos, end_pos, map_field, ast_path, line|
111
109
 
112
110
  astlet = traverse_ast(parsed_ast, ast_path)
113
111
 
@@ -143,8 +141,7 @@ module ParseHelper
143
141
  # ~~~
144
142
  def assert_diagnoses(diagnostic, code, source_maps='', versions=ALL_VERSIONS)
145
143
  with_versions(versions) do |version, parser|
146
- source_file = Parser::Source::Buffer.new('(assert_diagnoses)')
147
- source_file.source = code
144
+ source_file = Parser::Source::Buffer.new('(assert_diagnoses)', source: code)
148
145
 
149
146
  begin
150
147
  parser = parser.parse(source_file)
@@ -160,15 +157,14 @@ module ParseHelper
160
157
 
161
158
  level, reason, arguments = diagnostic
162
159
  arguments ||= {}
163
- message = Parser::MESSAGES[reason] % arguments
160
+ message = Parser::Messages.compile(reason, arguments)
164
161
 
165
162
  assert_equal level, emitted_diagnostic.level
166
163
  assert_equal reason, emitted_diagnostic.reason
167
164
  assert_equal arguments, emitted_diagnostic.arguments
168
165
  assert_equal message, emitted_diagnostic.message
169
166
 
170
- parse_source_map_descriptions(source_maps) \
171
- do |begin_pos, end_pos, map_field, ast_path, line|
167
+ parse_source_map_descriptions(source_maps) do |begin_pos, end_pos, map_field, ast_path, line|
172
168
 
173
169
  case map_field
174
170
  when 'location'
@@ -202,8 +198,7 @@ module ParseHelper
202
198
  # ~~~
203
199
  def assert_diagnoses_many(diagnostics, code, versions=ALL_VERSIONS)
204
200
  with_versions(versions) do |version, parser|
205
- source_file = Parser::Source::Buffer.new('(assert_diagnoses_many)')
206
- source_file.source = code
201
+ source_file = Parser::Source::Buffer.new('(assert_diagnoses_many)', source: code)
207
202
 
208
203
  begin
209
204
  parser = parser.parse(source_file)
@@ -216,7 +211,7 @@ module ParseHelper
216
211
  diagnostics.zip(@diagnostics) do |expected_diagnostic, actual_diagnostic|
217
212
  level, reason, arguments = expected_diagnostic
218
213
  arguments ||= {}
219
- message = Parser::MESSAGES[reason] % arguments
214
+ message = Parser::Messages.compile(reason, arguments)
220
215
 
221
216
  assert_equal level, actual_diagnostic.level
222
217
  assert_equal reason, actual_diagnostic.reason
@@ -228,8 +223,7 @@ module ParseHelper
228
223
 
229
224
  def refute_diagnoses(code, versions=ALL_VERSIONS)
230
225
  with_versions(versions) do |version, parser|
231
- source_file = Parser::Source::Buffer.new('(refute_diagnoses)')
232
- source_file.source = code
226
+ source_file = Parser::Source::Buffer.new('(refute_diagnoses)', source: code)
233
227
 
234
228
  begin
235
229
  parser = parser.parse(source_file)
@@ -245,8 +239,7 @@ module ParseHelper
245
239
 
246
240
  def assert_context(context, code, versions=ALL_VERSIONS)
247
241
  with_versions(versions) do |version, parser|
248
- source_file = Parser::Source::Buffer.new('(assert_context)')
249
- source_file.source = code
242
+ source_file = Parser::Source::Buffer.new('(assert_context)', source: code)
250
243
 
251
244
  begin
252
245
  parser.parse(source_file)
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'helper'
4
+
5
+ class TestASTProcessor < Minitest::Test
6
+ LEAF_NODES = %i[
7
+ sym str int float complex rational
8
+ true false nil self
9
+ __FILE__ __LINE__ __ENCODING__
10
+ cbase regopt zsuper
11
+ match_with_trailing_comma match_nil_pattern
12
+ forward_args forwarded_args numargs kwnilarg
13
+ objc_varargs objc_restarg objc_kwarg
14
+ ident
15
+ ].freeze
16
+
17
+ def setup
18
+ @traversible = Parser::AST::Processor
19
+ .instance_methods(false)
20
+ .map { |mid| mid.to_s.scan(/\Aon_(.*)/) }
21
+ .flatten
22
+ .map(&:to_sym)
23
+
24
+ @traversible += LEAF_NODES
25
+ end
26
+
27
+ def test_nodes_are_traversible
28
+ for_each_node do |node|
29
+ assert_includes @traversible, node.type
30
+ end
31
+ end
32
+ end
@@ -26,6 +26,6 @@ class TestBase < Minitest::Test
26
26
  def test_loc_dup
27
27
  ast = Parser::CurrentRuby.parse('1')
28
28
  assert_nil ast.loc.dup.node
29
- Parser::AST::Node.new(:root, [], :location => ast.loc)
29
+ Parser::AST::Node.new(:zsuper, [], :location => ast.loc)
30
30
  end
31
31
  end
@@ -4,8 +4,8 @@ require 'helper'
4
4
 
5
5
  class TestDiagnostic < Minitest::Test
6
6
  def setup
7
- @buffer = Parser::Source::Buffer.new('(string)')
8
- @buffer.source = 'if (this is some bad code + bugs)'
7
+ @buffer = Parser::Source::Buffer.new('(string)',
8
+ source: 'if (this is some bad code + bugs)')
9
9
 
10
10
  @range1 = Parser::Source::Range.new(@buffer, 0, 2) # if
11
11
  @range2 = Parser::Source::Range.new(@buffer, 4, 8) # this
@@ -16,7 +16,7 @@ class TestDiagnostic < Minitest::Test
16
16
  Parser::Diagnostic.new(:foobar, :escape_eof, {}, @range1)
17
17
  end
18
18
 
19
- assert_match /level/, error.message
19
+ assert_match(/level/, error.message)
20
20
  end
21
21
 
22
22
  def test_freezes
@@ -50,8 +50,8 @@ class TestDiagnostic < Minitest::Test
50
50
  end
51
51
 
52
52
  def test_multiline_render
53
- @buffer = Parser::Source::Buffer.new('(string)')
54
- @buffer.source = "abc abc abc\ndef def def\nghi ghi ghi\n"
53
+ @buffer = Parser::Source::Buffer.new('(string)',
54
+ source: "abc abc abc\ndef def def\nghi ghi ghi\n")
55
55
 
56
56
  location = Parser::Source::Range.new(@buffer, 4, 27)
57
57
 
@@ -80,8 +80,7 @@ class TestDiagnostic < Minitest::Test
80
80
  }
81
81
  }
82
82
  CODE
83
- @buffer = Parser::Source::Buffer.new('(string)')
84
- @buffer.source = source
83
+ @buffer = Parser::Source::Buffer.new('(string)', source: source)
85
84
 
86
85
  location = Parser::Source::Range.new(@buffer, 33, 34)
87
86
  diag = Parser::Diagnostic.new(:error, :unexpected_token, { :token => 'tNL' },
@@ -4,9 +4,6 @@ require 'helper'
4
4
 
5
5
  class TestDiagnosticEngine < Minitest::Test
6
6
  def setup
7
- @buffer = Parser::Source::Buffer.new('(source)')
8
- @buffer.source = 'foobar'
9
-
10
7
  @engine = Parser::Diagnostic::Engine.new
11
8
 
12
9
  @queue = []
@@ -14,7 +11,7 @@ class TestDiagnosticEngine < Minitest::Test
14
11
  end
15
12
 
16
13
  def test_process_warnings
17
- warn = Parser::Diagnostic.new(:warning, :invalid_escape, @buffer, 1..2)
14
+ warn = Parser::Diagnostic.new(:warning, :invalid_escape, {}, 1..2)
18
15
  @engine.process(warn)
19
16
 
20
17
  assert_equal [warn], @queue
@@ -23,7 +20,7 @@ class TestDiagnosticEngine < Minitest::Test
23
20
  def test_ignore_warnings
24
21
  @engine.ignore_warnings = true
25
22
 
26
- warn = Parser::Diagnostic.new(:warning, :invalid_escape, @buffer, 1..2)
23
+ warn = Parser::Diagnostic.new(:warning, :invalid_escape, {}, 1..2)
27
24
  @engine.process(warn)
28
25
 
29
26
  assert_equal [], @queue
@@ -32,7 +29,7 @@ class TestDiagnosticEngine < Minitest::Test
32
29
  def test_all_errors_are_fatal
33
30
  @engine.all_errors_are_fatal = true
34
31
 
35
- error = Parser::Diagnostic.new(:error, :invalid_escape, @buffer, 1..2)
32
+ error = Parser::Diagnostic.new(:error, :invalid_escape, {}, 1..2)
36
33
 
37
34
  err = assert_raises Parser::SyntaxError do
38
35
  @engine.process(error)
@@ -44,14 +41,14 @@ class TestDiagnosticEngine < Minitest::Test
44
41
  end
45
42
 
46
43
  def test_all_errors_are_collected
47
- error = Parser::Diagnostic.new(:error, :invalid_escape, @buffer, 1..2)
44
+ error = Parser::Diagnostic.new(:error, :invalid_escape, {}, 1..2)
48
45
  @engine.process(error)
49
46
 
50
47
  assert_equal [error], @queue
51
48
  end
52
49
 
53
50
  def test_fatal_error
54
- fatal = Parser::Diagnostic.new(:fatal, :invalid_escape, @buffer, 1..2)
51
+ fatal = Parser::Diagnostic.new(:fatal, :invalid_escape, {}, 1..2)
55
52
 
56
53
  assert_raises Parser::SyntaxError do
57
54
  @engine.process(fatal)
@@ -37,9 +37,8 @@ class TestLexer < Minitest::Test
37
37
  end
38
38
 
39
39
  def assert_escape(expected, input)
40
- source_buffer = Parser::Source::Buffer.new('(assert_escape)')
41
-
42
- source_buffer.source = "\"\\#{input}\"".encode(input.encoding)
40
+ source_buffer = Parser::Source::Buffer.new('(assert_escape)',
41
+ source: "\"\\#{input}\"".encode(input.encoding))
43
42
 
44
43
  @lex.reset
45
44
  @lex.source_buffer = source_buffer
@@ -71,8 +70,7 @@ class TestLexer < Minitest::Test
71
70
  end
72
71
 
73
72
  def assert_scanned(input, *args)
74
- source_buffer = Parser::Source::Buffer.new('(assert_scanned)')
75
- source_buffer.source = input
73
+ source_buffer = Parser::Source::Buffer.new('(assert_scanned)', source: input)
76
74
 
77
75
  @lex.reset(false)
78
76
  @lex.source_buffer = source_buffer
@@ -3587,8 +3585,7 @@ class TestLexer < Minitest::Test
3587
3585
  @lex.context = Parser::Context.new
3588
3586
  @lex.context.push(:block)
3589
3587
 
3590
- source_buffer = Parser::Source::Buffer.new('(assert_lex_numbered_parameter)')
3591
- source_buffer.source = input
3588
+ source_buffer = Parser::Source::Buffer.new('(assert_lex_numbered_parameter)', source: input)
3592
3589
 
3593
3590
  @lex.source_buffer = source_buffer
3594
3591
 
@@ -3606,7 +3603,7 @@ class TestLexer < Minitest::Test
3606
3603
 
3607
3604
  def refute_scanned_numbered_parameter(input, message = nil)
3608
3605
  err = assert_raises Parser::SyntaxError do
3609
- lex_token, (lex_value, lex_range) = lex_numbered_parameter(input)
3606
+ _lex_token, (_lex_value, _lex_range) = lex_numbered_parameter(input)
3610
3607
  end
3611
3608
 
3612
3609
  if message
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'helper'
4
+
5
+ class TestMeta < Minitest::Test
6
+ def test_NODE_TYPES
7
+ for_each_node do |node|
8
+ assert Parser::Meta::NODE_TYPES.include?(node.type),
9
+ "Type #{node.type} missing from Parser::Meta::NODE_TYPES"
10
+ end
11
+ end
12
+ end
@@ -5360,8 +5360,7 @@ class TestParser < Minitest::Test
5360
5360
 
5361
5361
  def test_crlf_line_endings
5362
5362
  with_versions(ALL_VERSIONS) do |_ver, parser|
5363
- source_file = Parser::Source::Buffer.new('(comments)')
5364
- source_file.source = "\r\nfoo"
5363
+ source_file = Parser::Source::Buffer.new('(comments)', source: "\r\nfoo")
5365
5364
 
5366
5365
  range = lambda do |from, to|
5367
5366
  Parser::Source::Range.new(source_file, from, to)
@@ -5434,8 +5433,7 @@ class TestParser < Minitest::Test
5434
5433
  with_versions(ALL_VERSIONS) do |_ver, parser|
5435
5434
  parser.builder.emit_file_line_as_literals = false
5436
5435
 
5437
- source_file = Parser::Source::Buffer.new('(comments)')
5438
- source_file.source = "[__FILE__, __LINE__]"
5436
+ source_file = Parser::Source::Buffer.new('(comments)', source: "[__FILE__, __LINE__]")
5439
5437
 
5440
5438
  ast = parser.parse(source_file)
5441
5439
 
@@ -5519,8 +5517,7 @@ class TestParser < Minitest::Test
5519
5517
 
5520
5518
  def assert_parses_with_comments(ast_pattern, source, comments_pattern)
5521
5519
  with_versions(ALL_VERSIONS) do |_ver, parser|
5522
- source_file = Parser::Source::Buffer.new('(comments)')
5523
- source_file.source = source
5520
+ source_file = Parser::Source::Buffer.new('(comments)', source: source)
5524
5521
 
5525
5522
  comments_pattern_here = comments_pattern.map do |(from, to)|
5526
5523
  range = Parser::Source::Range.new(source_file, from, to)
@@ -5551,8 +5548,8 @@ class TestParser < Minitest::Test
5551
5548
 
5552
5549
  def test_tokenize
5553
5550
  with_versions(ALL_VERSIONS) do |_ver, parser|
5554
- source_file = Parser::Source::Buffer.new('(tokenize)')
5555
- source_file.source = "1 + # foo\n 2"
5551
+ source_file = Parser::Source::Buffer.new('(tokenize)',
5552
+ source: "1 + # foo\n 2")
5556
5553
 
5557
5554
  range = lambda do |from, to|
5558
5555
  Parser::Source::Range.new(source_file, from, to)
@@ -5578,8 +5575,8 @@ class TestParser < Minitest::Test
5578
5575
 
5579
5576
  def test_tokenize_recover
5580
5577
  with_versions(ALL_VERSIONS) do |_ver, parser|
5581
- source_file = Parser::Source::Buffer.new('(tokenize)')
5582
- source_file.source = "1 + # foo\n "
5578
+ source_file = Parser::Source::Buffer.new('(tokenize)',
5579
+ source: "1 + # foo\n ")
5583
5580
 
5584
5581
  range = lambda do |from, to|
5585
5582
  Parser::Source::Range.new(source_file, from, to)
@@ -9227,8 +9224,7 @@ class TestParser < Minitest::Test
9227
9224
  code = "case 1; #{match_code}; then [#{lvar_names.join(', ')}]; end"
9228
9225
 
9229
9226
  with_versions(versions) do |version, parser|
9230
- source_file = Parser::Source::Buffer.new('(assert_context)')
9231
- source_file.source = code
9227
+ source_file = Parser::Source::Buffer.new('(assert_context)', source: code)
9232
9228
 
9233
9229
  lvar_names.each do |lvar_name|
9234
9230
  refute parser.static_env.declared?(lvar_name),
@@ -9238,7 +9234,7 @@ class TestParser < Minitest::Test
9238
9234
  before = parser.static_env.instance_variable_get(:@variables).to_a
9239
9235
 
9240
9236
  begin
9241
- parsed_ast = parser.parse(source_file)
9237
+ _parsed_ast = parser.parse(source_file)
9242
9238
  rescue Parser::SyntaxError => exc
9243
9239
  backtrace = exc.backtrace
9244
9240
  Exception.instance_method(:initialize).bind(exc).
@@ -9520,6 +9516,81 @@ class TestParser < Minitest::Test
9520
9516
  [:error, :unexpected_token, { :token => 'tEQL' }],
9521
9517
  %Q{def obj.foo = 42},
9522
9518
  %q{ ^ location},
9519
+ SINCE_2_8
9520
+ )
9521
+ end
9522
+
9523
+ def test_endless_method_with_rescue_mod
9524
+ assert_parses(
9525
+ s(:def_e, :m,
9526
+ s(:args),
9527
+ s(:rescue,
9528
+ s(:int, 1),
9529
+ s(:resbody, nil, nil,
9530
+ s(:int, 2)), nil)),
9531
+ %q{def m() = 1 rescue 2},
9532
+ %q{},
9533
+ SINCE_2_8)
9534
+
9535
+ assert_parses(
9536
+ s(:defs_e,
9537
+ s(:self), :m,
9538
+ s(:args),
9539
+ s(:rescue,
9540
+ s(:int, 1),
9541
+ s(:resbody, nil, nil,
9542
+ s(:int, 2)), nil)),
9543
+ %q{def self.m() = 1 rescue 2},
9544
+ %q{},
9545
+ SINCE_2_8)
9546
+ end
9547
+
9548
+ def test_rasgn
9549
+ assert_parses(
9550
+ s(:rasgn,
9551
+ s(:int, 1), s(:lvasgn, :a)),
9552
+ %q{1 => a},
9553
+ %q{~~~~~~ expression
9554
+ | ^^ operator},
9555
+ SINCE_2_8)
9556
+
9557
+ assert_parses(
9558
+ s(:rasgn,
9559
+ s(:send, s(:int, 1), :+, s(:int, 2)),
9560
+ s(:gvasgn, :$a)),
9561
+ %q{1 + 2 => $a},
9562
+ %q{~~~~~~~~~~~ expression
9563
+ | ^^ operator},
9564
+ SINCE_2_8)
9565
+ end
9566
+
9567
+ def test_mrasgn
9568
+ assert_parses(
9569
+ s(:mrasgn,
9570
+ s(:send, s(:int, 13), :divmod, s(:int, 5)),
9571
+ s(:mlhs, s(:lvasgn, :a), s(:lvasgn, :b))),
9572
+ %q{13.divmod(5) => a,b},
9573
+ %q{~~~~~~~~~~~~~~~~~~~ expression
9574
+ | ^^ operator},
9575
+ SINCE_2_8)
9576
+
9577
+ assert_parses(
9578
+ s(:mrasgn,
9579
+ s(:mrasgn,
9580
+ s(:send, s(:int, 13), :divmod, s(:int, 5)),
9581
+ s(:mlhs, s(:lvasgn, :a), s(:lvasgn, :b))),
9582
+ s(:mlhs, s(:lvasgn, :c), s(:lvasgn, :d))),
9583
+ %q{13.divmod(5) => a,b => c, d},
9584
+ %q{~~~~~~~~~~~~~~~~~~~ expression (mrasgn)
9585
+ |~~~~~~~~~~~~~~~~~~~~~~~~~~~ expression},
9586
+ SINCE_2_8)
9587
+ end
9588
+
9589
+ def test_rasgn_line_continuation
9590
+ assert_diagnoses(
9591
+ [:error, :unexpected_token, { :token => 'tASSOC' }],
9592
+ %Q{13.divmod(5)\n=> a,b; [a, b]},
9593
+ %{ ^^ location},
9523
9594
  SINCE_2_8)
9524
9595
  end
9525
9596
  end