Skip to content

Commit

Permalink
Add new Style/NumberedParametersLimit cop.
Browse files Browse the repository at this point in the history
  • Loading branch information
dvandersluis authored and bbatsov committed Sep 27, 2021
1 parent 1184830 commit 9815b39
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 0 deletions.
1 change: 1 addition & 0 deletions changelog/new_add_new_styleexcessivenumberedparameters.md
@@ -0,0 +1 @@
* [#10111](https://github.com/rubocop/rubocop/pull/10111): Add new `Style/NumberedParametersLimit` cop. ([@dvandersluis][])
6 changes: 6 additions & 0 deletions config/default.yml
Expand Up @@ -4135,6 +4135,12 @@ Style/Not:
VersionAdded: '0.9'
VersionChanged: '0.20'

Style/NumberedParametersLimit:
Description: 'Avoid excessive numbered params in a single block.'
Enabled: pending
VersionAdded: '<<next>>'
Max: 1

Style/NumericLiteralPrefix:
Description: 'Use smallcase prefixes for numeric literals.'
StyleGuide: '#numeric-literal-prefixes'
Expand Down
1 change: 1 addition & 0 deletions lib/rubocop.rb
Expand Up @@ -549,6 +549,7 @@
require_relative 'rubocop/cop/style/nil_lambda'
require_relative 'rubocop/cop/style/non_nil_check'
require_relative 'rubocop/cop/style/not'
require_relative 'rubocop/cop/style/numbered_parameters_limit'
require_relative 'rubocop/cop/style/numeric_literals'
require_relative 'rubocop/cop/style/numeric_literal_prefix'
require_relative 'rubocop/cop/style/numeric_predicate'
Expand Down
50 changes: 50 additions & 0 deletions lib/rubocop/cop/style/numbered_parameters_limit.rb
@@ -0,0 +1,50 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Style
# This cop detects use of an excessive amount of numbered parameters in a
# single block. Having too many numbered parameters can make code too
# cryptic and hard to read.
#
# The cop defaults to registering an offense if there is more than 1 numbered
# parameter but this maximum can be configured by setting `Max`.
#
# @example Max: 1 (default)
# # bad
# foo { _1.call(_2, _3, _4) }
#
# # good
# foo { do_something(_1) }
class NumberedParametersLimit < Base
extend TargetRubyVersion
extend ExcludeLimit

DEFAULT_MAX_VALUE = 1

minimum_target_ruby_version 2.7
exclude_limit 'Max'

MSG = 'Avoid using more than %<max>i numbered %<parameter>s; %<count>i detected.'

def on_numblock(node)
_send_node, param_count, * = *node
return if param_count <= max_count

parameter = max_count > 1 ? 'parameters' : 'parameter'
message = format(MSG, max: max_count, parameter: parameter, count: param_count)
add_offense(node, message: message) { self.max = param_count }
end

private

def max_count
max = cop_config.fetch('Max', DEFAULT_MAX_VALUE)

# Ruby does not allow more than 9 numbered parameters
[max, 9].min
end
end
end
end
end
74 changes: 74 additions & 0 deletions spec/rubocop/cop/style/numbered_parameters_limit_spec.rb
@@ -0,0 +1,74 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::Style::NumberedParametersLimit, :config do
let(:cop_config) { { 'Max' => max } }
let(:max) { 2 }

context 'with Ruby >= 2.7', :ruby27 do
it 'does not register an offense for a normal block with too many parameters' do
expect_no_offenses(<<~RUBY)
foo { |a, b, c, d, e, f, g| do_something(a,b,c,d,e,f,g) }
RUBY
end

it 'does not register an offense for a numblock with fewer than `Max` parameters' do
expect_no_offenses(<<~RUBY)
foo { do_something(_1) }
RUBY
end

it 'does not register an offense for a numblock with exactly `Max` parameters' do
expect_no_offenses(<<~RUBY)
foo { do_something(_1, _2) }
RUBY
end

context 'when there are more than `Max` numbered parameters' do
it 'registers an offense for a single line `numblock`' do
expect_offense(<<~RUBY)
foo { do_something(_1, _2, _3, _4, _5) }
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid using more than 2 numbered parameters; 5 detected.
RUBY
end

it 'registers an offense for a multiline `numblock`' do
expect_offense(<<~RUBY)
foo do
^^^^^^ Avoid using more than 2 numbered parameters; 5 detected.
do_something(_1, _2, _3, _4, _5)
end
RUBY
end
end

context 'when configuring Max' do
let(:max) { 5 }

it 'does not register an offense when there are not too many numbered params' do
expect_no_offenses(<<~RUBY)
foo { do_something(_1, _2, _3, _4, _5) }
RUBY
end
end

context 'when Max is 1' do
let(:max) { 1 }

it 'uses the right offense message' do
expect_offense(<<~RUBY)
foo { do_something(_1, _2, _3, _4, _5) }
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid using more than 1 numbered parameter; 5 detected.
RUBY
end
end

it 'sets Max properly for auto-gen-config' do
expect_offense(<<~RUBY)
foo { do_something(_1, _2, _3, _4, _5) }
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid using more than 2 numbered parameters; 5 detected.
RUBY

expect(cop.config_to_allow_offenses).to eq(exclude_limit: { 'Max' => 5 })
end
end
end

0 comments on commit 9815b39

Please sign in to comment.