Skip to content

Commit

Permalink
[Fix rubocop#12337] Support EnforcedStyleForRationalLiterals for `L…
Browse files Browse the repository at this point in the history
…ayout/SpaceAroundOperators`

Fixes rubocop#12337.

This PR supports `EnforcedStyleForRationalLiterals` option for `Layout/SpaceAroundOperators`.
Like `SupportedStylesForExponentOperator` option, the default follows the style guide with `no_space`.
  • Loading branch information
koic committed Nov 5, 2023
1 parent 605a5e2 commit 027323b
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 25 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* [#12337](https://github.com/rubocop/rubocop/issues/12337): Supports `EnforcedStyleForRationalLiterals` option for `Layout/SpaceAroundOperators`. ([@koic][])
4 changes: 4 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1351,6 +1351,10 @@ Layout/SpaceAroundOperators:
SupportedStylesForExponentOperator:
- space
- no_space
EnforcedStyleForRationalLiterals: no_space
SupportedStylesForRationalLiterals:
- space
- no_space

Layout/SpaceBeforeBlockBraces:
Description: >-
Expand Down
70 changes: 50 additions & 20 deletions lib/rubocop/cop/layout/space_around_operators.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,20 @@ module Layout
#
# # good
# a ** b
#
# @example EnforcedStyleForRationalLiterals: no_space (default)
# # bad
# 1 / 48r
#
# # good
# 1/48r
#
# @example EnforcedStyleForRationalLiterals: space
# # bad
# 1/48r
#
# # good
# 1 / 48r
class SpaceAroundOperators < Base
include PrecedingFollowingAlignment
include RangeHelp
Expand All @@ -64,30 +78,30 @@ def self.autocorrect_incompatible_with
end

def on_sclass(node)
check_operator(:sclass, node.loc.operator, node.source_range)
check_operator(:sclass, node.loc.operator, node)
end

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.source_range)
check_operator(:pair, node.loc.operator, node)
end

def on_if(node)
return unless node.ternary?

check_operator(:if, node.loc.question, node.if_branch.source_range)
check_operator(:if, node.loc.colon, node.else_branch.source_range)
check_operator(:if, node.loc.question, node.if_branch)
check_operator(:if, node.loc.colon, node.else_branch)
end

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

_, variable, = *node

check_operator(:resbody, node.loc.assoc, variable.source_range)
check_operator(:resbody, node.loc.assoc, variable)
end

def on_send(node)
Expand All @@ -96,7 +110,7 @@ def on_send(node)
if node.setter_method?
on_special_asgn(node)
elsif regular_operator?(node)
check_operator(:send, node.loc.selector, node.first_argument.source_range)
check_operator(:send, node.loc.selector, node.first_argument)
end
end

Expand All @@ -105,37 +119,37 @@ def on_assignment(node)

return unless rhs

check_operator(:assignment, node.loc.operator, rhs.source_range)
check_operator(:assignment, node.loc.operator, rhs)
end

def on_casgn(node)
_, _, right, = *node

return unless right

check_operator(:assignment, node.loc.operator, right.source_range)
check_operator(:assignment, node.loc.operator, right)
end

def on_binary(node)
_, rhs, = *node

return unless rhs

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

def on_special_asgn(node)
_, _, right, = *node

return unless right

check_operator(:special_asgn, node.loc.operator, right.source_range)
check_operator(:special_asgn, node.loc.operator, right)
end

def on_match_pattern(node)
return if target_ruby_version < 3.0

check_operator(:match_pattern, node.loc.operator, node.source_range)
check_operator(:match_pattern, node.loc.operator, node)
end

alias on_or on_binary
Expand Down Expand Up @@ -168,7 +182,7 @@ def check_operator(type, operator, right_operand)

offense(type, operator, with_space, right_operand) do |msg|
add_offense(operator, message: msg) do |corrector|
autocorrect(corrector, with_space)
autocorrect(corrector, with_space, right_operand)
end
end
end
Expand All @@ -178,11 +192,15 @@ def offense(type, operator, with_space, right_operand)
yield msg if msg
end

def autocorrect(corrector, range)
if range.source.include?('**') && !space_around_exponent_operator?
def autocorrect(corrector, range, right_operand)
range_source = range.source

if range_source.include?('**') && !space_around_exponent_operator?
corrector.replace(range, '**')
elsif range.source.end_with?("\n")
corrector.replace(range, " #{range.source.strip}\n")
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
Expand All @@ -202,14 +220,14 @@ def enclose_operator_with_space(corrector, range)
end

def offense_message(type, operator, with_space, right_operand)
if should_not_have_surrounding_space?(operator)
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, with_space)
excess_trailing_space?(right_operand.source_range, with_space)
"Operator `#{operator.source}` should be surrounded " \
'by a single space.'
end
Expand Down Expand Up @@ -247,12 +265,24 @@ def space_around_exponent_operator?
cop_config['EnforcedStyleForExponentOperator'] == 'space'
end

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

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

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

def should_not_have_surrounding_space?(operator)
operator.is?('**') ? !space_around_exponent_operator? : false
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
end
end
Expand Down
12 changes: 8 additions & 4 deletions spec/rubocop/cli/auto_gen_config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -320,8 +320,9 @@ def f
.to eq(['# Offense count: 1',
'# This cop supports safe autocorrection (--autocorrect).',
'# Configuration parameters: AllowForAlignment, ' \
'EnforcedStyleForExponentOperator.',
'EnforcedStyleForExponentOperator, EnforcedStyleForRationalLiterals.',
'# SupportedStylesForExponentOperator: space, no_space',
'# SupportedStylesForRationalLiterals: space, no_space',
'Layout/SpaceAroundOperators:',
' Exclude:',
" - 'example1.rb'",
Expand Down Expand Up @@ -862,8 +863,9 @@ def a; end
'# Offense count: 1',
'# This cop supports safe autocorrection (--autocorrect).',
'# Configuration parameters: AllowForAlignment, ' \
'EnforcedStyleForExponentOperator.',
'EnforcedStyleForExponentOperator, EnforcedStyleForRationalLiterals.',
'# SupportedStylesForExponentOperator: space, no_space',
'# SupportedStylesForRationalLiterals: space, no_space',
'Layout/SpaceAroundOperators:',
' Exclude:',
" - 'example1.rb'",
Expand Down Expand Up @@ -964,8 +966,9 @@ def a; end
'# Offense count: 1',
'# This cop supports safe autocorrection (--autocorrect).',
'# Configuration parameters: AllowForAlignment, ' \
'EnforcedStyleForExponentOperator.',
'EnforcedStyleForExponentOperator, EnforcedStyleForRationalLiterals.',
'# SupportedStylesForExponentOperator: space, no_space',
'# SupportedStylesForRationalLiterals: space, no_space',
'Layout/SpaceAroundOperators:',
' Exclude:',
" - 'example1.rb'",
Expand Down Expand Up @@ -1260,8 +1263,9 @@ def a; end
'',
'# This cop supports safe autocorrection (--autocorrect).',
'# Configuration parameters: AllowForAlignment, ' \
'EnforcedStyleForExponentOperator.',
'EnforcedStyleForExponentOperator, EnforcedStyleForRationalLiterals.',
'# SupportedStylesForExponentOperator: space, no_space',
'# SupportedStylesForRationalLiterals: space, no_space',
'Layout/SpaceAroundOperators:',
' Exclude:',
" - 'example1.rb'",
Expand Down
57 changes: 56 additions & 1 deletion spec/rubocop/cop/layout/space_around_operators_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
'Layout/HashAlignment' => { 'EnforcedHashRocketStyle' => hash_style },
'Layout/SpaceAroundOperators' => {
'AllowForAlignment' => allow_for_alignment,
'EnforcedStyleForExponentOperator' => exponent_operator_style
'EnforcedStyleForExponentOperator' => exponent_operator_style,
'EnforcedStyleForRationalLiterals' => rational_literals_style
},
'Layout/ExtraSpacing' => {
'Enabled' => force_equal_sign_alignment,
Expand All @@ -20,6 +21,7 @@
let(:hash_style) { 'key' }
let(:allow_for_alignment) { true }
let(:exponent_operator_style) { nil }
let(:rational_literals_style) { nil }
let(:force_equal_sign_alignment) { false }

it 'accepts operator surrounded by tabs' do
Expand Down Expand Up @@ -218,6 +220,44 @@ def self.===(other); end
expect_no_offenses('x = a * b**2')
end

it 'registers an offenses for slash in rational literals with spaces' do
expect_offense(<<~RUBY)
x = a * b / 42r
^ Space around operator `/` detected.
y = a * b/ 42r
^ Space around operator `/` detected.
RUBY

expect_correction(<<~RUBY)
x = a * b/42r
y = a * b/42r
RUBY
end

it 'accepts slash in rational literals without spaces' do
expect_no_offenses('x = a * b/42r')
end

it 'does not register an offenses for slash in non rational literals without spaces' do
expect_no_offenses(<<~RUBY)
x = a * b / 42
RUBY
end

it 'registers slash in non rational literals without spaces' do
expect_offense(<<~RUBY)
x = a * b/42
^ Surrounding space missing for operator `/`.
y = a * b/ 42
^ Surrounding space missing for operator `/`.
RUBY

expect_correction(<<~RUBY)
x = a * b / 42
y = a * b / 42
RUBY
end

context '>= Ruby 2.7', :ruby27 do
let(:target_ruby_version) { 2.7 }

Expand Down Expand Up @@ -259,6 +299,21 @@ def self.===(other); end
end
end

context 'when EnforcedStyleForRationalLiterals is space' do
let(:rational_literals_style) { 'space' }

it 'registers an offenses for rational literals without spaces' do
expect_offense(<<~RUBY)
x = a * b/42r
^ Surrounding space missing for operator `/`.
RUBY

expect_correction(<<~RUBY)
x = a * b / 42r
RUBY
end
end

it 'accepts unary operators without space' do
expect_no_offenses(<<~RUBY)
[].map(&:size)
Expand Down

0 comments on commit 027323b

Please sign in to comment.