parser 2.7.0.3 → 2.7.1.2

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