class RubbyCop::Cop::Style::ConditionalAssignment

Check for `if` and `case` statements where each branch is used for assignment to the same variable when using the return of the condition can be used instead.

@example

EnforcedStyle: assign_to_condition

# bad
if foo
  bar = 1
else
  bar = 2
end

case foo
when 'a'
  bar += 1
else
  bar += 2
end

if foo
  some_method
  bar = 1
else
  some_other_method
  bar = 2
end

# good
bar = if foo
        1
      else
        2
      end

bar += case foo
       when 'a'
         1
       else
         2
       end

bar << if foo
         some_method
         1
       else
         some_other_method
         2
       end

EnforcedStyle: assign_inside_condition
# bad
bar = if foo
        1
      else
        2
      end

bar += case foo
       when 'a'
         1
       else
         2
       end

bar << if foo
         some_method
         1
       else
         some_other_method
         2
       end

# good
if foo
  bar = 1
else
  bar = 2
end

case foo
when 'a'
  bar += 1
else
  bar += 2
end

if foo
  some_method
  bar = 1
else
  some_other_method
  bar = 2
end

Constants

ASSIGNMENT_TYPES
ASSIGN_TO_CONDITION_MSG
ENABLED
INDENTATION_WIDTH
LINE_LENGTH
MAX
MSG
SINGLE_LINE_CONDITIONS_ONLY
VARIABLE_ASSIGNMENT_TYPES
WIDTH

Public Instance Methods

on_case(node) click to toggle source
# File lib/rubbycop/cop/style/conditional_assignment.rb, line 257
def on_case(node)
  return unless style == :assign_to_condition
  return unless node.else_branch

  when_branches = expand_when_branches(node.when_branches)
  branches = [*when_branches, node.else_branch]

  check_node(node, branches)
end
on_if(node) click to toggle source
# File lib/rubbycop/cop/style/conditional_assignment.rb, line 243
def on_if(node)
  return unless style == :assign_to_condition
  return if node.elsif?

  else_branch = node.else_branch
  elsif_branches, else_branch = expand_elses(else_branch)

  return unless else_branch

  branches = [node.if_branch, *elsif_branches, else_branch]

  check_node(node, branches)
end
on_send(node) click to toggle source
# File lib/rubbycop/cop/style/conditional_assignment.rb, line 237
def on_send(node)
  return unless assignment_type?(node)

  check_assignment_to_condition(node)
end

Private Instance Methods

allowed_single_line?(branches) click to toggle source
# File lib/rubbycop/cop/style/conditional_assignment.rb, line 289
def allowed_single_line?(branches)
  single_line_conditions_only? && branches.any?(&:begin_type?)
end
allowed_statements?(branches) click to toggle source
# File lib/rubbycop/cop/style/conditional_assignment.rb, line 359
def allowed_statements?(branches)
  return false unless branches.all?

  statements = branches.map { |branch| tail(branch) }

  lhs_all_match?(statements) && statements.none?(&:masgn_type?) &&
    assignment_types_match?(*statements)
end
allowed_ternary?(assignment) click to toggle source
# File lib/rubbycop/cop/style/conditional_assignment.rb, line 285
def allowed_ternary?(assignment)
  assignment.if_type? && assignment.ternary? && !include_ternary?
end
assignment_node(node) click to toggle source
# File lib/rubbycop/cop/style/conditional_assignment.rb, line 301
def assignment_node(node)
  *_variable, assignment = *node

  if assignment.begin_type? && assignment.children.one?
    assignment, = *assignment
  end

  assignment
end
assignment_types_match?(*nodes) click to toggle source
# File lib/rubbycop/cop/style/conditional_assignment.rb, line 344
def assignment_types_match?(*nodes)
  return unless assignment_type?(nodes.first)

  nodes.map(&:type).uniq.one?
end
autocorrect(node) click to toggle source
# File lib/rubbycop/cop/style/conditional_assignment.rb, line 293
def autocorrect(node)
  if assignment_type?(node)
    move_assignment_inside_condition(node)
  else
    move_assignment_outside_condition(node)
  end
end
check_assignment_to_condition(node) click to toggle source
# File lib/rubbycop/cop/style/conditional_assignment.rb, line 269
def check_assignment_to_condition(node)
  return unless style == :assign_inside_condition
  return unless assignment_rhs_exist?(node)
  ignore_node(node)

  assignment = assignment_node(node)
  return unless condition?(assignment)
  return if allowed_ternary?(assignment)

  _condition, *branches, else_branch = *assignment
  return unless else_branch # empty else
  return if allowed_single_line?([*branches, else_branch])

  add_offense(node, :expression, ASSIGN_TO_CONDITION_MSG)
end
check_node(node, branches) click to toggle source
# File lib/rubbycop/cop/style/conditional_assignment.rb, line 350
def check_node(node, branches)
  return if allowed_ternary?(node)
  return unless allowed_statements?(branches)
  return if allowed_single_line?(branches)
  return if correction_exceeds_line_limit?(node, branches)

  add_offense(node, :expression)
end
correction_exceeds_line_limit?(node, branches) click to toggle source

If `Metrics/LineLength` is enabled, we do not want to introduce an offense by auto-correcting this cop. Find the max configured line length. Find the longest line of condition. Remove the assignment from lines that contain the offending assignment because after correcting, this will not be on the line anymore. Check if the length of the longest line + the length of the corrected assignment is greater than the max configured line length

# File lib/rubbycop/cop/style/conditional_assignment.rb, line 375
def correction_exceeds_line_limit?(node, branches)
  return false unless line_length_cop_enabled?

  assignment = lhs(tail(branches[0]))

  longest_rhs_exceeds_line_limit?(branches, assignment) ||
    longest_line_exceeds_line_limit?(node, assignment)
end
include_ternary?() click to toggle source
# File lib/rubbycop/cop/style/conditional_assignment.rb, line 428
def include_ternary?
  cop_config['IncludeTernaryExpressions']
end
indentation_width() click to toggle source
# File lib/rubbycop/cop/style/conditional_assignment.rb, line 420
def indentation_width
  config.for_cop(INDENTATION_WIDTH)[WIDTH] || 2
end
lhs_all_match?(branches) click to toggle source
# File lib/rubbycop/cop/style/conditional_assignment.rb, line 339
def lhs_all_match?(branches)
  first_lhs = lhs(branches.first)
  branches.all? { |branch| lhs(branch) == first_lhs }
end
line_length_cop_enabled?() click to toggle source
# File lib/rubbycop/cop/style/conditional_assignment.rb, line 412
def line_length_cop_enabled?
  config.for_cop(LINE_LENGTH)[ENABLED]
end
longest_line(node, assignment) click to toggle source
# File lib/rubbycop/cop/style/conditional_assignment.rb, line 396
def longest_line(node, assignment)
  assignment_regex = /#{Regexp.escape(assignment).gsub(' ', '\s*')}/
  lines = node.source.lines.map do |line|
    line.chomp.sub(assignment_regex, '')
  end
  longest_line = lines.max_by(&:length)
  longest_line + assignment
end
longest_line_exceeds_line_limit?(node, assignment) click to toggle source
# File lib/rubbycop/cop/style/conditional_assignment.rb, line 388
def longest_line_exceeds_line_limit?(node, assignment)
  longest_line(node, assignment).length > max_line_length
end
longest_rhs(branches) click to toggle source
# File lib/rubbycop/cop/style/conditional_assignment.rb, line 405
def longest_rhs(branches)
  line_lengths = branches.flat_map do |branch|
    branch.children.last.source.split("\n").map(&:length)
  end
  line_lengths.max
end
longest_rhs_exceeds_line_limit?(branches, assignment) click to toggle source
# File lib/rubbycop/cop/style/conditional_assignment.rb, line 384
def longest_rhs_exceeds_line_limit?(branches, assignment)
  longest_rhs_full_length(branches, assignment) > max_line_length
end
longest_rhs_full_length(branches, assignment) click to toggle source
# File lib/rubbycop/cop/style/conditional_assignment.rb, line 392
def longest_rhs_full_length(branches, assignment)
  longest_rhs(branches) + indentation_width + assignment.length
end
max_line_length() click to toggle source
# File lib/rubbycop/cop/style/conditional_assignment.rb, line 416
def max_line_length
  config.for_cop(LINE_LENGTH)[MAX]
end
move_assignment_inside_condition(node) click to toggle source
# File lib/rubbycop/cop/style/conditional_assignment.rb, line 323
def move_assignment_inside_condition(node)
  *_assignment, condition = *node

  if ternary_condition?(condition)
    TernaryCorrector.move_assignment_inside_condition(node)
  elsif condition.case_type?
    CaseCorrector.move_assignment_inside_condition(node)
  elsif condition.if_type?
    IfCorrector.move_assignment_inside_condition(node)
  end
end
move_assignment_outside_condition(node) click to toggle source
# File lib/rubbycop/cop/style/conditional_assignment.rb, line 311
def move_assignment_outside_condition(node)
  if node.case_type?
    CaseCorrector.correct(self, node)
  elsif node.ternary?
    TernaryCorrector.correct(node)
  elsif node.if?
    IfCorrector.correct(self, node)
  elsif node.unless?
    UnlessCorrector.correct(self, node)
  end
end
single_line_conditions_only?() click to toggle source
# File lib/rubbycop/cop/style/conditional_assignment.rb, line 424
def single_line_conditions_only?
  cop_config[SINGLE_LINE_CONDITIONS_ONLY]
end
ternary_condition?(node) click to toggle source
# File lib/rubbycop/cop/style/conditional_assignment.rb, line 335
def ternary_condition?(node)
  [node, node.children.first].any? { |n| n.if_type? && n.ternary? }
end