class RuboCop::Cop::Layout::SpaceAroundOperators

Checks that operators have space around them, except for ** which should or shouldn’t have surrounding space depending on configuration. It allows vertical alignment consisting of one or more whitespace around operators.

This cop has ‘AllowForAlignment` option. When `true`, allows most uses of extra spacing if the intent is to align with an operator on the previous or next line, not counting empty lines or comment lines.

@example

# bad
total = 3*4
"apple"+"juice"
my_number = 38/4

# good
total = 3 * 4
"apple" + "juice"
my_number = 38 / 4

@example AllowForAlignment: true (default)

# good
{
  1 =>  2,
  11 => 3
}

@example AllowForAlignment: false

# bad
{
  1 =>  2,
  11 => 3
}

@example EnforcedStyleForExponentOperator: no_space (default)

# bad
a ** b

# good
a**b

@example EnforcedStyleForExponentOperator: space

# bad
a**b

# good
a ** b

@example EnforcedStyleForRationalLiterals: no_space (default)

# bad
1 / 48r

# good
1/48r

@example EnforcedStyleForRationalLiterals: space

# bad
1/48r

# good
1 / 48r

Constants

EXCESSIVE_SPACE
IRREGULAR_METHODS

Public Class Methods

autocorrect_incompatible_with() click to toggle source
# File lib/rubocop/cop/layout/space_around_operators.rb, line 76
def self.autocorrect_incompatible_with
  [Style::SelfAssignment]
end

Public Instance Methods

on_and(node)
Alias for: on_binary
on_and_asgn(node)
Alias for: on_assignment
on_assignment(node) click to toggle source
# File lib/rubocop/cop/layout/space_around_operators.rb, line 117
def on_assignment(node)
  _, rhs, = *node

  return unless rhs

  check_operator(:assignment, node.loc.operator, rhs)
end
on_binary(node) click to toggle source
# File lib/rubocop/cop/layout/space_around_operators.rb, line 133
def on_binary(node)
  _, rhs, = *node

  return unless rhs

  check_operator(:binary, node.loc.operator, rhs)
end
Also aliased as: on_or, on_and, on_class
on_casgn(node) click to toggle source
# File lib/rubocop/cop/layout/space_around_operators.rb, line 125
def on_casgn(node)
  _, _, right, = *node

  return unless right

  check_operator(:assignment, node.loc.operator, right)
end
on_class(node)
Alias for: on_binary
on_cvasgn(node)
Alias for: on_assignment
on_gvasgn(node)
Alias for: on_assignment
on_if(node) click to toggle source
# File lib/rubocop/cop/layout/space_around_operators.rb, line 92
def on_if(node)
  return unless node.ternary?

  check_operator(:if, node.loc.question, node.if_branch)
  check_operator(:if, node.loc.colon, node.else_branch)
end
on_ivasgn(node)
Alias for: on_assignment
on_lvasgn(node)
Alias for: on_assignment
on_masgn(node)
Alias for: on_assignment
on_match_pattern(node) click to toggle source
# File lib/rubocop/cop/layout/space_around_operators.rb, line 149
def on_match_pattern(node)
  return if target_ruby_version < 3.0

  check_operator(:match_pattern, node.loc.operator, node)
end
on_op_asgn(node)
Alias for: on_special_asgn
on_or(node)
Alias for: on_binary
on_or_asgn(node)
Alias for: on_assignment
on_pair(node) click to toggle source
# File lib/rubocop/cop/layout/space_around_operators.rb, line 84
def on_pair(node)
  return unless node.hash_rocket?

  return if hash_table_style? && !node.parent.pairs_on_same_line?

  check_operator(:pair, node.loc.operator, node)
end
on_resbody(node) click to toggle source
# File lib/rubocop/cop/layout/space_around_operators.rb, line 99
def on_resbody(node)
  return unless node.loc.assoc

  _, variable, = *node

  check_operator(:resbody, node.loc.assoc, variable)
end
on_sclass(node) click to toggle source
# File lib/rubocop/cop/layout/space_around_operators.rb, line 80
def on_sclass(node)
  check_operator(:sclass, node.loc.operator, node)
end
on_send(node) click to toggle source
# File lib/rubocop/cop/layout/space_around_operators.rb, line 107
def on_send(node)
  return if rational_literal?(node)

  if node.setter_method?
    on_special_asgn(node)
  elsif regular_operator?(node)
    check_operator(:send, node.loc.selector, node.first_argument)
  end
end
on_special_asgn(node) click to toggle source
# File lib/rubocop/cop/layout/space_around_operators.rb, line 141
def on_special_asgn(node)
  _, _, right, = *node

  return unless right

  check_operator(:special_asgn, node.loc.operator, right)
end
Also aliased as: on_op_asgn

Private Instance Methods

align_hash_cop_config() click to toggle source
# File lib/rubocop/cop/layout/space_around_operators.rb, line 259
def align_hash_cop_config
  config.for_cop('Layout/HashAlignment')
end
autocorrect(corrector, range, right_operand) click to toggle source
# File lib/rubocop/cop/layout/space_around_operators.rb, line 198
def autocorrect(corrector, range, right_operand)
  range_source = range.source

  if range_source.include?('**') && !space_around_exponent_operator?
    corrector.replace(range, '**')
  elsif range_source.include?('/') && !space_around_slash_operator?(right_operand)
    corrector.replace(range, '/')
  elsif range_source.end_with?("\n")
    corrector.replace(range, " #{range_source.strip}\n")
  else
    enclose_operator_with_space(corrector, range)
  end
end
check_operator(type, operator, right_operand) click to toggle source
# File lib/rubocop/cop/layout/space_around_operators.rb, line 179
def check_operator(type, operator, right_operand)
  with_space = range_with_surrounding_space(operator)
  return if with_space.source.start_with?("\n")

  comment = processed_source.comment_at_line(operator.line)
  return if comment && with_space.last_column == comment.loc.column

  offense(type, operator, with_space, right_operand) do |msg|
    add_offense(operator, message: msg) do |corrector|
      autocorrect(corrector, with_space, right_operand)
    end
  end
end
enclose_operator_with_space(corrector, range) click to toggle source
# File lib/rubocop/cop/layout/space_around_operators.rb, line 212
def enclose_operator_with_space(corrector, range)
  operator = range.source

  # If `ForceEqualSignAlignment` is true, `Layout/ExtraSpacing` cop
  # inserts spaces before operator. If `Layout/SpaceAroundOperators` cop
  # inserts a space, it collides and raises the infinite loop error.
  if force_equal_sign_alignment? && !operator.end_with?(' ')
    corrector.insert_after(range, ' ')
  else
    corrector.replace(range, " #{operator.strip} ")
  end
end
excess_leading_space?(type, operator, with_space) click to toggle source
# File lib/rubocop/cop/layout/space_around_operators.rb, line 239
def excess_leading_space?(type, operator, with_space)
  return false unless allow_for_alignment?
  return false unless with_space.source.start_with?(EXCESSIVE_SPACE)

  return !aligned_with_operator?(operator) unless type == :assignment

  token            = Token.new(operator, nil, operator.source)
  align_preceding  = aligned_with_preceding_assignment(token)

  return false if align_preceding == :yes ||
                  aligned_with_subsequent_assignment(token) == :none

  aligned_with_subsequent_assignment(token) != :yes
end
excess_trailing_space?(right_operand, with_space) click to toggle source
# File lib/rubocop/cop/layout/space_around_operators.rb, line 254
def excess_trailing_space?(right_operand, with_space)
  with_space.source.end_with?(EXCESSIVE_SPACE) &&
    (!allow_for_alignment? || !aligned_with_something?(right_operand))
end
force_equal_sign_alignment?() click to toggle source
# File lib/rubocop/cop/layout/space_around_operators.rb, line 277
def force_equal_sign_alignment?
  config.for_cop('Layout/ExtraSpacing')['ForceEqualSignAlignment']
end
hash_table_style?() click to toggle source
# File lib/rubocop/cop/layout/space_around_operators.rb, line 263
def hash_table_style?
  align_hash_cop_config && align_hash_cop_config['EnforcedHashRocketStyle'] == 'table'
end
offense(type, operator, with_space, right_operand) { |msg| ... } click to toggle source
# File lib/rubocop/cop/layout/space_around_operators.rb, line 193
def offense(type, operator, with_space, right_operand)
  msg = offense_message(type, operator, with_space, right_operand)
  yield msg if msg
end
offense_message(type, operator, with_space, right_operand) click to toggle source
# File lib/rubocop/cop/layout/space_around_operators.rb, line 225
def offense_message(type, operator, with_space, right_operand)
  if should_not_have_surrounding_space?(operator, right_operand)
    return if with_space.is?(operator.source)

    "Space around operator `#{operator.source}` detected."
  elsif !/^\s.*\s$/.match?(with_space.source)
    "Surrounding space missing for operator `#{operator.source}`."
  elsif excess_leading_space?(type, operator, with_space) ||
        excess_trailing_space?(right_operand.source_range, with_space)
    "Operator `#{operator.source}` should be surrounded " \
      'by a single space.'
  end
end
operator_with_regular_syntax?(send_node) click to toggle source
# File lib/rubocop/cop/layout/space_around_operators.rb, line 175
def operator_with_regular_syntax?(send_node)
  send_node.operator_method? && !IRREGULAR_METHODS.include?(send_node.method_name)
end
regular_operator?(send_node) click to toggle source
# File lib/rubocop/cop/layout/space_around_operators.rb, line 169
def regular_operator?(send_node)
  return false if send_node.unary_operation? || send_node.dot? || send_node.double_colon?

  operator_with_regular_syntax?(send_node)
end
should_not_have_surrounding_space?(operator, right_operand) click to toggle source
# File lib/rubocop/cop/layout/space_around_operators.rb, line 281
def should_not_have_surrounding_space?(operator, right_operand)
  if operator.is?('**')
    !space_around_exponent_operator?
  elsif operator.is?('/')
    !space_around_slash_operator?(right_operand)
  else
    false
  end
end
space_around_exponent_operator?() click to toggle source
# File lib/rubocop/cop/layout/space_around_operators.rb, line 267
def space_around_exponent_operator?
  cop_config['EnforcedStyleForExponentOperator'] == 'space'
end
space_around_slash_operator?(right_operand) click to toggle source
# File lib/rubocop/cop/layout/space_around_operators.rb, line 271
def space_around_slash_operator?(right_operand)
  return true unless right_operand.rational_type?

  cop_config['EnforcedStyleForRationalLiterals'] == 'space'
end