typeprof 0.33.0 → 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_decl.rb +10 -6
- data/lib/typeprof/core/ast/sig_type.rb +32 -9
- data/lib/typeprof/core/ast/value.rb +52 -3
- data/lib/typeprof/core/ast/variable.rb +7 -1
- data/lib/typeprof/core/ast.rb +15 -3
- data/lib/typeprof/core/builtin.rb +1 -1
- data/lib/typeprof/core/env/method.rb +216 -24
- data/lib/typeprof/core/env.rb +33 -32
- data/lib/typeprof/core/graph/box.rb +17 -156
- data/lib/typeprof/core/service.rb +18 -4
- 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
|
|
@@ -14,16 +14,20 @@ module TypeProf::Core
|
|
|
14
14
|
@cpath = AST.resolve_rbs_name(raw_decl.name, lenv)
|
|
15
15
|
# TODO: decl.type_params
|
|
16
16
|
# TODO: decl.super_class.args
|
|
17
|
+
# TODO?: param.variance, param.unchecked, param.upper_bound
|
|
18
|
+
@params = raw_decl.type_params.map {|param| param.name }
|
|
19
|
+
sig_type_params = [@cpath, @params]
|
|
20
|
+
# The header (default types, self types, and superclass arguments) is
|
|
21
|
+
# resolved in the outer scope but may refer to the type parameters
|
|
22
|
+
@header_lenv = LocalEnv.new(@lenv.file_context, lenv.cref, {}, [], sig_type_params:)
|
|
17
23
|
ncref = CRef.new(@cpath, :class, nil, lenv.cref)
|
|
18
|
-
nlenv = LocalEnv.new(@lenv.file_context, ncref, {}, [])
|
|
24
|
+
nlenv = LocalEnv.new(@lenv.file_context, ncref, {}, [], sig_type_params:)
|
|
19
25
|
@members = raw_decl.members.map do |member|
|
|
20
26
|
AST.create_rbs_member(member, nlenv)
|
|
21
27
|
end.compact
|
|
22
|
-
# TODO?: param.variance, param.unchecked, param.upper_bound
|
|
23
|
-
@params = raw_decl.type_params.map {|param| param.name }
|
|
24
28
|
@params_default_types = raw_decl.type_params.map do |param|
|
|
25
29
|
ty = param.default_type
|
|
26
|
-
ty ? AST.create_rbs_type(ty,
|
|
30
|
+
ty ? AST.create_rbs_type(ty, @header_lenv) : nil
|
|
27
31
|
end
|
|
28
32
|
end
|
|
29
33
|
|
|
@@ -81,7 +85,7 @@ module TypeProf::Core
|
|
|
81
85
|
cpath = name.namespace.path + [self_type.name.name]
|
|
82
86
|
toplevel = name.namespace.absolute?
|
|
83
87
|
@self_types << [cpath, toplevel]
|
|
84
|
-
@self_type_args << self_type.args.map {|arg| AST.create_rbs_type(arg,
|
|
88
|
+
@self_type_args << self_type.args.map {|arg| AST.create_rbs_type(arg, @header_lenv) }
|
|
85
89
|
end
|
|
86
90
|
end
|
|
87
91
|
|
|
@@ -138,7 +142,7 @@ module TypeProf::Core
|
|
|
138
142
|
name = superclass.name
|
|
139
143
|
@superclass_cpath = name.namespace.path + [name.name]
|
|
140
144
|
@superclass_toplevel = name.namespace.absolute?
|
|
141
|
-
@superclass_args = superclass.args.map {|arg| AST.create_rbs_type(arg,
|
|
145
|
+
@superclass_args = superclass.args.map {|arg| AST.create_rbs_type(arg, @header_lenv) }
|
|
142
146
|
else
|
|
143
147
|
@superclass_cpath = nil
|
|
144
148
|
@superclass_toplevel = nil
|
|
@@ -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
|
|
@@ -883,18 +887,32 @@ module TypeProf::Core
|
|
|
883
887
|
|
|
884
888
|
def attrs = { var: }
|
|
885
889
|
|
|
890
|
+
# A reopened declaration may rename the type parameters (e.g., `Enumerable[E]`
|
|
891
|
+
# and `Enumerable[Elem]`), but the subst is keyed by the names of the module
|
|
892
|
+
# entity, so fall back to matching them by position
|
|
893
|
+
def resolve_var(genv, subst)
|
|
894
|
+
decl_cpath, decl_params = @lenv.sig_type_params
|
|
895
|
+
return subst[@var] unless decl_params
|
|
896
|
+
idx = decl_params.index(@var)
|
|
897
|
+
return subst[@var] unless idx
|
|
898
|
+
subst[genv.resolve_cpath(decl_cpath).type_params.keys[idx]]
|
|
899
|
+
end
|
|
900
|
+
|
|
886
901
|
def covariant_vertex0(genv, changes, vtx, subst)
|
|
887
|
-
|
|
888
|
-
|
|
902
|
+
var_vtx = resolve_var(genv, subst)
|
|
903
|
+
raise "unknown type variable: #{ @var }" unless var_vtx
|
|
904
|
+
changes.add_edge(genv, var_vtx, vtx)
|
|
889
905
|
end
|
|
890
906
|
|
|
891
907
|
def contravariant_vertex0(genv, changes, vtx, subst)
|
|
892
|
-
|
|
893
|
-
|
|
908
|
+
var_vtx = resolve_var(genv, subst)
|
|
909
|
+
raise "unknown type variable: #{ @var }" unless var_vtx
|
|
910
|
+
changes.add_edge(genv, Source.new(Type::Var.new(genv, @var, var_vtx)), vtx)
|
|
894
911
|
end
|
|
895
912
|
|
|
896
913
|
def typecheck(genv, changes, vtx, subst)
|
|
897
|
-
|
|
914
|
+
var_vtx = resolve_var(genv, subst)
|
|
915
|
+
changes.add_edge(genv, vtx.new_vertex(genv, self), var_vtx) unless vtx == var_vtx
|
|
898
916
|
true
|
|
899
917
|
end
|
|
900
918
|
|
|
@@ -1081,6 +1099,10 @@ module TypeProf::Core
|
|
|
1081
1099
|
return unless cpath
|
|
1082
1100
|
mod = genv.resolve_cpath(cpath)
|
|
1083
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
|
|
1084
1106
|
changes.add_edge(genv, Source.new(Type::Instance.new(genv, mod, args)), vtx)
|
|
1085
1107
|
end
|
|
1086
1108
|
|
|
@@ -1090,6 +1112,7 @@ module TypeProf::Core
|
|
|
1090
1112
|
return unless cpath
|
|
1091
1113
|
mod = genv.resolve_cpath(cpath)
|
|
1092
1114
|
args = @args.map {|arg| arg.contravariant_vertex(genv, changes, subst) }
|
|
1115
|
+
mod.type_params.drop(args.size).each { args << genv.untyped_arg }
|
|
1093
1116
|
changes.add_edge(genv, Source.new(Type::Instance.new(genv, mod, args)), vtx)
|
|
1094
1117
|
end
|
|
1095
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
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
module TypeProf::Core
|
|
2
2
|
class AST
|
|
3
3
|
def self.parse_rb(path, src, position_encoding)
|
|
4
|
-
|
|
4
|
+
build_rb(path, Prism.parse(src), position_encoding)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
# This needs the comments and the Prism::Source, which a ParseResult has but a
|
|
8
|
+
# node does not.
|
|
9
|
+
def self.build_rb(path, result, position_encoding)
|
|
10
|
+
unless result.is_a?(Prism::ParseResult)
|
|
11
|
+
raise ArgumentError, "expected Prism::ParseResult, got #{ result.class }"
|
|
12
|
+
end
|
|
5
13
|
|
|
6
14
|
return nil unless result.errors.empty?
|
|
7
15
|
|
|
8
16
|
# comments, errors, magic_comments
|
|
9
17
|
raw_scope = result.value
|
|
10
18
|
|
|
11
|
-
|
|
19
|
+
unless raw_scope.is_a?(Prism::ProgramNode)
|
|
20
|
+
raise ArgumentError, "expected Prism::ProgramNode, got #{ raw_scope.class }"
|
|
21
|
+
end
|
|
12
22
|
|
|
13
23
|
prism_source = result.source
|
|
14
24
|
file_context = FileContext.new(path, position_encoding, prism_source, result.comments)
|
|
@@ -268,6 +278,7 @@ module TypeProf::Core
|
|
|
268
278
|
when :splat_node then SplatNode.new(raw_node, lenv)
|
|
269
279
|
when :for_node then ForNode.new(raw_node, lenv)
|
|
270
280
|
when :alias_global_variable_node then AliasGlobalVariableNode.new(raw_node, lenv)
|
|
281
|
+
when :pre_execution_node then PreExecutionNode.new(raw_node, lenv)
|
|
271
282
|
when :post_execution_node then PostExecutionNode.new(raw_node, lenv)
|
|
272
283
|
when :flip_flop_node then FlipFlopNode.new(raw_node, lenv)
|
|
273
284
|
when :shareable_constant_node then create_node(raw_node.write, lenv)
|
|
@@ -352,7 +363,7 @@ module TypeProf::Core
|
|
|
352
363
|
|
|
353
364
|
when :capture_pattern_node then CapturePatternNode.new(raw_node, lenv)
|
|
354
365
|
|
|
355
|
-
when :if_node then IfPatternNode.new(raw_node, lenv)
|
|
366
|
+
when :if_node, :unless_node then IfPatternNode.new(raw_node, lenv)
|
|
356
367
|
|
|
357
368
|
when :pinned_variable_node then PinnedPatternNode.new(raw_node, lenv)
|
|
358
369
|
when :pinned_expression_node then PinnedPatternNode.new(raw_node, lenv)
|
|
@@ -386,6 +397,7 @@ module TypeProf::Core
|
|
|
386
397
|
|
|
387
398
|
when :array_node then ArrayNode.new(raw_node, lenv) # for %w[foo bar]
|
|
388
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)
|
|
389
401
|
|
|
390
402
|
else
|
|
391
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
|