lib/rbs/environment.rb in rbs-0.4.0 vs lib/rbs/environment.rb in rbs-0.5.0

- old
+ new

@@ -1,281 +1,410 @@ module RBS class Environment attr_reader :buffers attr_reader :declarations - attr_reader :name_to_decl - attr_reader :name_to_extensions - attr_reader :name_to_constant - attr_reader :name_to_global - attr_reader :name_to_alias + attr_reader :class_decls + attr_reader :interface_decls + attr_reader :alias_decls + attr_reader :constant_decls + attr_reader :global_decls - def initialize - @buffers = [] - @declarations = [] - - @name_to_decl = {} - @name_to_extensions = {} - @name_to_constant = {} - @name_to_global = {} - @name_to_alias = {} + module ContextUtil + def context + @context ||= begin + (outer + [decl]).each.with_object([Namespace.root]) do |decl, array| + array.unshift(array.first + decl.name.to_namespace) + end + end + end end - def initialize_copy(other) - @buffers = other.buffers.dup - @declarations = other.declarations.dup + class MultiEntry + D = Struct.new(:decl, :outer, keyword_init: true) do + include ContextUtil + end - @name_to_decl = other.name_to_decl.dup - @name_to_extensions = other.name_to_extensions.dup - @name_to_constant = other.name_to_constant.dup - @name_to_global = other.name_to_global.dup - @name_to_alias = other.name_to_alias.dup - end + attr_reader :name + attr_reader :decls - def cache_name(cache, name:, decl:) - if cache.key?(name) - raise DuplicatedDeclarationError.new(name, decl, cache[name]) + def initialize(name:) + @name = name + @decls = [] end - cache[name] = decl + + def insert(decl:, outer:) + decls << D.new(decl: decl, outer: outer) + end + + def type_params + primary.decl.type_params + end end - def <<(decl) - declarations << decl - case decl - when AST::Declarations::Class, AST::Declarations::Module, AST::Declarations::Interface - cache_name name_to_decl, name: decl.name.absolute!, decl: decl - when AST::Declarations::Extension - yield_self do - name = decl.name.absolute! - exts = name_to_extensions.fetch(name) do - name_to_extensions[name] = [] + class ModuleEntry < MultiEntry + def insert(decl:, outer:) + unless decl.is_a?(AST::Declarations::Module) + raise MixedClassModuleDeclarationError.new(name: name, decl: decl) + end + + unless decls.empty? + names = decls[0].decl.type_params.each.map(&:name) + unless names.size == decl.type_params.each.size && decls[0].decl.type_params == decl.type_params.rename_to(names) + raise GenericParameterMismatchError.new(name: name, decl: decl) end - exts << decl end - when AST::Declarations::Alias - cache_name name_to_alias, name: decl.name.absolute!, decl: decl - when AST::Declarations::Constant - cache_name name_to_constant, name: decl.name.absolute!, decl: decl - when AST::Declarations::Global - cache_name name_to_global, name: decl.name, decl: decl + + super(decl: decl, outer: outer) end - end - def find_class(type_name) - name_to_decl[type_name] - end + def primary + @primary ||= decls.find {|d| d.decl.self_type } || decls.first + end - def each_decl - if block_given? - name_to_decl.each do |name, decl| - yield name, decl - end - else - enum_for :each_decl + def self_type + primary.decl.self_type end end - def each_constant - if block_given? - name_to_constant.each do |name, decl| - yield name, decl + class ClassEntry < MultiEntry + def insert(decl:, outer:) + unless decl.is_a?(AST::Declarations::Class) + raise MixedClassModuleDeclarationError.new(name: name, decl: decl) end - else - enum_for :each_constant - end - end - def each_global - if block_given? - name_to_global.each do |name, global| - yield name, global + unless decls.empty? + names = decls[0].decl.type_params.each.map(&:name) + unless names.size == decl.type_params.each.size && decls[0].decl.type_params == decl.type_params.rename_to(names) + raise GenericParameterMismatchError.new(name: name, decl: decl) + end end - else - enum_for :each_global + + super(decl: decl, outer: outer) end - end - def each_alias(&block) - if block_given? - name_to_alias.each(&block) - else - enum_for :each_alias + def primary + @primary ||= begin + decls.find {|d| d.decl.super_class } || decls.first + end end end - def each_class_name(&block) - each_decl.select {|name,| class?(name) }.each(&block) - end + class SingleEntry + include ContextUtil - def class?(type_name) - find_class(type_name)&.yield_self do |decl| - decl.is_a?(AST::Declarations::Class) || decl.is_a?(AST::Declarations::Module) + attr_reader :name + attr_reader :outer + attr_reader :decl + + def initialize(name:, decl:, outer:) + @name = name + @decl = decl + @outer = outer end end - def find_type_decl(type_name) - name_to_decl[type_name] + def initialize + @buffers = [] + @declarations = [] + + @class_decls = {} + @interface_decls = {} + @alias_decls = {} + @constant_decls = {} + @global_decls = {} end - def find_extensions(type_name) - name_to_extensions[type_name] || [] + def initialize_copy(other) + @buffers = other.buffers.dup + @declarations = other.declarations.dup + + @class_decls = other.class_decls.dup + @interface_decls = other.interface_decls.dup + @alias_decls = other.alias_decls.dup + @constant_decls = other.constant_decls.dup + @global_decls = other.global_decls.dup end - def find_alias(type_name) - name_to_alias[type_name] + def self.from_loader(loader) + self.new.tap do |env| + loader.load(env: env) + end end - def each_extension(type_name, &block) - if block_given? - (name_to_extensions[type_name] || []).each(&block) - else - enum_for :each_extension, type_name + def cache_name(cache, name:, decl:, outer:) + if cache.key?(name) + raise DuplicatedDeclarationError.new(name, decl, cache[name]) end + + cache[name] = SingleEntry.new(name: name, decl: decl, outer: outer) end - def absolute_type_name_in(environment, name:, namespace:) - raise "Namespace should be absolute: #{namespace}" unless namespace.absolute? + def insert_decl(decl, outer:, namespace:) + case decl + when AST::Declarations::Class, AST::Declarations::Module + name = decl.name.with_prefix(namespace) - if name.absolute? - name if environment.key?(name) - else - absolute_name = name.with_prefix(namespace) + if constant_decls.key?(name) + raise DuplicatedDeclarationError.new(name, decl, constant_decls[name].decl) + end - if environment.key?(absolute_name) - absolute_name - else - if namespace.empty? - nil - else - parent = namespace.parent - absolute_type_name_in environment, name: name, namespace: parent + unless class_decls.key?(name) + case decl + when AST::Declarations::Class + class_decls[name] ||= ClassEntry.new(name: name) + when AST::Declarations::Module + class_decls[name] ||= ModuleEntry.new(name: name) end end + + class_decls[name].insert(decl: decl, outer: outer) + + prefix = outer + [decl] + ns = name.to_namespace + decl.each_decl do |d| + insert_decl(d, outer: prefix, namespace: ns) + end + + when AST::Declarations::Interface + cache_name interface_decls, name: decl.name.with_prefix(namespace), decl: decl, outer: outer + + when AST::Declarations::Alias + cache_name alias_decls, name: decl.name.with_prefix(namespace), decl: decl, outer: outer + + when AST::Declarations::Constant + name = decl.name.with_prefix(namespace) + + if class_decls.key?(name) + raise DuplicatedDeclarationError.new(name, decl, class_decls[name].decls[0].decl) + end + + cache_name constant_decls, name: name, decl: decl, outer: outer + + when AST::Declarations::Global + cache_name global_decls, name: decl.name, decl: decl, outer: outer + + when AST::Declarations::Extension + RBS.logger.warn "#{Location.to_string decl.location} Extension construct is deprecated: use class/module syntax instead" end end - def absolute_class_name(name, namespace:) - raise "Class name expected: #{name}" unless name.class? - absolute_type_name_in name_to_decl, name: name, namespace: namespace + def <<(decl) + declarations << decl + insert_decl(decl, outer: [], namespace: Namespace.root) + self end - def absolute_interface_name(name, namespace:) - raise "Interface name expected: #{name}" unless name.interface? - absolute_type_name_in name_to_decl, name: name, namespace: namespace - end + def resolve_type_names + resolver = TypeNameResolver.from_env(self) + env = Environment.new() - def absolute_alias_name(name, namespace:) - raise "Alias name expected: #{name}" unless name.alias? - absolute_type_name_in name_to_alias, name: name, namespace: namespace + declarations.each do |decl| + env << resolve_declaration(resolver, decl, outer: [], prefix: Namespace.root) + end + + env end - def absolute_type_name(type_name, namespace:) - absolute_name = case - when type_name.class? - absolute_class_name(type_name, namespace: namespace) - when type_name.alias? - absolute_alias_name(type_name, namespace: namespace) - when type_name.interface? - absolute_interface_name(type_name, namespace: namespace) - end + def resolve_declaration(resolver, decl, outer:, prefix:) + if decl.is_a?(AST::Declarations::Global) + return AST::Declarations::Global.new( + name: decl.name, + type: absolute_type(resolver, decl.type, context: [Namespace.root]), + location: decl.location, + comment: decl.comment + ) + end - absolute_name || yield(type_name) - end + context = (outer + [decl]).each.with_object([Namespace.root]) do |decl, array| + array.unshift(array.first + decl.name.to_namespace) + end - def absolute_name_or(name, type) - if name.absolute? - type - else - yield + case decl + when AST::Declarations::Class + outer_ = outer + [decl] + prefix_ = prefix + decl.name.to_namespace + AST::Declarations::Class.new( + name: decl.name.with_prefix(prefix), + type_params: decl.type_params, + super_class: decl.super_class&.yield_self do |super_class| + AST::Declarations::Class::Super.new( + name: absolute_type_name(resolver, super_class.name, context: context), + args: super_class.args.map {|type| absolute_type(resolver, type, context: context) } + ) + end, + members: decl.members.map do |member| + case member + when AST::Members::Base + resolve_member(resolver, member, context: context) + when AST::Declarations::Base + resolve_declaration( + resolver, + member, + outer: outer_, + prefix: prefix_ + ) + end + end, + location: decl.location, + annotations: decl.annotations, + comment: decl.comment + ) + when AST::Declarations::Module + outer_ = outer + [decl] + prefix_ = prefix + decl.name.to_namespace + AST::Declarations::Module.new( + name: decl.name.with_prefix(prefix), + type_params: decl.type_params, + self_type: decl.self_type&.yield_self do |self_type| + absolute_type(resolver, self_type, context: context) + end, + members: decl.members.map do |member| + case member + when AST::Members::Base + resolve_member(resolver, member, context: context) + when AST::Declarations::Base + resolve_declaration( + resolver, + member, + outer: outer_, + prefix: prefix_ + ) + end + end, + location: decl.location, + annotations: decl.annotations, + comment: decl.comment + ) + when AST::Declarations::Interface + AST::Declarations::Interface.new( + name: decl.name.with_prefix(prefix), + type_params: decl.type_params, + members: decl.members.map do |member| + resolve_member(resolver, member, context: context) + end, + comment: decl.comment, + location: decl.location, + annotations: decl.annotations + ) + when AST::Declarations::Alias + AST::Declarations::Alias.new( + name: decl.name.with_prefix(prefix), + type: absolute_type(resolver, decl.type, context: context), + location: decl.location, + annotations: decl.annotations, + comment: decl.comment + ) + + when AST::Declarations::Constant + AST::Declarations::Constant.new( + name: decl.name.with_prefix(prefix), + type: absolute_type(resolver, decl.type, context: context), + location: decl.location, + comment: decl.comment + ) end end - def absolute_type(type, namespace:, &block) - case type - when Types::ClassSingleton - absolute_name_or(type.name, type) do - absolute_name = absolute_type_name(type.name, namespace: namespace) { yield(type) } - Types::ClassSingleton.new(name: absolute_name, location: type.location) - end - when Types::ClassInstance - absolute_name = absolute_type_name(type.name, namespace: namespace) { yield(type) } - Types::ClassInstance.new(name: absolute_name, - args: type.args.map {|ty| - absolute_type(ty, namespace: namespace, &block) - }, - location: type.location) - when Types::Interface - absolute_name = absolute_type_name(type.name, namespace: namespace) { yield(type) } - Types::Interface.new(name: absolute_name, - args: type.args.map {|ty| - absolute_type(ty, namespace: namespace, &block) - }, - location: type.location) - when Types::Alias - absolute_name_or(type.name, type) do - absolute_name = absolute_type_name(type.name, namespace: namespace) { yield(type) } - Types::Alias.new(name: absolute_name, location: type.location) - end - when Types::Tuple - Types::Tuple.new( - types: type.types.map {|ty| absolute_type(ty, namespace: namespace, &block) }, - location: type.location + def resolve_member(resolver, member, context:) + case member + when AST::Members::MethodDefinition + AST::Members::MethodDefinition.new( + name: member.name, + kind: member.kind, + types: member.types.map do |type| + type.map_type {|ty| absolute_type(resolver, ty, context: context) } + end, + comment: member.comment, + overload: member.overload?, + annotations: member.annotations, + attributes: member.attributes, + location: member.location ) - when Types::Record - Types::Record.new( - fields: type.fields.transform_values {|ty| absolute_type(ty, namespace: namespace, &block) }, - location: type.location + when AST::Members::AttrAccessor + AST::Members::AttrAccessor.new( + name: member.name, + type: absolute_type(resolver, member.type, context: context), + annotations: member.annotations, + comment: member.comment, + location: member.location, + ivar_name: member.ivar_name ) - when Types::Union - Types::Union.new( - types: type.types.map {|ty| absolute_type(ty, namespace: namespace, &block) }, - location: type.location + when AST::Members::AttrReader + AST::Members::AttrReader.new( + name: member.name, + type: absolute_type(resolver, member.type, context: context), + annotations: member.annotations, + comment: member.comment, + location: member.location, + ivar_name: member.ivar_name ) - when Types::Intersection - Types::Intersection.new( - types: type.types.map {|ty| absolute_type(ty, namespace: namespace, &block) }, - location: type.location + when AST::Members::AttrWriter + AST::Members::AttrWriter.new( + name: member.name, + type: absolute_type(resolver, member.type, context: context), + annotations: member.annotations, + comment: member.comment, + location: member.location, + ivar_name: member.ivar_name ) - when Types::Optional - Types::Optional.new( - type: absolute_type(type.type, namespace: namespace, &block), - location: type.location + when AST::Members::InstanceVariable + AST::Members::InstanceVariable.new( + name: member.name, + type: absolute_type(resolver, member.type, context: context), + comment: member.comment, + location: member.location ) - when Types::Proc - Types::Proc.new( - type: type.type.map_type {|ty| absolute_type(ty, namespace: namespace, &block) }, - location: type.location + when AST::Members::ClassInstanceVariable + AST::Members::ClassInstanceVariable.new( + name: member.name, + type: absolute_type(resolver, member.type, context: context), + comment: member.comment, + location: member.location ) + when AST::Members::ClassVariable + AST::Members::ClassVariable.new( + name: member.name, + type: absolute_type(resolver, member.type, context: context), + comment: member.comment, + location: member.location + ) + when AST::Members::Include + AST::Members::Include.new( + name: absolute_type_name(resolver, member.name, context: context), + args: member.args.map {|type| absolute_type(resolver, type, context: context) }, + comment: member.comment, + location: member.location, + annotations: member.annotations + ) + when AST::Members::Extend + AST::Members::Extend.new( + name: absolute_type_name(resolver, member.name, context: context), + args: member.args.map {|type| absolute_type(resolver, type, context: context) }, + comment: member.comment, + location: member.location, + annotations: member.annotations + ) + when AST::Members::Prepend + AST::Members::Prepend.new( + name: absolute_type_name(resolver, member.name, context: context), + args: member.args.map {|type| absolute_type(resolver, type, context: context) }, + comment: member.comment, + location: member.location, + annotations: member.annotations + ) else - type + member end end - # Validates presence of the relative type, and application arity match. - def validate(type, namespace:) - case type - when Types::ClassInstance, Types::Interface - if type.name.namespace.relative? - type = absolute_type(type, namespace: namespace) do |type| - NoTypeFoundError.check!(type.name.absolute!, env: self, location: type.location) - end - end + def absolute_type_name(resolver, type_name, context:) + resolver.resolve(type_name, context: context) || type_name + end - decl = find_class(type.name) - unless decl - raise NoTypeFoundError.new(type_name: type.name, location: type.location) - end - - InvalidTypeApplicationError.check!( - type_name: type.name, - args: type.args, - params: decl.type_params, - location: type.location - ) - end - - type.each_type do |type_| - validate(type_, namespace: namespace) + def absolute_type(resolver, type, context:) + type.map_type_name do |name| + absolute_type_name(resolver, name, context: context) end end end end