Skip to content

Commit

Permalink
Add InternalAffairs/ExampleHeredocDelimiter cop
Browse files Browse the repository at this point in the history
  • Loading branch information
r7kamura committed Oct 21, 2022
1 parent 38e8511 commit 7502a9e
Show file tree
Hide file tree
Showing 8 changed files with 401 additions and 204 deletions.
4 changes: 4 additions & 0 deletions .rubocop.yml
Expand Up @@ -163,6 +163,10 @@ InternalAffairs/ExampleDescription:
Include:
- 'spec/rubocop/cop/**/*.rb'

InternalAffairs/ExampleHeredocDelimiter:
Include:
- 'spec/rubocop/cop/**/*.rb'

InternalAffairs/UndefinedConfig:
Include:
- 'lib/rubocop/cop/**/*.rb'
Expand Down
1 change: 1 addition & 0 deletions lib/rubocop/cop/internal_affairs.rb
Expand Up @@ -3,6 +3,7 @@
require_relative 'internal_affairs/cop_description'
require_relative 'internal_affairs/empty_line_between_expect_offense_and_correction'
require_relative 'internal_affairs/example_description'
require_relative 'internal_affairs/example_heredoc_delimiter'
require_relative 'internal_affairs/inherit_deprecated_cop_class'
require_relative 'internal_affairs/location_line_equality_comparison'
require_relative 'internal_affairs/method_name_end_with'
Expand Down
111 changes: 111 additions & 0 deletions lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb
@@ -0,0 +1,111 @@
# frozen_string_literal: true

module RuboCop
module Cop
module InternalAffairs
# Use `RUBY` for heredoc delimiter of example Ruby code.
#
# Some editors may apply better syntax highlighting by using appropriate language names for
# the delimiter.
#
# @example
# # bad
# expect_offense(<<~CODE)
# example_ruby_code
# CODE
#
# # good
# expect_offense(<<~RUBY)
# example_ruby_code
# RUBY
class ExampleHeredocDelimiter < Base
extend AutoCorrector

EXPECTED_HEREDOC_DELIMITER = 'RUBY'

MSG = 'Use `RUBY` for heredoc delimiter of example Ruby code.'

RESTRICT_ON_SEND = %i[
expect_correction
expect_no_corrections
expect_no_offenses
expect_offense
].freeze

# @param node [RuboCop::AST::SendNode]
# @return [void]
def on_send(node)
heredoc_node = heredoc_node_from(node)
return unless heredoc_node
return if expected_heredoc_delimiter?(heredoc_node)
return if expected_heredoc_delimiter_in_body?(heredoc_node)

add_offense(heredoc_node) do |corrector|
autocorrect(corrector, heredoc_node)
end
end

private

# @param corrector [RuboCop::Cop::Corrector]
# @param node [RuboCop::AST::StrNode]
# @return [void]
def autocorrect(corrector, node)
[
heredoc_openning_delimiter_range_from(node),
heredoc_closing_delimiter_range_from(node)
].each do |range|
corrector.replace(range, EXPECTED_HEREDOC_DELIMITER)
end
end

# @param node [RuboCop::AST::StrNode]
# @return [Boolean]
def expected_heredoc_delimiter_in_body?(node)
node.location.heredoc_body.source.lines.any? do |line|
line.strip == EXPECTED_HEREDOC_DELIMITER
end
end

# @param node [RuboCop::AST::StrNode]
# @return [Boolean]
def expected_heredoc_delimiter?(node)
heredoc_delimiter_string_from(node) == EXPECTED_HEREDOC_DELIMITER
end

# @param node [RuboCop::AST::SendNode]
# @return [RuboCop::AST::StrNode, nil]
def heredoc_node_from(node)
return unless node.first_argument.respond_to?(:heredoc?)
return unless node.first_argument.heredoc?

node.first_argument
end

# @param node [RuboCop::AST::StrNode]
# @return [String]
def heredoc_delimiter_string_from(node)
node.source[Heredoc::OPENING_DELIMITER, 2]
end

# @param node [RuboCop::AST::StrNode]
# @return [Parser::Source::Range]
def heredoc_openning_delimiter_range_from(node)
match_data = node.source.match(Heredoc::OPENING_DELIMITER)
node.location.expression.begin.adjust(
begin_pos: match_data.begin(2),
end_pos: match_data.end(2)
)
end

# @param node [RuboCop::AST::StrNode]
# @return [Parser::Source::Range]
def heredoc_closing_delimiter_range_from(node)
node.location.heredoc_end.end.adjust(
begin_pos: -heredoc_delimiter_string_from(node).length
)
end
end
end
end
end
24 changes: 12 additions & 12 deletions spec/rubocop/cop/bundler/duplicated_gem_spec.rb
Expand Up @@ -31,29 +31,29 @@

context 'and a gem is duplicated in default group' do
it 'registers an offense' do
expect_offense(<<-GEM, 'Gemfile')
expect_offense(<<-RUBY, 'Gemfile')
source 'https://rubygems.org'
gem 'rubocop'
gem 'rubocop'
^^^^^^^^^^^^^ Gem `rubocop` requirements already given on line 2 of the Gemfile.
GEM
RUBY
end
end

context 'and a duplicated gem is in a git/path/group/platforms block' do
it 'registers an offense' do
expect_offense(<<-GEM, 'Gemfile')
expect_offense(<<-RUBY, 'Gemfile')
gem 'rubocop'
group :development do
gem 'rubocop', path: '/path/to/gem'
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Gem `rubocop` requirements already given on line 1 of the Gemfile.
end
GEM
RUBY
end
end

it 'registers an offense when gem from default group is conditionally duplicated' do
expect_offense(<<-GEM, 'Gemfile')
expect_offense(<<-RUBY, 'Gemfile')
gem 'rubocop'
if Dir.exist? local
gem 'rubocop', path: local
Expand All @@ -62,22 +62,22 @@
gem 'rubocop', '~> 0.90.0'
^^^^^^^^^^^^^^^^^^^^^^^^^^ Gem `rubocop` requirements already given on line 1 of the Gemfile.
end
GEM
RUBY
end

it 'does not register an offense when gem is duplicated within `if-else` statement' do
expect_no_offenses(<<-GEM, 'Gemfile')
expect_no_offenses(<<-RUBY, 'Gemfile')
if Dir.exist?(local)
gem 'rubocop', path: local
gem 'flog', path: local
else
gem 'rubocop', '~> 0.90.0'
end
GEM
RUBY
end

it 'does not register an offense when gem is duplicated within `if-elsif` statement' do
expect_no_offenses(<<-GEM, 'Gemfile')
expect_no_offenses(<<-RUBY, 'Gemfile')
if Dir.exist?(local)
gem 'rubocop', path: local
elsif ENV['RUBOCOP_VERSION'] == 'master'
Expand All @@ -87,11 +87,11 @@
else
gem 'rubocop', '~> 0.90.0'
end
GEM
RUBY
end

it 'does not register an offense when gem is duplicated within `case` statement' do
expect_no_offenses(<<-GEM, 'Gemfile')
expect_no_offenses(<<-RUBY, 'Gemfile')
case
when Dir.exist?(local)
gem 'rubocop', path: local
Expand All @@ -100,7 +100,7 @@
else
gem 'rubocop', '~> 0.90.0'
end
GEM
RUBY
end
end
end

0 comments on commit 7502a9e

Please sign in to comment.