rubocop 1.57.2 → 1.60.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 (139) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +4 -4
  4. data/config/default.yml +46 -3
  5. data/lib/rubocop/config.rb +0 -2
  6. data/lib/rubocop/config_loader.rb +0 -1
  7. data/lib/rubocop/config_obsoletion.rb +11 -8
  8. data/lib/rubocop/config_validator.rb +0 -2
  9. data/lib/rubocop/cop/base.rb +6 -0
  10. data/lib/rubocop/cop/bundler/gem_comment.rb +2 -2
  11. data/lib/rubocop/cop/exclude_limit.rb +1 -1
  12. data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +2 -2
  13. data/lib/rubocop/cop/internal_affairs/method_name_equal.rb +19 -20
  14. data/lib/rubocop/cop/internal_affairs/node_first_or_last_argument.rb +53 -0
  15. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -2
  16. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  17. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  18. data/lib/rubocop/cop/layout/end_alignment.rb +5 -1
  19. data/lib/rubocop/cop/layout/extra_spacing.rb +4 -10
  20. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +22 -7
  21. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
  22. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  23. data/lib/rubocop/cop/layout/heredoc_indentation.rb +1 -1
  24. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +1 -1
  25. data/lib/rubocop/cop/layout/redundant_line_break.rb +2 -1
  26. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +4 -4
  27. data/lib/rubocop/cop/layout/single_line_block_chain.rb +5 -0
  28. data/lib/rubocop/cop/layout/space_around_operators.rb +50 -20
  29. data/lib/rubocop/cop/lint/assignment_in_condition.rb +4 -4
  30. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +2 -2
  31. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +1 -1
  32. data/lib/rubocop/cop/lint/debugger.rb +2 -1
  33. data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -1
  34. data/lib/rubocop/cop/lint/erb_new_arguments.rb +3 -3
  35. data/lib/rubocop/cop/lint/float_comparison.rb +10 -0
  36. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +2 -1
  37. data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +56 -0
  38. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +85 -0
  39. data/lib/rubocop/cop/lint/next_without_accumulator.rb +6 -21
  40. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -5
  41. data/lib/rubocop/cop/lint/number_conversion.rb +9 -4
  42. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +43 -0
  43. data/lib/rubocop/cop/lint/redundant_with_index.rb +2 -2
  44. data/lib/rubocop/cop/lint/redundant_with_object.rb +2 -2
  45. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +3 -4
  46. data/lib/rubocop/cop/lint/self_assignment.rb +38 -0
  47. data/lib/rubocop/cop/lint/shadowed_argument.rb +1 -0
  48. data/lib/rubocop/cop/lint/symbol_conversion.rb +7 -2
  49. data/lib/rubocop/cop/lint/syntax.rb +6 -3
  50. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -1
  51. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +2 -2
  52. data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
  53. data/lib/rubocop/cop/lint/useless_times.rb +1 -1
  54. data/lib/rubocop/cop/lint/void.rb +14 -1
  55. data/lib/rubocop/cop/metrics/abc_size.rb +3 -3
  56. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  57. data/lib/rubocop/cop/mixin/configurable_formatting.rb +1 -0
  58. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +1 -1
  59. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  60. data/lib/rubocop/cop/naming/block_forwarding.rb +12 -4
  61. data/lib/rubocop/cop/naming/constant_name.rb +1 -2
  62. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  63. data/lib/rubocop/cop/security/open.rb +2 -2
  64. data/lib/rubocop/cop/style/access_modifier_declarations.rb +2 -2
  65. data/lib/rubocop/cop/style/accessor_grouping.rb +1 -1
  66. data/lib/rubocop/cop/style/arguments_forwarding.rb +120 -17
  67. data/lib/rubocop/cop/style/array_first_last.rb +64 -0
  68. data/lib/rubocop/cop/style/auto_resource_cleanup.rb +21 -14
  69. data/lib/rubocop/cop/style/bisected_attr_accessor.rb +2 -2
  70. data/lib/rubocop/cop/style/case_like_if.rb +4 -4
  71. data/lib/rubocop/cop/style/class_check.rb +1 -0
  72. data/lib/rubocop/cop/style/collection_compact.rb +18 -8
  73. data/lib/rubocop/cop/style/combinable_loops.rb +13 -7
  74. data/lib/rubocop/cop/style/concat_array_literals.rb +1 -0
  75. data/lib/rubocop/cop/style/conditional_assignment.rb +2 -2
  76. data/lib/rubocop/cop/style/date_time.rb +5 -4
  77. data/lib/rubocop/cop/style/each_for_simple_loop.rb +7 -7
  78. data/lib/rubocop/cop/style/each_with_object.rb +2 -2
  79. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  80. data/lib/rubocop/cop/style/eval_with_location.rb +3 -14
  81. data/lib/rubocop/cop/style/exact_regexp_match.rb +2 -1
  82. data/lib/rubocop/cop/style/explicit_block_argument.rb +2 -2
  83. data/lib/rubocop/cop/style/hash_each_methods.rb +83 -10
  84. data/lib/rubocop/cop/style/hash_except.rb +2 -1
  85. data/lib/rubocop/cop/style/identical_conditional_branches.rb +4 -1
  86. data/lib/rubocop/cop/style/inverse_methods.rb +6 -5
  87. data/lib/rubocop/cop/style/invertible_unless_condition.rb +39 -2
  88. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +3 -2
  89. data/lib/rubocop/cop/style/map_to_hash.rb +17 -7
  90. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +14 -5
  91. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +2 -4
  92. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +20 -0
  93. data/lib/rubocop/cop/style/method_def_parentheses.rb +1 -1
  94. data/lib/rubocop/cop/style/missing_respond_to_missing.rb +2 -2
  95. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -3
  96. data/lib/rubocop/cop/style/next.rb +1 -1
  97. data/lib/rubocop/cop/style/numeric_literal_prefix.rb +1 -1
  98. data/lib/rubocop/cop/style/operator_method_call.rb +2 -2
  99. data/lib/rubocop/cop/style/parallel_assignment.rb +2 -2
  100. data/lib/rubocop/cop/style/parentheses_around_condition.rb +8 -0
  101. data/lib/rubocop/cop/style/redundant_argument.rb +3 -2
  102. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +3 -3
  103. data/lib/rubocop/cop/style/redundant_each.rb +7 -4
  104. data/lib/rubocop/cop/style/redundant_fetch_block.rb +3 -3
  105. data/lib/rubocop/cop/style/redundant_line_continuation.rb +10 -1
  106. data/lib/rubocop/cop/style/redundant_parentheses.rb +33 -10
  107. data/lib/rubocop/cop/style/redundant_return.rb +1 -1
  108. data/lib/rubocop/cop/style/redundant_self.rb +17 -2
  109. data/lib/rubocop/cop/style/redundant_sort.rb +9 -8
  110. data/lib/rubocop/cop/style/redundant_sort_by.rb +2 -2
  111. data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -1
  112. data/lib/rubocop/cop/style/sample.rb +2 -1
  113. data/lib/rubocop/cop/style/select_by_regexp.rb +7 -6
  114. data/lib/rubocop/cop/style/self_assignment.rb +1 -1
  115. data/lib/rubocop/cop/style/semicolon.rb +8 -0
  116. data/lib/rubocop/cop/style/single_argument_dig.rb +5 -2
  117. data/lib/rubocop/cop/style/slicing_with_range.rb +76 -10
  118. data/lib/rubocop/cop/style/string_chars.rb +1 -0
  119. data/lib/rubocop/cop/style/strip.rb +7 -4
  120. data/lib/rubocop/cop/style/super_with_args_parentheses.rb +35 -0
  121. data/lib/rubocop/cop/style/symbol_proc.rb +36 -0
  122. data/lib/rubocop/cop/style/unpack_first.rb +11 -14
  123. data/lib/rubocop/cops_documentation_generator.rb +11 -1
  124. data/lib/rubocop/ext/regexp_node.rb +9 -4
  125. data/lib/rubocop/formatter/disabled_config_formatter.rb +17 -6
  126. data/lib/rubocop/formatter/html_formatter.rb +1 -2
  127. data/lib/rubocop/formatter/json_formatter.rb +0 -1
  128. data/lib/rubocop/formatter.rb +1 -1
  129. data/lib/rubocop/lsp/routes.rb +1 -1
  130. data/lib/rubocop/options.rb +0 -8
  131. data/lib/rubocop/result_cache.rb +0 -1
  132. data/lib/rubocop/rspec/shared_contexts.rb +6 -0
  133. data/lib/rubocop/rspec/support.rb +1 -0
  134. data/lib/rubocop/runner.rb +1 -1
  135. data/lib/rubocop/server/cache.rb +1 -2
  136. data/lib/rubocop/version.rb +1 -1
  137. data/lib/rubocop.rb +4 -0
  138. metadata +15 -10
  139. /data/lib/rubocop/formatter/{git_hub_actions_formatter.rb → github_actions_formatter.rb} +0 -0
@@ -56,9 +56,9 @@ module RuboCop
56
56
  def_node_matcher :redundant_with_object?, <<~PATTERN
57
57
  {
58
58
  (block
59
- $(send _ {:each_with_object :with_object} _) (args (arg _)) ...)
59
+ $(call _ {:each_with_object :with_object} _) (args (arg _)) ...)
60
60
  (numblock
61
- $(send _ {:each_with_object :with_object} _) 1 ...)
61
+ $(call _ {:each_with_object :with_object} _) 1 ...)
62
62
  }
63
63
  PATTERN
64
64
 
@@ -45,10 +45,9 @@ module RuboCop
45
45
  bad_method?(node) do |safe_nav, method|
46
46
  return if nil_methods.include?(method) || PLUS_MINUS_METHODS.include?(node.method_name)
47
47
 
48
- location =
49
- Parser::Source::Range.new(node.source_range.source_buffer,
50
- safe_nav.source_range.end_pos,
51
- node.source_range.end_pos)
48
+ begin_range = node.loc.dot || safe_nav.source_range.end
49
+ location = begin_range.join(node.source_range.end)
50
+
52
51
  add_offense(location) do |corrector|
53
52
  autocorrect(corrector, offense_range: location, send_node: node)
54
53
  end
@@ -10,11 +10,18 @@ module RuboCop
10
10
  # foo = foo
11
11
  # foo, bar = foo, bar
12
12
  # Foo = Foo
13
+ # hash['foo'] = hash['foo']
14
+ # obj.attr = obj.attr
13
15
  #
14
16
  # # good
15
17
  # foo = bar
16
18
  # foo, bar = bar, foo
17
19
  # Foo = Bar
20
+ # hash['foo'] = hash['bar']
21
+ # obj.attr = obj.attr2
22
+ #
23
+ # # good (method calls possibly can return different results)
24
+ # hash[foo] = hash[foo]
18
25
  #
19
26
  class SelfAssignment < Base
20
27
  MSG = 'Self-assignment detected.'
@@ -26,6 +33,15 @@ module RuboCop
26
33
  gvasgn: :gvar
27
34
  }.freeze
28
35
 
36
+ def on_send(node)
37
+ if node.method?(:[]=)
38
+ handle_key_assignment(node) if node.arguments.size == 2
39
+ elsif node.assignment_method?
40
+ handle_attribute_assignment(node) if node.arguments.size == 1
41
+ end
42
+ end
43
+ alias on_csend on_send
44
+
29
45
  def on_lvasgn(node)
30
46
  lhs, rhs = *node
31
47
  return unless rhs
@@ -72,6 +88,28 @@ module RuboCop
72
88
  rhs.type == ASSIGNMENT_TYPE_TO_RHS_TYPE[lhs.type] &&
73
89
  rhs.children.first == lhs.children.first
74
90
  end
91
+
92
+ def handle_key_assignment(node)
93
+ value_node = node.arguments[1]
94
+
95
+ if value_node.send_type? && value_node.method?(:[]) &&
96
+ node.receiver == value_node.receiver &&
97
+ !node.first_argument.call_type? &&
98
+ node.first_argument == value_node.first_argument
99
+ add_offense(node)
100
+ end
101
+ end
102
+
103
+ def handle_attribute_assignment(node)
104
+ first_argument = node.first_argument
105
+ return unless first_argument.respond_to?(:arguments) && first_argument.arguments.empty?
106
+
107
+ if first_argument.call_type? &&
108
+ node.receiver == first_argument.receiver &&
109
+ first_argument.method_name.to_s == node.method_name.to_s.delete_suffix('=')
110
+ add_offense(node)
111
+ end
112
+ end
75
113
  end
76
114
  end
77
115
  end
@@ -123,6 +123,7 @@ module RuboCop
123
123
 
124
124
  # Shorthand assignments always use their arguments
125
125
  next false if assignment_node.shorthand_asgn?
126
+ next false unless assignment_node.parent
126
127
 
127
128
  node_within_block_or_conditional =
128
129
  node_within_block_or_conditional?(assignment_node.parent, argument.scope.node)
@@ -19,6 +19,7 @@ module RuboCop
19
19
  # 'underscored_string'.to_sym
20
20
  # :'underscored_symbol'
21
21
  # 'hyphenated-string'.to_sym
22
+ # "string_#{interpolation}".to_sym
22
23
  #
23
24
  # # good
24
25
  # :string
@@ -26,6 +27,7 @@ module RuboCop
26
27
  # :underscored_string
27
28
  # :underscored_symbol
28
29
  # :'hyphenated-string'
30
+ # :"string_#{interpolation}"
29
31
  #
30
32
  # @example EnforcedStyle: strict (default)
31
33
  #
@@ -75,9 +77,12 @@ module RuboCop
75
77
 
76
78
  def on_send(node)
77
79
  return unless node.receiver
78
- return unless node.receiver.str_type? || node.receiver.sym_type?
79
80
 
80
- register_offense(node, correction: node.receiver.value.to_sym.inspect)
81
+ if node.receiver.str_type? || node.receiver.sym_type?
82
+ register_offense(node, correction: node.receiver.value.to_sym.inspect)
83
+ elsif node.receiver.dstr_type?
84
+ register_offense(node, correction: ":\"#{node.receiver.value.to_sym}\"")
85
+ end
81
86
  end
82
87
 
83
88
  def on_sym(node)
@@ -17,9 +17,12 @@ module RuboCop
17
17
  private
18
18
 
19
19
  def add_offense_from_diagnostic(diagnostic, ruby_version)
20
- message =
21
- "#{diagnostic.message}\n(Using Ruby #{ruby_version} parser; " \
22
- 'configure using `TargetRubyVersion` parameter, under `AllCops`)'
20
+ message = if lsp_mode?
21
+ diagnostic.message
22
+ else
23
+ "#{diagnostic.message}\n(Using Ruby #{ruby_version} parser; " \
24
+ 'configure using `TargetRubyVersion` parameter, under `AllCops`)'
25
+ end
23
26
  add_offense(diagnostic.location, message: message, severity: diagnostic.level)
24
27
  end
25
28
 
@@ -34,7 +34,7 @@ module RuboCop
34
34
  MSG = 'Avoid leaving a trailing comma in attribute declarations.'
35
35
 
36
36
  def on_send(node)
37
- return unless node.attribute_accessor? && node.arguments.last.def_type?
37
+ return unless node.attribute_accessor? && node.last_argument.def_type?
38
38
 
39
39
  trailing_comma = trailing_comma_range(node)
40
40
 
@@ -69,8 +69,8 @@ module RuboCop
69
69
  # @!method reduce_with_block?(node)
70
70
  def_node_matcher :reduce_with_block?, <<~PATTERN
71
71
  {
72
- (block (send _recv {:reduce :inject} ...) args ...)
73
- (numblock (send _recv {:reduce :inject} ...) ...)
72
+ (block (call _recv {:reduce :inject} ...) args ...)
73
+ (numblock (call _recv {:reduce :inject} ...) ...)
74
74
  }
75
75
  PATTERN
76
76
 
@@ -256,7 +256,7 @@ module RuboCop
256
256
 
257
257
  def any_method_definition?(child)
258
258
  cop_config.fetch('MethodCreatingMethods', []).any? do |m|
259
- matcher_name = "#{m}_method?".to_sym
259
+ matcher_name = :"#{m}_method?"
260
260
  unless respond_to?(matcher_name)
261
261
  self.class.def_node_matcher matcher_name, <<~PATTERN
262
262
  {def (send nil? :#{m} ...)}
@@ -279,7 +279,7 @@ module RuboCop
279
279
 
280
280
  def any_context_creating_methods?(child)
281
281
  cop_config.fetch('ContextCreatingMethods', []).any? do |m|
282
- matcher_name = "#{m}_block?".to_sym
282
+ matcher_name = :"#{m}_block?"
283
283
  unless respond_to?(matcher_name)
284
284
  self.class.def_node_matcher matcher_name, <<~PATTERN
285
285
  ({block numblock} (send {nil? const} {:#{m}} ...) ...)
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Cop
5
5
  module Lint
6
6
  # Checks for uses of `Integer#times` that will never yield
7
- # (when the integer <= 0) or that will only ever yield once
7
+ # (when the integer ``<= 0``) or that will only ever yield once
8
8
  # (`1.times`).
9
9
  #
10
10
  # @safety
@@ -144,7 +144,7 @@ module RuboCop
144
144
  end
145
145
 
146
146
  def check_literal(node)
147
- return if !node.literal? || node.xstr_type? || node.range_type?
147
+ return if !entirely_literal?(node) || node.xstr_type? || node.range_type?
148
148
 
149
149
  add_offense(node, message: format(LIT_MSG, lit: node.source)) do |corrector|
150
150
  autocorrect_void_expression(corrector, node)
@@ -217,6 +217,19 @@ module RuboCop
217
217
  end
218
218
  corrector.replace(send_node.loc.selector, suggestion)
219
219
  end
220
+
221
+ def entirely_literal?(node)
222
+ case node.type
223
+ when :array
224
+ node.each_value.all? { |value| entirely_literal?(value) }
225
+ when :hash
226
+ return false unless node.each_key.all? { |key| entirely_literal?(key) }
227
+
228
+ node.each_value.all? { |value| entirely_literal?(value) }
229
+ else
230
+ node.literal?
231
+ end
232
+ end
220
233
  end
221
234
  end
222
235
  end
@@ -10,9 +10,9 @@ module RuboCop
10
10
  #
11
11
  # Interpreting ABC size:
12
12
  #
13
- # * <= 17 satisfactory
14
- # * 18..30 unsatisfactory
15
- # * > 30 dangerous
13
+ # * ``<= 17`` satisfactory
14
+ # * `18..30` unsatisfactory
15
+ # * `>` 30 dangerous
16
16
  #
17
17
  # You can have repeated "attributes" calls count as a single "branch".
18
18
  # For this purpose, attributes are any method with no argument; no attempt
@@ -218,7 +218,7 @@ module RuboCop
218
218
 
219
219
  # @api private
220
220
  def already_on_multiple_lines?(node)
221
- return node.first_line != node.arguments.last.last_line if node.def_type?
221
+ return node.first_line != node.last_argument.last_line if node.def_type?
222
222
 
223
223
  !node.single_line?
224
224
  end
@@ -18,6 +18,7 @@ module RuboCop
18
18
  alternative_styles.each do |alternative|
19
19
  return unexpected_style_detected(alternative) if valid_name?(node, name, alternative)
20
20
  end
21
+ unrecognized_style_detected
21
22
  end
22
23
 
23
24
  def valid_name?(node, name, given_style = style)
@@ -79,7 +79,7 @@ module RuboCop
79
79
  end
80
80
 
81
81
  def aligned_operator?(range, line)
82
- (aligned_identical?(range, line) || aligned_assignment?(range, line))
82
+ aligned_identical?(range, line) || aligned_assignment?(range, line)
83
83
  end
84
84
 
85
85
  def aligned_words?(range, line)
@@ -36,7 +36,7 @@ module RuboCop
36
36
  end
37
37
 
38
38
  def space_required_after?(token)
39
- token.left_curly_brace? && space_required_after_lcurly?
39
+ (token.left_curly_brace? || token.type == :tLAMBEG) && space_required_after_lcurly?
40
40
  end
41
41
 
42
42
  def space_required_after_lcurly?
@@ -48,24 +48,32 @@ module RuboCop
48
48
  MSG = 'Use %<style>s block forwarding.'
49
49
 
50
50
  def self.autocorrect_incompatible_with
51
- [Lint::AmbiguousOperator]
51
+ [Lint::AmbiguousOperator, Style::ArgumentsForwarding]
52
52
  end
53
53
 
54
+ # rubocop:disable Metrics/CyclomaticComplexity
54
55
  def on_def(node)
55
56
  return if node.arguments.empty?
56
57
 
57
- last_argument = node.arguments.last
58
+ last_argument = node.last_argument
58
59
  return if expected_block_forwarding_style?(node, last_argument)
59
60
 
60
- register_offense(last_argument, node)
61
-
61
+ invalid_syntax = false
62
62
  node.each_descendant(:block_pass) do |block_pass_node|
63
63
  next if block_pass_node.children.first&.sym_type? ||
64
64
  last_argument.source != block_pass_node.source
65
65
 
66
+ if block_pass_node.each_ancestor(:block, :numblock).any?
67
+ invalid_syntax = true
68
+ next
69
+ end
70
+
66
71
  register_offense(block_pass_node, node)
67
72
  end
73
+
74
+ register_offense(last_argument, node) unless invalid_syntax
68
75
  end
76
+ # rubocop:enable Metrics/CyclomaticComplexity
69
77
  alias on_defs on_def
70
78
 
71
79
  private
@@ -61,8 +61,7 @@ module RuboCop
61
61
  end
62
62
 
63
63
  def allowed_method_call_on_rhs?(node)
64
- node&.send_type? &&
65
- (node.receiver.nil? || !literal_receiver?(node))
64
+ node&.send_type? && (node.receiver.nil? || !literal_receiver?(node))
66
65
  end
67
66
 
68
67
  # @!method literal_receiver?(node)
@@ -203,7 +203,7 @@ module RuboCop
203
203
 
204
204
  # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
205
205
  def on_defined?(node)
206
- arg = node.arguments.first
206
+ arg = node.first_argument
207
207
  return false unless arg.ivar_type?
208
208
 
209
209
  method_node, method_name = find_definition(node)
@@ -23,6 +23,7 @@ module RuboCop
23
23
  # # bad
24
24
  # open(something)
25
25
  # open("| #{something}")
26
+ # open("| foo")
26
27
  # URI.open(something)
27
28
  #
28
29
  # # good
@@ -32,7 +33,6 @@ module RuboCop
32
33
  #
33
34
  # # good (literal strings)
34
35
  # open("foo.text")
35
- # open("| foo")
36
36
  # URI.open("http://example.com")
37
37
  class Open < Base
38
38
  MSG = 'The use of `%<receiver>sopen` is a serious security risk.'
@@ -40,7 +40,7 @@ module RuboCop
40
40
 
41
41
  # @!method open?(node)
42
42
  def_node_matcher :open?, <<~PATTERN
43
- (send ${nil? (const {nil? cbase} :URI)} :open $!str ...)
43
+ (send ${nil? (const {nil? cbase} :URI)} :open $_ ...)
44
44
  PATTERN
45
45
 
46
46
  def on_send(node)
@@ -168,12 +168,12 @@ module RuboCop
168
168
 
169
169
  def find_corresponding_def_node(node)
170
170
  if access_modifier_with_symbol?(node)
171
- method_name = node.arguments.first.value
171
+ method_name = node.first_argument.value
172
172
  node.parent.each_child_node(:def).find do |child|
173
173
  child.method?(method_name)
174
174
  end
175
175
  else
176
- node.arguments.first
176
+ node.first_argument
177
177
  end
178
178
  end
179
179
 
@@ -166,7 +166,7 @@ module RuboCop
166
166
  *processed_source.ast_with_comments[arg].map(&:text),
167
167
  "#{node.method_name} #{arg.source}"
168
168
  ]
169
- if arg == node.arguments.first
169
+ if arg == node.first_argument
170
170
  lines
171
171
  else
172
172
  indent = ' ' * node.loc.column
@@ -12,7 +12,16 @@ module RuboCop
12
12
  #
13
13
  # This cop also identifies places where `use_args(*args)`/`use_kwargs(**kwargs)` can be
14
14
  # replaced by `use_args(*)`/`use_kwargs(**)`; if desired, this functionality can be disabled
15
- # by setting UseAnonymousForwarding: false.
15
+ # by setting `UseAnonymousForwarding: false`.
16
+ #
17
+ # And this cop has `RedundantRestArgumentNames`, `RedundantKeywordRestArgumentNames`,
18
+ # and `RedundantBlockArgumentNames` options. This configuration is a list of redundant names
19
+ # that are sufficient for anonymizing meaningless naming.
20
+ #
21
+ # Meaningless names that are commonly used can be anonymized by default:
22
+ # e.g., `*args`, `**options`, `&block`, and so on.
23
+ #
24
+ # Names not on this list are likely to be meaningful and are allowed by default.
16
25
  #
17
26
  # @example
18
27
  # # bad
@@ -72,6 +81,38 @@ module RuboCop
72
81
  # bar(**kwargs)
73
82
  # end
74
83
  #
84
+ # @example RedundantRestArgumentNames: ['args', 'arguments'] (default)
85
+ # # bad
86
+ # def foo(*args)
87
+ # bar(*args)
88
+ # end
89
+ #
90
+ # # good
91
+ # def foo(*)
92
+ # bar(*)
93
+ # end
94
+ #
95
+ # @example RedundantKeywordRestArgumentNames: ['kwargs', 'options', 'opts'] (default)
96
+ # # bad
97
+ # def foo(**kwargs)
98
+ # bar(**kwargs)
99
+ # end
100
+ #
101
+ # # good
102
+ # def foo(**)
103
+ # bar(**)
104
+ # end
105
+ #
106
+ # @example RedundantBlockArgumentNames: ['blk', 'block', 'proc'] (default)
107
+ # # bad
108
+ # def foo(&block)
109
+ # bar(&block)
110
+ # end
111
+ #
112
+ # # good
113
+ # def foo(&)
114
+ # bar(&)
115
+ # end
75
116
  class ArgumentsForwarding < Base
76
117
  include RangeHelp
77
118
  extend AutoCorrector
@@ -85,17 +126,21 @@ module RuboCop
85
126
  FORWARDING_MSG = 'Use shorthand syntax `...` for arguments forwarding.'
86
127
  ARGS_MSG = 'Use anonymous positional arguments forwarding (`*`).'
87
128
  KWARGS_MSG = 'Use anonymous keyword arguments forwarding (`**`).'
129
+ BLOCK_MSG = 'Use anonymous block arguments forwarding (`&`).'
130
+
131
+ def self.autocorrect_incompatible_with
132
+ [Naming::BlockForwarding]
133
+ end
88
134
 
89
135
  def on_def(node)
90
136
  return unless node.body
91
137
 
92
- forwardable_args = extract_forwardable_args(node.arguments)
138
+ restarg, kwrestarg, blockarg = extract_forwardable_args(node.arguments)
139
+ forwardable_args = redundant_forwardable_named_args(restarg, kwrestarg, blockarg)
140
+ send_nodes = node.each_descendant(:send).to_a
93
141
 
94
142
  send_classifications = classify_send_nodes(
95
- node,
96
- node.each_descendant(:send).to_a,
97
- non_splat_or_block_pass_lvar_references(node.body),
98
- forwardable_args
143
+ node, send_nodes, non_splat_or_block_pass_lvar_references(node.body), forwardable_args
99
144
  )
100
145
 
101
146
  return if send_classifications.empty?
@@ -115,31 +160,54 @@ module RuboCop
115
160
  [args.find(&:restarg_type?), args.find(&:kwrestarg_type?), args.find(&:blockarg_type?)]
116
161
  end
117
162
 
163
+ def redundant_forwardable_named_args(restarg, kwrestarg, blockarg)
164
+ restarg_node = redundant_named_arg(restarg, 'RedundantRestArgumentNames', '*')
165
+ kwrestarg_node = redundant_named_arg(kwrestarg, 'RedundantKeywordRestArgumentNames', '**')
166
+ blockarg_node = redundant_named_arg(blockarg, 'RedundantBlockArgumentNames', '&')
167
+
168
+ [restarg_node, kwrestarg_node, blockarg_node]
169
+ end
170
+
118
171
  def only_forwards_all?(send_classifications)
119
172
  send_classifications.all? { |_, c, _, _| c == :all }
120
173
  end
121
174
 
175
+ # rubocop:disable Metrics/MethodLength
122
176
  def add_forward_all_offenses(node, send_classifications, forwardable_args)
123
- send_classifications.each do |send_node, _c, forward_rest, _forward_kwrest|
124
- register_forward_all_offense(send_node, send_node, forward_rest)
177
+ _rest_arg, _kwrest_arg, block_arg = *forwardable_args
178
+ registered_block_arg_offense = false
179
+
180
+ send_classifications.each do |send_node, _c, forward_rest, forward_kwrest, forward_block_arg| # rubocop:disable Layout/LineLength
181
+ if !forward_rest && !forward_kwrest && outside_block?(forward_block_arg)
182
+ register_forward_block_arg_offense(!forward_rest, node.arguments, block_arg)
183
+ register_forward_block_arg_offense(!forward_rest, send_node, forward_block_arg)
184
+
185
+ registered_block_arg_offense = true
186
+ break
187
+ else
188
+ register_forward_all_offense(send_node, send_node, forward_rest)
189
+ end
125
190
  end
126
191
 
192
+ return if registered_block_arg_offense
193
+
127
194
  rest_arg, _kwrest_arg, _block_arg = *forwardable_args
128
195
  register_forward_all_offense(node, node.arguments, rest_arg)
129
196
  end
197
+ # rubocop:enable Metrics/MethodLength
130
198
 
131
199
  def add_post_ruby_32_offenses(def_node, send_classifications, forwardable_args)
132
200
  return unless use_anonymous_forwarding?
133
201
 
134
202
  rest_arg, kwrest_arg, _block_arg = *forwardable_args
135
203
 
136
- send_classifications.each do |send_node, _c, forward_rest, forward_kwrest|
137
- if forward_rest
204
+ send_classifications.each do |send_node, _c, forward_rest, forward_kwrest, _forward_block_arg| # rubocop:disable Layout/LineLength
205
+ if outside_block?(forward_rest)
138
206
  register_forward_args_offense(def_node.arguments, rest_arg)
139
207
  register_forward_args_offense(send_node, forward_rest)
140
208
  end
141
209
 
142
- if forward_kwrest
210
+ if outside_block?(forward_kwrest)
143
211
  register_forward_kwargs_offense(!forward_rest, def_node.arguments, kwrest_arg)
144
212
  register_forward_kwargs_offense(!forward_rest, send_node, forward_kwrest)
145
213
  end
@@ -173,10 +241,7 @@ module RuboCop
173
241
 
174
242
  def classification_and_forwards(def_node, send_node, referenced_lvars, forwardable_args)
175
243
  classifier = SendNodeClassifier.new(
176
- def_node,
177
- send_node,
178
- referenced_lvars,
179
- forwardable_args,
244
+ def_node, send_node, referenced_lvars, forwardable_args,
180
245
  target_ruby_version: target_ruby_version,
181
246
  allow_only_rest_arguments: allow_only_rest_arguments?
182
247
  )
@@ -185,7 +250,28 @@ module RuboCop
185
250
 
186
251
  return unless classification
187
252
 
188
- [classification, classifier.forwarded_rest_arg, classifier.forwarded_kwrest_arg]
253
+ [
254
+ classification,
255
+ classifier.forwarded_rest_arg,
256
+ classifier.forwarded_kwrest_arg,
257
+ classifier.forwarded_block_arg
258
+ ]
259
+ end
260
+
261
+ def redundant_named_arg(arg, config_name, keyword)
262
+ return nil unless arg
263
+
264
+ redundant_arg_names = cop_config.fetch(config_name, []).map do |redundant_arg_name|
265
+ "#{keyword}#{redundant_arg_name}"
266
+ end << keyword
267
+
268
+ redundant_arg_names.include?(arg.source) ? arg : nil
269
+ end
270
+
271
+ def outside_block?(node)
272
+ return false unless node
273
+
274
+ node.each_ancestor(:block, :numblock).none?
189
275
  end
190
276
 
191
277
  def register_forward_args_offense(def_arguments_or_send, rest_arg_or_splat)
@@ -204,6 +290,16 @@ module RuboCop
204
290
  end
205
291
  end
206
292
 
293
+ def register_forward_block_arg_offense(add_parens, def_arguments_or_send, block_arg)
294
+ return if target_ruby_version <= 3.0
295
+
296
+ add_offense(block_arg, message: BLOCK_MSG) do |corrector|
297
+ add_parens_if_missing(def_arguments_or_send, corrector) if add_parens
298
+
299
+ corrector.replace(block_arg, '&')
300
+ end
301
+ end
302
+
207
303
  def register_forward_all_offense(def_or_send, send_or_arguments, rest_or_splat)
208
304
  arg_range = arguments_range(def_or_send, rest_or_splat)
209
305
 
@@ -278,7 +374,7 @@ module RuboCop
278
374
  end
279
375
 
280
376
  def classification
281
- return nil unless forwarded_rest_arg || forwarded_kwrest_arg
377
+ return nil unless forwarded_rest_arg || forwarded_kwrest_arg || forwarded_block_arg
282
378
 
283
379
  if can_forward_all?
284
380
  :all
@@ -362,9 +458,16 @@ module RuboCop
362
458
  def no_additional_args?
363
459
  forwardable_count = [@rest_arg, @kwrest_arg, @block_arg].compact.size
364
460
 
461
+ return false if missing_rest_arg_or_kwrest_arg?
462
+
365
463
  @def_node.arguments.size == forwardable_count &&
366
464
  @send_node.arguments.size == forwardable_count
367
465
  end
466
+
467
+ def missing_rest_arg_or_kwrest_arg?
468
+ (@rest_arg_name && !forwarded_rest_arg) ||
469
+ (@kwrest_arg_name && !forwarded_kwrest_arg)
470
+ end
368
471
  end
369
472
  end
370
473
  end