Skip to content

Commit

Permalink
Add new Style/MultilineInPatternThen cop
Browse files Browse the repository at this point in the history
Follow rubocop/ruby-style-guide#873.

This PR adds new `Style/MultilineInPatternThen` cop.
It checks uses of the `then` keyword in multi-line `in` statement.

```ruby
# bad
case expression
in pattern then
end

# good
case expression
in pattern
end
```

This cop is similar to `Style/MultilineWhenThen` (and `Style/MultilineIfThen`)
but with different supported syntax and Ruby version (requires 2.7 or higher).
  • Loading branch information
koic committed May 28, 2021
1 parent 9cb3e05 commit 3bd037c
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 1 deletion.
@@ -0,0 +1 @@
* [#9834](https://github.com/rubocop/rubocop/pull/9834): Add new `Style/MultilineInPatternThen` cop. ([@koic][])
6 changes: 6 additions & 0 deletions config/default.yml
Expand Up @@ -3836,6 +3836,12 @@ Style/MultilineIfThen:
VersionAdded: '0.9'
VersionChanged: '0.26'

Style/MultilineInPatternThen:
Description: 'Do not use `then` for multi-line `in` statement.'
StyleGuide: '#no-then'
Enabled: pending
VersionAdded: '<<next>>'

Style/MultilineMemoization:
Description: 'Wrap multiline memoizations in a `begin` and `end` block.'
Enabled: true
Expand Down
1 change: 1 addition & 0 deletions lib/rubocop.rb
Expand Up @@ -502,6 +502,7 @@
require_relative 'rubocop/cop/style/line_end_concatenation'
require_relative 'rubocop/cop/style/method_call_without_args_parentheses'
require_relative 'rubocop/cop/style/method_call_with_args_parentheses'
require_relative 'rubocop/cop/style/multiline_in_pattern_then'
require_relative 'rubocop/cop/style/redundant_assignment'
require_relative 'rubocop/cop/style/redundant_fetch_block'
require_relative 'rubocop/cop/style/redundant_file_extension_in_require'
Expand Down
62 changes: 62 additions & 0 deletions lib/rubocop/cop/style/multiline_in_pattern_then.rb
@@ -0,0 +1,62 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Style
# This cop checks uses of the `then` keyword in multi-line `in` statement.
#
# @example
# # bad
# case expression
# in pattern then
# end
#
# # good
# case expression
# in pattern
# end
#
# # good
# case expression
# in pattern then do_something
# end
#
# # good
# case expression
# in pattern then do_something(arg1,
# arg2)
# end
#
class MultilineInPatternThen < Base
include RangeHelp
extend AutoCorrector
extend TargetRubyVersion

minimum_target_ruby_version 2.7

MSG = 'Do not use `then` for multiline `in` statement.'

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

range = node.loc.begin
add_offense(range) do |corrector|
corrector.remove(
range_with_surrounding_space(range: range, side: :left, newlines: false)
)
end
end

private

# Requires `then` for write `in` and its body on the same line.
def require_then?(in_pattern_node)
return true if in_pattern_node.pattern.first_line != in_pattern_node.pattern.last_line
return false unless in_pattern_node.body

in_pattern_node.loc.line == in_pattern_node.body.loc.line
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
160 changes: 160 additions & 0 deletions spec/rubocop/cop/style/multiline_in_pattern_then_spec.rb
@@ -0,0 +1,160 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::Style::MultilineInPatternThen, :config do
context '>= Ruby 2.7', :ruby27 do
it 'registers an offense for empty `in` statement with `then`' do
expect_offense(<<~RUBY)
case foo
in bar then
^^^^ Do not use `then` for multiline `in` statement.
end
RUBY

expect_correction(<<~RUBY)
case foo
in bar
end
RUBY
end

it 'registers an offense for multiline (one line in a body) `in` statement with `then`' do
expect_offense(<<~RUBY)
case foo
in bar then
^^^^ Do not use `then` for multiline `in` statement.
do_something
end
RUBY

expect_correction(<<~RUBY)
case foo
in bar
do_something
end
RUBY
end

it 'registers an offense for multiline (two lines in a body) `in` statement with `then`' do
expect_offense(<<~RUBY)
case foo
in bar then
^^^^ Do not use `then` for multiline `in` statement.
do_something1
do_something2
end
RUBY

expect_correction(<<~RUBY)
case foo
in bar
do_something1
do_something2
end
RUBY
end

it "doesn't register an offense for singleline `in` statement with `then`" do
expect_no_offenses(<<~RUBY)
case foo
in bar then do_something
end
RUBY
end

it "doesn't register an offense when `then` required for a body of `in` is used" do
expect_no_offenses(<<~RUBY)
case cond
in foo then do_something(arg1,
arg2)
end
RUBY
end

it "doesn't register an offense for multiline `in` statement with `then` followed by other lines" do
expect_no_offenses(<<~RUBY)
case foo
in bar then do_something
do_another_thing
end
RUBY
end

it "doesn't register an offense for empty `in` statement without `then`" do
expect_no_offenses(<<~RUBY)
case foo
in bar
end
RUBY
end

it "doesn't register an offense for multiline `in` statement without `then`" do
expect_no_offenses(<<~RUBY)
case foo
in bar
do_something
end
RUBY
end

it 'does not register an offense for hash `in` statement with `then`' do
expect_no_offenses(<<~RUBY)
case condition
in foo then {
key: 'value'
}
end
RUBY
end

it 'does not register an offense for array `when` statement with `then`' do
expect_no_offenses(<<~RUBY)
case condition
in foo then [
'element'
]
end
RUBY
end

it 'autocorrects when the body of `in` branch starts with `then`' do
expect_offense(<<~RUBY)
case foo
in bar
then do_something
^^^^ Do not use `then` for multiline `in` statement.
end
RUBY

expect_correction(<<~RUBY)
case foo
in bar
do_something
end
RUBY
end

it 'registers an offense when one line for multiple condidate values of `in`' do
expect_offense(<<~RUBY)
case foo
in bar, baz then
^^^^ Do not use `then` for multiline `in` statement.
end
RUBY

expect_correction(<<~RUBY)
case foo
in bar, baz
end
RUBY
end

it 'does not register an offense when line break for multiple condidate values of `in`' do
expect_no_offenses(<<~RUBY)
case foo
in bar,
baz then do_something
end
RUBY
end
end
end

0 comments on commit 3bd037c

Please sign in to comment.