class RuboCop::Cop::Style::ArgumentsForwarding::SendNodeClassifier

Classifies send nodes for possible rest/kwrest/all (including block) forwarding.

def allow_offense_for_no_block?

def allow_offense_for_no_block?
  !@config.fetch(:allow_only_rest_arguments)
end

def any_arg_referenced?

def any_arg_referenced?
  referenced_rest_arg? || referenced_kwrest_arg? || referenced_block_arg?
end

def arguments

def arguments
  @send_node.arguments
end

def can_forward_all?

def can_forward_all?
  return false if any_arg_referenced?
  return false if ruby_32_missing_rest_or_kwest?
  return false unless offensive_block_forwarding?
  return false if forward_additional_kwargs?
  no_additional_args? || (target_ruby_version >= 3.0 && no_post_splat_args?)
end

def classification

def classification
  return nil unless forwarded_rest_arg || forwarded_kwrest_arg
  if can_forward_all?
    :all
  elsif target_ruby_version >= 3.2
    :rest_or_kwrest
  end
end

def forward_additional_kwargs?

def forward_additional_kwargs?
  return false unless forwarded_kwrest_arg
  !forwarded_kwrest_arg.parent.children.one?
end

def forwarded_block_arg

def forwarded_block_arg
  return nil if referenced_block_arg?
  arguments.find { |arg| forwarded_block_arg?(arg, @block_arg_name) }
end

def forwarded_kwrest_arg

def forwarded_kwrest_arg
  return nil if referenced_kwrest_arg?
  arguments.filter_map { |arg| extract_forwarded_kwrest_arg(arg, @kwrest_arg_name) }.first
end

def forwarded_rest_and_kwrest_args

def forwarded_rest_and_kwrest_args
  forwarded_rest_arg && forwarded_kwrest_arg
end

def forwarded_rest_arg

def forwarded_rest_arg
  return nil if referenced_rest_arg?
  arguments.find { |arg| forwarded_rest_arg?(arg, @rest_arg_name) }
end

def initialize(def_node, send_node, referenced_lvars, forwardable_args, **config)

def initialize(def_node, send_node, referenced_lvars, forwardable_args, **config)
  @def_node = def_node
  @send_node = send_node
  @referenced_lvars = referenced_lvars
  @rest_arg, @kwrest_arg, @block_arg = *forwardable_args
  @rest_arg_name, @kwrest_arg_name, @block_arg_name =
    *forwardable_args.map { |a| a&.name }
  @config = config
end

def no_additional_args?

def no_additional_args?
  forwardable_count = [@rest_arg, @kwrest_arg, @block_arg].compact.size
  @def_node.arguments.size == forwardable_count &&
    @send_node.arguments.size == forwardable_count
end

def no_post_splat_args?

def no_post_splat_args?
  splat_index = arguments.index(forwarded_rest_arg)
  arg_after_splat = arguments[splat_index + 1]
  [nil, :hash, :block_pass].include?(arg_after_splat&.type)
end

def offensive_block_forwarding?

def offensive_block_forwarding?
  @block_arg ? forwarded_block_arg : allow_offense_for_no_block?
end

def referenced_block_arg?

def referenced_block_arg?
  @referenced_lvars.include?(@block_arg_name)
end

def referenced_kwrest_arg?

def referenced_kwrest_arg?
  @referenced_lvars.include?(@kwrest_arg_name)
end

def referenced_rest_arg?

def referenced_rest_arg?
  @referenced_lvars.include?(@rest_arg_name)
end

def ruby_32_missing_rest_or_kwest?

def ruby_32_missing_rest_or_kwest?
  target_ruby_version >= 3.2 && !forwarded_rest_and_kwrest_args
end

def target_ruby_version

def target_ruby_version
  @config.fetch(:target_ruby_version)
end