Skip to content

Commit

Permalink
[Fix #7492] Add Style/TrailingCommaInBlockArgs (#7637)
Browse files Browse the repository at this point in the history
Co-authored-by: Bozhidar Batsov <bozhidar@batsov.com>
  • Loading branch information
ty-porter and bbatsov committed Mar 22, 2020
1 parent 27d35b3 commit 22cc72c
Show file tree
Hide file tree
Showing 7 changed files with 284 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -13,6 +13,7 @@
* [#7784](https://github.com/rubocop-hq/rubocop/pull/7784): Support Ruby 2.7's numbered parameter for `Lint/SafeNavigationChain`. ([@koic][])
* [#7331](https://github.com/rubocop-hq/rubocop/issues/7331): Add `forbidden` option to `Style/ModuleFunction` cop. ([@weh][])
* [#7699](https://github.com/rubocop-hq/rubocop/pull/7699): Add new `Lint/StructNewOverride` cop. ([@ybiquitous][])
* [#7637](https://github.com/rubocop-hq/rubocop/pull/7637): Add new `Style/TrailingCommaInBlockArgs` cop. ([@pawptart][])
* [#7809](https://github.com/rubocop-hq/rubocop/pull/7809): Add auto-correction for `Style/EndBlock` cop. ([@tejasbubane][])
* [#7739](https://github.com/rubocop-hq/rubocop/pull/7739): Add `IgnoreNotImplementedMethods` configuration to `Lint/UnusedMethodArgument`. ([@tejasbubane][])

Expand Down
6 changes: 6 additions & 0 deletions config/default.yml
Expand Up @@ -3853,6 +3853,12 @@ Style/TrailingCommaInArrayLiteral:
- consistent_comma
- no_comma

Style/TrailingCommaInBlockArgs:
Description: 'Checks for useless trailing commas in block arguments.'
Enabled: false
Safe: false
VersionAdded: '0.81'

Style/TrailingCommaInHashLiteral:
Description: 'Checks for trailing comma in hash literals.'
Enabled: true
Expand Down
1 change: 1 addition & 0 deletions lib/rubocop.rb
Expand Up @@ -558,6 +558,7 @@
require_relative 'rubocop/cop/style/trailing_body_on_module'
require_relative 'rubocop/cop/style/trailing_comma_in_arguments'
require_relative 'rubocop/cop/style/trailing_comma_in_array_literal'
require_relative 'rubocop/cop/style/trailing_comma_in_block_args'
require_relative 'rubocop/cop/style/trailing_comma_in_hash_literal'
require_relative 'rubocop/cop/style/trailing_method_end_statement'
require_relative 'rubocop/cop/style/trailing_underscore_variable'
Expand Down
85 changes: 85 additions & 0 deletions lib/rubocop/cop/style/trailing_comma_in_block_args.rb
@@ -0,0 +1,85 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Style
# This cop checks whether trailing commas in block arguments are
# required. Blocks with only one argument and a trailing comma require
# that comma to be present. Blocks with more than one argument never
# require a trailing comma.
#
# @example
# # bad
# add { |foo, bar,| foo + bar }
#
# # good
# add { |foo, bar| foo + bar }
#
# # good
# add { |foo,| foo }
#
# # good
# add { foo }
#
# # bad
# add do |foo, bar,|
# foo + bar
# end
#
# # good
# add do |foo, bar|
# foo + bar
# end
#
# # good
# add do |foo,|
# foo
# end
#
# # good
# add do
# foo + bar
# end
class TrailingCommaInBlockArgs < Cop
MSG = 'Useless trailing comma present in block arguments.'

def on_block(node)
return unless useless_trailing_comma?(node)

add_offense(node, location: last_comma(node).pos)
end

def autocorrect(node)
->(corrector) { corrector.replace(last_comma(node).pos, '') }
end

private

def useless_trailing_comma?(node)
arg_count(node) > 1 && trailing_comma?(node)
end

def arg_count(node)
node.arguments.each_descendant(:arg, :optarg, :kwoptarg).to_a.size
end

def trailing_comma?(node)
argument_tokens(node).last.comma?
end

def last_comma(node)
argument_tokens(node).last
end

def argument_tokens(node)
pipes = tokens(node).select { |token| token.type == :tPIPE }
begin_pos, end_pos = pipes.map do |pipe|
tokens(node).index(pipe)
end

tokens(node)[begin_pos + 1..end_pos - 1]
end
end
end
end
end
1 change: 1 addition & 0 deletions manual/cops.md
Expand Up @@ -465,6 +465,7 @@ In the following section you find all available cops:
* [Style/TrailingBodyOnModule](cops_style.md#styletrailingbodyonmodule)
* [Style/TrailingCommaInArguments](cops_style.md#styletrailingcommainarguments)
* [Style/TrailingCommaInArrayLiteral](cops_style.md#styletrailingcommainarrayliteral)
* [Style/TrailingCommaInBlockArgs](cops_style.md#styletrailingcommainblockargs)
* [Style/TrailingCommaInHashLiteral](cops_style.md#styletrailingcommainhashliteral)
* [Style/TrailingMethodEndStatement](cops_style.md#styletrailingmethodendstatement)
* [Style/TrailingUnderscoreVariable](cops_style.md#styletrailingunderscorevariable)
Expand Down
48 changes: 48 additions & 0 deletions manual/cops_style.md
Expand Up @@ -7223,6 +7223,54 @@ EnforcedStyleForMultiline | `no_comma` | `comma`, `consistent_comma`, `no_comma`

* [https://rubystyle.guide#no-trailing-array-commas](https://rubystyle.guide#no-trailing-array-commas)

## Style/TrailingCommaInBlockArgs

Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
--- | --- | --- | --- | ---
Disabled | No | Yes (Unsafe) | 0.81 | -

This cop checks whether trailing commas in block arguments are
required. Blocks with only one argument and a trailing comma require
that comma to be present. Blocks with more than one argument never
require a trailing comma.

add do |foo, bar,|
foo + bar
end

# good
add do |foo, bar|
foo + bar
end

# good
add do |foo,|
foo
end

# good
add do
foo + bar
end

### Examples

```ruby
# bad
add { |foo, bar,| foo + bar }

# good
add { |foo, bar| foo + bar }

# good
add { |foo,| foo }

# good
add { foo }

# bad
```

## Style/TrailingCommaInHashLiteral

Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
Expand Down
142 changes: 142 additions & 0 deletions spec/rubocop/cop/style/trailing_comma_in_block_args_spec.rb
@@ -0,0 +1,142 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::Style::TrailingCommaInBlockArgs do
subject(:cop) { described_class.new(config) }

let(:config) { RuboCop::Config.new }

context 'curly brace block format' do
it 'registers an offense when a trailing comma is not needed' do
expect_offense(<<~RUBY)
test { |a, b,| a + b }
^ Useless trailing comma present in block arguments.
RUBY
expect_correction(<<~RUBY)
test { |a, b| a + b }
RUBY
end

it 'does not register an offense when a trailing comma is required' do
expect_no_offenses(<<~RUBY)
test { |a,| a }
RUBY
end

it 'does not register an offense when no arguments are present' do
expect_no_offenses(<<~RUBY)
test { a }
RUBY
end

it 'does not register an offense when more than one argument is ' \
'present with no trailing comma' do
expect_no_offenses(<<~RUBY)
test { |a, b| a + b }
RUBY
end

it 'does not register an offense for default arguments' do
expect_no_offenses(<<~RUBY)
test { |a, b, c = nil| a + b + c }
RUBY
end

it 'does not register an offense for keyword arguments' do
expect_no_offenses(<<~RUBY)
test { |a, b, c: 1| a + b + c }
RUBY
end

it 'ignores commas in default argument strings' do
expect_no_offenses(<<~RUBY)
add { |foo, bar = ','| foo + bar }
RUBY
end

it 'preserves semicolons in block/local variables' do
expect_no_offenses(<<~RUBY)
add { |foo, bar,; baz| foo + bar }
RUBY
end
end

context 'do/end block format' do
it 'registers an offense when a trailing comma is not needed' do
expect_offense(<<~RUBY)
test do |a, b,|
^ Useless trailing comma present in block arguments.
a + b
end
RUBY
expect_correction(<<~RUBY)
test do |a, b|
a + b
end
RUBY
end

it 'does not register an offense when a trailing comma is required' do
expect_no_offenses(<<~RUBY)
test do |a,|
a
end
RUBY
end

it 'does not register an offense when no arguments are present' do
expect_no_offenses(<<~RUBY)
test do
a
end
RUBY
end

it 'does not register an offense for an empty block' do
expect_no_offenses(<<~RUBY)
test do ||
end
RUBY
end

it 'does not register an offense when more than one argument is ' \
'present with no trailing comma' do
expect_no_offenses(<<~RUBY)
test do |a, b|
a + b
end
RUBY
end

it 'does not register an offense for default arguments' do
expect_no_offenses(<<~RUBY)
test do |a, b, c = nil|
a + b + c
end
RUBY
end

it 'does not register an offense for keyword arguments' do
expect_no_offenses(<<~RUBY)
test do |a, b, c: 1|
a + b + c
end
RUBY
end

it 'ignores commas in default argument strings' do
expect_no_offenses(<<~RUBY)
add do |foo, bar = ','|
foo + bar
end
RUBY
end

it 'preserves semicolons in block/local variables' do
expect_no_offenses(<<~RUBY)
add do |foo, bar,; baz|
foo + bar
end
RUBY
end
end
end

0 comments on commit 22cc72c

Please sign in to comment.