rubocop 1.57.2 → 1.60.1

Sign up to get free protection for your applications and to get access to all the features.
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