class RuboCop::Cop::Naming::BlockForwarding

In Ruby 3.1, anonymous block forwarding has been added.

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

It also supports the opposite style by alternative ‘explicit` option. You can specify the block variable name for autocorrection with `BlockForwardingName`. The default variable name is `block`. If the name is already in use, it will not be autocorrected.

@example EnforcedStyle: anonymous (default)

# bad
def foo(&block)
  bar(&block)
end

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

@example EnforcedStyle: explicit

# bad
def foo(&)
  bar(&)
end

# good
def foo(&block)
  bar(&block)
end

Constants

MSG

Public Class Methods

autocorrect_incompatible_with() click to toggle source
# File lib/rubocop/cop/naming/block_forwarding.rb, line 50
def self.autocorrect_incompatible_with
  [Lint::AmbiguousOperator, Style::ArgumentsForwarding, Style::ExplicitBlockArgument]
end

Public Instance Methods

on_def(node) click to toggle source
# File lib/rubocop/cop/naming/block_forwarding.rb, line 54
def on_def(node)
  return if node.arguments.empty?

  last_argument = node.last_argument
  return if expected_block_forwarding_style?(node, last_argument)

  forwarded_args = node.each_descendant(:block_pass).with_object([]) do |block_pass, result|
    return nil if invalidates_syntax?(block_pass)
    next unless block_argument_name_matched?(block_pass, last_argument)

    result << block_pass
  end

  forwarded_args.each do |forwarded_arg|
    register_offense(forwarded_arg, node)
  end

  register_offense(last_argument, node)
end
Also aliased as: on_defs
on_defs(node)
Alias for: on_def

Private Instance Methods

anonymous_block_argument?(node) click to toggle source
# File lib/rubocop/cop/naming/block_forwarding.rb, line 114
def anonymous_block_argument?(node)
  node.blockarg_type? && node.name.nil?
end
block_argument_name_matched?(block_pass_node, last_argument) click to toggle source
# File lib/rubocop/cop/naming/block_forwarding.rb, line 87
def block_argument_name_matched?(block_pass_node, last_argument)
  return false if block_pass_node.children.first&.sym_type?

  last_argument.source == block_pass_node.source
end
block_forwarding_name() click to toggle source
# File lib/rubocop/cop/naming/block_forwarding.rb, line 146
def block_forwarding_name
  cop_config.fetch('BlockForwardingName', 'block')
end
expected_block_forwarding_style?(node, last_argument) click to toggle source
# File lib/rubocop/cop/naming/block_forwarding.rb, line 77
def expected_block_forwarding_style?(node, last_argument)
  if style == :anonymous
    !explicit_block_argument?(last_argument) ||
      use_kwarg_in_method_definition?(node) ||
      use_block_argument_as_local_variable?(node, last_argument.source[1..])
  else
    !anonymous_block_argument?(last_argument)
  end
end
explicit_block_argument?(node) click to toggle source
# File lib/rubocop/cop/naming/block_forwarding.rb, line 118
def explicit_block_argument?(node)
  node.blockarg_type? && !node.name.nil?
end
invalidates_syntax?(block_pass_node) click to toggle source

Prevents the following syntax error:

# foo.rb def foo(&)

block_method do
  bar(&)
end

end

$ ruby -vc foo.rb ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [x86_64-darwin22] foo.rb: foo.rb:4: anonymous block parameter is also used within block (SyntaxError)

# File lib/rubocop/cop/naming/block_forwarding.rb, line 106
def invalidates_syntax?(block_pass_node)
  block_pass_node.each_ancestor(:block, :numblock).any?
end
register_offense(block_argument, node) click to toggle source
# File lib/rubocop/cop/naming/block_forwarding.rb, line 122
def register_offense(block_argument, node)
  add_offense(block_argument, message: format(MSG, style: style)) do |corrector|
    if style == :anonymous
      corrector.replace(block_argument, '&')

      arguments = block_argument.parent

      add_parentheses(arguments, corrector) unless arguments.parenthesized_call?
    else
      unless use_block_argument_as_local_variable?(node, block_forwarding_name)
        corrector.replace(block_argument, "&#{block_forwarding_name}")
      end
    end
  end
end
use_block_argument_as_local_variable?(node, last_argument) click to toggle source
# File lib/rubocop/cop/naming/block_forwarding.rb, line 138
def use_block_argument_as_local_variable?(node, last_argument)
  return false if node.body.nil?

  node.body.each_descendant(:lvar, :lvasgn).any? do |lvar|
    !lvar.parent.block_pass_type? && lvar.node_parts[0].to_s == last_argument
  end
end
use_kwarg_in_method_definition?(node) click to toggle source
# File lib/rubocop/cop/naming/block_forwarding.rb, line 110
def use_kwarg_in_method_definition?(node)
  node.arguments.each_descendant(:kwarg, :kwoptarg).any?
end