diff --git a/CHANGELOG.md b/CHANGELOG.md index f2849321046..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) @@ -151,6 +152,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/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 e277dcfe376..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] ---- @@ -6369,9 +6369,20 @@ 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) +==== AllowInHeredoc: true [source,ruby] ---- @@ -6388,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 143492fa976..525bc235220 100644 --- a/lib/rubocop/cop/layout/trailing_whitespace.rb +++ b/lib/rubocop/cop/layout/trailing_whitespace.rb @@ -14,14 +14,25 @@ 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 # x = 0 # RUBY # - # @example AllowInHeredoc: true (default) + # # ok + # code = <<~RUBY + # x = 0 #{} + # RUBY + # + # # good + # trailing_whitespace = ' ' + # code = <<~RUBY + # x = 0#{trailing_whitespace} + # RUBY + # + # @example AllowInHeredoc: true # # The line in this example contains spaces after the 0. # # good # code = <<~RUBY @@ -35,36 +46,49 @@ 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| - 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| + process_line(line, index + 1) + end + end + + private + + def process_line(line, lineno) + heredoc = find_heredoc(lineno) + return if skip_heredoc? && heredoc + + range = offense_range(lineno, line) + add_offense(range) do |corrector| + if heredoc + corrector.insert_after(range, '#{}') unless static?(heredoc) # rubocop:disable Lint/InterpolationCheck + else corrector.remove(range) end end end - private + def static?(heredoc) + heredoc.loc.expression.source.end_with? "'" + 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 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 66dc1ecb998..cd05d5a9e3e 100644 --- a/spec/rubocop/cop/layout/trailing_whitespace_spec.rb +++ b/spec/rubocop/cop/layout/trailing_whitespace_spec.rb @@ -89,4 +89,37 @@ 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 + + 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