Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[prototype runtime] Fix error if direct inherited #1590

Merged
merged 2 commits into from
Nov 1, 2023
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
4 changes: 2 additions & 2 deletions lib/rbs/prototype/runtime.rb
Original file line number Diff line number Diff line change
Expand Up @@ -493,9 +493,9 @@ def generate_class(mod)
end #: AST::Declarations::Class?

unless decl
if mod < Struct
if StructGenerator.generatable?(mod)
decl = StructGenerator.new(mod).build_decl
elsif RUBY_VERSION >= '3.2' && mod < Data
elsif DataGenerator.generatable?(mod)
decl = DataGenerator.new(mod).build_decl
else
decl = AST::Declarations::Class.new(
Expand Down
89 changes: 64 additions & 25 deletions lib/rbs/prototype/runtime/value_object_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,16 @@ def build_member_accessors(ast_members_class)
end

class StructGenerator < ValueObjectBase
def self.generatable?(target)
return false unless target < Struct
# Avoid direct inherited class like `class Option < Struct`
return false unless target.respond_to?(:members)

true
end

private

CAN_CALL_KEYWORD_INIT_P = Struct.new(:tmp).respond_to?(:keyword_init?)

def build_super_class
Expand All @@ -104,31 +114,21 @@ def build_s_new
[:new, :[]].map do |name|
new_overloads = []

if CAN_CALL_KEYWORD_INIT_P ? (@target_class.keyword_init? == false || @target_class.keyword_init? == nil) : true
new_overloads << AST::Members::MethodDefinition::Overload.new(
annotations: [],
method_type: MethodType.new(
type: Types::Function.empty(Types::Bases::Instance.new(location: nil)).update(
optional_positionals: @target_class.members.map { |m| Types::Function::Param.new(name: m, type: untyped) },
),
type_params: [],
block: nil,
location: nil,
)
)
end
if CAN_CALL_KEYWORD_INIT_P ? (@target_class.keyword_init? == true || @target_class.keyword_init? == nil) : true
new_overloads << AST::Members::MethodDefinition::Overload.new(
annotations: [],
method_type: MethodType.new(
type: Types::Function.empty(Types::Bases::Instance.new(location: nil)).update(
optional_keywords: @target_class.members.to_h { |m| [m, Types::Function::Param.new(name: nil, type: untyped)] },
),
type_params: [],
block: nil,
location: nil,
)
)
if CAN_CALL_KEYWORD_INIT_P
case @target_class.keyword_init?
when false
new_overloads << build_overload_for_positional_arguments
when true
new_overloads << build_overload_for_keyword_arguments
when nil
new_overloads << build_overload_for_positional_arguments
new_overloads << build_overload_for_keyword_arguments
else
raise
end
else
new_overloads << build_overload_for_positional_arguments
new_overloads << build_overload_for_keyword_arguments
end

AST::Members::MethodDefinition.new(
Expand All @@ -144,6 +144,34 @@ def build_s_new
end
end

def build_overload_for_positional_arguments
AST::Members::MethodDefinition::Overload.new(
annotations: [],
method_type: MethodType.new(
type: Types::Function.empty(Types::Bases::Instance.new(location: nil)).update(
optional_positionals: @target_class.members.map { |m| Types::Function::Param.new(name: m, type: untyped) },
),
type_params: [],
block: nil,
location: nil,
)
)
end

def build_overload_for_keyword_arguments
AST::Members::MethodDefinition::Overload.new(
annotations: [],
method_type: MethodType.new(
type: Types::Function.empty(Types::Bases::Instance.new(location: nil)).update(
optional_keywords: @target_class.members.to_h { |m| [m, Types::Function::Param.new(name: nil, type: untyped)] },
),
type_params: [],
block: nil,
location: nil,
)
)
end

# def self.keyword_init?: () -> bool?
def build_s_keyword_init_p
return [] unless CAN_CALL_KEYWORD_INIT_P
Expand Down Expand Up @@ -179,6 +207,17 @@ def build_s_keyword_init_p
end

class DataGenerator < ValueObjectBase
def self.generatable?(target)
return false unless RUBY_VERSION >= '3.2'
return false unless target < Data
# Avoid direct inherited class like `class Option < Data`
return false unless target.respond_to?(:members)

true
end

private

def build_super_class
AST::Declarations::Class::Super.new(name: TypeName("::Data"), args: [], location: nil)
end
Expand Down
70 changes: 61 additions & 9 deletions sig/prototype/runtime.rbs
Original file line number Diff line number Diff line change
@@ -1,6 +1,67 @@
module RBS
module Prototype
class Runtime
module Helpers
private

def const_name: (Module const) -> String?

def const_name!: (Module const) -> String

# Returns the exact name & not compactly declared name
def only_name: (Module mod) -> String

def to_type_name: (String name, ?full_name: bool) -> TypeName

def untyped: () -> untyped
end

class ValueObjectBase
include Helpers

def build_decl: () -> AST::Declarations::Class

private

def build_member_accessors: (untyped ast_members_class) -> untyped

def build_s_members: () -> Array[AST::Members::MethodDefinition]

def initialize: (Class target_class) -> void
end

class StructGenerator < ValueObjectBase
def self.generatable?: (Class target) -> bool

private

def add_decl_members: (AST::Declarations::Class decl) -> void

def build_overload_for_keyword_arguments: () -> AST::Members::MethodDefinition::Overload

def build_overload_for_positional_arguments: () -> AST::Members::MethodDefinition::Overload

def build_s_keyword_init_p: () -> Array[AST::Members::MethodDefinition]

def build_s_new: () -> Array[AST::Members::MethodDefinition]

def build_super_class: () -> AST::Declarations::Class::Super

CAN_CALL_KEYWORD_INIT_P: bool
end

class DataGenerator < ValueObjectBase
def self.generatable?: (Class target) -> bool

private

def add_decl_members: (AST::Declarations::Class decl) -> void

def build_s_new: () -> Array[AST::Members::MethodDefinition]

def build_super_class: () -> AST::Declarations::Class::Super
end

class Todo
@builder: DefinitionBuilder

Expand Down Expand Up @@ -58,8 +119,6 @@ module RBS

def decls: () -> Array[AST::Declarations::t]

def to_type_name: (String name, ?full_name: bool) -> TypeName

def each_mixined_module: (TypeName type_name, Module mod) { (TypeName, TypeName, mixin_class) -> void } -> void

def each_mixined_module_one: (TypeName type_name, Module mod) { (TypeName, TypeName, bool) -> void } -> void
Expand Down Expand Up @@ -87,13 +146,6 @@ module RBS
# This generates/finds declarations in nested form & returns the last array of declarations
def ensure_outer_module_declarations: (Module mod) -> Array[AST::Declarations::Class::member]

# Returns the exact name & not compactly declared name
def only_name: (Module mod) -> String

def const_name: (Module const) -> String?

def const_name!: (Module const) -> String

def object_class: (untyped) -> untyped

def type_args: (TypeName type_name) -> Array[Types::t]
Expand Down
24 changes: 24 additions & 0 deletions test/rbs/runtime_prototype_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,8 @@ class StructInheritWithNil < Struct.new(:foo, :bar, keyword_init: nil)
end
StructKeywordInitTrue = Struct.new(:foo, :bar, keyword_init: true)
StructKeywordInitFalse = Struct.new(:foo, :bar, keyword_init: false)
class StructDirectInherited < Struct
end

def test_struct
SignatureManager.new do |manager|
Expand Down Expand Up @@ -846,6 +848,16 @@ def members: () -> [ :foo, :bar ]
end
RBS
end

p = Runtime.new(patterns: ["RBS::RuntimePrototypeTest::StructDirectInherited"], env: env, merge: false)
assert_write p.decls, <<~RBS
module RBS
class RuntimePrototypeTest < ::Test::Unit::TestCase
class StructDirectInherited < ::Struct[untyped]
end
end
end
RBS
end
end
end
Expand All @@ -854,6 +866,8 @@ def members: () -> [ :foo, :bar ]
class DataInherit < Data.define(:foo, :bar)
end
DataConst = Data.define(:foo, :bar)
class DataDirectInherit < Data
end

def test_data
SignatureManager.new do |manager|
Expand Down Expand Up @@ -903,6 +917,16 @@ def members: () -> [ :foo, :bar ]
end
end
RBS

p = Runtime.new(patterns: ["RBS::RuntimePrototypeTest::DataDirectInherit"], env: env, merge: false)
assert_write p.decls, <<~RBS
module RBS
class RuntimePrototypeTest < ::Test::Unit::TestCase
class DataDirectInherit < ::Data
end
end
end
RBS
end
end
end
Expand Down
4 changes: 4 additions & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ module Enumerable[A]
class Hash[unchecked out K, unchecked out V]
include Enumerable[[K, V]]
end

class Struct[Elem]
include Enumerable[Elem?]
end
SIG

def add_file(path, content)
Expand Down