parser 2.7.0.3 → 2.7.1.2

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.
@@ -111,6 +111,11 @@ module Parser
111
111
  @parser_class = Parser::Ruby27
112
112
  end
113
113
 
114
+ opts.on '--28', 'Parse as Ruby 2.8 would' do
115
+ require 'parser/ruby28'
116
+ @parser_class = Parser::Ruby28
117
+ end
118
+
114
119
  opts.on '--mac', 'Parse as MacRuby 0.12 would' do
115
120
  require 'parser/macruby'
116
121
  @parser_class = Parser::MacRuby
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Parser
4
+ module Source
5
+
6
+ class Map::EndlessDefinition < Map
7
+ attr_reader :keyword
8
+ attr_reader :operator
9
+ attr_reader :name
10
+ attr_reader :assignment
11
+
12
+ def initialize(keyword_l, operator_l, name_l, assignment_l, body_l)
13
+ @keyword = keyword_l
14
+ @operator = operator_l
15
+ @name = name_l
16
+ @assignment = assignment_l
17
+
18
+ super(@keyword.join(body_l))
19
+ end
20
+ end
21
+
22
+ end
23
+ end
@@ -298,6 +298,15 @@ module Parser
298
298
  (@end_pos <=> other.end_pos)
299
299
  end
300
300
 
301
+ alias_method :eql?, :==
302
+
303
+ ##
304
+ # Support for Ranges be used in as Hash indices and in Sets.
305
+ #
306
+ def hash
307
+ [@source_buffer, @begin_pos, @end_pos].hash
308
+ end
309
+
301
310
  ##
302
311
  # @return [String] a human-readable representation of this range.
303
312
  #
@@ -117,6 +117,43 @@ module Parser
117
117
  @action_root = TreeRewriter::Action.new(all_encompassing_range, @enforcer)
118
118
  end
119
119
 
120
+ ##
121
+ # Returns true iff no (non trivial) update has been recorded
122
+ #
123
+ # @return [Boolean]
124
+ #
125
+ def empty?
126
+ @action_root.empty?
127
+ end
128
+
129
+ ##
130
+ # Merges the updates of argument with the receiver.
131
+ # Policies of the receiver are used.
132
+ #
133
+ # @param [Rewriter] with
134
+ # @return [Rewriter] self
135
+ # @raise [ClobberingError] when clobbering is detected
136
+ #
137
+ def merge!(with)
138
+ raise 'TreeRewriter are not for the same source_buffer' unless
139
+ source_buffer == with.source_buffer
140
+
141
+ @action_root = @action_root.combine(with.action_root)
142
+ self
143
+ end
144
+
145
+ ##
146
+ # Returns a new rewriter that consists of the updates of the received
147
+ # and the given argument. Policies of the receiver are used.
148
+ #
149
+ # @param [Rewriter] with
150
+ # @return [Rewriter] merge of receiver and argument
151
+ # @raise [ClobberingError] when clobbering is detected
152
+ #
153
+ def merge(with)
154
+ dup.merge!(with)
155
+ end
156
+
120
157
  ##
121
158
  # Replaces the code of the source range `range` with `content`.
122
159
  #
@@ -184,29 +221,25 @@ module Parser
184
221
  #
185
222
  # @return [String]
186
223
  #
187
- def process
188
- source = @source_buffer.source.dup
189
- adjustment = 0
224
+ def process
225
+ source = @source_buffer.source
190
226
 
227
+ chunks = []
228
+ last_end = 0
191
229
  @action_root.ordered_replacements.each do |range, replacement|
192
- begin_pos = range.begin_pos + adjustment
193
- end_pos = begin_pos + range.length
194
-
195
- source[begin_pos...end_pos] = replacement
196
-
197
- adjustment += replacement.length - range.length
230
+ chunks << source[last_end...range.begin_pos] << replacement
231
+ last_end = range.end_pos
198
232
  end
199
-
200
- source
233
+ chunks << source[last_end...source.length]
234
+ chunks.join
201
235
  end
202
236
 
203
237
  ##
204
238
  # Provides a protected block where a sequence of multiple rewrite actions
205
239
  # are handled atomically. If any of the actions failed by clobbering,
206
- # all the actions are rolled back.
240
+ # all the actions are rolled back. Transactions can be nested.
207
241
  #
208
242
  # @raise [RuntimeError] when no block is passed
209
- # @raise [RuntimeError] when already in a transaction
210
243
  #
211
244
  def transaction
212
245
  unless block_given?
@@ -256,6 +289,10 @@ module Parser
256
289
 
257
290
  extend Deprecation
258
291
 
292
+ protected
293
+
294
+ attr_reader :action_root
295
+
259
296
  private
260
297
 
261
298
  ACTIONS = %i[accept warn raise].freeze
@@ -7,7 +7,7 @@ module Parser
7
7
  #
8
8
  # Actions are arranged in a tree and get combined so that:
9
9
  # children are strictly contained by their parent
10
- # sibblings all disjoint from one another
10
+ # sibblings all disjoint from one another and ordered
11
11
  # only actions with replacement==nil may have children
12
12
  #
13
13
  class TreeRewriter::Action
@@ -25,17 +25,23 @@ module Parser
25
25
  freeze
26
26
  end
27
27
 
28
- # Assumes action.children.empty?
29
28
  def combine(action)
30
- return self unless action.insertion? || action.replacement # Ignore empty action
29
+ return self if action.empty? # Ignore empty action
31
30
  do_combine(action)
32
31
  end
33
32
 
33
+ def empty?
34
+ @insert_before.empty? &&
35
+ @insert_after.empty? &&
36
+ @children.empty? &&
37
+ (@replacement == nil || (@replacement.empty? && @range.empty?))
38
+ end
39
+
34
40
  def ordered_replacements
35
41
  reps = []
36
42
  reps << [@range.begin, @insert_before] unless @insert_before.empty?
37
43
  reps << [@range, @replacement] if @replacement
38
- reps.concat(@children.sort_by(&:range).flat_map(&:ordered_replacements))
44
+ reps.concat(@children.flat_map(&:ordered_replacements))
39
45
  reps << [@range.end, @insert_after] unless @insert_after.empty?
40
46
  reps
41
47
  end
@@ -46,9 +52,11 @@ module Parser
46
52
 
47
53
  protected
48
54
 
49
- def with(range: @range, children: @children, insert_before: @insert_before, replacement: @replacement, insert_after: @insert_after)
55
+ attr_reader :children
56
+
57
+ def with(range: @range, enforcer: @enforcer, children: @children, insert_before: @insert_before, replacement: @replacement, insert_after: @insert_after)
50
58
  children = swallow(children) if replacement
51
- self.class.new(range, @enforcer, children: children, insert_before: insert_before, replacement: replacement, insert_after: insert_after)
59
+ self.class.new(range, enforcer, children: children, insert_before: insert_before, replacement: replacement, insert_after: insert_after)
52
60
  end
53
61
 
54
62
  # Assumes range.contains?(action.range) && action.children.empty?
@@ -61,19 +69,27 @@ module Parser
61
69
  end
62
70
 
63
71
  def place_in_hierarchy(action)
64
- family = @children.group_by { |child| child.relationship_with(action) }
72
+ family = analyse_hierarchy(action)
65
73
 
66
74
  if family[:fusible]
67
- fuse_deletions(action, family[:fusible], [*family[:sibbling], *family[:child]])
75
+ fuse_deletions(action, family[:fusible], [*family[:sibbling_left], *family[:child], *family[:sibbling_right]])
68
76
  else
69
77
  extra_sibbling = if family[:parent] # action should be a descendant of one of the children
70
- family[:parent][0].do_combine(action)
78
+ family[:parent].do_combine(action)
71
79
  elsif family[:child] # or it should become the parent of some of the children,
72
- action.with(children: family[:child])
80
+ action.with(children: family[:child], enforcer: @enforcer)
81
+ .combine_children(action.children)
73
82
  else # or else it should become an additional child
74
83
  action
75
84
  end
76
- with(children: [*family[:sibbling], extra_sibbling])
85
+ with(children: [*family[:sibbling_left], extra_sibbling, *family[:sibbling_right]])
86
+ end
87
+ end
88
+
89
+ # Assumes `more_children` all contained within `@range`
90
+ def combine_children(more_children)
91
+ more_children.inject(self) do |parent, new_child|
92
+ parent.place_in_hierarchy(new_child)
77
93
  end
78
94
  end
79
95
 
@@ -84,22 +100,76 @@ module Parser
84
100
  without_fusible.do_combine(fused_deletion)
85
101
  end
86
102
 
87
- # Returns what relationship self should have with `action`; either of
88
- # :sibbling, :parent, :child, :fusible or raises a CloberingError
89
- # In case of equal range, returns :parent
90
- def relationship_with(action)
91
- if action.range == @range || @range.contains?(action.range)
92
- :parent
93
- elsif @range.contained?(action.range)
94
- :child
95
- elsif @range.disjoint?(action.range)
96
- :sibbling
97
- elsif !action.insertion? && !insertion?
98
- @enforcer.call(:crossing_deletions) { {range: action.range, conflict: @range} }
99
- :fusible
103
+ # Similar to @children.bsearch_index || size
104
+ # except allows for a starting point
105
+ # and `bsearch_index` is only Ruby 2.3+
106
+ def bsearch_child_index(from = 0)
107
+ size = @children.size
108
+ (from...size).bsearch { |i| yield @children[i] } || size
109
+ end
110
+
111
+ # Returns the children in a hierarchy with respect to `action`:
112
+ # :sibbling_left, sibbling_right (for those that are disjoint from `action`)
113
+ # :parent (in case one of our children contains `action`)
114
+ # :child (in case `action` strictly contains some of our children)
115
+ # :fusible (in case `action` overlaps some children but they can be fused in one deletion)
116
+ # or raises a `CloberingError`
117
+ # In case a child has equal range to `action`, it is returned as `:parent`
118
+ # Reminder: an empty range 1...1 is considered disjoint from 1...10
119
+ def analyse_hierarchy(action)
120
+ r = action.range
121
+ # left_index is the index of the first child that isn't completely to the left of action
122
+ left_index = bsearch_child_index { |child| child.range.end_pos > r.begin_pos }
123
+ # right_index is the index of the first child that is completely on the right of action
124
+ start = left_index == 0 ? 0 : left_index - 1 # See "corner case" below for reason of -1
125
+ right_index = bsearch_child_index(start) { |child| child.range.begin_pos >= r.end_pos }
126
+ center = right_index - left_index
127
+ case center
128
+ when 0
129
+ # All children are disjoint from action, nothing else to do
130
+ when -1
131
+ # Corner case: if a child has empty range == action's range
132
+ # then it will appear to be both disjoint and to the left of action,
133
+ # as well as disjoint and to the right of action.
134
+ # Since ranges are equal, we return it as parent
135
+ left_index -= 1 # Fix indices, as otherwise this child would be
136
+ right_index += 1 # considered as a sibbling (both left and right!)
137
+ parent = @children[left_index]
100
138
  else
101
- @enforcer.call(:crossing_insertions) { {range: action.range, conflict: @range} }
139
+ overlap_left = @children[left_index].range.begin_pos <=> r.begin_pos
140
+ overlap_right = @children[right_index-1].range.end_pos <=> r.end_pos
141
+
142
+ # For one child to be the parent of action, we must have:
143
+ if center == 1 && overlap_left <= 0 && overlap_right >= 0
144
+ parent = @children[left_index]
145
+ else
146
+ # Otherwise consider all non disjoint elements (center) to be contained...
147
+ contained = @children[left_index...right_index]
148
+ fusible = check_fusible(action,
149
+ (contained.shift if overlap_left < 0), # ... but check first and last one
150
+ (contained.pop if overlap_right > 0) # ... for overlaps
151
+ )
152
+ end
102
153
  end
154
+
155
+ {
156
+ parent: parent,
157
+ sibbling_left: @children[0...left_index],
158
+ sibbling_right: @children[right_index...@children.size],
159
+ fusible: fusible,
160
+ child: contained,
161
+ }
162
+ end
163
+
164
+ # @param [Array(Action | nil)] fusible
165
+ def check_fusible(action, *fusible)
166
+ fusible.compact!
167
+ return if fusible.empty?
168
+ fusible.each do |child|
169
+ kind = action.insertion? || child.insertion? ? :crossing_insertions : :crossing_deletions
170
+ @enforcer.call(kind) { {range: action.range, conflict: child.range} }
171
+ end
172
+ fusible
103
173
  end
104
174
 
105
175
  # Assumes action.range == range && action.children.empty?
@@ -109,7 +179,7 @@ module Parser
109
179
  insert_before: "#{action.insert_before}#{insert_before}",
110
180
  replacement: action.replacement || @replacement,
111
181
  insert_after: "#{insert_after}#{action.insert_after}",
112
- )
182
+ ).combine_children(action.children)
113
183
  end
114
184
 
115
185
  def call_enforcer_for_merge(action)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Parser
4
- VERSION = '2.7.0.3'
4
+ VERSION = '2.7.1.2'
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
  )
@@ -20,6 +20,7 @@ if ENV.include?('COVERAGE') && SimpleCov.usable?
20
20
  ruby25.y
21
21
  ruby26.y
22
22
  ruby27.y
23
+ ruby28.y
23
24
  ),
24
25
  File.expand_path('../../lib/parser', __FILE__))
25
26
 
@@ -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}"
@@ -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
@@ -3569,6 +3569,18 @@ class TestLexer < Minitest::Test
3569
3569
  :tIDENTIFIER, 're', [1, 3])
3570
3570
  end
3571
3571
 
3572
+ def test_endless_method
3573
+ setup_lexer(28)
3574
+
3575
+ assert_scanned('def foo() = 42',
3576
+ :kDEF, "def", [0, 3],
3577
+ :tIDENTIFIER, "foo", [4, 7],
3578
+ :tLPAREN2, "(", [7, 8],
3579
+ :tRPAREN, ")", [8, 9],
3580
+ :tEQL, "=", [10, 11],
3581
+ :tINTEGER, 42, [12, 14])
3582
+ end
3583
+
3572
3584
  def lex_numbered_parameter(input)
3573
3585
  @lex.max_numparam_stack.push
3574
3586
 
@@ -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.
@@ -7644,12 +7645,26 @@ class TestParser < Minitest::Test
7644
7645
  %q{},
7645
7646
  SINCE_2_7)
7646
7647
 
7648
+ assert_parses(
7649
+ s(:send,
7650
+ s(:send, nil, :a), :foo),
7651
+ %Q{a #\n #\n.foo\n},
7652
+ %q{},
7653
+ SINCE_2_7)
7654
+
7647
7655
  assert_parses(
7648
7656
  s(:csend,
7649
7657
  s(:send, nil, :a), :foo),
7650
7658
  %Q{a #\n#\n&.foo\n},
7651
7659
  %q{},
7652
7660
  SINCE_2_7)
7661
+
7662
+ assert_parses(
7663
+ s(:csend,
7664
+ s(:send, nil, :a), :foo),
7665
+ %Q{a #\n #\n&.foo\n},
7666
+ %q{},
7667
+ SINCE_2_7)
7653
7668
  end
7654
7669
 
7655
7670
  def test_comments_before_leading_dot__before_27
@@ -8408,7 +8423,7 @@ class TestParser < Minitest::Test
8408
8423
  nil,
8409
8424
  s(:nil)),
8410
8425
  %q{in x, then nil},
8411
- %q{ ~ expression (in_pattern.array_pattern_with_tail)}
8426
+ %q{ ~~ expression (in_pattern.array_pattern_with_tail)}
8412
8427
  )
8413
8428
 
8414
8429
  assert_parses_pattern_match(
@@ -8454,7 +8469,7 @@ class TestParser < Minitest::Test
8454
8469
  nil,
8455
8470
  s(:nil)),
8456
8471
  %q{in x, y, then nil},
8457
- %q{ ~~~~ expression (in_pattern.array_pattern_with_tail)}
8472
+ %q{ ~~~~~ expression (in_pattern.array_pattern_with_tail)}
8458
8473
  )
8459
8474
 
8460
8475
  assert_parses_pattern_match(
@@ -8661,6 +8676,18 @@ class TestParser < Minitest::Test
8661
8676
  | ~ end (in_pattern.hash_pattern)}
8662
8677
  )
8663
8678
 
8679
+ assert_parses_pattern_match(
8680
+ s(:in_pattern,
8681
+ s(:hash_pattern,
8682
+ s(:pair, s(:sym, :a), s(:int, 1))),
8683
+ nil,
8684
+ s(:true)),
8685
+ %q{in { a: 1, } then true},
8686
+ %q{ ~~~~~~~~~ expression (in_pattern.hash_pattern)
8687
+ | ~ begin (in_pattern.hash_pattern)
8688
+ | ~ end (in_pattern.hash_pattern)}
8689
+ )
8690
+
8664
8691
  assert_parses_pattern_match(
8665
8692
  s(:in_pattern,
8666
8693
  s(:hash_pattern,
@@ -8732,6 +8759,67 @@ class TestParser < Minitest::Test
8732
8759
  %q{in a: 1, _a:, ** then true},
8733
8760
  %q{ ~~~~~~~~~~~~~ expression (in_pattern.hash_pattern)}
8734
8761
  )
8762
+
8763
+ assert_parses_pattern_match(
8764
+ s(:in_pattern,
8765
+ s(:hash_pattern,
8766
+ s(:pair,
8767
+ s(:sym, :a),
8768
+ s(:int, 1))), nil,
8769
+ s(:false)),
8770
+ %q{
8771
+ in {a: 1
8772
+ }
8773
+ false
8774
+ },
8775
+ %q{}
8776
+ )
8777
+
8778
+
8779
+ assert_parses_pattern_match(
8780
+ s(:in_pattern,
8781
+ s(:hash_pattern,
8782
+ s(:pair,
8783
+ s(:sym, :a),
8784
+ s(:int, 2))), nil,
8785
+ s(:false)),
8786
+ %q{
8787
+ in {a:
8788
+ 2}
8789
+ false
8790
+ },
8791
+ %q{}
8792
+ )
8793
+
8794
+ assert_parses_pattern_match(
8795
+ s(:in_pattern,
8796
+ s(:hash_pattern,
8797
+ s(:pair,
8798
+ s(:sym, :a),
8799
+ s(:hash_pattern,
8800
+ s(:match_var, :b))),
8801
+ s(:match_var, :c)), nil,
8802
+ s(:send, nil, :p,
8803
+ s(:lvar, :c))),
8804
+ %q{
8805
+ in a: {b:}, c:
8806
+ p c
8807
+ },
8808
+ %q{}
8809
+ )
8810
+
8811
+ assert_parses_pattern_match(
8812
+ s(:in_pattern,
8813
+ s(:hash_pattern,
8814
+ s(:match_var, :a)), nil,
8815
+ s(:true)),
8816
+ %q{
8817
+ in {a:
8818
+ }
8819
+ true
8820
+ },
8821
+ %q{}
8822
+ )
8735
8823
  end
8736
8824
 
8737
8825
  def test_pattern_matching_hash_with_string_keys
@@ -8846,6 +8934,22 @@ class TestParser < Minitest::Test
8846
8934
  %q{ ~~~~~~~ location},
8847
8935
  SINCE_2_7
8848
8936
  )
8937
+
8938
+ assert_diagnoses(
8939
+ [:error, :pm_interp_in_var_name],
8940
+ %q{case a; in "#{a}": 1; end},
8941
+ %q{ ~~~~~~~ location},
8942
+ SINCE_2_7
8943
+ )
8944
+ end
8945
+
8946
+ def test_pattern_matching_invalid_lvar_name
8947
+ assert_diagnoses(
8948
+ [:error, :lvar_name, { name: :a? }],
8949
+ %q{case a; in a?:; end},
8950
+ %q{ ~~ location},
8951
+ SINCE_2_7
8952
+ )
8849
8953
  end
8850
8954
 
8851
8955
  def test_pattern_matching_keyword_variable
@@ -8979,7 +9083,7 @@ class TestParser < Minitest::Test
8979
9083
  nil,
8980
9084
  s(:true)),
8981
9085
  %q{in A(1, 2) then true},
8982
- %q{ ~~~~~~ expression (in_pattern.const_pattern)
9086
+ %q{ ~~~~~~~ expression (in_pattern.const_pattern)
8983
9087
  | ~ begin (in_pattern.const_pattern)
8984
9088
  | ~ end (in_pattern.const_pattern)
8985
9089
  | ~ expression (in_pattern.const_pattern.const)
@@ -8995,7 +9099,7 @@ class TestParser < Minitest::Test
8995
9099
  nil,
8996
9100
  s(:true)),
8997
9101
  %q{in A(x:) then true},
8998
- %q{ ~~~~ expression (in_pattern.const_pattern)
9102
+ %q{ ~~~~~ expression (in_pattern.const_pattern)
8999
9103
  | ~ begin (in_pattern.const_pattern)
9000
9104
  | ~ end (in_pattern.const_pattern)
9001
9105
  | ~ expression (in_pattern.const_pattern.const)
@@ -9010,7 +9114,7 @@ class TestParser < Minitest::Test
9010
9114
  nil,
9011
9115
  s(:true)),
9012
9116
  %q{in A() then true},
9013
- %q{ ~~ expression (in_pattern.const_pattern)
9117
+ %q{ ~~~ expression (in_pattern.const_pattern)
9014
9118
  | ~ begin (in_pattern.const_pattern)
9015
9119
  | ~ end (in_pattern.const_pattern)
9016
9120
  | ~ expression (in_pattern.const_pattern.const)
@@ -9027,7 +9131,7 @@ class TestParser < Minitest::Test
9027
9131
  nil,
9028
9132
  s(:true)),
9029
9133
  %q{in A[1, 2] then true},
9030
- %q{ ~~~~~~ expression (in_pattern.const_pattern)
9134
+ %q{ ~~~~~~~ expression (in_pattern.const_pattern)
9031
9135
  | ~ begin (in_pattern.const_pattern)
9032
9136
  | ~ end (in_pattern.const_pattern)
9033
9137
  | ~ expression (in_pattern.const_pattern.const)
@@ -9043,7 +9147,7 @@ class TestParser < Minitest::Test
9043
9147
  nil,
9044
9148
  s(:true)),
9045
9149
  %q{in A[x:] then true},
9046
- %q{ ~~~~ expression (in_pattern.const_pattern)
9150
+ %q{ ~~~~~ expression (in_pattern.const_pattern)
9047
9151
  | ~ begin (in_pattern.const_pattern)
9048
9152
  | ~ end (in_pattern.const_pattern)
9049
9153
  | ~ expression (in_pattern.const_pattern.const)
@@ -9058,7 +9162,7 @@ class TestParser < Minitest::Test
9058
9162
  nil,
9059
9163
  s(:true)),
9060
9164
  %q{in A[] then true},
9061
- %q{ ~~ expression (in_pattern.const_pattern)
9165
+ %q{ ~~~ expression (in_pattern.const_pattern)
9062
9166
  | ~ begin (in_pattern.const_pattern)
9063
9167
  | ~ end (in_pattern.const_pattern)
9064
9168
  | ~~ expression (in_pattern.const_pattern.array_pattern)}
@@ -9340,4 +9444,82 @@ class TestParser < Minitest::Test
9340
9444
  %{},
9341
9445
  SINCE_1_9)
9342
9446
  end
9447
+
9448
+ def test_endless_method
9449
+ assert_parses(
9450
+ s(:def_e, :foo,
9451
+ s(:args),
9452
+ s(:int, 42)),
9453
+ %q{def foo() = 42},
9454
+ %q{~~~ keyword
9455
+ | ~~~ name
9456
+ | ^ assignment
9457
+ |~~~~~~~~~~~~~~ expression},
9458
+ SINCE_2_8)
9459
+
9460
+ assert_parses(
9461
+ s(:def_e, :inc,
9462
+ s(:args, s(:arg, :x)),
9463
+ s(:send,
9464
+ s(:lvar, :x), :+,
9465
+ s(:int, 1))),
9466
+ %q{def inc(x) = x + 1},
9467
+ %q{~~~ keyword
9468
+ | ~~~ name
9469
+ | ^ assignment
9470
+ |~~~~~~~~~~~~~~~~~~ expression},
9471
+ SINCE_2_8)
9472
+
9473
+ assert_parses(
9474
+ s(:defs_e, s(:send, nil, :obj), :foo,
9475
+ s(:args),
9476
+ s(:int, 42)),
9477
+ %q{def obj.foo() = 42},
9478
+ %q{~~~ keyword
9479
+ | ^ operator
9480
+ | ~~~ name
9481
+ | ^ assignment
9482
+ |~~~~~~~~~~~~~~~~~~ expression},
9483
+ SINCE_2_8)
9484
+
9485
+ assert_parses(
9486
+ s(:defs_e, s(:send, nil, :obj), :inc,
9487
+ s(:args, s(:arg, :x)),
9488
+ s(:send,
9489
+ s(:lvar, :x), :+,
9490
+ s(:int, 1))),
9491
+ %q{def obj.inc(x) = x + 1},
9492
+ %q{~~~ keyword
9493
+ | ~~~ name
9494
+ | ^ operator
9495
+ | ^ assignment
9496
+ |~~~~~~~~~~~~~~~~~~~~~~ expression},
9497
+ SINCE_2_8)
9498
+
9499
+ assert_parses(
9500
+ s(:def_e, :foo,
9501
+ s(:forward_args),
9502
+ s(:send, nil, :bar,
9503
+ s(:forwarded_args))),
9504
+ %q{def foo(...) = bar(...)},
9505
+ %q{~~~ keyword
9506
+ | ~~~ name
9507
+ | ^ assignment
9508
+ |~~~~~~~~~~~~~~~~~~~~~~~ expression},
9509
+ SINCE_2_8)
9510
+ end
9511
+
9512
+ def test_endless_method_without_brackets
9513
+ assert_diagnoses(
9514
+ [:error, :unexpected_token, { :token => 'tEQL' }],
9515
+ %Q{def foo = 42},
9516
+ %q{ ^ location},
9517
+ SINCE_2_8)
9518
+
9519
+ assert_diagnoses(
9520
+ [:error, :unexpected_token, { :token => 'tEQL' }],
9521
+ %Q{def obj.foo = 42},
9522
+ %q{ ^ location},
9523
+ SINCE_2_8)
9524
+ end
9343
9525
  end