class Sorbet::Private::HiddenMethodFinder

def self.main

def self.main
  self.new.main
end

def self.output_file

def self.output_file
  PATH
end

def all_modules_and_aliases

def all_modules_and_aliases
  puts "Naming all Modules"
  [constant_cache.all_module_names.sort, constant_cache.all_module_aliases]
end

def capture_stderr

def capture_stderr
  real_stderr = $stderr
  $stderr = StringIO.new
  yield
  $stderr.string
ensure
  $stderr = real_stderr
end

def categorize(line)

def categorize(line)
e.start_with?('#')
rn :errors
 :hidden

def constant_cache

def constant_cache
  @cache ||= Sorbet::Private::ConstantLookupCache.new
  @cache
end

def gen_source_rbi(classes, aliases)

def gen_source_rbi(classes, aliases)
  puts "Generating #{TMP_RBI} with #{classes.count} modules and #{aliases.count} aliases"
  serializer = Sorbet::Private::Serialize.new(constant_cache)
  buffer = []
  buffer << Sorbet::Private::Serialize.header
  # should we do something with these errors?
  capture_stderr do
    classes.each do |class_name|
      buffer << serializer.class_or_module(class_name)
    end
    aliases.each do |base, other_names|
      other_names.each do |other_name|
        buffer << serializer.alias(base, other_name)
      end
    end
  end
  File.write(TMP_RBI, buffer.join("\n"))
end

def looks_like_stub_name(name)

def looks_like_stub_name(name)
  name.include?('$')
end

def main

def main
  mk_dir
  require_everything
  classes, aliases = all_modules_and_aliases
  gen_source_rbi(classes, aliases)
  rm_rbis
  write_constants
  source, rbi = read_constants
  write_diff(source, rbi)
  split_rbi
  rm_dir
end

def mk_dir

def mk_dir
  FileUtils.mkdir_p(PATH) unless Dir.exist?(PATH)
end

def read_constants

def read_constants
  puts "Reading #{SOURCE_CONSTANTS}"
  source = JSON.parse(File.read(SOURCE_CONSTANTS))
  puts "Reading #{RBI_CONSTANTS}"
  rbi = JSON.parse(File.read(RBI_CONSTANTS))
  [source, rbi]
end

def real_name(mod)

def real_name(mod)
  constant_cache.name_by_class(mod)
end

def require_everything

def require_everything
  puts "Requiring all of your code"
  Sorbet::Private::RequireEverything.require_everything
end

def rm_dir

def rm_dir
  FileUtils.rm_r(TMP_PATH)
end

def rm_rbis

def rm_rbis
elete(HIDDEN_RBI) if File.exist?(HIDDEN_RBI)
elete(ERRORS_RBI) if File.exist?(ERRORS_RBI)

def serialize_alias(source_entry, rbi_entry, my_klass, source_symbols, rbi_symbols)

def serialize_alias(source_entry, rbi_entry, my_klass, source_symbols, rbi_symbols)
  return if rbi_entry["kind"] != "STATIC_FIELD"
  return if source_entry == rbi_entry
  if source_entry
    is_stub = source_entry['superClass'] && source_symbols[source_entry['superClass']] == 'Sorbet::Private::Static::StubModule'
    if !is_stub
      return
    end
  end
  return if !rbi_entry["aliasTo"]
  fqn = rbi_symbols[rbi_entry["id"]]
  other_fqn = rbi_symbols[rbi_entry["aliasTo"]]
  return if looks_like_stub_name(fqn)
  ret = String.new
  ret << "#{fqn} = #{other_fqn}\n"
  return ret
end

def serialize_class(source_entry, rbi_entry, klass, source_symbols, rbi_symbols, source_by_name)

def serialize_class(source_entry, rbi_entry, klass, source_symbols, rbi_symbols, source_by_name)
  return if rbi_entry["kind"] != "CLASS"
  name = rbi_entry["name"]["name"]
  if name.start_with?('<Class:')
    name = name.sub('<Class:', '').sub('>', '')
    my_klass_is_singleton = true
  else
    my_klass_is_singleton = false
  end
  begin
    my_klass = klass.const_get(name, false) # rubocop:disable PrisonGuard/NoDynamicConstAccess
  rescue LoadError, NameError, ArgumentError => e
    return "# #{e.message.gsub("\n", "\n# ")}"
  end
  return if !Sorbet::Private::RealStdlib.real_is_a?(my_klass, Class) && !Sorbet::Private::RealStdlib.real_is_a?(my_klass, Module)
  # We specifically don't typecheck anything in T:: since it is hardcoded
  # into sorbet
  return if real_name(my_klass) == 'T'
  source_type = nil
  if !source_entry
    if source_by_name[name]
      source_type = source_by_name[name]["kind"]
    end
  else
    source_type = source_entry["kind"]
  end
  if source_type && source_type != "CLASS"
    return "# The source says #{real_name(my_klass)} is a #{source_type} but reflection says it is a #{rbi_entry['kind']}"
  end
  if !source_entry
    source_children = []
    source_mixins = []
    is_stub = true
  else
    source_children = source_entry.fetch("children", [])
    source_mixins = source_entry.fetch("mixins", [])
    is_stub = source_entry['superClass'] && source_symbols[source_entry['superClass']] == 'Sorbet::Private::Static::StubModule'
  end
  rbi_children = rbi_entry.fetch("children", [])
  rbi_mixins = rbi_entry.fetch("mixins", [])
  methods = serialize_methods(source_children, rbi_children, my_klass, my_klass_is_singleton)
  includes = serialize_includes(source_mixins, rbi_mixins, my_klass, my_klass_is_singleton, source_symbols, rbi_symbols)
  values = serialize_values(source_children, rbi_children, my_klass, source_symbols)
  ret = []
  if !without_errors(methods).empty? || !without_errors(includes).empty? || !without_errors(values).empty? || is_stub
    fqn = real_name(my_klass)
    if fqn
      klass_str = String.new
      klass_str << (Sorbet::Private::RealStdlib.real_is_a?(my_klass, Class) ? "class #{fqn}\n" : "module #{fqn}\n")
      klass_str << includes.join("\n")
      klass_str << "\n" unless klass_str.end_with?("\n")
      klass_str << methods.join("\n")
      klass_str << "\n" unless klass_str.end_with?("\n")
      klass_str << values.join("\n")
      klass_str << "\n" unless klass_str.end_with?("\n")
      klass_str << "end\n"
      ret << klass_str
    end
  end
  children = serialize_constants(source_children, rbi_children, my_klass, my_klass_is_singleton, source_symbols, rbi_symbols)
  if children != ""
    ret << children
  end
  ret.empty? ? nil : ret.join("\n")
end

def serialize_constants(source, rbi, klass, is_singleton, source_symbols, rbi_symbols)

def serialize_constants(source, rbi, klass, is_singleton, source_symbols, rbi_symbols)
  source_by_name = source.map {|v| [v["name"]["name"], v]}.to_h
  ret = []
  rbi.each do |rbi_entry|
    source_entry = source_by_name[rbi_entry["name"]["name"]]
    ret << serialize_alias(source_entry, rbi_entry, klass, source_symbols, rbi_symbols)
    ret << serialize_class(source_entry, rbi_entry, klass, source_symbols, rbi_symbols, source_by_name)
  end
  ret.compact.join("\n")
end

def serialize_includes(source, rbi, klass, is_singleton, source_symbols, rbi_symbols)

def serialize_includes(source, rbi, klass, is_singleton, source_symbols, rbi_symbols)
[]
_mixins = source.map {|id| source_symbols[id]}
xins = rbi.map {|id| rbi_symbols[id]}
xins.each do |rbi_mixin|
source_mixins.include?(rbi_mixin)
yword = is_singleton ? "extend" : "include"
t << "  #{keyword} ::#{rbi_mixin}"

def serialize_methods(source, rbi, klass, is_singleton)

def serialize_methods(source, rbi, klass, is_singleton)
_by_name = source.map {|v| [v["name"]["name"], v]}.to_h
[]
= Sorbet::Private::Serialize.new(constant_cache)
ch do |rbi_entry|
 if rbi_entry["kind"] != "METHOD"
 = rbi_entry["name"]["name"]
 if source_by_name[name]
 if BLACKLIST.include?([klass.object_id, name])
 if name.start_with?('<') && name.end_with?('>')
n
 is_singleton
method = klass.singleton_method(name)
se
method = klass.instance_method(name)
d
ue => e
t << "# #{e.message.gsub("\n", "\n# ")}"
xt
r_method = method.super_method
xt if super_method && T::AbstractUtils.abstract_method?(method) == T::AbstractUtils.abstract_method?(super_method)
rs = capture_stderr do
t << maker.serialize_method(method, is_singleton, with_sig: false)
rs.split("\n").each do |line|
t << "# #{line}"

def serialize_values(source, rbi, klass, source_symbols)

def serialize_values(source, rbi, klass, source_symbols)
_by_name = source.map {|v| [v["name"]["name"], v]}.to_h
[]
ch do |rbi_entry|
 = rbi_entry["name"]["name"]
ce_entry = source_by_name[name]
ource_entry
_stub = source_entry['superClass'] && source_symbols[source_entry['superClass']] == 'Sorbet::Private::Static::StubModule'
xt unless is_stub
 if Sorbet::Private::ConstantLookupCache::DEPRECATED_CONSTANTS.include?("#{Sorbet::Private::RealStdlib.real_name(klass)}::#{name}")
n
_value = klass.const_get(name, false) # rubocop:disable PrisonGuard/NoDynamicConstAccess
ue StandardError, LoadError => e
t << "# #{e.message.gsub("\n", "\n# ")}"
xt
 if Sorbet::Private::RealStdlib.real_is_a?(my_value, Class) || Sorbet::Private::RealStdlib.real_is_a?(my_value, Module)
<< "  #{name} = ::T.let(nil, ::T.untyped)"

def split_rbi

def split_rbi
Generating split RBIs into #{PATH}"
 = {
en: String.new,
rs: String.new,
= File.read(DIFF_RBI)
tput = T.let(nil, T.untyped)
split("\n").each do |line|
gory = categorize(line)
ategory == :errors
Don't ever switch to errors output permanantly
tput[category] << line + "\n"
xt
category.nil?
r_output = output[category]
output << line + "\n"
rite(HIDDEN_RBI, HEADER + "\n" + output[:hidden])
rite(ERRORS_RBI, HEADER + "\n" + output[:errors])

def symbols_id_to_name(entry, prefix)

def symbols_id_to_name(entry, prefix)
  ret = {}
  symbols_id_to_name_real(entry, prefix, ret)
  ret
end

def symbols_id_to_name_real(entry, prefix, ret)

def symbols_id_to_name_real(entry, prefix, ret)
 entry["name"]["name"]
fix == '' || prefix == "<root>"
= name.to_s
= "#{prefix}::#{name}"
try["id"]] = fqn
fetch("children", []).each do |child|
ols_id_to_name_real(child, fqn, ret)

def without_errors(lines)

def without_errors(lines)
reject {|line| line.start_with?("#")}

def write_constants

def write_constants
  puts "Printing your code's symbol table into #{SOURCE_CONSTANTS}"
  io = IO.popen(
    [
      File.realpath("#{__dir__}/../bin/srb"),
      'tc',
      '--print=symbol-table-full-json',
      '--stdout-hup-hack',
      '--silence-dev-message',
      '--no-error-count',
    ],
    err: SOURCE_CONSTANTS_ERR
  )
  File.write(SOURCE_CONSTANTS, io.read)
  io.close
  raise "Your source can't be read by Sorbet.\nYou can try `find . -type f | xargs -L 1 -t bundle exec srb tc --no-config --error-white-list 1000` and hopefully the last file it is processing before it dies is the culprit.\nIf not, maybe the errors in this file will help: #{SOURCE_CONSTANTS_ERR}" if File.read(SOURCE_CONSTANTS).empty?
  puts "Printing #{TMP_RBI}'s symbol table into #{RBI_CONSTANTS}"
  io = IO.popen(
    [
      File.realpath("#{__dir__}/../bin/srb"),
      'tc',
      # Make sure we don't load a sorbet/config in your cwd
      '--no-config',
      '--print=symbol-table-full-json',
      # Method redefined with mismatched argument is ok since sometime
      # people monkeypatch over method
      '--error-black-list=4010',
      # Redefining constant is needed because we serialize things both as
      # aliases and in-class constants.
      '--error-black-list=4012',
      # Invalid nesting is ok because we don't generate all the intermediate
      # namespaces for aliases
      '--error-black-list=4015',
      '--stdout-hup-hack',
      '--silence-dev-message',
      '--no-error-count',
      TMP_RBI,
    ],
    err: RBI_CONSTANTS_ERR
  )
  File.write(RBI_CONSTANTS, io.read)
  io.close
  raise "#{TMP_RBI} had unexpected errors. Check this file for a clue: #{RBI_CONSTANTS_ERR}" unless $?.success?
end

def write_diff(source, rbi)

def write_diff(source, rbi)
  puts "Building rbi id to symbol map"
  rbi_symbols = symbols_id_to_name(rbi, '')
  puts "Building source id to symbol map"
  source_symbols = symbols_id_to_name(source, '')
  puts "Writing #{DIFF_RBI}"
  diff = serialize_constants(
    source.fetch("children", []),
    rbi.fetch("children", []),
    Object, false, source_symbols, rbi_symbols)
  File.write(DIFF_RBI, diff)
end