module Tapioca::Runtime::Reflection

def abstract_type_of(constant)

def abstract_type_of(constant)
  T::Private::Abstract::Data.get(constant, :abstract_type) ||
    T::Private::Abstract::Data.get(singleton_class_of(constant), :abstract_type)
end

def ancestors_of(constant)

def ancestors_of(constant)
  ANCESTORS_METHOD.bind_call(constant)
end

def are_equal?(object, other)

def are_equal?(object, other)
  EQUAL_METHOD.bind_call(object, other)
end

def child_module_for_parent_with_name(parent, name)

def child_module_for_parent_with_name(parent, name)
  return if parent.autoload?(name)
  child = constantize(name, inherit: true, namespace: parent)
  return unless Module === child
  return unless name_of(child) == "#{name_of(parent)}::#{name}"
  child
end

def class_of(object)

def class_of(object)
  CLASS_METHOD.bind_call(object)
end

def constant_defined?(constant)

def constant_defined?(constant)
  !UNDEFINED_CONSTANT.eql?(constant)
end

def constantize(symbol, inherit: false, namespace: Object)

def constantize(symbol, inherit: false, namespace: Object)
  namespace.const_get(symbol, inherit)
rescue NameError, LoadError, RuntimeError, ArgumentError, TypeError
  UNDEFINED_CONSTANT
end

def constants_of(constant)

def constants_of(constant)
  CONSTANTS_METHOD.bind_call(constant, false)
end

def descendants_of(klass)

def descendants_of(klass)
  result = ObjectSpace.each_object(klass.singleton_class).reject do |k|
    k.singleton_class? || k == klass
  end
  T.unsafe(result)
end

def file_candidates_for(constant)

def file_candidates_for(constant)
  relevant_methods_for(constant).filter_map do |method|
    method.source_location&.first
  end.to_set
end

def final_module?(constant)

def final_module?(constant)
  T::Private::Final.final_module?(constant)
end

def has_aliased_namespace?(name)

def has_aliased_namespace?(name)
  name_parts = name.split("::")
  name_parts.pop # drop the constant name, leaving just the namespace
  name_parts.each_with_object([]) do |name_part, namespaces|
    namespaces << "#{namespaces.last}::#{name_part}".delete_prefix("::")
  end.any? do |namespace|
    constant = constantize(namespace)
    next unless Module === constant
    # If the constant name doesn't match the namespace,
    # the namespace must contain an alias
    name_of(constant) != namespace
  end
end

def inherited_ancestors_of(constant)

def inherited_ancestors_of(constant)
  if Class === constant
    ancestors_of(superclass_of(constant) || Object)
  else
    Module.new.ancestors
  end
end

def method_defined_by_forwardable_module?(method)

def method_defined_by_forwardable_module?(method)
  method.source_location&.first == Object.const_source_location(:Forwardable)&.first
end

def method_of(constant, method)

def method_of(constant, method)
  METHOD_METHOD.bind_call(constant, method)
end

def methods_for(constant)

def methods_for(constant)
  modules = [constant, singleton_class_of(constant)]
  method_list_methods = [
    PUBLIC_INSTANCE_METHODS_METHOD,
    PROTECTED_INSTANCE_METHODS_METHOD,
    PRIVATE_INSTANCE_METHODS_METHOD,
  ]
  modules.product(method_list_methods).flat_map do |mod, method_list_method|
    method_list_method.bind_call(mod, false).map { |name| mod.instance_method(name) }
  end
end

def name_of(constant)

def name_of(constant)
  name = NAME_METHOD.bind_call(constant)
  name&.start_with?("#<") ? nil : name
end

def name_of_type(type)

def name_of_type(type)
  type.to_s
end

def object_id_of(object)

def object_id_of(object)
  OBJECT_ID_METHOD.bind_call(object)
end

def private_instance_methods_of(constant)

def private_instance_methods_of(constant)
  PRIVATE_INSTANCE_METHODS_METHOD.bind_call(constant)
end

def protected_instance_methods_of(constant)

def protected_instance_methods_of(constant)
  PROTECTED_INSTANCE_METHODS_METHOD.bind_call(constant)
end

def public_instance_methods_of(constant)

def public_instance_methods_of(constant)
  PUBLIC_INSTANCE_METHODS_METHOD.bind_call(constant)
end

def qualified_name_of(constant)

def qualified_name_of(constant)
  name = name_of(constant)
  return if name.nil?
  if name.start_with?("::")
    name
  else
    "::#{name}"
  end
end

def relevant_methods_for(constant)

def relevant_methods_for(constant)
  methods = methods_for(constant).select(&:source_location)
    .reject { |x| method_defined_by_forwardable_module?(x) }
  return methods unless methods.empty?
  constants_of(constant).flat_map do |const_name|
    if (mod = child_module_for_parent_with_name(constant, const_name.to_s))
      relevant_methods_for(mod)
    else
      []
    end
  end
end

def resolve_loc(locations)

def resolve_loc(locations)
  return "" unless locations
  resolved_loc = locations.find do |loc|
    label = loc.label
    next unless label
    REQUIRED_FROM_LABELS.include?(label) || label.start_with?("block in <class:")
  end
  return "" unless resolved_loc
  resolved_loc.absolute_path || ""
end

def sealed_module?(constant)

def sealed_module?(constant)
  T::Private::Sealed.sealed_module?(constant)
end

def signature_of(method)

def signature_of(method)
  signature_of!(method)
rescue LoadError, StandardError
  nil
end

def signature_of!(method)

def signature_of!(method)
  T::Utils.signature_for_method(method)
end

def singleton_class_of(constant)

def singleton_class_of(constant)
  SINGLETON_CLASS_METHOD.bind_call(constant)
end

def superclass_of(constant)

def superclass_of(constant)
  SUPERCLASS_METHOD.bind_call(constant)
end