class RuboCop::Cop::Naming::BlockForwarding


end
bar(&block)
def foo(&block)
# good
end
bar(&)
def foo(&)
# bad
@example EnforcedStyle: explicit
end
bar(&)
def foo(&)
# good
end
bar(&block)
def foo(&block)
# bad
@example EnforcedStyle: anonymous (default)

—-
end
block_method { bar(&block) }
# Using an anonymous block would be a syntax error on Ruby 3.3.0
def foo(&block)<br>—-<br>

def self.autocorrect_incompatible_with

def self.autocorrect_incompatible_with
  [Lint::AmbiguousOperator, Style::ArgumentsForwarding, Style::ExplicitBlockArgument]
end

def anonymous_block_argument?(node)

def anonymous_block_argument?(node)
  node.blockarg_type? && node.name.nil?
end

def block_argument_name_matched?(block_pass_node, last_argument)

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

def block_forwarding_name

def block_forwarding_name
  cop_config.fetch('BlockForwardingName', 'block')
end

def expected_block_forwarding_style?(node, last_argument)

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

def explicit_block_argument?(node)

def explicit_block_argument?(node)
  node.blockarg_type? && !node.name.nil?
end

def invalidates_syntax?(block_pass_node)

We disallow this also for earlier Ruby versions so that code is forwards compatible.
was a syntax error in unambiguous cases: https://bugs.ruby-lang.org/issues/20090
Ruby 3.3.0 had a bug where accessing an anonymous block argument inside of a block
def invalidates_syntax?(block_pass_node)
  target_ruby_version <= 3.3 && block_pass_node.each_ancestor(:any_block).any?
end

def on_def(node)

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

def register_offense(block_argument, node)

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

def use_block_argument_as_local_variable?(node, last_argument)

def use_block_argument_as_local_variable?(node, last_argument)
  return false if node.body.nil?
  node.body.each_node(:lvar, :lvasgn).any? do |lvar|
    !lvar.parent.block_pass_type? && lvar.node_parts[0].to_s == last_argument
  end
end

def use_kwarg_in_method_definition?(node)

def use_kwarg_in_method_definition?(node)
  node.arguments.each_descendant(:kwarg, :kwoptarg).any?
end