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.
Files changed (199) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +36 -16
  3. data/config/default.yml +276 -82
  4. data/config/obsoletion.yml +196 -0
  5. data/exe/rubocop +1 -1
  6. data/lib/rubocop.rb +31 -2
  7. data/lib/rubocop/cli.rb +5 -1
  8. data/lib/rubocop/cli/command/auto_genenerate_config.rb +1 -1
  9. data/lib/rubocop/cli/command/execute_runner.rb +26 -11
  10. data/lib/rubocop/cli/command/suggest_extensions.rb +80 -0
  11. data/lib/rubocop/cli/command/version.rb +1 -1
  12. data/lib/rubocop/comment_config.rb +1 -1
  13. data/lib/rubocop/config.rb +4 -0
  14. data/lib/rubocop/config_loader.rb +34 -8
  15. data/lib/rubocop/config_loader_resolver.rb +12 -6
  16. data/lib/rubocop/config_obsoletion.rb +65 -247
  17. data/lib/rubocop/config_obsoletion/changed_enforced_styles.rb +33 -0
  18. data/lib/rubocop/config_obsoletion/changed_parameter.rb +21 -0
  19. data/lib/rubocop/config_obsoletion/cop_rule.rb +34 -0
  20. data/lib/rubocop/config_obsoletion/extracted_cop.rb +44 -0
  21. data/lib/rubocop/config_obsoletion/parameter_rule.rb +44 -0
  22. data/lib/rubocop/config_obsoletion/removed_cop.rb +41 -0
  23. data/lib/rubocop/config_obsoletion/renamed_cop.rb +34 -0
  24. data/lib/rubocop/config_obsoletion/rule.rb +41 -0
  25. data/lib/rubocop/config_obsoletion/split_cop.rb +27 -0
  26. data/lib/rubocop/config_regeneration.rb +1 -1
  27. data/lib/rubocop/config_validator.rb +25 -10
  28. data/lib/rubocop/cop/autocorrect_logic.rb +21 -6
  29. data/lib/rubocop/cop/badge.rb +9 -24
  30. data/lib/rubocop/cop/base.rb +33 -16
  31. data/lib/rubocop/cop/bundler/duplicated_gem.rb +26 -6
  32. data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
  33. data/lib/rubocop/cop/commissioner.rb +37 -23
  34. data/lib/rubocop/cop/cop.rb +2 -2
  35. data/lib/rubocop/cop/corrector.rb +3 -1
  36. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
  37. data/lib/rubocop/cop/correctors/string_literal_corrector.rb +6 -8
  38. data/lib/rubocop/cop/force.rb +1 -1
  39. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +3 -3
  40. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +4 -5
  41. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +1 -1
  42. data/lib/rubocop/cop/generator.rb +3 -10
  43. data/lib/rubocop/cop/generator/configuration_injector.rb +1 -1
  44. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +1 -1
  45. data/lib/rubocop/cop/layout/block_alignment.rb +3 -4
  46. data/lib/rubocop/cop/layout/class_structure.rb +22 -3
  47. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  48. data/lib/rubocop/cop/layout/else_alignment.rb +15 -2
  49. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +80 -10
  50. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -0
  51. data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +6 -1
  52. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -1
  53. data/lib/rubocop/cop/layout/end_alignment.rb +3 -3
  54. data/lib/rubocop/cop/layout/end_of_line.rb +5 -5
  55. data/lib/rubocop/cop/layout/extra_spacing.rb +1 -2
  56. data/lib/rubocop/cop/layout/first_argument_indentation.rb +7 -2
  57. data/lib/rubocop/cop/layout/hash_alignment.rb +4 -4
  58. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +12 -0
  59. data/lib/rubocop/cop/layout/line_length.rb +10 -13
  60. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +7 -3
  61. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +24 -18
  62. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +1 -1
  63. data/lib/rubocop/cop/layout/space_around_operators.rb +4 -1
  64. data/lib/rubocop/cop/layout/space_inside_parens.rb +35 -13
  65. data/lib/rubocop/cop/layout/trailing_whitespace.rb +37 -13
  66. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +2 -1
  67. data/lib/rubocop/cop/lint/constant_definition_in_block.rb +26 -2
  68. data/lib/rubocop/cop/lint/debugger.rb +17 -28
  69. data/lib/rubocop/cop/lint/duplicate_branch.rb +93 -0
  70. data/lib/rubocop/cop/lint/duplicate_case_condition.rb +2 -12
  71. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +77 -0
  72. data/lib/rubocop/cop/lint/else_layout.rb +29 -3
  73. data/lib/rubocop/cop/lint/empty_block.rb +82 -0
  74. data/lib/rubocop/cop/lint/empty_class.rb +93 -0
  75. data/lib/rubocop/cop/lint/flip_flop.rb +8 -2
  76. data/lib/rubocop/cop/lint/interpolation_check.rb +7 -2
  77. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +39 -7
  78. data/lib/rubocop/cop/lint/loop.rb +4 -4
  79. data/lib/rubocop/cop/lint/missing_super.rb +7 -4
  80. data/lib/rubocop/cop/lint/nested_percent_literal.rb +14 -0
  81. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +58 -0
  82. data/lib/rubocop/cop/lint/number_conversion.rb +46 -13
  83. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +27 -8
  84. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +19 -16
  85. data/lib/rubocop/cop/lint/shadowed_exception.rb +4 -5
  86. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +13 -0
  87. data/lib/rubocop/cop/lint/to_enum_arguments.rb +86 -0
  88. data/lib/rubocop/cop/lint/to_json.rb +1 -1
  89. data/lib/rubocop/cop/lint/unexpected_block_arity.rb +85 -0
  90. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +199 -0
  91. data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
  92. data/lib/rubocop/cop/lint/useless_method_definition.rb +2 -4
  93. data/lib/rubocop/cop/lint/useless_setter_call.rb +6 -1
  94. data/lib/rubocop/cop/metrics/abc_size.rb +25 -1
  95. data/lib/rubocop/cop/metrics/block_length.rb +13 -7
  96. data/lib/rubocop/cop/metrics/method_length.rb +7 -2
  97. data/lib/rubocop/cop/metrics/parameter_lists.rb +68 -2
  98. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +20 -10
  99. data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +146 -0
  100. data/lib/rubocop/cop/metrics/utils/repeated_csend_discount.rb +6 -1
  101. data/lib/rubocop/cop/migration/department_name.rb +1 -1
  102. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  103. data/lib/rubocop/cop/mixin/configurable_numbering.rb +4 -3
  104. data/lib/rubocop/cop/mixin/enforce_superclass.rb +9 -1
  105. data/lib/rubocop/cop/mixin/ignored_methods.rb +36 -3
  106. data/lib/rubocop/cop/mixin/line_length_help.rb +1 -1
  107. data/lib/rubocop/cop/mixin/method_complexity.rb +6 -0
  108. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
  109. data/lib/rubocop/cop/mixin/statement_modifier.rb +9 -4
  110. data/lib/rubocop/cop/mixin/string_help.rb +4 -1
  111. data/lib/rubocop/cop/mixin/visibility_help.rb +1 -3
  112. data/lib/rubocop/cop/naming/accessor_method_name.rb +15 -1
  113. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +12 -2
  114. data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +11 -5
  115. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +67 -18
  116. data/lib/rubocop/cop/naming/predicate_name.rb +2 -1
  117. data/lib/rubocop/cop/naming/variable_number.rb +100 -8
  118. data/lib/rubocop/cop/offense.rb +3 -3
  119. data/lib/rubocop/cop/security/open.rb +12 -10
  120. data/lib/rubocop/cop/style/accessor_grouping.rb +1 -1
  121. data/lib/rubocop/cop/style/and_or.rb +11 -3
  122. data/lib/rubocop/cop/style/arguments_forwarding.rb +142 -0
  123. data/lib/rubocop/cop/style/bisected_attr_accessor.rb +0 -4
  124. data/lib/rubocop/cop/style/case_like_if.rb +0 -4
  125. data/lib/rubocop/cop/style/character_literal.rb +10 -11
  126. data/lib/rubocop/cop/style/class_and_module_children.rb +8 -3
  127. data/lib/rubocop/cop/style/collection_compact.rb +91 -0
  128. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +169 -0
  129. data/lib/rubocop/cop/style/documentation.rb +12 -1
  130. data/lib/rubocop/cop/style/double_negation.rb +6 -1
  131. data/lib/rubocop/cop/style/float_division.rb +44 -1
  132. data/lib/rubocop/cop/style/format_string.rb +8 -3
  133. data/lib/rubocop/cop/style/format_string_token.rb +47 -2
  134. data/lib/rubocop/cop/style/hash_syntax.rb +3 -3
  135. data/lib/rubocop/cop/style/identical_conditional_branches.rb +7 -2
  136. data/lib/rubocop/cop/style/if_inside_else.rb +37 -1
  137. data/lib/rubocop/cop/style/if_unless_modifier.rb +11 -3
  138. data/lib/rubocop/cop/style/if_with_semicolon.rb +39 -4
  139. data/lib/rubocop/cop/style/infinite_loop.rb +4 -0
  140. data/lib/rubocop/cop/style/ip_addresses.rb +1 -1
  141. data/lib/rubocop/cop/style/keyword_parameters_order.rb +12 -0
  142. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +10 -13
  143. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +8 -13
  144. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +7 -11
  145. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +11 -2
  146. data/lib/rubocop/cop/style/mixin_grouping.rb +0 -4
  147. data/lib/rubocop/cop/style/multiple_comparison.rb +55 -7
  148. data/lib/rubocop/cop/style/negated_if_else_condition.rb +106 -0
  149. data/lib/rubocop/cop/style/nil_lambda.rb +52 -0
  150. data/lib/rubocop/cop/style/numeric_literals.rb +14 -11
  151. data/lib/rubocop/cop/style/perl_backrefs.rb +86 -9
  152. data/lib/rubocop/cop/style/raise_args.rb +21 -6
  153. data/lib/rubocop/cop/style/redundant_argument.rb +88 -0
  154. data/lib/rubocop/cop/style/redundant_condition.rb +2 -1
  155. data/lib/rubocop/cop/style/redundant_parentheses.rb +4 -0
  156. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +7 -1
  157. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +24 -8
  158. data/lib/rubocop/cop/style/redundant_self.rb +3 -0
  159. data/lib/rubocop/cop/style/safe_navigation.rb +16 -4
  160. data/lib/rubocop/cop/style/semicolon.rb +3 -0
  161. data/lib/rubocop/cop/style/single_line_block_params.rb +30 -7
  162. data/lib/rubocop/cop/style/sole_nested_conditional.rb +65 -3
  163. data/lib/rubocop/cop/style/special_global_vars.rb +1 -13
  164. data/lib/rubocop/cop/style/static_class.rb +97 -0
  165. data/lib/rubocop/cop/style/string_concatenation.rb +39 -2
  166. data/lib/rubocop/cop/style/string_literals.rb +14 -8
  167. data/lib/rubocop/cop/style/string_literals_in_interpolation.rb +4 -3
  168. data/lib/rubocop/cop/style/swap_values.rb +108 -0
  169. data/lib/rubocop/cop/style/symbol_proc.rb +5 -3
  170. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +3 -1
  171. data/lib/rubocop/cop/style/while_until_modifier.rb +9 -0
  172. data/lib/rubocop/cop/team.rb +6 -1
  173. data/lib/rubocop/cop/util.rb +6 -2
  174. data/lib/rubocop/cop/variable_force/branch.rb +1 -1
  175. data/lib/rubocop/cop/variable_force/scope.rb +1 -1
  176. data/lib/rubocop/core_ext/hash.rb +20 -0
  177. data/lib/rubocop/ext/regexp_node.rb +36 -11
  178. data/lib/rubocop/ext/regexp_parser.rb +95 -0
  179. data/lib/rubocop/formatter/disabled_config_formatter.rb +21 -6
  180. data/lib/rubocop/formatter/emacs_style_formatter.rb +2 -0
  181. data/lib/rubocop/formatter/formatter_set.rb +2 -1
  182. data/lib/rubocop/formatter/git_hub_actions_formatter.rb +47 -0
  183. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  184. data/lib/rubocop/formatter/simple_text_formatter.rb +2 -0
  185. data/lib/rubocop/formatter/tap_formatter.rb +2 -0
  186. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
  187. data/lib/rubocop/lockfile.rb +40 -0
  188. data/lib/rubocop/magic_comment.rb +2 -2
  189. data/lib/rubocop/options.rb +11 -1
  190. data/lib/rubocop/rake_task.rb +2 -2
  191. data/lib/rubocop/rspec/shared_contexts.rb +4 -0
  192. data/lib/rubocop/runner.rb +1 -1
  193. data/lib/rubocop/target_finder.rb +1 -1
  194. data/lib/rubocop/target_ruby.rb +65 -1
  195. data/lib/rubocop/version.rb +56 -6
  196. metadata +50 -9
  197. data/bin/console +0 -10
  198. data/bin/rubocop-profile +0 -32
  199. 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.nil?
58
- false
59
- elsif node.zsuper_type?
57
+ if node&.zsuper_type?
60
58
  true
61
- elsif node.super_type?
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(node)
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 excluded_method?(node)
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 excluded_method?(node)
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
- excluded_methods.any? do |config|
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
- excluded_methods = cop_config['ExcludedMethods']
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| !%i[kwoptarg kwarg].include?(a.type) }
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.each_node do |child|
46
- @assignment += 1 if assignment?(child)
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