typeprof 0.31.0 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -1
- data/doc/report_guide.md +88 -0
- data/lib/typeprof/cli/cli.rb +9 -3
- data/lib/typeprof/code_range.rb +7 -5
- data/lib/typeprof/core/ast/base.rb +18 -6
- data/lib/typeprof/core/ast/call.rb +96 -32
- data/lib/typeprof/core/ast/const.rb +12 -9
- data/lib/typeprof/core/ast/control.rb +60 -30
- data/lib/typeprof/core/ast/meta.rb +194 -2
- data/lib/typeprof/core/ast/method.rb +74 -20
- data/lib/typeprof/core/ast/misc.rb +84 -7
- data/lib/typeprof/core/ast/module.rb +33 -3
- data/lib/typeprof/core/ast/sig_decl.rb +85 -24
- data/lib/typeprof/core/ast/sig_type.rb +78 -32
- data/lib/typeprof/core/ast/value.rb +14 -6
- data/lib/typeprof/core/ast/variable.rb +11 -4
- data/lib/typeprof/core/ast.rb +97 -14
- data/lib/typeprof/core/builtin.rb +184 -12
- data/lib/typeprof/core/env/method.rb +171 -6
- data/lib/typeprof/core/env/method_entity.rb +18 -15
- data/lib/typeprof/core/env/module_entity.rb +56 -18
- data/lib/typeprof/core/env/static_read.rb +4 -4
- data/lib/typeprof/core/env/type_alias_entity.rb +1 -1
- data/lib/typeprof/core/env/value_entity.rb +25 -3
- data/lib/typeprof/core/env.rb +81 -15
- data/lib/typeprof/core/graph/box.rb +380 -53
- data/lib/typeprof/core/graph/change_set.rb +59 -46
- data/lib/typeprof/core/graph/filter.rb +8 -5
- data/lib/typeprof/core/graph/vertex.rb +20 -19
- data/lib/typeprof/core/service.rb +317 -23
- data/lib/typeprof/core/type.rb +43 -8
- data/lib/typeprof/core/util.rb +6 -0
- data/lib/typeprof/lsp/messages.rb +5 -0
- data/lib/typeprof/lsp/server.rb +35 -4
- data/lib/typeprof/version.rb +1 -1
- metadata +3 -2
|
@@ -69,6 +69,20 @@ module TypeProf::Core
|
|
|
69
69
|
else
|
|
70
70
|
false
|
|
71
71
|
end
|
|
72
|
+
elsif a_args.positionals.size == 3
|
|
73
|
+
# ary[start, len] = val
|
|
74
|
+
# Use SplatBox to extract element types from the assigned value
|
|
75
|
+
elem_vtx = case ty
|
|
76
|
+
when Type::Array
|
|
77
|
+
ty.get_elem(@genv)
|
|
78
|
+
when Type::Instance
|
|
79
|
+
ty.mod == @genv.mod_ary ? ty.args[0] : nil
|
|
80
|
+
end
|
|
81
|
+
return false unless elem_vtx
|
|
82
|
+
val = a_args.positionals[2]
|
|
83
|
+
splat_ret = changes.add_splat_box(@genv, val).ret
|
|
84
|
+
changes.add_edge(@genv, splat_ret, elem_vtx)
|
|
85
|
+
true
|
|
72
86
|
else
|
|
73
87
|
false
|
|
74
88
|
end
|
|
@@ -90,7 +104,7 @@ module TypeProf::Core
|
|
|
90
104
|
def hash_aref(changes, node, ty, a_args, ret)
|
|
91
105
|
if a_args.positionals.size == 1
|
|
92
106
|
case ty
|
|
93
|
-
when Type::Hash
|
|
107
|
+
when Type::Hash
|
|
94
108
|
idx = node.positional_args[0]
|
|
95
109
|
idx = idx.is_a?(AST::SymbolNode) ? idx.lit : nil
|
|
96
110
|
value = ty.get_value(idx)
|
|
@@ -101,6 +115,20 @@ module TypeProf::Core
|
|
|
101
115
|
changes.add_edge(@genv, Source.new(), ret)
|
|
102
116
|
end
|
|
103
117
|
true
|
|
118
|
+
when Type::Record
|
|
119
|
+
idx = node.positional_args[0]
|
|
120
|
+
idx = idx.is_a?(AST::SymbolNode) ? idx.lit : nil
|
|
121
|
+
value = ty.get_value(idx)
|
|
122
|
+
if value
|
|
123
|
+
changes.add_edge(@genv, value, ret)
|
|
124
|
+
else
|
|
125
|
+
changes.add_edge(@genv, Source.new(@genv.nil_type), ret)
|
|
126
|
+
end
|
|
127
|
+
# Symbol variable access - add nil possibility
|
|
128
|
+
if idx.nil?
|
|
129
|
+
changes.add_edge(@genv, Source.new(@genv.nil_type), ret)
|
|
130
|
+
end
|
|
131
|
+
true
|
|
104
132
|
else
|
|
105
133
|
false
|
|
106
134
|
end
|
|
@@ -125,6 +153,15 @@ module TypeProf::Core
|
|
|
125
153
|
end
|
|
126
154
|
changes.add_edge(@genv, val, ret)
|
|
127
155
|
true
|
|
156
|
+
when Type::Record
|
|
157
|
+
val = a_args.positionals[1]
|
|
158
|
+
idx = node.positional_args[0]
|
|
159
|
+
if idx.is_a?(AST::SymbolNode)
|
|
160
|
+
field_vtx = ty.get_value(idx.lit)
|
|
161
|
+
changes.add_edge(@genv, val, field_vtx) if field_vtx
|
|
162
|
+
end
|
|
163
|
+
changes.add_edge(@genv, val, ret)
|
|
164
|
+
true
|
|
128
165
|
else
|
|
129
166
|
false
|
|
130
167
|
end
|
|
@@ -133,19 +170,154 @@ module TypeProf::Core
|
|
|
133
170
|
end
|
|
134
171
|
end
|
|
135
172
|
|
|
173
|
+
def object_method(changes, node, ty, a_args, ret)
|
|
174
|
+
if a_args.positionals.size == 1
|
|
175
|
+
sym_node = node.positional_args[0]
|
|
176
|
+
if sym_node.is_a?(AST::SymbolNode)
|
|
177
|
+
method_ty = Type::Method.new(@genv, ty, sym_node.lit)
|
|
178
|
+
changes.add_edge(@genv, Source.new(method_ty), ret)
|
|
179
|
+
return true
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
false
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def method_call(changes, node, ty, a_args, ret)
|
|
186
|
+
case ty
|
|
187
|
+
when Type::Method
|
|
188
|
+
recv = Source.new(ty.recv_ty)
|
|
189
|
+
box = changes.add_method_call_box(@genv, recv, ty.mid, a_args, false)
|
|
190
|
+
changes.add_edge(@genv, box.ret, ret)
|
|
191
|
+
true
|
|
192
|
+
else
|
|
193
|
+
false
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def kernel_send(changes, node, ty, a_args, ret)
|
|
198
|
+
return false if a_args.positionals.empty?
|
|
199
|
+
|
|
200
|
+
if a_args.splat_flags[0]
|
|
201
|
+
# send(*array) case: extract method name and args from array elements
|
|
202
|
+
splat_vtx = a_args.positionals[0]
|
|
203
|
+
changes.add_edge(@genv, splat_vtx, changes.target)
|
|
204
|
+
|
|
205
|
+
rest_positionals = a_args.positionals[1..]
|
|
206
|
+
rest_splat_flags = a_args.splat_flags[1..]
|
|
207
|
+
|
|
208
|
+
splat_vtx.each_type do |ary_ty|
|
|
209
|
+
next unless ary_ty.is_a?(Type::Array)
|
|
210
|
+
|
|
211
|
+
if ary_ty.elems && ary_ty.elems.size >= 1
|
|
212
|
+
# Tuple: use per-element precision
|
|
213
|
+
method_name_vtx = ary_ty.elems[0]
|
|
214
|
+
changes.add_edge(@genv, method_name_vtx, changes.target)
|
|
215
|
+
|
|
216
|
+
elem_args = ary_ty.elems[1..] + rest_positionals
|
|
217
|
+
elem_flags = ::Array.new(ary_ty.elems.size - 1, false) + rest_splat_flags
|
|
218
|
+
send_a_args = ActualArguments.new(elem_args, elem_flags, a_args.keywords, a_args.block)
|
|
219
|
+
|
|
220
|
+
method_name_vtx.each_type do |sym_ty|
|
|
221
|
+
if sym_ty.is_a?(Type::Symbol)
|
|
222
|
+
recv = Source.new(ty)
|
|
223
|
+
box = changes.add_method_call_box(@genv, recv, sym_ty.sym, send_a_args, false)
|
|
224
|
+
changes.add_edge(@genv, box.ret, ret)
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
else
|
|
228
|
+
# Non-tuple array: use unified element type for method name
|
|
229
|
+
elem_vtx = ary_ty.get_elem(@genv)
|
|
230
|
+
next unless elem_vtx
|
|
231
|
+
changes.add_edge(@genv, elem_vtx, changes.target)
|
|
232
|
+
|
|
233
|
+
send_a_args = ActualArguments.new(rest_positionals, rest_splat_flags, a_args.keywords, a_args.block)
|
|
234
|
+
|
|
235
|
+
elem_vtx.each_type do |sym_ty|
|
|
236
|
+
if sym_ty.is_a?(Type::Symbol)
|
|
237
|
+
recv = Source.new(ty)
|
|
238
|
+
box = changes.add_method_call_box(@genv, recv, sym_ty.sym, send_a_args, false)
|
|
239
|
+
changes.add_edge(@genv, box.ret, ret)
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
else
|
|
245
|
+
# send(:sym, ...) case
|
|
246
|
+
changes.add_edge(@genv, a_args.positionals[0], changes.target)
|
|
247
|
+
send_a_args = ActualArguments.new(
|
|
248
|
+
a_args.positionals[1..],
|
|
249
|
+
a_args.splat_flags[1..],
|
|
250
|
+
a_args.keywords,
|
|
251
|
+
a_args.block,
|
|
252
|
+
)
|
|
253
|
+
a_args.positionals[0].each_type do |sym_ty|
|
|
254
|
+
if sym_ty.is_a?(Type::Symbol)
|
|
255
|
+
recv = Source.new(ty)
|
|
256
|
+
box = changes.add_method_call_box(@genv, recv, sym_ty.sym, send_a_args, false)
|
|
257
|
+
changes.add_edge(@genv, box.ret, ret)
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
true
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def kernel_array(changes, node, ty, a_args, ret)
|
|
265
|
+
return false unless a_args.positionals.size == 1
|
|
266
|
+
|
|
267
|
+
arg_vtx = a_args.positionals[0]
|
|
268
|
+
elem_vtx = Vertex.new(node)
|
|
269
|
+
handled = false
|
|
270
|
+
needs_elem_wrap = false
|
|
271
|
+
|
|
272
|
+
arg_vtx.each_type do |arg_ty|
|
|
273
|
+
handled = true
|
|
274
|
+
case arg_ty
|
|
275
|
+
when Type::Instance
|
|
276
|
+
if arg_ty.mod == @genv.mod_range && arg_ty.args && !arg_ty.args.empty?
|
|
277
|
+
changes.add_edge(@genv, arg_ty.args[0], elem_vtx)
|
|
278
|
+
needs_elem_wrap = true
|
|
279
|
+
elsif arg_ty.mod == @genv.mod_ary
|
|
280
|
+
changes.add_edge(@genv, Source.new(arg_ty), ret)
|
|
281
|
+
else
|
|
282
|
+
changes.add_edge(@genv, Source.new(arg_ty), elem_vtx)
|
|
283
|
+
needs_elem_wrap = true
|
|
284
|
+
end
|
|
285
|
+
when Type::Array
|
|
286
|
+
changes.add_edge(@genv, Source.new(arg_ty), ret)
|
|
287
|
+
else
|
|
288
|
+
changes.add_edge(@genv, Source.new(arg_ty), elem_vtx)
|
|
289
|
+
needs_elem_wrap = true
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
return false unless handled
|
|
294
|
+
|
|
295
|
+
if needs_elem_wrap
|
|
296
|
+
ary_ty = @genv.gen_ary_type(elem_vtx)
|
|
297
|
+
changes.add_edge(@genv, Source.new(ary_ty), ret)
|
|
298
|
+
end
|
|
299
|
+
true
|
|
300
|
+
end
|
|
301
|
+
|
|
136
302
|
def deploy
|
|
137
|
-
|
|
138
|
-
class_new
|
|
139
|
-
object_class
|
|
140
|
-
proc_call
|
|
141
|
-
array_aref
|
|
142
|
-
array_aset
|
|
143
|
-
array_push
|
|
144
|
-
hash_aref
|
|
145
|
-
hash_aset
|
|
146
|
-
|
|
303
|
+
[
|
|
304
|
+
[method(:class_new), [:Class], false, :new],
|
|
305
|
+
[method(:object_class), [:Object], false, :class],
|
|
306
|
+
[method(:proc_call), [:Proc], false, :call],
|
|
307
|
+
[method(:array_aref), [:Array], false, :[]],
|
|
308
|
+
[method(:array_aset), [:Array], false, :[]=],
|
|
309
|
+
[method(:array_push), [:Array], false, :<<],
|
|
310
|
+
[method(:hash_aref), [:Hash], false, :[]],
|
|
311
|
+
[method(:hash_aset), [:Hash], false, :[]=],
|
|
312
|
+
[method(:object_method), [:Kernel], false, :method],
|
|
313
|
+
[method(:method_call), [:Method], false, :call],
|
|
314
|
+
[method(:kernel_send), [:BasicObject], false, :__send__],
|
|
315
|
+
[method(:kernel_send), [:Kernel], false, :public_send],
|
|
316
|
+
[method(:kernel_send), [:Kernel], false, :send],
|
|
317
|
+
[method(:kernel_array), [:Kernel], false, :Array],
|
|
318
|
+
].each do |builtin, cpath, singleton, mid|
|
|
147
319
|
me = @genv.resolve_method(cpath, singleton, mid)
|
|
148
|
-
me.builtin =
|
|
320
|
+
me.builtin = builtin
|
|
149
321
|
end
|
|
150
322
|
end
|
|
151
323
|
end
|
|
@@ -41,6 +41,17 @@ module TypeProf::Core
|
|
|
41
41
|
ActualArguments.new(positionals, splat_flags, keywords, block)
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
+
def with_keywords_as_last_positional_hash
|
|
45
|
+
return self unless @keywords
|
|
46
|
+
|
|
47
|
+
ActualArguments.new(
|
|
48
|
+
@positionals + [@keywords],
|
|
49
|
+
@splat_flags + [false],
|
|
50
|
+
nil,
|
|
51
|
+
@block
|
|
52
|
+
)
|
|
53
|
+
end
|
|
54
|
+
|
|
44
55
|
def get_rest_args(genv, changes, start_rest, end_rest)
|
|
45
56
|
vtxs = []
|
|
46
57
|
|
|
@@ -69,6 +80,9 @@ module TypeProf::Core
|
|
|
69
80
|
case ty
|
|
70
81
|
when Type::Hash
|
|
71
82
|
changes.add_edge(genv, ty.get_value(name), vtx)
|
|
83
|
+
when Type::Record
|
|
84
|
+
field_vtx = ty.get_value(name)
|
|
85
|
+
changes.add_edge(genv, field_vtx, vtx) if field_vtx
|
|
72
86
|
when Type::Instance
|
|
73
87
|
if ty.mod == genv.mod_hash
|
|
74
88
|
changes.add_edge(genv, ty.args[1], vtx)
|
|
@@ -81,6 +95,162 @@ module TypeProf::Core
|
|
|
81
95
|
end
|
|
82
96
|
end
|
|
83
97
|
|
|
98
|
+
class ForwardingArguments
|
|
99
|
+
def initialize(req_positionals, opt_positionals, opt_positional_elems, rest_positionals, post_positionals, req_keyword_pairs, opt_keyword_pairs, rest_keywords, block)
|
|
100
|
+
@req_positionals = req_positionals
|
|
101
|
+
@opt_positionals = opt_positionals
|
|
102
|
+
@opt_positional_elems = opt_positional_elems
|
|
103
|
+
@rest_positionals = rest_positionals
|
|
104
|
+
@post_positionals = post_positionals
|
|
105
|
+
@req_keyword_pairs = req_keyword_pairs
|
|
106
|
+
@opt_keyword_pairs = opt_keyword_pairs
|
|
107
|
+
@rest_keywords = rest_keywords
|
|
108
|
+
@block = block
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
attr_reader :block
|
|
112
|
+
|
|
113
|
+
def to_actual_arguments(genv, changes, node)
|
|
114
|
+
positionals = @req_positionals.dup
|
|
115
|
+
splat_flags = ::Array.new(positionals.size, false)
|
|
116
|
+
|
|
117
|
+
@opt_positionals.each do |arg|
|
|
118
|
+
positionals << arg
|
|
119
|
+
splat_flags << true
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
if @rest_positionals
|
|
123
|
+
positionals << @rest_positionals
|
|
124
|
+
splat_flags << true
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
@post_positionals.each do |arg|
|
|
128
|
+
positionals << arg
|
|
129
|
+
splat_flags << false
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
keywords = build_keyword_args(genv, changes, node)
|
|
133
|
+
ActualArguments.new(positionals, splat_flags, keywords, @block)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def accept_actual_arguments(genv, changes, a_args)
|
|
137
|
+
if a_args.splat_flags.any?
|
|
138
|
+
start_rest = [a_args.splat_flags.index(true), @req_positionals.size + @opt_positionals.size].min
|
|
139
|
+
end_rest = [a_args.splat_flags.rindex(true) + 1, a_args.positionals.size - @post_positionals.size].max
|
|
140
|
+
rest_vtxs = a_args.get_rest_args(genv, changes, start_rest, end_rest)
|
|
141
|
+
|
|
142
|
+
@req_positionals.each_with_index do |f_vtx, i|
|
|
143
|
+
if i < start_rest
|
|
144
|
+
changes.add_edge(genv, a_args.positionals[i], f_vtx)
|
|
145
|
+
else
|
|
146
|
+
rest_vtxs.each do |vtx|
|
|
147
|
+
changes.add_edge(genv, vtx, f_vtx)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
@opt_positional_elems.each_with_index do |elem_vtx, i|
|
|
153
|
+
i += @req_positionals.size
|
|
154
|
+
if i < start_rest
|
|
155
|
+
changes.add_edge(genv, a_args.positionals[i], elem_vtx)
|
|
156
|
+
else
|
|
157
|
+
rest_vtxs.each do |vtx|
|
|
158
|
+
changes.add_edge(genv, vtx, elem_vtx)
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
@post_positionals.each_with_index do |f_vtx, i|
|
|
164
|
+
i += a_args.positionals.size - @post_positionals.size
|
|
165
|
+
if end_rest <= i
|
|
166
|
+
changes.add_edge(genv, a_args.positionals[i], f_vtx)
|
|
167
|
+
else
|
|
168
|
+
rest_vtxs.each do |vtx|
|
|
169
|
+
changes.add_edge(genv, vtx, f_vtx)
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
else
|
|
175
|
+
@req_positionals.each_with_index do |f_vtx, i|
|
|
176
|
+
changes.add_edge(genv, a_args.positionals[i], f_vtx)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
@post_positionals.each_with_index do |f_vtx, i|
|
|
180
|
+
i -= @post_positionals.size
|
|
181
|
+
changes.add_edge(genv, a_args.positionals[i], f_vtx)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
start_rest = @req_positionals.size
|
|
185
|
+
end_rest = a_args.positionals.size - @post_positionals.size
|
|
186
|
+
i = 0
|
|
187
|
+
while i < @opt_positional_elems.size && start_rest < end_rest
|
|
188
|
+
changes.add_edge(genv, a_args.positionals[start_rest], @opt_positional_elems[i])
|
|
189
|
+
i += 1
|
|
190
|
+
start_rest += 1
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
changes.add_edge(genv, a_args.block, @block) if @block && a_args.block
|
|
195
|
+
|
|
196
|
+
return unless a_args.keywords
|
|
197
|
+
|
|
198
|
+
@req_keyword_pairs.each do |name, f_vtx|
|
|
199
|
+
changes.add_edge(genv, a_args.get_keyword_arg(genv, changes, name), f_vtx)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
@opt_keyword_pairs.each do |name, f_vtx|
|
|
203
|
+
changes.add_edge(genv, a_args.get_keyword_arg(genv, changes, name), f_vtx)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
if @rest_keywords
|
|
207
|
+
named_keys = @req_keyword_pairs.map(&:first) + @opt_keyword_pairs.map(&:first)
|
|
208
|
+
a_args.keywords.each_type do |kw_ty|
|
|
209
|
+
case kw_ty
|
|
210
|
+
when Type::Record
|
|
211
|
+
rest_fields = kw_ty.fields.reject {|key, _| named_keys.include?(key) }
|
|
212
|
+
base = kw_ty.base_type(genv)
|
|
213
|
+
rest_record = Type::Record.new(genv, rest_fields, base)
|
|
214
|
+
changes.add_edge(genv, Source.new(rest_record), @rest_keywords)
|
|
215
|
+
when Type::Hash, Type::Instance
|
|
216
|
+
changes.add_edge(genv, Source.new(kw_ty), @rest_keywords)
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
private
|
|
223
|
+
|
|
224
|
+
def build_keyword_args(genv, changes, node)
|
|
225
|
+
return nil if @req_keyword_pairs.empty? && @opt_keyword_pairs.empty? && !@rest_keywords
|
|
226
|
+
return @rest_keywords if @req_keyword_pairs.empty? && @opt_keyword_pairs.empty?
|
|
227
|
+
|
|
228
|
+
unified_key = Vertex.new(node)
|
|
229
|
+
unified_val = Vertex.new(node)
|
|
230
|
+
literal_pairs = {}
|
|
231
|
+
|
|
232
|
+
@req_keyword_pairs.each do |name, vtx|
|
|
233
|
+
changes.add_edge(genv, Source.new(Type::Symbol.new(genv, name)), unified_key)
|
|
234
|
+
changes.add_edge(genv, vtx, unified_val)
|
|
235
|
+
literal_pairs[name] = vtx
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
@opt_keyword_pairs.each do |name, vtx|
|
|
239
|
+
changes.add_edge(genv, Source.new(Type::Symbol.new(genv, name)), unified_key)
|
|
240
|
+
changes.add_edge(genv, vtx, unified_val)
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
base_hash_type = genv.gen_hash_type(unified_key, unified_val)
|
|
244
|
+
changes.add_hash_splat_box(genv, @rest_keywords, unified_key, unified_val) if @rest_keywords
|
|
245
|
+
|
|
246
|
+
if literal_pairs.empty?
|
|
247
|
+
Source.new(base_hash_type)
|
|
248
|
+
else
|
|
249
|
+
Source.new(Type::Record.new(genv, literal_pairs, base_hash_type))
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
|
|
84
254
|
class Block
|
|
85
255
|
#: (AST::CallBaseNode, Vertex, Array[Vertex], Array[EscapeBox]) -> void
|
|
86
256
|
def initialize(node, f_ary_arg, f_args, next_boxes)
|
|
@@ -94,12 +264,7 @@ module TypeProf::Core
|
|
|
94
264
|
|
|
95
265
|
def accept_args(genv, changes, caller_positionals)
|
|
96
266
|
if caller_positionals.size == 1 && @f_args.size >= 2
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
@f_args.each_with_index do |f_arg, i|
|
|
100
|
-
elem_vtx = changes.add_splat_box(genv, single_arg, i).ret
|
|
101
|
-
changes.add_edge(genv, elem_vtx, f_arg)
|
|
102
|
-
end
|
|
267
|
+
changes.add_edge(genv, caller_positionals[0], @f_ary_arg)
|
|
103
268
|
else
|
|
104
269
|
caller_positionals.zip(@f_args) do |a_arg, f_arg|
|
|
105
270
|
changes.add_edge(genv, a_arg, f_arg) if f_arg
|
|
@@ -2,60 +2,63 @@ module TypeProf::Core
|
|
|
2
2
|
class MethodEntity
|
|
3
3
|
def initialize
|
|
4
4
|
@builtin = nil
|
|
5
|
-
@decls = Set[]
|
|
6
|
-
@overloading_decls = Set[]
|
|
7
|
-
@defs = Set[]
|
|
8
|
-
@aliases = {}
|
|
9
|
-
@method_call_boxes = Set[]
|
|
10
5
|
end
|
|
11
6
|
|
|
12
|
-
attr_reader :decls, :defs, :aliases, :method_call_boxes
|
|
13
7
|
attr_accessor :builtin
|
|
14
8
|
|
|
9
|
+
def decls = @decls ||= Set.empty
|
|
10
|
+
def defs = @defs ||= Set.empty
|
|
11
|
+
def aliases = @aliases ||= {}
|
|
12
|
+
def method_call_boxes = @method_call_boxes ||= Set.empty
|
|
13
|
+
|
|
14
|
+
private def overloading_decls = @overloading_decls ||= Set.empty
|
|
15
|
+
|
|
15
16
|
def add_decl(decl)
|
|
16
17
|
if decl.overloading
|
|
17
|
-
|
|
18
|
+
overloading_decls << decl
|
|
18
19
|
else
|
|
19
|
-
|
|
20
|
+
decls << decl
|
|
20
21
|
end
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
def remove_decl(decl)
|
|
24
25
|
if decl.overloading
|
|
25
|
-
|
|
26
|
+
overloading_decls.delete(decl) || raise
|
|
26
27
|
else
|
|
27
|
-
|
|
28
|
+
decls.delete(decl) || raise
|
|
28
29
|
end
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
def add_def(mdef)
|
|
32
|
-
|
|
33
|
+
defs << mdef
|
|
33
34
|
self
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
def remove_def(mdef)
|
|
37
|
-
|
|
38
|
+
defs.delete(mdef) || raise
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
def add_alias(node, old_mid)
|
|
41
|
-
|
|
42
|
+
aliases[node] = old_mid
|
|
42
43
|
end
|
|
43
44
|
|
|
44
45
|
def remove_alias(node)
|
|
45
|
-
|
|
46
|
+
aliases.delete(node) || raise
|
|
46
47
|
end
|
|
47
48
|
|
|
48
49
|
def exist?
|
|
49
|
-
@builtin || !@decls.empty? || !@defs.empty?
|
|
50
|
+
@builtin || (@decls && !@decls.empty?) || (@defs && !@defs.empty?)
|
|
50
51
|
end
|
|
51
52
|
|
|
52
53
|
def add_run_all_mdefs(genv)
|
|
54
|
+
return unless @defs
|
|
53
55
|
@defs.each do |mdef|
|
|
54
56
|
genv.add_run(mdef)
|
|
55
57
|
end
|
|
56
58
|
end
|
|
57
59
|
|
|
58
60
|
def add_run_all_method_call_boxes(genv)
|
|
61
|
+
return unless @method_call_boxes
|
|
59
62
|
@method_call_boxes.each do |box|
|
|
60
63
|
genv.add_run(box)
|
|
61
64
|
end
|
|
@@ -3,13 +3,18 @@ module TypeProf::Core
|
|
|
3
3
|
def initialize(cpath, outer_module = self)
|
|
4
4
|
@cpath = cpath
|
|
5
5
|
|
|
6
|
-
@module_decls = Set
|
|
7
|
-
@module_defs = Set
|
|
8
|
-
@include_decls = Set
|
|
9
|
-
@include_defs = Set
|
|
6
|
+
@module_decls = Set.empty
|
|
7
|
+
@module_defs = Set.empty
|
|
8
|
+
@include_decls = Set.empty
|
|
9
|
+
@include_defs = Set.empty
|
|
10
10
|
@prepend_decls = []
|
|
11
11
|
@prepend_defs = []
|
|
12
12
|
|
|
13
|
+
# `class Foo = Bar` / `module Foo = Bar` declarations attached to this entity.
|
|
14
|
+
# Maps an alias decl to the target ModuleEntity at the time of registration.
|
|
15
|
+
@alias_decls = {}
|
|
16
|
+
@alias_target = nil
|
|
17
|
+
|
|
13
18
|
@inner_modules = {}
|
|
14
19
|
@outer_module = outer_module
|
|
15
20
|
|
|
@@ -25,7 +30,7 @@ module TypeProf::Core
|
|
|
25
30
|
|
|
26
31
|
# class Foo[X, Y, Z] < Bar[A, B, C]
|
|
27
32
|
@superclass_type_args = nil # A, B, C
|
|
28
|
-
@type_params =
|
|
33
|
+
@type_params = {} # X, Y, Z
|
|
29
34
|
|
|
30
35
|
@consts = {}
|
|
31
36
|
@methods = { true => {}, false => {} }
|
|
@@ -34,14 +39,16 @@ module TypeProf::Core
|
|
|
34
39
|
@type_aliases = {}
|
|
35
40
|
|
|
36
41
|
@static_reads = {}
|
|
37
|
-
@subclass_checks = Set
|
|
38
|
-
@ivar_reads = Set
|
|
39
|
-
@cvar_reads = Set
|
|
42
|
+
@subclass_checks = Set.empty
|
|
43
|
+
@ivar_reads = Set.empty # should be handled in @ivars ??
|
|
44
|
+
@cvar_reads = Set.empty
|
|
40
45
|
end
|
|
41
46
|
|
|
42
47
|
attr_reader :cpath
|
|
43
48
|
attr_reader :module_decls
|
|
44
49
|
attr_reader :module_defs
|
|
50
|
+
attr_reader :alias_decls
|
|
51
|
+
attr_reader :alias_target
|
|
45
52
|
|
|
46
53
|
attr_reader :inner_modules
|
|
47
54
|
attr_reader :outer_module
|
|
@@ -79,7 +86,7 @@ module TypeProf::Core
|
|
|
79
86
|
end
|
|
80
87
|
|
|
81
88
|
def exist?
|
|
82
|
-
!@module_decls.empty? || !@module_defs.empty?
|
|
89
|
+
!@module_decls.empty? || !@module_defs.empty? || !@alias_decls.empty?
|
|
83
90
|
end
|
|
84
91
|
|
|
85
92
|
def on_inner_modules_changed(genv, changed_cname)
|
|
@@ -115,9 +122,10 @@ module TypeProf::Core
|
|
|
115
122
|
@module_decls << decl
|
|
116
123
|
|
|
117
124
|
if @type_params
|
|
118
|
-
update_type_params
|
|
125
|
+
update_type_params
|
|
119
126
|
else
|
|
120
|
-
@type_params =
|
|
127
|
+
@type_params = {}
|
|
128
|
+
decl.params.zip(decl.params_default_types) {|name, default_type| @type_params[name] = default_type }
|
|
121
129
|
end
|
|
122
130
|
|
|
123
131
|
if decl.is_a?(AST::SigClassNode) && !@superclass_type_args
|
|
@@ -133,7 +141,7 @@ module TypeProf::Core
|
|
|
133
141
|
@outer_module.get_const(get_cname).remove_decl(decl)
|
|
134
142
|
@module_decls.delete(decl) || raise
|
|
135
143
|
|
|
136
|
-
update_type_params
|
|
144
|
+
update_type_params
|
|
137
145
|
if decl.is_a?(AST::SigClassNode) && @superclass_type_args == decl.superclass_args
|
|
138
146
|
@superclass_type_args = nil
|
|
139
147
|
@module_decls.each do |decl|
|
|
@@ -152,13 +160,12 @@ module TypeProf::Core
|
|
|
152
160
|
@module_decls.each do |decl|
|
|
153
161
|
params = decl.params
|
|
154
162
|
next unless params
|
|
155
|
-
if @type_params
|
|
156
|
-
@type_params =
|
|
157
|
-
|
|
158
|
-
@type_params = params
|
|
163
|
+
if !@type_params || @type_params.size < params.size
|
|
164
|
+
@type_params = {}
|
|
165
|
+
params.zip(decl.params_default_types) {|name, default_type| @type_params[name] = default_type }
|
|
159
166
|
end
|
|
160
167
|
end
|
|
161
|
-
@type_params ||=
|
|
168
|
+
@type_params ||= {}
|
|
162
169
|
# TODO: report an error if there are multiple inconsistent declarations
|
|
163
170
|
end
|
|
164
171
|
|
|
@@ -176,6 +183,22 @@ module TypeProf::Core
|
|
|
176
183
|
on_module_removed(genv)
|
|
177
184
|
end
|
|
178
185
|
|
|
186
|
+
def add_alias_decl(genv, decl, target_mod)
|
|
187
|
+
on_module_added(genv)
|
|
188
|
+
@alias_decls[decl] = target_mod
|
|
189
|
+
@alias_target = @alias_decls.values.first
|
|
190
|
+
ce = @outer_module.get_const(get_cname)
|
|
191
|
+
ce.add_decl(decl)
|
|
192
|
+
ce
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def remove_alias_decl(genv, decl)
|
|
196
|
+
@outer_module.get_const(get_cname).remove_decl(decl)
|
|
197
|
+
@alias_decls.delete(decl) || raise
|
|
198
|
+
@alias_target = @alias_decls.values.first
|
|
199
|
+
on_module_removed(genv)
|
|
200
|
+
end
|
|
201
|
+
|
|
179
202
|
def add_include_decl(genv, node)
|
|
180
203
|
@include_decls << node
|
|
181
204
|
genv.add_static_eval_queue(:parent_modules_changed, self)
|
|
@@ -237,7 +260,7 @@ module TypeProf::Core
|
|
|
237
260
|
old_parent.child_modules.delete(self) if set.empty?
|
|
238
261
|
end
|
|
239
262
|
if new_parent
|
|
240
|
-
set = new_parent.child_modules[self] ||= Set
|
|
263
|
+
set = new_parent.child_modules[self] ||= Set.empty
|
|
241
264
|
set << origin
|
|
242
265
|
end
|
|
243
266
|
return [new_parent, true]
|
|
@@ -261,6 +284,8 @@ module TypeProf::Core
|
|
|
261
284
|
next
|
|
262
285
|
when AST::ModuleNode
|
|
263
286
|
return nil
|
|
287
|
+
when AST::StructNewNode
|
|
288
|
+
return [] # inherits from Object (Struct < Object)
|
|
264
289
|
else
|
|
265
290
|
raise
|
|
266
291
|
end
|
|
@@ -436,6 +461,19 @@ module TypeProf::Core
|
|
|
436
461
|
@ivars[singleton][name] ||= ValueEntity.new
|
|
437
462
|
end
|
|
438
463
|
|
|
464
|
+
def add_ivar_decl(genv, singleton, name, decl)
|
|
465
|
+
ive = get_ivar(singleton, name)
|
|
466
|
+
ive.add_decl(decl)
|
|
467
|
+
ive.on_decl_changed(genv)
|
|
468
|
+
ive
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
def remove_ivar_decl(genv, singleton, name, decl)
|
|
472
|
+
ive = get_ivar(singleton, name)
|
|
473
|
+
ive.remove_decl(decl)
|
|
474
|
+
ive.on_decl_changed(genv)
|
|
475
|
+
end
|
|
476
|
+
|
|
439
477
|
def get_cvar(name)
|
|
440
478
|
@cvars[name] ||= ValueEntity.new
|
|
441
479
|
end
|
|
@@ -2,8 +2,8 @@ module TypeProf::Core
|
|
|
2
2
|
class StaticRead
|
|
3
3
|
def initialize(name)
|
|
4
4
|
@name = name
|
|
5
|
-
@followers = Set
|
|
6
|
-
@source_modules = Set
|
|
5
|
+
@followers = Set.empty
|
|
6
|
+
@source_modules = Set.empty
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
attr_reader :name, :followers
|
|
@@ -37,11 +37,11 @@ module TypeProf::Core
|
|
|
37
37
|
scope = cref.cpath
|
|
38
38
|
mod = genv.resolve_cpath(scope)
|
|
39
39
|
genv.each_superclass(mod, false) do |mod, _singleton|
|
|
40
|
-
break if mod == genv.mod_object && break_object
|
|
40
|
+
break if mod == genv.mod_object && (break_object || cref.outer)
|
|
41
41
|
|
|
42
42
|
unless @source_modules.include?(mod)
|
|
43
43
|
@source_modules << mod
|
|
44
|
-
(mod.static_reads[@name] ||= Set
|
|
44
|
+
(mod.static_reads[@name] ||= Set.empty) << self
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
return if check_module(genv, mod)
|