lib/steep/interface/builder.rb in steep-0.5.1 vs lib/steep/interface/builder.rb in steep-0.6.0

- old
+ new

@@ -13,53 +13,59 @@ super + " #{chain.join(" ~> ")}" end end attr_reader :signatures - attr_reader :cache + attr_reader :class_cache + attr_reader :module_cache + attr_reader :instance_cache + attr_reader :interface_cache def initialize(signatures:) - @cache = {} @signatures = signatures + @class_cache = {} + @module_cache = {} + @instance_cache = {} + @interface_cache = {} end - def absolute_type_name(type_name, current:) - if current - begin - case type_name - when TypeName::Instance - type_name.map_module_name {|name| - signatures.find_class_or_module(name, current_module: current).name - } - when TypeName::Module - type_name.map_module_name {|name| - signatures.find_module(name, current_module: current).name - } - when TypeName::Class - type_name.map_module_name {|name| - signatures.find_class(name, current_module: current).name - } - else - type_name - end - rescue => exn - STDERR.puts "Cannot find absolute type name: #{exn.inspect}" - type_name - end - else - type_name.map_module_name(&:absolute!) - end - end - def absolute_type(type, current:) case type - when AST::Types::Name - AST::Types::Name.new( - name: absolute_type_name(type.name, current: current), - args: type.args.map {|ty| absolute_type(ty, current: current) }, + when AST::Types::Name::Instance + signature = signatures.find_class_or_module(type.name, current_module: current) + AST::Types::Name::Instance.new( + name: signature.name, + args: type.args.map {|arg| absolute_type(arg, current: current) }, location: type.location ) + when AST::Types::Name::Class + signature = signatures.find_class(type.name, current_module: current) + AST::Types::Name::Class.new( + name: signature.name, + constructor: type.constructor, + location: type.location + ) + when AST::Types::Name::Module + signature = signatures.find_class_or_module(type.name, current_module: current) + AST::Types::Name::Module.new( + name: signature.name, + location: type.location + ) + when AST::Types::Name::Interface + signature = signatures.find_interface(type.name, namespace: current) + AST::Types::Name::Interface.new( + name: signature.name, + args: type.args.map {|arg| absolute_type(arg, current: current) }, + location: type.location + ) + when AST::Types::Name::Alias + signature = signatures.find_alias(type.name, namespace: current) + AST::Types::Name::Alias.new( + name: signature.name, + args: type.args.map {|arg| absolute_type(arg, current: current) }, + location: type.location + ) when AST::Types::Union AST::Types::Union.build( types: type.types.map {|ty| absolute_type(ty, current: current) }, location: type.location ) @@ -77,57 +83,76 @@ AST::Types::Proc.new( params: type.params.map_type {|ty| absolute_type(ty, current: current) }, return_type: absolute_type(type.return_type, current: current), location: type.location ) + when AST::Types::Hash + AST::Types::Hash.new( + elements: type.elements.transform_values {|ty| absolute_type(ty, current: current) }, + location: type.location + ) else type end end - def build(type_name, current: nil, with_initialize: false) - type_name = absolute_type_name(type_name, current: current) - cache_key = [type_name, with_initialize] - cached = cache[cache_key] + def cache_interface(cache, key:, &block) + cached = cache[key] case cached when nil - begin - cache[cache_key] = type_name - - interface = case type_name - when TypeName::Instance - instance_to_interface(signatures.find_class_or_module(type_name.name), with_initialize: with_initialize) - when TypeName::Module - module_to_interface(signatures.find_module(type_name.name)) - when TypeName::Class - class_to_interface(signatures.find_class_or_module(type_name.name), - constructor: type_name.constructor) - when TypeName::Interface - interface_to_interface(type_name.name, - signatures.find_interface(type_name.name)) - else - raise "Unexpected type_name: #{type_name.inspect}" - end - - cache[cache_key]= interface - rescue RecursiveDefinitionError => exn - exn.chain.unshift(type_name) - raise - end - when TypeName::Base - raise RecursiveDefinitionError, type_name + cache[key] = key + cache[key] = yield + when key + raise RecursiveDefinitionError, key else cached end + rescue RecursiveDefinitionError => exn + cache.delete key + raise exn end - def merge_mixin(type_name, args, methods:, ivars:, supers:, current:) - mixed = block_given? ? yield : build(type_name, current: current) + def assert_absolute_name!(name) + raise "Name should be absolute: #{name}" unless name.absolute? + end - supers.push(*mixed.supers) - instantiated = mixed.instantiate( + def build_class(module_name, constructor:) + assert_absolute_name! module_name + signature = signatures.find_class(module_name, current_module: AST::Namespace.root) + cache_interface(class_cache, key: [signature.name, !!constructor]) do + class_to_interface(signature, constructor: constructor) + end + end + + def build_module(module_name) + assert_absolute_name! module_name + signature = signatures.find_module(module_name, current_module: AST::Namespace.root) + cache_interface(module_cache, key: signature.name) do + module_to_interface(signature) + end + end + + def build_instance(module_name, with_initialize:) + assert_absolute_name! module_name + signature = signatures.find_class_or_module(module_name, current_module: AST::Namespace.root) + cache_interface(instance_cache, key: [signature.name, with_initialize]) do + instance_to_interface(signature, with_initialize: with_initialize) + end + end + + def build_interface(interface_name) + signature = signatures.find_interface(interface_name) + cache_interface(interface_cache, key: [signature.name]) do + interface_to_interface(nil, signature) + end + end + + def merge_mixin(interface, args, methods:, ivars:, supers:, current:) + supers.push(*interface.supers) + + instantiated = interface.instantiate( type: AST::Types::Self.new, args: args, instance_type: AST::Types::Instance.new, module_type: AST::Types::Class.new ) @@ -141,17 +166,17 @@ end merge_ivars ivars, instantiated.ivars end - def add_method(type_name, method, methods:, extra_attributes: []) + def add_method(type_name, method, methods:, extra_attributes: [], current:) super_method = methods[method.name] new_method = Method.new( type_name: type_name, name: method.name, types: method.types.map do |method_type| - method_type_to_method_type(method_type, current: type_name.name) + method_type_to_method_type(method_type, current: current) end, super_method: super_method, attributes: method.attributes + extra_attributes ) @@ -161,90 +186,93 @@ new_method end end def class_to_interface(sig, constructor:) - type_name = TypeName::Class.new(name: sig.name, constructor: constructor) + module_name = sig.name + namespace = module_name.namespace.append(module_name.name) supers = [] methods = { new: Method.new( - type_name: TypeName::Class.new(name: "Builtin", constructor: true), + type_name: Names::Module.parse(name: "__Builtin__"), name: :new, types: [ MethodType.new(type_params: [], params: Params.empty, block: nil, return_type: AST::Types::Instance.new, location: nil ) ], super_method: nil, - attributes: [] + attributes: [:incompatible] ) } - klass = build(TypeName::Instance.new(name: ModuleName.parse("::Class"))) + klass = build_instance(AST::Builtin::Class.module_name, with_initialize: false) instantiated = klass.instantiate( type: AST::Types::Self.new, - args: [AST::Types::Instance.new], + args: [], instance_type: AST::Types::Instance.new, module_type: AST::Types::Class.new ) methods.merge!(instantiated.methods) - unless sig.name == ModuleName.parse("::BasicObject") - super_class_name = sig.super_class&.name&.absolute! || ModuleName.parse("::Object") - merge_mixin(TypeName::Class.new(name: super_class_name, constructor: constructor), - [], - methods: methods, - ivars: {}, - supers: supers, - current: sig.name) + unless module_name == AST::Builtin::BasicObject.module_name + super_class_name = sig.super_class&.name&.yield_self {|name| signatures.find_class(name, current_module: namespace).name } || AST::Builtin::Object.module_name + class_interface = build_class(super_class_name, constructor: constructor) + merge_mixin(class_interface, [], methods: methods, ivars: {}, supers: supers, current: namespace) end sig.members.each do |member| case member when AST::Signature::Members::Include - merge_mixin(TypeName::Module.new(name: member.name), - [], - methods: methods, - supers: supers, - ivars: {}, - current: sig.name) + member_name = signatures.find_module(member.name, current_module: AST::Namespace.root).name + build_module(member_name).yield_self do |module_interface| + merge_mixin(module_interface, + [], + methods: methods, + supers: supers, + ivars: {}, + current: namespace) + end when AST::Signature::Members::Extend - merge_mixin(TypeName::Instance.new(name: member.name), - member.args.map {|type| absolute_type(type, current: sig.name) }, - methods: methods, - ivars: {}, - supers: supers, - current: sig.name) + member_name = signatures.find_module(member.name, current_module: AST::Namespace.root).name + build_instance(member_name, with_initialize: false).yield_self do |module_interface| + merge_mixin(module_interface, + member.args.map {|type| absolute_type(type, current: namespace) }, + methods: methods, + ivars: {}, + supers: supers, + current: namespace) + end end end sig.members.each do |member| case member when AST::Signature::Members::Method case when member.module_method? - add_method(type_name, member, methods: methods) + add_method(module_name, member, methods: methods, current: namespace) when member.instance_method? && member.name == :initialize if constructor methods[:new] = Method.new( - type_name: type_name, + type_name: module_name, name: :new, types: member.types.map do |method_type_sig| - method_type = method_type_to_method_type(method_type_sig, current: sig.name).with(return_type: AST::Types::Instance.new) + method_type = method_type_to_method_type(method_type_sig, current: namespace).with(return_type: AST::Types::Instance.new) args = (sig.params&.variables || []) + method_type.type_params method_type.with( type_params: args, return_type: AST::Types::Instance.new ) end, super_method: nil, - attributes: [] + attributes: [:incompatible] ) end end end end @@ -252,37 +280,47 @@ signatures.find_extensions(sig.name).each do |ext| ext.members.each do |member| case member when AST::Signature::Members::Method if member.module_method? - add_method(type_name, member, methods: methods) + add_method(module_name, member, methods: methods, current: namespace) end end end end + if methods[:new]&.type_name == AST::Builtin::Class.module_name + new_types = [MethodType.new(type_params: [], + params: Params.empty, + block: nil, + return_type: AST::Types::Instance.new, + location: nil)] + methods[:new] = methods[:new].with_types(new_types) + end + unless constructor methods.delete(:new) end Abstract.new( - name: type_name, + name: module_name, params: [], methods: methods, supers: supers, ivar_chains: {} ) end def module_to_interface(sig) - type_name = TypeName::Module.new(name: sig.name) + module_name = sig.name + namespace = module_name.namespace.append(module_name.name) - supers = [sig.self_type].compact.map {|type| absolute_type(type, current: nil) } + supers = [sig.self_type].compact.map {|type| absolute_type(type, current: namespace) } methods = {} ivar_chains = {} - module_instance = build(TypeName::Instance.new(name: ModuleName.parse("::Module"))) + module_instance = build_instance(AST::Builtin::Module.module_name, with_initialize: false) instantiated = module_instance.instantiate( type: AST::Types::Self.new, args: [], instance_type: AST::Types::Instance.new, module_type: AST::Types::Class.new @@ -290,77 +328,89 @@ methods.merge!(instantiated.methods) sig.members.each do |member| case member when AST::Signature::Members::Include - merge_mixin(TypeName::Module.new(name: member.name), - member.args.map {|type| absolute_type(type, current: sig.name) }, - methods: methods, - ivars: ivar_chains, - supers: supers, - current: sig.name) + member_name = signatures.find_module(member.name, current_module: AST::Namespace.root).name + build_module(member_name).yield_self do |module_interface| + merge_mixin(module_interface, + member.args.map {|type| absolute_type(type, current: namespace) }, + methods: methods, + ivars: ivar_chains, + supers: supers, + current: namespace) + end when AST::Signature::Members::Extend - merge_mixin(TypeName::Instance.new(name: member.name), - member.args.map {|type| absolute_type(type, current: sig.name) }, - methods: methods, - ivars: ivar_chains, - supers: supers, - current: sig.name) + member_name = signatures.find_module(member.name, current_module: AST::Namespace.root).name + build_instance(member_name, with_initialize: false).yield_self do |module_interface| + merge_mixin(module_interface, + member.args.map {|type| absolute_type(type, current: namespace) }, + methods: methods, + ivars: ivar_chains, + supers: supers, + current: namespace) + + end end end sig.members.each do |member| case member when AST::Signature::Members::Method if member.module_method? - add_method(type_name, member, methods: methods) + add_method(module_name, member, methods: methods, current: namespace) end when AST::Signature::Members::Ivar merge_ivars(ivar_chains, - { member.name => absolute_type(member.type, current: sig.name) }) + { member.name => absolute_type(member.type, current: namespace) }) when AST::Signature::Members::Attr - merge_attribute(sig, ivar_chains, methods, type_name, member) + merge_attribute(sig, ivar_chains, methods, module_name, member) end end - signatures.find_extensions(sig.name).each do |ext| + signatures.find_extensions(module_name).each do |ext| ext.members.each do |member| case member when AST::Signature::Members::Method if member.module_method? - add_method(type_name, member, methods: methods) + add_method(module_name, member, methods: methods, current: namespace) end end end end Abstract.new( - name: type_name, + name: module_name, params: [], methods: methods, supers: supers, ivar_chains: ivar_chains ) end def instance_to_interface(sig, with_initialize:) - type_name = TypeName::Instance.new(name: sig.name) + module_name = sig.name + namespace = module_name.namespace.append(module_name.name) params = sig.params&.variables || [] supers = [] methods = {} ivar_chains = {} if sig.is_a?(AST::Signature::Class) - unless sig.name == ModuleName.parse("::BasicObject") - super_class_name = sig.super_class&.name || ModuleName.parse("::Object") - super_class_interface = build(TypeName::Instance.new(name: super_class_name), current: nil, with_initialize: with_initialize) + unless sig.name == AST::Builtin::BasicObject.module_name + super_class_name = sig.super_class&.name || AST::Builtin::Object.module_name + if super_class_name.relative? + super_class_name = signatures.find_class(super_class_name, current_module: namespace).name + end + super_class_interface = build_instance(super_class_name, + with_initialize: with_initialize) supers.push(*super_class_interface.supers) instantiated = super_class_interface.instantiate( type: AST::Types::Self.new, - args: (sig.super_class&.args || []).map {|type| absolute_type(type, current: nil) }, + args: (sig.super_class&.args || []).map {|type| absolute_type(type, current: namespace) }, instance_type: AST::Types::Instance.new, module_type: AST::Types::Class.new ) methods.merge!(instantiated.methods) @@ -368,68 +418,71 @@ end end if sig.is_a?(AST::Signature::Module) if sig.self_type - supers << sig.self_type + supers << absolute_type(sig.self_type, current: namespace) end end sig.members.each do |member| case member when AST::Signature::Members::Include - merge_mixin(TypeName::Instance.new(name: member.name), - member.args.map {|type| absolute_type(type, current: sig.name) }, - methods: methods, - ivars: ivar_chains, - supers: supers, - current: sig.name) + member_name = signatures.find_module(member.name, current_module: namespace).name + build_instance(member_name, with_initialize: false).yield_self do |module_interface| + merge_mixin(module_interface, + member.args.map {|type| absolute_type(type, current: namespace) }, + methods: methods, + ivars: ivar_chains, + supers: supers, + current: namespace) + end end end sig.members.each do |member| case member when AST::Signature::Members::Method if member.instance_method? if with_initialize || member.name != :initialize extra_attrs = member.name == :initialize ? [:incompatible] : [] - add_method(type_name, member, methods: methods, extra_attributes: extra_attrs) + add_method(module_name, member, methods: methods, extra_attributes: extra_attrs, current: namespace) end end when AST::Signature::Members::Ivar merge_ivars(ivar_chains, - { member.name => absolute_type(member.type, current: sig.name) }) + { member.name => absolute_type(member.type, current: namespace) }) when AST::Signature::Members::Attr - merge_attribute(sig, ivar_chains, methods, type_name, member) + merge_attribute(sig, ivar_chains, methods, module_name, member, current: namespace) end end signatures.find_extensions(sig.name).each do |ext| ext.members.each do |member| case member when AST::Signature::Members::Method if member.instance_method? - add_method(type_name, member, methods: methods) + add_method(module_name, member, methods: methods, current: namespace) end end end end Abstract.new( - name: type_name, + name: module_name, params: params, methods: methods, supers: supers, ivar_chains: ivar_chains ) end - def merge_attribute(sig, ivar_chains, methods, type_name, member) + def merge_attribute(sig, ivar_chains, methods, type_name, member, current:) if member.ivar != false ivar_name = member.ivar || "@#{member.name}".to_sym merge_ivars(ivar_chains, - { ivar_name => absolute_type(member.type, current: sig.name) }) + { ivar_name => absolute_type(member.type, current: current) }) end reader_method = AST::Signature::Members::Method.new( location: member.location, name: member.name, @@ -441,46 +494,48 @@ block: nil, return_type: member.type) ], attributes: [] ) - add_method(type_name, reader_method, methods: methods) + add_method(type_name, reader_method, methods: methods, current: current) if member.accessor? writer_method = AST::Signature::Members::Method.new( location: member.location, name: "#{member.name}=".to_sym, kind: :instance, types: [ AST::MethodType.new(location: member.type.location, type_params: nil, - params: AST::MethodType::Params::Required.new(location: member.type.location, - type: member.type), + params: AST::MethodType::Params::Required.new( + location: member.type.location, + type: member.type + ), block: nil, return_type: member.type) ], attributes: [] ) - add_method(type_name, writer_method, methods: methods) + add_method(type_name, writer_method, methods: methods, current: current) end end def merge_ivars(dest, new_vars) new_vars.each do |name, new_type| dest[name] = IvarChain.new(type: new_type, parent: dest[name]) end end def interface_to_interface(_, sig) - type_name = TypeName::Interface.new(name: sig.name) + type_name = sig.name variables = sig.params&.variables || [] methods = sig.methods.each.with_object({}) do |method, methods| methods[method.name] = Method.new( type_name: type_name, name: method.name, types: method.types.map do |method_type| - method_type_to_method_type(method_type, current: nil) + method_type_to_method_type(method_type, current: type_name.namespace) end, super_method: nil, attributes: [] ) end