Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new Style/RedundantHeredocDelimiterQuotes cop #11528

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* [#11528](https://github.com/rubocop/rubocop/pull/11528): Add new `Style/RedundantHeredocDelimiterQuotes` cop. ([@koic][])
5 changes: 5 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4843,6 +4843,11 @@ Style/RedundantFreeze:
VersionAdded: '0.34'
VersionChanged: '0.66'

Style/RedundantHeredocDelimiterQuotes:
Description: 'Checks for redundant heredoc delimiter quotes.'
Enabled: pending
VersionAdded: '<<next>>'

Style/RedundantInitialize:
Description: 'Checks for redundant `initialize` methods.'
Enabled: pending
Expand Down
1 change: 1 addition & 0 deletions lib/rubocop.rb
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,7 @@
require_relative 'rubocop/cop/style/redundant_each'
require_relative 'rubocop/cop/style/redundant_fetch_block'
require_relative 'rubocop/cop/style/redundant_file_extension_in_require'
require_relative 'rubocop/cop/style/redundant_heredoc_delimiter_quotes'
require_relative 'rubocop/cop/style/redundant_initialize'
require_relative 'rubocop/cop/style/redundant_self_assignment'
require_relative 'rubocop/cop/style/redundant_self_assignment_branch'
Expand Down
57 changes: 57 additions & 0 deletions lib/rubocop/cop/style/redundant_heredoc_delimiter_quotes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Style
# Checks for redundant heredoc delimiter quotes.
#
# @example
#
# # bad
# do_something(<<~'EOS')
# no string interpolation style text
# EOS
#
# # good
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably you should also add a good example for 'EOS', so it's a bit clearer when it should be used.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Certainly! I added some examples for that.

# do_something(<<~EOS)
# no string interpolation style text
# EOS
#
# do_something(<<~'EOS')
# #{string_interpolation_style_text_not_evaluated}
# EOS
#
# do_something(<<~'EOS')
# escaped character\.
# EOS
#
class RedundantHeredocDelimiterQuotes < Base
include Heredoc
extend AutoCorrector

MSG = 'Remove the redundant heredoc delimiter quotes, use `%<replacement>s` instead.'
STRING_INTERPOLATION_OR_ESCAPED_CHARACTER_PATTERN = /#(\{|@|\$)|\\/.freeze
Copy link
Contributor

@splattael splattael Feb 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@koic #11543 adds a missing spec to test escapes - the last \\ in the regexp.


def on_heredoc(node)
return if need_heredoc_delimiter_quotes?(node)

replacement = "#{heredoc_type(node)}#{delimiter_string(node)}"

add_offense(node, message: format(MSG, replacement: replacement)) do |corrector|
corrector.replace(node, replacement)
end
end

private

def need_heredoc_delimiter_quotes?(node)
heredoc_delimiter = node.source.delete(heredoc_type(node))
return true unless heredoc_delimiter.start_with?("'", '"')

node.loc.heredoc_end.source.strip.match?(/\W/) ||
node.loc.heredoc_body.source.match?(STRING_INTERPOLATION_OR_ESCAPED_CHARACTER_PATTERN)
end
end
end
end
end
20 changes: 10 additions & 10 deletions spec/rubocop/cli/autocorrect_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -716,12 +716,12 @@ def method
end

it 'corrects Style/InverseMethods and Style/Not offenses' do
source = <<~'RUBY'
source = <<~RUBY
x.select {|y| not y.z }
RUBY
create_file('example.rb', source)
expect(cli.run(['--autocorrect-all', '--only', 'Style/InverseMethods,Style/Not'])).to eq(0)
corrected = <<~'RUBY'
corrected = <<~RUBY
x.reject {|y| y.z }
RUBY
expect(File.read('example.rb')).to eq(corrected)
Expand All @@ -732,7 +732,7 @@ def method
AllCops:
TargetRubyVersion: 2.6
YAML
source = <<~'RUBY'
source = <<~RUBY
until x
if foo
foo.some_method do
Expand All @@ -743,7 +743,7 @@ def method
RUBY
create_file('example.rb', source)
expect(cli.run(['--autocorrect-all', '--only', 'Style/Next,Style/SafeNavigation'])).to eq(0)
corrected = <<~'RUBY'
corrected = <<~RUBY
until x
next unless foo
foo.some_method do
Expand All @@ -755,7 +755,7 @@ def method
end

it 'corrects `Lint/Lambda` and `Lint/UnusedBlockArgument` offenses' do
source = <<~'RUBY'
source = <<~RUBY
c = -> event do
puts 'Hello world'
end
Expand All @@ -765,7 +765,7 @@ def method
'--autocorrect-all',
'--only', 'Lint/Lambda,Lint/UnusedBlockArgument'
])).to eq(0)
corrected = <<~'RUBY'
corrected = <<~RUBY
c = lambda do |_event|
puts 'Hello world'
end
Expand Down Expand Up @@ -1100,7 +1100,7 @@ def method
EnforcedStyle: indented_internal_methods
YAML

source = <<~'RUBY'
source = <<~RUBY
class Foo
private

Expand All @@ -1121,7 +1121,7 @@ def do_something
].join(',')
])).to eq(0)

corrected = <<~'RUBY'
corrected = <<~RUBY
class Foo
private

Expand All @@ -1135,7 +1135,7 @@ def do_something

it 'corrects IndentationWidth and IndentationConsistency offenses' \
'without correcting `Style/TrailingBodyOnClass`' do
source = <<~'RUBY'
source = <<~RUBY
class Test foo
def func1
end
Expand All @@ -1151,7 +1151,7 @@ def func2
['Layout/IndentationConsistency', 'Layout/IndentationWidth'].join(',')
])).to eq(0)

corrected = <<~'RUBY'
corrected = <<~RUBY
class Test foo
def func1
end
Expand Down
2 changes: 1 addition & 1 deletion spec/rubocop/config_loader_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -786,7 +786,7 @@ def enabled?(cop)
end

if custom_dept_to_disable == 'Foo'
message = <<~'OUTPUT'.chomp
message = <<~OUTPUT.chomp
unrecognized cop or department Foo found in parent_rubocop.yml
Foo is not a department. Use `Foo/Bar`.
OUTPUT
Expand Down
4 changes: 2 additions & 2 deletions spec/rubocop/cop/layout/class_structure_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ def do_something
end

it 'registers an offense and corrects when xstr heredoc constant is defined after public method' do
expect_offense(<<~'RUBY')
expect_offense(<<~RUBY)
class Foo
def do_something
end
Expand All @@ -375,7 +375,7 @@ def do_something
end
RUBY

expect_correction(<<~'RUBY')
expect_correction(<<~RUBY)
class Foo
CONSTANT = <<~`EOS`
str
Expand Down
8 changes: 4 additions & 4 deletions spec/rubocop/cop/layout/dot_position_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@

context 'with multiple heredocs' do
it 'registers an offense' do
expect_offense(<<~'RUBY')
expect_offense(<<~RUBY)
my_method.
^ Place the . on the next line, together with the method name.
something(<<~HERE, <<~THERE).
Expand All @@ -246,7 +246,7 @@
somethingelse
RUBY

expect_correction(<<~'RUBY')
expect_correction(<<~RUBY)
my_method
.something(<<~HERE, <<~THERE)
something
Expand Down Expand Up @@ -440,7 +440,7 @@

context 'with multiple heredocs' do
it 'registers an offense' do
expect_offense(<<~'RUBY')
expect_offense(<<~RUBY)
my_method
.something(<<~HERE, <<~THERE)
^ Place the . on the previous line, together with the method call receiver.
Expand All @@ -452,7 +452,7 @@
^ Place the . on the previous line, together with the method call receiver.
RUBY

expect_correction(<<~'RUBY')
expect_correction(<<~RUBY)
my_method.
something(<<~HERE, <<~THERE).
something
Expand Down
2 changes: 1 addition & 1 deletion spec/rubocop/cop/layout/else_alignment_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@
end

it 'accepts case match without else' do
expect_no_offenses(<<~'RUBY')
expect_no_offenses(<<~RUBY)
case 0
in a
p a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def foo

it 'accepts a `raise` guard clause not followed by empty line when guard ' \
'clause is after condition without method invocation' do
expect_no_offenses(<<~'RUBY')
expect_no_offenses(<<~RUBY)
def foo
raise unless $1 == o

Expand Down
6 changes: 3 additions & 3 deletions spec/rubocop/cop/layout/indentation_consistency_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
end

it 'accepts when using access modifier at the top level' do
expect_no_offenses(<<~'RUBY')
expect_no_offenses(<<~RUBY)
public

def foo
Expand All @@ -21,15 +21,15 @@ def foo

it 'registers and corrects an offense when using access modifier and indented method definition ' \
'at the top level' do
expect_offense(<<~'RUBY')
expect_offense(<<~RUBY)
public

def foo
^^^^^^^ Inconsistent indentation detected.
end
RUBY

expect_correction(<<~'RUBY')
expect_correction(<<~RUBY)
public

def foo
Expand Down
4 changes: 2 additions & 2 deletions spec/rubocop/cop/layout/indentation_width_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -905,7 +905,7 @@ def x
end

it 'accepts aligned values in `in` clause' do
expect_no_offenses(<<~'RUBY')
expect_no_offenses(<<~RUBY)
case condition
in [42]
foo
Expand All @@ -916,7 +916,7 @@ def x
end

it 'accepts aligned value in `in` clause and `else` is empty' do
expect_no_offenses(<<~'RUBY')
expect_no_offenses(<<~RUBY)
case x
in 42
foo
Expand Down
6 changes: 3 additions & 3 deletions spec/rubocop/cop/layout/leading_comment_space_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@
let(:cop_config) { { 'AllowGemfileRubyComment' => false } }

it 'registers an offense when using ruby config as comment' do
expect_offense(<<~'RUBY')
expect_offense(<<~RUBY)
# Specific version (comment) will be used by RVM
#ruby=2.7.0
^^^^^^^^^^^ Missing space after `#`.
Expand All @@ -184,7 +184,7 @@

context 'file not named Gemfile' do
it 'registers an offense when using ruby config as comment' do
expect_offense(<<~'RUBY', 'test/test_case.rb')
expect_offense(<<~RUBY, 'test/test_case.rb')
# Specific version (comment) will be used by RVM
#ruby=2.7.0
^^^^^^^^^^^ Missing space after `#`.
Expand All @@ -197,7 +197,7 @@

context 'file named Gemfile' do
it 'does not register an offense when using ruby config as comment' do
expect_no_offenses(<<~'RUBY', 'Gemfile')
expect_no_offenses(<<~RUBY, 'Gemfile')
# Specific version (comment) will be used by RVM
#ruby=2.7.0
#ruby-gemset=myproject
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
end

it 'accepts a multiline string literal' do
expect_no_offenses(<<~'RUBY')
expect_no_offenses(<<~RUBY)
puts %(
foo
bar
Expand Down Expand Up @@ -128,7 +128,7 @@ def some_method
end

it 'accepts a heredoc string ...' do
expect_no_offenses(<<~'RUBY')
expect_no_offenses(<<~RUBY)
let(:source) do
<<~CODE
func({
Expand All @@ -152,7 +152,7 @@ def some_method
end

it 'accepts an empty heredoc string with interpolation' do
expect_no_offenses(<<~'RUBY')
expect_no_offenses(<<~RUBY)
puts(<<~TEXT)
TEXT
RUBY
Expand Down
2 changes: 1 addition & 1 deletion spec/rubocop/cop/layout/space_inside_parens_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
end

it 'registers an offense for space around heredoc start' do
expect_offense(<<~'RUBY')
expect_offense(<<~RUBY)
f( <<~HEREDOC )
^ Space inside parentheses detected.
^ Space inside parentheses detected.
Expand Down
4 changes: 2 additions & 2 deletions spec/rubocop/cop/lint/empty_interpolation_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
^^^ Empty interpolation detected.
RUBY

expect_correction(<<~'RUBY')
expect_correction(<<~RUBY)
"this is the "
RUBY
end
Expand All @@ -18,7 +18,7 @@
^^^^ Empty interpolation detected.
RUBY

expect_correction(<<~'RUBY')
expect_correction(<<~RUBY)
"this is the "
RUBY
end
Expand Down
2 changes: 1 addition & 1 deletion spec/rubocop/cop/lint/mixed_regexp_capture_types_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

# See https://github.com/rubocop/rubocop/issues/8083
it 'does not register offense when using a Regexp cannot be processed by regexp_parser gem' do
expect_no_offenses(<<~'RUBY')
expect_no_offenses(<<~RUBY)
/data = ({"words":.+}}}[^}]*})/m
RUBY
end
Expand Down