lib/rubocop/cop/lint/unused_block_argument.rb
# frozen_string_literal: true
module RuboCop
module Cop
module Lint
# Checks for unused block arguments.
#
# @example
# # bad
# do_something do |used, unused|
# puts used
# end
#
# do_something do |bar|
# puts :foo
# end
#
# define_method(:foo) do |bar|
# puts :baz
# end
#
# # good
# do_something do |used, _unused|
# puts used
# end
#
# do_something do
# puts :foo
# end
#
# define_method(:foo) do |_bar|
# puts :baz
# end
#
# @example IgnoreEmptyBlocks: true (default)
# # good
# do_something { |unused| }
#
# @example IgnoreEmptyBlocks: false
# # bad
# do_something { |unused| }
#
# @example AllowUnusedKeywordArguments: false (default)
# # bad
# do_something do |unused: 42|
# foo
# end
#
# @example AllowUnusedKeywordArguments: true
# # good
# do_something do |unused: 42|
# foo
# end
#
class UnusedBlockArgument < Base
include UnusedArgument
extend AutoCorrector
def self.joining_forces
VariableForce
end
private
def autocorrect(corrector, node)
UnusedArgCorrector.correct(corrector, processed_source, node)
end
def check_argument(variable)
return if allowed_block?(variable) ||
allowed_keyword_argument?(variable) ||
used_block_local?(variable)
super
end
def used_block_local?(variable)
variable.explicit_block_local_variable? && !variable.assignments.empty?
end
def allowed_block?(variable)
!variable.block_argument? || (ignore_empty_blocks? && empty_block?(variable))
end
def allowed_keyword_argument?(variable)
variable.keyword_argument? && allow_unused_keyword_arguments?
end
def message(variable)
message = "Unused #{variable_type(variable)} - `#{variable.name}`."
if variable.explicit_block_local_variable?
message
else
augment_message(message, variable)
end
end
def augment_message(message, variable)
scope = variable.scope
all_arguments = scope.variables.each_value.select(&:block_argument?)
augmentation = if scope.node.lambda?
message_for_lambda(variable, all_arguments)
else
message_for_normal_block(variable, all_arguments)
end
[message, augmentation].join(' ')
end
def variable_type(variable)
if variable.explicit_block_local_variable?
'block local variable'
else
'block argument'
end
end
def message_for_normal_block(variable, all_arguments)
if all_arguments.none?(&:referenced?) && !define_method_call?(variable)
if all_arguments.count > 1
"You can omit all the arguments if you don't care about them."
else
"You can omit the argument if you don't care about it."
end
else
message_for_underscore_prefix(variable)
end
end
def message_for_lambda(variable, all_arguments)
message = message_for_underscore_prefix(variable)
if all_arguments.none?(&:referenced?)
proc_message = 'Also consider using a proc without arguments ' \
'instead of a lambda if you want it ' \
"to accept any arguments but don't care about them."
end
[message, proc_message].compact.join(' ')
end
def message_for_underscore_prefix(variable)
"If it's necessary, use `_` or `_#{variable.name}` " \
"as an argument name to indicate that it won't be used."
end
def define_method_call?(variable)
call, = *variable.scope.node
_, method, = *call
method == :define_method
end
def empty_block?(variable)
_send, _args, body = *variable.scope.node
body.nil?
end
def allow_unused_keyword_arguments?
cop_config['AllowUnusedKeywordArguments']
end
def ignore_empty_blocks?
cop_config['IgnoreEmptyBlocks']
end
end
end
end
end