module IRB::TypeCompletion::Types

Constants

OBJECT_TO_TYPE_SAMPLE_SIZE

Attributes

rbs_builder[R]
rbs_load_error[R]

Public Class Methods

class_name_of(klass) click to toggle source
# File irb/type_completion/types.rb, line 32
def self.class_name_of(klass)
  klass = klass.superclass if klass.singleton_class?
  Methods::MODULE_NAME_METHOD.bind_call klass
end
intersect?(a, b) click to toggle source
# File irb/type_completion/types.rb, line 123
def self.intersect?(a, b)
  atypes = a.types.group_by(&:class)
  btypes = b.types.group_by(&:class)
  if atypes[SingletonType] && btypes[SingletonType]
    aa, bb = [atypes, btypes].map {|types| types[SingletonType].map(&:module_or_class) }
    return true if (aa & bb).any?
  end

  aa, bb = [atypes, btypes].map {|types| (types[InstanceType] || []).map(&:klass) }
  (aa.flat_map(&:ancestors) & bb).any?
end
load_rbs_builder() click to toggle source
# File irb/type_completion/types.rb, line 21
def self.load_rbs_builder
  require 'rbs'
  require 'rbs/cli'
  loader = RBS::CLI::LibraryOptions.new.loader
  loader.add path: Pathname('sig')
  @rbs_builder = RBS::DefinitionBuilder.new env: RBS::Environment.from_loader(loader).resolve_type_names
rescue LoadError, StandardError => e
  @rbs_load_error = e
  nil
end
method_return_type(type, method_name) click to toggle source
# File irb/type_completion/types.rb, line 49
def self.method_return_type(type, method_name)
  receivers = type.types.map do |t|
    case t
    in SingletonType
      [t, t.module_or_class, true]
    in InstanceType
      [t, t.klass, false]
    end
  end
  types = receivers.flat_map do |receiver_type, klass, singleton|
    method = rbs_search_method klass, method_name, singleton
    next [] unless method
    method.method_types.map do |method|
      from_rbs_type(method.type.return_type, receiver_type, {})
    end
  end
  UnionType[*types]
end
preload_in_thread() click to toggle source
# File irb/type_completion/types.rb, line 12
def self.preload_in_thread
  return if @preload_started

  @preload_started = true
  Thread.new do
    load_rbs_builder
  end
end
rbs_methods(type, method_name, args_types, kwargs_type, has_block) click to toggle source
# File irb/type_completion/types.rb, line 68
def self.rbs_methods(type, method_name, args_types, kwargs_type, has_block)
  return [] unless rbs_builder

  receivers = type.types.map do |t|
    case t
    in SingletonType
      [t, t.module_or_class, true]
    in InstanceType
      [t, t.klass, false]
    end
  end
  has_splat = args_types.include?(nil)
  methods_with_score = receivers.flat_map do |receiver_type, klass, singleton|
    method = rbs_search_method klass, method_name, singleton
    next [] unless method
    method.method_types.map do |method_type|
      score = 0
      score += 2 if !!method_type.block == has_block
      reqs = method_type.type.required_positionals
      opts = method_type.type.optional_positionals
      rest = method_type.type.rest_positionals
      trailings = method_type.type.trailing_positionals
      keyreqs = method_type.type.required_keywords
      keyopts = method_type.type.optional_keywords
      keyrest = method_type.type.rest_keywords
      args = args_types
      if kwargs_type&.any? && keyreqs.empty? && keyopts.empty? && keyrest.nil?
        kw_value_type = UnionType[*kwargs_type.values]
        args += [InstanceType.new(Hash, K: SYMBOL, V: kw_value_type)]
      end
      if has_splat
        score += 1 if args.count(&:itself) <= reqs.size + opts.size + trailings.size
      elsif reqs.size + trailings.size <= args.size && (rest || args.size <= reqs.size + opts.size + trailings.size)
        score += 2
        centers = args[reqs.size...-trailings.size]
        given = args.first(reqs.size) + centers.take(opts.size) + args.last(trailings.size)
        expected = (reqs + opts.take(centers.size) + trailings).map(&:type)
        if rest
          given << UnionType[*centers.drop(opts.size)]
          expected << rest.type
        end
        if given.any?
          score += given.zip(expected).count do |t, e|
            e = from_rbs_type e, receiver_type
            intersect?(t, e) || (intersect?(STRING, e) && t.methods.include?(:to_str)) || (intersect?(INTEGER, e) && t.methods.include?(:to_int)) || (intersect?(ARRAY, e) && t.methods.include?(:to_ary))
          end.fdiv(given.size)
        end
      end
      [[method_type, given || [], expected || []], score]
    end
  end
  max_score = methods_with_score.map(&:last).max
  methods_with_score.select { _2 == max_score }.map(&:first)
end
rbs_search_method(klass, method_name, singleton) click to toggle source
# File irb/type_completion/types.rb, line 37
def self.rbs_search_method(klass, method_name, singleton)
  klass.ancestors.each do |ancestor|
    name = class_name_of ancestor
    next unless name && rbs_builder
    type_name = RBS::TypeName(name).absolute!
    definition = (singleton ? rbs_builder.build_singleton(type_name) : rbs_builder.build_instance(type_name)) rescue nil
    method = definition.methods[method_name] if definition
    return method if method
  end
  nil
end
type_from_object(object) click to toggle source
# File irb/type_completion/types.rb, line 135
def self.type_from_object(object)
  case object
  when Array
    InstanceType.new Array, { Elem: union_type_from_objects(object) }
  when Hash
    InstanceType.new Hash, { K: union_type_from_objects(object.keys), V: union_type_from_objects(object.values) }
  when Module
    SingletonType.new object
  else
    klass = Methods::OBJECT_SINGLETON_CLASS_METHOD.bind_call(object) rescue Methods::OBJECT_CLASS_METHOD.bind_call(object)
    InstanceType.new klass
  end
end
union_type_from_objects(objects) click to toggle source
# File irb/type_completion/types.rb, line 149
def self.union_type_from_objects(objects)
  values = objects.size <= OBJECT_TO_TYPE_SAMPLE_SIZE ? objects : objects.sample(OBJECT_TO_TYPE_SAMPLE_SIZE)
  klasses = values.map { Methods::OBJECT_CLASS_METHOD.bind_call(_1) }
  UnionType[*klasses.uniq.map { InstanceType.new _1 }]
end