lib/rubocop/cop/style/redundant_self_assignment_branch.rb
# frozen_string_literal: true
module RuboCop
module Cop
module Style
# Checks for places where conditional branch makes redundant self-assignment.
#
# It only detects local variable because it may replace state of instance variable,
# class variable, and global variable that have state across methods with `nil`.
#
# @example
#
# # bad
# foo = condition ? bar : foo
#
# # good
# foo = bar if condition
#
# # bad
# foo = condition ? foo : bar
#
# # good
# foo = bar unless condition
#
class RedundantSelfAssignmentBranch < Base
include RangeHelp
extend AutoCorrector
MSG = 'Remove the self-assignment branch.'
# @!method bad_method?(node)
def_node_matcher :bad_method?, <<~PATTERN
(send nil? :bad_method ...)
PATTERN
def on_lvasgn(node)
variable, expression = *node
return unless use_if_and_else_branch?(expression)
if_branch = expression.if_branch
else_branch = expression.else_branch
return if inconvertible_to_modifier?(if_branch, else_branch)
if self_assign?(variable, if_branch)
register_offense(expression, if_branch, else_branch, 'unless')
elsif self_assign?(variable, else_branch)
register_offense(expression, else_branch, if_branch, 'if')
end
end
private
def use_if_and_else_branch?(expression)
return false unless expression&.if_type?
!expression.ternary? || !expression.else?
end
def inconvertible_to_modifier?(if_branch, else_branch)
multiple_statements?(if_branch) || multiple_statements?(else_branch) ||
(else_branch.respond_to?(:elsif?) && else_branch.elsif?)
end
def multiple_statements?(branch)
return false unless branch&.begin_type?
!branch.children.empty?
end
def self_assign?(variable, branch)
variable.to_s == branch&.source
end
def register_offense(if_node, offense_branch, opposite_branch, keyword)
add_offense(offense_branch) do |corrector|
assignment_value = opposite_branch ? opposite_branch.source : 'nil'
replacement = "#{assignment_value} #{keyword} #{if_node.condition.source}"
if opposite_branch.respond_to?(:heredoc?) && opposite_branch.heredoc?
replacement += opposite_branch.loc.heredoc_end.with(
begin_pos: opposite_branch.source_range.end_pos
).source
end
corrector.replace(if_node, replacement)
end
end
end
end
end
end