parser 2.7.0.4 → 2.7.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +18 -29
  4. data/CHANGELOG.md +53 -1
  5. data/README.md +6 -6
  6. data/Rakefile +2 -1
  7. data/doc/AST_FORMAT.md +54 -5
  8. data/lib/parser.rb +1 -0
  9. data/lib/parser/all.rb +1 -0
  10. data/lib/parser/ast/processor.rb +7 -0
  11. data/lib/parser/builders/default.rb +63 -14
  12. data/lib/parser/current.rb +13 -4
  13. data/lib/parser/diagnostic.rb +1 -1
  14. data/lib/parser/diagnostic/engine.rb +1 -2
  15. data/lib/parser/lexer.rl +7 -0
  16. data/lib/parser/messages.rb +15 -0
  17. data/lib/parser/meta.rb +2 -2
  18. data/lib/parser/ruby27.y +16 -5
  19. data/lib/parser/ruby28.y +3016 -0
  20. data/lib/parser/runner.rb +26 -2
  21. data/lib/parser/runner/ruby_rewrite.rb +2 -2
  22. data/lib/parser/source/buffer.rb +3 -1
  23. data/lib/parser/source/comment/associator.rb +14 -4
  24. data/lib/parser/source/map/endless_definition.rb +23 -0
  25. data/lib/parser/source/range.rb +16 -0
  26. data/lib/parser/source/tree_rewriter.rb +49 -12
  27. data/lib/parser/source/tree_rewriter/action.rb +96 -26
  28. data/lib/parser/tree_rewriter.rb +1 -2
  29. data/lib/parser/version.rb +1 -1
  30. data/parser.gemspec +2 -1
  31. data/test/helper.rb +25 -6
  32. data/test/parse_helper.rb +11 -17
  33. data/test/test_ast_processor.rb +32 -0
  34. data/test/test_base.rb +1 -1
  35. data/test/test_current.rb +2 -0
  36. data/test/test_diagnostic.rb +6 -7
  37. data/test/test_diagnostic_engine.rb +5 -8
  38. data/test/test_lexer.rb +17 -8
  39. data/test/test_meta.rb +12 -0
  40. data/test/test_parser.rb +260 -21
  41. data/test/test_runner_parse.rb +22 -1
  42. data/test/test_runner_rewrite.rb +1 -1
  43. data/test/test_source_buffer.rb +4 -1
  44. data/test/test_source_comment.rb +2 -2
  45. data/test/test_source_comment_associator.rb +47 -15
  46. data/test/test_source_map.rb +1 -2
  47. data/test/test_source_range.rb +29 -9
  48. data/test/test_source_rewriter.rb +4 -4
  49. data/test/test_source_rewriter_action.rb +2 -2
  50. data/test/test_source_tree_rewriter.rb +96 -6
  51. metadata +14 -7
@@ -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.0.4'
4
+ VERSION = '2.7.1.3'
5
5
  end
@@ -32,6 +32,7 @@ Gem::Specification.new do |spec|
32
32
  lib/parser/ruby25.rb
33
33
  lib/parser/ruby26.rb
34
34
  lib/parser/ruby27.rb
35
+ lib/parser/ruby28.rb
35
36
  lib/parser/macruby.rb
36
37
  lib/parser/rubymotion.rb
37
38
  )
@@ -44,7 +45,7 @@ Gem::Specification.new do |spec|
44
45
  spec.add_dependency 'ast', '~> 2.4.0'
45
46
 
46
47
  spec.add_development_dependency 'bundler', '>= 1.15', '< 3.0.0'
47
- spec.add_development_dependency 'rake', '~> 10.0'
48
+ spec.add_development_dependency 'rake', '~> 13.0.1'
48
49
  spec.add_development_dependency 'racc', '= 1.4.15'
49
50
  spec.add_development_dependency 'cliver', '~> 0.3.2'
50
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?
@@ -20,6 +18,7 @@ if ENV.include?('COVERAGE') && SimpleCov.usable?
20
18
  ruby25.y
21
19
  ruby26.y
22
20
  ruby27.y
21
+ ruby28.y
23
22
  ),
24
23
  File.expand_path('../../lib/parser', __FILE__))
25
24
 
@@ -27,9 +26,9 @@ if ENV.include?('COVERAGE') && SimpleCov.usable?
27
26
  at_exit { RaccCoverage.stop }
28
27
 
29
28
  SimpleCov.start do
30
- self.formatter = SimpleCov::Formatter::MultiFormatter[
31
- SimpleCov::Formatter::HTMLFormatter,
32
- ]
29
+ self.formatter = SimpleCov::Formatter::MultiFormatter.new(
30
+ SimpleCov::Formatter::HTMLFormatter
31
+ )
33
32
 
34
33
  add_group 'Grammars' do |source_file|
35
34
  source_file.filename =~ %r{\.y$}
@@ -52,9 +51,29 @@ require 'minitest/autorun'
52
51
  $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
53
52
  require 'parser'
54
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
+
55
74
  class Parser::AST::Node
56
75
  def initialize(type, *)
57
- raise "Type #{type} missing from Parser::Meta::NODE_TYPES" unless Parser::Meta::NODE_TYPES.include?(type)
76
+ NodeCollector.nodes << self
58
77
  super
59
78
  end
60
79
  end
@@ -7,7 +7,7 @@ module ParseHelper
7
7
  require 'parser/macruby'
8
8
  require 'parser/rubymotion'
9
9
 
10
- ALL_VERSIONS = %w(1.8 1.9 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 mac ios)
10
+ ALL_VERSIONS = %w(1.8 1.9 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 mac ios)
11
11
 
12
12
  def setup
13
13
  @diagnostics = []
@@ -27,6 +27,7 @@ module ParseHelper
27
27
  when '2.5' then parser = Parser::Ruby25.new
28
28
  when '2.6' then parser = Parser::Ruby26.new
29
29
  when '2.7' then parser = Parser::Ruby27.new
30
+ when '2.8' then parser = Parser::Ruby28.new
30
31
  when 'mac' then parser = Parser::MacRuby.new
31
32
  when 'ios' then parser = Parser::RubyMotion.new
32
33
  else raise "Unrecognized Ruby version #{version}"
@@ -84,8 +85,7 @@ module ParseHelper
84
85
  end
85
86
 
86
87
  def try_parsing(ast, code, parser, source_maps, version)
87
- source_file = Parser::Source::Buffer.new('(assert_parses)')
88
- source_file.source = code
88
+ source_file = Parser::Source::Buffer.new('(assert_parses)', source: code)
89
89
 
90
90
  begin
91
91
  parsed_ast = parser.parse(source_file)
@@ -105,8 +105,7 @@ module ParseHelper
105
105
  assert_equal ast, parsed_ast,
106
106
  "(#{version}) AST equality"
107
107
 
108
- parse_source_map_descriptions(source_maps) \
109
- 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|
110
109
 
111
110
  astlet = traverse_ast(parsed_ast, ast_path)
112
111
 
@@ -142,8 +141,7 @@ module ParseHelper
142
141
  # ~~~
143
142
  def assert_diagnoses(diagnostic, code, source_maps='', versions=ALL_VERSIONS)
144
143
  with_versions(versions) do |version, parser|
145
- source_file = Parser::Source::Buffer.new('(assert_diagnoses)')
146
- source_file.source = code
144
+ source_file = Parser::Source::Buffer.new('(assert_diagnoses)', source: code)
147
145
 
148
146
  begin
149
147
  parser = parser.parse(source_file)
@@ -159,15 +157,14 @@ module ParseHelper
159
157
 
160
158
  level, reason, arguments = diagnostic
161
159
  arguments ||= {}
162
- message = Parser::MESSAGES[reason] % arguments
160
+ message = Parser::Messages.compile(reason, arguments)
163
161
 
164
162
  assert_equal level, emitted_diagnostic.level
165
163
  assert_equal reason, emitted_diagnostic.reason
166
164
  assert_equal arguments, emitted_diagnostic.arguments
167
165
  assert_equal message, emitted_diagnostic.message
168
166
 
169
- parse_source_map_descriptions(source_maps) \
170
- 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|
171
168
 
172
169
  case map_field
173
170
  when 'location'
@@ -201,8 +198,7 @@ module ParseHelper
201
198
  # ~~~
202
199
  def assert_diagnoses_many(diagnostics, code, versions=ALL_VERSIONS)
203
200
  with_versions(versions) do |version, parser|
204
- source_file = Parser::Source::Buffer.new('(assert_diagnoses_many)')
205
- source_file.source = code
201
+ source_file = Parser::Source::Buffer.new('(assert_diagnoses_many)', source: code)
206
202
 
207
203
  begin
208
204
  parser = parser.parse(source_file)
@@ -215,7 +211,7 @@ module ParseHelper
215
211
  diagnostics.zip(@diagnostics) do |expected_diagnostic, actual_diagnostic|
216
212
  level, reason, arguments = expected_diagnostic
217
213
  arguments ||= {}
218
- message = Parser::MESSAGES[reason] % arguments
214
+ message = Parser::Messages.compile(reason, arguments)
219
215
 
220
216
  assert_equal level, actual_diagnostic.level
221
217
  assert_equal reason, actual_diagnostic.reason
@@ -227,8 +223,7 @@ module ParseHelper
227
223
 
228
224
  def refute_diagnoses(code, versions=ALL_VERSIONS)
229
225
  with_versions(versions) do |version, parser|
230
- source_file = Parser::Source::Buffer.new('(refute_diagnoses)')
231
- source_file.source = code
226
+ source_file = Parser::Source::Buffer.new('(refute_diagnoses)', source: code)
232
227
 
233
228
  begin
234
229
  parser = parser.parse(source_file)
@@ -244,8 +239,7 @@ module ParseHelper
244
239
 
245
240
  def assert_context(context, code, versions=ALL_VERSIONS)
246
241
  with_versions(versions) do |version, parser|
247
- source_file = Parser::Source::Buffer.new('(assert_context)')
248
- source_file.source = code
242
+ source_file = Parser::Source::Buffer.new('(assert_context)', source: code)
249
243
 
250
244
  begin
251
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
@@ -22,6 +22,8 @@ class TestCurrent < Minitest::Test
22
22
  assert_equal Parser::Ruby26, Parser::CurrentRuby
23
23
  when /^2\.7\.\d+/
24
24
  assert_equal Parser::Ruby27, Parser::CurrentRuby
25
+ when /^2\.8\.\d+/
26
+ assert_equal Parser::Ruby28, Parser::CurrentRuby
25
27
  else
26
28
  flunk "Update test_current for #{RUBY_VERSION}"
27
29
  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
@@ -3569,14 +3567,25 @@ class TestLexer < Minitest::Test
3569
3567
  :tIDENTIFIER, 're', [1, 3])
3570
3568
  end
3571
3569
 
3570
+ def test_endless_method
3571
+ setup_lexer(28)
3572
+
3573
+ assert_scanned('def foo() = 42',
3574
+ :kDEF, "def", [0, 3],
3575
+ :tIDENTIFIER, "foo", [4, 7],
3576
+ :tLPAREN2, "(", [7, 8],
3577
+ :tRPAREN, ")", [8, 9],
3578
+ :tEQL, "=", [10, 11],
3579
+ :tINTEGER, 42, [12, 14])
3580
+ end
3581
+
3572
3582
  def lex_numbered_parameter(input)
3573
3583
  @lex.max_numparam_stack.push
3574
3584
 
3575
3585
  @lex.context = Parser::Context.new
3576
3586
  @lex.context.push(:block)
3577
3587
 
3578
- source_buffer = Parser::Source::Buffer.new('(assert_lex_numbered_parameter)')
3579
- source_buffer.source = input
3588
+ source_buffer = Parser::Source::Buffer.new('(assert_lex_numbered_parameter)', source: input)
3580
3589
 
3581
3590
  @lex.source_buffer = source_buffer
3582
3591
 
@@ -3594,7 +3603,7 @@ class TestLexer < Minitest::Test
3594
3603
 
3595
3604
  def refute_scanned_numbered_parameter(input, message = nil)
3596
3605
  err = assert_raises Parser::SyntaxError do
3597
- lex_token, (lex_value, lex_range) = lex_numbered_parameter(input)
3606
+ _lex_token, (_lex_value, _lex_range) = lex_numbered_parameter(input)
3598
3607
  end
3599
3608
 
3600
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
@@ -29,6 +29,7 @@ class TestParser < Minitest::Test
29
29
  SINCE_2_5 = SINCE_2_4 - %w(2.4)
30
30
  SINCE_2_6 = SINCE_2_5 - %w(2.5)
31
31
  SINCE_2_7 = SINCE_2_6 - %w(2.6)
32
+ SINCE_2_8 = SINCE_2_7 - %w(2.7)
32
33
 
33
34
  # Guidelines for test naming:
34
35
  # * Test structure follows structure of AST_FORMAT.md.
@@ -5359,8 +5360,7 @@ class TestParser < Minitest::Test
5359
5360
 
5360
5361
  def test_crlf_line_endings
5361
5362
  with_versions(ALL_VERSIONS) do |_ver, parser|
5362
- source_file = Parser::Source::Buffer.new('(comments)')
5363
- source_file.source = "\r\nfoo"
5363
+ source_file = Parser::Source::Buffer.new('(comments)', source: "\r\nfoo")
5364
5364
 
5365
5365
  range = lambda do |from, to|
5366
5366
  Parser::Source::Range.new(source_file, from, to)
@@ -5433,8 +5433,7 @@ class TestParser < Minitest::Test
5433
5433
  with_versions(ALL_VERSIONS) do |_ver, parser|
5434
5434
  parser.builder.emit_file_line_as_literals = false
5435
5435
 
5436
- source_file = Parser::Source::Buffer.new('(comments)')
5437
- source_file.source = "[__FILE__, __LINE__]"
5436
+ source_file = Parser::Source::Buffer.new('(comments)', source: "[__FILE__, __LINE__]")
5438
5437
 
5439
5438
  ast = parser.parse(source_file)
5440
5439
 
@@ -5518,8 +5517,7 @@ class TestParser < Minitest::Test
5518
5517
 
5519
5518
  def assert_parses_with_comments(ast_pattern, source, comments_pattern)
5520
5519
  with_versions(ALL_VERSIONS) do |_ver, parser|
5521
- source_file = Parser::Source::Buffer.new('(comments)')
5522
- source_file.source = source
5520
+ source_file = Parser::Source::Buffer.new('(comments)', source: source)
5523
5521
 
5524
5522
  comments_pattern_here = comments_pattern.map do |(from, to)|
5525
5523
  range = Parser::Source::Range.new(source_file, from, to)
@@ -5550,8 +5548,8 @@ class TestParser < Minitest::Test
5550
5548
 
5551
5549
  def test_tokenize
5552
5550
  with_versions(ALL_VERSIONS) do |_ver, parser|
5553
- source_file = Parser::Source::Buffer.new('(tokenize)')
5554
- source_file.source = "1 + # foo\n 2"
5551
+ source_file = Parser::Source::Buffer.new('(tokenize)',
5552
+ source: "1 + # foo\n 2")
5555
5553
 
5556
5554
  range = lambda do |from, to|
5557
5555
  Parser::Source::Range.new(source_file, from, to)
@@ -5577,8 +5575,8 @@ class TestParser < Minitest::Test
5577
5575
 
5578
5576
  def test_tokenize_recover
5579
5577
  with_versions(ALL_VERSIONS) do |_ver, parser|
5580
- source_file = Parser::Source::Buffer.new('(tokenize)')
5581
- source_file.source = "1 + # foo\n "
5578
+ source_file = Parser::Source::Buffer.new('(tokenize)',
5579
+ source: "1 + # foo\n ")
5582
5580
 
5583
5581
  range = lambda do |from, to|
5584
5582
  Parser::Source::Range.new(source_file, from, to)
@@ -8422,7 +8420,7 @@ class TestParser < Minitest::Test
8422
8420
  nil,
8423
8421
  s(:nil)),
8424
8422
  %q{in x, then nil},
8425
- %q{ ~ expression (in_pattern.array_pattern_with_tail)}
8423
+ %q{ ~~ expression (in_pattern.array_pattern_with_tail)}
8426
8424
  )
8427
8425
 
8428
8426
  assert_parses_pattern_match(
@@ -8468,7 +8466,7 @@ class TestParser < Minitest::Test
8468
8466
  nil,
8469
8467
  s(:nil)),
8470
8468
  %q{in x, y, then nil},
8471
- %q{ ~~~~ expression (in_pattern.array_pattern_with_tail)}
8469
+ %q{ ~~~~~ expression (in_pattern.array_pattern_with_tail)}
8472
8470
  )
8473
8471
 
8474
8472
  assert_parses_pattern_match(
@@ -8675,6 +8673,18 @@ class TestParser < Minitest::Test
8675
8673
  | ~ end (in_pattern.hash_pattern)}
8676
8674
  )
8677
8675
 
8676
+ assert_parses_pattern_match(
8677
+ s(:in_pattern,
8678
+ s(:hash_pattern,
8679
+ s(:pair, s(:sym, :a), s(:int, 1))),
8680
+ nil,
8681
+ s(:true)),
8682
+ %q{in { a: 1, } then true},
8683
+ %q{ ~~~~~~~~~ expression (in_pattern.hash_pattern)
8684
+ | ~ begin (in_pattern.hash_pattern)
8685
+ | ~ end (in_pattern.hash_pattern)}
8686
+ )
8687
+
8678
8688
  assert_parses_pattern_match(
8679
8689
  s(:in_pattern,
8680
8690
  s(:hash_pattern,
@@ -8746,6 +8756,67 @@ class TestParser < Minitest::Test
8746
8756
  %q{in a: 1, _a:, ** then true},
8747
8757
  %q{ ~~~~~~~~~~~~~ expression (in_pattern.hash_pattern)}
8748
8758
  )
8759
+
8760
+ assert_parses_pattern_match(
8761
+ s(:in_pattern,
8762
+ s(:hash_pattern,
8763
+ s(:pair,
8764
+ s(:sym, :a),
8765
+ s(:int, 1))), nil,
8766
+ s(:false)),
8767
+ %q{
8768
+ in {a: 1
8769
+ }
8770
+ false
8771
+ },
8772
+ %q{}
8773
+ )
8774
+
8775
+
8776
+ assert_parses_pattern_match(
8777
+ s(:in_pattern,
8778
+ s(:hash_pattern,
8779
+ s(:pair,
8780
+ s(:sym, :a),
8781
+ s(:int, 2))), nil,
8782
+ s(:false)),
8783
+ %q{
8784
+ in {a:
8785
+ 2}
8786
+ false
8787
+ },
8788
+ %q{}
8789
+ )
8790
+
8791
+ assert_parses_pattern_match(
8792
+ s(:in_pattern,
8793
+ s(:hash_pattern,
8794
+ s(:pair,
8795
+ s(:sym, :a),
8796
+ s(:hash_pattern,
8797
+ s(:match_var, :b))),
8798
+ s(:match_var, :c)), nil,
8799
+ s(:send, nil, :p,
8800
+ s(:lvar, :c))),
8801
+ %q{
8802
+ in a: {b:}, c:
8803
+ p c
8804
+ },
8805
+ %q{}
8806
+ )
8807
+
8808
+ assert_parses_pattern_match(
8809
+ s(:in_pattern,
8810
+ s(:hash_pattern,
8811
+ s(:match_var, :a)), nil,
8812
+ s(:true)),
8813
+ %q{
8814
+ in {a:
8815
+ }
8816
+ true
8817
+ },
8818
+ %q{}
8819
+ )
8749
8820
  end
8750
8821
 
8751
8822
  def test_pattern_matching_hash_with_string_keys
@@ -8860,6 +8931,22 @@ class TestParser < Minitest::Test
8860
8931
  %q{ ~~~~~~~ location},
8861
8932
  SINCE_2_7
8862
8933
  )
8934
+
8935
+ assert_diagnoses(
8936
+ [:error, :pm_interp_in_var_name],
8937
+ %q{case a; in "#{a}": 1; end},
8938
+ %q{ ~~~~~~~ location},
8939
+ SINCE_2_7
8940
+ )
8941
+ end
8942
+
8943
+ def test_pattern_matching_invalid_lvar_name
8944
+ assert_diagnoses(
8945
+ [:error, :lvar_name, { name: :a? }],
8946
+ %q{case a; in a?:; end},
8947
+ %q{ ~~ location},
8948
+ SINCE_2_7
8949
+ )
8863
8950
  end
8864
8951
 
8865
8952
  def test_pattern_matching_keyword_variable
@@ -8993,7 +9080,7 @@ class TestParser < Minitest::Test
8993
9080
  nil,
8994
9081
  s(:true)),
8995
9082
  %q{in A(1, 2) then true},
8996
- %q{ ~~~~~~ expression (in_pattern.const_pattern)
9083
+ %q{ ~~~~~~~ expression (in_pattern.const_pattern)
8997
9084
  | ~ begin (in_pattern.const_pattern)
8998
9085
  | ~ end (in_pattern.const_pattern)
8999
9086
  | ~ expression (in_pattern.const_pattern.const)
@@ -9009,7 +9096,7 @@ class TestParser < Minitest::Test
9009
9096
  nil,
9010
9097
  s(:true)),
9011
9098
  %q{in A(x:) then true},
9012
- %q{ ~~~~ expression (in_pattern.const_pattern)
9099
+ %q{ ~~~~~ expression (in_pattern.const_pattern)
9013
9100
  | ~ begin (in_pattern.const_pattern)
9014
9101
  | ~ end (in_pattern.const_pattern)
9015
9102
  | ~ expression (in_pattern.const_pattern.const)
@@ -9024,7 +9111,7 @@ class TestParser < Minitest::Test
9024
9111
  nil,
9025
9112
  s(:true)),
9026
9113
  %q{in A() then true},
9027
- %q{ ~~ expression (in_pattern.const_pattern)
9114
+ %q{ ~~~ expression (in_pattern.const_pattern)
9028
9115
  | ~ begin (in_pattern.const_pattern)
9029
9116
  | ~ end (in_pattern.const_pattern)
9030
9117
  | ~ expression (in_pattern.const_pattern.const)
@@ -9041,7 +9128,7 @@ class TestParser < Minitest::Test
9041
9128
  nil,
9042
9129
  s(:true)),
9043
9130
  %q{in A[1, 2] then true},
9044
- %q{ ~~~~~~ expression (in_pattern.const_pattern)
9131
+ %q{ ~~~~~~~ expression (in_pattern.const_pattern)
9045
9132
  | ~ begin (in_pattern.const_pattern)
9046
9133
  | ~ end (in_pattern.const_pattern)
9047
9134
  | ~ expression (in_pattern.const_pattern.const)
@@ -9057,7 +9144,7 @@ class TestParser < Minitest::Test
9057
9144
  nil,
9058
9145
  s(:true)),
9059
9146
  %q{in A[x:] then true},
9060
- %q{ ~~~~ expression (in_pattern.const_pattern)
9147
+ %q{ ~~~~~ expression (in_pattern.const_pattern)
9061
9148
  | ~ begin (in_pattern.const_pattern)
9062
9149
  | ~ end (in_pattern.const_pattern)
9063
9150
  | ~ expression (in_pattern.const_pattern.const)
@@ -9072,7 +9159,7 @@ class TestParser < Minitest::Test
9072
9159
  nil,
9073
9160
  s(:true)),
9074
9161
  %q{in A[] then true},
9075
- %q{ ~~ expression (in_pattern.const_pattern)
9162
+ %q{ ~~~ expression (in_pattern.const_pattern)
9076
9163
  | ~ begin (in_pattern.const_pattern)
9077
9164
  | ~ end (in_pattern.const_pattern)
9078
9165
  | ~~ expression (in_pattern.const_pattern.array_pattern)}
@@ -9137,8 +9224,7 @@ class TestParser < Minitest::Test
9137
9224
  code = "case 1; #{match_code}; then [#{lvar_names.join(', ')}]; end"
9138
9225
 
9139
9226
  with_versions(versions) do |version, parser|
9140
- source_file = Parser::Source::Buffer.new('(assert_context)')
9141
- source_file.source = code
9227
+ source_file = Parser::Source::Buffer.new('(assert_context)', source: code)
9142
9228
 
9143
9229
  lvar_names.each do |lvar_name|
9144
9230
  refute parser.static_env.declared?(lvar_name),
@@ -9148,7 +9234,7 @@ class TestParser < Minitest::Test
9148
9234
  before = parser.static_env.instance_variable_get(:@variables).to_a
9149
9235
 
9150
9236
  begin
9151
- parsed_ast = parser.parse(source_file)
9237
+ _parsed_ast = parser.parse(source_file)
9152
9238
  rescue Parser::SyntaxError => exc
9153
9239
  backtrace = exc.backtrace
9154
9240
  Exception.instance_method(:initialize).bind(exc).
@@ -9354,4 +9440,157 @@ class TestParser < Minitest::Test
9354
9440
  %{},
9355
9441
  SINCE_1_9)
9356
9442
  end
9443
+
9444
+ def test_endless_method
9445
+ assert_parses(
9446
+ s(:def_e, :foo,
9447
+ s(:args),
9448
+ s(:int, 42)),
9449
+ %q{def foo() = 42},
9450
+ %q{~~~ keyword
9451
+ | ~~~ name
9452
+ | ^ assignment
9453
+ |~~~~~~~~~~~~~~ expression},
9454
+ SINCE_2_8)
9455
+
9456
+ assert_parses(
9457
+ s(:def_e, :inc,
9458
+ s(:args, s(:arg, :x)),
9459
+ s(:send,
9460
+ s(:lvar, :x), :+,
9461
+ s(:int, 1))),
9462
+ %q{def inc(x) = x + 1},
9463
+ %q{~~~ keyword
9464
+ | ~~~ name
9465
+ | ^ assignment
9466
+ |~~~~~~~~~~~~~~~~~~ expression},
9467
+ SINCE_2_8)
9468
+
9469
+ assert_parses(
9470
+ s(:defs_e, s(:send, nil, :obj), :foo,
9471
+ s(:args),
9472
+ s(:int, 42)),
9473
+ %q{def obj.foo() = 42},
9474
+ %q{~~~ keyword
9475
+ | ^ operator
9476
+ | ~~~ name
9477
+ | ^ assignment
9478
+ |~~~~~~~~~~~~~~~~~~ expression},
9479
+ SINCE_2_8)
9480
+
9481
+ assert_parses(
9482
+ s(:defs_e, s(:send, nil, :obj), :inc,
9483
+ s(:args, s(:arg, :x)),
9484
+ s(:send,
9485
+ s(:lvar, :x), :+,
9486
+ s(:int, 1))),
9487
+ %q{def obj.inc(x) = x + 1},
9488
+ %q{~~~ keyword
9489
+ | ~~~ name
9490
+ | ^ operator
9491
+ | ^ assignment
9492
+ |~~~~~~~~~~~~~~~~~~~~~~ expression},
9493
+ SINCE_2_8)
9494
+
9495
+ assert_parses(
9496
+ s(:def_e, :foo,
9497
+ s(:forward_args),
9498
+ s(:send, nil, :bar,
9499
+ s(:forwarded_args))),
9500
+ %q{def foo(...) = bar(...)},
9501
+ %q{~~~ keyword
9502
+ | ~~~ name
9503
+ | ^ assignment
9504
+ |~~~~~~~~~~~~~~~~~~~~~~~ expression},
9505
+ SINCE_2_8)
9506
+ end
9507
+
9508
+ def test_endless_method_without_brackets
9509
+ assert_diagnoses(
9510
+ [:error, :unexpected_token, { :token => 'tEQL' }],
9511
+ %Q{def foo = 42},
9512
+ %q{ ^ location},
9513
+ SINCE_2_8)
9514
+
9515
+ assert_diagnoses(
9516
+ [:error, :unexpected_token, { :token => 'tEQL' }],
9517
+ %Q{def obj.foo = 42},
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},
9594
+ SINCE_2_8)
9595
+ end
9357
9596
  end