File Manager
module RBS
class DefinitionBuilder
attr_reader :env
attr_reader :type_name_resolver
attr_reader :ancestor_builder
attr_reader :method_builder
attr_reader :instance_cache
attr_reader :singleton_cache
attr_reader :singleton0_cache
attr_reader :interface_cache
def initialize(env:)
@env = env
@type_name_resolver = TypeNameResolver.from_env(env)
@ancestor_builder = AncestorBuilder.new(env: env)
@method_builder = MethodBuilder.new(env: env)
@instance_cache = {}
@singleton_cache = {}
@singleton0_cache = {}
@interface_cache = {}
end
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
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(
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
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)
self_type = Types::ClassInstance.new(name: type_name,
args: Types::Variable.build(entry.type_params.each.map(&: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_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
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
define_methods(definition,
interface_methods: interface_methods,
methods: methods,
super_interface_method: entry.is_a?(Environment::ModuleEntry))
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 == :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)
end
end
when AST::Members::InstanceVariable
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
one_ancestors.each_prepended_module do |mod|
defn = build_instance(mod.name)
merge_definition(src: defn,
dest: definition,
subst: Substitution.build(defn.type_params, mod.args))
end
end
end
end
end
# Builds a definition for singleton without .new method.
#
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_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_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(definition, interface_methods: interface_methods, methods: methods, super_interface_method: false)
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
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)
instance_type = Types::ClassInstance.new(
name: type_name,
args: entry.type_params.each.map { Types::Bases::Any.new(location: nil) },
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.build([], [], instance_type: instance_type)
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 }
# The method is _untyped new_.
instance = build_instance(type_name)
initialize = instance.methods[:initialize]
if initialize
class_params = entry.type_params.each.map(&:name)
# Inject a virtual _typed new_.
initialize_defs = initialize.defs
definition.methods[: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)
method_type_param_vars = Set.new(method_type.type_params)
if class_type_param_vars.intersect?(method_type_param_vars)
renamed_method_params = method_type.type_params.map do |name|
if class_type_param_vars.include?(name)
Types::Variable.fresh(name).name
else
name
end
end
method_params = class_params + renamed_method_params
sub = Substitution.build(method_type.type_params, Types::Variable.build(renamed_method_params))
else
method_params = class_params + method_type.type_params
sub = Substitution.build([], [])
end
method_type = method_type.map_type {|ty| ty.sub(sub) }
method_type = method_type.update(
type_params: method_params,
type: method_type.type.with_return_type(
Types::ClassInstance.new(
name: type_name,
args: Types::Variable.build(entry.type_params.each.map(&:name)),
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
)
end
end
end
end
end
end
end
def validate_params_with(type_params, result:)
type_params.each do |param|
unless param.skip_validation
unless result.compatible?(param.name, with_annotation: param.variance)
yield param
end
end
end
end
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
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|
result = calculator.in_method_type(method_type: method_type, variables: param_names)
validate_params_with(type_params, result: result) do |param|
raise InvalidVarianceAnnotationError.new(
type_name: definition.type_name,
param: param,
location: method_type.location
)
end
end
end
end
end
def insert_variable(type_name, variables, name:, type:)
variables[name] = Definition::Variable.new(
parent_variable: variables[name],
type: type,
declared_in: type_name
)
end
def define_methods(definition, interface_methods:, methods:, 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] || definition.methods[original.old_name]
unless original_method
raise UnknownMethodAliasError.new(
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: method_def.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
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
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
def merge_method(type_name, methods, name, method, sub, implemented_in: :keep, keep_super: false)
defs = method.defs.yield_self do |defs|
if sub.empty? && implemented_in == :keep
defs
else
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
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
def try_cache(type_name, cache:, key: type_name)
# @type var cc: Hash[untyped, Definition | false | nil]
cc = _ = cache
cached = cc[key]
case cached
when Definition
cached
when false
raise
when nil
cc[key] = false
begin
cc[key] = yield
rescue => ex
cc.delete(key)
raise ex
end
else
raise
end
end
def expand_alias(type_name)
entry = env.alias_decls[type_name] or raise "Unknown name for expand_alias: #{type_name}"
ensure_namespace!(type_name.namespace, location: entry.decl.location)
entry.decl.type
end
end
end
File Manager Version 1.0, Coded By Lucas
Email: hehe@yahoo.com