diff --git a/lib/typeprof/core/ast/misc.rb b/lib/typeprof/core/ast/misc.rb index 9c94bc5e..52db028d 100644 --- a/lib/typeprof/core/ast/misc.rb +++ b/lib/typeprof/core/ast/misc.rb @@ -256,9 +256,9 @@ def install0(genv) vtx = @expr.install(genv) a_args = ActualArguments.new([], [], nil, nil) - vtx = @changes.add_method_call_box(genv, vtx, :to_a, a_args, false).ret + to_a_vtx = @changes.add_method_call_box(genv, vtx, :to_a, a_args, false, suppress_errors: true).ret - @changes.add_splat_box(genv, vtx).ret + @changes.add_splat_box(genv, to_a_vtx, nil, vtx).ret end end diff --git a/lib/typeprof/core/graph/box.rb b/lib/typeprof/core/graph/box.rb index 1e62042c..91fb388d 100644 --- a/lib/typeprof/core/graph/box.rb +++ b/lib/typeprof/core/graph/box.rb @@ -600,11 +600,13 @@ def wrong_return_type(f_ret_show, changes) end class SplatBox < Box - def initialize(node, genv, ary, idx) + def initialize(node, genv, ary, idx, orig = nil) super(node) @ary = ary @idx = idx + @orig = orig @ary.add_edge(genv, self) + @orig.add_edge(genv, self) if @orig @ret = Vertex.new(node) end @@ -629,6 +631,12 @@ def run0(genv, changes) "???" end end + # For types where to_a is not defined, [*x] wraps x as [x] + if @orig && @ary.types.empty? + @orig.each_type do |ty| + changes.add_edge(genv, Source.new(ty), @ret) + end + end end end @@ -992,7 +1000,7 @@ def run0(genv, changes) end class MethodCallBox < Box - def initialize(node, genv, recv, mid, a_args, subclasses) + def initialize(node, genv, recv, mid, a_args, subclasses, suppress_errors: false) raise mid.to_s unless mid super(node) @recv = recv.new_vertex(genv, node) @@ -1003,6 +1011,7 @@ def initialize(node, genv, recv, mid, a_args, subclasses) @a_args.block.add_edge(genv, self) if @a_args.block @ret = Vertex.new(node) @subclasses = subclasses + @suppress_errors = suppress_errors @generics = {} end @@ -1014,10 +1023,11 @@ def run0(genv, changes) error_count = 0 resolve(genv, changes) do |me, ty, mid, orig_ty| if !me - # TODO: undefined method error - if error_count < 3 - meth = @node.mid_code_range ? :mid_code_range : :code_range - changes.add_diagnostic(meth, "undefined method: #{ orig_ty.show }##{ mid }") + unless @suppress_errors + if error_count < 3 + meth = @node.mid_code_range ? :mid_code_range : :code_range + changes.add_diagnostic(meth, "undefined method: #{ orig_ty.show }##{ mid }") + end end error_count += 1 elsif me.builtin && me.builtin[changes, @node, orig_ty, @a_args, @ret] @@ -1062,7 +1072,7 @@ def run0(genv, changes) edges.each do |src, dst| changes.add_edge(genv, src, dst) end - if error_count > 3 + if error_count > 3 && !@suppress_errors meth = @node.mid_code_range ? :mid_code_range : :code_range changes.add_diagnostic(meth, "... and other #{ error_count - 3 } errors") end diff --git a/lib/typeprof/core/graph/change_set.rb b/lib/typeprof/core/graph/change_set.rb index 802d864a..57bada27 100644 --- a/lib/typeprof/core/graph/change_set.rb +++ b/lib/typeprof/core/graph/change_set.rb @@ -69,9 +69,9 @@ def add_edge(genv, src, dst) # TODO: if an edge is removed during one analysis, we may need to remove sub-boxes? - def add_method_call_box(genv, recv, mid, a_args, subclasses) - key = [:mcall, recv, mid, a_args, subclasses] - @new_boxes[key] ||= MethodCallBox.new(@node, genv, recv, mid, a_args, subclasses) + def add_method_call_box(genv, recv, mid, a_args, subclasses, suppress_errors: false) + key = [:mcall, recv, mid, a_args, subclasses, suppress_errors] + @new_boxes[key] ||= MethodCallBox.new(@node, genv, recv, mid, a_args, subclasses, suppress_errors: suppress_errors) end def add_escape_box(genv, a_ret) @@ -79,9 +79,9 @@ def add_escape_box(genv, a_ret) @new_boxes[key] ||= EscapeBox.new(@node, genv, a_ret) end - def add_splat_box(genv, arg, idx = nil) - key = [:splat, arg, idx] - @new_boxes[key] ||= SplatBox.new(@node, genv, arg, idx) + def add_splat_box(genv, arg, idx = nil, orig = nil) + key = [:splat, arg, idx, orig] + @new_boxes[key] ||= SplatBox.new(@node, genv, arg, idx, orig) end def add_hash_splat_box(genv, arg, unified_key, unified_val) diff --git a/scenario/known-issues/splat-union-fallback.rb b/scenario/known-issues/splat-union-fallback.rb new file mode 100644 index 00000000..a6b6b625 --- /dev/null +++ b/scenario/known-issues/splat-union-fallback.rb @@ -0,0 +1,12 @@ +## update +def foo(x) + [*x] +end + +foo([:int]) +foo(:sym) + +## assert +class Object + def foo: (Array[:int] | :sym) -> Array[:int | :sym] +end