class Orthoses::Trace::Attribute

def build_trace_hook

def build_trace_hook
  TracePoint.new(:call) do |tp1|
    mod_name = Orthoses::Utils.module_name(tp1.self)
    if m = tp1.self.to_s.match(/#<Class:([\w:]+)>/)
      mod_name = m[1]
    end
    next unless mod_name
    next unless target?(mod_name)
    is_singleton = tp1.self.singleton_class?
    prefix = is_singleton ? "self." : ""
    kind = tp1.method_id
    tp1.binding.local_variable_get(:names).each do |name|
      attr_module = Module.new{
        if [:attr_accessor, :attr_reader, :attr].include?(kind)
          define_method(name) do
            super()
          end
        end
        if [:attr_accessor, :attr_writer, :attr].include?(kind)
          define_method("#{name}=") do |object|
            super(object)
          end
        end
      }
      tp1.self.prepend attr_module
      if [:attr_accessor, :attr_reader, :attr].include?(kind)
        TracePoint.new(:return) do |tp2|
          type = Utils.object_to_rbs(tp2.return_value)
          @captured_dict[mod_name][[kind, prefix, name]] << type
        end.enable(target: attr_module.instance_method(name))
      end
      if [:attr_accessor, :attr_writer, :attr].include?(kind)
        TracePoint.new(:call) do |tp2|
          type = Utils.object_to_rbs(tp2.binding.local_variable_get(:object))
          @captured_dict[mod_name][[kind, prefix, name]] << type
        end.enable(target: attr_module.instance_method("#{name}="))
      end
    end
  end
end

def call

def call
  ::Module.prepend(Hook)
  store =
    build_trace_hook.enable(target: Hook.instance_method(:attr)) do
      build_trace_hook.enable(target: Hook.instance_method(:attr_accessor)) do
        build_trace_hook.enable(target: Hook.instance_method(:attr_reader)) do
          build_trace_hook.enable(target: Hook.instance_method(:attr_writer)) do
            @loader.call
          end
        end
      end
    end
  @captured_dict.each do |mod_name, captures|
    captures.each do |(kind, prefix, name), types|
      types.sort! if @sort_union_types
      injected = Utils::TypeList.new(types).inject
      store[mod_name] << "#{kind} #{prefix}#{name}: #{injected}"
    end
  end
  store
end

def initialize(loader, patterns:, sort_union_types: true)

def initialize(loader, patterns:, sort_union_types: true)
  @loader = loader
  @patterns = patterns
  @sort_union_types = sort_union_types
  @captured_dict = Hash.new { |h, k| h[k] = Hash.new { |hh, kk| hh[kk] = [] } }
end