class Sorbet::Private::ConstantLookupCache
This class walks global namespace to find all modules and discover all of their names. At the time you ask for it it, it takes a “spashot” of the world. If new modules were defined after an instance of this class was created, they won't be visible through a previously returned instance.
Constants
- ConstantEntry
- DEPRECATED_CONSTANTS
We won't be able to see if someone mankeypatches these.
Public Class Methods
new()
click to toggle source
# File lib/constant_cache.rb, line 68 def initialize @all_constants = {} dfs_module(Object, nil, @all_constants, nil) Sorbet::Private::Status.done @consts_by_name = {} @all_constants.each_value do |struct| fill_primary_name(struct) @consts_by_name[struct.primary_name] = struct.const end end
Public Instance Methods
all_module_aliases()
click to toggle source
# File lib/constant_cache.rb, line 95 def all_module_aliases ret = {} @all_constants.map do |_key, struct| next if struct.nil? || !Sorbet::Private::RealStdlib.real_is_a?(struct.const, Module) || struct.aliases.size < 2 if struct.owner != nil ret[struct.primary_name] = struct.aliases.reject do |name| # ignore the primary next true if name == struct.primary_name prefix, _, _ = name.rpartition('::') # an alias that exists at the top-level next false if prefix == "" # if the prefix is the same syntactically, then this is a good alias next false if prefix == struct.owner.primary_name # skip the alias if the owner is the same other_owner_const = Sorbet::Private::RealStdlib.real_const_get(Object, prefix, false) struct.owner.const == other_owner_const end else # top-level names ret[struct.primary_name] = struct.aliases.reject {|name| name == struct.primary_name } end end ret end
all_module_names()
click to toggle source
# File lib/constant_cache.rb, line 79 def all_module_names ret = @all_constants.select {|_k, v| Sorbet::Private::RealStdlib.real_is_a?(v.const, Module)}.map do |_key, struct| raise "should never happen" if !struct.primary_name struct.primary_name end ret end
all_named_modules()
click to toggle source
# File lib/constant_cache.rb, line 87 def all_named_modules ret = @all_constants.select {|_k, v| Sorbet::Private::RealStdlib.real_is_a?(v.const, Module)}.map do |_key, struct| raise "should never happen" if !struct.primary_name struct.const end ret end
class_by_name(name)
click to toggle source
# File lib/constant_cache.rb, line 125 def class_by_name(name) @consts_by_name[name] end
name_by_class(klass)
click to toggle source
# File lib/constant_cache.rb, line 129 def name_by_class(klass) @all_constants[Sorbet::Private::RealStdlib.real_object_id(klass)]&.primary_name end
Private Instance Methods
dfs_module(mod, prefix, ret, owner)
click to toggle source
# File lib/constant_cache.rb, line 133 def dfs_module(mod, prefix, ret, owner) raise "error #{prefix}: #{mod} is not a module" if !Sorbet::Private::RealStdlib.real_is_a?(mod, Module) name = Sorbet::Private::RealStdlib.real_name(mod) Sorbet::Private::Status.say("Naming #{name}", print_without_tty: false) return if name == 'RSpec::ExampleGroups' # These are all anonymous classes and will be quadratic in the number of classes to name them. We also know they don't have any hidden definitions begin constants = Sorbet::Private::RealStdlib.real_constants(mod) rescue TypeError puts "Failed to call `constants` on #{prefix}" return nil end go_deeper = [] # make this a bfs to prefer shorter names constants.each do |nested| begin next if Sorbet::Private::RealStdlib.real_autoload?(mod, nested) # some constants aren't autoloaded even after require_everything, e.g. StateMachine::Graph begin next if DEPRECATED_CONSTANTS.include?("#{prefix}::#{nested}") # avoid stdout spew rescue NameError # If it isn't to_s-able, that is ok, it won't be in our deprecated list nil end begin nested_constant = Sorbet::Private::RealStdlib.real_const_get(mod, nested, false) # rubocop:disable PrisonGuard/NoDynamicConstAccess rescue LoadError => err puts "Got #{err.class} when trying to get nested name #{name}::#{nested}" next rescue NameError, ArgumentError => err puts "Got #{err.class} when trying to get nested name #{name}::#{nested}_" # some stuff fails to load, like # `const_get': uninitialized constant YARD::Parser::Ruby::Legacy::RipperParser (NameError) # Did you mean? YARD::Parser::Ruby::Legacy::RipperParser end object_id = Sorbet::Private::RealStdlib.real_object_id(nested_constant) maybe_seen_already = ret[object_id] if Object.eql?(mod) || !prefix nested_name = nested.to_s else nested_name = prefix + "::" + nested.to_s end if maybe_seen_already if nested_name != maybe_seen_already.primary_name maybe_seen_already.aliases << nested_name maybe_seen_already.owner = owner end if maybe_seen_already.primary_name.nil? && Sorbet::Private::RealStdlib.real_is_a?(nested_constant, Module) realName = Sorbet::Private::RealStdlib.real_name(nested_constant) maybe_seen_already.primary_name = realName end else entry = ConstantEntry.new(nested, nested_name, nil, [nested_name], nested_constant, owner) ret[object_id] = entry if Sorbet::Private::RealStdlib.real_is_a?(nested_constant, Module) && Object != nested_constant go_deeper << [entry, nested_constant, nested_name] end end end end # Horrible horrible hack to get private constants that are `include`d. # This doesn't recurse so you can't get private constants inside the private # constants, but I really hope you don't need those... Sorbet::Private::RealStdlib.real_ancestors(mod).each do |ancestor| object_id = Sorbet::Private::RealStdlib.real_object_id(ancestor) name = Sorbet::Private::RealStdlib.real_name(ancestor) next unless name next if ret[object_id] prefix, _, const_name = name.rpartition('::') entry = ConstantEntry.new(const_name.to_sym, name, name, [name], ancestor, nil) ret[object_id] = entry end sorted_deeper = go_deeper.sort_by do |entry, nested_constant, nested_name| Sorbet::Private::RealStdlib.real_name(nested_constant) end sorted_deeper.each do |entry, nested_constant, nested_name| dfs_module(nested_constant, nested_name, ret, entry) end nil end
fill_primary_name(struct)
click to toggle source
# File lib/constant_cache.rb, line 217 def fill_primary_name(struct) return if struct.primary_name if !struct.owner struct.primary_name = struct.found_name else fill_primary_name(struct.owner) struct.primary_name = struct.owner.primary_name + "::" + struct.const_name.to_s end end