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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +14 -23
- data/CHANGELOG.md +42 -0
- data/README.md +5 -4
- data/Rakefile +1 -0
- data/doc/AST_FORMAT.md +32 -5
- data/lib/parser.rb +1 -0
- data/lib/parser/all.rb +1 -0
- data/lib/parser/ast/processor.rb +4 -0
- data/lib/parser/builders/default.rb +54 -14
- data/lib/parser/current.rb +13 -4
- data/lib/parser/lexer.rl +1 -1
- data/lib/parser/meta.rb +1 -1
- data/lib/parser/ruby27.y +16 -5
- data/lib/parser/ruby28.y +2974 -0
- data/lib/parser/runner.rb +5 -0
- data/lib/parser/source/map/endless_definition.rb +23 -0
- data/lib/parser/source/range.rb +9 -0
- data/lib/parser/source/tree_rewriter.rb +50 -13
- data/lib/parser/source/tree_rewriter/action.rb +96 -26
- data/lib/parser/version.rb +1 -1
- data/parser.gemspec +1 -0
- data/test/helper.rb +1 -0
- data/test/parse_helper.rb +2 -1
- data/test/test_current.rb +2 -0
- data/test/test_lexer.rb +12 -0
- data/test/test_parser.rb +190 -8
- data/test/test_source_range.rb +15 -0
- data/test/test_source_tree_rewriter.rb +93 -3
- metadata +8 -5
data/lib/parser/runner.rb
CHANGED
@@ -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
|
data/lib/parser/source/range.rb
CHANGED
@@ -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
|
-
|
188
|
-
source = @source_buffer.source
|
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
|
-
|
193
|
-
|
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
|
-
|
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
|
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.
|
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
|
-
|
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,
|
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 =
|
72
|
+
family = analyse_hierarchy(action)
|
65
73
|
|
66
74
|
if family[:fusible]
|
67
|
-
fuse_deletions(action, family[:fusible], [*family[:
|
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]
|
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[:
|
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
|
-
#
|
88
|
-
#
|
89
|
-
#
|
90
|
-
def
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
-
@
|
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)
|
data/lib/parser/version.rb
CHANGED
data/parser.gemspec
CHANGED
data/test/helper.rb
CHANGED
data/test/parse_helper.rb
CHANGED
@@ -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}"
|
data/test/test_current.rb
CHANGED
@@ -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
|
data/test/test_lexer.rb
CHANGED
@@ -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
|
|
data/test/test_parser.rb
CHANGED
@@ -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{
|
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{
|
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{
|
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{
|
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{
|
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{
|
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{
|
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{
|
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
|