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)
autocorrected.
The default variable name is ‘block`. If the name is already in use, it will not be
You can specify the block variable name for autocorrection with `BlockForwardingName`.
It also supports the opposite style by alternative `explicit` option.
by `do_something(&)`.
This cop identifies places where `do_something(&block)` can be replaced
In Ruby 3.1, anonymous block forwarding has been added.
def self.autocorrect_incompatible_with
def self.autocorrect_incompatible_with [Lint::AmbiguousOperator, Style::ArgumentsForwarding] 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)
foo.rb: foo.rb:4: anonymous block parameter is also used within block (SyntaxError)
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [x86_64-darwin22]
$ ruby -vc foo.rb
end
end
bar(&)
block_method do
def foo(&)
# foo.rb
Prevents the following syntax error:
def invalidates_syntax?(block_pass_node) block_pass_node.each_ancestor(:block, :numblock).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_descendant(: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