123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::Cop::Layout::SpaceAroundOperators

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, ::RuboCop::Cop::AutoCorrector, ::RuboCop::Cop::Base, ::RuboCop::ExcludeLimit, NodePattern::Macros, RuboCop::AST::Sexp
Instance Chain:
Inherits: RuboCop::Cop::Base
Defined in: lib/rubocop/cop/layout/space_around_operators.rb

Overview

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.

Examples:

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

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

AllowForAlignment: true (default)

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

AllowForAlignment: false

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

EnforcedStyleForExponentOperator: no_space (default)

# bad
a ** b

# good
a**b

EnforcedStyleForExponentOperator: space

# bad
a**b

# good
a ** b

EnforcedStyleForRationalLiterals: no_space (default)

# bad
1 / 48r

# good
1/48r

EnforcedStyleForRationalLiterals: space

# bad
1/48r

# good
1 / 48r

Constant Summary

::RuboCop::Cop::Base - Inherited

EMPTY_OFFENSES, RESTRICT_ON_SEND

::RuboCop::Cop::RangeHelp - Included

BYTE_ORDER_MARK, NOT_GIVEN

Class Attribute Summary

::RuboCop::Cop::AutoCorrector - Extended

::RuboCop::Cop::Base - Inherited

.gem_requirements, .lint?,
.support_autocorrect?

Returns if class supports autocorrect.

.support_multiple_source?

Override if your cop should be called repeatedly for multiple investigations Between calls to on_new_investigation and on_investigation_end, the result of processed_source will remain constant.

Class Method Summary

::RuboCop::Cop::Base - Inherited

.autocorrect_incompatible_with

List of cops that should not try to autocorrect at the same time as this cop.

.badge

Naming.

.callbacks_needed, .cop_name, .department,
.documentation_url

Returns a url to view this cops documentation online.

.exclude_from_registry

Call for abstract Cop classes.

.inherited,
.joining_forces

Override and return the Force class(es) you need to join.

.match?

Returns true if the cop name or the cop namespace matches any of the given names.

.new,
.requires_gem

Register a version requirement for the given gem name.

.restrict_on_send

::RuboCop::ExcludeLimit - Extended

exclude_limit

Sets up a configuration option to have an exclude limit tracked.

transform

Instance Attribute Summary

Instance Method Summary

::RuboCop::Cop::RationalLiteral - Included

::RuboCop::Cop::RangeHelp - Included

#add_range, #column_offset_between,
#contents_range

A range containing only the contents of a literal with delimiters (e.g.

#directions,
#effective_column

Returns the column attribute of the range, except if the range is on the first line and there’s a byte order mark at the beginning of that line, in which case 1 is subtracted from the column value.

#final_pos, #move_pos, #move_pos_str, #range_between, #range_by_whole_lines, #range_with_comments, #range_with_comments_and_lines, #range_with_surrounding_comma, #range_with_surrounding_space, #source_range

::RuboCop::Cop::PrecedingFollowingAlignment - Included

::RuboCop::Cop::Base - Inherited

#add_global_offense

Adds an offense that has no particular location.

#add_offense

Adds an offense on the specified range (or node with an expression) Unless that offense is disabled for this range, a corrector will be yielded to provide the cop the opportunity to autocorrect the offense.

#begin_investigation

Called before any investigation.

#callbacks_needed,
#cop_config

Configuration Helpers.

#cop_name, #excluded_file?,
#external_dependency_checksum

This method should be overridden when a cop’s behavior depends on state that lives outside of these locations:

#inspect,
#message

Gets called if no message is specified when calling add_offense or add_global_offense Cops are discouraged to override this; instead pass your message directly.

#name

Alias for Base#cop_name.

#offenses,
#on_investigation_end

Called after all on_…​

#on_new_investigation

Called before all on_…​

#on_other_file

Called instead of all on_…​

#parse

There should be very limited reasons for a Cop to do it’s own parsing.

#parser_engine,
#ready

Called between investigations.

#relevant_file?, #target_rails_version, #target_ruby_version, #annotate, #apply_correction, #attempt_correction,
#callback_argument

Reserved for Cop::Cop.

#complete_investigation

Called to complete an investigation.

#correct, #current_corrector,
#current_offense_locations

Reserved for Commissioner:

#current_offenses, #currently_disabled_lines, #custom_severity, #default_severity, #disable_uncorrectable, #enabled_line?, #file_name_matches_any?, #find_message, #find_severity, #range_for_original, #range_from_node_or_range,
#reset_investigation

Actually private methods.

#use_corrector

::RuboCop::Cop::AutocorrectLogic - Included

::RuboCop::Cop::IgnoredNode - Included

Constructor Details

This class inherits a constructor from RuboCop::Cop::Base

Class Method Details

.autocorrect_incompatible_with

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 76

def self.autocorrect_incompatible_with
  [Style::SelfAssignment]
end

Instance Attribute Details

#force_equal_sign_alignment?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 276

def force_equal_sign_alignment?
  config.for_cop('Layout/ExtraSpacing')['ForceEqualSignAlignment']
end

#hash_table_style?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 262

def hash_table_style?
  align_hash_cop_config && align_hash_cop_config['EnforcedHashRocketStyle'] == 'table'
end

#space_around_exponent_operator?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 266

def space_around_exponent_operator?
  cop_config['EnforcedStyleForExponentOperator'] == 'space'
end

Instance Method Details

#align_hash_cop_config (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 258

def align_hash_cop_config
  config.for_cop('Layout/HashAlignment')
end

#autocorrect(corrector, range, right_operand) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 197

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) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 178

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) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 211

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) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 238

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) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 253

def excess_trailing_space?(right_operand, with_space)
  with_space.source.end_with?(EXCESSIVE_SPACE) &&
    (!allow_for_alignment? || !aligned_with_something?(right_operand))
end

#offense(type, operator, with_space, right_operand) {|msg| ... } (private)

Yields:

  • (msg)
[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 192

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) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 224

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

#on_and(node)

Alias for #on_binary.

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 155

alias on_and      on_binary

#on_and_asgn(node)

Alias for #on_assignment.

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 163

alias on_and_asgn on_assignment

#on_assignment(node) Also known as: #on_lvasgn, #on_casgn, #on_masgn, #on_ivasgn, #on_cvasgn, #on_gvasgn, #on_or_asgn, #on_and_asgn, #on_op_asgn

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 115

def on_assignment(node)
  rhs = node.rhs

  return unless rhs

  type = node.op_asgn_type? ? :special_asgn : :assignment
  check_operator(type, node.loc.operator, rhs)
end

#on_binary(node) Also known as: #on_or, #on_and

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 132

def on_binary(node)
  rhs = node.rhs

  return unless rhs

  check_operator(:binary, node.loc.operator, rhs)
end

#on_casgn(node)

Alias for #on_assignment.

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 157

alias on_casgn    on_assignment

#on_class(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 124

def on_class(node)
  rhs = node.parent_class

  return unless rhs

  check_operator(:class, node.loc.operator, rhs)
end

#on_cvasgn(node)

Alias for #on_assignment.

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 160

alias on_cvasgn   on_assignment

#on_gvasgn(node)

Alias for #on_assignment.

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 161

alias on_gvasgn   on_assignment

#on_if(node)

[ GitHub ]

  
# 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.

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 159

alias on_ivasgn   on_assignment

#on_lvasgn(node)

Alias for #on_assignment.

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 156

alias on_lvasgn   on_assignment

#on_masgn(node)

Alias for #on_assignment.

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 158

alias on_masgn    on_assignment

#on_match_pattern(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 148

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_assignment.

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 164

alias on_op_asgn  on_assignment

#on_or(node)

Alias for #on_binary.

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 154

alias on_or       on_binary

#on_or_asgn(node)

Alias for #on_assignment.

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 162

alias on_or_asgn  on_assignment

#on_pair(node)

[ GitHub ]

  
# 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)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 99

def on_resbody(node)
  return unless node.loc.assoc

  check_operator(:resbody, node.loc.assoc, node.exception_variable)
end

#on_sclass(node)

[ GitHub ]

  
# 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)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 105

def on_send(node)
  return if rational_literal?(node)

  if node.setter_method?
    on_setter_method(node)
  elsif regular_operator?(node)
    check_operator(:send, node.loc.selector, node.first_argument)
  end
end

#on_setter_method(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 140

def on_setter_method(node)
  rhs = node.first_argument

  return unless rhs

  check_operator(:special_asgn, node.loc.operator, node.first_argument)
end

#operator_with_regular_syntax?(send_node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 174

def operator_with_regular_syntax?(send_node)
  send_node.operator_method? && !IRREGULAR_METHODS.include?(send_node.method_name)
end

#regular_operator?(send_node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 168

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) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 280

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_slash_operator?(right_operand) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/space_around_operators.rb', line 270

def space_around_slash_operator?(right_operand)
  return true unless right_operand.rational_type?

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