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/InPatternThen cop #9833

Merged
merged 1 commit into from May 30, 2021
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
1 change: 1 addition & 0 deletions changelog/new_add_new_style_in_pattern_then_cop.md
@@ -0,0 +1 @@
* [#9833](https://github.com/rubocop/rubocop/pull/9833): Add new `Style/InPatternThen` cop. ([@koic][])
6 changes: 6 additions & 0 deletions config/default.yml
Expand Up @@ -3608,6 +3608,12 @@ Style/ImplicitRuntimeError:
Enabled: false
VersionAdded: '0.41'

Style/InPatternThen:
Description: 'Checks for `in;` uses in `case` expressions.'
StyleGuide: '#no-in-pattern-semicolons'
Enabled: pending
VersionAdded: '<<next>>'

Style/InfiniteLoop:
Description: >-
Use Kernel#loop for infinite loops.
Expand Down
1 change: 1 addition & 0 deletions lib/rubocop.rb
Expand Up @@ -492,6 +492,7 @@
require_relative 'rubocop/cop/style/if_with_boolean_literal_branches'
require_relative 'rubocop/cop/style/if_with_semicolon'
require_relative 'rubocop/cop/style/implicit_runtime_error'
require_relative 'rubocop/cop/style/in_pattern_then'
require_relative 'rubocop/cop/style/infinite_loop'
require_relative 'rubocop/cop/style/inverse_methods'
require_relative 'rubocop/cop/style/inline_comment'
Expand Down
56 changes: 56 additions & 0 deletions lib/rubocop/cop/style/in_pattern_then.rb
@@ -0,0 +1,56 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Style
# This cop checks for `in;` uses in `case` expressions.
#
# @example
# # bad
# case expression
# in pattern_a; foo
# in pattern_b; bar
# end
#
# # good
# case expression
# in pattern_a then foo
# in pattern_b then bar
# end
#
class InPatternThen < Base
extend AutoCorrector
extend TargetRubyVersion

minimum_target_ruby_version 2.7

MSG = 'Do not use `in %<pattern>s;`. Use `in %<pattern>s then` instead.'

def on_in_pattern(node)
return if node.multiline? || node.then? || !node.body

pattern = node.pattern
pattern_source = if pattern.match_alt_type?
alternative_pattern_source(pattern)
else
pattern.source
end

add_offense(node.loc.begin, message: format(MSG, pattern: pattern_source)) do |corrector|
corrector.replace(node.loc.begin, ' then')
end
end

private

def alternative_pattern_source(pattern)
return pattern.children.map(&:source) unless pattern.children.first.match_alt_type?

pattern_sources = alternative_pattern_source(pattern.children.first)

(pattern_sources << pattern.children[1].source).join(' | ')
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.6.0', '< 2.0')
s.add_runtime_dependency('rubocop-ast', '>= 1.7.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
88 changes: 88 additions & 0 deletions spec/rubocop/cop/style/in_pattern_then_spec.rb
@@ -0,0 +1,88 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::Style::InPatternThen, :config do
context '>= Ruby 2.7', :ruby27 do
it 'registers an offense for `in b;`' do
expect_offense(<<~RUBY)
case a
in b; c
^ Do not use `in b;`. Use `in b then` instead.
end
RUBY

expect_correction(<<~RUBY)
case a
in b then c
end
RUBY
end

it 'registers an offense for `in b, c, d;` (array pattern)' do
expect_offense(<<~RUBY)
case a
in b, c, d; e
^ Do not use `in b, c, d;`. Use `in b, c, d then` instead.
end
RUBY

expect_correction(<<~RUBY)
case a
in b, c, d then e
end
RUBY
end

it 'registers an offense for `in b | c | d;` (alternative pattern)' do
expect_offense(<<~RUBY)
case a
in b | c | d; e
^ Do not use `in b | c | d;`. Use `in b | c | d then` instead.
end
RUBY

expect_correction(<<~RUBY)
case a
in b | c | d then e
end
RUBY
end

it 'registers an offense for `in b, c | d;`' do
expect_offense(<<~RUBY)
case a
in b, c | d; e
^ Do not use `in b, c | d;`. Use `in b, c | d then` instead.
end
RUBY

expect_correction(<<~RUBY)
case a
in b, c | d then e
end
RUBY
end

it 'accepts `;` separating statements in the body of `in`' do
expect_no_offenses(<<~RUBY)
case a
in b then c; d
end

case e
in f
g; h
end
RUBY
end

context 'when inspecting a case statement with an empty branch' do
it 'does not register an offense' do
expect_no_offenses(<<~RUBY)
case condition
in pattern
end
RUBY
end
end
end
end