-
Notifications
You must be signed in to change notification settings - Fork 216
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #938 from ruby/const-resolver
Add Resolver::ConstantResolver and Resolver::TypeNameResolver
- Loading branch information
Showing
11 changed files
with
828 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
module RBS | ||
module Resolver | ||
class ConstantResolver | ||
class Table | ||
attr_reader :children_table, :toplevel | ||
attr_reader :constants_table | ||
|
||
def initialize(environment) | ||
@children_table = {} | ||
@toplevel = {} | ||
|
||
@constants_table = {} | ||
|
||
environment.class_decls.each_key do |name| | ||
children_table[name] = {} | ||
end | ||
|
||
environment.class_decls.each do |name, entry| | ||
unless name.namespace.empty? | ||
parent = name.namespace.to_type_name | ||
|
||
table = children_table[parent] or raise | ||
constant = constant_of_module(name, entry) | ||
else | ||
table = toplevel | ||
constant = constant_of_module(name, entry) | ||
end | ||
|
||
table[name.name] = constant | ||
constants_table[name] = constant | ||
end | ||
|
||
environment.constant_decls.each do |name, entry| | ||
unless name.namespace.empty? | ||
parent = name.namespace.to_type_name | ||
|
||
table = children_table[parent] or raise | ||
constant = constant_of_constant(name, entry) | ||
else | ||
table = toplevel | ||
constant = constant_of_constant(name, entry) | ||
end | ||
|
||
table[name.name] = constant | ||
end | ||
end | ||
|
||
def children(name) | ||
children_table[name] | ||
end | ||
|
||
def constant(name) | ||
constants_table[name] | ||
end | ||
|
||
def constant_of_module(name, entry) | ||
type = Types::ClassSingleton.new( | ||
name: name, | ||
location: nil | ||
) | ||
|
||
Constant.new(name: name, type: type, entry: entry) | ||
end | ||
|
||
def constant_of_constant(name, entry) | ||
Constant.new(name: name, type: entry.decl.type, entry: entry) | ||
end | ||
end | ||
|
||
attr_reader :builder, :table | ||
attr_reader :context_constants_cache, :child_constants_cache | ||
|
||
def initialize(builder:) | ||
@builder = builder | ||
@table = Table.new(builder.env) | ||
@context_constants_cache = {} | ||
@child_constants_cache = {} | ||
end | ||
|
||
def resolve(name, context:) | ||
cs = constants(context) or raise "Broken context is given" | ||
cs[name] | ||
end | ||
|
||
def constants(context) | ||
unless context_constants_cache.key?(context) | ||
load_context_constants(context) | ||
end | ||
|
||
context_constants_cache[context] | ||
end | ||
|
||
def resolve_child(module_name, name) | ||
children(module_name)[name] | ||
end | ||
|
||
def children(module_name) | ||
unless child_constants_cache.key?(module_name) | ||
load_child_constants(module_name) | ||
end | ||
|
||
child_constants_cache[module_name] or raise | ||
end | ||
|
||
def load_context_constants(context) | ||
# @type var consts: Hash[Symbol, Constant] | ||
consts = {} | ||
|
||
if context | ||
if last = context[1] | ||
constants_from_ancestors(last, constants: consts) | ||
end | ||
end | ||
constants_from_context(context, constants: consts) or return | ||
constants_itself(context, constants: consts) | ||
|
||
context_constants_cache[context] = consts | ||
end | ||
|
||
def load_child_constants(name) | ||
# @type var constants: Hash[Symbol, Constant] | ||
constants = {} | ||
|
||
if table.children(name) | ||
builder.ancestor_builder.instance_ancestors(name).ancestors.each do |ancestor| | ||
if ancestor.is_a?(Definition::Ancestor::Instance) | ||
if ancestor.name == BuiltinNames::Object.name | ||
if name != BuiltinNames::Object.name | ||
next | ||
end | ||
end | ||
|
||
case ancestor.source | ||
when AST::Members::Include, :super, nil | ||
consts = table.children(ancestor.name) or raise | ||
constants.merge!(consts) | ||
end | ||
end | ||
end | ||
end | ||
|
||
child_constants_cache[name] = constants | ||
end | ||
|
||
def constants_from_context(context, constants:) | ||
if context | ||
parent, last = context | ||
|
||
constants_from_context(parent, constants: constants) or return false | ||
|
||
if last | ||
consts = table.children(last) or return false | ||
constants.merge!(consts) | ||
end | ||
else | ||
constants.merge!(table.toplevel) | ||
end | ||
|
||
true | ||
end | ||
|
||
def constants_from_ancestors(module_name, constants:) | ||
builder.ancestor_builder.instance_ancestors(module_name).ancestors.each do |ancestor| | ||
if ancestor.is_a?(Definition::Ancestor::Instance) | ||
case ancestor.source | ||
when AST::Members::Include, :super, nil | ||
consts = table.children(ancestor.name) or raise | ||
constants.merge!(consts) | ||
end | ||
end | ||
end | ||
end | ||
|
||
def constants_itself(context, constants:) | ||
if context | ||
_, typename = context | ||
|
||
if typename | ||
if (ns = typename.namespace).empty? | ||
constant = table.toplevel[typename.name] or raise | ||
else | ||
hash = table.children(ns.to_type_name) or raise | ||
constant = hash[typename.name] | ||
end | ||
|
||
constants[typename.name] = constant | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
module RBS | ||
module Resolver | ||
class TypeNameResolver | ||
attr_reader :all_names | ||
attr_reader :cache | ||
|
||
def initialize(env) | ||
@all_names = Set[] | ||
@cache = {} | ||
|
||
all_names.merge(env.class_decls.keys) | ||
all_names.merge(env.interface_decls.keys) | ||
all_names.merge(env.alias_decls.keys) | ||
end | ||
|
||
def try_cache(query) | ||
cache.fetch(query) do | ||
result = yield | ||
cache[query] = result | ||
end | ||
end | ||
|
||
def resolve(type_name, context:) | ||
if type_name.absolute? | ||
return type_name | ||
end | ||
|
||
try_cache([type_name, context]) do | ||
resolve_in(type_name, context) | ||
end | ||
end | ||
|
||
def resolve_in(type_name, context) | ||
if context | ||
parent, child = context | ||
case child | ||
when false | ||
resolve_in(type_name, parent) | ||
when TypeName | ||
name = type_name.with_prefix(child.to_namespace) | ||
has_name?(name) || resolve_in(type_name, parent) | ||
end | ||
else | ||
has_name?(type_name.absolute!) | ||
end | ||
end | ||
|
||
def has_name?(full_name) | ||
if all_names.include?(full_name) | ||
full_name | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
module RBS | ||
module Resolver | ||
class ConstantResolver | ||
# Table stores the set of immediate child constants of a module. | ||
# | ||
# ```rb | ||
# table = RBS::ConstantResolver::Table.new(env) | ||
# | ||
# table.children(TypeName("::Object")) # -> { ... } Returns a hash of name and constants. | ||
# table.children(TypeName("::File::PATH_SEPARATOR")) # -> nil Returns nil because the constant is not a module. | ||
# | ||
# table.toplevel # -> { ... } Returns a hash of top level constants. | ||
# ``` | ||
# | ||
# The `#toplevel` is incompatible with Ruby. | ||
# All constants in Ruby are defined under `Object`, and they are accessed with `::` (Colon3) operator. | ||
# RBS is different. | ||
# `::` constants are _toplevel_ constants, and they are not defined under `::Object`. | ||
# | ||
# The behavior is simulated in `ConstantResolver`. | ||
# | ||
class Table | ||
attr_reader children_table: Hash[TypeName, Hash[Symbol, Constant]?] | ||
attr_reader constants_table: Hash[TypeName, Constant] | ||
attr_reader toplevel: Hash[Symbol, Constant] | ||
|
||
def initialize: (Environment) -> void | ||
|
||
def constant: (TypeName constant_name) -> Constant? | ||
|
||
# Returns a set of constants defined under `module_name`. | ||
# Returns `nil` if there is no module with given `module_name`. | ||
# | ||
def children: (TypeName module_name) -> Hash[Symbol, Constant]? | ||
|
||
private | ||
|
||
def constant_of_module: (TypeName name, Environment::ClassEntry | Environment::ModuleEntry) -> Constant | ||
|
||
def constant_of_constant: (TypeName name, Environment::SingleEntry[TypeName, AST::Declarations::Constant]) -> Constant | ||
end | ||
|
||
attr_reader builder: DefinitionBuilder | ||
attr_reader table: Table | ||
attr_reader context_constants_cache: Hash[context, Hash[Symbol, Constant]?] | ||
attr_reader child_constants_cache: Hash[TypeName, Hash[Symbol, Constant]] | ||
|
||
def initialize: (builder: DefinitionBuilder) -> void | ||
|
||
# Resolves to `Constant` with given constant name `name` and `context`. | ||
# Returns `nil` if the constant cannot be resolved from the context. | ||
# | ||
def resolve: (Symbol name, context: context) -> Constant? | ||
|
||
# Returns the available all constants from `context`. | ||
# | ||
# Returns `nil` when the `context` is invalid. | ||
def constants: (context) -> Hash[Symbol, Constant]? | ||
|
||
# Resolves the module_name and constant name to `Constant` | ||
# | ||
# ```ruby | ||
# A::B | ||
# ^ <- module_name | ||
# ^ <- constant_name | ||
# ``` | ||
# | ||
# Find the | ||
def resolve_child: (TypeName module_name, Symbol constant_name) -> Constant? | ||
|
||
# Returns the table of all constants accessible with `::` (colon2) operator. | ||
# | ||
# * The constants under the module are included. | ||
# * The constants under the ancestor modules are included. | ||
# * The constants under the `::Object` class are not included. | ||
# * The top level constants are not included. | ||
# | ||
def children: (TypeName module_name) -> Hash[Symbol, Constant] | ||
|
||
private | ||
|
||
def load_context_constants: (context) -> void | ||
|
||
def load_child_constants: (TypeName) -> void | ||
|
||
def constants_from_context: (context, constants: Hash[Symbol, Constant]) -> bool | ||
|
||
def constants_from_ancestors: (TypeName, constants: Hash[Symbol, Constant]) -> void | ||
|
||
def constants_itself: (context, constants: Hash[Symbol, Constant]) -> void | ||
end | ||
end | ||
end |
Oops, something went wrong.