Skip to content

Commit

Permalink
Add new Lint/EmptyInPattern cop
Browse files Browse the repository at this point in the history
This PR adds new `Lint/EmptyInPattern` cop for Ruby 2.7's pattern matching.
It checks for the presence of `in` branches without a body.

```ruby
# bad
case condition
in [a]
  do_something
in [a, b]
end

# good
case condition
in [a]
  do_something
in [a, b]
  nil
end

# good - AllowComments: true (default)
case condition
in [a]
  do_something
in [a, b]
  # noop
end

# bad - AllowComments: false
case condition
in [a]
  do_something
in [a, b]
  # noop
end
```

This cop is similar to `Lint/EmptyWhen`, but with different supported
syntax and Ruby version (requires 2.7 or higher).

And this PR use rubocop/rubocop-ast#183 feature,
so it requires RuboCop AST 1.6.0 or higher.
  • Loading branch information
koic committed May 26, 2021
1 parent b7928e7 commit 0cc37f6
Show file tree
Hide file tree
Showing 6 changed files with 235 additions and 1 deletion.
1 change: 1 addition & 0 deletions changelog/new_add_new_lint_empty_in_pattern_cop.md
@@ -0,0 +1 @@
* [#9825](https://github.com/rubocop/rubocop/pull/9825): Add new `Lint/EmptyInPattern` cop. ([@koic][])
5 changes: 5 additions & 0 deletions config/default.yml
Expand Up @@ -1646,6 +1646,11 @@ Lint/EmptyFile:
AllowComments: true
VersionAdded: '0.90'

Lint/EmptyInPattern:
Description: 'Checks for the presence of `in` pattern branches without a body.'
Enabled: pending
VersionAdded: '<<next>>'

Lint/EmptyInterpolation:
Description: 'Checks for empty string interpolation.'
Enabled: true
Expand Down
1 change: 1 addition & 0 deletions lib/rubocop.rb
Expand Up @@ -288,6 +288,7 @@
require_relative 'rubocop/cop/lint/empty_ensure'
require_relative 'rubocop/cop/lint/empty_expression'
require_relative 'rubocop/cop/lint/empty_file'
require_relative 'rubocop/cop/lint/empty_in_pattern'
require_relative 'rubocop/cop/lint/empty_interpolation'
require_relative 'rubocop/cop/lint/empty_when'
require_relative 'rubocop/cop/lint/ensure_return'
Expand Down
62 changes: 62 additions & 0 deletions lib/rubocop/cop/lint/empty_in_pattern.rb
@@ -0,0 +1,62 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Lint
# This cop checks for the presence of `in` pattern branches without a body.
#
# @example
#
# # bad
# case condition
# in [a]
# do_something
# in [a, b]
# end
#
# # good
# case condition
# in [a]
# do_something
# in [a, b]
# nil
# end
#
# @example AllowComments: true (default)
#
# # good
# case condition
# in [a]
# do_something
# in [a, b]
# # noop
# end
#
# @example AllowComments: false
#
# # bad
# case condition
# in [a]
# do_something
# in [a, b]
# # noop
# end
#
class EmptyInPattern < Base
extend TargetRubyVersion

MSG = 'Avoid `in` branches without a body.'

minimum_target_ruby_version 2.7

def on_case_match(node)
node.in_pattern_branches.each do |branch|
next if branch.body || cop_config['AllowComments'] && comment_lines?(node)

add_offense(branch)
end
end
end
end
end
end
2 changes: 1 addition & 1 deletion rubocop.gemspec
Expand Up @@ -35,7 +35,7 @@ Gem::Specification.new do |s|
s.add_runtime_dependency('rainbow', '>= 2.2.2', '< 4.0')
s.add_runtime_dependency('regexp_parser', '>= 1.8', '< 3.0')
s.add_runtime_dependency('rexml')
s.add_runtime_dependency('rubocop-ast', '>= 1.5.0', '< 2.0')
s.add_runtime_dependency('rubocop-ast', '>= 1.6.0', '< 2.0')
s.add_runtime_dependency('ruby-progressbar', '~> 1.7')
s.add_runtime_dependency('unicode-display_width', '>= 1.4.0', '< 3.0')

Expand Down
165 changes: 165 additions & 0 deletions spec/rubocop/cop/lint/empty_in_pattern_spec.rb
@@ -0,0 +1,165 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::Lint::EmptyInPattern, :config do
let(:cop_config) { { 'AllowComments' => false } }

context 'when a `in` body is missing', :ruby27 do
it 'registers an offense for a missing `in` body' do
expect_offense(<<~RUBY)
case foo
in [a] then 1
in [a, b] # nothing
^^^^^^^^^ Avoid `in` branches without a body.
end
RUBY

expect_no_corrections
end

it 'registers an offense for missing `in` body followed by `else`' do
expect_offense(<<~RUBY)
case foo
in [a] then 1
in [a, b] # nothing
^^^^^^^^^ Avoid `in` branches without a body.
else 3
end
RUBY

expect_no_corrections
end

it 'registers an offense for missing `in` ... `then` body' do
expect_offense(<<~RUBY)
case foo
in [a] then 1
in [a, b] then # nothing
^^^^^^^^^ Avoid `in` branches without a body.
end
RUBY

expect_no_corrections
end

it 'registers an offense for missing `in` ... then `body` followed by `else`' do
expect_offense(<<~RUBY)
case foo
in [a] then 1
in [a, b] then # nothing
^^^^^^^^^ Avoid `in` branches without a body.
else 3
end
RUBY

expect_no_corrections
end

it 'registers an offense for missing `in` body with a comment' do
expect_offense(<<~RUBY)
case foo
in [a]
1
in [a, b]
^^^^^^^^^ Avoid `in` branches without a body.
# nothing
end
RUBY

expect_no_corrections
end

it 'registers an offense for missing `in` body with a comment followed by `else`' do
expect_offense(<<~RUBY)
case foo
in [a]
1
in [a, b]
^^^^^^^^^ Avoid `in` branches without a body.
# nothing
else
3
end
RUBY

expect_no_corrections
end
end

context 'when a `in` body is present', :ruby27 do
it 'accepts `case` with `in` ... `then` statements' do
expect_no_offenses(<<~RUBY)
case foo
in [a] then 1
in [a, b] then 2
end
RUBY
end

it 'accepts `case` with `in` ... `then` statements and else clause' do
expect_no_offenses(<<~RUBY)
case foo
in [a] then 1
in [a, b] then 2
else 3
end
RUBY
end

it 'accepts `case` with `in` bodies' do
expect_no_offenses(<<~RUBY)
case foo
in [a]
1
in [a, b]
2
end
RUBY
end

it 'accepts `case` with `in` bodies and `else` clause' do
expect_no_offenses(<<~RUBY)
case foo
in [a]
1
in [a, b]
2
else
3
end
RUBY
end
end

context 'when `AllowComments: true`', :ruby27 do
let(:cop_config) { { 'AllowComments' => true } }

it 'accepts an empty `in` body with a comment' do
expect_no_offenses(<<~RUBY)
case condition
in [a]
do_something
in [a, b]
# do nothing
end
RUBY
end
end

context 'when `AllowComments: false`', :ruby27 do
let(:cop_config) { { 'AllowComments' => false } }

it 'registers an offense for empty `in` body with a comment' do
expect_offense(<<~RUBY)
case condition
in [a]
do_something
in [a, b]
^^^^^^^^^ Avoid `in` branches without a body.
# do nothing
end
RUBY

expect_no_corrections
end
end
end

0 comments on commit 0cc37f6

Please sign in to comment.