Skip to content

Commit

Permalink
[Fix #12199] Fix false negatives for `Layout/MultilineMethodCallInden…
Browse files Browse the repository at this point in the history
…tation`

Fixes #12199.

This PR fixes false negatives for `Layout/MultilineMethodCallIndentation`
when using safe navigation operator.
  • Loading branch information
koic authored and bbatsov committed Sep 12, 2023
1 parent aaf4f56 commit 1b7da81
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* [#12199](https://github.com/rubocop/rubocop/issues/12199): Fix false negatives for `Layout/MultilineMethodCallIndentation` when using safe navigation operator. ([@koic][])
4 changes: 2 additions & 2 deletions lib/rubocop/cop/layout/multiline_method_call_indentation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def relevant_node?(send_node)
def right_hand_side(send_node)
dot = send_node.loc.dot
selector = send_node.loc.selector
if send_node.dot? && selector && same_line?(dot, selector)
if (send_node.dot? || send_node.safe_navigation?) && selector && same_line?(dot, selector)
dot.join(selector)
elsif selector
selector
Expand Down Expand Up @@ -179,7 +179,7 @@ def syntactic_alignment_base(lhs, rhs)
# a.b
# .c
def semantic_alignment_base(node, rhs)
return unless rhs.source.start_with?('.')
return unless rhs.source.start_with?('.', '&.')

node = semantic_alignment_node(node)
return unless node&.loc&.selector
Expand Down
5 changes: 3 additions & 2 deletions lib/rubocop/cop/mixin/multiline_expression_indentation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,17 @@ def on_send(node)
range = offending_range(node, lhs, rhs, style)
check(range, node, lhs, rhs)
end
alias on_csend on_send

private

# In a chain of method calls, we regard the top send node as the base
# In a chain of method calls, we regard the top call node as the base
# for indentation of all lines following the first. For example:
# a.
# b c { block }. <-- b is indented relative to a
# d <-- d is indented relative to a
def left_hand_side(lhs)
while lhs.parent&.send_type? && lhs.parent.loc.dot && !lhs.parent.assignment_method?
while lhs.parent&.call_type? && lhs.parent.loc.dot && !lhs.parent.assignment_method?
lhs = lhs.parent
end
lhs
Expand Down
114 changes: 114 additions & 0 deletions spec/rubocop/cop/layout/multiline_method_call_indentation_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,113 @@
.b
RUBY
end

context 'when using safe navigation operator' do
it 'registers an offense and corrects no indentation of second line' do
expect_offense(<<~RUBY)
a&.
b
^ Use 2 (not 0) spaces for indenting an expression spanning multiple lines.
RUBY

expect_correction(<<~RUBY)
a&.
b
RUBY
end

it 'registers an offense and corrects 3 spaces indentation of 2nd line' do
expect_offense(<<~RUBY)
a&.
b
^ Use 2 (not 3) spaces for indenting an expression spanning multiple lines.
c&.
d
^ Use 2 (not 3) spaces for indenting an expression spanning multiple lines.
RUBY

expect_correction(<<~RUBY)
a&.
b
c&.
d
RUBY
end

it 'registers an offense and corrects extra indentation of third line' do
expect_offense(<<~RUBY)
a&.
b&.
c
^ Use 2 (not 4) spaces for indenting an expression spanning multiple lines.
RUBY

expect_correction(<<~RUBY)
a&.
b&.
c
RUBY
end

it 'registers an offense and corrects the emacs ruby-mode 1.1 ' \
'indentation of an expression in an array' do
expect_offense(<<~RUBY)
[
a&.
b
^ Use 2 (not 0) spaces for indenting an expression spanning multiple lines.
]
RUBY

expect_correction(<<~RUBY)
[
a&.
b
]
RUBY
end

it 'registers an offense and corrects extra indentation of 3rd line in typical RSpec code' do
expect_offense(<<~RUBY)
expect { Foo.new }&.
to change { Bar.count }&.
from(1)&.to(2)
^^^^ Use 2 (not 6) spaces for indenting an expression spanning multiple lines.
RUBY

expect_correction(<<~RUBY)
expect { Foo.new }&.
to change { Bar.count }&.
from(1)&.to(2)
RUBY
end

it 'registers an offense and corrects proc call without a selector' do
expect_offense(<<~RUBY)
a
&.(args)
^^^ Use 2 (not 1) spaces for indenting an expression spanning multiple lines.
RUBY

expect_correction(<<~RUBY)
a
&.(args)
RUBY
end

it 'registers an offense and corrects one space indentation of 2nd line' do
expect_offense(<<~RUBY)
a
&.b
^^^ Use 2 (not 1) spaces for indenting an expression spanning multiple lines.
RUBY

expect_correction(<<~RUBY)
a
&.b
RUBY
end
end
end

context 'when EnforcedStyle is aligned' do
Expand Down Expand Up @@ -255,6 +362,13 @@ def foo
RUBY
end

it 'accepts methods being aligned with safe navigation method call that is an argument' do
expect_no_offenses(<<~RUBY)
do_something obj.foo(key: value)
&.bar(arg)
RUBY
end

context '>= Ruby 2.7', :ruby27 do
it 'accepts methods being aligned with method that is an argument' \
'when using numbered parameter' do
Expand Down

0 comments on commit 1b7da81

Please sign in to comment.