Skip to content

Commit

Permalink
Merge pull request #364 from ruby/overload-from-super-class
Browse files Browse the repository at this point in the history
Allow overloading from super class methods
  • Loading branch information
soutaro committed Aug 11, 2020
2 parents c237420 + 98063b5 commit 2e3e088
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 20 deletions.
13 changes: 13 additions & 0 deletions lib/rbs/definition.rb
Expand Up @@ -45,6 +45,15 @@ def annotations
def update(type: self.type, member: self.member, defined_in: self.defined_in, implemented_in: self.implemented_in)
TypeDef.new(type: type, member: member, defined_in: defined_in, implemented_in: implemented_in)
end

def overload?
case member
when AST::Members::MethodDefinition
member.overload?
else
false
end
end
end

attr_reader :super_method
Expand Down Expand Up @@ -79,6 +88,10 @@ def annotations
@annotations ||= @extra_annotations + defs.flat_map(&:annotations)
end

def members
@members ||= defs.map(&:member).uniq
end

# @deprecated
def attributes
[]
Expand Down
30 changes: 18 additions & 12 deletions lib/rbs/definition_builder.rb
Expand Up @@ -569,7 +569,7 @@ def method_definition_members(type_name, entry, kind:)
member, visibility = array[0]
result[method_name] = [visibility, nil, member]

when array.count {|pair| !pair[0].overload? } == 1
else
visibilities = array.group_by {|pair| pair[1] }

if visibilities.size > 1
Expand All @@ -583,14 +583,6 @@ def method_definition_members(type_name, entry, kind:)

overloads, primary = array.map(&:first).partition(&:overload?)
result[method_name] = [array[0][1], nil, *primary, *overloads]

else
raise InvalidOverloadMethodError.new(
type_name: type_name,
method_name: method_name,
kind: :instance,
members: array.map(&:first)
)
end
end
end
Expand Down Expand Up @@ -973,8 +965,15 @@ def merge_definitions(type_name, pairs, entry:, self_type:, ancestors:)
Substitution.build([], [])
end

kind = case ancestor
when Definition::Ancestor::Instance
:instance
when Definition::Ancestor::Singleton
:singleton
end

current_definition.methods.each do |name, method|
merge_method definition.methods, name, method, sub
merge_method type_name, definition.methods, name, method, sub, kind: kind
end

current_definition.instance_variables.each do |name, variable|
Expand All @@ -998,13 +997,20 @@ def merge_variable(variables, name, variable)
)
end

def merge_method(methods, name, method, sub)
def merge_method(type_name, methods, name, method, sub, kind:)
super_method = methods[name]

defs = if method.defs.all? {|d| d.overload? }
raise InvalidOverloadMethodError.new(type_name: type_name, method_name: name, kind: kind, members: method.members) unless super_method
method.defs + super_method.defs
else
method.defs
end

methods[name] = Definition::Method.new(
super_method: super_method,
accessibility: method.accessibility,
defs: sub.mapping.empty? ? method.defs : method.defs.map {|defn| defn.update(type: defn.type.sub(sub)) }
defs: sub.mapping.empty? ? defs : defs.map {|defn| defn.update(type: defn.type.sub(sub)) }
)
end

Expand Down
49 changes: 41 additions & 8 deletions test/rbs/definition_builder_test.rb
Expand Up @@ -499,14 +499,8 @@ def test2: (Integer) -> String | ...
end

env.class_decls[type_name("::InvalidOverloadError")].tap do |entry|
error = assert_raises RBS::InvalidOverloadMethodError do
builder.method_definition_members(type_name("::InvalidOverloadError"), entry, kind: :instance)
end

assert_equal type_name("::InvalidOverloadError"), error.type_name
assert_equal :foo, error.method_name
assert_equal :instance, error.kind
assert_equal 1, error.members.size
# Only overloading `...` method definitions (without non-overloading) is allowed
builder.method_definition_members(type_name("::InvalidOverloadError"), entry, kind: :instance)
end

env.class_decls[type_name("::UsingTestInterface")].tap do |entry|
Expand Down Expand Up @@ -1532,4 +1526,43 @@ class Hello::World
end
end
end

def test_definition_method_type_def_overload_from_super
SignatureManager.new do |manager|
manager.files.merge!(Pathname("foo.rbs") => <<-EOF)
class World
def foo: () -> String
end
class Hello < World
def foo: (Integer) -> String | ...
end
EOF
manager.build do |env|
builder = DefinitionBuilder.new(env: env)

builder.build_instance(type_name("::Hello")).tap do |definition|
foo = definition.methods[:foo]
assert_equal ["(::Integer) -> ::String", "() -> ::String"], foo.method_types.map(&:to_s)
end
end
end
end

def test_definition_method_type_def_overload_from_super_no_super
SignatureManager.new do |manager|
manager.files.merge!(Pathname("foo.rbs") => <<-EOF)
class Hello
def foo: (Integer) -> String | ...
end
EOF
manager.build do |env|
builder = DefinitionBuilder.new(env: env)

assert_raises RBS::InvalidOverloadMethodError do
builder.build_instance(type_name("::Hello"))
end
end
end
end
end

0 comments on commit 2e3e088

Please sign in to comment.