class RuboCop::Cop::Style::ConditionalAssignment
end
bar = 2
some_other_method
else
bar = 1
some_method
if foo
end
bar += 2
else
bar += 1
when ‘a’
case foo
end
bar = 2
else
bar = 1
if foo
# good
end
2
some_other_method
else
1
some_method
bar << if foo
end
2
else
1
when ‘a’
bar += case foo
end
2
else
1
bar = if foo
# bad
EnforcedStyle: assign_inside_condition
end
2
some_other_method
else
1
some_method
bar << if foo
end
2
else
1
when ‘a’
bar += case foo
end
2
else
1
bar = if foo
# good
end
bar = 2
some_other_method
else
bar = 1
some_method
if foo
end
bar += 2
else
bar += 1
when ‘a’
case foo
end
bar = 2
else
bar = 1
if foo
# bad
EnforcedStyle: assign_to_condition
@example
condition can be used instead.
assignment to the same variable when using the return of the
Check for ‘if` and `case` statements where each branch is used for
def allowed_statements?(branches)
def allowed_statements?(branches) return false unless branches.all? statements = branches.map { |branch| tail(branch) } lhs_all_match?(statements) && !statements.any?(&:masgn_type?) && assignment_types_match?(*statements) end
def allowed_ternary?(assignment)
def allowed_ternary?(assignment) assignment.if_type? && assignment.ternary? && !include_ternary? end
def assignment_node(node)
def assignment_node(node) *_variable, assignment = *node if assignment.begin_type? && assignment.children.one? assignment, = *assignment end assignment end
def assignment_type?(branch)
The shovel operator `<<` does not have its own type. It is a `send`
def assignment_type?(branch) return true if ASSIGNMENT_TYPES.include?(branch.type) if branch.send_type? return true if METHODS.include?(branch.method_name) || branch.method_name.to_s.end_with?(EQUAL) end false end
def assignment_types_match?(*nodes)
def assignment_types_match?(*nodes) return unless assignment_type?(nodes.first) nodes.map(&:type).uniq.one? end
def autocorrect(node)
def autocorrect(node) if assignment_type?(node) move_assignment_inside_condition(node) else move_assignment_outside_condition(node) end end
def check_assignment_to_condition(node)
def check_assignment_to_condition(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 single_line_conditions_only? && [*branches, else_branch].any?(&:begin_type?) add_offense(node, :expression, ASSIGN_TO_CONDITION_MSG) end
def check_node(node, branches)
def check_node(node, branches) return if allowed_ternary?(node) return unless allowed_statements?(branches) return if single_line_conditions_only? && branches.any?(&:begin_type?) return if correction_exceeds_line_limit?(node, branches) add_offense(node, :expression) end
def condition?(node)
def condition?(node) CONDITION_TYPES.include?(node.type) end
def correction_exceeds_line_limit?(node, branches)
of the longest line + the length of the corrected assignment is
correcting, this will not be on the line anymore. Check if the length
from lines that contain the offending assignment because after
length. Find the longest line of condition. Remove the assignment
offense by auto-correcting this cop. Find the max configured line
If `Metrics/LineLength` is enabled, we do not want to introduce an
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
def include_ternary?
def include_ternary? cop_config['IncludeTernaryExpressions'] end
def indentation_width
def indentation_width config.for_cop(INDENTATION_WIDTH)[WIDTH] || 2 end
def lhs_all_match?(branches)
def lhs_all_match?(branches) first_lhs = lhs(branches.first) branches.all? { |branch| lhs(branch) == first_lhs } end
def line_length_cop_enabled?
def line_length_cop_enabled? config.for_cop(LINE_LENGTH)[ENABLED] end
def longest_line(node, assignment)
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
def longest_line_exceeds_line_limit?(node, assignment)
def longest_line_exceeds_line_limit?(node, assignment) longest_line(node, assignment).length > max_line_length end
def longest_rhs(branches)
def longest_rhs(branches) line_lengths = branches.flat_map do |branch| branch.children.last.source.split("\n").map(&:length) end line_lengths.max end
def longest_rhs_exceeds_line_limit?(branches, assignment)
def longest_rhs_exceeds_line_limit?(branches, assignment) longest_rhs_full_length(branches, assignment) > max_line_length end
def longest_rhs_full_length(branches, assignment)
def longest_rhs_full_length(branches, assignment) longest_rhs(branches) + indentation_width + assignment.length end
def max_line_length
def max_line_length config.for_cop(LINE_LENGTH)[MAX] end
def move_assignment_inside_condition(node)
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
def move_assignment_outside_condition(node)
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
def on_case(node)
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
def on_if(node)
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
def on_send(node)
def on_send(node) return unless assignment_type?(node) return unless style == :assign_inside_condition return unless assignment_rhs_exist?(node) check_assignment_to_condition(node) end
def single_line_conditions_only?
def single_line_conditions_only? cop_config[SINGLE_LINE_CONDITIONS_ONLY] end
def ternary_condition?(node)
def ternary_condition?(node) [node, node.children.first].any? { |n| n.if_type? && n.ternary? } end