typeprof 0.33.1 → 0.33.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d5fa5ffcf2f4e98328b77be29d4e14574083acd097b9af8bac36c2f8e1113626
4
- data.tar.gz: e4a075b8befec5fbf855d4c83cb17c66db0e0441d1e964249b82072a21b40f93
3
+ metadata.gz: 88b4b5188230ac4fb6ab7b45bf924f94271d93d1664219f35414873283a9ef5b
4
+ data.tar.gz: f20527a6973ccd740f1c7b8b045216f1ad3d5e757a1262fc00ddd86dd8d2d1cd
5
5
  SHA512:
6
- metadata.gz: 15d81769ed88b1f1ae097a913a8bc2f4912a36fb792900f86ef4071fe8e04edf1268fb84540e0a56bbc0c30ddeed12b7965aceb1e14fe5917557909146a936f5
7
- data.tar.gz: a1ceade33cf5282b8d117dba231a0a59121a2d32865a8bdea17b5b44adb25f4eee53534a4c14b67a3442a1538557430408662b1802aa6769085a37a39a4c7db5
6
+ metadata.gz: e977f6ef1c6927bb6f85f366af885d6e8b2768d6f26226bf7065ad8a5fbb55ba6537463109d722e112d3be0c77f2523f195a2515118c8cc943a19d71f2f6976f
7
+ data.tar.gz: 3a757a2cf9d5fe5ea21c0080e10ccc0c93b37e4c54a3508e1b7e4ce808d40b7d713355aa1bb8ca943ffe33b8dd3eb47c7b9169d725d3f699d531d9b711aa7136
@@ -218,6 +218,20 @@ module TypeProf::Core
218
218
  end
219
219
  end
220
220
 
221
+ # Where a diagnostic about the value this node yields is placed: a node
222
+ # with a body points at the body's last statement, the rest at itself.
223
+ def ret_code_range = code_range
224
+
225
+ def install_multi_targets(genv, multi_targets, positionals, lenv)
226
+ multi_targets.each do |idx, raw_multi_target|
227
+ param_vtx = positionals[idx]
228
+ lefts = raw_multi_target.lefts.map do |n|
229
+ lenv.new_var(n.is_a?(Prism::MultiTargetNode) ? nil : n.name, self)
230
+ end
231
+ @changes.add_masgn_box(genv, param_vtx, lefts, nil, nil)
232
+ end
233
+ end
234
+
221
235
  def pretty_print_instance_variables
222
236
  super() - [:@raw_node, :@lenv, :@prev_node, :@static_ret, :@changes]
223
237
  end
@@ -1,6 +1,155 @@
1
1
  module TypeProf::Core
2
2
  class AST
3
+ # @lenv is the scope the block closes over; the body has its own LocalEnv.
4
+ class BlockNode < Node
5
+ def initialize(raw_node, lenv, method_body: false)
6
+ super(raw_node, lenv)
7
+
8
+ @tbl = raw_node.locals
9
+ # A `super` in a block calls the super of the enclosing method, but a
10
+ # block given to define_method is the body of another method, whose name
11
+ # is not known here
12
+ ncref = CRef.new(lenv.cref.cpath, :instance, method_body ? nil : lenv.cref.mid, lenv.cref, in_method: method_body || lenv.cref.in_method)
13
+ # A `return` in a block exits the enclosing method, so the body writes into
14
+ # its return boxes. A lambda's `return` exits the lambda, so it gets its own.
15
+ nlenv = LocalEnv.new(lenv.file_context, ncref, {}, lambda? ? [] : lenv.return_boxes)
16
+
17
+ # parse_params with no parameters returns the canonical empty set, so the
18
+ # readers below never have to ask whether there were any.
19
+ @params = AST.parse_params(@tbl, nil, nlenv)
20
+ @f_args = []
21
+ @multi_targets = {}
22
+ @opt_positional_defaults = []
23
+ case raw_node.parameters
24
+ when Prism::BlockParametersNode
25
+ # `{ || ... }` (empty pipes) and `{ |; x| ... }` (block-local-only)
26
+ # yield BlockParametersNode whose inner `parameters` is nil.
27
+ @params = AST.parse_params(@tbl, raw_node.parameters.parameters, nlenv)
28
+ @f_args = @params[:req_positionals] + @params[:opt_positionals]
29
+ @multi_targets = @params[:req_multi_targets]
30
+ @opt_positional_defaults = @params[:opt_positional_defaults]
31
+ when Prism::NumberedParametersNode
32
+ @f_args = 1.upto(raw_node.parameters.maximum).map {|n| :"_#{n}" }
33
+ when Prism::ItParametersNode
34
+ @f_args = [:it]
35
+ when nil
36
+ else
37
+ raise "not supported yet: #{ raw_node.parameters.class }"
38
+ end
39
+ @body = raw_node.body ? AST.create_node(raw_node.body, nlenv) : DummyNilNode.new(code_range, nlenv)
40
+ end
41
+
42
+ attr_reader :tbl, :f_args, :opt_positional_defaults, :body
43
+
44
+ # FormalArguments carries vertices; the keyword names stay on the node, which
45
+ # is where FormalArguments#pass_arguments looks them up.
46
+ def req_keywords = @params[:req_keywords]
47
+ def opt_keywords = @params[:opt_keywords]
48
+ def rest_keywords = @params[:rest_keywords]
49
+ def opt_keyword_defaults = @params[:opt_keyword_defaults]
50
+ def no_keywords = @params[:no_keywords]
51
+
52
+ def subnodes = { opt_positional_defaults:, body:, opt_keyword_defaults: }
53
+ # f_args covers only the parameters a block binds, so the rest have to be
54
+ # compared too or an edit that only touches them looks like no edit at all.
55
+ def attrs = { tbl:, f_args:, formal_names: }
56
+
57
+ def formal_names
58
+ @params.values_at(
59
+ :req_positionals, :opt_positionals, :rest_positionals, :post_positionals,
60
+ :req_keywords, :opt_keywords, :rest_keywords, :block,
61
+ )
62
+ end
63
+
64
+ def install0(genv)
65
+ blenv = @body.lenv
66
+ # `...` in a define_method block still forwards the enclosing method's
67
+ # arguments; a bare `super` there is rejected in CallBaseNode#install0
68
+ blenv.forward_args = @lenv.forward_args
69
+ # An outer block may have shadowed the same name already; its entry is
70
+ # the method's variable, so it must win over the outer block's own one
71
+ blenv.shadowed_vars = @lenv.locals.slice(*@tbl).merge(@lenv.shadowed_vars)
72
+ @lenv.locals.each {|var, vtx| blenv.locals[var] = vtx }
73
+ @tbl.each {|var| blenv.locals[var] = Source.new(genv.nil_type) }
74
+ blenv.locals[:"*self"] = blenv.cref.get_self(genv)
75
+
76
+ f_args = @f_args.map {|arg| blenv.new_var(arg, self) }
77
+
78
+ req_count = f_args.size - @opt_positional_defaults.size
79
+ @opt_positional_defaults.each_with_index do |expr, i|
80
+ @changes.add_edge(genv, expr.install(genv), f_args[req_count + i])
81
+ end
82
+
83
+ install_multi_targets(genv, @multi_targets, f_args, blenv)
84
+ formals = build_formals(genv, blenv, f_args)
85
+
86
+ @lenv.locals.each do |var, vtx|
87
+ blenv.set_var(var, vtx)
88
+ end
89
+ vars = []
90
+ @body.modified_vars(@lenv.locals.keys - @tbl, vars)
91
+ vars.uniq!
92
+ vars.each do |var|
93
+ vtx = @lenv.get_var(var)
94
+ nvtx = vtx.new_vertex(genv, self)
95
+ @lenv.set_var(var, nvtx)
96
+ blenv.set_var(var, nvtx)
97
+ end
98
+
99
+ blenv.locals[:"*expected_block_ret"] = Vertex.new(self)
100
+ # Present already when the lambda sits in a method; a top-level one still
101
+ # needs it, or ReturnNode drops the returned value on the floor.
102
+ blenv.locals[:"*expected_method_ret"] ||= Vertex.new(self) if lambda?
103
+ @body.install(genv)
104
+ blenv.add_next_box(@changes.add_escape_box(genv, @body.ret))
105
+
106
+ if lambda?
107
+ # `return` and `break` leave the lambda itself, so they reach the caller
108
+ # of #call the same way the body's own value does.
109
+ blenv.return_boxes.each {|box| blenv.add_next_box(box) }
110
+ blenv.add_next_box(@changes.add_escape_box(genv, blenv.break_vtx)) if blenv.break_vtx
111
+ end
112
+
113
+ vars.each do |var|
114
+ @changes.add_edge(genv, blenv.get_var(var), @lenv.get_var(var))
115
+ end
116
+
117
+ f_ary_arg = Vertex.new(self)
118
+ # TODO: support splat "do |a, *b, c|"
119
+ f_args.each_with_index do |f_arg, i|
120
+ elem_vtx = @changes.add_splat_box(genv, f_ary_arg, i).ret
121
+ @changes.add_edge(genv, elem_vtx, f_arg)
122
+ end
123
+ block = Block.new(self, f_ary_arg, f_args, blenv.next_boxes, formals)
124
+ Source.new(Type::Proc.new(genv, block))
125
+ end
126
+
127
+ # A block is yielded to, and what a yielding method passes is the positional
128
+ # list alone; there are no formals to bind beyond it. Keyword defaults are
129
+ # still code in the block, so they are analyzed without being bound.
130
+ def build_formals(genv, blenv, f_args)
131
+ opt_keyword_defaults.each {|expr| expr.install(genv) }
132
+ nil
133
+ end
134
+
135
+ # Block-local variables shadow the outer ones, so writes to them are not
136
+ # modifications of the enclosing scope.
137
+ def modified_vars(tbl, vars)
138
+ super(tbl - @tbl, vars)
139
+ end
140
+
141
+ # A block's `break` leaves the method that yielded, so the call it belongs to
142
+ # takes the value; a lambda's `break` leaves the lambda and is wired above.
143
+ def lambda? = false
144
+
145
+ def break_vtx = lambda? ? nil : @body.lenv.break_vtx
146
+
147
+ def ret_code_range = @body.ret_code_range
148
+ end
149
+
3
150
  class CallBaseNode < Node
151
+ DEFINE_METHOD_MIDS = [:define_method, :define_singleton_method].freeze
152
+
4
153
  def initialize(raw_node, recv, mid, mid_code_range_loc, raw_args, last_arg, raw_block, lenv, forwarding_arguments: false)
5
154
  super(raw_node, lenv)
6
155
 
@@ -14,10 +163,7 @@ module TypeProf::Core
14
163
  @keyword_args = nil
15
164
 
16
165
  @block_pass = nil
17
- @block_tbl = nil
18
- @block_f_args = nil
19
- @block_opt_positional_defaults = nil
20
- @block_body = nil
166
+ @block = nil
21
167
  @safe_navigation = raw_node.respond_to?(:safe_navigation?) && raw_node.safe_navigation?
22
168
  @anonymous_block_forwarding = false
23
169
  @forwarding_arguments = forwarding_arguments
@@ -55,47 +201,7 @@ module TypeProf::Core
55
201
  @anonymous_block_forwarding = true
56
202
  end
57
203
  else
58
- @block_pass = nil
59
- @block_tbl = raw_block.locals
60
- @block_multi_targets = {}
61
- @block_f_args = case raw_block.parameters
62
- when Prism::BlockParametersNode
63
- # `{ || ... }` (empty pipes) and `{ |; x| ... }`
64
- # (block-local-only) yield BlockParametersNode
65
- # whose inner `parameters` is nil.
66
- params = raw_block.parameters.parameters
67
- if params
68
- req = params.requireds.each_with_index.map do |n, i|
69
- if n.is_a?(Prism::MultiTargetNode)
70
- @block_multi_targets[i] = n
71
- nil
72
- else
73
- n.name
74
- end
75
- end
76
- opt = params.optionals.map {|n| n.name }
77
- req + opt
78
- else
79
- []
80
- end
81
- when Prism::NumberedParametersNode
82
- 1.upto(raw_block.parameters.maximum).map { |n| :"_#{n}" }
83
- when Prism::ItParametersNode
84
- [:it]
85
- when nil
86
- []
87
- else
88
- raise "not supported yet: #{ raw_block.parameters.class }"
89
- end
90
- ncref = CRef.new(lenv.cref.cpath, :instance, @mid, lenv.cref)
91
- nlenv = LocalEnv.new(@lenv.file_context, ncref, {}, @lenv.return_boxes)
92
- @block_opt_positional_defaults = []
93
- if raw_block.parameters.is_a?(Prism::BlockParametersNode) && raw_block.parameters.parameters
94
- raw_block.parameters.parameters.optionals.each do |n|
95
- @block_opt_positional_defaults << AST.create_node(n.value, nlenv)
96
- end
97
- end
98
- @block_body = raw_block.body ? AST.create_node(raw_block.body, nlenv) : DummyNilNode.new(code_range, lenv)
204
+ @block = BlockNode.new(raw_block, lenv, method_body: DEFINE_METHOD_MIDS.include?(@mid))
99
205
  end
100
206
  end
101
207
 
@@ -108,12 +214,11 @@ module TypeProf::Core
108
214
  @mid_code_range ||= @lenv.code_range_from_node(@mid_code_range_loc) if @mid_code_range_loc
109
215
  end
110
216
  attr_reader :positional_args, :splat_flags, :keyword_args
111
- attr_reader :block_tbl, :block_f_args, :block_opt_positional_defaults, :block_body, :block_pass, :anonymous_block_forwarding
112
- attr_reader :block_multi_targets
217
+ attr_reader :block, :block_pass, :anonymous_block_forwarding
113
218
  attr_reader :safe_navigation, :forwarding_arguments
114
219
 
115
- def subnodes = { recv:, positional_args:, keyword_args:, block_opt_positional_defaults:, block_body:, block_pass: }
116
- def attrs = { mid:, splat_flags:, block_tbl:, block_f_args:, yield:, safe_navigation:, anonymous_block_forwarding:, forwarding_arguments: }
220
+ def subnodes = { recv:, positional_args:, keyword_args:, block:, block_pass: }
221
+ def attrs = { mid:, splat_flags:, yield:, safe_navigation:, anonymous_block_forwarding:, forwarding_arguments: }
117
222
 
118
223
  def install0(genv)
119
224
  recv = @recv ? @recv.install(genv) : @yield ? @lenv.get_var(:"*given_block") : @lenv.get_var(:"*self")
@@ -123,14 +228,27 @@ module TypeProf::Core
123
228
  recv = NilFilter.new(genv, self, recv, false).next_vtx
124
229
  end
125
230
 
126
- if @forwarding_arguments
127
- forward_a_args = (@lenv.forward_args || raise).to_actual_arguments(
128
- genv,
129
- @changes,
130
- self,
131
- include_leading_positionals: @forwarding_arguments != :rest,
132
- activation_required: @forwarding_arguments == :rest,
133
- )
231
+ if @mid == :"*super"
232
+ if !@lenv.cref.in_method
233
+ @changes.add_diagnostic(:code_range, "super called outside of method")
234
+ elsif @forwarding_arguments && !@lenv.cref.mid
235
+ # A define_method block: the arguments of the enclosing method, if any,
236
+ # are not the ones a bare `super` would pass
237
+ @changes.add_diagnostic(:code_range, "implicit argument passing of super from method defined by define_method() is not supported")
238
+ end
239
+ end
240
+
241
+ if @forwarding_arguments && (@mid != :"*super" || @lenv.cref.mid)
242
+ forward_args = @lenv.forward_args
243
+ end
244
+
245
+ if forward_args
246
+ forward_a_args =
247
+ if @forwarding_arguments == :all
248
+ forward_args.to_implicit_super_arguments(genv, @changes, self, @lenv)
249
+ else
250
+ forward_args.to_actual_arguments(genv, @changes, self)
251
+ end
134
252
  # An anonymous rest cannot appear here: `bar(*, ...)` is a syntax error
135
253
  leading_args = @positional_args.map {|arg| arg.install(genv) }
136
254
  a_args = forward_a_args.prepend_positionals(leading_args, @splat_flags)
@@ -146,87 +264,27 @@ module TypeProf::Core
146
264
  a_args = ActualArguments.new(positional_args, @splat_flags, @keyword_args ? @keyword_args.install(genv) : nil, nil)
147
265
  end
148
266
 
149
- if @block_body
150
- block_body = @block_body # kinda type annotationty
151
- block_tbl = @block_tbl || raise
152
- block_body.lenv.forward_args = @lenv.forward_args
153
- @lenv.locals.each {|var, vtx| block_body.lenv.locals[var] = vtx }
154
- block_tbl.each {|var| block_body.lenv.locals[var] = Source.new(genv.nil_type) }
155
- block_body.lenv.locals[:"*self"] = block_body.lenv.cref.get_self(genv)
156
-
157
- blk_f_args = []
158
- if @block_f_args
159
- @block_f_args.each do |arg|
160
- blk_f_args << block_body.lenv.new_var(arg, self)
161
- end
162
- end
163
-
164
- if @block_opt_positional_defaults && !@block_opt_positional_defaults.empty?
165
- req_count = blk_f_args.size - @block_opt_positional_defaults.size
166
- @block_opt_positional_defaults.each_with_index do |expr, i|
167
- @changes.add_edge(genv, expr.install(genv), blk_f_args[req_count + i])
168
- end
169
- end
170
-
171
- if @block_multi_targets
172
- @block_multi_targets.each do |idx, raw_multi_target|
173
- param_vtx = blk_f_args[idx]
174
- lefts = raw_multi_target.lefts.map do |n|
175
- block_body.lenv.new_var(n.is_a?(Prism::MultiTargetNode) ? nil : n.name, self)
176
- end
177
- @changes.add_masgn_box(genv, param_vtx, lefts, nil, nil)
178
- end
179
- end
180
-
181
- @lenv.locals.each do |var, vtx|
182
- block_body.lenv.set_var(var, vtx)
183
- end
184
- vars = []
185
- block_body.modified_vars(@lenv.locals.keys - block_tbl, vars)
186
- vars.uniq!
187
- vars.each do |var|
188
- vtx = @lenv.get_var(var)
189
- nvtx = vtx.new_vertex(genv, self)
190
- @lenv.set_var(var, nvtx)
191
- block_body.lenv.set_var(var, nvtx)
192
- end
193
-
194
- block_body.lenv.locals[:"*expected_block_ret"] = Vertex.new(self)
195
- block_body.install(genv)
196
- block_body.lenv.add_next_box(@changes.add_escape_box(genv, block_body.ret))
197
-
198
- vars.each do |var|
199
- @changes.add_edge(genv, block_body.lenv.get_var(var), @lenv.get_var(var))
200
- end
201
-
202
- blk_f_ary_arg = Vertex.new(self)
203
- # TODO: support splat "do |a, *b, c|"
204
- blk_f_args.each_with_index do |f_arg, i|
205
- elem_vtx = @changes.add_splat_box(genv, blk_f_ary_arg, i).ret
206
- @changes.add_edge(genv, elem_vtx, f_arg)
207
- end
208
- block = Block.new(self, blk_f_ary_arg, blk_f_args, block_body.lenv.next_boxes)
209
- blk_ty = Source.new(Type::Proc.new(genv, block))
267
+ if @block
268
+ blk_ty = @block.install(genv)
210
269
  elsif @block_pass
211
270
  blk_ty = @block_pass.install(genv)
212
271
  elsif @anonymous_block_forwarding
213
272
  blk_ty = @lenv.get_var(:"*anonymous_block")
214
- elsif @forwarding_arguments
273
+ elsif forward_args
215
274
  blk_ty = forward_a_args.block
216
275
  end
217
276
 
218
- if @forwarding_arguments
219
- a_args = a_args.with_block(blk_ty, omittable: !@block_body && !@block_pass && !@anonymous_block_forwarding)
277
+ if forward_args
278
+ a_args = a_args.with_block(blk_ty, omittable: !@block && !@block_pass && !@anonymous_block_forwarding)
220
279
  else
221
280
  a_args = a_args.with_block(blk_ty)
222
281
  end
223
282
  box = @changes.add_method_call_box(genv, recv, @mid, a_args, !@recv)
224
283
 
225
- block_body = @block_body
226
- if block_body && block_body.lenv.break_vtx
284
+ if @block && @block.break_vtx
227
285
  ret = Vertex.new(self)
228
286
  @changes.add_edge(genv, box.ret, ret)
229
- @changes.add_edge(genv, block_body.lenv.break_vtx, ret)
287
+ @changes.add_edge(genv, @block.break_vtx, ret)
230
288
  else
231
289
  ret = box.ret
232
290
  end
@@ -238,18 +296,6 @@ module TypeProf::Core
238
296
  ret
239
297
  end
240
298
 
241
- def block_last_stmt_code_range
242
- if @block_body
243
- if @block_body.is_a?(AST::StatementsNode)
244
- @block_body.stmts.last.code_range
245
- else
246
- @block_body.code_range
247
- end
248
- else
249
- nil
250
- end
251
- end
252
-
253
299
  def retrieve_at(pos, &blk)
254
300
  yield self if mid_code_range&.include?(pos)
255
301
  each_subnode do |subnode|
@@ -258,20 +304,6 @@ module TypeProf::Core
258
304
  end
259
305
  end
260
306
 
261
- def modified_vars(tbl, vars)
262
- subnodes.each do |key, subnode|
263
- next unless subnode
264
- if subnode.is_a?(AST::Node)
265
- if key == :block_body
266
- subnode.modified_vars(tbl - self.block_tbl, vars)
267
- else
268
- subnode.modified_vars(tbl, vars)
269
- end
270
- else
271
- subnode.each {|n| n&.modified_vars(tbl, vars) }
272
- end
273
- end
274
- end
275
307
  end
276
308
 
277
309
  class CallNode < CallBaseNode
@@ -620,8 +620,13 @@ module TypeProf::Core
620
620
  def install0(genv)
621
621
  ret = Vertex.new(self)
622
622
 
623
+ retrying_clauses = @rescue_clauses.select {|clause| has_retry?(clause) }
624
+
623
625
  vars = []
624
626
  @body.modified_vars(@lenv.locals.keys, vars) if @body
627
+ unless retrying_clauses.empty?
628
+ retrying_clauses.each {|clause| clause.modified_vars(@lenv.locals.keys, vars) }
629
+ end
625
630
  vars.uniq!
626
631
 
627
632
  old_vtxs = {}
@@ -630,6 +635,17 @@ module TypeProf::Core
630
635
  old_vtxs[var] = vtx
631
636
  end
632
637
 
638
+ retry_vtxs = {}
639
+ unless retrying_clauses.empty?
640
+ # A retry starts the begin body again with the locals from the rescue clause.
641
+ vars.each do |var|
642
+ vtx = Vertex.new(self)
643
+ @changes.add_edge(genv, old_vtxs[var], vtx)
644
+ retry_vtxs[var] = vtx
645
+ @lenv.set_var(var, vtx)
646
+ end
647
+ end
648
+
633
649
  @changes.add_edge(genv, @body.install(genv), ret)
634
650
 
635
651
  body_vtxs = {}
@@ -639,12 +655,14 @@ module TypeProf::Core
639
655
 
640
656
  clause_vtxs_list = []
641
657
  @rescue_clauses.each do |clause|
658
+ rescue_input_vtxs = {}
642
659
  vars.each do |var|
643
660
  old_vtx = old_vtxs[var]
644
661
  nvtx = old_vtx.new_vertex(genv, self)
645
662
 
646
663
  @changes.add_edge(genv, body_vtxs[var], nvtx) unless body_vtxs[var] == old_vtxs[var]
647
664
 
665
+ rescue_input_vtxs[var] = nvtx
648
666
  @lenv.set_var(var, nvtx)
649
667
  end
650
668
 
@@ -654,6 +672,14 @@ module TypeProf::Core
654
672
  vars.each do |var|
655
673
  clause_vtxs_list.last[var] = @lenv.get_var(var)
656
674
  end
675
+
676
+ if retrying_clauses.include?(clause)
677
+ vars.each do |var|
678
+ # Avoid a redundant cycle when neither the body nor rescue changes this local.
679
+ next if clause_vtxs_list.last[var] == rescue_input_vtxs[var] && body_vtxs[var] == retry_vtxs[var]
680
+ @changes.add_edge(genv, clause_vtxs_list.last[var], retry_vtxs[var])
681
+ end
682
+ end
657
683
  end
658
684
 
659
685
  if @else_clause
@@ -703,6 +729,25 @@ module TypeProf::Core
703
729
 
704
730
  ret
705
731
  end
732
+
733
+ private
734
+
735
+ def has_retry?(root)
736
+ nodes = [root]
737
+ until nodes.empty?
738
+ node = nodes.pop
739
+ next if node != root && (node.is_a?(DefNode) || node.is_a?(LambdaNode))
740
+ if node != root && node.is_a?(BeginNode)
741
+ # Its body and else remain in this retry scope; its rescue clauses form a new one.
742
+ nodes << node.body
743
+ nodes << node.else_clause if node.else_clause
744
+ next
745
+ end
746
+ return true if node.is_a?(RetryNode)
747
+ node.each_subnode {|subnode| nodes << subnode }
748
+ end
749
+ false
750
+ end
706
751
  end
707
752
 
708
753
  class RetryNode < Node
@@ -305,8 +305,9 @@ module TypeProf::Core
305
305
  def attrs = { static_cpath:, members:, kind: }
306
306
 
307
307
  # Interface expected by MethodDefBox
308
- def req_positionals = @kind == :struct ? @members : []
309
- def opt_positionals = []
308
+ def req_positionals = []
309
+ # Struct.new(:x).new is valid, so every member is optional
310
+ def opt_positionals = @kind == :struct ? @members : []
310
311
  def rest_positionals = nil
311
312
  def post_positionals = []
312
313
  def req_keywords = @kind == :data ? @members : []
@@ -323,6 +324,15 @@ module TypeProf::Core
323
324
  ive = genv.resolve_ivar(struct_base_cpath, false, member)
324
325
  ive.add_def(self)
325
326
  end
327
+ if @kind == :struct
328
+ # Struct.[] calls the receiver's initialize, like Class#new
329
+ me = genv.resolve_method(struct_base_cpath, true, :[])
330
+ unless me.builtin
331
+ me.builtin = Builtin.new(genv).method(:class_new)
332
+ # Unlike a method definition, a builtin does not notify the calls itself
333
+ me.add_run_all_method_call_boxes(genv)
334
+ end
335
+ end
326
336
  @block_body.define(genv) if @block_body
327
337
  cdef
328
338
  end
@@ -345,11 +355,18 @@ module TypeProf::Core
345
355
  def undefine0(genv)
346
356
  mod = genv.resolve_cpath(@static_cpath)
347
357
  mod.remove_module_def(genv, self)
348
- genv.resolve_cpath(struct_base_cpath).remove_module_def(genv, self)
358
+ base = genv.resolve_cpath(struct_base_cpath)
359
+ base.remove_module_def(genv, self)
349
360
  @members.each do |member|
350
361
  ive = genv.resolve_ivar(struct_base_cpath, false, member)
351
362
  ive.remove_def(self)
352
363
  end
364
+ # On an update, the new node has already set the builtin
365
+ if base.module_defs.to_a.none? { _1.kind == :struct }
366
+ me = genv.resolve_method(struct_base_cpath, true, :[])
367
+ me.builtin = nil
368
+ me.add_run_all_method_call_boxes(genv)
369
+ end
353
370
  @block_body.undefine(genv) if @block_body
354
371
  end
355
372
 
@@ -388,20 +405,13 @@ module TypeProf::Core
388
405
  end
389
406
  init_ret = @changes.add_escape_box(genv, Source.new(genv.nil_type))
390
407
  if @kind == :struct
391
- init_f_args = FormalArguments.new(init_vtxs, [], nil, [], [], [], nil, nil)
408
+ init_f_args = FormalArguments.new([], init_vtxs, nil, [], [], [], nil, nil)
392
409
  else
393
410
  # Data.define uses keyword arguments
394
411
  init_f_args = FormalArguments.new([], [], nil, [], init_vtxs, [], nil, nil)
395
412
  end
396
413
  @changes.add_method_def_box(genv, cpath, false, :initialize, init_f_args, [init_ret])
397
414
 
398
- # Struct.[] is an alias for Struct.new
399
- if @kind == :struct
400
- # Struct.[] builds the struct class itself, not the base class
401
- self_ret = @changes.add_escape_box(genv, Source.new(Type::Instance.new(genv, genv.resolve_cpath(@static_cpath), [])))
402
- @changes.add_method_def_box(genv, cpath, true, :[], init_f_args, [self_ret])
403
- end
404
-
405
415
  # Install block body (additional method definitions)
406
416
  if @block_body
407
417
  @block_body.lenv.locals[:"*self"] = @block_body.lenv.cref.get_self(genv)
@@ -77,7 +77,17 @@ module TypeProf::Core
77
77
  end
78
78
  end
79
79
 
80
- rest_positionals = raw_args.rest ? (raw_args.rest.name || :"*anonymous_rest") : nil
80
+ rest_positionals =
81
+ case raw_args.rest
82
+ when nil
83
+ nil
84
+ when Prism::ImplicitRestNode
85
+ # `{ |a,| }`. The trailing comma says there is a rest without naming
86
+ # it, so the node carries no name to ask for.
87
+ :"*anonymous_rest"
88
+ else
89
+ raw_args.rest.name || :"*anonymous_rest"
90
+ end
81
91
 
82
92
  req_keywords = []
83
93
  opt_keywords = []
@@ -244,8 +254,8 @@ module TypeProf::Core
244
254
  opt_positionals = @opt_positionals.map {|var| @body.lenv.new_var(var, self) }
245
255
  rest_positionals = @rest_positionals ? @body.lenv.new_var(@rest_positionals, self) : nil
246
256
  post_positionals = @post_positionals.map {|var| @body.lenv.new_var(var, self) }
247
- install_multi_targets(genv, @req_multi_targets, req_positionals)
248
- install_multi_targets(genv, @post_multi_targets, post_positionals)
257
+ install_multi_targets(genv, @req_multi_targets, req_positionals, @body.lenv)
258
+ install_multi_targets(genv, @post_multi_targets, post_positionals, @body.lenv)
249
259
  req_keywords = @req_keywords.map {|var| @body.lenv.new_var(var, self) }
250
260
  opt_keywords = @opt_keywords.map {|var| @body.lenv.new_var(var, self) }
251
261
  rest_keywords = @rest_keywords ? @body.lenv.new_var(@rest_keywords, self) : nil
@@ -290,6 +300,17 @@ module TypeProf::Core
290
300
  forward_rest_keywords,
291
301
  forward_block,
292
302
  forward_activation,
303
+ {
304
+ req_positionals: @req_positionals,
305
+ opt_positionals: @opt_positionals,
306
+ # The variables of `...` and anonymous parameters hold only a
307
+ # placeholder, so what the caller passed is forwarded instead
308
+ rest_positionals: [:"...", :"*anonymous_rest"].include?(@rest_positionals) ? nil : @rest_positionals,
309
+ post_positionals: @post_positionals,
310
+ req_keywords: @req_keywords,
311
+ opt_keywords: @opt_keywords,
312
+ rest_keywords: [:"...", :"**anonymous_keyword"].include?(@rest_keywords) ? nil : @rest_keywords,
313
+ },
293
314
  )
294
315
 
295
316
  if @body
@@ -317,27 +338,7 @@ module TypeProf::Core
317
338
  Source.new(Type::Symbol.new(genv, @mid))
318
339
  end
319
340
 
320
- def install_multi_targets(genv, multi_targets, positionals)
321
- multi_targets.each do |idx, raw_multi_target|
322
- param_vtx = positionals[idx]
323
- lefts = raw_multi_target.lefts.map do |n|
324
- @body.lenv.new_var(n.is_a?(Prism::MultiTargetNode) ? nil : n.name, self)
325
- end
326
- @changes.add_masgn_box(genv, param_vtx, lefts, nil, nil)
327
- end
328
- end
329
-
330
- def last_stmt_code_range
331
- if @body
332
- if @body.is_a?(AST::StatementsNode)
333
- @body.stmts.last.code_range
334
- else
335
- @body.code_range
336
- end
337
- else
338
- nil
339
- end
340
- end
341
+ def ret_code_range = @body.ret_code_range
341
342
 
342
343
  def retrieve_at(pos, &blk)
343
344
  if @rbs_method_type