@@ -25,8 +25,8 @@ class CompilationError < StandardError
25
25
# offsets in the file.
26
26
attr_reader :offset_cache
27
27
28
- # The locals in the current scope.
29
- attr_reader :locals
28
+ # The types of values that can be forwarded in the current scope.
29
+ attr_reader :forwarding
30
30
31
31
# Whether or not the current node is in a destructure.
32
32
attr_reader :in_destructure
@@ -36,13 +36,13 @@ class CompilationError < StandardError
36
36
37
37
# Initialize a new compiler with the given parser, offset cache, and
38
38
# options.
39
- def initialize ( parser , offset_cache , locals : nil , in_destructure : false , in_pattern : false )
39
+ def initialize ( parser , offset_cache , forwarding : [ ] , in_destructure : false , in_pattern : false )
40
40
@parser = parser
41
41
@builder = parser . builder
42
42
@source_buffer = parser . source_buffer
43
43
@offset_cache = offset_cache
44
44
45
- @locals = locals
45
+ @forwarding = forwarding
46
46
@in_destructure = in_destructure
47
47
@in_pattern = in_pattern
48
48
end
@@ -135,7 +135,7 @@ def visit_assoc_node(node)
135
135
# { **foo }
136
136
# ^^^^^
137
137
def visit_assoc_splat_node ( node )
138
- if node . value . nil? && locals . include? ( :** )
138
+ if node . value . nil? && forwarding . include? ( :** )
139
139
builder . forwarded_kwrestarg ( token ( node . operator_loc ) )
140
140
else
141
141
builder . kwsplat ( token ( node . operator_loc ) , visit ( node . value ) )
@@ -372,7 +372,7 @@ def visit_class_node(node)
372
372
visit ( node . constant_path ) ,
373
373
token ( node . inheritance_operator_loc ) ,
374
374
visit ( node . superclass ) ,
375
- node . body &.accept ( copy_compiler ( locals : node . locals ) ) ,
375
+ node . body &.accept ( copy_compiler ( forwarding : [ ] ) ) ,
376
376
token ( node . end_keyword_loc )
377
377
)
378
378
end
@@ -519,6 +519,8 @@ def visit_constant_path_target_node(node)
519
519
# def self.foo; end
520
520
# ^^^^^^^^^^^^^^^^^
521
521
def visit_def_node ( node )
522
+ forwarding = find_forwarding ( node . parameters )
523
+
522
524
if node . equal_loc
523
525
if node . receiver
524
526
builder . def_endless_singleton (
@@ -528,15 +530,15 @@ def visit_def_node(node)
528
530
token ( node . name_loc ) ,
529
531
builder . args ( token ( node . lparen_loc ) , visit ( node . parameters ) || [ ] , token ( node . rparen_loc ) , false ) ,
530
532
token ( node . equal_loc ) ,
531
- node . body &.accept ( copy_compiler ( locals : node . locals ) )
533
+ node . body &.accept ( copy_compiler ( forwarding : forwarding ) )
532
534
)
533
535
else
534
536
builder . def_endless_method (
535
537
token ( node . def_keyword_loc ) ,
536
538
token ( node . name_loc ) ,
537
539
builder . args ( token ( node . lparen_loc ) , visit ( node . parameters ) || [ ] , token ( node . rparen_loc ) , false ) ,
538
540
token ( node . equal_loc ) ,
539
- node . body &.accept ( copy_compiler ( locals : node . locals ) )
541
+ node . body &.accept ( copy_compiler ( forwarding : forwarding ) )
540
542
)
541
543
end
542
544
elsif node . receiver
@@ -546,15 +548,15 @@ def visit_def_node(node)
546
548
token ( node . operator_loc ) ,
547
549
token ( node . name_loc ) ,
548
550
builder . args ( token ( node . lparen_loc ) , visit ( node . parameters ) || [ ] , token ( node . rparen_loc ) , false ) ,
549
- node . body &.accept ( copy_compiler ( locals : node . locals ) ) ,
551
+ node . body &.accept ( copy_compiler ( forwarding : forwarding ) ) ,
550
552
token ( node . end_keyword_loc )
551
553
)
552
554
else
553
555
builder . def_method (
554
556
token ( node . def_keyword_loc ) ,
555
557
token ( node . name_loc ) ,
556
558
builder . args ( token ( node . lparen_loc ) , visit ( node . parameters ) || [ ] , token ( node . rparen_loc ) , false ) ,
557
- node . body &.accept ( copy_compiler ( locals : node . locals ) ) ,
559
+ node . body &.accept ( copy_compiler ( forwarding : forwarding ) ) ,
558
560
token ( node . end_keyword_loc )
559
561
)
560
562
end
@@ -1008,7 +1010,7 @@ def visit_lambda_node(node)
1008
1010
else
1009
1011
builder . args ( nil , [ ] , nil , false )
1010
1012
end ,
1011
- node . body &.accept ( copy_compiler ( locals : node . locals ) ) ,
1013
+ node . body &.accept ( copy_compiler ( forwarding : find_forwarding ( node . parameters &. parameters ) ) ) ,
1012
1014
[ node . closing , srange ( node . closing_loc ) ]
1013
1015
)
1014
1016
end
@@ -1103,7 +1105,7 @@ def visit_module_node(node)
1103
1105
builder . def_module (
1104
1106
token ( node . module_keyword_loc ) ,
1105
1107
visit ( node . constant_path ) ,
1106
- node . body &.accept ( copy_compiler ( locals : node . locals ) ) ,
1108
+ node . body &.accept ( copy_compiler ( forwarding : [ ] ) ) ,
1107
1109
token ( node . end_keyword_loc )
1108
1110
)
1109
1111
end
@@ -1284,7 +1286,7 @@ def visit_pre_execution_node(node)
1284
1286
1285
1287
# The top-level program node.
1286
1288
def visit_program_node ( node )
1287
- node . statements . accept ( copy_compiler ( locals : node . locals ) )
1289
+ visit ( node . statements )
1288
1290
end
1289
1291
1290
1292
# 0..5
@@ -1415,7 +1417,7 @@ def visit_singleton_class_node(node)
1415
1417
token ( node . class_keyword_loc ) ,
1416
1418
token ( node . operator_loc ) ,
1417
1419
visit ( node . expression ) ,
1418
- node . body &.accept ( copy_compiler ( locals : node . locals ) ) ,
1420
+ node . body &.accept ( copy_compiler ( forwarding : [ ] ) ) ,
1419
1421
token ( node . end_keyword_loc )
1420
1422
)
1421
1423
end
@@ -1447,7 +1449,7 @@ def visit_source_line_node(node)
1447
1449
# def foo(*); bar(*); end
1448
1450
# ^
1449
1451
def visit_splat_node ( node )
1450
- if node . expression . nil? && locals . include? ( :* )
1452
+ if node . expression . nil? && forwarding . include? ( :* )
1451
1453
builder . forwarded_restarg ( token ( node . operator_loc ) )
1452
1454
elsif in_destructure
1453
1455
builder . restarg ( token ( node . operator_loc ) , token ( node . expression &.location ) )
@@ -1658,8 +1660,23 @@ def visit_yield_node(node)
1658
1660
1659
1661
# Initialize a new compiler with the given option overrides, used to
1660
1662
# visit a subtree with the given options.
1661
- def copy_compiler ( locals : self . locals , in_destructure : self . in_destructure , in_pattern : self . in_pattern )
1662
- Compiler . new ( parser , offset_cache , locals : locals , in_destructure : in_destructure , in_pattern : in_pattern )
1663
+ def copy_compiler ( forwarding : self . forwarding , in_destructure : self . in_destructure , in_pattern : self . in_pattern )
1664
+ Compiler . new ( parser , offset_cache , forwarding : forwarding , in_destructure : in_destructure , in_pattern : in_pattern )
1665
+ end
1666
+
1667
+ # When *, **, &, or ... are used as an argument in a method call, we
1668
+ # check if they were allowed by the current context. To determine that
1669
+ # we build this lookup table.
1670
+ def find_forwarding ( node )
1671
+ return [ ] if node . nil?
1672
+
1673
+ forwarding = [ ]
1674
+ forwarding << :* if node . rest . is_a? ( RestParameterNode ) && node . rest . name . nil?
1675
+ forwarding << :** if node . keyword_rest . is_a? ( KeywordRestParameterNode ) && node . keyword_rest . name . nil?
1676
+ forwarding << :& if !node . block . nil? && node . block . name . nil?
1677
+ forwarding |= [ :& , :"..." ] if node . keyword_rest . is_a? ( ForwardingParameterNode )
1678
+
1679
+ forwarding
1663
1680
end
1664
1681
1665
1682
# Blocks can have a special set of parameters that automatically expand
@@ -1732,7 +1749,7 @@ def visit_block(call, block)
1732
1749
else
1733
1750
builder . args ( nil , [ ] , nil , false )
1734
1751
end ,
1735
- visit ( block . body ) ,
1752
+ block . body &. accept ( copy_compiler ( forwarding : find_forwarding ( block . parameters &. parameters ) ) ) ,
1736
1753
token ( block . closing_loc )
1737
1754
)
1738
1755
else
0 commit comments