From 8b4f88f3e304bad496d4dc3c72814adc63fd013c Mon Sep 17 00:00:00 2001 From: Marc-Andre Lafortune Date: Thu, 10 Sep 2020 11:02:46 -0400 Subject: [PATCH 1/3] Fix `Layout/TrailingWhitespace` auto-correction in heredoc --- CHANGELOG.md | 1 + docs/modules/ROOT/pages/cops_layout.adoc | 11 ++++++ lib/rubocop/cop/layout/trailing_whitespace.rb | 39 ++++++++++++++----- .../cop/layout/trailing_whitespace_spec.rb | 21 ++++++++++ 4 files changed, 62 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2849321046..6d95026ee81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -151,6 +151,7 @@ * [#8661](https://github.com/rubocop-hq/rubocop/pull/8661): Fix an incorrect auto-correct for `Style/MultilineTernaryOperator` when returning a multiline ternary operator expression. ([@koic][]) * [#8526](https://github.com/rubocop-hq/rubocop/pull/8526): Fix a false positive for `Style/CaseEquality` cop when the receiver is not a camel cased constant. ([@koic][]) * [#8673](https://github.com/rubocop-hq/rubocop/issues/8673): Fix the JSON parse error when specifying `--format=json` and `--stdin` options. ([@koic][]) +* [#8692](https://github.com/rubocop-hq/rubocop/pull/8692): Fix `Layout/TrailingWhitespace` auto-correction in heredoc. ([@marcandre][]) ### Changes diff --git a/docs/modules/ROOT/pages/cops_layout.adoc b/docs/modules/ROOT/pages/cops_layout.adoc index e277dcfe376..0e47de9ca53 100644 --- a/docs/modules/ROOT/pages/cops_layout.adoc +++ b/docs/modules/ROOT/pages/cops_layout.adoc @@ -6369,6 +6369,17 @@ x = 0 code = <<~RUBY x = 0 RUBY + +# ok +code = <<~RUBY + x = 0 #{} +RUBY + +# good +trailing_whitespace = ' ' +code = <<~RUBY + x = 0#{trailing_whitespace} +RUBY ---- ==== AllowInHeredoc: true (default) diff --git a/lib/rubocop/cop/layout/trailing_whitespace.rb b/lib/rubocop/cop/layout/trailing_whitespace.rb index 143492fa976..4c5d7eff40f 100644 --- a/lib/rubocop/cop/layout/trailing_whitespace.rb +++ b/lib/rubocop/cop/layout/trailing_whitespace.rb @@ -21,6 +21,17 @@ module Layout # x = 0 # RUBY # + # # ok + # code = <<~RUBY + # x = 0 #{} + # RUBY + # + # # good + # trailing_whitespace = ' ' + # code = <<~RUBY + # x = 0#{trailing_whitespace} + # RUBY + # # @example AllowInHeredoc: true (default) # # The line in this example contains spaces after the 0. # # good @@ -35,28 +46,36 @@ class TrailingWhitespace < Base MSG = 'Trailing whitespace detected.' def on_new_investigation - heredoc_ranges = extract_heredoc_ranges(processed_source.ast) + @heredoc_ranges = extract_heredoc_ranges(processed_source.ast) processed_source.lines.each_with_index do |line, index| - lineno = index + 1 - next unless line.end_with?(' ', "\t") - next if skip_heredoc? && inside_heredoc?(heredoc_ranges, lineno) - range = offense_range(lineno, line) - add_offense(range) do |corrector| - corrector.remove(range) - end + process_line(line, index + 1) end end private + def process_line(line, lineno) + in_heredoc = inside_heredoc?(lineno) + return if skip_heredoc? && in_heredoc + + range = offense_range(lineno, line) + add_offense(range) do |corrector| + if in_heredoc + corrector.insert_after(range, '#{}') # rubocop:disable Lint/InterpolationCheck + else + corrector.remove(range) + end + end + end + def skip_heredoc? cop_config.fetch('AllowInHeredoc', false) end - def inside_heredoc?(heredoc_ranges, line_number) - heredoc_ranges.any? { |r| r.include?(line_number) } + def inside_heredoc?(line_number) + @heredoc_ranges.any? { |r| r.include?(line_number) } end def extract_heredoc_ranges(ast) diff --git a/spec/rubocop/cop/layout/trailing_whitespace_spec.rb b/spec/rubocop/cop/layout/trailing_whitespace_spec.rb index 66dc1ecb998..fc91808e5ac 100644 --- a/spec/rubocop/cop/layout/trailing_whitespace_spec.rb +++ b/spec/rubocop/cop/layout/trailing_whitespace_spec.rb @@ -89,4 +89,25 @@ expect(offenses.size).to eq(1) end end + + context 'when `AllowInHeredoc` is set to false' do + let(:cop_config) { { 'AllowInHeredoc' => false } } + + it 'corrects safely trailing whitespace in a heredoc string' do + expect_offense(<<~RUBY) + x = <<~EXAMPLE + has trailing#{trailing_whitespace} + ^ Trailing whitespace detected. + no trailing + EXAMPLE + RUBY + + expect_correction(<<~RUBY) + x = <<~EXAMPLE + has trailing \#{} + no trailing + EXAMPLE + RUBY + end + end end From 475de55df6c8479857664376e9794df01e665a4d Mon Sep 17 00:00:00 2001 From: Marc-Andre Lafortune Date: Thu, 10 Sep 2020 12:14:45 -0400 Subject: [PATCH 2/3] Don't autocorrect static heredocs with trailing whitespaces --- lib/rubocop/cop/layout/trailing_whitespace.rb | 23 +++++++++++-------- .../cop/layout/trailing_whitespace_spec.rb | 12 ++++++++++ 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/lib/rubocop/cop/layout/trailing_whitespace.rb b/lib/rubocop/cop/layout/trailing_whitespace.rb index 4c5d7eff40f..d896bb4bf4b 100644 --- a/lib/rubocop/cop/layout/trailing_whitespace.rb +++ b/lib/rubocop/cop/layout/trailing_whitespace.rb @@ -46,7 +46,7 @@ class TrailingWhitespace < Base MSG = 'Trailing whitespace detected.' def on_new_investigation - @heredoc_ranges = extract_heredoc_ranges(processed_source.ast) + @heredocs = extract_heredocs(processed_source.ast) processed_source.lines.each_with_index do |line, index| next unless line.end_with?(' ', "\t") @@ -57,33 +57,38 @@ def on_new_investigation private def process_line(line, lineno) - in_heredoc = inside_heredoc?(lineno) - return if skip_heredoc? && in_heredoc + heredoc = find_heredoc(lineno) + return if skip_heredoc? && heredoc range = offense_range(lineno, line) add_offense(range) do |corrector| - if in_heredoc - corrector.insert_after(range, '#{}') # rubocop:disable Lint/InterpolationCheck + if heredoc + corrector.insert_after(range, '#{}') unless static?(heredoc) # rubocop:disable Lint/InterpolationCheck else corrector.remove(range) end end end + def static?(heredoc) + heredoc.loc.expression.source.end_with? "'" + end + def skip_heredoc? cop_config.fetch('AllowInHeredoc', false) end - def inside_heredoc?(line_number) - @heredoc_ranges.any? { |r| r.include?(line_number) } + def find_heredoc(line_number) + @heredocs.each { |node, r| return node if r.include?(line_number) } + nil end - def extract_heredoc_ranges(ast) + def extract_heredocs(ast) return [] unless ast ast.each_node(:str, :dstr, :xstr).select(&:heredoc?).map do |node| body = node.location.heredoc_body - (body.first_line...body.last_line) + [node, body.first_line...body.last_line] end end diff --git a/spec/rubocop/cop/layout/trailing_whitespace_spec.rb b/spec/rubocop/cop/layout/trailing_whitespace_spec.rb index fc91808e5ac..cd05d5a9e3e 100644 --- a/spec/rubocop/cop/layout/trailing_whitespace_spec.rb +++ b/spec/rubocop/cop/layout/trailing_whitespace_spec.rb @@ -109,5 +109,17 @@ EXAMPLE RUBY end + + it 'does not correct trailing whitespace in a static heredoc string' do + expect_offense(<<~RUBY) + x = <<~'EXAMPLE' + has trailing#{trailing_whitespace} + ^ Trailing whitespace detected. + no trailing + EXAMPLE + RUBY + + expect_no_corrections + end end end From a4a2403587bd4abd7302b62b43f395a5dc6c93d9 Mon Sep 17 00:00:00 2001 From: Marc-Andre Lafortune Date: Thu, 10 Sep 2020 11:15:56 -0400 Subject: [PATCH 3/3] Default changed to disallow `Layout/TrailingWhitespace` in heredoc. --- CHANGELOG.md | 1 + config/default.yml | 2 +- docs/modules/ROOT/pages/cops_layout.adoc | 6 +++--- lib/rubocop/cop/layout/trailing_whitespace.rb | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d95026ee81..c5b52d82be5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ * [#8882](https://github.com/rubocop-hq/rubocop/pull/8882): **(Potentially breaking)** RuboCop assumes that Cop classes do not define new `on_` methods at runtime (e.g. via `extend` in `initialize`). ([@marcandre][]) * [#7966](https://github.com/rubocop-hq/rubocop/issues/7966): **(Breaking)** Enable all pending cops for RuboCop 1.0. ([@koic][]) * [#8490](https://github.com/rubocop-hq/rubocop/pull/8490): **(Breaking)** Change logic for cop department name computation. Cops inside deep namespaces (5 or more levels deep) now belong to departments with names that are calculated by joining module names starting from the third one with slashes as separators. For example, cop `Rubocop::Cop::Foo::Bar::Baz` now belongs to `Foo/Bar` department (previously it was `Bar`). ([@dsavochkin][]) +* [#8692](https://github.com/rubocop-hq/rubocop/pull/8692): Default changed to disallow `Layout/TrailingWhitespace` in heredoc. ([@marcandre][]) ## 0.93.1 (2020-10-12) diff --git a/config/default.yml b/config/default.yml index 9d3bf8129a2..05104802b7c 100644 --- a/config/default.yml +++ b/config/default.yml @@ -1335,7 +1335,7 @@ Layout/TrailingWhitespace: Enabled: true VersionAdded: '0.49' VersionChanged: '0.83' - AllowInHeredoc: true + AllowInHeredoc: false #################### Lint ################################## ### Warnings diff --git a/docs/modules/ROOT/pages/cops_layout.adoc b/docs/modules/ROOT/pages/cops_layout.adoc index 0e47de9ca53..fd34e085b79 100644 --- a/docs/modules/ROOT/pages/cops_layout.adoc +++ b/docs/modules/ROOT/pages/cops_layout.adoc @@ -6360,7 +6360,7 @@ x = 0 x = 0 ---- -==== AllowInHeredoc: false +==== AllowInHeredoc: false (default) [source,ruby] ---- @@ -6382,7 +6382,7 @@ code = <<~RUBY RUBY ---- -==== AllowInHeredoc: true (default) +==== AllowInHeredoc: true [source,ruby] ---- @@ -6399,7 +6399,7 @@ RUBY | Name | Default value | Configurable values | AllowInHeredoc -| `true` +| `false` | Boolean |=== diff --git a/lib/rubocop/cop/layout/trailing_whitespace.rb b/lib/rubocop/cop/layout/trailing_whitespace.rb index d896bb4bf4b..525bc235220 100644 --- a/lib/rubocop/cop/layout/trailing_whitespace.rb +++ b/lib/rubocop/cop/layout/trailing_whitespace.rb @@ -14,7 +14,7 @@ module Layout # # good # x = 0 # - # @example AllowInHeredoc: false + # @example AllowInHeredoc: false (default) # # The line in this example contains spaces after the 0. # # bad # code = <<~RUBY @@ -32,7 +32,7 @@ module Layout # x = 0#{trailing_whitespace} # RUBY # - # @example AllowInHeredoc: true (default) + # @example AllowInHeredoc: true # # The line in this example contains spaces after the 0. # # good # code = <<~RUBY