Skip to content

Commit

Permalink
Add autocorrect support for Style/SafeNavigationChain
Browse files Browse the repository at this point in the history
  • Loading branch information
r7kamura committed Jul 14, 2022
1 parent 8d484e3 commit 73b54ae
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 1 deletion.
1 change: 1 addition & 0 deletions changelog/change_add_autocorrect_support_for.md
@@ -0,0 +1 @@
* [#10817](https://github.com/rubocop/rubocop/pull/10817): Add autocorrect support for `Style/SafeNavigationChain`. ([@r7kamura][])
36 changes: 35 additions & 1 deletion lib/rubocop/cop/lint/safe_navigation_chain.rb
Expand Up @@ -25,6 +25,7 @@ module Lint
# x&.foo || bar
class SafeNavigationChain < Base
include NilMethods
extend AutoCorrector
extend TargetRubyVersion

minimum_target_ruby_version 2.3
Expand All @@ -48,12 +49,45 @@ def on_send(node)
Parser::Source::Range.new(node.source_range.source_buffer,
safe_nav.source_range.end_pos,
method_chain.source_range.end_pos)
add_offense(location)
add_offense(location) do |corrector|
autocorrect(corrector, offense_range: location, send_node: method_chain)
end
end
end

private

# @param [Parser::Source::Range] offense_range
# @param [RuboCop::AST::SendNode] send_node
# @return [String]
def add_safe_navigation_operator(offense_range:, send_node:)
source = \
if send_node.method?(:[]) || send_node.method?(:[]=)
format(
'%<method_name>s(%<arguments>s)',
arguments: send_node.arguments.map(&:source).join(', '),
method_name: send_node.method_name
)
else
offense_range.source.dup
end
source.prepend('.') unless send_node.dot?
source.prepend('&')
end

# @param [RuboCop::Cop::Corrector] corrector
# @param [Parser::Source::Range] offense_range
# @param [RuboCop::AST::SendNode] send_node
def autocorrect(corrector, offense_range:, send_node:)
corrector.replace(
offense_range,
add_safe_navigation_operator(
offense_range: offense_range,
send_node: send_node
)
)
end

def method_chain(node)
chain = node
chain = chain.parent if chain.send_type? && chain.parent&.call_type?
Expand Down
86 changes: 86 additions & 0 deletions spec/rubocop/cop/lint/safe_navigation_chain_spec.rb
Expand Up @@ -39,6 +39,10 @@
x&.foo.bar
^^^^ Do not chain ordinary method call after safe navigation operator.
RUBY

expect_correction(<<~RUBY)
x&.foo&.bar
RUBY
end

it 'registers an offense for ordinary method call exists after ' \
Expand All @@ -47,6 +51,10 @@
x&.foo(x).bar(y)
^^^^^^^ Do not chain ordinary method call after safe navigation operator.
RUBY

expect_correction(<<~RUBY)
x&.foo(x)&.bar(y)
RUBY
end

it 'registers an offense for ordinary method chain exists after safe navigation method call' do
Expand All @@ -55,6 +63,11 @@
x&.foo.bar.baz
^^^^^^^^ Do not chain ordinary method call after safe navigation operator.
RUBY

expect_correction(<<~RUBY)
something
x&.foo&.bar&.baz
RUBY
end

it 'registers an offense for ordinary method chain exists after ' \
Expand All @@ -63,6 +76,10 @@
x&.foo(x).bar(y).baz(z)
^^^^^^^^^^^^^^ Do not chain ordinary method call after safe navigation operator.
RUBY

expect_correction(<<~RUBY)
x&.foo(x)&.bar(y)&.baz(z)
RUBY
end

it 'registers an offense for ordinary method chain exists after ' \
Expand All @@ -72,6 +89,11 @@
x&.select(&:foo).bar
^^^^ Do not chain ordinary method call after safe navigation operator.
RUBY

expect_correction(<<~RUBY)
something
x&.select(&:foo)&.bar
RUBY
end

it 'registers an offense for ordinary method chain exists after ' \
Expand All @@ -81,55 +103,88 @@
x&.select { |x| foo(x) }.bar
^^^^ Do not chain ordinary method call after safe navigation operator.
RUBY

expect_correction(<<~RUBY)
something
x&.select { |x| foo(x) }&.bar
RUBY
end

it 'registers an offense for safe navigation with < operator' do
expect_offense(<<~RUBY)
x&.foo < bar
^^^^^^ Do not chain ordinary method call after safe navigation operator.
RUBY

expect_correction(<<~RUBY)
x&.foo&. < bar
RUBY
end

it 'registers an offense for safe navigation with > operator' do
expect_offense(<<~RUBY)
x&.foo > bar
^^^^^^ Do not chain ordinary method call after safe navigation operator.
RUBY

expect_correction(<<~RUBY)
x&.foo&. > bar
RUBY
end

it 'registers an offense for safe navigation with <= operator' do
expect_offense(<<~RUBY)
x&.foo <= bar
^^^^^^^ Do not chain ordinary method call after safe navigation operator.
RUBY

expect_correction(<<~RUBY)
x&.foo&. <= bar
RUBY
end

it 'registers an offense for safe navigation with >= operator' do
expect_offense(<<~RUBY)
x&.foo >= bar
^^^^^^^ Do not chain ordinary method call after safe navigation operator.
RUBY

expect_correction(<<~RUBY)
x&.foo&. >= bar
RUBY
end

it 'registers an offense for safe navigation with + operator' do
expect_offense(<<~RUBY)
x&.foo + bar
^^^^^^ Do not chain ordinary method call after safe navigation operator.
RUBY

expect_correction(<<~RUBY)
x&.foo&. + bar
RUBY
end

it 'registers an offense for safe navigation with [] operator' do
expect_offense(<<~RUBY)
x&.foo[bar]
^^^^^ Do not chain ordinary method call after safe navigation operator.
RUBY

expect_correction(<<~RUBY)
x&.foo&.[](bar)
RUBY
end

it 'registers an offense for safe navigation with []= operator' do
expect_offense(<<~RUBY)
x&.foo[bar] = baz
^^^^^^^^^^^ Do not chain ordinary method call after safe navigation operator.
RUBY

expect_correction(<<~RUBY)
x&.foo&.[]=(bar, baz)
RUBY
end

context 'proper highlighting' do
Expand All @@ -139,6 +194,11 @@
x&.foo.bar.baz
^^^^^^^^ Do not chain ordinary method call after safe navigation operator.
RUBY

expect_correction(<<~RUBY)
something
x&.foo&.bar&.baz
RUBY
end

it 'when there are methods after' do
Expand All @@ -147,6 +207,11 @@
^^^^^^^^ Do not chain ordinary method call after safe navigation operator.
something
RUBY

expect_correction(<<~RUBY)
x&.foo&.bar&.baz
something
RUBY
end

it 'when in a method' do
Expand All @@ -156,6 +221,12 @@ def something
^^^^^^^^ Do not chain ordinary method call after safe navigation operator.
end
RUBY

expect_correction(<<~RUBY)
def something
x&.foo&.bar&.baz
end
RUBY
end

it 'when in a begin' do
Expand All @@ -165,13 +236,23 @@ def something
^^^^^^^^ Do not chain ordinary method call after safe navigation operator.
end
RUBY

expect_correction(<<~RUBY)
begin
x&.foo&.bar&.baz
end
RUBY
end

it 'when used with a modifier if' do
expect_offense(<<~RUBY)
x&.foo.bar.baz if something
^^^^^^^^ Do not chain ordinary method call after safe navigation operator.
RUBY

expect_correction(<<~RUBY)
x&.foo&.bar&.baz if something
RUBY
end
end
end
Expand All @@ -184,6 +265,11 @@ def something
x&.select { foo(_1) }.bar
^^^^ Do not chain ordinary method call after safe navigation operator.
RUBY

expect_correction(<<~RUBY)
something
x&.select { foo(_1) }&.bar
RUBY
end
end
end

0 comments on commit 73b54ae

Please sign in to comment.