parser 2.7.0.4 → 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 (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