module T::NonForcingConstants
def self.non_forcing_is_a?(val, klass, package: nil)
def self.non_forcing_is_a?(val, klass, package: nil) method_name = "T::NonForcingConstants.non_forcing_is_a?" if klass.empty? raise ArgumentError.new("The string given to `#{method_name}` must not be empty") end # We don't treat packages differently at runtime, but the static # type-checker still needs to have the package and constant # separated out. This just re-assembles the string as needed if !package.nil? klass = "::#{package}::#{klass}" end current_klass = T.let(nil, T.nilable(Module)) current_prefix = T.let(nil, T.nilable(String)) parts = klass.split('::') parts.each do |part| if current_klass.nil? # First iteration if part != "" && package.nil? # if we've supplied a package, we're probably running in # package mode, which means absolute references are # meaningless raise ArgumentError.new("The string given to `#{method_name}` must be an absolute constant reference that starts with `::`") end current_klass = Object current_prefix = '' # if this had a :: prefix, then there's no more loading to # do---skip to the next one next if part == "" end if current_klass.autoload?(part) # There's an autoload registered for that constant, which means it's not # yet loaded. `value` can't be an instance of something not yet loaded. return false end # Sorbet guarantees that the string is an absolutely resolved name. search_inheritance_chain = false if !current_klass.const_defined?(part, search_inheritance_chain) return false end current_klass = current_klass.const_get(part) current_prefix = "#{current_prefix}::#{part}" if !Module.===(current_klass) raise ArgumentError.new("#{current_prefix} is not a class or module") end end current_klass.===(val) end