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 +4 -4
- data/lib/typeprof/core/ast/base.rb +14 -0
- data/lib/typeprof/core/ast/call.rb +182 -150
- data/lib/typeprof/core/ast/control.rb +45 -0
- data/lib/typeprof/core/ast/meta.rb +21 -11
- data/lib/typeprof/core/ast/method.rb +25 -24
- data/lib/typeprof/core/ast/misc.rb +9 -2
- data/lib/typeprof/core/ast/pattern.rb +4 -2
- data/lib/typeprof/core/ast/sig_type.rb +13 -4
- data/lib/typeprof/core/ast/value.rb +52 -3
- data/lib/typeprof/core/ast/variable.rb +7 -1
- data/lib/typeprof/core/ast.rb +3 -1
- data/lib/typeprof/core/builtin.rb +1 -1
- data/lib/typeprof/core/env/method.rb +216 -24
- data/lib/typeprof/core/env.rb +24 -9
- data/lib/typeprof/core/graph/box.rb +17 -156
- data/lib/typeprof/core/service.rb +12 -3
- data/lib/typeprof/core/type.rb +2 -2
- data/lib/typeprof/lsp/util.rb +1 -1
- data/lib/typeprof/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 88b4b5188230ac4fb6ab7b45bf924f94271d93d1664219f35414873283a9ef5b
|
|
4
|
+
data.tar.gz: f20527a6973ccd740f1c7b8b045216f1ad3d5e757a1262fc00ddd86dd8d2d1cd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
@
|
|
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
|
-
@
|
|
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 :
|
|
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:,
|
|
116
|
-
def attrs = { mid:, splat_flags:,
|
|
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 @
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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 @
|
|
150
|
-
|
|
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
|
|
273
|
+
elsif forward_args
|
|
215
274
|
blk_ty = forward_a_args.block
|
|
216
275
|
end
|
|
217
276
|
|
|
218
|
-
if
|
|
219
|
-
a_args = a_args.with_block(blk_ty, omittable: !@
|
|
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
|
-
|
|
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,
|
|
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 =
|
|
309
|
-
|
|
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)
|
|
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(
|
|
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 =
|
|
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
|
|
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
|