typeprof 0.31.1 → 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 +27 -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 +77 -31
- data/lib/typeprof/core/ast/value.rb +14 -6
- data/lib/typeprof/core/ast/variable.rb +11 -4
- data/lib/typeprof/core/ast.rb +95 -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 +79 -17
- data/lib/typeprof/core/graph/box.rb +379 -52
- 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 +41 -7
- 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
data/lib/typeprof/core/ast.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module TypeProf::Core
|
|
2
2
|
class AST
|
|
3
|
-
def self.parse_rb(path, src)
|
|
3
|
+
def self.parse_rb(path, src, position_encoding)
|
|
4
4
|
result = Prism.parse(src)
|
|
5
5
|
|
|
6
6
|
return nil unless result.errors.empty?
|
|
@@ -10,19 +10,58 @@ module TypeProf::Core
|
|
|
10
10
|
|
|
11
11
|
raise unless raw_scope.type == :program_node
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
prism_source = result.source
|
|
14
|
+
file_context = FileContext.new(path, position_encoding, prism_source, result.comments)
|
|
14
15
|
|
|
15
16
|
cref = CRef::Toplevel
|
|
16
|
-
lenv = LocalEnv.new(
|
|
17
|
+
lenv = LocalEnv.new(file_context, cref, {}, [])
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
ignore_ranges = collect_ignore_ranges(result)
|
|
20
|
+
ProgramNode.new(raw_scope, lenv, ignore_ranges: ignore_ranges)
|
|
19
21
|
end
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
# Collect line ranges marked with `# typeprof:ignore` comments.
|
|
24
|
+
# Each range is suppressed in ProgramNode#each_diagnostic.
|
|
25
|
+
#
|
|
26
|
+
# Inline form (suppresses the line containing the comment):
|
|
27
|
+
# foo(1, 2) # typeprof:ignore
|
|
28
|
+
#
|
|
29
|
+
# Block form (suppresses lines between :start and :end):
|
|
30
|
+
# # typeprof:ignore:start
|
|
31
|
+
# foo(1, 2)
|
|
32
|
+
# # typeprof:ignore:end
|
|
33
|
+
#
|
|
34
|
+
# An unmatched `:start` extends to the end of the file.
|
|
35
|
+
IGNORE_RE = /\A#\s*typeprof:ignore\s*\z/
|
|
36
|
+
IGNORE_START_RE = /\A#\s*typeprof:ignore:start\s*\z/
|
|
37
|
+
IGNORE_END_RE = /\A#\s*typeprof:ignore:end\s*\z/
|
|
38
|
+
def self.collect_ignore_ranges(prism_result)
|
|
39
|
+
ranges = []
|
|
40
|
+
start_line = nil
|
|
41
|
+
prism_result.comments.each do |c|
|
|
42
|
+
text = c.location.slice
|
|
43
|
+
line = c.location.start_line
|
|
44
|
+
if text.match?(IGNORE_START_RE)
|
|
45
|
+
start_line ||= line
|
|
46
|
+
elsif text.match?(IGNORE_END_RE)
|
|
47
|
+
if start_line
|
|
48
|
+
ranges << (start_line..line)
|
|
49
|
+
start_line = nil
|
|
50
|
+
end
|
|
51
|
+
elsif text.match?(IGNORE_RE)
|
|
52
|
+
ranges << (line..line)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
ranges << (start_line..Float::INFINITY) if start_line
|
|
56
|
+
ranges
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
#: (untyped, TypeProf::Core::LocalEnv, ?bool, ?bool) -> TypeProf::Core::AST::Node
|
|
60
|
+
def self.create_node(raw_node, lenv, use_result = true, allow_meta = false)
|
|
23
61
|
while true
|
|
24
62
|
case raw_node.type
|
|
25
63
|
when :parentheses_node
|
|
64
|
+
return DummyNilNode.new(lenv.code_range_from_node(raw_node), lenv) if raw_node.body.nil?
|
|
26
65
|
raw_node = raw_node.body
|
|
27
66
|
when :implicit_node
|
|
28
67
|
raw_node = raw_node.value
|
|
@@ -64,7 +103,13 @@ module TypeProf::Core
|
|
|
64
103
|
when :constant_read_node, :constant_path_node
|
|
65
104
|
ConstantReadNode.new(raw_node, lenv)
|
|
66
105
|
when :constant_write_node, :constant_path_write_node
|
|
67
|
-
|
|
106
|
+
if (members = detect_struct_new(raw_node.value))
|
|
107
|
+
StructNewNode.new(raw_node, members, :struct, lenv)
|
|
108
|
+
elsif (members = detect_data_define(raw_node.value))
|
|
109
|
+
StructNewNode.new(raw_node, members, :data, lenv)
|
|
110
|
+
else
|
|
111
|
+
ConstantWriteNode.new(raw_node, AST.create_node(raw_node.value, lenv), lenv)
|
|
112
|
+
end
|
|
68
113
|
when :constant_operator_write_node
|
|
69
114
|
read = ConstantReadNode.new(raw_node, lenv)
|
|
70
115
|
rhs = OperatorNode.new(raw_node, read, lenv)
|
|
@@ -132,6 +177,7 @@ module TypeProf::Core
|
|
|
132
177
|
when :class_variable_operator_write_node
|
|
133
178
|
read = ClassVariableReadNode.new(raw_node, lenv)
|
|
134
179
|
rhs = OperatorNode.new(raw_node, read, lenv)
|
|
180
|
+
ClassVariableWriteNode.new(raw_node, rhs, lenv)
|
|
135
181
|
when :class_variable_or_write_node
|
|
136
182
|
read = ClassVariableReadNode.new(raw_node, lenv)
|
|
137
183
|
rhs = OrNode.new(raw_node, read, raw_node.value, lenv)
|
|
@@ -233,15 +279,20 @@ module TypeProf::Core
|
|
|
233
279
|
when :forwarding_super_node then ForwardingSuperNode.new(raw_node, lenv)
|
|
234
280
|
when :yield_node then YieldNode.new(raw_node, lenv)
|
|
235
281
|
when :call_node
|
|
236
|
-
if !raw_node.receiver
|
|
237
|
-
# TODO: handle them only when it is directly under class or module
|
|
282
|
+
if allow_meta && !raw_node.receiver && !lenv.cref.mid && [:class, :metaclass].include?(lenv.cref.scope_level)
|
|
238
283
|
case raw_node.name
|
|
239
284
|
when :include
|
|
240
285
|
return IncludeMetaNode.new(raw_node, lenv)
|
|
241
286
|
when :attr_reader
|
|
242
287
|
return AttrReaderMetaNode.new(raw_node, lenv)
|
|
288
|
+
when :attr_writer
|
|
289
|
+
return AttrWriterMetaNode.new(raw_node, lenv)
|
|
243
290
|
when :attr_accessor
|
|
244
291
|
return AttrAccessorMetaNode.new(raw_node, lenv)
|
|
292
|
+
when :module_function
|
|
293
|
+
if raw_node.arguments.nil?
|
|
294
|
+
return ModuleFunctionMetaNode.new(raw_node, lenv)
|
|
295
|
+
end
|
|
245
296
|
end
|
|
246
297
|
end
|
|
247
298
|
CallNode.new(raw_node, lenv)
|
|
@@ -252,7 +303,7 @@ module TypeProf::Core
|
|
|
252
303
|
end
|
|
253
304
|
|
|
254
305
|
def self.create_target_node(raw_node, lenv)
|
|
255
|
-
dummy_node = DummyRHSNode.new(
|
|
306
|
+
dummy_node = DummyRHSNode.new(lenv.code_range_from_node(raw_node.location), lenv)
|
|
256
307
|
case raw_node.type
|
|
257
308
|
when :local_variable_target_node
|
|
258
309
|
LocalVariableWriteNode.new(raw_node, dummy_node, lenv)
|
|
@@ -305,7 +356,7 @@ module TypeProf::Core
|
|
|
305
356
|
when :pinned_expression_node then PinnedPatternNode.new(raw_node, lenv)
|
|
306
357
|
|
|
307
358
|
when :local_variable_target_node
|
|
308
|
-
dummy_node = DummyRHSNode.new(
|
|
359
|
+
dummy_node = DummyRHSNode.new(lenv.code_range_from_node(raw_node.location), lenv)
|
|
309
360
|
LocalVariableWriteNode.new(raw_node, dummy_node, lenv)
|
|
310
361
|
|
|
311
362
|
when :constant_read_node, :constant_path_node
|
|
@@ -364,11 +415,12 @@ module TypeProf::Core
|
|
|
364
415
|
return cpath + names.reverse if cpath
|
|
365
416
|
end
|
|
366
417
|
|
|
367
|
-
def self.parse_rbs(path, src)
|
|
418
|
+
def self.parse_rbs(path, src, position_encoding)
|
|
368
419
|
_buffer, _directives, raw_decls = RBS::Parser.parse_signature(src)
|
|
369
420
|
|
|
370
421
|
cref = CRef::Toplevel
|
|
371
|
-
|
|
422
|
+
file_context = FileContext.new(path, position_encoding)
|
|
423
|
+
lenv = LocalEnv.new(file_context, cref, {}, [])
|
|
372
424
|
|
|
373
425
|
raw_decls.map do |raw_decl|
|
|
374
426
|
AST.create_rbs_decl(raw_decl, lenv)
|
|
@@ -385,7 +437,10 @@ module TypeProf::Core
|
|
|
385
437
|
SigInterfaceNode.new(raw_decl, lenv)
|
|
386
438
|
when RBS::AST::Declarations::Constant
|
|
387
439
|
SigConstNode.new(raw_decl, lenv)
|
|
388
|
-
when RBS::AST::Declarations::
|
|
440
|
+
when RBS::AST::Declarations::ClassAlias
|
|
441
|
+
SigClassAliasNode.new(raw_decl, lenv)
|
|
442
|
+
when RBS::AST::Declarations::ModuleAlias
|
|
443
|
+
SigModuleAliasNode.new(raw_decl, lenv)
|
|
389
444
|
when RBS::AST::Declarations::TypeAlias
|
|
390
445
|
SigTypeAliasNode.new(raw_decl, lenv)
|
|
391
446
|
# TODO: check
|
|
@@ -481,5 +536,31 @@ module TypeProf::Core
|
|
|
481
536
|
raise "unknown RBS type: #{ raw_decl.class }"
|
|
482
537
|
end
|
|
483
538
|
end
|
|
539
|
+
|
|
540
|
+
def self.detect_struct_new(raw_value)
|
|
541
|
+
return nil unless raw_value.type == :call_node
|
|
542
|
+
return nil unless raw_value.name == :new
|
|
543
|
+
recv = raw_value.receiver
|
|
544
|
+
return nil unless recv&.type == :constant_read_node && recv.name == :Struct
|
|
545
|
+
extract_symbol_args(raw_value)
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
def self.detect_data_define(raw_value)
|
|
549
|
+
return nil unless raw_value.type == :call_node
|
|
550
|
+
return nil unless raw_value.name == :define
|
|
551
|
+
recv = raw_value.receiver
|
|
552
|
+
return nil unless recv&.type == :constant_read_node && recv.name == :Data
|
|
553
|
+
extract_symbol_args(raw_value)
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
def self.extract_symbol_args(raw_call)
|
|
557
|
+
return nil unless raw_call.arguments
|
|
558
|
+
members = []
|
|
559
|
+
raw_call.arguments.arguments.each do |arg|
|
|
560
|
+
return nil unless arg.type == :symbol_node
|
|
561
|
+
members << arg.value.to_sym
|
|
562
|
+
end
|
|
563
|
+
members
|
|
564
|
+
end
|
|
484
565
|
end
|
|
485
566
|
end
|
|
@@ -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
|