123456789_123456789_123456789_123456789_123456789_

Class: RBS::Environment

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.newEnvironment

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 145

def initialize
  @signatures = {}
  @declarations = []

  @class_decls = {}
  @interface_decls = {}
  @type_alias_decls = {}
  @constant_decls = {}
  @global_decls = {}
  @class_alias_decls = {}
  @normalize_module_name_cache = {}
end

Class Method Details

.from_loader(loader)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 170

def self.from_loader(loader)
  self.new.tap do |env|
    loader.load(env: env)
  end
end

Instance Attribute Details

#class_alias_decls (readonly)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 12

attr_reader :class_alias_decls

#class_decls (readonly)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 7

attr_reader :class_decls

#constant_decls (readonly)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 10

attr_reader :constant_decls

#declarations (readonly)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 5

attr_reader :declarations

#global_decls (readonly)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 11

attr_reader :global_decls

#interface_decls (readonly)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 8

attr_reader :interface_decls

#signatures (readonly)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 14

attr_reader :signatures

#type_alias_decls (readonly)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 9

attr_reader :type_alias_decls

Instance Method Details

#<<(decl)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 470

def <<(decl)
  declarations << decl
  insert_decl(decl, outer: [], namespace: Namespace.root)
  self
end

#absolute_type(resolver, map, type, context:)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 792

def absolute_type(resolver, map, type, context:)
  type.map_type_name do |name, _, _|
    absolute_type_name(resolver, map, name, context: context)
  end
end

#absolute_type_name(resolver, map, type_name, context:)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 787

def absolute_type_name(resolver, map, type_name, context:)
  type_name = map.resolve(type_name)
  resolver.resolve(type_name, context: context) || type_name
end

#add_signature(buffer:, directives:, decls:)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 476

def add_signature(buffer:, directives:, decls:)
  signatures[buffer] = [directives, decls]
  decls.each do |decl|
    self << decl
  end
end

#append_context(context, decl)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 528

def append_context(context, decl)
  if (_, last = context)
    last or raise
    [context, last + decl.name]
  else
    [nil, decl.name.absolute!]
  end
end

#buffers

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 803

def buffers
  signatures.keys
end

#class_alias?(name) ⇒ Boolean

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 218

def class_alias?(name)
  if decl = class_alias_decls[name]
    decl.decl.is_a?(AST::Declarations::ClassAlias)
  else
    false
  end
end

#class_decl?(name) ⇒ Boolean

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 202

def class_decl?(name)
  class_decls[name].is_a?(ClassEntry)
end

#class_entry(type_name)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 226

def class_entry(type_name)
  case
  when (class_entry = class_decls[type_name]).is_a?(ClassEntry)
    class_entry
  when (class_alias = class_alias_decls[type_name]).is_a?(ClassAliasEntry)
    class_alias
  end
end

#constant_decl?(name) ⇒ Boolean

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 198

def constant_decl?(name)
  constant_decls.key?(name)
end

#constant_entry(type_name)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 274

def constant_entry(type_name)
  class_entry(type_name) || module_entry(type_name) || constant_decls[type_name]
end

#constant_name?(name) ⇒ Boolean

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 194

def constant_name?(name)
  constant_decl?(name) || module_name?(name)
end

#initialize_copy(other)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 158

def initialize_copy(other)
  @signatures = other.signatures.dup
  @declarations = other.declarations.dup

  @class_decls = other.class_decls.dup
  @interface_decls = other.interface_decls.dup
  @type_alias_decls = other.type_alias_decls.dup
  @constant_decls = other.constant_decls.dup
  @global_decls = other.global_decls.dup
  @class_alias_decls = other.class_alias_decls.dup
end

#insert_decl(decl, outer:, namespace:)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 373

def insert_decl(decl, outer:, namespace:)
  case decl
  when AST::Declarations::Class, AST::Declarations::Module
    name = decl.name.with_prefix(namespace)

    if cdecl = constant_entry(name)
      if cdecl.is_a?(ConstantEntry) || cdecl.is_a?(ModuleAliasEntry) || cdecl.is_a?(ClassAliasEntry)
        raise DuplicatedDeclarationError.new(name, decl, cdecl.decl)
      end
    end

    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

    existing_entry = class_decls[name]

    case
    when decl.is_a?(AST::Declarations::Module) && existing_entry.is_a?(ModuleEntry)
      existing_entry.insert(decl: decl, outer: outer)
    when decl.is_a?(AST::Declarations::Class) && existing_entry.is_a?(ClassEntry)
      existing_entry.insert(decl: decl, outer: outer)
    else
      raise DuplicatedDeclarationError.new(name, decl, existing_entry.decls[0].decl)
    end

    prefix = outer + [decl]
    ns = name.to_namespace
    decl.each_decl do |d|
      insert_decl(d, outer: prefix, namespace: ns)
    end

  when AST::Declarations::Interface
    name = decl.name.with_prefix(namespace)

    if interface_entry = interface_decls[name]
      raise DuplicatedDeclarationError.new(name, decl, interface_entry.decl)
    end

    interface_decls[name] = InterfaceEntry.new(name: name, decl: decl, outer: outer)

  when AST::Declarations::TypeAlias
    name = decl.name.with_prefix(namespace)

    if entry = type_alias_decls[name]
      raise DuplicatedDeclarationError.new(name, decl, entry.decl)
    end

    type_alias_decls[name] = TypeAliasEntry.new(name: name, decl: decl, outer: outer)

  when AST::Declarations::Constant
    name = decl.name.with_prefix(namespace)

    if entry = constant_entry(name)
      case entry
      when ClassAliasEntry, ModuleAliasEntry, ConstantEntry
        raise DuplicatedDeclarationError.new(name, decl, entry.decl)
      when ClassEntry, ModuleEntry
        raise DuplicatedDeclarationError.new(name, decl, *entry.decls.map(&:decl))
      end
    end

    constant_decls[name] = ConstantEntry.new(name: name, decl: decl, outer: outer)

  when AST::Declarations::Global
    if entry = global_decls[decl.name]
      raise DuplicatedDeclarationError.new(decl.name, decl, entry.decl)
    end

    global_decls[decl.name] = GlobalEntry.new(name: decl.name, decl: decl, outer: outer)

  when AST::Declarations::ClassAlias, AST::Declarations::ModuleAlias
    name = decl.new_name.with_prefix(namespace)

    if entry = constant_entry(name)
      case entry
      when ClassAliasEntry, ModuleAliasEntry, ConstantEntry
        raise DuplicatedDeclarationError.new(name, decl, entry.decl)
      when ClassEntry, ModuleEntry
        raise DuplicatedDeclarationError.new(name, decl, *entry.decls.map(&:decl))
      end
    end

    case decl
    when AST::Declarations::ClassAlias
      class_alias_decls[name] = ClassAliasEntry.new(name: name, decl: decl, outer: outer)
    when AST::Declarations::ModuleAlias
      class_alias_decls[name] = ModuleAliasEntry.new(name: name, decl: decl, outer: outer)
    end
  end
end

#inspect

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 798

def inspect
  ivars = %i[@declarations @class_decls @class_alias_decls @interface_decls @type_alias_decls @constant_decls @global_decls]
  "\#<RBS::Environment #{ivars.map { |iv| "#{iv}=(#{instance_variable_get(iv).size} items)"}.join(' ')}>"
end

#interface_name?(name) ⇒ Boolean

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 176

def interface_name?(name)
  interface_decls.key?(name)
end

#module_alias?(name) ⇒ Boolean

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 210

def module_alias?(name)
  if decl = class_alias_decls[name]
    decl.decl.is_a?(AST::Declarations::ModuleAlias)
  else
    false
  end
end

#module_class_entry(type_name)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 266

def module_class_entry(type_name)
  class_entry(type_name) || module_entry(type_name)
end

#module_decl?(name) ⇒ Boolean

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 206

def module_decl?(name)
  class_decls[name].is_a?(ModuleEntry)
end

#module_entry(type_name)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 235

def module_entry(type_name)
  case
  when (module_entry = class_decls[type_name]).is_a?(ModuleEntry)
    module_entry
  when (module_alias = class_alias_decls[type_name]).is_a?(ModuleAliasEntry)
    module_alias
  end
end

#module_name?(name) ⇒ Boolean

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 184

def module_name?(name)
  class_decls.key?(name) || class_alias_decls.key?(name)
end

#normalize_module_name(name)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 332

def normalize_module_name(name)
  normalize_module_name?(name) or name
end

#normalize_module_name?(name) ⇒ Boolean

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 336

def normalize_module_name?(name)
  raise "Class/module name is expected: #{name}" unless name.class?
  name = name.absolute! unless name.absolute?

  if @normalize_module_name_cache.key?(name)
    return @normalize_module_name_cache[name]
  end

  unless name.namespace.empty?
    parent = name.namespace.to_type_name
    if normalized_parent = normalize_module_name?(parent)
      type_name = TypeName.new(namespace: normalized_parent.to_namespace, name: name.name)
    else
      @normalize_module_name_cache[name] = nil
      return
    end
  else
    type_name = name
  end

  @normalize_module_name_cache[name] = false

  entry = constant_entry(type_name)

  normalized_type_name =
    case entry
    when ClassEntry, ModuleEntry
      type_name
    when ClassAliasEntry, ModuleAliasEntry
      normalize_module_name?(entry.decl.old_name)
    else
      nil
    end

  @normalize_module_name_cache[name] = normalized_type_name
end

#normalize_type_name(name)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 328

def normalize_type_name(name)
  normalize_type_name?(name) || name
end

#normalize_type_name!(name)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 297

def normalize_type_name!(name)
  result = normalize_type_name?(name)

  case result
  when TypeName
    result
  when false
    raise "Type name `#{name}` cannot be normalized because it's a cyclic definition"
  when nil
    raise "Type name `#{name}` cannot be normalized because of unknown type name in the path"
  end
end

#normalize_type_name?(name) ⇒ Boolean

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 278

def normalize_type_name?(name)
  return normalize_module_name?(name) if name.class?

  type_name =
    unless name.namespace.empty?
      parent = name.namespace.to_type_name
      parent = normalize_module_name?(parent)
      return parent unless parent

      TypeName.new(namespace: parent.to_namespace, name: name.name)
    else
      name
    end

  if type_name?(type_name)
    type_name
  end
end

#normalized_class_entry(type_name)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 244

def normalized_class_entry(type_name)
  if name = normalize_module_name?(type_name)
    case entry = class_entry(name)
    when ClassEntry, nil
      entry
    when ClassAliasEntry
      raise
    end
  end
end

#normalized_module_class_entry(type_name)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 270

def normalized_module_class_entry(type_name)
  normalized_class_entry(type_name) || normalized_module_entry(type_name)
end

#normalized_module_entry(type_name)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 255

def normalized_module_entry(type_name)
  if name = normalize_module_name?(type_name)
    case entry = module_entry(name)
    when ModuleEntry, nil
      entry
    when ModuleAliasEntry
      raise
    end
  end
end

#normalized_type_name!(name)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 323

def normalized_type_name!(name)
  normalized_type_name?(name) or raise "Normalized type name is expected but given `#{name}`, which is normalized to `#{normalize_type_name?(name)}`"
  name
end

#normalized_type_name?(type_name) ⇒ Boolean

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 310

def normalized_type_name?(type_name)
  case
  when type_name.interface?
    interface_decls.key?(type_name)
  when type_name.class?
    class_decls.key?(type_name)
  when type_name.alias?
    type_alias_decls.key?(type_name)
  else
    false
  end
end

#resolve_declaration(resolver, map, decl, outer:, prefix:)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 537

def resolve_declaration(resolver, map, decl, outer:, prefix:)
  if decl.is_a?(AST::Declarations::Global)
    # @type var decl: AST::Declarations::Global
    return AST::Declarations::Global.new(
      name: decl.name,
      type: absolute_type(resolver, map, decl.type, context: nil),
      location: decl.location,
      comment: decl.comment
    )
  end

  context = resolver_context(*outer)

  case decl
  when AST::Declarations::Class
    outer_context = context
    inner_context = append_context(outer_context, decl)

    outer_ = outer + [decl]
    prefix_ = prefix + decl.name.to_namespace
    AST::Declarations::Class.new(
      name: decl.name.with_prefix(prefix),
      type_params: resolve_type_params(resolver, map, decl.type_params, context: inner_context),
      super_class: decl.super_class&.yield_self do |super_class|
        AST::Declarations::Class::Super.new(
          name: absolute_type_name(resolver, map, super_class.name, context: outer_context),
          args: super_class.args.map {|type| absolute_type(resolver, map, type, context: outer_context) },
          location: super_class.location
        )
      end,
      members: decl.members.map do |member|
        case member
        when AST::Members::Base
          resolve_member(resolver, map, member, context: inner_context)
        when AST::Declarations::Base
          resolve_declaration(
            resolver,
            map,
            member,
            outer: outer_,
            prefix: prefix_
          )
        else
          raise
        end
      end,
      location: decl.location,
      annotations: decl.annotations,
      comment: decl.comment
    )

  when AST::Declarations::Module
    outer_context = context
    inner_context = append_context(outer_context, decl)

    outer_ = outer + [decl]
    prefix_ = prefix + decl.name.to_namespace
    AST::Declarations::Module.new(
      name: decl.name.with_prefix(prefix),
      type_params: resolve_type_params(resolver, map, decl.type_params, context: inner_context),
      self_types: decl.self_types.map do |module_self|
        AST::Declarations::Module::Self.new(
          name: absolute_type_name(resolver, map, module_self.name, context: inner_context),
          args: module_self.args.map {|type| absolute_type(resolver, map, type, context: inner_context) },
          location: module_self.location
        )
      end,
      members: decl.members.map do |member|
        case member
        when AST::Members::Base
          resolve_member(resolver, map, member, context: inner_context)
        when AST::Declarations::Base
          resolve_declaration(
            resolver,
            map,
            member,
            outer: outer_,
            prefix: prefix_
          )
        else
          raise
        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: resolve_type_params(resolver, map, decl.type_params, context: context),
      members: decl.members.map do |member|
        resolve_member(resolver, map, member, context: context)
      end,
      comment: decl.comment,
      location: decl.location,
      annotations: decl.annotations
    )

  when AST::Declarations::TypeAlias
    AST::Declarations::TypeAlias.new(
      name: decl.name.with_prefix(prefix),
      type_params: resolve_type_params(resolver, map, decl.type_params, context: context),
      type: absolute_type(resolver, map, 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, map, decl.type, context: context),
      location: decl.location,
      comment: decl.comment
    )

  when AST::Declarations::ClassAlias
    AST::Declarations::ClassAlias.new(
      new_name: decl.new_name.with_prefix(prefix),
      old_name: absolute_type_name(resolver, map, decl.old_name, context: context),
      location: decl.location,
      comment: decl.comment
    )

  when AST::Declarations::ModuleAlias
    AST::Declarations::ModuleAlias.new(
      new_name: decl.new_name.with_prefix(prefix),
      old_name: absolute_type_name(resolver, map, decl.old_name, context: context),
      location: decl.location,
      comment: decl.comment
    )
  end
end

#resolve_member(resolver, map, member, context:)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 673

def resolve_member(resolver, map, member, context:)
  case member
  when AST::Members::MethodDefinition
    AST::Members::MethodDefinition.new(
      name: member.name,
      kind: member.kind,
      overloads: member.overloads.map do |overload|
        overload.update(
          method_type: resolve_method_type(resolver, map, overload.method_type, context: context)
        )
      end,
      comment: member.comment,
      overloading: member.overloading?,
      annotations: member.annotations,
      location: member.location,
      visibility: member.visibility
    )
  when AST::Members::AttrAccessor
    AST::Members::AttrAccessor.new(
      name: member.name,
      type: absolute_type(resolver, map, member.type, context: context),
      kind: member.kind,
      annotations: member.annotations,
      comment: member.comment,
      location: member.location,
      ivar_name: member.ivar_name,
      visibility: member.visibility
    )
  when AST::Members::AttrReader
    AST::Members::AttrReader.new(
      name: member.name,
      type: absolute_type(resolver, map, member.type, context: context),
      kind: member.kind,
      annotations: member.annotations,
      comment: member.comment,
      location: member.location,
      ivar_name: member.ivar_name,
      visibility: member.visibility
    )
  when AST::Members::AttrWriter
    AST::Members::AttrWriter.new(
      name: member.name,
      type: absolute_type(resolver, map, member.type, context: context),
      kind: member.kind,
      annotations: member.annotations,
      comment: member.comment,
      location: member.location,
      ivar_name: member.ivar_name,
      visibility: member.visibility
    )
  when AST::Members::InstanceVariable
    AST::Members::InstanceVariable.new(
      name: member.name,
      type: absolute_type(resolver, map, member.type, context: context),
      comment: member.comment,
      location: member.location
    )
  when AST::Members::ClassInstanceVariable
    AST::Members::ClassInstanceVariable.new(
      name: member.name,
      type: absolute_type(resolver, map, 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, map, member.type, context: context),
      comment: member.comment,
      location: member.location
    )
  when AST::Members::Include
    AST::Members::Include.new(
      name: absolute_type_name(resolver, map, member.name, context: context),
      args: member.args.map {|type| absolute_type(resolver, map, 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, map, member.name, context: context),
      args: member.args.map {|type| absolute_type(resolver, map, 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, map, member.name, context: context),
      args: member.args.map {|type| absolute_type(resolver, map, type, context: context) },
      comment: member.comment,
      location: member.location,
      annotations: member.annotations
    )
  else
    member
  end
end

#resolve_method_type(resolver, map, type, context:)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 773

def resolve_method_type(resolver, map, type, context:)
  type.map_type do |ty|
    absolute_type(resolver, map, ty, context: context)
  end.map_type_bound do |bound|
    _ = absolute_type(resolver, map, bound, context: context)
  end
end

#resolve_type_names(only: nil)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 489

def resolve_type_names(only: nil)
  resolver = Resolver::TypeNameResolver.new(self)
  env = Environment.new

  table = UseMap::Table.new()
  table.known_types.merge(class_decls.keys)
  table.known_types.merge(class_alias_decls.keys)
  table.known_types.merge(type_alias_decls.keys)
  table.known_types.merge(interface_decls.keys)
  table.compute_children

  signatures.each do |buffer, (dirs, decls)|
    map = UseMap.new(table: table)
    dirs.each do |dir|
      dir.clauses.each do |clause|
        map.build_map(clause)
      end
    end

    decls = decls.map do |decl|
      if only && !only.member?(decl)
        decl
      else
        resolve_declaration(resolver, map, decl, outer: [], prefix: Namespace.root)
      end
    end

    env.add_signature(buffer: buffer, directives: dirs, decls: decls)
  end

  env
end

#resolve_type_params(resolver, map, params, context:)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 781

def resolve_type_params(resolver, map, params, context:)
  params.map do |param|
    param.map_type {|type| _ = absolute_type(resolver, map, type, context: context) }
  end
end

#resolver_context(*nesting)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 522

def resolver_context(*nesting)
  nesting.inject(nil) {|context, decl| #$ Resolver::context
    append_context(context, decl)
  }
end

#type_alias_name?(name) ⇒ Boolean

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 180

def type_alias_name?(name)
  type_alias_decls.key?(name)
end

#type_name?(name) ⇒ Boolean

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 188

def type_name?(name)
  interface_name?(name) ||
    type_alias_name?(name) ||
    module_name?(name)
end

#unload(buffers)

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 807

def unload(buffers)
  env = Environment.new

  signatures.each do |buf, (dirs, decls)|
    next if buffers.include?(buf)
    env.add_signature(buffer: buf, directives: dirs, decls: decls)
  end

  env
end

#validate_type_params

[ GitHub ]

  
# File 'lib/rbs/environment.rb', line 483

def validate_type_params
  class_decls.each_value do |decl|
    decl.primary
  end
end