123456789_123456789_123456789_123456789_123456789_

Class: RBS::DefinitionBuilder

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(env:, ancestor_builder: nil, method_builder: nil) ⇒ DefinitionBuilder

[ GitHub ]

  
# File 'lib/rbs/definition_builder.rb', line 15

def initialize(env:, ancestor_builder: nil, method_builder: nil)
  @env = env
  @type_name_resolver = TypeNameResolver.from_env(env)
  @ancestor_builder = ancestor_builder || AncestorBuilder.new(env: env)
  @method_builder = method_builder || MethodBuilder.new(env: env)

  @instance_cache = {}
  @singleton_cache = {}
  @singleton0_cache = {}
  @interface_cache = {}
end

Instance Attribute Details

#ancestor_builder (readonly)

[ GitHub ]

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

attr_reader :ancestor_builder

#env (readonly)

[ GitHub ]

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

attr_reader :env

#instance_cache (readonly)

[ GitHub ]

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

attr_reader :instance_cache

#interface_cache (readonly)

[ GitHub ]

  
# File 'lib/rbs/definition_builder.rb', line 13

attr_reader :interface_cache

#method_builder (readonly)

[ GitHub ]

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

attr_reader :method_builder

#singleton0_cache (readonly)

[ GitHub ]

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

attr_reader :singleton0_cache

#singleton_cache (readonly)

[ GitHub ]

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

attr_reader :singleton_cache

#type_name_resolver (readonly)

[ GitHub ]

  
# File 'lib/rbs/definition_builder.rb', line 6

attr_reader :type_name_resolver

Instance Method Details

#build_instance(type_name, no_self_types: false)

[ GitHub ]

  
# File 'lib/rbs/definition_builder.rb', line 137

def build_instance(type_name, no_self_types: false)
  try_cache(type_name, cache: instance_cache, key: [type_name, no_self_types]) do
    entry = env.class_decls[type_name] or raise "Unknown name for build_instance: #{type_name}"
    ensure_namespace!(type_name.namespace, location: entry.decls[0].decl.location)

    case entry
    when Environment::ClassEntry, Environment::ModuleEntry
      ancestors = ancestor_builder.instance_ancestors(type_name)
      args = entry.type_params.map {|param| Types::Variable.new(name: param.name, location: param.location) }
      self_type = Types::ClassInstance.new(name: type_name, args: args, location: nil)

      Definition.new(type_name: type_name, entry: entry, self_type: self_type, ancestors: ancestors).tap do |definition|
        one_ancestors = ancestor_builder.one_instance_ancestors(type_name)
        methods = method_builder.build_instance(type_name)

        validate_type_params definition, methods: methods, ancestors: one_ancestors

        if super_class = one_ancestors.super_class
          case super_class
          when Definition::Ancestor::Instance
            build_instance(super_class.name).yield_self do |defn|
              merge_definition(src: defn,
                              dest: definition,
                              subst: Substitution.build(defn.type_params, super_class.args),
                              keep_super: true)
            end
          else
            raise
          end
        end

        if self_types = one_ancestors.self_types
          unless no_self_types
            self_types.each do |ans|
              defn = if ans.name.interface?
                       build_interface(ans.name)
                     else
                       build_instance(ans.name)
                     end

              # Successor interface method overwrites.
              merge_definition(
                src: defn,
                dest: definition,
                subst: Substitution.build(defn.type_params, ans.args),
                keep_super: true
              )
            end
          else
            methods_with_self = build_instance(type_name, no_self_types: false).methods
          end
        end

        one_ancestors.each_included_module do |mod|
          defn = build_instance(mod.name, no_self_types: true)
          merge_definition(src: defn,
                           dest: definition,
                           subst: Substitution.build(defn.type_params, mod.args))
        end

        interface_methods = {}

        one_ancestors.each_included_interface do |mod|
          defn = build_interface(mod.name)
          subst = Substitution.build(defn.type_params, mod.args)

          defn.methods.each do |name, method|
            if interface_methods.key?(name)
              include_member = mod.source

              raise unless include_member.is_a?(AST::Members::Include)

              raise DuplicatedInterfaceMethodDefinitionError.new(
                type: self_type,
                method_name: name,
                member: include_member
              )
            end

            merge_method(type_name, interface_methods, name, method, subst, implemented_in: type_name)
          end
        end

        if entry.is_a?(Environment::ModuleEntry)
          define_methods_module_instance(
            definition,
            methods: methods,
            interface_methods: interface_methods,
            module_self_methods: methods_with_self
          )
        else
          define_methods_instance(definition, methods: methods, interface_methods: interface_methods)
        end

        entry.decls.each do |d|
          subst = Substitution.build(d.decl.type_params.each.map(&:name), args)

          d.decl.members.each do |member|
            case member
            when AST::Members::AttrReader, AST::Members::AttrAccessor, AST::Members::AttrWriter
              if member.kind == :instance
                ivar_name = case member.ivar_name
                            when false
                              nil
                            else
                              member.ivar_name || :"@#{member.name}"
                            end

                if ivar_name
                  insert_variable(type_name,
                                  definition.instance_variables,
                                  name: ivar_name,
                                  type: member.type.sub(subst))
                end
              end

            when AST::Members::InstanceVariable
              insert_variable(type_name,
                              definition.instance_variables,
                              name: member.name,
                              type: member.type.sub(subst))

            when AST::Members::ClassVariable
              insert_variable(type_name, definition.class_variables, name: member.name, type: member.type)
            end
          end
        end

        one_ancestors.each_prepended_module do |mod|
          defn = build_instance(mod.name, no_self_types: true)
          merge_definition(src: defn,
                           dest: definition,
                           subst: Substitution.build(defn.type_params, mod.args))
        end
      end
    end
  end
end

#build_interface(type_name)

[ GitHub ]

  
# File 'lib/rbs/definition_builder.rb', line 35

def build_interface(type_name)
  try_cache(type_name, cache: interface_cache) do
    entry = env.interface_decls[type_name] or raise "Unknown name for build_interface: #{type_name}"
    declaration = entry.decl
    ensure_namespace!(type_name.namespace, location: declaration.location)

    self_type = Types::Interface.new(
      name: type_name,
      args: Types::Variable.build(declaration.type_params.each.map(&:name)),
      location: nil
    )

    ancestors = ancestor_builder.interface_ancestors(type_name)
    Definition.new(type_name: type_name, entry: entry, self_type: self_type, ancestors: ancestors).tap do |definition|
      included_interfaces = ancestor_builder.one_interface_ancestors(type_name).included_interfaces or raise
      included_interfaces.each do |mod|
        defn = build_interface(mod.name)
        subst = Substitution.build(defn.type_params, mod.args)

        defn.methods.each do |name, method|
          definition.methods[name] = method.sub(subst)
        end
      end

      methods = method_builder.build_interface(type_name)
      one_ancestors = ancestor_builder.one_interface_ancestors(type_name)

      validate_type_params(definition, methods: methods, ancestors: one_ancestors)

      methods.each do |defn|
        method = case original = defn.original
                 when AST::Members::MethodDefinition
                   defs = original.types.map do |method_type|
                     Definition::Method::TypeDef.new(
                       type: method_type,
                       member: original,
                       defined_in: type_name,
                       implemented_in: nil
                     )
                   end

                   Definition::Method.new(
                     super_method: nil,
                     defs: defs,
                     accessibility: :public,
                     alias_of: nil
                   )
                 when AST::Members::Alias
                   unless definition.methods.key?(original.old_name)
                     raise UnknownMethodAliasError.new(
                       type_name: type_name,
                       original_name: original.old_name,
                       aliased_name: original.new_name,
                       location: original.location
                     )
                   end

                   original_method = definition.methods[original.old_name]
                   Definition::Method.new(
                     super_method: nil,
                     defs: original_method.defs.map do |defn|
                       defn.update(implemented_in: nil, defined_in: type_name)
                     end,
                     accessibility: :public,
                     alias_of: original_method
                   )
                 when nil
                   unless definition.methods.key?(defn.name)
                     raise InvalidOverloadMethodError.new(
                       type_name: type_name,
                       method_name: defn.name,
                       kind: :instance,
                       members: defn.overloads
                     )
                   end

                   definition.methods[defn.name]

                 when AST::Members::AttrReader, AST::Members::AttrWriter, AST::Members::AttrAccessor
                   raise

                 end

        defn.overloads.each do |overload|
          overload_defs = overload.types.map do |method_type|
            Definition::Method::TypeDef.new(
              type: method_type,
              member: overload,
              defined_in: type_name,
              implemented_in: nil
            )
          end

          method.defs.unshift(*overload_defs)
        end

        definition.methods[defn.name] = method
      end
    end
  end
end

#build_singleton(type_name)

[ GitHub ]

  
# File 'lib/rbs/definition_builder.rb', line 375

def build_singleton(type_name)
  try_cache type_name, cache: singleton_cache do
    entry = env.class_decls[type_name] or raise "Unknown name for build_singleton: #{type_name}"
    ensure_namespace!(type_name.namespace, location: entry.decls[0].decl.location)

    case entry
    when Environment::ClassEntry, Environment::ModuleEntry
      ancestors = ancestor_builder.singleton_ancestors(type_name)
      self_type = Types::ClassSingleton.new(name: type_name, location: nil)

      Definition.new(type_name: type_name, entry: entry, self_type: self_type, ancestors: ancestors).tap do |definition|
        def0 = build_singleton0(type_name)
        subst = Substitution.new

        merge_definition(src: def0, dest: definition, subst: subst, keep_super: true)

        if entry.is_a?(Environment::ClassEntry)
          new_method = definition.methods[:new]
          if new_method.defs.all? {|d| d.defined_in == BuiltinNames::Class.name }
            alias_methods = definition.methods.each.with_object([]) do |entry, array|
              # @type var method: Definition::Method?
              name, method = entry
              while method
                if method.alias_of == new_method
                  array << name
                  break
                end
                method = method.alias_of
              end
            end

            # The method is _untyped new_.

            instance = build_instance(type_name)
            initialize = instance.methods[:initialize]

            if initialize
              class_params = entry.type_params

              # Inject a virtual _typed new_.
              initialize_defs = initialize.defs
              typed_new = Definition::Method.new(
                super_method: new_method,
                defs: initialize_defs.map do |initialize_def|
                  method_type = initialize_def.type

                  class_type_param_vars = Set.new(class_params.map(&:name))
                  method_type_param_vars = Set.new(method_type.type_params.map(&:name))

                  if class_type_param_vars.intersect?(method_type_param_vars)
                    new_method_param_names = method_type.type_params.map do |method_param|
                      if class_type_param_vars.include?(method_param.name)
                        Types::Variable.fresh(method_param.name).name
                      else
                        method_param.name
                      end
                    end

                    sub = Substitution.build(
                      method_type.type_params.map(&:name),
                      Types::Variable.build(new_method_param_names)
                    )

                    method_params = class_params + AST::TypeParam.rename(method_type.type_params, new_names: new_method_param_names)
                    method_type = method_type
                      .update(type_params: [])
                      .sub(sub)
                      .update(type_params: method_params)
                  else
                    method_type = method_type
                      .update(type_params: class_params + method_type.type_params)
                  end

                  method_type = method_type.update(
                    type: method_type.type.with_return_type(
                      Types::ClassInstance.new(
                        name: type_name,
                        args: entry.type_params.map {|param| Types::Variable.new(name: param.name, location: param.location) },
                        location: nil
                      )
                    )
                  )

                  Definition::Method::TypeDef.new(
                    type: method_type,
                    member: initialize_def.member,
                    defined_in: initialize_def.defined_in,
                    implemented_in: initialize_def.implemented_in
                  )
                end,
                accessibility: :public,
                annotations: [],
                alias_of: nil
              )

              definition.methods[:new] = typed_new

              alias_methods.each do |alias_name|
                definition.methods[alias_name] = definition.methods[alias_name].update(
                  alias_of: typed_new,
                  defs: typed_new.defs
                )
              end
            end
          end
        end
      end
    end
  end
end

#build_singleton0(type_name)

Builds a definition for singleton without .new method.

[ GitHub ]

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

def build_singleton0(type_name)
  try_cache type_name, cache: singleton0_cache do
    entry = env.class_decls[type_name] or raise "Unknown name for build_singleton0: #{type_name}"
    ensure_namespace!(type_name.namespace, location: entry.decls[0].decl.location)

    case entry
    when Environment::ClassEntry, Environment::ModuleEntry
      ancestors = ancestor_builder.singleton_ancestors(type_name)
      self_type = Types::ClassSingleton.new(name: type_name, location: nil)

      Definition.new(type_name: type_name, entry: entry, self_type: self_type, ancestors: ancestors).tap do |definition|
        one_ancestors = ancestor_builder.one_singleton_ancestors(type_name)

        if super_class = one_ancestors.super_class
          case super_class
          when Definition::Ancestor::Instance
            defn = build_instance(super_class.name)
            merge_definition(src: defn,
                             dest: definition,
                             subst: Substitution.build(defn.type_params, super_class.args),
                             keep_super: true)
          when Definition::Ancestor::Singleton
            defn = build_singleton0(super_class.name)
            merge_definition(src: defn, dest: definition, subst: Substitution.new, keep_super: true)
          end
        end

        one_ancestors.each_extended_module do |mod|
          mod.args.each do |arg|
            validate_type_presence(arg)
          end

          mod_defn = build_instance(mod.name, no_self_types: true)
          merge_definition(src: mod_defn,
                           dest: definition,
                           subst: Substitution.build(mod_defn.type_params, mod.args))
        end

        interface_methods = {}
        one_ancestors.each_extended_interface do |mod|
          mod.args.each do |arg|
            validate_type_presence(arg)
          end

          mod_defn = build_interface(mod.name)
          subst = Substitution.build(mod_defn.type_params, mod.args)

          mod_defn.methods.each do |name, method|
            if interface_methods.key?(name)
              src_member = mod.source

              raise unless src_member.is_a?(AST::Members::Extend)

              raise DuplicatedInterfaceMethodDefinitionError.new(
                type: self_type,
                method_name: name,
                member: src_member
              )
            end

            merge_method(type_name, interface_methods, name, method, subst, implemented_in: type_name)
          end
        end

        methods = method_builder.build_singleton(type_name)
        define_methods_singleton(definition, methods: methods, interface_methods: interface_methods)

        entry.decls.each do |d|
          d.decl.members.each do |member|
            case member
            when AST::Members::AttrReader, AST::Members::AttrAccessor, AST::Members::AttrWriter
              if member.kind == :singleton
                ivar_name = case member.ivar_name
                            when false
                              nil
                            else
                              member.ivar_name || :"@#{member.name}"
                            end

                if ivar_name
                  insert_variable(type_name, definition.instance_variables, name: ivar_name, type: member.type)
                end
              end

            when AST::Members::ClassInstanceVariable
              insert_variable(type_name, definition.instance_variables, name: member.name, type: member.type)

            when AST::Members::ClassVariable
              insert_variable(type_name, definition.class_variables, name: member.name, type: member.type)
            end
          end
        end
      end
    end
  end
end

#define_methods(definition, methods:, interface_methods:, methods_with_self:, super_interface_method:)

[ GitHub ]

  
# File 'lib/rbs/definition_builder.rb', line 624

def define_methods(definition, methods:, interface_methods:, methods_with_self:, super_interface_method:)
  methods.each do |method_def|
    method_name = method_def.name
    original = method_def.original

    if original.is_a?(AST::Members::Alias)
      existing_method = interface_methods[method_name] || definition.methods[method_name]
      original_method =
        interface_methods[original.old_name] ||
        methods_with_self&.[](original.old_name) ||
        definition.methods[original.old_name]

      unless original_method
        raise UnknownMethodAliasError.new(
          type_name: definition.type_name,
          original_name: original.old_name,
          aliased_name: original.new_name,
          location: original.location
        )
      end

      method = Definition::Method.new(
        super_method: existing_method,
        defs: original_method.defs.map do |defn|
          defn.update(defined_in: definition.type_name, implemented_in: definition.type_name)
        end,
        accessibility: original_method.accessibility,
        alias_of: original_method
      )
    else
      if interface_methods.key?(method_name)
        interface_method = interface_methods[method_name]

        if original = method_def.original
          raise DuplicatedMethodDefinitionError.new(
            type: definition.self_type,
            method_name: method_name,
            members: [original]
          )
        end

        definition.methods[method_name] = interface_method
      end

      existing_method = definition.methods[method_name]

      case original
      when AST::Members::MethodDefinition
        defs = original.types.map do |method_type|
          Definition::Method::TypeDef.new(
            type: method_type,
            member: original,
            defined_in: definition.type_name,
            implemented_in: definition.type_name
          )
        end

        # @type var accessibility: RBS::Definition::accessibility
        accessibility = if method_name == :initialize
                          :private
                        else
                          method_def.accessibility
                        end

        method = Definition::Method.new(
          super_method: existing_method,
          defs: defs,
          accessibility: accessibility,
          alias_of: nil
        )

      when AST::Members::AttrReader, AST::Members::AttrWriter, AST::Members::AttrAccessor
        method_type = if method_name.to_s.end_with?("=")
                        # setter
                        MethodType.new(
                          type_params: [],
                          type: Types::Function.empty(original.type).update(
                            required_positionals: [
                              Types::Function::Param.new(type: original.type, name: original.name)
                            ]
                          ),
                          block: nil,
                          location: nil
                        )
                      else
                        # getter
                        MethodType.new(
                          type_params: [],
                          type: Types::Function.empty(original.type),
                          block: nil,
                          location: nil
                        )
                      end
        defs = [
          Definition::Method::TypeDef.new(
            type: method_type,
            member: original,
            defined_in: definition.type_name,
            implemented_in: definition.type_name
          )
        ]

        method = Definition::Method.new(
          super_method: existing_method,
          defs: defs,
          accessibility: method_def.accessibility,
          alias_of: nil
        )

      when nil
        unless definition.methods.key?(method_name)
          raise InvalidOverloadMethodError.new(
            type_name: definition.type_name,
            method_name: method_name,
            kind: :instance,
            members: method_def.overloads
          )
        end

        if !super_interface_method && existing_method.defs.any? {|defn| defn.defined_in.interface? }
          super_method = existing_method.super_method
        else
          super_method = existing_method
        end

        method = Definition::Method.new(
          super_method: super_method,
          defs: existing_method.defs.map do |defn|
            defn.update(implemented_in: definition.type_name)
          end,
          accessibility: existing_method.accessibility,
          alias_of: existing_method.alias_of
        )
      end
    end

    method_def.overloads.each do |overload|
      type_defs = overload.types.map do |method_type|
        Definition::Method::TypeDef.new(
          type: method_type,
          member: overload,
          defined_in: definition.type_name,
          implemented_in: definition.type_name
        )
      end

      method.defs.unshift(*type_defs)
    end

    definition.methods[method_name] = method
  end

  interface_methods.each do |name, method|
    unless methods.methods.key?(name)
      merge_method(definition.type_name, definition.methods, name, method, Substitution.new)
    end
  end
end

#define_methods_instance(definition, methods:, interface_methods:)

[ GitHub ]

  
# File 'lib/rbs/definition_builder.rb', line 600

def define_methods_instance(definition, methods:, interface_methods:)
  define_methods(
    definition,
    methods: methods,
    interface_methods: interface_methods,
    methods_with_self: nil,
    super_interface_method: false
  )
end

#define_methods_module_instance(definition, methods:, interface_methods:, module_self_methods:)

[ GitHub ]

  
# File 'lib/rbs/definition_builder.rb', line 610

def define_methods_module_instance(definition, methods:, interface_methods:, module_self_methods:)
  define_methods(definition, methods: methods, interface_methods: interface_methods, methods_with_self: module_self_methods, super_interface_method: true)
end

#define_methods_singleton(definition, methods:, interface_methods:)

[ GitHub ]

  
# File 'lib/rbs/definition_builder.rb', line 614

def define_methods_singleton(definition, methods:, interface_methods:)
  define_methods(
    definition,
    methods: methods,
    interface_methods: interface_methods,
    methods_with_self: nil,
    super_interface_method: false
  )
end

#ensure_namespace!(namespace, location:)

[ GitHub ]

  
# File 'lib/rbs/definition_builder.rb', line 27

def ensure_namespace!(namespace, location:)
  namespace.ascend do |ns|
    unless ns.empty?
      NoTypeFoundError.check!(ns.to_type_name, env: env, location: location)
    end
  end
end

#expand_alias(type_name)

[ GitHub ]

  
# File 'lib/rbs/definition_builder.rb', line 863

def expand_alias(type_name)
  expand_alias2(type_name, [])
end

#expand_alias1(type_name)

[ GitHub ]

  
# File 'lib/rbs/definition_builder.rb', line 867

def expand_alias1(type_name)
  entry = env.alias_decls[type_name] or raise "Unknown alias name: #{type_name}"
  as = entry.decl.type_params.each.map { Types::Bases::Any.new(location: nil) }
  expand_alias2(type_name, as)
end

#expand_alias2(type_name, args)

[ GitHub ]

  
# File 'lib/rbs/definition_builder.rb', line 873

def expand_alias2(type_name, args)
  entry = env.alias_decls[type_name] or raise "Unknown alias name: #{type_name}"

  ensure_namespace!(type_name.namespace, location: entry.decl.location)
  params = entry.decl.type_params.each.map(&:name)

  unless params.size == args.size
    as = "[#{args.join(", ")}]" unless args.empty?
    ps = "[#{params.join(", ")}]" unless params.empty?

    raise "Invalid type application: type = #{type_name}#{as}, decl = #{type_name}#{ps}"
  end

  type = entry.decl.type

  unless params.empty?
    subst = Substitution.build(params, args)
    type = type.sub(subst)
  end

  type
end

#insert_variable(type_name, variables, name:, type:)

[ GitHub ]

  
# File 'lib/rbs/definition_builder.rb', line 592

def insert_variable(type_name, variables, name:, type:)
  variables[name] = Definition::Variable.new(
    parent_variable: variables[name],
    type: type,
    declared_in: type_name
  )
end

#merge_definition(src:, dest:, subst:, implemented_in: :keep, keep_super: false)

[ GitHub ]

  
# File 'lib/rbs/definition_builder.rb', line 783

def merge_definition(src:, dest:, subst:, implemented_in: :keep, keep_super: false)
  src.methods.each do |name, method|
    merge_method(dest.type_name, dest.methods, name, method, subst, implemented_in: implemented_in, keep_super: keep_super)
  end

  src.instance_variables.each do |name, variable|
    merge_variable(dest.instance_variables, name, variable, subst, keep_super: keep_super)
  end

  src.class_variables.each do |name, variable|
    merge_variable(dest.class_variables, name, variable, subst, keep_super: keep_super)
  end
end

#merge_method(type_name, methods, name, method, sub, implemented_in: :keep, keep_super: false)

[ GitHub ]

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

def merge_method(type_name, methods, name, method, sub, implemented_in: :keep, keep_super: false)
  if sub.empty? && implemented_in == :keep && keep_super
    methods[name] = method
  else
    if sub.empty? && implemented_in == :keep
      defs = method.defs
    else
      defs = method.defs.map do |defn|
        defn.update(
          type: sub.empty? ? defn.type : defn.type.sub(sub),
          implemented_in: case implemented_in
                          when :keep
                            defn.implemented_in
                          when nil
                            nil
                          else
                            implemented_in
                          end
        )
      end

      defs = method.defs.map do |defn|
        defn.update(
          type: sub.empty? ? defn.type : defn.type.sub(sub),
          implemented_in: case implemented_in
                          when :keep
                            defn.implemented_in
                          when nil
                            nil
                          else
                            implemented_in
                          end
        )
      end
    end

    super_method = methods[name]

    methods[name] = Definition::Method.new(
      super_method: keep_super ? method.super_method : super_method,
      accessibility: method.accessibility,
      defs: defs,
      alias_of: method.alias_of
    )
  end
end

#merge_variable(variables, name, variable, sub, keep_super: false)

[ GitHub ]

  
# File 'lib/rbs/definition_builder.rb', line 797

def merge_variable(variables, name, variable, sub, keep_super: false)
  super_variable = variables[name]

  variables[name] = Definition::Variable.new(
    parent_variable: keep_super ? variable.parent_variable : super_variable,
    type: sub.empty? ? variable.type : variable.type.sub(sub),
    declared_in: variable.declared_in
  )
end

#source_location(source, decl)

[ GitHub ]

  
# File 'lib/rbs/definition_builder.rb', line 496

def source_location(source, decl)
  case source
  when nil
    decl.location
  when :super
    case decl
    when AST::Declarations::Class
      decl.super_class&.location
    end
  else
    source.location
  end
end

#try_cache(type_name, cache:, key: nil)

[ GitHub ]

  
# File 'lib/rbs/definition_builder.rb', line 854

def try_cache(type_name, cache:, key: nil)
  # @type var cc: Hash[untyped, Definition | nil]
  # @type var key: untyped
  key ||= type_name
  cc = _ = cache

  cc[key] ||= yield
end

#update(env:, except:, ancestor_builder:)

[ GitHub ]

  
# File 'lib/rbs/definition_builder.rb', line 896

def update(env:, except:, ancestor_builder:)
  method_builder = self.method_builder.update(env: env, except: except)

  DefinitionBuilder.new(env: env, ancestor_builder: ancestor_builder, method_builder: method_builder).tap do |builder|
    builder.instance_cache.merge!(instance_cache)
    builder.singleton_cache.merge!(singleton_cache)
    builder.singleton0_cache.merge!(singleton0_cache)
    builder.interface_cache.merge!(interface_cache)

    except.each do |name|
      builder.instance_cache.delete([name, true])
      builder.instance_cache.delete([name, false])
      builder.singleton_cache.delete(name)
      builder.singleton0_cache.delete(name)
      builder.interface_cache.delete(name)
    end
  end
end

#validate_params_with(type_params, result:)

[ GitHub ]

  
# File 'lib/rbs/definition_builder.rb', line 486

def validate_params_with(type_params, result:)
  type_params.each do |param|
    unless param.unchecked?
      unless result.compatible?(param.name, with_annotation: param.variance)
        yield param
      end
    end
  end
end

#validate_type_name(name, location)

Raises:

[ GitHub ]

  
# File 'lib/rbs/definition_builder.rb', line 926

def validate_type_name(name, location)
  name = name.absolute!

  return if name.class? && env.class_decls.key?(name)
  return if name.interface? && env.interface_decls.key?(name)
  return if name.alias? && env.alias_decls.key?(name)

  raise NoTypeFoundError.new(type_name: name, location: location)
end

#validate_type_params(definition, ancestors:, methods:)

[ GitHub ]

  
# File 'lib/rbs/definition_builder.rb', line 510

def validate_type_params(definition, ancestors:, methods:)
  type_params = definition.type_params_decl

  calculator = VarianceCalculator.new(builder: self)
  param_names = type_params.each.map(&:name)

  ancestors.each_ancestor do |ancestor|
    case ancestor
    when Definition::Ancestor::Instance
      result = calculator.in_inherit(name: ancestor.name, args: ancestor.args, variables: param_names)
      validate_params_with(type_params, result: result) do |param|
        decl = case entry = definition.entry
               when Environment::ModuleEntry, Environment::ClassEntry
                 entry.primary.decl
               when Environment::SingleEntry
                 entry.decl
               end

        raise InvalidVarianceAnnotationError.new(
          type_name: definition.type_name,
          param: param,
          location: source_location(ancestor.source, decl)
        )
      end
    end
  end

  methods.each do |defn|
    next if defn.name == :initialize

    method_types = case original = defn.original
                   when AST::Members::MethodDefinition
                     original.types
                   when AST::Members::AttrWriter, AST::Members::AttrReader, AST::Members::AttrAccessor
                     if defn.name.to_s.end_with?("=")
                       [
                         MethodType.new(
                           type_params: [],
                           type: Types::Function.empty(original.type).update(
                             required_positionals: [
                               Types::Function::Param.new(type: original.type, name: original.name)
                             ]
                           ),
                           block: nil,
                           location: original.location
                         )
                       ]
                     else
                       [
                         MethodType.new(
                           type_params: [],
                           type: Types::Function.empty(original.type),
                           block: nil,
                           location: original.location
                         )
                       ]
                     end
                   when AST::Members::Alias
                     nil
                   when nil
                     nil
                   end

    if method_types
      method_types.each do |method_type|
        merged_params = type_params
          .reject {|param| method_type.type_param_names.include?(param.name) }
          .concat(method_type.type_params)

        result = calculator.in_method_type(method_type: method_type, variables: param_names)
        validate_params_with(merged_params, result: result) do |param|
          raise InvalidVarianceAnnotationError.new(
            type_name: definition.type_name,
            param: param,
            location: method_type.location
          )
        end
      end
    end
  end
end

#validate_type_presence(type)

[ GitHub ]

  
# File 'lib/rbs/definition_builder.rb', line 915

def validate_type_presence(type)
  case type
  when Types::ClassInstance, Types::ClassSingleton, Types::Interface, Types::Alias
    validate_type_name(type.name, type.location)
  end

  type.each_type do |type|
    validate_type_presence(type)
  end
end