Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions lib/typeprof/core/ast/meta.rb
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,9 @@ def subnodes = { block_body: }
def attrs = { static_cpath:, members:, kind: }

# Interface expected by MethodDefBox
def req_positionals = @kind == :struct ? @members : []
def opt_positionals = []
def req_positionals = []
# Struct.new(:x).new is valid, so every member is optional
def opt_positionals = @kind == :struct ? @members : []
def rest_positionals = nil
def post_positionals = []
def req_keywords = @kind == :data ? @members : []
Expand All @@ -323,6 +324,10 @@ def define0(genv)
ive = genv.resolve_ivar(struct_base_cpath, false, member)
ive.add_def(self)
end
if @kind == :struct
# Struct.[] calls the receiver's initialize, like Class#new
genv.resolve_method(struct_base_cpath, true, :[]).builtin = Builtin.new(genv).method(:class_new)
end
@block_body.define(genv) if @block_body
cdef
end
Expand All @@ -345,11 +350,16 @@ def define_copy(genv)
def undefine0(genv)
mod = genv.resolve_cpath(@static_cpath)
mod.remove_module_def(genv, self)
genv.resolve_cpath(struct_base_cpath).remove_module_def(genv, self)
base = genv.resolve_cpath(struct_base_cpath)
base.remove_module_def(genv, self)
@members.each do |member|
ive = genv.resolve_ivar(struct_base_cpath, false, member)
ive.remove_def(self)
end
# On an update, the new node has already set the builtin
if base.module_defs.to_a.none? { _1.kind == :struct }
genv.resolve_method(struct_base_cpath, true, :[]).builtin = nil
end
@block_body.undefine(genv) if @block_body
end

Expand Down Expand Up @@ -388,20 +398,13 @@ def install0(genv)
end
init_ret = @changes.add_escape_box(genv, Source.new(genv.nil_type))
if @kind == :struct
init_f_args = FormalArguments.new(init_vtxs, [], nil, [], [], [], nil, nil)
init_f_args = FormalArguments.new([], init_vtxs, nil, [], [], [], nil, nil)
else
# Data.define uses keyword arguments
init_f_args = FormalArguments.new([], [], nil, [], init_vtxs, [], nil, nil)
end
@changes.add_method_def_box(genv, cpath, false, :initialize, init_f_args, [init_ret])

# Struct.[] is an alias for Struct.new
if @kind == :struct
# Struct.[] builds the struct class itself, not the base class
self_ret = @changes.add_escape_box(genv, Source.new(Type::Instance.new(genv, genv.resolve_cpath(@static_cpath), [])))
@changes.add_method_def_box(genv, cpath, true, :[], init_f_args, [self_ret])
end

# Install block body (additional method definitions)
if @block_body
@block_body.lenv.locals[:"*self"] = @block_body.lenv.cref.get_self(genv)
Expand Down
4 changes: 2 additions & 2 deletions lib/typeprof/core/graph/box.rb
Original file line number Diff line number Diff line change
Expand Up @@ -840,7 +840,7 @@ def call(changes, genv, a_args, ret)
end
end

def show(output_parameter_names)
def show(output_parameter_names, ret: nil)
block_show = []
if @record_block.used
blk_f_args = @record_block.f_args.map {|arg| arg.show }.join(", ")
Expand Down Expand Up @@ -891,7 +891,7 @@ def show(output_parameter_names)
args = args.join(", ")
s = args.empty? ? [] : ["(#{ args })"]
s << "#{ block_show.sort.join(" | ") }" unless block_show.empty?
s << "-> #{ @mid == :initialize ? "void" : @ret.show }"
s << "-> #{ ret || (@mid == :initialize ? "void" : @ret.show) }"
s.join(" ")
end
end
Expand Down
8 changes: 8 additions & 0 deletions lib/typeprof/core/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,14 @@ def dump_declarations(path)
!@genv.resolve_method(node.static_cpath, mdef.singleton, mdef.mid).defs.empty?
out << " " * stack.size + "def #{ mdef.singleton ? "self." : "" }#{ mdef.mid }: " + mdef.show(@options[:output_parameter_names])
end
if node.is_a?(AST::StructNewNode) && node.kind == :struct &&
@genv.resolve_method(node.static_cpath, true, :[]).defs.empty?
# Struct.[] takes the same arguments as the initialize it reaches
init_mdef = @genv.resolve_method(node.static_cpath, false, :initialize).defs.to_a.first ||
@genv.resolve_method(node.struct_base_cpath, false, :initialize).defs.to_a.first
ret = Type::Instance.new(@genv, mod, []).show
out << " " * stack.size + "def self.[]: " + init_mdef.show(@options[:output_parameter_names], ret:)
end
else
stack.pop
out << " " * stack.size + "end"
Expand Down
13 changes: 13 additions & 0 deletions scenario/diagnostics/struct-aref.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## update
Pt = Struct.new(:x, :y) do
def initialize(x = 0, y = 0)
super
end
end

Pt[]
Pt[3, 4]
Pt[1, 2, 3]

## diagnostics
(9,2)-(9,11): wrong number of arguments (3 for 0...2)
10 changes: 10 additions & 0 deletions scenario/diagnostics/struct-optional-members.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
## update
Pt = Struct.new(:x, :y)

Pt.new
Pt.new(1)
Pt[]
Pt[1, 2, 3]

## diagnostics
(6,2)-(6,11): wrong number of arguments (3 for 0...2)
10 changes: 5 additions & 5 deletions scenario/incremental/struct-new-block.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ def name: -> String
def name=: (untyped) -> untyped
def age: -> Integer
def age=: (untyped) -> untyped
def initialize: (String, Integer) -> void
def self.[]: (String, Integer) -> Dog
def initialize: (?String, ?Integer) -> void
def self.[]: (?String, ?Integer) -> Dog
end

## update
Expand All @@ -26,7 +26,7 @@ def name: -> String
def name=: (untyped) -> untyped
def age: -> Integer
def age=: (untyped) -> untyped
def self.[]: (String, Integer) -> Dog
def self.[]: (String, String) -> Dog
def initialize: (String, String) -> void
end

Expand All @@ -40,6 +40,6 @@ def name: -> String
def name=: (untyped) -> untyped
def age: -> Integer
def age=: (untyped) -> untyped
def initialize: (String, Integer) -> void
def self.[]: (String, Integer) -> Dog
def initialize: (?String, ?Integer) -> void
def self.[]: (?String, ?Integer) -> Dog
end
18 changes: 9 additions & 9 deletions scenario/misc/struct_new.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ def bar: -> Integer
def bar=: (Integer) -> Integer
def baz: -> String
def baz=: (untyped) -> untyped
def initialize: (Integer, String) -> void
def self.[]: (Integer, String) -> Foo
def initialize: (?Integer, ?String) -> void
def self.[]: (?Integer, ?String) -> Foo
end

## update
Expand Down Expand Up @@ -41,8 +41,8 @@ def double
class Bar
def n: -> Integer
def n=: (untyped) -> untyped
def initialize: (Integer) -> void
def self.[]: (Integer) -> Bar
def initialize: (?Integer) -> void
def self.[]: (?Integer) -> Bar
def double: -> Integer
end

Expand All @@ -64,8 +64,8 @@ def ivar
class Baz
def v: -> Integer
def v=: (untyped) -> untyped
def initialize: (Integer) -> void
def self.[]: (Integer) -> Baz
def initialize: (?Integer) -> void
def self.[]: (?Integer) -> Baz
def set_label: -> String
def ivar: -> String
end
Expand All @@ -88,7 +88,7 @@ def x: -> Integer
def x=: (untyped) -> untyped
def y: -> Integer
def y=: (untyped) -> untyped
def self.[]: (Integer, Integer) -> Pt
def self.[]: (?Integer, ?Integer) -> Pt
def initialize: (?Integer, ?Integer) -> void
end

Expand All @@ -106,7 +106,7 @@ def name: -> String
def name=: (untyped) -> untyped
def age: -> Integer
def age=: (untyped) -> untyped
def self.[]: (String, Integer) -> Dog
def self.[]: (String, String) -> Dog
def initialize: (String, String) -> void
end

Expand Down Expand Up @@ -140,7 +140,7 @@ def x: -> Integer
def x=: (untyped) -> untyped
def y: -> Integer
def y=: (untyped) -> untyped
def self.[]: (Integer, Integer) -> Pt
def self.[]: (?Integer, ?Integer) -> Pt
end
class Pt
def initialize: (?Integer, ?Integer) -> void
Expand Down
Loading