rubocop 0.93.1 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +36 -16
- data/config/default.yml +276 -82
- data/config/obsoletion.yml +196 -0
- data/exe/rubocop +1 -1
- data/lib/rubocop.rb +31 -2
- data/lib/rubocop/cli.rb +5 -1
- data/lib/rubocop/cli/command/auto_genenerate_config.rb +1 -1
- data/lib/rubocop/cli/command/execute_runner.rb +26 -11
- data/lib/rubocop/cli/command/suggest_extensions.rb +80 -0
- data/lib/rubocop/cli/command/version.rb +1 -1
- data/lib/rubocop/comment_config.rb +1 -1
- data/lib/rubocop/config.rb +4 -0
- data/lib/rubocop/config_loader.rb +34 -8
- data/lib/rubocop/config_loader_resolver.rb +12 -6
- data/lib/rubocop/config_obsoletion.rb +65 -247
- data/lib/rubocop/config_obsoletion/changed_enforced_styles.rb +33 -0
- data/lib/rubocop/config_obsoletion/changed_parameter.rb +21 -0
- data/lib/rubocop/config_obsoletion/cop_rule.rb +34 -0
- data/lib/rubocop/config_obsoletion/extracted_cop.rb +44 -0
- data/lib/rubocop/config_obsoletion/parameter_rule.rb +44 -0
- data/lib/rubocop/config_obsoletion/removed_cop.rb +41 -0
- data/lib/rubocop/config_obsoletion/renamed_cop.rb +34 -0
- data/lib/rubocop/config_obsoletion/rule.rb +41 -0
- data/lib/rubocop/config_obsoletion/split_cop.rb +27 -0
- data/lib/rubocop/config_regeneration.rb +1 -1
- data/lib/rubocop/config_validator.rb +25 -10
- data/lib/rubocop/cop/autocorrect_logic.rb +21 -6
- data/lib/rubocop/cop/badge.rb +9 -24
- data/lib/rubocop/cop/base.rb +33 -16
- data/lib/rubocop/cop/bundler/duplicated_gem.rb +26 -6
- data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
- data/lib/rubocop/cop/commissioner.rb +37 -23
- data/lib/rubocop/cop/cop.rb +2 -2
- data/lib/rubocop/cop/corrector.rb +3 -1
- data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
- data/lib/rubocop/cop/correctors/string_literal_corrector.rb +6 -8
- data/lib/rubocop/cop/force.rb +1 -1
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +3 -3
- data/lib/rubocop/cop/gemspec/required_ruby_version.rb +4 -5
- data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +1 -1
- data/lib/rubocop/cop/generator.rb +3 -10
- 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/block_alignment.rb +3 -4
- data/lib/rubocop/cop/layout/class_structure.rb +22 -3
- data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/else_alignment.rb +15 -2
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +80 -10
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -0
- data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +6 -1
- data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -1
- data/lib/rubocop/cop/layout/end_alignment.rb +3 -3
- data/lib/rubocop/cop/layout/end_of_line.rb +5 -5
- data/lib/rubocop/cop/layout/extra_spacing.rb +1 -2
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +7 -2
- data/lib/rubocop/cop/layout/hash_alignment.rb +4 -4
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +12 -0
- data/lib/rubocop/cop/layout/line_length.rb +10 -13
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +7 -3
- data/lib/rubocop/cop/layout/space_around_block_parameters.rb +24 -18
- data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_operators.rb +4 -1
- data/lib/rubocop/cop/layout/space_inside_parens.rb +35 -13
- data/lib/rubocop/cop/layout/trailing_whitespace.rb +37 -13
- data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +2 -1
- data/lib/rubocop/cop/lint/constant_definition_in_block.rb +26 -2
- data/lib/rubocop/cop/lint/debugger.rb +17 -28
- data/lib/rubocop/cop/lint/duplicate_branch.rb +93 -0
- data/lib/rubocop/cop/lint/duplicate_case_condition.rb +2 -12
- data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +77 -0
- data/lib/rubocop/cop/lint/else_layout.rb +29 -3
- data/lib/rubocop/cop/lint/empty_block.rb +82 -0
- data/lib/rubocop/cop/lint/empty_class.rb +93 -0
- data/lib/rubocop/cop/lint/flip_flop.rb +8 -2
- data/lib/rubocop/cop/lint/interpolation_check.rb +7 -2
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +39 -7
- data/lib/rubocop/cop/lint/loop.rb +4 -4
- data/lib/rubocop/cop/lint/missing_super.rb +7 -4
- data/lib/rubocop/cop/lint/nested_percent_literal.rb +14 -0
- data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +58 -0
- data/lib/rubocop/cop/lint/number_conversion.rb +46 -13
- data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +27 -8
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +19 -16
- data/lib/rubocop/cop/lint/shadowed_exception.rb +4 -5
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +13 -0
- data/lib/rubocop/cop/lint/to_enum_arguments.rb +86 -0
- data/lib/rubocop/cop/lint/to_json.rb +1 -1
- data/lib/rubocop/cop/lint/unexpected_block_arity.rb +85 -0
- data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +199 -0
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
- data/lib/rubocop/cop/lint/useless_method_definition.rb +2 -4
- data/lib/rubocop/cop/lint/useless_setter_call.rb +6 -1
- 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 +68 -2
- 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/migration/department_name.rb +1 -1
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -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/line_length_help.rb +1 -1
- 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/statement_modifier.rb +9 -4
- data/lib/rubocop/cop/mixin/string_help.rb +4 -1
- data/lib/rubocop/cop/mixin/visibility_help.rb +1 -3
- data/lib/rubocop/cop/naming/accessor_method_name.rb +15 -1
- data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +12 -2
- data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +11 -5
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +67 -18
- data/lib/rubocop/cop/naming/predicate_name.rb +2 -1
- data/lib/rubocop/cop/naming/variable_number.rb +100 -8
- data/lib/rubocop/cop/offense.rb +3 -3
- data/lib/rubocop/cop/security/open.rb +12 -10
- data/lib/rubocop/cop/style/accessor_grouping.rb +1 -1
- data/lib/rubocop/cop/style/and_or.rb +11 -3
- data/lib/rubocop/cop/style/arguments_forwarding.rb +142 -0
- data/lib/rubocop/cop/style/bisected_attr_accessor.rb +0 -4
- data/lib/rubocop/cop/style/case_like_if.rb +0 -4
- data/lib/rubocop/cop/style/character_literal.rb +10 -11
- data/lib/rubocop/cop/style/class_and_module_children.rb +8 -3
- data/lib/rubocop/cop/style/collection_compact.rb +91 -0
- data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +169 -0
- data/lib/rubocop/cop/style/documentation.rb +12 -1
- data/lib/rubocop/cop/style/double_negation.rb +6 -1
- data/lib/rubocop/cop/style/float_division.rb +44 -1
- data/lib/rubocop/cop/style/format_string.rb +8 -3
- data/lib/rubocop/cop/style/format_string_token.rb +47 -2
- data/lib/rubocop/cop/style/hash_syntax.rb +3 -3
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +7 -2
- data/lib/rubocop/cop/style/if_inside_else.rb +37 -1
- data/lib/rubocop/cop/style/if_unless_modifier.rb +11 -3
- data/lib/rubocop/cop/style/if_with_semicolon.rb +39 -4
- data/lib/rubocop/cop/style/infinite_loop.rb +4 -0
- data/lib/rubocop/cop/style/ip_addresses.rb +1 -1
- data/lib/rubocop/cop/style/keyword_parameters_order.rb +12 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +10 -13
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +8 -13
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +7 -11
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +11 -2
- data/lib/rubocop/cop/style/mixin_grouping.rb +0 -4
- data/lib/rubocop/cop/style/multiple_comparison.rb +55 -7
- data/lib/rubocop/cop/style/negated_if_else_condition.rb +106 -0
- data/lib/rubocop/cop/style/nil_lambda.rb +52 -0
- data/lib/rubocop/cop/style/numeric_literals.rb +14 -11
- data/lib/rubocop/cop/style/perl_backrefs.rb +86 -9
- data/lib/rubocop/cop/style/raise_args.rb +21 -6
- data/lib/rubocop/cop/style/redundant_argument.rb +88 -0
- data/lib/rubocop/cop/style/redundant_condition.rb +2 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +4 -0
- data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +7 -1
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +24 -8
- data/lib/rubocop/cop/style/redundant_self.rb +3 -0
- data/lib/rubocop/cop/style/safe_navigation.rb +16 -4
- data/lib/rubocop/cop/style/semicolon.rb +3 -0
- data/lib/rubocop/cop/style/single_line_block_params.rb +30 -7
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +65 -3
- data/lib/rubocop/cop/style/special_global_vars.rb +1 -13
- data/lib/rubocop/cop/style/static_class.rb +97 -0
- data/lib/rubocop/cop/style/string_concatenation.rb +39 -2
- data/lib/rubocop/cop/style/string_literals.rb +14 -8
- data/lib/rubocop/cop/style/string_literals_in_interpolation.rb +4 -3
- data/lib/rubocop/cop/style/swap_values.rb +108 -0
- data/lib/rubocop/cop/style/symbol_proc.rb +5 -3
- data/lib/rubocop/cop/style/trailing_underscore_variable.rb +3 -1
- data/lib/rubocop/cop/style/while_until_modifier.rb +9 -0
- data/lib/rubocop/cop/team.rb +6 -1
- data/lib/rubocop/cop/util.rb +6 -2
- 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 +36 -11
- data/lib/rubocop/ext/regexp_parser.rb +95 -0
- data/lib/rubocop/formatter/disabled_config_formatter.rb +21 -6
- data/lib/rubocop/formatter/emacs_style_formatter.rb +2 -0
- data/lib/rubocop/formatter/formatter_set.rb +2 -1
- data/lib/rubocop/formatter/git_hub_actions_formatter.rb +47 -0
- data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
- data/lib/rubocop/formatter/simple_text_formatter.rb +2 -0
- data/lib/rubocop/formatter/tap_formatter.rb +2 -0
- data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
- data/lib/rubocop/lockfile.rb +40 -0
- data/lib/rubocop/magic_comment.rb +2 -2
- data/lib/rubocop/options.rb +11 -1
- data/lib/rubocop/rake_task.rb +2 -2
- data/lib/rubocop/rspec/shared_contexts.rb +4 -0
- data/lib/rubocop/runner.rb +1 -1
- data/lib/rubocop/target_finder.rb +1 -1
- data/lib/rubocop/target_ruby.rb +65 -1
- data/lib/rubocop/version.rb +56 -6
- metadata +50 -9
- data/bin/console +0 -10
- data/bin/rubocop-profile +0 -32
- data/bin/setup +0 -7
@@ -54,11 +54,9 @@ module RuboCop
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def delegating?(node, def_node)
|
57
|
-
if node
|
58
|
-
false
|
59
|
-
elsif node.zsuper_type?
|
57
|
+
if node&.zsuper_type?
|
60
58
|
true
|
61
|
-
elsif node
|
59
|
+
elsif node&.super_type?
|
62
60
|
node.arguments.map(&:source) == def_node.arguments.map(&:source)
|
63
61
|
else
|
64
62
|
false
|
@@ -5,6 +5,7 @@ module RuboCop
|
|
5
5
|
module Lint
|
6
6
|
# This cop checks for setter call to local variable as the final
|
7
7
|
# expression of a function definition.
|
8
|
+
# Its auto-correction is marked as unsafe because return value will be changed.
|
8
9
|
#
|
9
10
|
# NOTE: There are edge cases in which the local variable references a
|
10
11
|
# value that is also accessible outside the local scope. This is not
|
@@ -29,6 +30,8 @@ module RuboCop
|
|
29
30
|
# x
|
30
31
|
# end
|
31
32
|
class UselessSetterCall < Base
|
33
|
+
extend AutoCorrector
|
34
|
+
|
32
35
|
MSG = 'Useless setter call to local variable `%<variable>s`.'
|
33
36
|
ASSIGNMENT_TYPES = %i[lvasgn ivasgn cvasgn gvasgn].freeze
|
34
37
|
|
@@ -45,7 +48,9 @@ module RuboCop
|
|
45
48
|
|
46
49
|
loc_name = receiver.loc.name
|
47
50
|
|
48
|
-
add_offense(loc_name, message: format(MSG, variable: loc_name.source))
|
51
|
+
add_offense(loc_name, message: format(MSG, variable: loc_name.source)) do |corrector|
|
52
|
+
corrector.insert_after(last_expr, "\n#{indent(last_expr)}#{loc_name.source}")
|
53
|
+
end
|
49
54
|
end
|
50
55
|
alias on_defs on_def
|
51
56
|
|
@@ -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,13 +4,75 @@ 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]'
|
59
|
+
|
60
|
+
NAMED_KEYWORD_TYPES = %i[kwoptarg kwarg].freeze
|
61
|
+
private_constant :NAMED_KEYWORD_TYPES
|
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
|
14
76
|
|
15
77
|
def on_args(node)
|
16
78
|
count = args_count(node)
|
@@ -33,7 +95,7 @@ module RuboCop
|
|
33
95
|
if count_keyword_args?
|
34
96
|
node.children.size
|
35
97
|
else
|
36
|
-
node.children.count { |a|
|
98
|
+
node.children.count { |a| !NAMED_KEYWORD_TYPES.include?(a.type) }
|
37
99
|
end
|
38
100
|
end
|
39
101
|
|
@@ -41,6 +103,10 @@ module RuboCop
|
|
41
103
|
cop_config['Max']
|
42
104
|
end
|
43
105
|
|
106
|
+
def max_optional_parameters
|
107
|
+
cop_config['MaxOptionalParameters']
|
108
|
+
end
|
109
|
+
|
44
110
|
def count_keyword_args?
|
45
111
|
cop_config['CountKeywordArgs']
|
46
112
|
end
|
@@ -13,6 +13,7 @@ module RuboCop
|
|
13
13
|
class AbcSizeCalculator
|
14
14
|
include IteratingBlock
|
15
15
|
include RepeatedCsendDiscount
|
16
|
+
prepend RepeatedAttributeDiscount
|
16
17
|
|
17
18
|
# > Branch -- an explicit forward program branch out of scope -- a
|
18
19
|
# > function call, class method call ..
|
@@ -24,8 +25,8 @@ module RuboCop
|
|
24
25
|
# > http://c2.com/cgi/wiki?AbcMetric
|
25
26
|
CONDITION_NODES = CyclomaticComplexity::COUNTED_NODES.freeze
|
26
27
|
|
27
|
-
def self.calculate(node)
|
28
|
-
new(node).calculate
|
28
|
+
def self.calculate(node, discount_repeated_attributes: false)
|
29
|
+
new(node, discount_repeated_attributes: discount_repeated_attributes).calculate
|
29
30
|
end
|
30
31
|
|
31
32
|
# TODO: move to rubocop-ast
|
@@ -42,14 +43,8 @@ module RuboCop
|
|
42
43
|
end
|
43
44
|
|
44
45
|
def calculate
|
45
|
-
@node
|
46
|
-
|
47
|
-
|
48
|
-
if branch?(child)
|
49
|
-
evaluate_branch_nodes(child)
|
50
|
-
elsif condition?(child)
|
51
|
-
evaluate_condition_node(child)
|
52
|
-
end
|
46
|
+
visit_depth_last(@node) do |child|
|
47
|
+
calculate_node(child)
|
53
48
|
end
|
54
49
|
|
55
50
|
[
|
@@ -80,6 +75,21 @@ module RuboCop
|
|
80
75
|
|
81
76
|
private
|
82
77
|
|
78
|
+
def visit_depth_last(node, &block)
|
79
|
+
node.each_child_node { |child| visit_depth_last(child, &block) }
|
80
|
+
yield node
|
81
|
+
end
|
82
|
+
|
83
|
+
def calculate_node(node)
|
84
|
+
@assignment += 1 if assignment?(node)
|
85
|
+
|
86
|
+
if branch?(node)
|
87
|
+
evaluate_branch_nodes(node)
|
88
|
+
elsif condition?(node)
|
89
|
+
evaluate_condition_node(node)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
83
93
|
def assignment?(node)
|
84
94
|
return compound_assignment(node) if node.masgn_type? || node.shorthand_asgn?
|
85
95
|
|
@@ -0,0 +1,146 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Metrics
|
6
|
+
module Utils
|
7
|
+
# @api private
|
8
|
+
#
|
9
|
+
# Identifies repetitions `{c}send` calls with no arguments:
|
10
|
+
#
|
11
|
+
# foo.bar
|
12
|
+
# foo.bar # => repeated
|
13
|
+
# foo.bar.baz.qux # => inner send repeated
|
14
|
+
# foo.bar.baz.other # => both inner send repeated
|
15
|
+
# foo.bar(2) # => not repeated
|
16
|
+
#
|
17
|
+
# It also invalidates sequences if a receiver is reassigned:
|
18
|
+
#
|
19
|
+
# xx.foo.bar
|
20
|
+
# xx.foo.baz # => inner send repeated
|
21
|
+
# self.xx = any # => invalidates everything so far
|
22
|
+
# xx.foo.baz # => no repetition
|
23
|
+
# self.xx.foo.baz # => all repeated
|
24
|
+
#
|
25
|
+
module RepeatedAttributeDiscount
|
26
|
+
extend NodePattern::Macros
|
27
|
+
include RuboCop::AST::Sexp
|
28
|
+
|
29
|
+
# Plug into the calculator
|
30
|
+
def initialize(node, discount_repeated_attributes: false)
|
31
|
+
super(node)
|
32
|
+
return unless discount_repeated_attributes
|
33
|
+
|
34
|
+
self_attributes = {} # Share hash for `(send nil? :foo)` and `(send (self) :foo)`
|
35
|
+
@known_attributes = {
|
36
|
+
s(:self) => self_attributes,
|
37
|
+
nil => self_attributes
|
38
|
+
}
|
39
|
+
# example after running `obj = foo.bar; obj.baz.qux`
|
40
|
+
# { nil => {foo: {bar: {}}},
|
41
|
+
# s(self) => same hash ^,
|
42
|
+
# s(:lvar, :obj) => {baz: {qux: {}}}
|
43
|
+
# }
|
44
|
+
end
|
45
|
+
|
46
|
+
def discount_repeated_attributes?
|
47
|
+
defined?(@known_attributes)
|
48
|
+
end
|
49
|
+
|
50
|
+
def evaluate_branch_nodes(node)
|
51
|
+
return if discount_repeated_attributes? && discount_repeated_attribute?(node)
|
52
|
+
|
53
|
+
super
|
54
|
+
end
|
55
|
+
|
56
|
+
def calculate_node(node)
|
57
|
+
update_repeated_attribute(node) if discount_repeated_attributes?
|
58
|
+
super
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def_node_matcher :attribute_call?, <<~PATTERN
|
64
|
+
( {csend send} _receiver _method # and no parameters
|
65
|
+
)
|
66
|
+
PATTERN
|
67
|
+
|
68
|
+
def discount_repeated_attribute?(send_node)
|
69
|
+
return false unless attribute_call?(send_node)
|
70
|
+
|
71
|
+
repeated = true
|
72
|
+
find_attributes(send_node) do |hash, lookup|
|
73
|
+
return false if hash.nil?
|
74
|
+
|
75
|
+
repeated = false
|
76
|
+
hash[lookup] = {}
|
77
|
+
end
|
78
|
+
|
79
|
+
repeated
|
80
|
+
end
|
81
|
+
|
82
|
+
def update_repeated_attribute(node)
|
83
|
+
return unless (receiver, method = setter_to_getter(node))
|
84
|
+
|
85
|
+
calls = find_attributes(receiver) { return }
|
86
|
+
if method # e.g. `self.foo = 42`
|
87
|
+
calls.delete(method)
|
88
|
+
else # e.g. `var = 42`
|
89
|
+
calls.clear
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def_node_matcher :root_node?, <<~PATTERN
|
94
|
+
{ nil? | self # e.g. receiver of `my_method` or `self.my_attr`
|
95
|
+
| lvar | ivar | cvar | gvar # e.g. receiver of `var.my_method`
|
96
|
+
| const } # e.g. receiver of `MyConst.foo.bar`
|
97
|
+
PATTERN
|
98
|
+
|
99
|
+
# Returns the "known_attributes" for the `node` by walking the receiver tree
|
100
|
+
# If at any step the subdirectory does not exist, it is yielded with the
|
101
|
+
# associated key (method_name)
|
102
|
+
# If the node is not a series of `(c)send` calls with no arguments,
|
103
|
+
# then `nil` is yielded
|
104
|
+
def find_attributes(node, &block)
|
105
|
+
if attribute_call?(node)
|
106
|
+
calls = find_attributes(node.receiver, &block)
|
107
|
+
value = node.method_name
|
108
|
+
elsif root_node?(node)
|
109
|
+
calls = @known_attributes
|
110
|
+
value = node
|
111
|
+
else
|
112
|
+
return yield nil
|
113
|
+
end
|
114
|
+
|
115
|
+
calls.fetch(value) do
|
116
|
+
yield [calls, value]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
VAR_SETTER_TO_GETTER = {
|
121
|
+
lvasgn: :lvar,
|
122
|
+
ivasgn: :ivar,
|
123
|
+
cvasgn: :cvar,
|
124
|
+
gvasgn: :gvar
|
125
|
+
}.freeze
|
126
|
+
|
127
|
+
# @returns `[receiver, method | nil]` for the given setter `node`
|
128
|
+
# or `nil` if it is not a setter.
|
129
|
+
def setter_to_getter(node)
|
130
|
+
if (type = VAR_SETTER_TO_GETTER[node.type])
|
131
|
+
# (lvasgn :my_var (int 42)) => [(lvar my_var), nil]
|
132
|
+
[s(type, node.children.first), nil]
|
133
|
+
elsif node.shorthand_asgn? # (or-asgn (send _receiver :foo) _value)
|
134
|
+
# (or-asgn (send _receiver :foo) _value) => [_receiver, :foo]
|
135
|
+
node.children.first.children
|
136
|
+
elsif node.respond_to?(:setter_method?) && node.setter_method?
|
137
|
+
# (send _receiver :foo= (int 42) ) => [_receiver, :foo]
|
138
|
+
method_name = node.method_name[0...-1].to_sym
|
139
|
+
[node.receiver, method_name]
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|