rubocop 1.3.0 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/config/default.yml +75 -16
- data/lib/rubocop.rb +5 -0
- data/lib/rubocop/cli.rb +5 -1
- data/lib/rubocop/cli/command/execute_runner.rb +26 -11
- data/lib/rubocop/cli/command/suggest_extensions.rb +67 -0
- data/lib/rubocop/config_loader.rb +12 -4
- data/lib/rubocop/config_loader_resolver.rb +5 -1
- data/lib/rubocop/config_obsoletion.rb +21 -3
- data/lib/rubocop/config_regeneration.rb +1 -1
- data/lib/rubocop/config_validator.rb +8 -1
- data/lib/rubocop/cop/autocorrect_logic.rb +21 -6
- data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +1 -1
- data/lib/rubocop/cop/generator.rb +2 -9
- data/lib/rubocop/cop/generator/configuration_injector.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +1 -1
- data/lib/rubocop/cop/layout/class_structure.rb +15 -3
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +80 -10
- data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +6 -1
- data/lib/rubocop/cop/layout/end_of_line.rb +5 -5
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +7 -2
- data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +1 -1
- data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +2 -1
- data/lib/rubocop/cop/lint/constant_definition_in_block.rb +4 -1
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
- data/lib/rubocop/cop/lint/missing_super.rb +7 -4
- data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +1 -1
- data/lib/rubocop/cop/lint/to_enum_arguments.rb +6 -15
- data/lib/rubocop/cop/lint/unexpected_block_arity.rb +85 -0
- data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +20 -6
- data/lib/rubocop/cop/metrics/abc_size.rb +25 -1
- data/lib/rubocop/cop/metrics/block_length.rb +13 -7
- data/lib/rubocop/cop/metrics/method_length.rb +7 -2
- data/lib/rubocop/cop/metrics/parameter_lists.rb +64 -1
- data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +20 -10
- data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +146 -0
- data/lib/rubocop/cop/metrics/utils/repeated_csend_discount.rb +6 -1
- data/lib/rubocop/cop/mixin/configurable_numbering.rb +4 -3
- data/lib/rubocop/cop/mixin/enforce_superclass.rb +9 -1
- data/lib/rubocop/cop/mixin/ignored_methods.rb +36 -3
- data/lib/rubocop/cop/mixin/method_complexity.rb +6 -0
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
- data/lib/rubocop/cop/mixin/visibility_help.rb +1 -3
- data/lib/rubocop/cop/style/and_or.rb +10 -0
- data/lib/rubocop/cop/style/class_and_module_children.rb +8 -3
- data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +8 -1
- data/lib/rubocop/cop/style/documentation.rb +12 -1
- data/lib/rubocop/cop/style/format_string.rb +8 -3
- data/lib/rubocop/cop/style/if_with_semicolon.rb +39 -4
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -2
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +11 -2
- data/lib/rubocop/cop/style/negated_if_else_condition.rb +3 -1
- data/lib/rubocop/cop/style/numeric_literals.rb +14 -11
- data/lib/rubocop/cop/style/redundant_argument.rb +75 -0
- data/lib/rubocop/cop/style/redundant_condition.rb +2 -1
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +1 -1
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +49 -3
- data/lib/rubocop/cop/style/symbol_proc.rb +5 -3
- data/lib/rubocop/cop/util.rb +1 -1
- data/lib/rubocop/cop/variable_force/branch.rb +1 -1
- data/lib/rubocop/cop/variable_force/scope.rb +1 -1
- data/lib/rubocop/core_ext/hash.rb +20 -0
- data/lib/rubocop/ext/regexp_node.rb +5 -10
- data/lib/rubocop/ext/regexp_parser.rb +2 -9
- data/lib/rubocop/formatter/disabled_config_formatter.rb +21 -6
- data/lib/rubocop/options.rb +5 -0
- data/lib/rubocop/rake_task.rb +2 -2
- data/lib/rubocop/runner.rb +1 -1
- data/lib/rubocop/target_finder.rb +1 -1
- data/lib/rubocop/target_ruby.rb +12 -4
- data/lib/rubocop/version.rb +1 -1
- metadata +12 -10
- data/bin/console +0 -10
- data/bin/rubocop-profile +0 -32
- data/bin/setup +0 -7
@@ -53,7 +53,8 @@ module RuboCop
|
|
53
53
|
def find_offense_node(node, regexp_receiver)
|
54
54
|
return node unless node.parent
|
55
55
|
|
56
|
-
if node.parent.send_type?
|
56
|
+
if (node.parent.send_type? && node.receiver) ||
|
57
|
+
method_chain_to_regexp_receiver?(node, regexp_receiver)
|
57
58
|
node = find_offense_node(node.parent, regexp_receiver)
|
58
59
|
end
|
59
60
|
|
@@ -50,8 +50,11 @@ module RuboCop
|
|
50
50
|
# end
|
51
51
|
# end
|
52
52
|
#
|
53
|
-
# @example AllowedMethods: ['enums']
|
53
|
+
# @example AllowedMethods: ['enums'] (default)
|
54
54
|
# # good
|
55
|
+
#
|
56
|
+
# # `enums` for Typed Enums via `T::Enum` in Sorbet.
|
57
|
+
# # https://sorbet.org/docs/tenum
|
55
58
|
# class TestEnum < T::Enum
|
56
59
|
# enums do
|
57
60
|
# Foo = new("foo")
|
@@ -95,7 +95,7 @@ module RuboCop
|
|
95
95
|
def autocorrected_value_for_array(node)
|
96
96
|
return node.source.gsub('"', '\"') unless node.percent_literal?
|
97
97
|
|
98
|
-
contents_range(node).source.split
|
98
|
+
contents_range(node).source.split.to_s.gsub('"', '\"')
|
99
99
|
end
|
100
100
|
|
101
101
|
# Does node print its own source when converted to a string?
|
@@ -6,6 +6,11 @@ module RuboCop
|
|
6
6
|
# This cop checks for the presence of constructors and lifecycle callbacks
|
7
7
|
# without calls to `super`.
|
8
8
|
#
|
9
|
+
# This cop does not consider `method_missing` (and `respond_to_missing?`)
|
10
|
+
# because in some cases it makes sense to overtake what is considered a
|
11
|
+
# missing method. In other cases, the theoretical ideal handling could be
|
12
|
+
# challenging or verbose for no actual gain.
|
13
|
+
#
|
9
14
|
# @example
|
10
15
|
# # bad
|
11
16
|
# class Employee < Person
|
@@ -43,15 +48,13 @@ module RuboCop
|
|
43
48
|
|
44
49
|
STATELESS_CLASSES = %w[BasicObject Object].freeze
|
45
50
|
|
46
|
-
OBJECT_LIFECYCLE_CALLBACKS = %i[method_missing respond_to_missing?].freeze
|
47
51
|
CLASS_LIFECYCLE_CALLBACKS = %i[inherited].freeze
|
48
52
|
METHOD_LIFECYCLE_CALLBACKS = %i[method_added method_removed method_undefined
|
49
53
|
singleton_method_added singleton_method_removed
|
50
54
|
singleton_method_undefined].freeze
|
51
55
|
|
52
|
-
CALLBACKS = (
|
53
|
-
|
54
|
-
METHOD_LIFECYCLE_CALLBACKS).to_set.freeze
|
56
|
+
CALLBACKS = (CLASS_LIFECYCLE_CALLBACKS +
|
57
|
+
METHOD_LIFECYCLE_CALLBACKS).to_set.freeze
|
55
58
|
|
56
59
|
def on_def(node)
|
57
60
|
return unless offender?(node)
|
@@ -8,23 +8,14 @@ module RuboCop
|
|
8
8
|
#
|
9
9
|
# @example
|
10
10
|
# # bad
|
11
|
-
# def
|
12
|
-
# return to_enum(
|
11
|
+
# def foo(x, y = 1)
|
12
|
+
# return to_enum(__callee__, x) # `y` is missing
|
13
13
|
# end
|
14
14
|
#
|
15
15
|
# # good
|
16
|
-
# def
|
17
|
-
# return to_enum(
|
18
|
-
#
|
19
|
-
#
|
20
|
-
# # bad
|
21
|
-
# def method(required:)
|
22
|
-
# return to_enum(:method, required: something) # `required` has incorrect value
|
23
|
-
# end
|
24
|
-
#
|
25
|
-
# # good
|
26
|
-
# def method(required:)
|
27
|
-
# return to_enum(:method, required: required)
|
16
|
+
# def foo(x, y = 1)
|
17
|
+
# return to_enum(__callee__, x, y)
|
18
|
+
# # alternatives to `__callee__` are `__method__` and `:foo`
|
28
19
|
# end
|
29
20
|
#
|
30
21
|
class ToEnumArguments < Base
|
@@ -37,7 +28,7 @@ module RuboCop
|
|
37
28
|
PATTERN
|
38
29
|
|
39
30
|
def_node_matcher :method_name?, <<~PATTERN
|
40
|
-
{(send nil? :__method__) (sym %1)}
|
31
|
+
{(send nil? {:__method__ :__callee__}) (sym %1)}
|
41
32
|
PATTERN
|
42
33
|
|
43
34
|
def_node_matcher :passing_keyword_arg?, <<~PATTERN
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# This cop checks for a block that is known to need more positional
|
7
|
+
# block arguments than are given (by default this is configured for
|
8
|
+
# `Enumerable` methods needing 2 arguments). Optional arguments are allowed,
|
9
|
+
# although they don't generally make sense as the default value will
|
10
|
+
# be used. Blocks that have no receiver, or take splatted arguments
|
11
|
+
# (ie. `*args`) are always accepted.
|
12
|
+
#
|
13
|
+
# Keyword arguments (including `**kwargs`) do not get counted towards
|
14
|
+
# this, as they are not used by the methods in question.
|
15
|
+
#
|
16
|
+
# NOTE: This cop matches for method names only and hence cannot tell apart
|
17
|
+
# methods with same name in different classes.
|
18
|
+
#
|
19
|
+
# Method names and their expected arity can be configured like this:
|
20
|
+
#
|
21
|
+
# Methods:
|
22
|
+
# inject: 2
|
23
|
+
# reduce: 2
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# # bad
|
27
|
+
# values.reduce {}
|
28
|
+
# values.min { |a| a }
|
29
|
+
# values.sort { |a; b| a + b }
|
30
|
+
#
|
31
|
+
# # good
|
32
|
+
# values.reduce { |memo, obj| memo << obj }
|
33
|
+
# values.min { |a, b| a <=> b }
|
34
|
+
# values.sort { |*x| x[0] <=> x[1] }
|
35
|
+
#
|
36
|
+
class UnexpectedBlockArity < Base
|
37
|
+
MSG = '`%<method>s` expects at least %<expected>i positional arguments, got %<actual>i.'
|
38
|
+
|
39
|
+
def on_block(node)
|
40
|
+
return if acceptable?(node)
|
41
|
+
|
42
|
+
expected = expected_arity(node.method_name)
|
43
|
+
actual = arg_count(node)
|
44
|
+
return if actual >= expected
|
45
|
+
|
46
|
+
message = format(MSG, method: node.method_name, expected: expected, actual: actual)
|
47
|
+
add_offense(node, message: message)
|
48
|
+
end
|
49
|
+
|
50
|
+
alias on_numblock on_block
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def methods
|
55
|
+
cop_config.fetch('Methods', [])
|
56
|
+
end
|
57
|
+
|
58
|
+
def acceptable?(node)
|
59
|
+
!(included_method?(node.method_name) && node.receiver)
|
60
|
+
end
|
61
|
+
|
62
|
+
def included_method?(name)
|
63
|
+
methods.key?(name.to_s)
|
64
|
+
end
|
65
|
+
|
66
|
+
def expected_arity(method)
|
67
|
+
cop_config['Methods'][method.to_s]
|
68
|
+
end
|
69
|
+
|
70
|
+
def arg_count(node)
|
71
|
+
return node.children[1] if node.numblock_type? # the maximum numbered param for the block
|
72
|
+
|
73
|
+
# Only `arg`, `optarg` and `mlhs` (destructuring) count as arguments that
|
74
|
+
# can be used. Keyword arguments are not used for these methods so are
|
75
|
+
# ignored.
|
76
|
+
node.arguments.count do |arg|
|
77
|
+
return Float::INFINITY if arg.restarg_type?
|
78
|
+
|
79
|
+
arg.arg_type? || arg.optarg_type? || arg.mlhs_type?
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -54,6 +54,9 @@ module RuboCop
|
|
54
54
|
# value
|
55
55
|
# end
|
56
56
|
#
|
57
|
+
# # good, recursive
|
58
|
+
# keys.reduce(self) { |result, key| result[key] }
|
59
|
+
#
|
57
60
|
# # ignored as the return value cannot be determined
|
58
61
|
# enum.reduce do |acc, el|
|
59
62
|
# x = foo(acc, el)
|
@@ -64,7 +67,10 @@ module RuboCop
|
|
64
67
|
MSG_INDEX = 'Do not return an element of the accumulator in `%<method>s`.'
|
65
68
|
|
66
69
|
def_node_matcher :reduce_with_block?, <<~PATTERN
|
67
|
-
|
70
|
+
{
|
71
|
+
(block (send _recv {:reduce :inject} ...) args ...)
|
72
|
+
(numblock (send _recv {:reduce :inject} ...) ...)
|
73
|
+
}
|
68
74
|
PATTERN
|
69
75
|
|
70
76
|
def_node_matcher :accumulator_index?, <<~PATTERN
|
@@ -103,9 +109,11 @@ module RuboCop
|
|
103
109
|
|
104
110
|
def on_block(node)
|
105
111
|
return unless reduce_with_block?(node)
|
112
|
+
return unless node.argument_list.length >= 2
|
106
113
|
|
107
114
|
check_return_values(node)
|
108
115
|
end
|
116
|
+
alias on_numblock on_block
|
109
117
|
|
110
118
|
private
|
111
119
|
|
@@ -131,7 +139,7 @@ module RuboCop
|
|
131
139
|
element_name = block_arg_name(block_node, 1)
|
132
140
|
message_opts = { method: block_node.method_name, accum: accumulator_name }
|
133
141
|
|
134
|
-
if (node = returned_accumulator_index(return_values, accumulator_name))
|
142
|
+
if (node = returned_accumulator_index(return_values, accumulator_name, element_name))
|
135
143
|
add_offense(node, message: format(MSG_INDEX, message_opts))
|
136
144
|
elsif potential_offense?(return_values, block_node.body, element_name, accumulator_name)
|
137
145
|
return_values.each do |return_val|
|
@@ -143,14 +151,20 @@ module RuboCop
|
|
143
151
|
end
|
144
152
|
|
145
153
|
def block_arg_name(node, index)
|
146
|
-
node.
|
154
|
+
node.argument_list[index].name
|
147
155
|
end
|
148
156
|
|
149
|
-
# Look for an index of the accumulator being returned
|
157
|
+
# Look for an index of the accumulator being returned, except where the index
|
158
|
+
# is the element.
|
150
159
|
# This is always an offense, in order to try to catch potential exceptions
|
151
160
|
# due to type mismatches
|
152
|
-
def returned_accumulator_index(return_values, accumulator_name)
|
153
|
-
return_values.detect
|
161
|
+
def returned_accumulator_index(return_values, accumulator_name, element_name)
|
162
|
+
return_values.detect do |val|
|
163
|
+
next unless accumulator_index?(val, accumulator_name)
|
164
|
+
next true if val.method?(:[]=)
|
165
|
+
|
166
|
+
val.arguments.none? { |arg| lvar_used?(arg, element_name) }
|
167
|
+
end
|
154
168
|
end
|
155
169
|
|
156
170
|
def potential_offense?(return_values, block_body, element_name, accumulator_name)
|
@@ -7,6 +7,27 @@ module RuboCop
|
|
7
7
|
# configured maximum. The ABC size is based on assignments, branches
|
8
8
|
# (method calls), and conditions. See http://c2.com/cgi/wiki?AbcMetric
|
9
9
|
# and https://en.wikipedia.org/wiki/ABC_Software_Metric.
|
10
|
+
#
|
11
|
+
# You can have repeated "attributes" calls count as a single "branch".
|
12
|
+
# For this purpose, attributes are any method with no argument; no attempt
|
13
|
+
# is meant to distinguish actual `attr_reader` from other methods.
|
14
|
+
#
|
15
|
+
# @example CountRepeatedAttributes: false (default is true)
|
16
|
+
#
|
17
|
+
# # `model` and `current_user`, refenced 3 times each,
|
18
|
+
# # are each counted as only 1 branch each if
|
19
|
+
# # `CountRepeatedAttributes` is set to 'false'
|
20
|
+
#
|
21
|
+
# def search
|
22
|
+
# @posts = model.active.visible_by(current_user)
|
23
|
+
# .search(params[:q])
|
24
|
+
# @posts = model.some_process(@posts, current_user)
|
25
|
+
# @posts = model.another_process(@posts, current_user)
|
26
|
+
#
|
27
|
+
# render 'pages/search/page'
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# This cop also takes into account `IgnoredMethods` (defaults to `[]`)
|
10
31
|
class AbcSize < Base
|
11
32
|
include MethodComplexity
|
12
33
|
|
@@ -16,7 +37,10 @@ module RuboCop
|
|
16
37
|
private
|
17
38
|
|
18
39
|
def complexity(node)
|
19
|
-
Utils::AbcSizeCalculator.calculate(
|
40
|
+
Utils::AbcSizeCalculator.calculate(
|
41
|
+
node,
|
42
|
+
discount_repeated_attributes: !cop_config['CountRepeatedAttributes']
|
43
|
+
)
|
20
44
|
end
|
21
45
|
end
|
22
46
|
end
|
@@ -12,6 +12,10 @@ module RuboCop
|
|
12
12
|
# Available are: 'array', 'hash', and 'heredoc'. Each literal
|
13
13
|
# will be counted as one line regardless of its actual size.
|
14
14
|
#
|
15
|
+
#
|
16
|
+
# NOTE: The `ExcludedMethods` configuration is deprecated and only kept
|
17
|
+
# for backwards compatibility. Please use `IgnoredMethods` instead.
|
18
|
+
#
|
15
19
|
# @example CountAsOne: ['array', 'heredoc']
|
16
20
|
#
|
17
21
|
# something do
|
@@ -33,11 +37,15 @@ module RuboCop
|
|
33
37
|
# NOTE: This cop does not apply for `Struct` definitions.
|
34
38
|
class BlockLength < Base
|
35
39
|
include CodeLength
|
40
|
+
include IgnoredMethods
|
41
|
+
|
42
|
+
ignored_methods deprecated_key: 'ExcludedMethods'
|
36
43
|
|
37
44
|
LABEL = 'Block'
|
38
45
|
|
39
46
|
def on_block(node)
|
40
|
-
return if
|
47
|
+
return if ignored_method?(node.method_name)
|
48
|
+
return if method_receiver_excluded?(node)
|
41
49
|
return if node.class_constructor? || node.struct_constructor?
|
42
50
|
|
43
51
|
check_code_length(node)
|
@@ -45,11 +53,13 @@ module RuboCop
|
|
45
53
|
|
46
54
|
private
|
47
55
|
|
48
|
-
def
|
56
|
+
def method_receiver_excluded?(node)
|
49
57
|
node_receiver = node.receiver&.source&.gsub(/\s+/, '')
|
50
58
|
node_method = String(node.method_name)
|
51
59
|
|
52
|
-
|
60
|
+
ignored_methods.any? do |config|
|
61
|
+
next unless config.is_a?(String)
|
62
|
+
|
53
63
|
receiver, method = config.split('.')
|
54
64
|
|
55
65
|
unless method
|
@@ -61,10 +71,6 @@ module RuboCop
|
|
61
71
|
end
|
62
72
|
end
|
63
73
|
|
64
|
-
def excluded_methods
|
65
|
-
cop_config['ExcludedMethods'] || []
|
66
|
-
end
|
67
|
-
|
68
74
|
def cop_label
|
69
75
|
LABEL
|
70
76
|
end
|
@@ -11,6 +11,9 @@ module RuboCop
|
|
11
11
|
# Available are: 'array', 'hash', and 'heredoc'. Each literal
|
12
12
|
# will be counted as one line regardless of its actual size.
|
13
13
|
#
|
14
|
+
# NOTE: The `ExcludedMethods` configuration is deprecated and only kept
|
15
|
+
# for backwards compatibility. Please use `IgnoredMethods` instead.
|
16
|
+
#
|
14
17
|
# @example CountAsOne: ['array', 'heredoc']
|
15
18
|
#
|
16
19
|
# def m
|
@@ -31,12 +34,14 @@ module RuboCop
|
|
31
34
|
#
|
32
35
|
class MethodLength < Base
|
33
36
|
include CodeLength
|
37
|
+
include IgnoredMethods
|
38
|
+
|
39
|
+
ignored_methods deprecated_key: 'ExcludedMethods'
|
34
40
|
|
35
41
|
LABEL = 'Method'
|
36
42
|
|
37
43
|
def on_def(node)
|
38
|
-
|
39
|
-
return if excluded_methods.include?(String(node.method_name))
|
44
|
+
return if ignored_method?(node.method_name)
|
40
45
|
|
41
46
|
check_code_length(node)
|
42
47
|
end
|
@@ -4,17 +4,76 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module Metrics
|
6
6
|
# This cop checks for methods with too many parameters.
|
7
|
+
#
|
7
8
|
# The maximum number of parameters is configurable.
|
8
|
-
# Keyword arguments can optionally be excluded from the total count
|
9
|
+
# Keyword arguments can optionally be excluded from the total count,
|
10
|
+
# as they add less complexity than positional or optional parameters.
|
11
|
+
#
|
12
|
+
# @example Max: 3
|
13
|
+
# # good
|
14
|
+
# def foo(a, b, c = 1)
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# @example Max: 2
|
18
|
+
# # bad
|
19
|
+
# def foo(a, b, c = 1)
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# @example CountKeywordArgs: true (default)
|
23
|
+
# # counts keyword args towards the maximum
|
24
|
+
#
|
25
|
+
# # bad (assuming Max is 3)
|
26
|
+
# def foo(a, b, c, d: 1)
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# # good (assuming Max is 3)
|
30
|
+
# def foo(a, b, c: 1)
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# @example CountKeywordArgs: false
|
34
|
+
# # don't count keyword args towards the maximum
|
35
|
+
#
|
36
|
+
# # good (assuming Max is 3)
|
37
|
+
# def foo(a, b, c, d: 1)
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# This cop also checks for the maximum number of optional parameters.
|
41
|
+
# This can be configured using the `MaxOptionalParameters` config option.
|
42
|
+
#
|
43
|
+
# @example MaxOptionalParameters: 3 (default)
|
44
|
+
# # good
|
45
|
+
# def foo(a = 1, b = 2, c = 3)
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# @example MaxOptionalParameters: 2
|
49
|
+
# # bad
|
50
|
+
# def foo(a = 1, b = 2, c = 3)
|
51
|
+
# end
|
52
|
+
#
|
9
53
|
class ParameterLists < Base
|
10
54
|
include ConfigurableMax
|
11
55
|
|
12
56
|
MSG = 'Avoid parameter lists longer than %<max>d parameters. ' \
|
13
57
|
'[%<count>d/%<max>d]'
|
58
|
+
OPTIONAL_PARAMETERS_MSG = 'Method has too many optional parameters. [%<count>d/%<max>d]'
|
14
59
|
|
15
60
|
NAMED_KEYWORD_TYPES = %i[kwoptarg kwarg].freeze
|
16
61
|
private_constant :NAMED_KEYWORD_TYPES
|
17
62
|
|
63
|
+
def on_def(node)
|
64
|
+
optargs = node.arguments.select(&:optarg_type?)
|
65
|
+
return if optargs.count <= max_optional_parameters
|
66
|
+
|
67
|
+
message = format(
|
68
|
+
OPTIONAL_PARAMETERS_MSG,
|
69
|
+
max: max_optional_parameters,
|
70
|
+
count: optargs.count
|
71
|
+
)
|
72
|
+
|
73
|
+
add_offense(node, message: message)
|
74
|
+
end
|
75
|
+
alias on_defs on_def
|
76
|
+
|
18
77
|
def on_args(node)
|
19
78
|
count = args_count(node)
|
20
79
|
return unless count > max_params
|
@@ -44,6 +103,10 @@ module RuboCop
|
|
44
103
|
cop_config['Max']
|
45
104
|
end
|
46
105
|
|
106
|
+
def max_optional_parameters
|
107
|
+
cop_config['MaxOptionalParameters']
|
108
|
+
end
|
109
|
+
|
47
110
|
def count_keyword_args?
|
48
111
|
cop_config['CountKeywordArgs']
|
49
112
|
end
|