lib/rubocop/cop/layout/multiline_operation_indentation.rb
# frozen_string_literal: true
module RuboCop
module Cop
module Layout
# Checks the indentation of the right hand side operand in binary operations that
# span more than one line.
#
# The `aligned` style checks that operators are aligned if they are part of an `if` or `while`
# condition, an explicit `return` statement, etc. In other contexts, the second operand should
# be indented regardless of enforced style.
#
# @example EnforcedStyle: aligned (default)
# # bad
# if a +
# b
# something &&
# something_else
# end
#
# # good
# if a +
# b
# something &&
# something_else
# end
#
# @example EnforcedStyle: indented
# # bad
# if a +
# b
# something &&
# something_else
# end
#
# # good
# if a +
# b
# something &&
# something_else
# end
#
class MultilineOperationIndentation < Base
include ConfigurableEnforcedStyle
include Alignment
include MultilineExpressionIndentation
extend AutoCorrector
def on_and(node)
check_and_or(node)
end
def on_or(node)
check_and_or(node)
end
def validate_config
return unless style == :aligned && cop_config['IndentationWidth']
raise ValidationError, 'The `Layout/MultilineOperationIndentation` ' \
'cop only accepts an `IndentationWidth` ' \
'configuration parameter when ' \
'`EnforcedStyle` is `indented`.'
end
private
def autocorrect(corrector, node)
AlignmentCorrector.correct(corrector, processed_source, node, @column_delta)
end
def relevant_node?(node)
return false if node.send_type? && node.unary_operation?
!node.loc.dot # Don't check method calls with dot operator.
end
def check_and_or(node)
lhs, rhs = *node
range = offending_range(node, lhs, rhs.source_range, style)
check(range, node, lhs, rhs.source_range)
end
def offending_range(node, lhs, rhs, given_style)
return false unless begins_its_line?(rhs)
return false if not_for_this_cop?(node)
correct_column = if should_align?(node, rhs, given_style)
node.loc.column
else
indentation(lhs) + correct_indentation(node)
end
@column_delta = correct_column - rhs.column
rhs if @column_delta.nonzero?
end
def should_align?(node, rhs, given_style)
assignment_node = part_of_assignment_rhs(node, rhs)
if assignment_node
assignment_rhs = CheckAssignment.extract_rhs(assignment_node)
return true if begins_its_line?(assignment_rhs.source_range)
end
given_style == :aligned &&
(kw_node_with_special_indentation(node) ||
assignment_node ||
argument_in_method_call(node, :with_or_without_parentheses))
end
def message(node, lhs, rhs)
what = operation_description(node, rhs)
if should_align?(node, rhs, style)
"Align the operands of #{what} spanning multiple lines."
else
used_indentation = rhs.column - indentation(lhs)
"Use #{correct_indentation(node)} (not #{used_indentation}) " \
"spaces for indenting #{what} spanning multiple lines."
end
end
def right_hand_side(send_node)
send_node.first_argument.source_range
end
end
end
end
end