parser 2.7.1.2 → 2.7.1.3

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