ruby-next-parser 3.0.0.1 → 3.1.0.0
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/lib/parser/ruby-next/AST_FORMAT.md +0 -24
- data/lib/parser/ruby-next/ast/processor.rb +0 -4
- data/lib/parser/ruby-next/builder.rb +0 -4
- data/lib/parser/ruby-next/lexer.rb +12288 -11352
- data/lib/parser/ruby-next/lexer.rl +44 -8
- data/lib/parser/ruby-next/meta.rb +1 -1
- data/lib/parser/ruby-next/version.rb +1 -1
- data/lib/parser/rubynext.rb +4110 -3931
- data/lib/parser/rubynext.y +207 -92
- metadata +10 -10
data/lib/parser/rubynext.y
CHANGED
@@ -17,7 +17,8 @@ token kCLASS kMODULE kDEF kUNDEF kBEGIN kRESCUE kENSURE kEND kIF kUNLESS
|
|
17
17
|
tWORDS_BEG tQWORDS_BEG tSYMBOLS_BEG tQSYMBOLS_BEG tSTRING_DBEG
|
18
18
|
tSTRING_DVAR tSTRING_END tSTRING_DEND tSTRING tSYMBOL
|
19
19
|
tNL tEH tCOLON tCOMMA tSPACE tSEMI tLAMBDA tLAMBEG tCHARACTER
|
20
|
-
tRATIONAL tIMAGINARY tLABEL_END tANDDOT
|
20
|
+
tRATIONAL tIMAGINARY tLABEL_END tANDDOT tBDOT2 tBDOT3
|
21
|
+
tMETHREF
|
21
22
|
|
22
23
|
prechigh
|
23
24
|
right tBANG tTILDE tUPLUS
|
@@ -46,7 +47,17 @@ preclow
|
|
46
47
|
|
47
48
|
rule
|
48
49
|
|
49
|
-
program:
|
50
|
+
program: {
|
51
|
+
@current_arg_stack.push(nil)
|
52
|
+
@max_numparam_stack.push
|
53
|
+
}
|
54
|
+
top_compstmt
|
55
|
+
{
|
56
|
+
result = val[1]
|
57
|
+
|
58
|
+
@current_arg_stack.pop
|
59
|
+
@max_numparam_stack.pop
|
60
|
+
}
|
50
61
|
|
51
62
|
top_compstmt: top_stmts opt_terms
|
52
63
|
{
|
@@ -206,25 +217,8 @@ rule
|
|
206
217
|
{
|
207
218
|
result = @builder.multi_assign(val[0], val[1], val[2])
|
208
219
|
}
|
209
|
-
| rassign
|
210
220
|
| expr
|
211
221
|
|
212
|
-
rassign: arg_value tASSOC lhs
|
213
|
-
{
|
214
|
-
result = @builder.rassign(val[0], val[1], val[2])
|
215
|
-
}
|
216
|
-
| arg_value tASSOC mlhs
|
217
|
-
{
|
218
|
-
result = @builder.multi_rassign(val[0], val[1], val[2])
|
219
|
-
}
|
220
|
-
| rassign tASSOC lhs
|
221
|
-
{
|
222
|
-
result = @builder.rassign(val[0], val[1], val[2])
|
223
|
-
}
|
224
|
-
| rassign tASSOC mlhs
|
225
|
-
{
|
226
|
-
result = @builder.multi_rassign(val[0], val[1], val[2])
|
227
|
-
}
|
228
222
|
|
229
223
|
command_asgn: lhs tEQL command_rhs
|
230
224
|
{
|
@@ -268,6 +262,74 @@ rule
|
|
268
262
|
val[0], val[1], val[2]),
|
269
263
|
val[3], val[4])
|
270
264
|
}
|
265
|
+
| defn_head f_opt_paren_args tEQL command
|
266
|
+
{
|
267
|
+
_def_t, name_t = val[0]
|
268
|
+
endless_method_name(name_t)
|
269
|
+
|
270
|
+
result = @builder.def_endless_method(*val[0],
|
271
|
+
val[1], val[2], val[3])
|
272
|
+
|
273
|
+
@lexer.cmdarg.pop
|
274
|
+
@lexer.cond.pop
|
275
|
+
@static_env.unextend
|
276
|
+
@context.pop
|
277
|
+
@current_arg_stack.pop
|
278
|
+
}
|
279
|
+
| defn_head f_opt_paren_args tEQL command kRESCUE_MOD arg
|
280
|
+
{
|
281
|
+
_def_t, name_t = val[0]
|
282
|
+
endless_method_name(name_t)
|
283
|
+
|
284
|
+
rescue_body = @builder.rescue_body(val[4],
|
285
|
+
nil, nil, nil,
|
286
|
+
nil, val[5])
|
287
|
+
|
288
|
+
method_body = @builder.begin_body(val[3], [ rescue_body ])
|
289
|
+
|
290
|
+
result = @builder.def_endless_method(*val[0],
|
291
|
+
val[1], val[2], method_body)
|
292
|
+
|
293
|
+
@lexer.cmdarg.pop
|
294
|
+
@lexer.cond.pop
|
295
|
+
@static_env.unextend
|
296
|
+
@context.pop
|
297
|
+
@current_arg_stack.pop
|
298
|
+
}
|
299
|
+
| defs_head f_opt_paren_args tEQL command
|
300
|
+
{
|
301
|
+
_def_t, _recv, _dot_t, name_t = val[0]
|
302
|
+
endless_method_name(name_t)
|
303
|
+
|
304
|
+
result = @builder.def_endless_singleton(*val[0],
|
305
|
+
val[1], val[2], val[3])
|
306
|
+
|
307
|
+
@lexer.cmdarg.pop
|
308
|
+
@lexer.cond.pop
|
309
|
+
@static_env.unextend
|
310
|
+
@context.pop
|
311
|
+
@current_arg_stack.pop
|
312
|
+
}
|
313
|
+
| defs_head f_opt_paren_args tEQL command kRESCUE_MOD arg
|
314
|
+
{
|
315
|
+
_def_t, _recv, _dot_t, name_t = val[0]
|
316
|
+
endless_method_name(name_t)
|
317
|
+
|
318
|
+
rescue_body = @builder.rescue_body(val[4],
|
319
|
+
nil, nil, nil,
|
320
|
+
nil, val[5])
|
321
|
+
|
322
|
+
method_body = @builder.begin_body(val[3], [ rescue_body ])
|
323
|
+
|
324
|
+
result = @builder.def_endless_singleton(*val[0],
|
325
|
+
val[1], val[2], method_body)
|
326
|
+
|
327
|
+
@lexer.cmdarg.pop
|
328
|
+
@lexer.cond.pop
|
329
|
+
@static_env.unextend
|
330
|
+
@context.pop
|
331
|
+
@current_arg_stack.pop
|
332
|
+
}
|
271
333
|
| backref tOP_ASGN command_rhs
|
272
334
|
{
|
273
335
|
@builder.op_assign(val[0], val[1], val[2])
|
@@ -301,19 +363,35 @@ rule
|
|
301
363
|
{
|
302
364
|
result = @builder.not_op(val[0], nil, val[1], nil)
|
303
365
|
}
|
366
|
+
| arg tASSOC
|
367
|
+
{
|
368
|
+
@lexer.state = :expr_beg
|
369
|
+
@lexer.command_start = false
|
370
|
+
@pattern_variables.push
|
371
|
+
|
372
|
+
result = @lexer.in_kwarg
|
373
|
+
@lexer.in_kwarg = true
|
374
|
+
}
|
375
|
+
p_top_expr_body
|
376
|
+
{
|
377
|
+
@pattern_variables.pop
|
378
|
+
@lexer.in_kwarg = val[2]
|
379
|
+
result = @builder.match_pattern(val[0], val[1], val[3])
|
380
|
+
}
|
304
381
|
| arg kIN
|
305
382
|
{
|
306
383
|
@lexer.state = :expr_beg
|
307
384
|
@lexer.command_start = false
|
308
|
-
pattern_variables.push
|
385
|
+
@pattern_variables.push
|
309
386
|
|
310
387
|
result = @lexer.in_kwarg
|
311
388
|
@lexer.in_kwarg = true
|
312
389
|
}
|
313
|
-
|
390
|
+
p_top_expr_body
|
314
391
|
{
|
392
|
+
@pattern_variables.pop
|
315
393
|
@lexer.in_kwarg = val[2]
|
316
|
-
result = @builder.
|
394
|
+
result = @builder.match_pattern_p(val[0], val[1], val[3])
|
317
395
|
}
|
318
396
|
| arg =tLBRACE_ARG
|
319
397
|
|
@@ -328,7 +406,7 @@ rule
|
|
328
406
|
result = [ val[1], val[2] ]
|
329
407
|
}
|
330
408
|
|
331
|
-
def_name:
|
409
|
+
def_name: fname
|
332
410
|
{
|
333
411
|
@static_env.extend_static
|
334
412
|
@lexer.cmdarg.push(false)
|
@@ -864,13 +942,10 @@ rule
|
|
864
942
|
result = @builder.ternary(val[0], val[1],
|
865
943
|
val[2], val[4], val[5])
|
866
944
|
}
|
867
|
-
| defn_head
|
945
|
+
| defn_head f_opt_paren_args tEQL arg
|
868
946
|
{
|
869
947
|
_def_t, name_t = val[0]
|
870
|
-
|
871
|
-
if name_t[0].end_with?('=')
|
872
|
-
diagnostic :error, :endless_setter, nil, name_t
|
873
|
-
end
|
948
|
+
endless_method_name(name_t)
|
874
949
|
|
875
950
|
result = @builder.def_endless_method(*val[0],
|
876
951
|
val[1], val[2], val[3])
|
@@ -881,8 +956,11 @@ rule
|
|
881
956
|
@context.pop
|
882
957
|
@current_arg_stack.pop
|
883
958
|
}
|
884
|
-
| defn_head
|
959
|
+
| defn_head f_opt_paren_args tEQL arg kRESCUE_MOD arg
|
885
960
|
{
|
961
|
+
_def_t, name_t = val[0]
|
962
|
+
endless_method_name(name_t)
|
963
|
+
|
886
964
|
rescue_body = @builder.rescue_body(val[4],
|
887
965
|
nil, nil, nil,
|
888
966
|
nil, val[5])
|
@@ -898,8 +976,11 @@ rule
|
|
898
976
|
@context.pop
|
899
977
|
@current_arg_stack.pop
|
900
978
|
}
|
901
|
-
| defs_head
|
979
|
+
| defs_head f_opt_paren_args tEQL arg
|
902
980
|
{
|
981
|
+
_def_t, _recv, _dot_t, name_t = val[0]
|
982
|
+
endless_method_name(name_t)
|
983
|
+
|
903
984
|
result = @builder.def_endless_singleton(*val[0],
|
904
985
|
val[1], val[2], val[3])
|
905
986
|
|
@@ -909,8 +990,11 @@ rule
|
|
909
990
|
@context.pop
|
910
991
|
@current_arg_stack.pop
|
911
992
|
}
|
912
|
-
| defs_head
|
993
|
+
| defs_head f_opt_paren_args tEQL arg kRESCUE_MOD arg
|
913
994
|
{
|
995
|
+
_def_t, _recv, _dot_t, name_t = val[0]
|
996
|
+
endless_method_name(name_t)
|
997
|
+
|
914
998
|
rescue_body = @builder.rescue_body(val[4],
|
915
999
|
nil, nil, nil,
|
916
1000
|
nil, val[5])
|
@@ -1853,6 +1937,8 @@ opt_block_args_tail:
|
|
1853
1937
|
}
|
1854
1938
|
p_top_expr then
|
1855
1939
|
{
|
1940
|
+
@pattern_variables.pop
|
1941
|
+
@pattern_hash_keys.pop
|
1856
1942
|
@lexer.in_kwarg = val[1]
|
1857
1943
|
}
|
1858
1944
|
compstmt p_cases
|
@@ -1933,6 +2019,7 @@ opt_block_args_tail:
|
|
1933
2019
|
}
|
1934
2020
|
|
1935
2021
|
p_expr_basic: p_value
|
2022
|
+
| p_variable
|
1936
2023
|
| p_const p_lparen p_args rparen
|
1937
2024
|
{
|
1938
2025
|
@pattern_hash_keys.pop
|
@@ -2178,8 +2265,8 @@ opt_block_args_tail:
|
|
2178
2265
|
{
|
2179
2266
|
result = @builder.range_exclusive(val[0], val[1], nil)
|
2180
2267
|
}
|
2181
|
-
| p_variable
|
2182
2268
|
| p_var_ref
|
2269
|
+
| p_expr_ref
|
2183
2270
|
| p_const
|
2184
2271
|
| tBDOT2 p_primitive
|
2185
2272
|
{
|
@@ -2206,7 +2293,7 @@ opt_block_args_tail:
|
|
2206
2293
|
|
2207
2294
|
p_variable: tIDENTIFIER
|
2208
2295
|
{
|
2209
|
-
result = @builder.match_var(val[0])
|
2296
|
+
result = @builder.assignable(@builder.match_var(val[0]))
|
2210
2297
|
}
|
2211
2298
|
|
2212
2299
|
p_var_ref: tCARET tIDENTIFIER
|
@@ -2220,6 +2307,18 @@ opt_block_args_tail:
|
|
2220
2307
|
result = @builder.pin(val[0], lvar)
|
2221
2308
|
}
|
2222
2309
|
|
2310
|
+
| tCARET nonlocal_var
|
2311
|
+
{
|
2312
|
+
non_lvar = @builder.accessible(val[1])
|
2313
|
+
result = @builder.pin(val[0], non_lvar)
|
2314
|
+
}
|
2315
|
+
|
2316
|
+
p_expr_ref: tCARET tLPAREN expr_value tRPAREN
|
2317
|
+
{
|
2318
|
+
expr = @builder.begin(val[1], val[2], val[3])
|
2319
|
+
result = @builder.pin(val[0], expr)
|
2320
|
+
}
|
2321
|
+
|
2223
2322
|
p_const: tCOLON3 cname
|
2224
2323
|
{
|
2225
2324
|
result = @builder.const_global(val[0], val[1])
|
@@ -2491,6 +2590,19 @@ regexp_contents: # nothing
|
|
2491
2590
|
result = @builder.complex(val[0])
|
2492
2591
|
}
|
2493
2592
|
|
2593
|
+
nonlocal_var: tIVAR
|
2594
|
+
{
|
2595
|
+
result = @builder.ivar(val[0])
|
2596
|
+
}
|
2597
|
+
| tGVAR
|
2598
|
+
{
|
2599
|
+
result = @builder.gvar(val[0])
|
2600
|
+
}
|
2601
|
+
| tCVAR
|
2602
|
+
{
|
2603
|
+
result = @builder.cvar(val[0])
|
2604
|
+
}
|
2605
|
+
|
2494
2606
|
user_variable: tIDENTIFIER
|
2495
2607
|
{
|
2496
2608
|
result = @builder.ident(val[0])
|
@@ -2543,46 +2655,6 @@ keyword_variable: kNIL
|
|
2543
2655
|
|
2544
2656
|
var_ref: user_variable
|
2545
2657
|
{
|
2546
|
-
if (node = val[0]) && node.type == :ident
|
2547
|
-
name = node.children[0]
|
2548
|
-
|
2549
|
-
if name =~ /\A_[1-9]\z/ && !static_env.declared?(name) && context.in_dynamic_block?
|
2550
|
-
# definitely an implicit param
|
2551
|
-
location = node.loc.expression
|
2552
|
-
|
2553
|
-
if max_numparam_stack.has_ordinary_params?
|
2554
|
-
diagnostic :error, :ordinary_param_defined, nil, [nil, location]
|
2555
|
-
end
|
2556
|
-
|
2557
|
-
raw_context = context.stack.dup
|
2558
|
-
raw_max_numparam_stack = max_numparam_stack.stack.dup
|
2559
|
-
|
2560
|
-
# ignore current block scope
|
2561
|
-
raw_context.pop
|
2562
|
-
raw_max_numparam_stack.pop
|
2563
|
-
|
2564
|
-
raw_context.reverse_each do |outer_scope|
|
2565
|
-
if outer_scope == :block || outer_scope == :lambda
|
2566
|
-
outer_scope_has_numparams = raw_max_numparam_stack.pop > 0
|
2567
|
-
|
2568
|
-
if outer_scope_has_numparams
|
2569
|
-
diagnostic :error, :numparam_used_in_outer_scope, nil, [nil, location]
|
2570
|
-
else
|
2571
|
-
# for now it's ok, but an outer scope can also be a block
|
2572
|
-
# with numparams, so we need to continue
|
2573
|
-
end
|
2574
|
-
else
|
2575
|
-
# found an outer scope that can't have numparams
|
2576
|
-
# like def/class/etc
|
2577
|
-
break
|
2578
|
-
end
|
2579
|
-
end
|
2580
|
-
|
2581
|
-
static_env.declare(name)
|
2582
|
-
max_numparam_stack.register(name[1].to_i)
|
2583
|
-
end
|
2584
|
-
end
|
2585
|
-
|
2586
2658
|
result = @builder.accessible(val[0])
|
2587
2659
|
}
|
2588
2660
|
| keyword_variable
|
@@ -2621,6 +2693,12 @@ keyword_variable: kNIL
|
|
2621
2693
|
result = nil
|
2622
2694
|
}
|
2623
2695
|
|
2696
|
+
f_opt_paren_args: f_paren_args
|
2697
|
+
| none
|
2698
|
+
{
|
2699
|
+
result = @builder.args(nil, [], nil)
|
2700
|
+
}
|
2701
|
+
|
2624
2702
|
f_paren_args: tLPAREN2 f_args rparen
|
2625
2703
|
{
|
2626
2704
|
result = @builder.args(val[0], val[1], val[2])
|
@@ -2966,24 +3044,7 @@ keyword_variable: kNIL
|
|
2966
3044
|
{
|
2967
3045
|
result = []
|
2968
3046
|
}
|
2969
|
-
|
|
2970
|
-
|
2971
|
-
assoc_items : assoc_item
|
2972
|
-
{
|
2973
|
-
result = [ val[0] ]
|
2974
|
-
}
|
2975
|
-
| assoc_items tCOMMA assoc_item
|
2976
|
-
{
|
2977
|
-
result = val[0] << val[2]
|
2978
|
-
}
|
2979
|
-
|
2980
|
-
assoc_item: assoc
|
2981
|
-
|
|
2982
|
-
tIDENTIFIER
|
2983
|
-
{
|
2984
|
-
lvar = @builder.accessible(@builder.ident(val[0]))
|
2985
|
-
result = @builder.ipair(lvar)
|
2986
|
-
}
|
3047
|
+
| assocs trailer
|
2987
3048
|
|
2988
3049
|
assocs: assoc
|
2989
3050
|
{
|
@@ -3002,6 +3063,10 @@ keyword_variable: kNIL
|
|
3002
3063
|
{
|
3003
3064
|
result = @builder.pair_keyword(val[0], val[1])
|
3004
3065
|
}
|
3066
|
+
| tLABEL
|
3067
|
+
{
|
3068
|
+
result = @builder.pair_label(val[0])
|
3069
|
+
}
|
3005
3070
|
| tSTRING_BEG string_contents tLABEL_END arg_value
|
3006
3071
|
{
|
3007
3072
|
result = @builder.pair_quoted(val[0], val[1], val[2], val[3])
|
@@ -3064,9 +3129,59 @@ require 'parser/ruby-next/parser_ext'
|
|
3064
3129
|
prepend Parser::NextExt
|
3065
3130
|
|
3066
3131
|
def version
|
3067
|
-
|
3132
|
+
31
|
3068
3133
|
end
|
3069
3134
|
|
3070
3135
|
def default_encoding
|
3071
3136
|
Encoding::UTF_8
|
3072
3137
|
end
|
3138
|
+
|
3139
|
+
def endless_method_name(name_t)
|
3140
|
+
if !%w[=== == != <= >=].include?(name_t[0]) && name_t[0].end_with?('=')
|
3141
|
+
diagnostic :error, :endless_setter, nil, name_t
|
3142
|
+
end
|
3143
|
+
end
|
3144
|
+
|
3145
|
+
def try_declare_numparam(node)
|
3146
|
+
name = node.children[0]
|
3147
|
+
|
3148
|
+
if name =~ /\A_[1-9]\z/ && !static_env.declared?(name) && context.in_dynamic_block?
|
3149
|
+
# definitely an implicit param
|
3150
|
+
location = node.loc.expression
|
3151
|
+
|
3152
|
+
if max_numparam_stack.has_ordinary_params?
|
3153
|
+
diagnostic :error, :ordinary_param_defined, nil, [nil, location]
|
3154
|
+
end
|
3155
|
+
|
3156
|
+
raw_context = context.stack.dup
|
3157
|
+
raw_max_numparam_stack = max_numparam_stack.stack.dup
|
3158
|
+
|
3159
|
+
# ignore current block scope
|
3160
|
+
raw_context.pop
|
3161
|
+
raw_max_numparam_stack.pop
|
3162
|
+
|
3163
|
+
raw_context.reverse_each do |outer_scope|
|
3164
|
+
if outer_scope == :block || outer_scope == :lambda
|
3165
|
+
outer_scope_has_numparams = raw_max_numparam_stack.pop > 0
|
3166
|
+
|
3167
|
+
if outer_scope_has_numparams
|
3168
|
+
diagnostic :error, :numparam_used_in_outer_scope, nil, [nil, location]
|
3169
|
+
else
|
3170
|
+
# for now it's ok, but an outer scope can also be a block
|
3171
|
+
# with numparams, so we need to continue
|
3172
|
+
end
|
3173
|
+
else
|
3174
|
+
# found an outer scope that can't have numparams
|
3175
|
+
# like def/class/etc
|
3176
|
+
break
|
3177
|
+
end
|
3178
|
+
end
|
3179
|
+
|
3180
|
+
static_env.declare(name)
|
3181
|
+
max_numparam_stack.register(name[1].to_i)
|
3182
|
+
|
3183
|
+
true
|
3184
|
+
else
|
3185
|
+
false
|
3186
|
+
end
|
3187
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-next-parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.0
|
4
|
+
version: 3.1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vladimir Dementyev
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-11-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: parser
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 3.0.3.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 3.0.3.0
|
27
27
|
description: "\n Parser extension to support edge and experimental Ruby syntax\n
|
28
28
|
\ "
|
29
29
|
email:
|
@@ -46,9 +46,9 @@ homepage: http://github.com/ruby-next/parser
|
|
46
46
|
licenses:
|
47
47
|
- MIT
|
48
48
|
metadata:
|
49
|
-
homepage_uri: http://github.com/
|
50
|
-
source_code_uri: http://github.com/
|
51
|
-
post_install_message:
|
49
|
+
homepage_uri: http://github.com/ruby-next/parser
|
50
|
+
source_code_uri: http://github.com/ruby-next/parser
|
51
|
+
post_install_message:
|
52
52
|
rdoc_options: []
|
53
53
|
require_paths:
|
54
54
|
- lib
|
@@ -63,8 +63,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
63
63
|
- !ruby/object:Gem::Version
|
64
64
|
version: '0'
|
65
65
|
requirements: []
|
66
|
-
rubygems_version: 3.
|
67
|
-
signing_key:
|
66
|
+
rubygems_version: 3.2.22
|
67
|
+
signing_key:
|
68
68
|
specification_version: 4
|
69
69
|
summary: Parser extension to support edge and experimental Ruby syntax
|
70
70
|
test_files: []
|