class RuboCop::Cop::Style::ArgumentsForwarding

In Ruby 2.7, arguments forwarding has been added.

This cop identifies places where ‘do_something(*args, &block)` can be replaced by `do_something(…)`.

In Ruby 3.2, anonymous args/kwargs forwarding has been added.

This cop also identifies places where ‘use_args(*args)`/`use_kwargs(**kwargs)` can be replaced by `use_args(*)`/`use_kwargs(**)`; if desired, this functionality can be disabled by setting UseAnonymousForwarding: false.

@example

# bad
def foo(*args, &block)
  bar(*args, &block)
end

# bad
def foo(*args, **kwargs, &block)
  bar(*args, **kwargs, &block)
end

# good
def foo(...)
  bar(...)
end

@example UseAnonymousForwarding: true (default, only relevant for Ruby >= 3.2)

# bad
def foo(*args, **kwargs)
  args_only(*args)
  kwargs_only(**kwargs)
end

# good
def foo(*, **)
  args_only(*)
  kwargs_only(**)
end

@example UseAnonymousForwarding: false (only relevant for Ruby >= 3.2)

# good
def foo(*args, **kwargs)
  args_only(*args)
  kwargs_only(**kwargs)
end

@example AllowOnlyRestArgument: true (default, only relevant for Ruby < 3.2)

# good
def foo(*args)
  bar(*args)
end

def foo(**kwargs)
  bar(**kwargs)
end

@example AllowOnlyRestArgument: false (only relevant for Ruby < 3.2)

# bad
# The following code can replace the arguments with `...`,
# but it will change the behavior. Because `...` forwards block also.
def foo(*args)
  bar(*args)
end

def foo(**kwargs)
  bar(**kwargs)
end

Constants

ARGS_MSG
FORWARDING_LVAR_TYPES
FORWARDING_MSG
KWARGS_MSG

Public Instance Methods

on_def(node) click to toggle source
# File lib/rubocop/cop/style/arguments_forwarding.rb, line 88
def on_def(node)
  return unless node.body

  forwardable_args = extract_forwardable_args(node.arguments)

  send_classifications = classify_send_nodes(
    node,
    node.each_descendant(:send).to_a,
    non_splat_or_block_pass_lvar_references(node.body),
    forwardable_args
  )

  return if send_classifications.empty?

  if only_forwards_all?(send_classifications)
    add_forward_all_offenses(node, send_classifications)
  elsif target_ruby_version >= 3.2
    add_post_ruby_32_offenses(node, send_classifications, forwardable_args)
  end
end
Also aliased as: on_defs
on_defs(node)
Alias for: on_def

Private Instance Methods

add_forward_all_offenses(node, send_classifications) click to toggle source
# File lib/rubocop/cop/style/arguments_forwarding.rb, line 121
def add_forward_all_offenses(node, send_classifications)
  send_classifications.each_key do |send_node|
    register_forward_all_offense_on_forwarding_method(send_node)
  end

  register_forward_all_offense_on_method_def(node)
end
add_post_ruby_32_offenses(def_node, send_classifications, forwardable_args) click to toggle source
# File lib/rubocop/cop/style/arguments_forwarding.rb, line 129
def add_post_ruby_32_offenses(def_node, send_classifications, forwardable_args)
  return unless use_anonymous_forwarding?

  rest_arg, kwrest_arg, _block_arg = *forwardable_args

  send_classifications.each do |send_node, (_c, forward_rest, forward_kwrest)|
    if forward_rest
      register_forward_args_offense(def_node.arguments, rest_arg)
      register_forward_args_offense(send_node, forward_rest)
    end

    if forward_kwrest
      register_forward_kwargs_offense(!forward_rest, def_node.arguments, kwrest_arg)
      register_forward_kwargs_offense(!forward_rest, send_node, forward_kwrest)
    end
  end
end
allow_only_rest_arguments?() click to toggle source
# File lib/rubocop/cop/style/arguments_forwarding.rb, line 231
def allow_only_rest_arguments?
  cop_config.fetch('AllowOnlyRestArgument', true)
end
arguments_range(node) click to toggle source
# File lib/rubocop/cop/style/arguments_forwarding.rb, line 225
def arguments_range(node)
  arguments = node.arguments

  range_between(arguments.first.source_range.begin_pos, arguments.last.source_range.end_pos)
end
classification_and_forwards(def_node, send_node, referenced_lvars, forwardable_args) click to toggle source
# File lib/rubocop/cop/style/arguments_forwarding.rb, line 170
def classification_and_forwards(def_node, send_node, referenced_lvars, forwardable_args)
  classifier = SendNodeClassifier.new(
    def_node,
    send_node,
    referenced_lvars,
    forwardable_args,
    target_ruby_version: target_ruby_version,
    allow_only_rest_arguments: allow_only_rest_arguments?
  )

  classification = classifier.classification

  return unless classification

  [classification, classifier.forwarded_rest_arg, classifier.forwarded_kwrest_arg]
end
classify_send_nodes(def_node, send_nodes, referenced_lvars, forwardable_args) click to toggle source
# File lib/rubocop/cop/style/arguments_forwarding.rb, line 157
def classify_send_nodes(def_node, send_nodes, referenced_lvars, forwardable_args)
  send_nodes.to_h do |send_node|
    classification_and_forwards = classification_and_forwards(
      def_node,
      send_node,
      referenced_lvars,
      forwardable_args
    )

    [send_node, classification_and_forwards]
  end.compact
end
extract_forwardable_args(args) click to toggle source
# File lib/rubocop/cop/style/arguments_forwarding.rb, line 113
def extract_forwardable_args(args)
  [args.find(&:restarg_type?), args.find(&:kwrestarg_type?), args.find(&:blockarg_type?)]
end
non_splat_or_block_pass_lvar_references(body) click to toggle source
# File lib/rubocop/cop/style/arguments_forwarding.rb, line 147
def non_splat_or_block_pass_lvar_references(body)
  body.each_descendant(:lvar, :lvasgn).filter_map do |lvar|
    parent = lvar.parent

    next if lvar.lvar_type? && FORWARDING_LVAR_TYPES.include?(parent.type)

    lvar.children.first
  end.uniq
end
only_forwards_all?(send_classifications) click to toggle source
# File lib/rubocop/cop/style/arguments_forwarding.rb, line 117
def only_forwards_all?(send_classifications)
  send_classifications.each_value.all? { |c, _, _| c == :all }
end
register_forward_all_offense_on_forwarding_method(forwarding_method) click to toggle source
# File lib/rubocop/cop/style/arguments_forwarding.rb, line 207
def register_forward_all_offense_on_forwarding_method(forwarding_method)
  add_offense(arguments_range(forwarding_method), message: FORWARDING_MSG) do |corrector|
    begin_pos = forwarding_method.loc.selector&.end_pos || forwarding_method.loc.dot.end_pos
    range = range_between(begin_pos, forwarding_method.source_range.end_pos)

    corrector.replace(range, '(...)')
  end
end
register_forward_all_offense_on_method_def(method_definition) click to toggle source
# File lib/rubocop/cop/style/arguments_forwarding.rb, line 216
def register_forward_all_offense_on_method_def(method_definition)
  add_offense(arguments_range(method_definition), message: FORWARDING_MSG) do |corrector|
    arguments_range = range_with_surrounding_space(
      method_definition.arguments.source_range, side: :left
    )
    corrector.replace(arguments_range, '(...)')
  end
end
register_forward_args_offense(def_arguments_or_send, rest_arg_or_splat) click to toggle source
# File lib/rubocop/cop/style/arguments_forwarding.rb, line 187
def register_forward_args_offense(def_arguments_or_send, rest_arg_or_splat)
  add_offense(rest_arg_or_splat, message: ARGS_MSG) do |corrector|
    unless parentheses?(def_arguments_or_send)
      add_parentheses(def_arguments_or_send, corrector)
    end

    corrector.replace(rest_arg_or_splat, '*')
  end
end
register_forward_kwargs_offense(add_parens, def_arguments_or_send, kwrest_arg_or_splat) click to toggle source
# File lib/rubocop/cop/style/arguments_forwarding.rb, line 197
def register_forward_kwargs_offense(add_parens, def_arguments_or_send, kwrest_arg_or_splat)
  add_offense(kwrest_arg_or_splat, message: KWARGS_MSG) do |corrector|
    if add_parens && !parentheses?(def_arguments_or_send)
      add_parentheses(def_arguments_or_send, corrector)
    end

    corrector.replace(kwrest_arg_or_splat, '**')
  end
end
use_anonymous_forwarding?() click to toggle source
# File lib/rubocop/cop/style/arguments_forwarding.rb, line 235
def use_anonymous_forwarding?
  cop_config.fetch('UseAnonymousForwarding', false)
end