123456789_123456789_123456789_123456789_123456789_

Class: RBS::DefinitionBuilder::AncestorBuilder

Relationships & Source Files
Namespace Children
Classes:
Inherits: Object
Defined in: lib/rbs/definition_builder/ancestor_builder.rb

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(env:) ⇒ AncestorBuilder

[ GitHub ]

  
# File 'lib/rbs/definition_builder/ancestor_builder.rb', line 162

def initialize(env:)
  @env = env

  @one_instance_ancestors_cache = {}
  @instance_ancestors_cache = {}

  @one_singleton_ancestors_cache = {}
  @singleton_ancestors_cache = {}

  @one_interface_ancestors_cache = {}
  @interface_ancestors_cache = {}
end

Instance Attribute Details

#env (readonly)

[ GitHub ]

  
# File 'lib/rbs/definition_builder/ancestor_builder.rb', line 151

attr_reader :env

#instance_ancestors_cache (readonly)

[ GitHub ]

  
# File 'lib/rbs/definition_builder/ancestor_builder.rb', line 154

attr_reader :instance_ancestors_cache

#interface_ancestors_cache (readonly)

[ GitHub ]

  
# File 'lib/rbs/definition_builder/ancestor_builder.rb', line 160

attr_reader :interface_ancestors_cache

#one_instance_ancestors_cache (readonly)

[ GitHub ]

  
# File 'lib/rbs/definition_builder/ancestor_builder.rb', line 153

attr_reader :one_instance_ancestors_cache

#one_interface_ancestors_cache (readonly)

[ GitHub ]

  
# File 'lib/rbs/definition_builder/ancestor_builder.rb', line 159

attr_reader :one_interface_ancestors_cache

#one_singleton_ancestors_cache (readonly)

[ GitHub ]

  
# File 'lib/rbs/definition_builder/ancestor_builder.rb', line 156

attr_reader :one_singleton_ancestors_cache

#singleton_ancestors_cache (readonly)

[ GitHub ]

  
# File 'lib/rbs/definition_builder/ancestor_builder.rb', line 157

attr_reader :singleton_ancestors_cache

Instance Method Details

#fill_ancestor_source(ancestor, name:, source:, &block)

[ GitHub ]

  
# File 'lib/rbs/definition_builder/ancestor_builder.rb', line 606

def fill_ancestor_source(ancestor, name:, source:, &block)
  case ancestor
  when Definition::Ancestor::Instance
    if ancestor.name == name && !ancestor.source
      Definition::Ancestor::Instance.new(name: ancestor.name, args: ancestor.args, source: source)
    else
      ancestor
    end
  else
    ancestor
  end
end

#instance_ancestors(type_name, building_ancestors: [])

[ GitHub ]

  
# File 'lib/rbs/definition_builder/ancestor_builder.rb', line 434

def instance_ancestors(type_name, building_ancestors: [])
  as = instance_ancestors_cache[type_name] and return as

  entry = env.class_decls[type_name] or raise "Unknown name for instance_ancestors: #{type_name}"
  params = entry.type_params.each.map(&:name)
  args = entry.type_params.map do |type_param|
    Types::Variable.new(name: type_param.name, location: type_param.location)
  end
  self_ancestor = Definition::Ancestor::Instance.new(name: type_name, args: args, source: nil)

  RecursiveAncestorError.check!(self_ancestor,
                                ancestors: building_ancestors,
                                location: entry.primary.decl.location)
  building_ancestors.push self_ancestor

  one_ancestors = one_instance_ancestors(type_name)

  # @type var ancestors: Array[::RBS::Definition::Ancestor::t]
  ancestors = []

  case entry
  when Environment::ClassEntry
    if super_class = one_ancestors.super_class
      # @type var super_class: Definition::Ancestor::Instance
      super_name = super_class.name
      super_args = super_class.args

      super_ancestors =
        instance_ancestors(super_name, building_ancestors: building_ancestors)
          .apply(super_args, env: env, location: entry.primary.decl.super_class&.location)
      super_ancestors.map! {|ancestor| fill_ancestor_source(ancestor, name: super_name, source: :super) }
      ancestors.unshift(*super_ancestors)
    end
  end

  if self_types = one_ancestors.self_types
    self_types.each do |mod|
      if mod.name.class?
        # Ensure there is no loop in ancestors chain
        instance_ancestors(mod.name, building_ancestors: building_ancestors)
      end
    end
  end

  if included_modules = one_ancestors.included_modules
    included_modules.each do |mod|
      name = mod.name
      arg_types = mod.args
      mod.source.is_a?(AST::Members::Include) or raise
      mod_ancestors =
        instance_ancestors(name, building_ancestors: building_ancestors)
          .apply(arg_types, env: env, location: mod.source.location)
      mod_ancestors.map! {|ancestor| fill_ancestor_source(ancestor, name: name, source: mod.source) }
      ancestors.unshift(*mod_ancestors)
    end
  end

  ancestors.unshift(self_ancestor)

  if prepended_modules = one_ancestors.prepended_modules
    prepended_modules.each do |mod|
      name = mod.name
      arg_types = mod.args
      mod.source.is_a?(AST::Members::Prepend) or raise
      mod_ancestors =
        instance_ancestors(name, building_ancestors: building_ancestors)
          .apply(arg_types, env: env, location: mod.source.location)
      mod_ancestors.map! {|ancestor| fill_ancestor_source(ancestor, name: name, source: mod.source) }
      ancestors.unshift(*mod_ancestors)
    end
  end

  building_ancestors.pop

  instance_ancestors_cache[type_name] = Definition::InstanceAncestors.new(
    type_name: type_name,
    params: params,
    ancestors: ancestors
  )
end

#interface_ancestors(type_name, building_ancestors: [])

[ GitHub ]

  
# File 'lib/rbs/definition_builder/ancestor_builder.rb', line 570

def interface_ancestors(type_name, building_ancestors: [])
  as = interface_ancestors_cache[type_name] and return as

  entry = env.interface_decls[type_name] or raise "Unknown name for interface_ancestors: #{type_name}"
  params = entry.decl.type_params.each.map(&:name)
  args = Types::Variable.build(params)
  self_ancestor = Definition::Ancestor::Instance.new(name: type_name, args: args, source: nil)

  RecursiveAncestorError.check!(self_ancestor,
                                ancestors: building_ancestors,
                                location: entry.decl.location)
  building_ancestors.push self_ancestor

  one_ancestors = one_interface_ancestors(type_name)
  ancestors = []

  included_interfaces = one_ancestors.included_interfaces or raise
  included_interfaces.each do |a|
    a.source.is_a?(AST::Members::Include) or raise
    included_ancestors =
      interface_ancestors(a.name, building_ancestors: building_ancestors)
        .apply(a.args, env: env, location: a.source.location)
    included_ancestors.map! {|ancestor| fill_ancestor_source(ancestor, name: a.name, source: a.source) }
    ancestors.unshift(*included_ancestors)
  end

  ancestors.unshift(self_ancestor)
  building_ancestors.pop

  interface_ancestors_cache[type_name] = Definition::InstanceAncestors.new(
    type_name: type_name,
    params: params,
    ancestors: ancestors
  )
end

#mixin_ancestors(entry, type_name, included_modules:, included_interfaces:, extended_modules:, prepended_modules:, extended_interfaces:)

[ GitHub ]

  
# File 'lib/rbs/definition_builder/ancestor_builder.rb', line 414

def mixin_ancestors(entry, type_name, included_modules:, included_interfaces:, extended_modules:, prepended_modules:, extended_interfaces:)
  entry.decls.each do |d|
    decl = d.decl

    align_params = Substitution.build(
      decl.type_params.each.map(&:name),
      entry.type_params.map {|param| Types::Variable.new(name: param.name, location: param.location) }
    )

    mixin_ancestors0(decl,
                     type_name,
                     align_params: align_params,
                     included_modules: included_modules,
                     included_interfaces: included_interfaces,
                     extended_modules: extended_modules,
                     prepended_modules: prepended_modules,
                     extended_interfaces: extended_interfaces)
  end
end

#mixin_ancestors0(decl, type_name, align_params:, included_modules:, included_interfaces:, extended_modules:, prepended_modules:, extended_interfaces:)

[ GitHub ]

  
# File 'lib/rbs/definition_builder/ancestor_builder.rb', line 348

def mixin_ancestors0(decl, type_name, align_params:, included_modules:, included_interfaces:, extended_modules:, prepended_modules:, extended_interfaces:)
  decl.each_mixin do |member|
    case member
    when AST::Members::Include
      module_name = member.name
      module_args = member.args.map {|type| align_params ? type.sub(align_params) : type }

      case
      when member.name.class? && included_modules
        MixinClassError.check!(type_name: type_name, env: env, member: member)
        NoMixinFoundError.check!(member.name, env: env, member: member)

        module_decl = env.normalized_module_entry(module_name) or raise
        module_args = AST::TypeParam.normalize_args(module_decl.type_params, module_args)

        module_name = env.normalize_module_name(module_name)
        included_modules << Definition::Ancestor::Instance.new(name: module_name, args: module_args, source: member)
      when member.name.interface? && included_interfaces
        NoMixinFoundError.check!(member.name, env: env, member: member)

        interface_decl = env.interface_decls.fetch(module_name)
        module_args = AST::TypeParam.normalize_args(interface_decl.decl.type_params, module_args)

        included_interfaces << Definition::Ancestor::Instance.new(name: module_name, args: module_args, source: member)
      end

    when AST::Members::Prepend
      if prepended_modules
        MixinClassError.check!(type_name: type_name, env: env, member: member)
        NoMixinFoundError.check!(member.name, env: env, member: member)

        module_decl = env.normalized_module_entry(member.name) or raise
        module_name = module_decl.name

        module_args = member.args.map {|type| align_params ? type.sub(align_params) : type }
        module_args = AST::TypeParam.normalize_args(module_decl.type_params, module_args)

        prepended_modules << Definition::Ancestor::Instance.new(name: module_name, args: module_args, source: member)
      end

    when AST::Members::Extend
      module_name = member.name
      module_args = member.args

      case
      when member.name.class? && extended_modules
        MixinClassError.check!(type_name: type_name, env: env, member: member)
        NoMixinFoundError.check!(member.name, env: env, member: member)

        module_decl = env.normalized_module_entry(module_name) or raise
        module_args = AST::TypeParam.normalize_args(module_decl.type_params, module_args)

        module_name = env.normalize_module_name(module_name)
        extended_modules << Definition::Ancestor::Instance.new(name: module_name, args: module_args, source: member)
      when member.name.interface? && extended_interfaces
        NoMixinFoundError.check!(member.name, env: env, member: member)

        interface_decl = env.interface_decls.fetch(module_name)
        module_args = AST::TypeParam.normalize_args(interface_decl.decl.type_params, module_args)

        extended_interfaces << Definition::Ancestor::Instance.new(name: module_name, args: module_args, source: member)
      end
    end
  end
end

#one_instance_ancestors(type_name)

[ GitHub ]

  
# File 'lib/rbs/definition_builder/ancestor_builder.rb', line 192

def one_instance_ancestors(type_name)
  type_name = env.normalize_module_name(type_name)

  as = one_instance_ancestors_cache[type_name] and return as

  entry = env.class_decls[type_name] or raise "Unknown name for one_instance_ancestors: #{type_name}"
  params = entry.type_params.each.map(&:name)

  case entry
  when Environment::ClassEntry
    validate_super_class!(type_name, entry)
    primary = entry.primary
    super_class = primary.decl.super_class

    if type_name != BuiltinNames::BasicObject.name
      if super_class
        super_name = super_class.name
        super_args = super_class.args
      else
        super_name = BuiltinNames::Object.name
        super_args = []
      end

      super_name = env.normalize_module_name(super_name)

      NoSuperclassFoundError.check!(super_name, env: env, location: primary.decl.location)
      if super_class
        InheritModuleError.check!(super_class, env: env)
      end

      super_entry = env.normalized_class_entry(super_name) or raise
      super_args = AST::TypeParam.normalize_args(super_entry.type_params, super_args)

      ancestors = OneAncestors.class_instance(
        type_name: type_name,
        params: params,
        super_class: Definition::Ancestor::Instance.new(name: super_name, args: super_args, source: :super)
      )
    else
      ancestors = OneAncestors.class_instance(
        type_name: type_name,
        params: params,
        super_class: nil
      )
    end
  when Environment::ModuleEntry
    ancestors = OneAncestors.module_instance(type_name: type_name, params: params)

    self_types = ancestors.self_types or raise
    if entry.self_types.empty?
      self_types.push Definition::Ancestor::Instance.new(name: BuiltinNames::Object.name, args: [], source: nil)
    else
      entry.self_types.each do |module_self|
        NoSelfTypeFoundError.check!(module_self, env: env)

        module_name = module_self.name
        if module_name.class?
          module_entry = env.normalized_module_class_entry(module_name) or raise
          module_name = module_entry.name
          self_args = AST::TypeParam.normalize_args(module_entry.type_params, module_self.args)
        end
        if module_name.interface?
          interface_entry = env.interface_decls.fetch(module_name)
          self_args = AST::TypeParam.normalize_args(interface_entry.decl.type_params, module_self.args)
        end
        self_args or raise

        self_types.push Definition::Ancestor::Instance.new(name: module_name, args: self_args, source: module_self)
      end
    end
  end

  mixin_ancestors(entry,
                  type_name,
                  included_modules: ancestors.included_modules,
                  included_interfaces: ancestors.included_interfaces,
                  prepended_modules: ancestors.prepended_modules,
                  extended_modules: nil,
                  extended_interfaces: nil)

  one_instance_ancestors_cache[type_name] = ancestors
end

#one_interface_ancestors(type_name)

[ GitHub ]

  
# File 'lib/rbs/definition_builder/ancestor_builder.rb', line 329

def one_interface_ancestors(type_name)
  one_interface_ancestors_cache[type_name] ||=
    begin
      entry = env.interface_decls[type_name] or raise "Unknown name for one_interface_ancestors: #{type_name}"
      params = entry.decl.type_params.each.map(&:name)

      OneAncestors.interface(type_name: type_name, params: params).tap do |ancestors|
        mixin_ancestors0(entry.decl,
                         type_name,
                         align_params: nil,
                         included_modules: nil,
                         included_interfaces: ancestors.included_interfaces,
                         prepended_modules: nil,
                         extended_modules: nil,
                         extended_interfaces: nil)
      end
    end
end

#one_singleton_ancestors(type_name)

[ GitHub ]

  
# File 'lib/rbs/definition_builder/ancestor_builder.rb', line 275

def one_singleton_ancestors(type_name)
  type_name = env.normalize_module_name(type_name)
  as = one_singleton_ancestors_cache[type_name] and return as

  entry = env.class_decls[type_name] or raise "Unknown name for one_singleton_ancestors: #{type_name}"

  case entry
  when Environment::ClassEntry
    validate_super_class!(type_name, entry)
    primary = entry.primary
    super_class = primary.decl.super_class

    if type_name != BuiltinNames::BasicObject.name
      if super_class
        super_name = super_class.name
      else
        super_name = BuiltinNames::Object.name
      end

      super_name = env.normalize_module_name(super_name)

      NoSuperclassFoundError.check!(super_name, env: env, location: primary.decl.location)
      if super_class
        InheritModuleError.check!(super_class, env: env)
      end

      ancestors = OneAncestors.singleton(
        type_name: type_name,
        super_class: Definition::Ancestor::Singleton.new(name: super_name)
      )
    else
      ancestors = OneAncestors.singleton(
        type_name: type_name,
        super_class: Definition::Ancestor::Instance.new(name: BuiltinNames::Class.name, args: [], source: :super)
      )
    end
  when Environment::ModuleEntry
    ancestors = OneAncestors.singleton(
      type_name: type_name,
      super_class: Definition::Ancestor::Instance.new(name: BuiltinNames::Module.name, args: [], source: :super)
    )
  end

  mixin_ancestors(entry,
                  type_name,
                  included_modules: nil,
                  included_interfaces: nil,
                  prepended_modules: nil,
                  extended_modules: ancestors.extended_modules,
                  extended_interfaces: ancestors.extended_interfaces)

  one_singleton_ancestors_cache[type_name] = ancestors
end

#singleton_ancestors(type_name, building_ancestors: [])

[ GitHub ]

  
# File 'lib/rbs/definition_builder/ancestor_builder.rb', line 515

def singleton_ancestors(type_name, building_ancestors: [])
  as = singleton_ancestors_cache[type_name] and return as

  entry = env.class_decls[type_name] or raise "Unknown name for singleton_ancestors: #{type_name}"
  self_ancestor = Definition::Ancestor::Singleton.new(name: type_name)

  RecursiveAncestorError.check!(self_ancestor,
                                ancestors: building_ancestors,
                                location: entry.primary.decl.location)
  building_ancestors.push self_ancestor

  one_ancestors = one_singleton_ancestors(type_name)

  ancestors = []

  case super_class = one_ancestors.super_class
  when Definition::Ancestor::Instance
    super_name = super_class.name
    super_args = super_class.args

    super_ancestors =
      instance_ancestors(super_name, building_ancestors: building_ancestors)
        .apply(super_args, env: env, location: nil)
    super_ancestors.map! {|ancestor| fill_ancestor_source(ancestor, name: super_name, source: :super) }
    ancestors.unshift(*super_ancestors)

  when Definition::Ancestor::Singleton
    super_name = super_class.name

    super_ancestors = singleton_ancestors(super_name, building_ancestors: [])
    ancestors.unshift(*super_ancestors.ancestors)
  end

  extended_modules = one_ancestors.extended_modules or raise
  extended_modules.each do |mod|
    name = mod.name
    args = mod.args
    mod.source.is_a?(AST::Members::Extend) or raise
    mod_ancestors =
      instance_ancestors(name, building_ancestors: building_ancestors)
        .apply(args, env: env, location: mod.source.location)
    mod_ancestors.map! {|ancestor| fill_ancestor_source(ancestor, name: name, source: mod.source) }
    ancestors.unshift(*mod_ancestors)
  end

  ancestors.unshift(self_ancestor)

  building_ancestors.pop

  singleton_ancestors_cache[type_name] = Definition::SingletonAncestors.new(
    type_name: type_name,
    ancestors: ancestors
  )
end

#validate_super_class!(type_name, entry)

[ GitHub ]

  
# File 'lib/rbs/definition_builder/ancestor_builder.rb', line 175

def validate_super_class!(type_name, entry)
  with_super_classes = entry.decls.select {|d| d.decl.super_class }

  return if with_super_classes.size <= 1

  super_types = with_super_classes.map do |d|
    super_class = d.decl.super_class or raise
    Types::ClassInstance.new(name: super_class.name, args: super_class.args, location: nil)
  end

  super_types.uniq!

  return if super_types.size == 1

  raise SuperclassMismatchError.new(name: type_name, entry: entry)
end