From 1d81c00a2750c87cee0e6445fe3a13d72b75bd69 Mon Sep 17 00:00:00 2001 From: Daniel Vandersluis Date: Sat, 15 May 2021 12:07:24 -0400 Subject: [PATCH] [Fix #9799] Fix invalid line splitting by `Layout/LineLength` for `send` nodes with heredoc arguments. --- .../fix_fix_invalid_line_splitting_by.md | 1 + lib/rubocop/cop/mixin/check_line_breakable.rb | 16 +++++++ spec/rubocop/cop/layout/line_length_spec.rb | 47 +++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 changelog/fix_fix_invalid_line_splitting_by.md diff --git a/changelog/fix_fix_invalid_line_splitting_by.md b/changelog/fix_fix_invalid_line_splitting_by.md new file mode 100644 index 00000000000..487531f53ca --- /dev/null +++ b/changelog/fix_fix_invalid_line_splitting_by.md @@ -0,0 +1 @@ +* [#9799](https://github.com/rubocop/rubocop/issues/9799): Fix invalid line splitting by `Layout/LineLength` for `send` nodes with heredoc arguments. ([@dvandersluis][]) diff --git a/lib/rubocop/cop/mixin/check_line_breakable.rb b/lib/rubocop/cop/mixin/check_line_breakable.rb index 0e6bad0a106..5a3ae63830a 100644 --- a/lib/rubocop/cop/mixin/check_line_breakable.rb +++ b/lib/rubocop/cop/mixin/check_line_breakable.rb @@ -76,11 +76,27 @@ def extract_first_element_over_column_limit(node, elements, max) i = 0 i += 1 while within_column_limit?(elements[i], max, line) + i = shift_elements_for_heredoc_arg(node, elements, i) + + return if i.nil? return elements.first if i.zero? elements[i - 1] end + # @api private + # If a send node contains a heredoc argument, splitting cannot happen + # after the heredoc or else it will cause a syntax error. + def shift_elements_for_heredoc_arg(node, elements, index) + return index unless node.send_type? + + heredoc_index = elements.index { |arg| (arg.str_type? || arg.dstr_type?) && arg.heredoc? } + return index unless heredoc_index + return nil if heredoc_index.zero? + + heredoc_index >= index ? index : heredoc_index + 1 + end + # @api private def within_column_limit?(element, max, line) element && element.loc.column <= max && element.loc.line == line diff --git a/spec/rubocop/cop/layout/line_length_spec.rb b/spec/rubocop/cop/layout/line_length_spec.rb index cc2d919cc24..726a2fefe7c 100644 --- a/spec/rubocop/cop/layout/line_length_spec.rb +++ b/spec/rubocop/cop/layout/line_length_spec.rb @@ -727,6 +727,53 @@ def baz(bar) RUBY end end + + context 'with a heredoc argument' do + it 'does not break up the line' do + args = 'x' * 25 + expect_offense(<<~RUBY, args: args) + foo(<<~STRING, #{args}xxx) + _{args}^^^^ Line is too long. [44/40] + STRING + RUBY + + expect_no_corrections + end + + context 'and other arguments before the heredoc' do + it 'can break up the line before the heredoc argument' do + args = 'x' * 20 + expect_offense(<<~RUBY, args: args) + foo(abc, <<~STRING, #{args}xxx) + _{args}^^^^ Line is too long. [44/40] + STRING + RUBY + + expect_correction(<<~RUBY) + foo(abc,#{trailing_whitespace} + <<~STRING, #{args}xxx) + STRING + RUBY + end + end + + context 'and the heredoc is after the line should split' do + it 'can break up the line before the heredoc argument' do + args = 'x' * 34 + expect_offense(<<~RUBY, args: args) + foo(#{args}, <<~STRING) + _{args} ^^^^^^^^^^ Line is too long. [50/40] + STRING + RUBY + + expect_correction(<<~RUBY) + foo(#{args},#{trailing_whitespace} + <<~STRING) + STRING + RUBY + end + end + end end context 'array' do