Rubocop: Style/TrailingWhitespace marks offenses when whitespace is in a string

Created on 15 Jun 2017  路  9Comments  路  Source: rubocop-hq/rubocop

Expected behavior

Writing a multiline string with trailing whitespace for exact formatting must not cause a Style/TrailingWhitespace offense because rubocop cannot know the intended use of a string.

Actual behavior

Rubocop marks strings with trailing whitespace as a Style/TrailingWhitespace offense

Steps to reproduce the problem

Write t.rb:

STRING = <<-STRING
This line has a trailing space: 

This line has a trailing tab:   

Nothing in this file should cause a Style/TrailingWhitespace offense.
STRING

run rubocop:

$ rubocop t.rb
Inspecting 1 file
C

Offenses:

t.rb:1:10: C: Freeze mutable objects assigned to constants.
STRING = <<-STRING
         ^^^^^^^^^
t.rb:2:1: C: Use 2 spaces for indentation in a heredoc by using some library(e.g. ActiveSupport's String#strip_heredoc).
This line has a trailing space:  ...
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
t.rb:2:32: C: Trailing whitespace detected.
This line has a trailing space: 
                               ^
t.rb:4:30: C: Trailing whitespace detected.
This line has a trailing tab:   
                             ^

1 file inspected, 4 offenses detected

I do not expect the cops on lines 2 or 4 to appear.

RuboCop version

Include the output of rubocop -V. Here's an example:

$ rubocop -V
0.49.1 (using Parser 2.4.0.0, running on ruby 2.4.1 x86_64-darwin16)
bug

All 9 comments

Guys, I've looked into implementation of this cop. Wanted to fix this bug, but I have couple questions.

Current implementation looks like this:

        def investigate(processed_source)
          processed_source.lines.each_with_index do |line, index|
            next unless line.end_with?(' ', "\t")
            # couple more lines
          end
        end

So it goes through each line of code. Each line of code is a string.

To fix this bug I need to work with line of code as with node, right?
I found mixin called Heredoc which provides method on_heredoc. I could use that one.

Do you have any suggestions on proper implementation for this case?

Btw, for this cop there is an example that says: _it registers an offense for trailing whitespace in a heredoc string_ , so it was expected behavior for author of the cop. :)

@smakagon: The high level strategy I would use might be:

  1. Store all the heredoc line ranges in the file in a collection.
  2. When checking all the lines for trailing spaces, also check that it isn't in the collection from 1.

it was expected behavior for author of the cop

Yes. The suggested behaviour needs a configuration option. 馃檪

@Drenmi thanks for the answer.

To store all heredoc line ranges, I need to figure out that it's a heredoc.
It's not possible with current way of processing with: investigate(processed_source).

I was struggling with that because from one side I need to work with code as with an array of strings, but from the other side I want to know if it's a heredoc.

The investigate method is called before the on_ methods. If it was the other way around we'd be laughing. Now it's a bit tricky to figure out how we can first store all the line numbers where there are heredocs, and then check all lines. Or did you have a solution in mind for this @Drenmi?

@jonas054: Hm. That definitely makes it less straightforward than I envisioned. 馃

@Drenmi I played around a little and tried the following:

diff --git a/lib/rubocop/cop/layout/trailing_whitespace.rb b/lib/rubocop/cop/layout/trailing_whitespace.rb
index a662b5b..5d70a74 100644
--- a/lib/rubocop/cop/layout/trailing_whitespace.rb
+++ b/lib/rubocop/cop/layout/trailing_whitespace.rb
@@ -5,6 +5,7 @@ module RuboCop
     module Layout
       # This cop looks for trailing whitespace in the source code.
       class TrailingWhitespace < Cop
+        include Heredoc
         MSG = 'Trailing whitespace detected.'.freeze

         def investigate(processed_source)
@@ -19,6 +20,11 @@ module RuboCop
           end
         end

+        def on_heredoc(node)
+          heredoc_body = node.loc.heredoc_body
+          offenses.reject! { |o| heredoc_body.overlaps?(o.location) }
+        end
+
         def autocorrect(range)
           ->(corrector) { corrector.remove(range) }
         end

This is perhaps a bit ugly. We haven't used offenses.reject! in any cops before, removing already added offenses. But it does get the job done.

RubuCop still seems to want to treat heredoc strings as code, at least in some cops:

~ruby
puts <<~MSG
\nSome message: #{mystuff}
This is bad!
MSG
~

2018-02-21_01
2018-02-21_02

This is in RubyMine 2017.3.2 with RubuCop 0.52.1.

@reitzig Squiggly heredocs (~MSG) were introduced in Ruby 2.3. That's why the parser gem has problems with the syntax.

Add this to your configuration:

AllCops:
  TargetRubyVersion: 2.3

That makes sense. I'll have to see how to configure this for the "built-in" RuboCop in RubyMine. Thanks!

For reference: there are issues on the RubyMine bugtracker about this, e.g. this.

_Amendment:_ Adding a .rubocop.yml to the project root was enough.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

deivid-rodriguez picture deivid-rodriguez  路  3Comments

Ana06 picture Ana06  路  3Comments

AndreiMotinga picture AndreiMotinga  路  3Comments

Aqualon picture Aqualon  路  3Comments

benoittgt picture benoittgt  路  3Comments