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.
- 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
|