diff --git a/changelog/fix_a_false_positive_for_layout_single_line_block_chain.md b/changelog/fix_a_false_positive_for_layout_single_line_block_chain.md new file mode 100644 index 00000000000..5ba4b5f8cb5 --- /dev/null +++ b/changelog/fix_a_false_positive_for_layout_single_line_block_chain.md @@ -0,0 +1 @@ +* [#10063](https://github.com/rubocop/rubocop/pull/10063): Fix a false positive for `Layout/SingleLineBlockChain` when method call chained on a new line after a single line block with trailing dot. ([@koic][]) diff --git a/lib/rubocop/cop/layout/single_line_block_chain.rb b/lib/rubocop/cop/layout/single_line_block_chain.rb index 8c807d16b1a..a73230404d8 100644 --- a/lib/rubocop/cop/layout/single_line_block_chain.rb +++ b/lib/rubocop/cop/layout/single_line_block_chain.rb @@ -37,15 +37,26 @@ def offending_range(node) return unless receiver&.block_type? receiver_location = receiver.loc - closing_block_delimiter_line_number = receiver_location.end.line - return if receiver_location.begin.line < closing_block_delimiter_line_number + closing_block_delimiter_line_num = receiver_location.end.line + return if receiver_location.begin.line < closing_block_delimiter_line_num node_location = node.loc dot_range = node_location.dot return unless dot_range - return if dot_range.line > closing_block_delimiter_line_number + return unless call_method_after_block?(node, dot_range, closing_block_delimiter_line_num) - range_between(dot_range.begin_pos, node_location.selector.end_pos) + range_between(dot_range.begin_pos, selector_range(node).end_pos) + end + + def call_method_after_block?(node, dot_range, closing_block_delimiter_line_num) + return false if dot_range.line > closing_block_delimiter_line_num + + dot_range.column < selector_range(node).column + end + + def selector_range(node) + # l.(1) has no selector, so we use the opening parenthesis instead + node.loc.selector || node.loc.begin end end end diff --git a/spec/rubocop/cli/autocorrect_spec.rb b/spec/rubocop/cli/autocorrect_spec.rb index 1d1bf52b2be..565b2464291 100644 --- a/spec/rubocop/cli/autocorrect_spec.rb +++ b/spec/rubocop/cli/autocorrect_spec.rb @@ -2070,6 +2070,21 @@ def do_even_more_stuff RUBY end + it 'corrects `Layout/DotPosition` and `Layout/SingleLineBlockChain` offenses' do + source_file = Pathname('example.rb') + create_file(source_file, <<~RUBY) + example.select { |item| item.cond? }. + join('-') + RUBY + + expect(cli.run(['-a', '--only', 'Layout/DotPosition,Layout/SingleLineBlockChain'])).to eq(0) + + expect(source_file.read).to eq(<<~RUBY) + example.select { |item| item.cond? } + .join('-') + RUBY + end + it 'does not correct Style/IfUnlessModifier offense disabled by a comment directive and ' \ 'does not fire Lint/RedundantCopDisableDirective offense even though that directive ' \ 'would make the modifier form too long' do diff --git a/spec/rubocop/cop/layout/single_line_block_chain_spec.rb b/spec/rubocop/cop/layout/single_line_block_chain_spec.rb index 6f3b969043a..a5480f27460 100644 --- a/spec/rubocop/cop/layout/single_line_block_chain_spec.rb +++ b/spec/rubocop/cop/layout/single_line_block_chain_spec.rb @@ -13,6 +13,18 @@ RUBY end + it 'registers an offense for no selector method call chained on the same line as a block' do + expect_offense(<<~RUBY) + example.select { |item| item.cond? }.(42) + ^^ Put method call on a separate line if chained to a single line block. + RUBY + + expect_correction(<<~RUBY) + example.select { |item| item.cond? } + .(42) + RUBY + end + it 'does not register an offense for method call chained on a new line after a single line block' do expect_no_offenses(<<~RUBY) example.select { |item| item.cond? } @@ -20,6 +32,13 @@ RUBY end + it 'does not register an offense for method call chained on a new line after a single line block with trailing dot' do + expect_no_offenses(<<~RUBY) + example.select { |item| item.cond? }. + join('-') + RUBY + end + it 'does not register an offense for method call chained without a dot' do expect_no_offenses(<<~RUBY) example.select { |item| item.cond? } + 2