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
|
@@ -16,6 +16,8 @@ module TypeProf::Core
|
|
|
16
16
|
|
|
17
17
|
attr_reader :stmts
|
|
18
18
|
|
|
19
|
+
def ret_code_range = @stmts.last.code_range
|
|
20
|
+
|
|
19
21
|
def subnodes = { stmts: }
|
|
20
22
|
|
|
21
23
|
def install0(genv)
|
|
@@ -23,8 +25,13 @@ module TypeProf::Core
|
|
|
23
25
|
|
|
24
26
|
post_stmts = []
|
|
25
27
|
|
|
28
|
+
# BEGIN runs before the surrounding statements, END after them
|
|
29
|
+
@stmts.each do |stmt|
|
|
30
|
+
stmt.install(genv) if stmt.is_a?(PreExecutionNode)
|
|
31
|
+
end
|
|
32
|
+
|
|
26
33
|
@stmts.each do |stmt|
|
|
27
|
-
next if stmt.
|
|
34
|
+
next if stmt.is_a?(PreExecutionNode)
|
|
28
35
|
|
|
29
36
|
if stmt.is_a?(PostExecutionNode)
|
|
30
37
|
post_stmts << stmt
|
|
@@ -126,7 +133,7 @@ module TypeProf::Core
|
|
|
126
133
|
if @rights
|
|
127
134
|
@rights.each {|lhs| lhs.install(genv) }
|
|
128
135
|
@rights.each {|lhs| lhs.rhs.ret || raise(lhs.rhs.inspect) }
|
|
129
|
-
rights = @rights.map {|
|
|
136
|
+
rights = @rights.map {|lhs| lhs.rhs.ret }
|
|
130
137
|
end
|
|
131
138
|
|
|
132
139
|
box = @changes.add_masgn_box(genv, value, lefts, rest_elem, rights)
|
|
@@ -144,7 +144,8 @@ module TypeProf::Core
|
|
|
144
144
|
raise if raw_node.statements.type != :statements_node
|
|
145
145
|
raise if raw_node.statements.body.size != 1
|
|
146
146
|
@body = AST.create_pattern_node(raw_node.statements.body[0], lenv)
|
|
147
|
-
raise if raw_node.subsequent
|
|
147
|
+
raise if raw_node.type == :if_node && raw_node.subsequent
|
|
148
|
+
raise if raw_node.type == :unless_node && raw_node.else_clause
|
|
148
149
|
end
|
|
149
150
|
|
|
150
151
|
attr_reader :cond, :body
|
|
@@ -152,8 +153,9 @@ module TypeProf::Core
|
|
|
152
153
|
def subnodes = { cond:, body: }
|
|
153
154
|
|
|
154
155
|
def install_pattern0(genv, subject)
|
|
155
|
-
|
|
156
|
+
# The guard is evaluated after the pattern binds its variables
|
|
156
157
|
@body.install_pattern(genv, subject)
|
|
158
|
+
@cond.install(genv)
|
|
157
159
|
subject
|
|
158
160
|
end
|
|
159
161
|
end
|
|
@@ -39,7 +39,7 @@ module TypeProf::Core
|
|
|
39
39
|
if prep_decl.is_a?(AST::SigPrependNode) && prep_mod.type_params
|
|
40
40
|
prep_ty = genv.get_instance_type(prep_mod, prep_decl.args, changes, {}, a_ty)
|
|
41
41
|
else
|
|
42
|
-
type_params = prep_mod.type_params.map {|(_name, _default_ty)|
|
|
42
|
+
type_params = prep_mod.type_params.map {|(_name, _default_ty)| genv.untyped_arg } # TODO: better support
|
|
43
43
|
prep_ty = Type::Instance.new(genv, prep_mod, type_params)
|
|
44
44
|
end
|
|
45
45
|
if prep_ty.mod == f_mod
|
|
@@ -64,7 +64,7 @@ module TypeProf::Core
|
|
|
64
64
|
if inc_decl.is_a?(AST::SigIncludeNode) && inc_mod.type_params
|
|
65
65
|
inc_ty = genv.get_instance_type(inc_mod, inc_decl.args, changes, {}, a_ty)
|
|
66
66
|
else
|
|
67
|
-
type_params = inc_mod.type_params.map {|(_name, _default_ty)|
|
|
67
|
+
type_params = inc_mod.type_params.map {|(_name, _default_ty)| genv.untyped_arg } # TODO: better support
|
|
68
68
|
inc_ty = Type::Instance.new(genv, inc_mod, type_params)
|
|
69
69
|
end
|
|
70
70
|
if inc_ty.mod == f_mod
|
|
@@ -89,7 +89,7 @@ module TypeProf::Core
|
|
|
89
89
|
if ext_decl.is_a?(AST::SigExtendNode) && ext_mod.type_params
|
|
90
90
|
ext_ty = genv.get_instance_type(ext_mod, ext_decl.args, changes, {}, a_ty)
|
|
91
91
|
else
|
|
92
|
-
type_params = ext_mod.type_params.map {|(_name, _default_ty)|
|
|
92
|
+
type_params = ext_mod.type_params.map {|(_name, _default_ty)| genv.untyped_arg } # TODO: better support
|
|
93
93
|
ext_ty = Type::Instance.new(genv, ext_mod, type_params)
|
|
94
94
|
end
|
|
95
95
|
if ext_ty.mod == f_mod
|
|
@@ -705,6 +705,10 @@ module TypeProf::Core
|
|
|
705
705
|
return unless cpath
|
|
706
706
|
mod = genv.resolve_cpath(cpath)
|
|
707
707
|
args = @args.map {|arg| arg.covariant_vertex(genv, changes, subst) }
|
|
708
|
+
# Omitted type arguments (e.g., `Foo` for `Foo[T]`) default to untyped
|
|
709
|
+
mod.type_params.drop(args.size).each do |_, default_ty|
|
|
710
|
+
args << (default_ty ? default_ty.covariant_vertex(genv, changes, subst) : genv.untyped_arg)
|
|
711
|
+
end
|
|
708
712
|
changes.add_edge(genv, Source.new(Type::Instance.new(genv, mod, args)), vtx)
|
|
709
713
|
end
|
|
710
714
|
|
|
@@ -716,7 +720,7 @@ module TypeProf::Core
|
|
|
716
720
|
# TODO: report error for wrong type arguments
|
|
717
721
|
# TODO: support default type args
|
|
718
722
|
args = mod.type_params.zip(@args).map do |_, arg|
|
|
719
|
-
arg ? arg.contravariant_vertex(genv, changes, subst) :
|
|
723
|
+
arg ? arg.contravariant_vertex(genv, changes, subst) : genv.untyped_arg
|
|
720
724
|
end
|
|
721
725
|
changes.add_edge(genv, Source.new(Type::Instance.new(genv, mod, args)), vtx)
|
|
722
726
|
end
|
|
@@ -1095,6 +1099,10 @@ module TypeProf::Core
|
|
|
1095
1099
|
return unless cpath
|
|
1096
1100
|
mod = genv.resolve_cpath(cpath)
|
|
1097
1101
|
args = @args.map {|arg| arg.covariant_vertex(genv, changes, subst) }
|
|
1102
|
+
# Omitted type arguments (e.g., `Foo` for `Foo[T]`) default to untyped
|
|
1103
|
+
mod.type_params.drop(args.size).each do |_, default_ty|
|
|
1104
|
+
args << (default_ty ? default_ty.covariant_vertex(genv, changes, subst) : genv.untyped_arg)
|
|
1105
|
+
end
|
|
1098
1106
|
changes.add_edge(genv, Source.new(Type::Instance.new(genv, mod, args)), vtx)
|
|
1099
1107
|
end
|
|
1100
1108
|
|
|
@@ -1104,6 +1112,7 @@ module TypeProf::Core
|
|
|
1104
1112
|
return unless cpath
|
|
1105
1113
|
mod = genv.resolve_cpath(cpath)
|
|
1106
1114
|
args = @args.map {|arg| arg.contravariant_vertex(genv, changes, subst) }
|
|
1115
|
+
mod.type_params.drop(args.size).each { args << genv.untyped_arg }
|
|
1107
1116
|
changes.add_edge(genv, Source.new(Type::Instance.new(genv, mod, args)), vtx)
|
|
1108
1117
|
end
|
|
1109
1118
|
|
|
@@ -326,10 +326,59 @@ module TypeProf::Core
|
|
|
326
326
|
end
|
|
327
327
|
end
|
|
328
328
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
329
|
+
# Prism::LambdaNode has the same locals/parameters/body as Prism::BlockNode.
|
|
330
|
+
# A lambda literal is a block without a call: `->` never dispatches to a
|
|
331
|
+
# user-defined `lambda` method.
|
|
332
|
+
class LambdaNode < BlockNode
|
|
333
|
+
def lambda? = true
|
|
334
|
+
|
|
335
|
+
# A diagnostic on the `===` call below, such as wrong arity, points at the
|
|
336
|
+
# whole lambda.
|
|
337
|
+
def mid_code_range = nil
|
|
338
|
+
|
|
339
|
+
# `in ->(x) { ... }` matches by calling the lambda with the subject.
|
|
340
|
+
def install_pattern0(genv, subject)
|
|
341
|
+
recv = install0(genv)
|
|
342
|
+
a_args = ActualArguments.new([subject], [false], nil, nil)
|
|
343
|
+
@changes.add_method_call_box(genv, recv, :===, a_args, false)
|
|
344
|
+
recv
|
|
332
345
|
end
|
|
346
|
+
|
|
347
|
+
# A lambda is entered like a method, so every parameter kind binds, not just
|
|
348
|
+
# the positionals a block is handed.
|
|
349
|
+
def build_formals(genv, blenv, f_args)
|
|
350
|
+
# f_args is the block-shaped list, required then optional; numbered and `it`
|
|
351
|
+
# parameters put names there that @params does not carry.
|
|
352
|
+
req_count = f_args.size - @opt_positional_defaults.size
|
|
353
|
+
rest = new_formal(blenv, @params[:rest_positionals])
|
|
354
|
+
post = @params[:post_positionals].map {|v| blenv.new_var(v, self) }
|
|
355
|
+
req_keywords = self.req_keywords.map {|v| blenv.new_var(v, self) }
|
|
356
|
+
opt_keywords = self.opt_keywords.map {|v| blenv.new_var(v, self) }
|
|
357
|
+
rest_keywords = new_formal(blenv, self.rest_keywords)
|
|
358
|
+
block = new_formal(blenv, @params[:block])
|
|
359
|
+
|
|
360
|
+
if rest
|
|
361
|
+
@changes.add_edge(genv, Source.new(genv.gen_ary_type(Vertex.new(self))), rest)
|
|
362
|
+
end
|
|
363
|
+
# Only the anonymous `**` needs seeding, as it does for a method: a named
|
|
364
|
+
# one takes its type from what the call passes.
|
|
365
|
+
if self.rest_keywords == :"**anonymous_keyword"
|
|
366
|
+
@changes.add_edge(genv, Source.new(genv.gen_hash_type(Vertex.new(self), Vertex.new(self))), rest_keywords)
|
|
367
|
+
end
|
|
368
|
+
install_multi_targets(genv, @params[:post_multi_targets], post, blenv)
|
|
369
|
+
opt_keyword_defaults.zip(opt_keywords) do |expr, vtx|
|
|
370
|
+
@changes.add_edge(genv, expr.install(genv), vtx)
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
FormalArguments.new(
|
|
374
|
+
f_args[0, req_count], f_args[req_count..], rest, post,
|
|
375
|
+
req_keywords, opt_keywords, rest_keywords, block,
|
|
376
|
+
)
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
private
|
|
380
|
+
|
|
381
|
+
def new_formal(blenv, name) = name ? blenv.new_var(name, self) : nil
|
|
333
382
|
end
|
|
334
383
|
end
|
|
335
384
|
end
|
|
@@ -257,7 +257,7 @@ module TypeProf::Core
|
|
|
257
257
|
end
|
|
258
258
|
end
|
|
259
259
|
|
|
260
|
-
class
|
|
260
|
+
class ExecutionBaseNode < Node
|
|
261
261
|
def initialize(raw_node, lenv)
|
|
262
262
|
super(raw_node, lenv)
|
|
263
263
|
@body = raw_node.statements ? AST.create_node(raw_node.statements, lenv) : DummyNilNode.new(TypeProf::CodeRange.new(code_range.last, code_range.last), lenv)
|
|
@@ -273,6 +273,12 @@ module TypeProf::Core
|
|
|
273
273
|
end
|
|
274
274
|
end
|
|
275
275
|
|
|
276
|
+
class PreExecutionNode < ExecutionBaseNode
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
class PostExecutionNode < ExecutionBaseNode
|
|
280
|
+
end
|
|
281
|
+
|
|
276
282
|
class ClassVariableWriteNode < Node
|
|
277
283
|
def initialize(raw_node, rhs, lenv)
|
|
278
284
|
super(raw_node, lenv)
|
data/lib/typeprof/core/ast.rb
CHANGED
|
@@ -278,6 +278,7 @@ module TypeProf::Core
|
|
|
278
278
|
when :splat_node then SplatNode.new(raw_node, lenv)
|
|
279
279
|
when :for_node then ForNode.new(raw_node, lenv)
|
|
280
280
|
when :alias_global_variable_node then AliasGlobalVariableNode.new(raw_node, lenv)
|
|
281
|
+
when :pre_execution_node then PreExecutionNode.new(raw_node, lenv)
|
|
281
282
|
when :post_execution_node then PostExecutionNode.new(raw_node, lenv)
|
|
282
283
|
when :flip_flop_node then FlipFlopNode.new(raw_node, lenv)
|
|
283
284
|
when :shareable_constant_node then create_node(raw_node.write, lenv)
|
|
@@ -362,7 +363,7 @@ module TypeProf::Core
|
|
|
362
363
|
|
|
363
364
|
when :capture_pattern_node then CapturePatternNode.new(raw_node, lenv)
|
|
364
365
|
|
|
365
|
-
when :if_node then IfPatternNode.new(raw_node, lenv)
|
|
366
|
+
when :if_node, :unless_node then IfPatternNode.new(raw_node, lenv)
|
|
366
367
|
|
|
367
368
|
when :pinned_variable_node then PinnedPatternNode.new(raw_node, lenv)
|
|
368
369
|
when :pinned_expression_node then PinnedPatternNode.new(raw_node, lenv)
|
|
@@ -396,6 +397,7 @@ module TypeProf::Core
|
|
|
396
397
|
|
|
397
398
|
when :array_node then ArrayNode.new(raw_node, lenv) # for %w[foo bar]
|
|
398
399
|
when :range_node then RangeNode.new(raw_node, lenv) # TODO: support range pattern correctly
|
|
400
|
+
when :lambda_node then LambdaNode.new(raw_node, lenv)
|
|
399
401
|
|
|
400
402
|
else
|
|
401
403
|
raise "unknown pattern node type: #{ raw_node.type }"
|
|
@@ -25,7 +25,7 @@ module TypeProf::Core
|
|
|
25
25
|
def proc_call(changes, node, ty, a_args, ret)
|
|
26
26
|
case ty
|
|
27
27
|
when Type::Proc
|
|
28
|
-
ty.block.
|
|
28
|
+
ty.block.pass_arguments(@genv, changes, a_args)
|
|
29
29
|
ty.block.add_ret(@genv, changes, ret)
|
|
30
30
|
true
|
|
31
31
|
else
|
|
@@ -21,6 +21,136 @@ module TypeProf::Core
|
|
|
21
21
|
attr_reader :opt_keywords
|
|
22
22
|
attr_reader :rest_keywords
|
|
23
23
|
attr_reader :block
|
|
24
|
+
|
|
25
|
+
# Binds a call's actual arguments to these formals. `node` supplies the
|
|
26
|
+
# keyword names, which live on the AST node rather than in the vertices.
|
|
27
|
+
def pass_arguments(changes, genv, a_args, node)
|
|
28
|
+
if a_args.splat_flags.any?
|
|
29
|
+
# there is at least one splat actual argument
|
|
30
|
+
|
|
31
|
+
lower = @req_positionals.size + @post_positionals.size
|
|
32
|
+
upper = @rest_positionals ? nil : lower + @opt_positionals.size
|
|
33
|
+
if upper && upper < a_args.positionals.size
|
|
34
|
+
meth = changes.node.mid_code_range ? :mid_code_range : :code_range
|
|
35
|
+
err = "#{ a_args.positionals.size } for #{ lower }#{ upper ? lower < upper ? "...#{ upper }" : "" : "+" }"
|
|
36
|
+
changes.add_diagnostic(meth, "wrong number of arguments (#{ err })")
|
|
37
|
+
return false
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
start_rest = [a_args.splat_flags.index(true), @req_positionals.size + @opt_positionals.size].min
|
|
41
|
+
end_rest = [a_args.splat_flags.rindex(true) + 1, a_args.positionals.size - @post_positionals.size].max
|
|
42
|
+
rest_vtxs = a_args.get_rest_args(genv, changes, start_rest, end_rest)
|
|
43
|
+
|
|
44
|
+
@req_positionals.each_with_index do |f_vtx, i|
|
|
45
|
+
if i < start_rest
|
|
46
|
+
changes.add_edge(genv, a_args.positionals[i], f_vtx)
|
|
47
|
+
else
|
|
48
|
+
rest_vtxs.each do |vtx|
|
|
49
|
+
changes.add_edge(genv, vtx, f_vtx)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
@opt_positionals.each_with_index do |f_vtx, i|
|
|
54
|
+
i += @req_positionals.size
|
|
55
|
+
if i < start_rest
|
|
56
|
+
changes.add_edge(genv, a_args.positionals[i], f_vtx)
|
|
57
|
+
else
|
|
58
|
+
rest_vtxs.each do |vtx|
|
|
59
|
+
changes.add_edge(genv, vtx, f_vtx)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
@post_positionals.each_with_index do |f_vtx, i|
|
|
64
|
+
i += a_args.positionals.size - @post_positionals.size
|
|
65
|
+
if end_rest <= i
|
|
66
|
+
changes.add_edge(genv, a_args.positionals[i], f_vtx)
|
|
67
|
+
else
|
|
68
|
+
rest_vtxs.each do |vtx|
|
|
69
|
+
changes.add_edge(genv, vtx, f_vtx)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
if @rest_positionals
|
|
75
|
+
rest_vtxs.each do |vtx|
|
|
76
|
+
@rest_positionals.each_type do |ty|
|
|
77
|
+
if ty.is_a?(Type::Instance) && ty.mod == genv.mod_ary && ty.args[0]
|
|
78
|
+
changes.add_edge(genv, vtx, ty.args[0])
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
else
|
|
84
|
+
# there is no splat actual argument
|
|
85
|
+
|
|
86
|
+
lower = @req_positionals.size + @post_positionals.size
|
|
87
|
+
upper = @rest_positionals ? nil : lower + @opt_positionals.size
|
|
88
|
+
if a_args.positionals.size < lower || (upper && upper < a_args.positionals.size)
|
|
89
|
+
meth = changes.node.mid_code_range ? :mid_code_range : :code_range
|
|
90
|
+
err = "#{ a_args.positionals.size } for #{ lower }#{ upper ? lower < upper ? "...#{ upper }" : "" : "+" }"
|
|
91
|
+
changes.add_diagnostic(meth, "wrong number of arguments (#{ err })")
|
|
92
|
+
return false
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
@req_positionals.each_with_index do |f_vtx, i|
|
|
96
|
+
changes.add_edge(genv, a_args.positionals[i], f_vtx)
|
|
97
|
+
end
|
|
98
|
+
@post_positionals.each_with_index do |f_vtx, i|
|
|
99
|
+
i -= @post_positionals.size
|
|
100
|
+
changes.add_edge(genv, a_args.positionals[i], f_vtx)
|
|
101
|
+
end
|
|
102
|
+
start_rest = @req_positionals.size
|
|
103
|
+
end_rest = a_args.positionals.size - @post_positionals.size
|
|
104
|
+
i = 0
|
|
105
|
+
while i < @opt_positionals.size && start_rest < end_rest
|
|
106
|
+
f_arg = @opt_positionals[i]
|
|
107
|
+
changes.add_edge(genv, a_args.positionals[start_rest], f_arg)
|
|
108
|
+
i += 1
|
|
109
|
+
start_rest += 1
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
if start_rest < end_rest
|
|
113
|
+
if @rest_positionals
|
|
114
|
+
(start_rest..end_rest-1).each do |i|
|
|
115
|
+
@rest_positionals.each_type do |ty|
|
|
116
|
+
if ty.is_a?(Type::Instance) && ty.mod == genv.mod_ary && ty.args[0]
|
|
117
|
+
changes.add_edge(genv, a_args.positionals[i], ty.args[0])
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
if a_args.keywords
|
|
126
|
+
# TODO: support diagnostics
|
|
127
|
+
node.req_keywords.zip(@req_keywords) do |name, f_vtx|
|
|
128
|
+
changes.add_edge(genv, a_args.get_keyword_arg(genv, changes, name), f_vtx)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
node.opt_keywords.zip(@opt_keywords).each do |name, f_vtx|
|
|
132
|
+
changes.add_edge(genv, a_args.get_keyword_arg(genv, changes, name), f_vtx)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
if node.rest_keywords
|
|
136
|
+
named_keys = node.req_keywords + node.opt_keywords
|
|
137
|
+
a_args.keywords.each_type do |kw_ty|
|
|
138
|
+
case kw_ty
|
|
139
|
+
when Type::Record
|
|
140
|
+
rest_fields = kw_ty.fields.reject {|key, _| named_keys.include?(key) }
|
|
141
|
+
base = kw_ty.base_type(genv)
|
|
142
|
+
rest_record = Type::Record.new(genv, rest_fields, base)
|
|
143
|
+
changes.add_edge(genv, Source.new(rest_record), @rest_keywords)
|
|
144
|
+
when Type::Hash, Type::Instance
|
|
145
|
+
changes.add_edge(genv, Source.new(kw_ty), @rest_keywords)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
return true
|
|
152
|
+
end
|
|
153
|
+
|
|
24
154
|
end
|
|
25
155
|
|
|
26
156
|
class ActualArguments
|
|
@@ -52,6 +182,15 @@ module TypeProf::Core
|
|
|
52
182
|
)
|
|
53
183
|
end
|
|
54
184
|
|
|
185
|
+
# Keywords passed to formals that take none are the last positional hash.
|
|
186
|
+
def with_keywords_normalized_for(node)
|
|
187
|
+
return self unless keywords
|
|
188
|
+
return self if node.no_keywords || node.rest_keywords
|
|
189
|
+
return self unless node.req_keywords.empty? && node.opt_keywords.empty?
|
|
190
|
+
|
|
191
|
+
with_keywords_as_last_positional_hash
|
|
192
|
+
end
|
|
193
|
+
|
|
55
194
|
def prepend_positionals(positionals, splat_flags)
|
|
56
195
|
return self if positionals.empty?
|
|
57
196
|
|
|
@@ -245,7 +384,7 @@ module TypeProf::Core
|
|
|
245
384
|
end
|
|
246
385
|
|
|
247
386
|
class ForwardingArguments
|
|
248
|
-
def initialize(req_positionals, opt_positionals, rest_positionals, post_positionals, req_keyword_pairs, opt_keyword_pairs, rest_keywords, block, activation)
|
|
387
|
+
def initialize(req_positionals, opt_positionals, rest_positionals, post_positionals, req_keyword_pairs, opt_keyword_pairs, rest_keywords, block, activation, param_names)
|
|
249
388
|
@req_positionals = req_positionals
|
|
250
389
|
@opt_positionals = opt_positionals
|
|
251
390
|
@rest_positionals = rest_positionals
|
|
@@ -255,33 +394,68 @@ module TypeProf::Core
|
|
|
255
394
|
@rest_keywords = rest_keywords
|
|
256
395
|
@block = block
|
|
257
396
|
@activation = activation
|
|
397
|
+
@param_names = param_names
|
|
258
398
|
end
|
|
259
399
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
400
|
+
# The arguments that `...` forwards: only the ones the caller passed to it.
|
|
401
|
+
# The parameters before `...` (`def foo(a, b = 1, ...)`) are not forwarded,
|
|
402
|
+
# and none can follow it.
|
|
403
|
+
def to_actual_arguments(genv, changes, node)
|
|
404
|
+
positionals = []
|
|
405
|
+
splat_flags = []
|
|
406
|
+
positionals_omittable = []
|
|
264
407
|
|
|
265
|
-
@
|
|
266
|
-
positionals << Source.new(genv.gen_ary_type(
|
|
408
|
+
if @rest_positionals
|
|
409
|
+
positionals << Source.new(genv.gen_ary_type(@rest_positionals))
|
|
267
410
|
splat_flags << true
|
|
268
411
|
positionals_omittable << true
|
|
269
412
|
end
|
|
270
413
|
|
|
271
|
-
|
|
414
|
+
# `...` cannot follow keyword parameters, so only the rest keywords remain
|
|
415
|
+
ForwardingActualArguments.new(positionals, splat_flags, @rest_keywords, @block, positionals_omittable, !!@rest_keywords, true, @activation, true)
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
# A bare `super` passes the current values of the parameters, which
|
|
419
|
+
# include the defaults of omitted ones and any reassignment.
|
|
420
|
+
def to_implicit_super_arguments(genv, changes, node, lenv)
|
|
421
|
+
positionals = []
|
|
422
|
+
splat_flags = []
|
|
423
|
+
positionals_omittable = []
|
|
424
|
+
|
|
425
|
+
# A destructured parameter has no name, so it is passed as it was given
|
|
426
|
+
@param_names[:req_positionals].zip(@req_positionals) do |name, vtx|
|
|
427
|
+
positionals << (name ? lenv.get_method_var(name) : vtx)
|
|
428
|
+
splat_flags << false
|
|
429
|
+
positionals_omittable << false
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
@param_names[:opt_positionals].each do |name|
|
|
433
|
+
positionals << lenv.get_method_var(name)
|
|
434
|
+
splat_flags << false
|
|
435
|
+
positionals_omittable << false
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
if @param_names[:rest_positionals]
|
|
439
|
+
positionals << lenv.get_method_var(@param_names[:rest_positionals])
|
|
440
|
+
splat_flags << true
|
|
441
|
+
positionals_omittable << true
|
|
442
|
+
elsif @rest_positionals
|
|
272
443
|
positionals << Source.new(genv.gen_ary_type(@rest_positionals))
|
|
273
444
|
splat_flags << true
|
|
274
445
|
positionals_omittable << true
|
|
275
446
|
end
|
|
276
447
|
|
|
277
|
-
@post_positionals.
|
|
278
|
-
positionals <<
|
|
448
|
+
@param_names[:post_positionals].zip(@post_positionals) do |name, vtx|
|
|
449
|
+
positionals << (name ? lenv.get_method_var(name) : vtx)
|
|
279
450
|
splat_flags << false
|
|
280
451
|
positionals_omittable << false
|
|
281
452
|
end
|
|
282
453
|
|
|
283
|
-
|
|
284
|
-
|
|
454
|
+
req_keyword_pairs = @param_names[:req_keywords].map {|name| [name, lenv.get_method_var(name)] }
|
|
455
|
+
opt_keyword_pairs = @param_names[:opt_keywords].map {|name| [name, lenv.get_method_var(name)] }
|
|
456
|
+
rest_keywords = @param_names[:rest_keywords] ? lenv.get_method_var(@param_names[:rest_keywords]) : @rest_keywords
|
|
457
|
+
keywords, keywords_omittable = build_keyword_args(genv, changes, node, req_keyword_pairs, opt_keyword_pairs, rest_keywords)
|
|
458
|
+
ForwardingActualArguments.new(positionals, splat_flags, keywords, @block, positionals_omittable, keywords_omittable, true, @activation, false)
|
|
285
459
|
end
|
|
286
460
|
|
|
287
461
|
def accept_actual_arguments(genv, changes, a_args)
|
|
@@ -386,18 +560,16 @@ module TypeProf::Core
|
|
|
386
560
|
|
|
387
561
|
private
|
|
388
562
|
|
|
389
|
-
def build_keyword_args(genv, changes, node)
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
if @req_keyword_pairs.empty? && opt_keyword_pairs.empty?
|
|
393
|
-
return @rest_keywords, !!@rest_keywords
|
|
563
|
+
def build_keyword_args(genv, changes, node, req_keyword_pairs, opt_keyword_pairs, rest_keywords)
|
|
564
|
+
if req_keyword_pairs.empty? && opt_keyword_pairs.empty?
|
|
565
|
+
return rest_keywords, !!rest_keywords
|
|
394
566
|
end
|
|
395
567
|
|
|
396
568
|
unified_key = Vertex.new(node)
|
|
397
569
|
unified_val = Vertex.new(node)
|
|
398
570
|
literal_pairs = {}
|
|
399
571
|
|
|
400
|
-
|
|
572
|
+
req_keyword_pairs.each do |name, vtx|
|
|
401
573
|
changes.add_edge(genv, Source.new(Type::Symbol.new(genv, name)), unified_key)
|
|
402
574
|
changes.add_edge(genv, vtx, unified_val)
|
|
403
575
|
literal_pairs[name] = vtx
|
|
@@ -410,13 +582,13 @@ module TypeProf::Core
|
|
|
410
582
|
end
|
|
411
583
|
|
|
412
584
|
base_hash_type = genv.gen_hash_type(unified_key, unified_val)
|
|
413
|
-
changes.add_hash_splat_box(genv,
|
|
585
|
+
changes.add_hash_splat_box(genv, rest_keywords, unified_key, unified_val) if rest_keywords
|
|
414
586
|
|
|
415
587
|
if literal_pairs.empty?
|
|
416
588
|
[Source.new(base_hash_type), false]
|
|
417
|
-
elsif
|
|
589
|
+
elsif rest_keywords
|
|
418
590
|
fallback = Source.new(Type::Record.new(genv, literal_pairs, base_hash_type))
|
|
419
|
-
[changes.add_keyword_merge_box(genv,
|
|
591
|
+
[changes.add_keyword_merge_box(genv, rest_keywords, literal_pairs, fallback).ret, false]
|
|
420
592
|
else
|
|
421
593
|
[Source.new(Type::Record.new(genv, literal_pairs, base_hash_type)), false]
|
|
422
594
|
end
|
|
@@ -424,18 +596,34 @@ module TypeProf::Core
|
|
|
424
596
|
end
|
|
425
597
|
|
|
426
598
|
class Block
|
|
427
|
-
#: (AST::
|
|
428
|
-
def initialize(node, f_ary_arg, f_args, next_boxes)
|
|
599
|
+
#: (AST::BlockNode, Vertex, Array[Vertex], Array[EscapeBox], FormalArguments?) -> void
|
|
600
|
+
def initialize(node, f_ary_arg, f_args, next_boxes, formals = nil)
|
|
429
601
|
@node = node
|
|
430
602
|
@f_ary_arg = f_ary_arg
|
|
431
603
|
@f_args = f_args
|
|
432
604
|
@next_boxes = next_boxes
|
|
605
|
+
# Set when the body is entered like a method rather than yielded to, which
|
|
606
|
+
# is to say for a lambda: the full formals then bind the call's arguments.
|
|
607
|
+
@formals = formals
|
|
433
608
|
end
|
|
434
609
|
|
|
435
610
|
attr_reader :node, :f_args, :next_boxes
|
|
436
611
|
|
|
612
|
+
# The arguments of a call that enters this body directly, as Proc#call does.
|
|
613
|
+
def pass_arguments(genv, changes, a_args)
|
|
614
|
+
if @formals
|
|
615
|
+
a_args = a_args.with_keywords_normalized_for(@node)
|
|
616
|
+
@formals.pass_arguments(changes, genv, a_args, @node)
|
|
617
|
+
changes.add_edge(genv, a_args.block, @formals.block) if @formals.block && a_args.block
|
|
618
|
+
else
|
|
619
|
+
accept_args(genv, changes, a_args.positionals)
|
|
620
|
+
end
|
|
621
|
+
end
|
|
622
|
+
|
|
437
623
|
def accept_args(genv, changes, caller_positionals)
|
|
438
|
-
if caller_positionals.size == 1 && @f_args.size >= 2
|
|
624
|
+
if caller_positionals.size == 1 && @f_args.size >= 2 && !@formals
|
|
625
|
+
# A block deconstructs a sole array argument over its parameters; a
|
|
626
|
+
# lambda takes it as the one argument it is.
|
|
439
627
|
changes.add_edge(genv, caller_positionals[0], @f_ary_arg)
|
|
440
628
|
else
|
|
441
629
|
caller_positionals.zip(@f_args) do |a_arg, f_arg|
|
|
@@ -452,6 +640,10 @@ module TypeProf::Core
|
|
|
452
640
|
end
|
|
453
641
|
|
|
454
642
|
class RecordBlock
|
|
643
|
+
def pass_arguments(genv, changes, a_args)
|
|
644
|
+
accept_args(genv, changes, a_args.positionals)
|
|
645
|
+
end
|
|
646
|
+
|
|
455
647
|
def initialize(node)
|
|
456
648
|
@node = node
|
|
457
649
|
@used = false
|
data/lib/typeprof/core/env.rb
CHANGED
|
@@ -42,6 +42,11 @@ module TypeProf::Core
|
|
|
42
42
|
|
|
43
43
|
@bot_type = Type::Bot.new(self)
|
|
44
44
|
|
|
45
|
+
# The vertex for a type argument that is not given. This must be shared:
|
|
46
|
+
# a fresh vertex makes a different Instance type and a different edge on
|
|
47
|
+
# every run, so a box that uses it is re-run forever.
|
|
48
|
+
@untyped_arg = Source.new
|
|
49
|
+
|
|
45
50
|
@run_count = 0
|
|
46
51
|
end
|
|
47
52
|
|
|
@@ -52,7 +57,7 @@ module TypeProf::Core
|
|
|
52
57
|
attr_reader :obj_type, :nil_type, :true_type, :false_type, :str_type
|
|
53
58
|
attr_reader :int_type, :float_type, :rational_type, :complex_type
|
|
54
59
|
attr_reader :proc_type, :symbol_type, :method_type, :set_type, :regexp_type
|
|
55
|
-
attr_reader :bot_type
|
|
60
|
+
attr_reader :bot_type, :untyped_arg
|
|
56
61
|
|
|
57
62
|
def gen_ary_type(elem_vtx)
|
|
58
63
|
Type::Instance.new(self, @mod_ary, [elem_vtx])
|
|
@@ -130,18 +135,18 @@ module TypeProf::Core
|
|
|
130
135
|
ty_env = base_ty_env.dup
|
|
131
136
|
if base_ty.is_a?(Type::Instance)
|
|
132
137
|
base_ty.mod.type_params.zip(base_ty.args) do |(param, default_ty), arg|
|
|
133
|
-
ty_env[param] = arg || (default_ty ? default_ty.covariant_vertex(self, changes, ty_env) :
|
|
138
|
+
ty_env[param] = arg || (default_ty ? default_ty.covariant_vertex(self, changes, ty_env) : @untyped_arg)
|
|
134
139
|
end
|
|
135
140
|
elsif base_ty.is_a?(Type::Singleton)
|
|
136
141
|
base_ty.mod.type_params&.each do |(param, default_ty)|
|
|
137
|
-
ty_env[param] = default_ty ? default_ty.covariant_vertex(self, changes, ty_env) :
|
|
142
|
+
ty_env[param] = default_ty ? default_ty.covariant_vertex(self, changes, ty_env) : @untyped_arg
|
|
138
143
|
end
|
|
139
144
|
end
|
|
140
145
|
args = mod.type_params.zip(type_args).map do |(param, default_ty), arg|
|
|
141
146
|
if changes
|
|
142
|
-
(arg || default_ty)&.covariant_vertex(self, changes, ty_env) ||
|
|
147
|
+
(arg || default_ty)&.covariant_vertex(self, changes, ty_env) || @untyped_arg
|
|
143
148
|
else
|
|
144
|
-
|
|
149
|
+
@untyped_arg
|
|
145
150
|
end
|
|
146
151
|
end
|
|
147
152
|
Type::Instance.new(self, mod, args)
|
|
@@ -374,12 +379,13 @@ module TypeProf::Core
|
|
|
374
379
|
@ivar_narrowings = {}
|
|
375
380
|
@strict_const_scope = false
|
|
376
381
|
@forward_args = forward_args
|
|
382
|
+
@shadowed_vars = {}
|
|
377
383
|
# [cpath, names] of the type parameters of the enclosing RBS declaration
|
|
378
384
|
@sig_type_params = sig_type_params
|
|
379
385
|
end
|
|
380
386
|
|
|
381
387
|
attr_reader :file_context, :cref, :locals, :return_boxes, :break_vtx, :next_boxes, :strict_const_scope, :sig_type_params
|
|
382
|
-
attr_accessor :module_function, :forward_args
|
|
388
|
+
attr_accessor :module_function, :forward_args, :shadowed_vars
|
|
383
389
|
|
|
384
390
|
def path = @file_context&.path
|
|
385
391
|
def code_range_from_node(node)
|
|
@@ -398,6 +404,12 @@ module TypeProf::Core
|
|
|
398
404
|
@locals[name] || raise("#{ name }")
|
|
399
405
|
end
|
|
400
406
|
|
|
407
|
+
# A bare `super` passes the variables of the method, not the block
|
|
408
|
+
# parameters that shadow them
|
|
409
|
+
def get_method_var(name)
|
|
410
|
+
@shadowed_vars[name] || get_var(name)
|
|
411
|
+
end
|
|
412
|
+
|
|
401
413
|
def exist_var?(name)
|
|
402
414
|
!!@locals[name]
|
|
403
415
|
end
|
|
@@ -443,20 +455,23 @@ module TypeProf::Core
|
|
|
443
455
|
end
|
|
444
456
|
|
|
445
457
|
class CRef
|
|
446
|
-
|
|
458
|
+
# in_method: whether this is inside a method body, which a define_method
|
|
459
|
+
# block is even though its method name (mid) is not known
|
|
460
|
+
def initialize(cpath, scope_level, mid, outer, in_method: !mid.nil?)
|
|
447
461
|
@cpath = cpath
|
|
448
462
|
@scope_level = scope_level
|
|
449
463
|
@mid = mid
|
|
450
464
|
@outer = outer
|
|
465
|
+
@in_method = in_method
|
|
451
466
|
end
|
|
452
467
|
|
|
453
|
-
attr_reader :cpath, :scope_level, :mid, :outer
|
|
468
|
+
attr_reader :cpath, :scope_level, :mid, :outer, :in_method
|
|
454
469
|
|
|
455
470
|
def get_self(genv)
|
|
456
471
|
case @scope_level
|
|
457
472
|
when :instance
|
|
458
473
|
mod = genv.resolve_cpath(@cpath || [])
|
|
459
|
-
type_params = mod.type_params.map {|(_name, _default_ty)|
|
|
474
|
+
type_params = mod.type_params.map {|(_name, _default_ty)| genv.untyped_arg } # TODO: better support
|
|
460
475
|
ty = Type::Instance.new(genv, mod, type_params)
|
|
461
476
|
Source.new(ty)
|
|
462
477
|
when :class
|