class Steep::TypeInference::SendArgs

def block_pass_arg

def block_pass_arg
  node = arguments.find {|node| node.type == :block_pass }
  block = method_type.block
  BlockPassArg.new(node: node, block: block)
end

def each

def each
  if block_given?
    errors = []
    positional_count = 0
    positional_arg.tap do |args|
      while (value, args = args.next())
        yield value
        case value
        when PositionalArgs::SplatArg
          type = value.type
          case type
          when nil
            raise
          when AST::Types::Tuple
            ts, args = args.consume(type.types.size, node: value.node)
            case ts
            when Array
              ty = AST::Types::Tuple.new(types: ts.map(&:type))
              yield PositionalArgs::NodeTypePair.new(node: value.node, type: ty)
            when PositionalArgs::UnexpectedArg
              errors << ts
              yield ts
            end
          else
            if t = args.uniform_type
              args.following_args.each do |node|
                yield PositionalArgs::NodeTypePair.new(node: node, type: t)
              end
            else
              args.following_args.each do |node|
                arg = PositionalArgs::UnexpectedArg.new(node: node)
                yield arg
                errors << arg
              end
            end
            break
          end
        when PositionalArgs::UnexpectedArg, PositionalArgs::MissingArg
          errors << value
        end
      end
    end
    keyword_args.tap do |args|
      while (a, args = args.next)
        case a
        when KeywordArgs::MissingKeyword
          errors << a
        when KeywordArgs::UnexpectedKeyword
          errors << a
        end
        yield a
        case a
        when KeywordArgs::SplatArg
          case type = a.type
          when nil
            raise
          when AST::Types::Record
            keys = type.elements.keys
            ts, args = args.consume_keys(keys, node: a.node)
            case ts
            when KeywordArgs::UnexpectedKeyword
              yield ts
              errors << ts
            when Array
              record = AST::Types::Record.new(elements: Hash[keys.zip(ts)])
              yield KeywordArgs::ArgTypePairs.new(pairs: [[a.node, record]])
            end
          else
            args = args.update(index: args.index + 1)
            if args.rest_type
              type = AST::Builtin::Hash.instance_type(AST::Builtin::Symbol.instance_type, args.possible_value_type)
              yield KeywordArgs::ArgTypePairs.new(pairs: [[a.node, type]])
            else
              yield KeywordArgs::UnexpectedKeyword.new(keyword: nil, node: a.node)
            end
          end
        end
      end
    end
    pass = block_pass_arg
    # if pass.node
    #   yield pass
    # end
    diagnostics = []
    missing_keywords = []
    errors.each do |error|
      case error
      when KeywordArgs::UnexpectedKeyword
        diagnostics << Diagnostic::Ruby::UnexpectedKeywordArgument.new(
          node: error.node,
          method_type: method_type,
          method_name: method_name
        )
      when KeywordArgs::MissingKeyword
        missing_keywords.push(*error.keywords)
      when PositionalArgs::UnexpectedArg
        diagnostics << Diagnostic::Ruby::UnexpectedPositionalArgument.new(
          node: error.node,
          method_type: method_type,
          method_name: method_name
        )
      when PositionalArgs::MissingArg
        diagnostics << Diagnostic::Ruby::InsufficientPositionalArguments.new(
          node: node,
          method_name: method_name,
          method_type: method_type
        )
      end
    end
    unless missing_keywords.empty?
      diagnostics << Diagnostic::Ruby::InsufficientKeywordArguments.new(
        node: node,
        method_name: method_name,
        method_type: method_type,
        missing_keywords: missing_keywords
      )
    end
    diagnostics
  else
    enum_for :each
  end
end

def initialize(node:, arguments:, method_name:, method_type:)

def initialize(node:, arguments:, method_name:, method_type:)
  @node = node
  @arguments = arguments
  @method_type = method_type
  @method_name = method_name
end

def keyword_args

def keyword_args
  KeywordArgs.new(
    kwarg_nodes: kwargs_node&.children || [],
    keyword_params: keyword_params
  )
end

def keyword_params

def keyword_params
  method_type.type.params.keyword_params
end

def kwargs_node

def kwargs_node
  unless keyword_params.empty?
    arguments.find {|node| node.type == :kwargs }
  end
end

def positional_arg

def positional_arg
  args = if keyword_params.empty?
           arguments.take_while {|node| node.type != :block_pass }
         else
           arguments.take_while {|node| node.type != :kwargs && node.type != :block_pass }
         end
  PositionalArgs.new(args: args, index: 0, positional_params: positional_params)
end

def positional_params

def positional_params
  method_type.type.params.positional_params
end