parser 2.6.0.0 → 3.1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/parser/all.rb +3 -0
- data/lib/parser/ast/processor.rb +48 -1
- data/lib/parser/base.rb +30 -6
- data/lib/parser/builders/default.rb +670 -38
- data/lib/parser/context.rb +24 -26
- data/lib/parser/current.rb +36 -9
- data/lib/parser/current_arg_stack.rb +46 -0
- data/lib/parser/diagnostic/engine.rb +1 -2
- data/lib/parser/diagnostic.rb +1 -1
- data/lib/parser/lexer/dedenter.rb +58 -49
- data/lib/parser/lexer/explanation.rb +1 -1
- data/lib/parser/lexer.rb +13837 -11893
- data/lib/parser/macruby.rb +2544 -2489
- data/lib/parser/max_numparam_stack.rb +56 -0
- data/lib/parser/messages.rb +78 -44
- data/lib/parser/meta.rb +13 -3
- data/lib/parser/ruby18.rb +2313 -2259
- data/lib/parser/ruby19.rb +2537 -2488
- data/lib/parser/ruby20.rb +2724 -2673
- data/lib/parser/ruby21.rb +2766 -2727
- data/lib/parser/ruby22.rb +2683 -2628
- data/lib/parser/ruby23.rb +2796 -2755
- data/lib/parser/ruby24.rb +2812 -2771
- data/lib/parser/ruby25.rb +2703 -2670
- data/lib/parser/ruby26.rb +2794 -2747
- data/lib/parser/ruby27.rb +7914 -0
- data/lib/parser/ruby28.rb +8047 -0
- data/lib/parser/ruby30.rb +8096 -0
- data/lib/parser/ruby31.rb +8354 -0
- data/lib/parser/rubymotion.rb +2527 -2485
- data/lib/parser/runner/ruby_parse.rb +2 -2
- data/lib/parser/runner/ruby_rewrite.rb +2 -2
- data/lib/parser/runner.rb +36 -2
- data/lib/parser/source/buffer.rb +53 -28
- data/lib/parser/source/comment/associator.rb +31 -8
- data/lib/parser/source/comment.rb +14 -1
- data/lib/parser/source/map/method_definition.rb +25 -0
- data/lib/parser/source/range.rb +19 -3
- data/lib/parser/source/tree_rewriter/action.rb +137 -28
- data/lib/parser/source/tree_rewriter.rb +144 -14
- data/lib/parser/static_environment.rb +23 -0
- data/lib/parser/tree_rewriter.rb +3 -3
- data/lib/parser/variables_stack.rb +36 -0
- data/lib/parser/version.rb +1 -1
- data/lib/parser.rb +4 -0
- data/parser.gemspec +12 -19
- metadata +34 -99
- data/.gitignore +0 -32
- data/.travis.yml +0 -45
- data/.yardopts +0 -21
- data/CHANGELOG.md +0 -943
- data/CONTRIBUTING.md +0 -17
- data/Gemfile +0 -10
- data/README.md +0 -301
- data/Rakefile +0 -165
- data/ci/run_rubocop_specs +0 -14
- data/doc/AST_FORMAT.md +0 -1735
- data/doc/CUSTOMIZATION.md +0 -37
- data/doc/INTERNALS.md +0 -21
- data/doc/css/.gitkeep +0 -0
- data/doc/css/common.css +0 -68
- data/lib/parser/lexer.rl +0 -2383
- data/lib/parser/macruby.y +0 -2198
- data/lib/parser/ruby18.y +0 -1934
- data/lib/parser/ruby19.y +0 -2175
- data/lib/parser/ruby20.y +0 -2353
- data/lib/parser/ruby21.y +0 -2357
- data/lib/parser/ruby22.y +0 -2364
- data/lib/parser/ruby23.y +0 -2370
- data/lib/parser/ruby24.y +0 -2408
- data/lib/parser/ruby25.y +0 -2405
- data/lib/parser/ruby26.y +0 -2413
- data/lib/parser/rubymotion.y +0 -2182
- data/test/bug_163/fixtures/input.rb +0 -5
- data/test/bug_163/fixtures/output.rb +0 -5
- data/test/bug_163/rewriter.rb +0 -20
- data/test/helper.rb +0 -52
- data/test/parse_helper.rb +0 -315
- data/test/racc_coverage_helper.rb +0 -133
- data/test/test_base.rb +0 -31
- data/test/test_current.rb +0 -27
- data/test/test_diagnostic.rb +0 -96
- data/test/test_diagnostic_engine.rb +0 -62
- data/test/test_encoding.rb +0 -99
- data/test/test_lexer.rb +0 -3543
- data/test/test_lexer_stack_state.rb +0 -78
- data/test/test_parse_helper.rb +0 -80
- data/test/test_parser.rb +0 -7087
- data/test/test_runner_rewrite.rb +0 -47
- data/test/test_source_buffer.rb +0 -162
- data/test/test_source_comment.rb +0 -36
- data/test/test_source_comment_associator.rb +0 -367
- data/test/test_source_map.rb +0 -15
- data/test/test_source_range.rb +0 -172
- data/test/test_source_rewriter.rb +0 -541
- data/test/test_source_rewriter_action.rb +0 -46
- data/test/test_source_tree_rewriter.rb +0 -173
- data/test/test_static_environment.rb +0 -45
- data/test/using_tree_rewriter/fixtures/input.rb +0 -3
- data/test/using_tree_rewriter/fixtures/output.rb +0 -3
- data/test/using_tree_rewriter/using_tree_rewriter.rb +0 -9
@@ -82,6 +82,129 @@ module Parser
|
|
82
82
|
|
83
83
|
@emit_index = false
|
84
84
|
|
85
|
+
class << self
|
86
|
+
##
|
87
|
+
# AST compatibility attribute; causes a single non-mlhs
|
88
|
+
# block argument to be wrapped in s(:procarg0).
|
89
|
+
#
|
90
|
+
# If set to false (the default), block arguments `|a|` are emitted as
|
91
|
+
# `s(:args, s(:procarg0, :a))`
|
92
|
+
#
|
93
|
+
# If set to true, block arguments `|a|` are emitted as
|
94
|
+
# `s(:args, s(:procarg0, s(:arg, :a))`
|
95
|
+
#
|
96
|
+
# @return [Boolean]
|
97
|
+
attr_accessor :emit_arg_inside_procarg0
|
98
|
+
end
|
99
|
+
|
100
|
+
@emit_arg_inside_procarg0 = false
|
101
|
+
|
102
|
+
class << self
|
103
|
+
##
|
104
|
+
# AST compatibility attribute; arguments forwarding initially
|
105
|
+
# didn't have support for leading arguments
|
106
|
+
# (i.e. `def m(a, ...); end` was a syntax error). However, Ruby 3.0
|
107
|
+
# added support for any number of arguments in front of the `...`.
|
108
|
+
#
|
109
|
+
# If set to false (the default):
|
110
|
+
# 1. `def m(...) end` is emitted as
|
111
|
+
# s(:def, :m, s(:forward_args), nil)
|
112
|
+
# 2. `def m(a, b, ...) end` is emitted as
|
113
|
+
# s(:def, :m,
|
114
|
+
# s(:args, s(:arg, :a), s(:arg, :b), s(:forward_arg)))
|
115
|
+
#
|
116
|
+
# If set to true it uses a single format:
|
117
|
+
# 1. `def m(...) end` is emitted as
|
118
|
+
# s(:def, :m, s(:args, s(:forward_arg)))
|
119
|
+
# 2. `def m(a, b, ...) end` is emitted as
|
120
|
+
# s(:def, :m, s(:args, s(:arg, :a), s(:arg, :b), s(:forward_arg)))
|
121
|
+
#
|
122
|
+
# It does't matter that much on 2.7 (because there can't be any leading arguments),
|
123
|
+
# but on 3.0 it should be better enabled to use a single AST format.
|
124
|
+
#
|
125
|
+
# @return [Boolean]
|
126
|
+
attr_accessor :emit_forward_arg
|
127
|
+
end
|
128
|
+
|
129
|
+
@emit_forward_arg = false
|
130
|
+
|
131
|
+
class << self
|
132
|
+
##
|
133
|
+
# AST compatibility attribute; Starting from Ruby 2.7 keyword arguments
|
134
|
+
# of method calls that are passed explicitly as a hash (i.e. with curly braces)
|
135
|
+
# are treated as positional arguments and Ruby 2.7 emits a warning on such method
|
136
|
+
# call. Ruby 3.0 given an ArgumentError.
|
137
|
+
#
|
138
|
+
# If set to false (the default) the last hash argument is emitted as `hash`:
|
139
|
+
#
|
140
|
+
# ```
|
141
|
+
# (send nil :foo
|
142
|
+
# (hash
|
143
|
+
# (pair
|
144
|
+
# (sym :bar)
|
145
|
+
# (int 42))))
|
146
|
+
# ```
|
147
|
+
#
|
148
|
+
# If set to true it is emitted as `kwargs`:
|
149
|
+
#
|
150
|
+
# ```
|
151
|
+
# (send nil :foo
|
152
|
+
# (kwargs
|
153
|
+
# (pair
|
154
|
+
# (sym :bar)
|
155
|
+
# (int 42))))
|
156
|
+
# ```
|
157
|
+
#
|
158
|
+
# Note that `kwargs` node is just a replacement for `hash` argument,
|
159
|
+
# so if there's are multiple arguments (or a `kwsplat`) all of them
|
160
|
+
# are wrapped into `kwargs` instead of `hash`:
|
161
|
+
#
|
162
|
+
# ```
|
163
|
+
# (send nil :foo
|
164
|
+
# (kwargs
|
165
|
+
# (pair
|
166
|
+
# (sym :a)
|
167
|
+
# (int 42))
|
168
|
+
# (kwsplat
|
169
|
+
# (send nil :b))
|
170
|
+
# (pair
|
171
|
+
# (sym :c)
|
172
|
+
# (int 10))))
|
173
|
+
# ```
|
174
|
+
attr_accessor :emit_kwargs
|
175
|
+
end
|
176
|
+
|
177
|
+
@emit_kwargs = false
|
178
|
+
|
179
|
+
class << self
|
180
|
+
##
|
181
|
+
# AST compatibility attribute; Starting from 3.0 Ruby returns
|
182
|
+
# true/false from single-line pattern matching with `in` keyword.
|
183
|
+
#
|
184
|
+
# Before 3.0 there was an exception if given value doesn't match pattern.
|
185
|
+
#
|
186
|
+
# NOTE: This attribute affects only Ruby 2.7 grammar.
|
187
|
+
# 3.0 grammar always emits `match_pattern`/`match_pattern_p`
|
188
|
+
#
|
189
|
+
# If compatibility attribute set to false `foo in bar` is emitted as `in_match`:
|
190
|
+
#
|
191
|
+
# ```
|
192
|
+
# (in-match
|
193
|
+
# (send nil :foo)
|
194
|
+
# (match-var :bar))
|
195
|
+
# ```
|
196
|
+
#
|
197
|
+
# If set to true it's emitted as `match_pattern_p`:
|
198
|
+
# ```
|
199
|
+
# (match-pattern-p
|
200
|
+
# (send nil :foo)
|
201
|
+
# (match-var :bar))
|
202
|
+
# ```
|
203
|
+
attr_accessor :emit_match_pattern
|
204
|
+
end
|
205
|
+
|
206
|
+
@emit_match_pattern = false
|
207
|
+
|
85
208
|
class << self
|
86
209
|
##
|
87
210
|
# @api private
|
@@ -90,6 +213,10 @@ module Parser
|
|
90
213
|
@emit_procarg0 = true
|
91
214
|
@emit_encoding = true
|
92
215
|
@emit_index = true
|
216
|
+
@emit_arg_inside_procarg0 = true
|
217
|
+
@emit_forward_arg = true
|
218
|
+
@emit_kwargs = true
|
219
|
+
@emit_match_pattern = true
|
93
220
|
end
|
94
221
|
end
|
95
222
|
|
@@ -262,18 +389,23 @@ module Parser
|
|
262
389
|
if !dedent_level.nil?
|
263
390
|
dedenter = Lexer::Dedenter.new(dedent_level)
|
264
391
|
|
265
|
-
|
392
|
+
case node.type
|
393
|
+
when :str
|
266
394
|
str = node.children.first
|
267
395
|
dedenter.dedent(str)
|
268
|
-
|
269
|
-
node.children.
|
396
|
+
when :dstr, :xstr
|
397
|
+
children = node.children.map do |str_node|
|
270
398
|
if str_node.type == :str
|
271
399
|
str = str_node.children.first
|
272
400
|
dedenter.dedent(str)
|
401
|
+
next nil if str.empty?
|
273
402
|
else
|
274
403
|
dedenter.interrupt
|
275
404
|
end
|
405
|
+
str_node
|
276
406
|
end
|
407
|
+
|
408
|
+
node = node.updated(nil, children.compact)
|
277
409
|
end
|
278
410
|
end
|
279
411
|
|
@@ -386,12 +518,52 @@ module Parser
|
|
386
518
|
n(:pair, [ key, value ], pair_map)
|
387
519
|
end
|
388
520
|
|
521
|
+
def pair_label(key_t)
|
522
|
+
key_l = loc(key_t)
|
523
|
+
value_l = key_l.adjust(end_pos: -1)
|
524
|
+
|
525
|
+
label = value(key_t)
|
526
|
+
value =
|
527
|
+
if label =~ /\A[[:lower:]]/
|
528
|
+
n(:ident, [ label.to_sym ], Source::Map::Variable.new(value_l))
|
529
|
+
else
|
530
|
+
n(:const, [ nil, label.to_sym ], Source::Map::Constant.new(nil, value_l, value_l))
|
531
|
+
end
|
532
|
+
pair_keyword(key_t, accessible(value))
|
533
|
+
end
|
534
|
+
|
389
535
|
def kwsplat(dstar_t, arg)
|
390
536
|
n(:kwsplat, [ arg ],
|
391
537
|
unary_op_map(dstar_t, arg))
|
392
538
|
end
|
393
539
|
|
394
540
|
def associate(begin_t, pairs, end_t)
|
541
|
+
0.upto(pairs.length - 1) do |i|
|
542
|
+
(i + 1).upto(pairs.length - 1) do |j|
|
543
|
+
key1, = *pairs[i]
|
544
|
+
key2, = *pairs[j]
|
545
|
+
|
546
|
+
do_warn = false
|
547
|
+
|
548
|
+
# keys have to be simple nodes, MRI ignores equal composite keys like
|
549
|
+
# `{ a(1) => 1, a(1) => 1 }`
|
550
|
+
case key1.type
|
551
|
+
when :sym, :str, :int, :float
|
552
|
+
if key1 == key2
|
553
|
+
do_warn = true
|
554
|
+
end
|
555
|
+
when :rational, :complex, :regexp
|
556
|
+
if @parser.version >= 31 && key1 == key2
|
557
|
+
do_warn = true
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
if do_warn
|
562
|
+
diagnostic :warning, :duplicate_hash_key, nil, key2.loc.expression
|
563
|
+
end
|
564
|
+
end
|
565
|
+
end
|
566
|
+
|
395
567
|
n(:hash, [ *pairs ],
|
396
568
|
collection_map(begin_t, pairs, end_t))
|
397
569
|
end
|
@@ -476,14 +648,29 @@ module Parser
|
|
476
648
|
when :ident
|
477
649
|
name, = *node
|
478
650
|
|
479
|
-
if
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
651
|
+
if %w[? !].any? { |c| name.to_s.end_with?(c) }
|
652
|
+
diagnostic :error, :invalid_id_to_get,
|
653
|
+
{ :identifier => name.to_s }, node.loc.expression
|
654
|
+
end
|
655
|
+
|
656
|
+
# Numbered parameters are not declared anywhere,
|
657
|
+
# so they take precedence over method calls in numblock contexts
|
658
|
+
if @parser.version >= 27 && @parser.try_declare_numparam(node)
|
659
|
+
return node.updated(:lvar)
|
660
|
+
end
|
661
|
+
|
662
|
+
unless @parser.static_env.declared?(name)
|
663
|
+
return n(:send, [ nil, name ],
|
484
664
|
var_send_map(node))
|
485
665
|
end
|
486
666
|
|
667
|
+
if name.to_s == parser.current_arg_stack.top
|
668
|
+
diagnostic :error, :circular_argument_reference,
|
669
|
+
{ :var_name => name.to_s }, node.loc.expression
|
670
|
+
end
|
671
|
+
|
672
|
+
node.updated(:lvar)
|
673
|
+
|
487
674
|
else
|
488
675
|
node
|
489
676
|
end
|
@@ -527,7 +714,7 @@ module Parser
|
|
527
714
|
node.updated(:gvasgn)
|
528
715
|
|
529
716
|
when :const
|
530
|
-
|
717
|
+
if @parser.context.in_def
|
531
718
|
diagnostic :error, :dynamic_const, nil, node.loc.expression
|
532
719
|
end
|
533
720
|
|
@@ -535,10 +722,28 @@ module Parser
|
|
535
722
|
|
536
723
|
when :ident
|
537
724
|
name, = *node
|
725
|
+
|
726
|
+
var_name = node.children[0].to_s
|
727
|
+
name_loc = node.loc.expression
|
728
|
+
|
729
|
+
check_assignment_to_numparam(var_name, name_loc)
|
730
|
+
check_reserved_for_numparam(var_name, name_loc)
|
731
|
+
|
538
732
|
@parser.static_env.declare(name)
|
539
733
|
|
540
734
|
node.updated(:lvasgn)
|
541
735
|
|
736
|
+
when :match_var
|
737
|
+
name, = *node
|
738
|
+
|
739
|
+
var_name = node.children[0].to_s
|
740
|
+
name_loc = node.loc.expression
|
741
|
+
|
742
|
+
check_assignment_to_numparam(var_name, name_loc)
|
743
|
+
check_reserved_for_numparam(var_name, name_loc)
|
744
|
+
|
745
|
+
node
|
746
|
+
|
542
747
|
when :nil, :self, :true, :false,
|
543
748
|
:__FILE__, :__LINE__, :__ENCODING__
|
544
749
|
diagnostic :error, :invalid_assignment, nil, node.loc.expression
|
@@ -624,23 +829,38 @@ module Parser
|
|
624
829
|
|
625
830
|
def def_method(def_t, name_t, args,
|
626
831
|
body, end_t)
|
832
|
+
check_reserved_for_numparam(value(name_t), loc(name_t))
|
833
|
+
|
627
834
|
n(:def, [ value(name_t).to_sym, args, body ],
|
628
835
|
definition_map(def_t, nil, name_t, end_t))
|
629
836
|
end
|
630
837
|
|
838
|
+
def def_endless_method(def_t, name_t, args,
|
839
|
+
assignment_t, body)
|
840
|
+
check_reserved_for_numparam(value(name_t), loc(name_t))
|
841
|
+
|
842
|
+
n(:def, [ value(name_t).to_sym, args, body ],
|
843
|
+
endless_definition_map(def_t, nil, name_t, assignment_t, body))
|
844
|
+
end
|
845
|
+
|
631
846
|
def def_singleton(def_t, definee, dot_t,
|
632
847
|
name_t, args,
|
633
848
|
body, end_t)
|
634
|
-
|
635
|
-
|
636
|
-
:regexp, :array, :hash
|
849
|
+
validate_definee(definee)
|
850
|
+
check_reserved_for_numparam(value(name_t), loc(name_t))
|
637
851
|
|
638
|
-
|
852
|
+
n(:defs, [ definee, value(name_t).to_sym, args, body ],
|
853
|
+
definition_map(def_t, dot_t, name_t, end_t))
|
854
|
+
end
|
639
855
|
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
856
|
+
def def_endless_singleton(def_t, definee, dot_t,
|
857
|
+
name_t, args,
|
858
|
+
assignment_t, body)
|
859
|
+
validate_definee(definee)
|
860
|
+
check_reserved_for_numparam(value(name_t), loc(name_t))
|
861
|
+
|
862
|
+
n(:defs, [ definee, value(name_t).to_sym, args, body ],
|
863
|
+
endless_definition_map(def_t, dot_t, name_t, assignment_t, body))
|
644
864
|
end
|
645
865
|
|
646
866
|
def undef_method(undef_t, names)
|
@@ -659,16 +879,44 @@ module Parser
|
|
659
879
|
|
660
880
|
def args(begin_t, args, end_t, check_args=true)
|
661
881
|
args = check_duplicate_args(args) if check_args
|
662
|
-
|
663
|
-
|
882
|
+
validate_no_forward_arg_after_restarg(args)
|
883
|
+
|
884
|
+
map = collection_map(begin_t, args, end_t)
|
885
|
+
if !self.class.emit_forward_arg && args.length == 1 && args[0].type == :forward_arg
|
886
|
+
n(:forward_args, [], map)
|
887
|
+
else
|
888
|
+
n(:args, args, map)
|
889
|
+
end
|
890
|
+
end
|
891
|
+
|
892
|
+
def numargs(max_numparam)
|
893
|
+
n(:numargs, [ max_numparam ], nil)
|
894
|
+
end
|
895
|
+
|
896
|
+
def forward_only_args(begin_t, dots_t, end_t)
|
897
|
+
if self.class.emit_forward_arg
|
898
|
+
arg = forward_arg(dots_t)
|
899
|
+
n(:args, [ arg ],
|
900
|
+
collection_map(begin_t, [ arg ], end_t))
|
901
|
+
else
|
902
|
+
n(:forward_args, [], collection_map(begin_t, token_map(dots_t), end_t))
|
903
|
+
end
|
904
|
+
end
|
905
|
+
|
906
|
+
def forward_arg(dots_t)
|
907
|
+
n(:forward_arg, [], token_map(dots_t))
|
664
908
|
end
|
665
909
|
|
666
910
|
def arg(name_t)
|
911
|
+
check_reserved_for_numparam(value(name_t), loc(name_t))
|
912
|
+
|
667
913
|
n(:arg, [ value(name_t).to_sym ],
|
668
914
|
variable_map(name_t))
|
669
915
|
end
|
670
916
|
|
671
917
|
def optarg(name_t, eql_t, value)
|
918
|
+
check_reserved_for_numparam(value(name_t), loc(name_t))
|
919
|
+
|
672
920
|
n(:optarg, [ value(name_t).to_sym, value ],
|
673
921
|
variable_map(name_t).
|
674
922
|
with_operator(loc(eql_t)).
|
@@ -677,6 +925,7 @@ module Parser
|
|
677
925
|
|
678
926
|
def restarg(star_t, name_t=nil)
|
679
927
|
if name_t
|
928
|
+
check_reserved_for_numparam(value(name_t), loc(name_t))
|
680
929
|
n(:restarg, [ value(name_t).to_sym ],
|
681
930
|
arg_prefix_map(star_t, name_t))
|
682
931
|
else
|
@@ -686,17 +935,23 @@ module Parser
|
|
686
935
|
end
|
687
936
|
|
688
937
|
def kwarg(name_t)
|
938
|
+
check_reserved_for_numparam(value(name_t), loc(name_t))
|
939
|
+
|
689
940
|
n(:kwarg, [ value(name_t).to_sym ],
|
690
941
|
kwarg_map(name_t))
|
691
942
|
end
|
692
943
|
|
693
944
|
def kwoptarg(name_t, value)
|
945
|
+
check_reserved_for_numparam(value(name_t), loc(name_t))
|
946
|
+
|
694
947
|
n(:kwoptarg, [ value(name_t).to_sym, value ],
|
695
948
|
kwarg_map(name_t, value))
|
696
949
|
end
|
697
950
|
|
698
951
|
def kwrestarg(dstar_t, name_t=nil)
|
699
952
|
if name_t
|
953
|
+
check_reserved_for_numparam(value(name_t), loc(name_t))
|
954
|
+
|
700
955
|
n(:kwrestarg, [ value(name_t).to_sym ],
|
701
956
|
arg_prefix_map(dstar_t, name_t))
|
702
957
|
else
|
@@ -705,19 +960,36 @@ module Parser
|
|
705
960
|
end
|
706
961
|
end
|
707
962
|
|
963
|
+
def kwnilarg(dstar_t, nil_t)
|
964
|
+
n0(:kwnilarg,
|
965
|
+
arg_prefix_map(dstar_t, nil_t))
|
966
|
+
end
|
967
|
+
|
708
968
|
def shadowarg(name_t)
|
969
|
+
check_reserved_for_numparam(value(name_t), loc(name_t))
|
970
|
+
|
709
971
|
n(:shadowarg, [ value(name_t).to_sym ],
|
710
972
|
variable_map(name_t))
|
711
973
|
end
|
712
974
|
|
713
975
|
def blockarg(amper_t, name_t)
|
714
|
-
|
976
|
+
if !name_t.nil?
|
977
|
+
check_reserved_for_numparam(value(name_t), loc(name_t))
|
978
|
+
end
|
979
|
+
|
980
|
+
arg_name = name_t ? value(name_t).to_sym : nil
|
981
|
+
n(:blockarg, [ arg_name ],
|
715
982
|
arg_prefix_map(amper_t, name_t))
|
716
983
|
end
|
717
984
|
|
718
985
|
def procarg0(arg)
|
719
986
|
if self.class.emit_procarg0
|
720
|
-
arg.
|
987
|
+
if arg.type == :arg && self.class.emit_arg_inside_procarg0
|
988
|
+
n(:procarg0, [ arg ],
|
989
|
+
Source::Map::Collection.new(nil, nil, arg.location.expression))
|
990
|
+
else
|
991
|
+
arg.updated(:procarg0)
|
992
|
+
end
|
721
993
|
else
|
722
994
|
arg
|
723
995
|
end
|
@@ -802,9 +1074,18 @@ module Parser
|
|
802
1074
|
end
|
803
1075
|
end
|
804
1076
|
|
1077
|
+
def forwarded_args(dots_t)
|
1078
|
+
n(:forwarded_args, [], token_map(dots_t))
|
1079
|
+
end
|
1080
|
+
|
805
1081
|
def call_method(receiver, dot_t, selector_t,
|
806
1082
|
lparen_t=nil, args=[], rparen_t=nil)
|
807
1083
|
type = call_type_for_dot(dot_t)
|
1084
|
+
|
1085
|
+
if self.class.emit_kwargs
|
1086
|
+
rewrite_hash_args_to_kwargs(args)
|
1087
|
+
end
|
1088
|
+
|
808
1089
|
if selector_t.nil?
|
809
1090
|
n(type, [ receiver, :call, *args ],
|
810
1091
|
send_map(receiver, dot_t, nil, lparen_t, args, rparen_t))
|
@@ -831,19 +1112,26 @@ module Parser
|
|
831
1112
|
end
|
832
1113
|
|
833
1114
|
last_arg = call_args.last
|
834
|
-
if last_arg && last_arg.type == :block_pass
|
1115
|
+
if last_arg && (last_arg.type == :block_pass || last_arg.type == :forwarded_args)
|
835
1116
|
diagnostic :error, :block_and_blockarg, nil, last_arg.loc.expression, [loc(begin_t)]
|
836
1117
|
end
|
837
1118
|
|
1119
|
+
if args.type == :numargs
|
1120
|
+
block_type = :numblock
|
1121
|
+
args = args.children[0]
|
1122
|
+
else
|
1123
|
+
block_type = :block
|
1124
|
+
end
|
1125
|
+
|
838
1126
|
if [:send, :csend, :index, :super, :zsuper, :lambda].include?(method_call.type)
|
839
|
-
n(
|
1127
|
+
n(block_type, [ method_call, args, body ],
|
840
1128
|
block_map(method_call.loc.expression, begin_t, end_t))
|
841
1129
|
else
|
842
1130
|
# Code like "return foo 1 do end" is reduced in a weird sequence.
|
843
1131
|
# Here, method_call is actually (return).
|
844
1132
|
actual_send, = *method_call
|
845
1133
|
block =
|
846
|
-
n(
|
1134
|
+
n(block_type, [ actual_send, args, body ],
|
847
1135
|
block_map(actual_send.loc.expression, begin_t, end_t))
|
848
1136
|
|
849
1137
|
n(method_call.type, [ block ],
|
@@ -875,6 +1163,10 @@ module Parser
|
|
875
1163
|
end
|
876
1164
|
|
877
1165
|
def index(receiver, lbrack_t, indexes, rbrack_t)
|
1166
|
+
if self.class.emit_kwargs
|
1167
|
+
rewrite_hash_args_to_kwargs(indexes)
|
1168
|
+
end
|
1169
|
+
|
878
1170
|
if self.class.emit_index
|
879
1171
|
n(:index, [ receiver, *indexes ],
|
880
1172
|
index_map(receiver, lbrack_t, rbrack_t))
|
@@ -1037,6 +1329,10 @@ module Parser
|
|
1037
1329
|
end
|
1038
1330
|
end
|
1039
1331
|
|
1332
|
+
if %i[yield super].include?(type) && self.class.emit_kwargs
|
1333
|
+
rewrite_hash_args_to_kwargs(args)
|
1334
|
+
end
|
1335
|
+
|
1040
1336
|
n(type, args,
|
1041
1337
|
keyword_map(keyword_t, lparen_t, args, rparen_t))
|
1042
1338
|
end
|
@@ -1155,6 +1451,219 @@ module Parser
|
|
1155
1451
|
end
|
1156
1452
|
end
|
1157
1453
|
|
1454
|
+
#
|
1455
|
+
# PATTERN MATCHING
|
1456
|
+
#
|
1457
|
+
|
1458
|
+
def case_match(case_t, expr, in_bodies, else_t, else_body, end_t)
|
1459
|
+
else_body = n(:empty_else, nil, token_map(else_t)) if else_t && !else_body
|
1460
|
+
n(:case_match, [ expr, *(in_bodies << else_body)],
|
1461
|
+
condition_map(case_t, expr, nil, nil, else_t, else_body, end_t))
|
1462
|
+
end
|
1463
|
+
|
1464
|
+
def in_match(lhs, in_t, rhs)
|
1465
|
+
n(:in_match, [lhs, rhs],
|
1466
|
+
binary_op_map(lhs, in_t, rhs))
|
1467
|
+
end
|
1468
|
+
|
1469
|
+
def match_pattern(lhs, match_t, rhs)
|
1470
|
+
n(:match_pattern, [lhs, rhs],
|
1471
|
+
binary_op_map(lhs, match_t, rhs))
|
1472
|
+
end
|
1473
|
+
|
1474
|
+
def match_pattern_p(lhs, match_t, rhs)
|
1475
|
+
n(:match_pattern_p, [lhs, rhs],
|
1476
|
+
binary_op_map(lhs, match_t, rhs))
|
1477
|
+
end
|
1478
|
+
|
1479
|
+
def in_pattern(in_t, pattern, guard, then_t, body)
|
1480
|
+
children = [pattern, guard, body]
|
1481
|
+
n(:in_pattern, children,
|
1482
|
+
keyword_map(in_t, then_t, children.compact, nil))
|
1483
|
+
end
|
1484
|
+
|
1485
|
+
def if_guard(if_t, if_body)
|
1486
|
+
n(:if_guard, [if_body], guard_map(if_t, if_body))
|
1487
|
+
end
|
1488
|
+
|
1489
|
+
def unless_guard(unless_t, unless_body)
|
1490
|
+
n(:unless_guard, [unless_body], guard_map(unless_t, unless_body))
|
1491
|
+
end
|
1492
|
+
|
1493
|
+
def match_var(name_t)
|
1494
|
+
name = value(name_t).to_sym
|
1495
|
+
name_l = loc(name_t)
|
1496
|
+
|
1497
|
+
check_lvar_name(name, name_l)
|
1498
|
+
check_duplicate_pattern_variable(name, name_l)
|
1499
|
+
@parser.static_env.declare(name)
|
1500
|
+
|
1501
|
+
n(:match_var, [ name ],
|
1502
|
+
variable_map(name_t))
|
1503
|
+
end
|
1504
|
+
|
1505
|
+
def match_hash_var(name_t)
|
1506
|
+
name = value(name_t).to_sym
|
1507
|
+
|
1508
|
+
expr_l = loc(name_t)
|
1509
|
+
name_l = expr_l.adjust(end_pos: -1)
|
1510
|
+
|
1511
|
+
check_lvar_name(name, name_l)
|
1512
|
+
check_duplicate_pattern_variable(name, name_l)
|
1513
|
+
@parser.static_env.declare(name)
|
1514
|
+
|
1515
|
+
n(:match_var, [ name ],
|
1516
|
+
Source::Map::Variable.new(name_l, expr_l))
|
1517
|
+
end
|
1518
|
+
|
1519
|
+
def match_hash_var_from_str(begin_t, strings, end_t)
|
1520
|
+
if strings.length > 1
|
1521
|
+
diagnostic :error, :pm_interp_in_var_name, nil, loc(begin_t).join(loc(end_t))
|
1522
|
+
end
|
1523
|
+
|
1524
|
+
string = strings[0]
|
1525
|
+
|
1526
|
+
case string.type
|
1527
|
+
when :str
|
1528
|
+
# MRI supports plain strings in hash pattern matching
|
1529
|
+
name, = *string
|
1530
|
+
name_l = string.loc.expression
|
1531
|
+
|
1532
|
+
check_lvar_name(name, name_l)
|
1533
|
+
check_duplicate_pattern_variable(name, name_l)
|
1534
|
+
|
1535
|
+
@parser.static_env.declare(name)
|
1536
|
+
|
1537
|
+
if (begin_l = string.loc.begin)
|
1538
|
+
# exclude beginning of the string from the location of the variable
|
1539
|
+
name_l = name_l.adjust(begin_pos: begin_l.length)
|
1540
|
+
end
|
1541
|
+
|
1542
|
+
if (end_l = string.loc.end)
|
1543
|
+
# exclude end of the string from the location of the variable
|
1544
|
+
name_l = name_l.adjust(end_pos: -end_l.length)
|
1545
|
+
end
|
1546
|
+
|
1547
|
+
expr_l = loc(begin_t).join(string.loc.expression).join(loc(end_t))
|
1548
|
+
n(:match_var, [ name.to_sym ],
|
1549
|
+
Source::Map::Variable.new(name_l, expr_l))
|
1550
|
+
when :begin
|
1551
|
+
match_hash_var_from_str(begin_t, string.children, end_t)
|
1552
|
+
else
|
1553
|
+
# we only can get here if there is an interpolation, e.g., ``in "#{ a }":`
|
1554
|
+
diagnostic :error, :pm_interp_in_var_name, nil, loc(begin_t).join(loc(end_t))
|
1555
|
+
end
|
1556
|
+
end
|
1557
|
+
|
1558
|
+
def match_rest(star_t, name_t = nil)
|
1559
|
+
if name_t.nil?
|
1560
|
+
n0(:match_rest,
|
1561
|
+
unary_op_map(star_t))
|
1562
|
+
else
|
1563
|
+
name = match_var(name_t)
|
1564
|
+
n(:match_rest, [ name ],
|
1565
|
+
unary_op_map(star_t, name))
|
1566
|
+
end
|
1567
|
+
end
|
1568
|
+
|
1569
|
+
def hash_pattern(lbrace_t, kwargs, rbrace_t)
|
1570
|
+
args = check_duplicate_args(kwargs)
|
1571
|
+
n(:hash_pattern, args,
|
1572
|
+
collection_map(lbrace_t, args, rbrace_t))
|
1573
|
+
end
|
1574
|
+
|
1575
|
+
def array_pattern(lbrack_t, elements, rbrack_t)
|
1576
|
+
return n(:array_pattern, nil, collection_map(lbrack_t, [], rbrack_t)) if elements.nil?
|
1577
|
+
|
1578
|
+
trailing_comma = false
|
1579
|
+
|
1580
|
+
node_elements = elements.map do |element|
|
1581
|
+
if element.type == :match_with_trailing_comma
|
1582
|
+
trailing_comma = true
|
1583
|
+
element.children.first
|
1584
|
+
else
|
1585
|
+
trailing_comma = false
|
1586
|
+
element
|
1587
|
+
end
|
1588
|
+
end
|
1589
|
+
|
1590
|
+
node_type = trailing_comma ? :array_pattern_with_tail : :array_pattern
|
1591
|
+
|
1592
|
+
n(node_type, node_elements,
|
1593
|
+
collection_map(lbrack_t, elements, rbrack_t))
|
1594
|
+
end
|
1595
|
+
|
1596
|
+
def find_pattern(lbrack_t, elements, rbrack_t)
|
1597
|
+
n(:find_pattern, elements,
|
1598
|
+
collection_map(lbrack_t, elements, rbrack_t))
|
1599
|
+
end
|
1600
|
+
|
1601
|
+
def match_with_trailing_comma(match, comma_t)
|
1602
|
+
n(:match_with_trailing_comma, [ match ], expr_map(match.loc.expression.join(loc(comma_t))))
|
1603
|
+
end
|
1604
|
+
|
1605
|
+
def const_pattern(const, ldelim_t, pattern, rdelim_t)
|
1606
|
+
n(:const_pattern, [const, pattern],
|
1607
|
+
Source::Map::Collection.new(
|
1608
|
+
loc(ldelim_t), loc(rdelim_t),
|
1609
|
+
const.loc.expression.join(loc(rdelim_t))
|
1610
|
+
)
|
1611
|
+
)
|
1612
|
+
end
|
1613
|
+
|
1614
|
+
def pin(pin_t, var)
|
1615
|
+
n(:pin, [ var ],
|
1616
|
+
send_unary_op_map(pin_t, var))
|
1617
|
+
end
|
1618
|
+
|
1619
|
+
def match_alt(left, pipe_t, right)
|
1620
|
+
source_map = binary_op_map(left, pipe_t, right)
|
1621
|
+
|
1622
|
+
n(:match_alt, [ left, right ],
|
1623
|
+
source_map)
|
1624
|
+
end
|
1625
|
+
|
1626
|
+
def match_as(value, assoc_t, as)
|
1627
|
+
source_map = binary_op_map(value, assoc_t, as)
|
1628
|
+
|
1629
|
+
n(:match_as, [ value, as ],
|
1630
|
+
source_map)
|
1631
|
+
end
|
1632
|
+
|
1633
|
+
def match_nil_pattern(dstar_t, nil_t)
|
1634
|
+
n0(:match_nil_pattern,
|
1635
|
+
arg_prefix_map(dstar_t, nil_t))
|
1636
|
+
end
|
1637
|
+
|
1638
|
+
def match_pair(label_type, label, value)
|
1639
|
+
if label_type == :label
|
1640
|
+
check_duplicate_pattern_key(label[0], label[1])
|
1641
|
+
pair_keyword(label, value)
|
1642
|
+
else
|
1643
|
+
begin_t, parts, end_t = label
|
1644
|
+
label_loc = loc(begin_t).join(loc(end_t))
|
1645
|
+
|
1646
|
+
# quoted label like "label": value
|
1647
|
+
if (var_name = static_string(parts))
|
1648
|
+
check_duplicate_pattern_key(var_name, label_loc)
|
1649
|
+
else
|
1650
|
+
diagnostic :error, :pm_interp_in_var_name, nil, label_loc
|
1651
|
+
end
|
1652
|
+
|
1653
|
+
pair_quoted(begin_t, parts, end_t, value)
|
1654
|
+
end
|
1655
|
+
end
|
1656
|
+
|
1657
|
+
def match_label(label_type, label)
|
1658
|
+
if label_type == :label
|
1659
|
+
match_hash_var(label)
|
1660
|
+
else
|
1661
|
+
# quoted label like "label": value
|
1662
|
+
begin_t, strings, end_t = label
|
1663
|
+
match_hash_var_from_str(begin_t, strings, end_t)
|
1664
|
+
end
|
1665
|
+
end
|
1666
|
+
|
1158
1667
|
private
|
1159
1668
|
|
1160
1669
|
#
|
@@ -1210,18 +1719,18 @@ module Parser
|
|
1210
1719
|
case this_arg.type
|
1211
1720
|
when :arg, :optarg, :restarg, :blockarg,
|
1212
1721
|
:kwarg, :kwoptarg, :kwrestarg,
|
1213
|
-
:shadowarg
|
1722
|
+
:shadowarg
|
1214
1723
|
|
1215
|
-
|
1724
|
+
check_duplicate_arg(this_arg, map)
|
1216
1725
|
|
1217
|
-
|
1218
|
-
that_name, = *that_arg
|
1726
|
+
when :procarg0
|
1219
1727
|
|
1220
|
-
if
|
1221
|
-
|
1222
|
-
|
1223
|
-
|
1224
|
-
|
1728
|
+
if this_arg.children[0].is_a?(Symbol)
|
1729
|
+
# s(:procarg0, :a)
|
1730
|
+
check_duplicate_arg(this_arg, map)
|
1731
|
+
else
|
1732
|
+
# s(:procarg0, s(:arg, :a), ...)
|
1733
|
+
check_duplicate_args(this_arg.children, map)
|
1225
1734
|
end
|
1226
1735
|
|
1227
1736
|
when :mlhs
|
@@ -1230,6 +1739,60 @@ module Parser
|
|
1230
1739
|
end
|
1231
1740
|
end
|
1232
1741
|
|
1742
|
+
def check_duplicate_arg(this_arg, map={})
|
1743
|
+
this_name, = *this_arg
|
1744
|
+
|
1745
|
+
that_arg = map[this_name]
|
1746
|
+
that_name, = *that_arg
|
1747
|
+
|
1748
|
+
if that_arg.nil?
|
1749
|
+
map[this_name] = this_arg
|
1750
|
+
elsif arg_name_collides?(this_name, that_name)
|
1751
|
+
diagnostic :error, :duplicate_argument, nil,
|
1752
|
+
this_arg.loc.name, [ that_arg.loc.name ]
|
1753
|
+
end
|
1754
|
+
end
|
1755
|
+
|
1756
|
+
def validate_no_forward_arg_after_restarg(args)
|
1757
|
+
restarg = nil
|
1758
|
+
forward_arg = nil
|
1759
|
+
args.each do |arg|
|
1760
|
+
case arg.type
|
1761
|
+
when :restarg then restarg = arg
|
1762
|
+
when :forward_arg then forward_arg = arg
|
1763
|
+
end
|
1764
|
+
end
|
1765
|
+
|
1766
|
+
if !forward_arg.nil? && !restarg.nil?
|
1767
|
+
diagnostic :error, :forward_arg_after_restarg, nil, forward_arg.loc.expression, [restarg.loc.expression]
|
1768
|
+
end
|
1769
|
+
end
|
1770
|
+
|
1771
|
+
def check_assignment_to_numparam(name, loc)
|
1772
|
+
# MRI < 2.7 treats numbered parameters as regular variables
|
1773
|
+
# and so it's allowed to perform assignments like `_1 = 42`.
|
1774
|
+
return if @parser.version < 27
|
1775
|
+
|
1776
|
+
assigning_to_numparam =
|
1777
|
+
@parser.context.in_dynamic_block? &&
|
1778
|
+
name =~ /\A_([1-9])\z/ &&
|
1779
|
+
@parser.max_numparam_stack.has_numparams?
|
1780
|
+
|
1781
|
+
if assigning_to_numparam
|
1782
|
+
diagnostic :error, :cant_assign_to_numparam, { :name => name }, loc
|
1783
|
+
end
|
1784
|
+
end
|
1785
|
+
|
1786
|
+
def check_reserved_for_numparam(name, loc)
|
1787
|
+
# MRI < 3.0 accepts assignemnt to variables like _1
|
1788
|
+
# if it's not a numbered parameter. MRI 3.0 and newer throws an error.
|
1789
|
+
return if @parser.version < 30
|
1790
|
+
|
1791
|
+
if name =~ /\A_([1-9])\z/
|
1792
|
+
diagnostic :error, :reserved_for_numparam, { :name => name }, loc
|
1793
|
+
end
|
1794
|
+
end
|
1795
|
+
|
1233
1796
|
def arg_name_collides?(this_name, that_name)
|
1234
1797
|
case @parser.version
|
1235
1798
|
when 18
|
@@ -1245,6 +1808,32 @@ module Parser
|
|
1245
1808
|
end
|
1246
1809
|
end
|
1247
1810
|
|
1811
|
+
def check_lvar_name(name, loc)
|
1812
|
+
if name =~ /\A[[[:lower:]]_][[[:alnum:]]_]*\z/
|
1813
|
+
# OK
|
1814
|
+
else
|
1815
|
+
diagnostic :error, :lvar_name, { name: name }, loc
|
1816
|
+
end
|
1817
|
+
end
|
1818
|
+
|
1819
|
+
def check_duplicate_pattern_variable(name, loc)
|
1820
|
+
return if name.to_s.start_with?('_')
|
1821
|
+
|
1822
|
+
if @parser.pattern_variables.declared?(name)
|
1823
|
+
diagnostic :error, :duplicate_variable_name, { name: name.to_s }, loc
|
1824
|
+
end
|
1825
|
+
|
1826
|
+
@parser.pattern_variables.declare(name)
|
1827
|
+
end
|
1828
|
+
|
1829
|
+
def check_duplicate_pattern_key(name, loc)
|
1830
|
+
if @parser.pattern_hash_keys.declared?(name)
|
1831
|
+
diagnostic :error, :duplicate_pattern_key, { name: name.to_s }, loc
|
1832
|
+
end
|
1833
|
+
|
1834
|
+
@parser.pattern_hash_keys.declare(name)
|
1835
|
+
end
|
1836
|
+
|
1248
1837
|
#
|
1249
1838
|
# SOURCE MAPS
|
1250
1839
|
#
|
@@ -1389,10 +1978,12 @@ module Parser
|
|
1389
1978
|
end
|
1390
1979
|
|
1391
1980
|
def range_map(start_e, op_t, end_e)
|
1392
|
-
if end_e
|
1981
|
+
if start_e && end_e
|
1393
1982
|
expr_l = join_exprs(start_e, end_e)
|
1394
|
-
|
1983
|
+
elsif start_e
|
1395
1984
|
expr_l = start_e.loc.expression.join(loc(op_t))
|
1985
|
+
elsif end_e
|
1986
|
+
expr_l = loc(op_t).join(end_e.loc.expression)
|
1396
1987
|
end
|
1397
1988
|
|
1398
1989
|
Source::Map::Operator.new(loc(op_t), expr_l)
|
@@ -1432,9 +2023,17 @@ module Parser
|
|
1432
2023
|
end
|
1433
2024
|
|
1434
2025
|
def definition_map(keyword_t, operator_t, name_t, end_t)
|
1435
|
-
Source::Map::
|
1436
|
-
|
1437
|
-
|
2026
|
+
Source::Map::MethodDefinition.new(loc(keyword_t),
|
2027
|
+
loc(operator_t), loc(name_t),
|
2028
|
+
loc(end_t), nil, nil)
|
2029
|
+
end
|
2030
|
+
|
2031
|
+
def endless_definition_map(keyword_t, operator_t, name_t, assignment_t, body_e)
|
2032
|
+
body_l = body_e.loc.expression
|
2033
|
+
|
2034
|
+
Source::Map::MethodDefinition.new(loc(keyword_t),
|
2035
|
+
loc(operator_t), loc(name_t), nil,
|
2036
|
+
loc(assignment_t), body_l)
|
1438
2037
|
end
|
1439
2038
|
|
1440
2039
|
def send_map(receiver_e, dot_t, selector_t, begin_t=nil, args=[], end_t=nil)
|
@@ -1591,6 +2190,13 @@ module Parser
|
|
1591
2190
|
begin_l.join(end_l))
|
1592
2191
|
end
|
1593
2192
|
|
2193
|
+
def guard_map(keyword_t, guard_body_e)
|
2194
|
+
keyword_l = loc(keyword_t)
|
2195
|
+
guard_body_l = guard_body_e.loc.expression
|
2196
|
+
|
2197
|
+
Source::Map::Keyword.new(keyword_l, nil, nil, keyword_l.join(guard_body_l))
|
2198
|
+
end
|
2199
|
+
|
1594
2200
|
#
|
1595
2201
|
# HELPERS
|
1596
2202
|
#
|
@@ -1672,6 +2278,32 @@ module Parser
|
|
1672
2278
|
@parser.send :yyerror
|
1673
2279
|
end
|
1674
2280
|
end
|
2281
|
+
|
2282
|
+
def validate_definee(definee)
|
2283
|
+
case definee.type
|
2284
|
+
when :int, :str, :dstr, :sym, :dsym,
|
2285
|
+
:regexp, :array, :hash
|
2286
|
+
|
2287
|
+
diagnostic :error, :singleton_literal, nil, definee.loc.expression
|
2288
|
+
false
|
2289
|
+
else
|
2290
|
+
true
|
2291
|
+
end
|
2292
|
+
end
|
2293
|
+
|
2294
|
+
def rewrite_hash_args_to_kwargs(args)
|
2295
|
+
if args.any? && kwargs?(args.last)
|
2296
|
+
# foo(..., bar: baz)
|
2297
|
+
args[args.length - 1] = args[args.length - 1].updated(:kwargs)
|
2298
|
+
elsif args.length > 1 && args.last.type == :block_pass && kwargs?(args[args.length - 2])
|
2299
|
+
# foo(..., bar: baz, &blk)
|
2300
|
+
args[args.length - 2] = args[args.length - 2].updated(:kwargs)
|
2301
|
+
end
|
2302
|
+
end
|
2303
|
+
|
2304
|
+
def kwargs?(node)
|
2305
|
+
node.type == :hash && node.loc.begin.nil? && node.loc.end.nil?
|
2306
|
+
end
|
1675
2307
|
end
|
1676
2308
|
|
1677
2309
|
end
|