Skip to content

Commit

Permalink
[Fix #11219] Make Style/SelectByRegexp aware of !~ method
Browse files Browse the repository at this point in the history
Fixes #11219.

This PR makes `Style/SelectByRegexp` aware of `!~` method.
  • Loading branch information
koic authored and bbatsov committed Dec 2, 2022
1 parent b246658 commit 658d1bc
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* [#11219](https://github.com/rubocop/rubocop/issues/11219): Make `Style/SelectByRegexp` aware of `!~` method. ([@koic][])
12 changes: 8 additions & 4 deletions lib/rubocop/cop/style/select_by_regexp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ class SelectByRegexp < Base
MSG = 'Prefer `%<replacement>s` to `%<original_method>s` with a regexp match.'
RESTRICT_ON_SEND = %i[select find_all reject].freeze
REPLACEMENTS = { select: 'grep', find_all: 'grep', reject: 'grep_v' }.freeze
REGEXP_METHODS = %i[match? =~].to_set.freeze
OPPOSITE_REPLACEMENTS = { select: 'grep_v', find_all: 'grep_v', reject: 'grep' }.freeze
REGEXP_METHODS = %i[match? =~ !~].to_set.freeze

# @!method regexp_match?(node)
def_node_matcher :regexp_match?, <<~PATTERN
Expand Down Expand Up @@ -90,8 +91,10 @@ def on_send(node)
return unless (regexp_method_send_node = extract_send_node(block_node))
return if match_predicate_without_receiver?(regexp_method_send_node)

opposite = regexp_method_send_node.send_type? && regexp_method_send_node.method?(:!~)
regexp = find_regexp(regexp_method_send_node, block_node)
register_offense(node, block_node, regexp)

register_offense(node, block_node, regexp, opposite)
end

private
Expand All @@ -102,8 +105,9 @@ def receiver_allowed?(node)
node.hash_type? || creates_hash?(node) || env_const?(node)
end

def register_offense(node, block_node, regexp)
replacement = REPLACEMENTS[node.method_name.to_sym]
def register_offense(node, block_node, regexp, opposite)
method_name = node.method_name.to_sym
replacement = opposite ? OPPOSITE_REPLACEMENTS[method_name] : REPLACEMENTS[method_name]
message = format(MSG, replacement: replacement, original_method: node.method_name)

add_offense(block_node, message: message) do |corrector|
Expand Down
101 changes: 101 additions & 0 deletions spec/rubocop/cop/style/select_by_regexp_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -321,4 +321,105 @@
end
end
end

{ 'select' => 'grep_v', 'find_all' => 'grep_v', 'reject' => 'grep' }.each do |method, correction|
message = "Prefer `#{correction}` to `#{method}` with a regexp match."

context "with #{method}" do
it 'registers an offense and corrects for `blockvar !~ regexp`' do
expect_offense(<<~RUBY, method: method)
array.#{method} { |x| x !~ /regexp/ }
^^^^^^^{method}^^^^^^^^^^^^^^^^^^^^^^ #{message}
RUBY

expect_correction(<<~RUBY)
array.#{correction}(/regexp/)
RUBY
end

it 'registers an offense and corrects for `blockvar !~ lvar`' do
expect_offense(<<~RUBY, method: method)
lvar = /regexp/
array.#{method} { |x| x !~ lvar }
^^^^^^^{method}^^^^^^^^^^^^^^^^^^ #{message}
RUBY

expect_correction(<<~RUBY)
lvar = /regexp/
array.#{correction}(lvar)
RUBY
end

it 'registers an offense and corrects for `regexp !~ blockvar`' do
expect_offense(<<~RUBY, method: method)
array.#{method} { |x| /regexp/ !~ x }
^^^^^^^{method}^^^^^^^^^^^^^^^^^^^^^^ #{message}
RUBY

expect_correction(<<~RUBY)
array.#{correction}(/regexp/)
RUBY
end

it 'registers an offense and corrects for `lvar !~ blockvar`' do
expect_offense(<<~RUBY, method: method)
lvar = /regexp/
array.#{method} { |x| lvar !~ x }
^^^^^^^{method}^^^^^^^^^^^^^^^^^^ #{message}
RUBY

expect_correction(<<~RUBY)
lvar = /regexp/
array.#{correction}(lvar)
RUBY
end

it 'registers an offense and corrects when there is no explicit regexp' do
expect_offense(<<~RUBY, method: method)
array.#{method} { |x| x !~ y }
^^^^^^^{method}^^^^^^^^^^^^^^^ #{message}
array.#{method} { |x| x !~ REGEXP }
^^^^^^^{method}^^^^^^^^^^^^^^^^^^^^ #{message}
array.#{method} { |x| x !~ foo.bar.baz(quux) }
^^^^^^^{method}^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{message}
RUBY

expect_correction(<<~RUBY)
array.#{correction}(y)
array.#{correction}(REGEXP)
array.#{correction}(foo.bar.baz(quux))
RUBY
end

context 'with `numblock`s', :ruby27 do
it 'registers an offense and corrects for `blockvar !~ regexp`' do
expect_offense(<<~RUBY, method: method)
array.#{method} { _1 !~ /regexp/ }
^^^^^^^{method}^^^^^^^^^^^^^^^^^^^ #{message}
RUBY

expect_correction(<<~RUBY)
array.#{correction}(/regexp/)
RUBY
end

it 'registers an offense and corrects for `regexp !~ blockvar`' do
expect_offense(<<~RUBY, method: method)
array.#{method} { /regexp/ !~ _1 }
^^^^^^^{method}^^^^^^^^^^^^^^^^^^^ #{message}
RUBY

expect_correction(<<~RUBY)
array.#{correction}(/regexp/)
RUBY
end

it 'does not register an offense if there is more than one numbered param' do
expect_no_offenses(<<~RUBY)
array.#{method} { _1 !~ _2 }
RUBY
end
end
end
end
end

0 comments on commit 658d1bc

Please sign in to comment.