rubocop 1.56.4 → 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 (163) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +4 -4
  4. data/config/default.yml +57 -3
  5. data/lib/rubocop/cli/command/auto_generate_config.rb +10 -5
  6. data/lib/rubocop/cli.rb +1 -1
  7. data/lib/rubocop/config.rb +0 -2
  8. data/lib/rubocop/config_loader.rb +0 -1
  9. data/lib/rubocop/config_obsoletion.rb +11 -8
  10. data/lib/rubocop/config_validator.rb +0 -2
  11. data/lib/rubocop/cop/base.rb +6 -0
  12. data/lib/rubocop/cop/bundler/gem_comment.rb +2 -2
  13. data/lib/rubocop/cop/exclude_limit.rb +1 -1
  14. data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +2 -2
  15. data/lib/rubocop/cop/internal_affairs/method_name_equal.rb +19 -20
  16. data/lib/rubocop/cop/internal_affairs/node_first_or_last_argument.rb +53 -0
  17. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -2
  18. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  19. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  20. data/lib/rubocop/cop/layout/end_alignment.rb +12 -2
  21. data/lib/rubocop/cop/layout/extra_spacing.rb +4 -10
  22. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +22 -7
  23. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
  24. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  25. data/lib/rubocop/cop/layout/heredoc_indentation.rb +1 -1
  26. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  27. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +1 -1
  28. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +16 -1
  29. data/lib/rubocop/cop/layout/redundant_line_break.rb +2 -1
  30. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +4 -4
  31. data/lib/rubocop/cop/layout/single_line_block_chain.rb +5 -0
  32. data/lib/rubocop/cop/layout/space_around_operators.rb +50 -20
  33. data/lib/rubocop/cop/layout/space_inside_parens.rb +1 -1
  34. data/lib/rubocop/cop/lint/assignment_in_condition.rb +4 -4
  35. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +2 -2
  36. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +1 -1
  37. data/lib/rubocop/cop/lint/debugger.rb +11 -1
  38. data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -1
  39. data/lib/rubocop/cop/lint/empty_block.rb +1 -1
  40. data/lib/rubocop/cop/lint/erb_new_arguments.rb +3 -3
  41. data/lib/rubocop/cop/lint/float_comparison.rb +10 -0
  42. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +2 -1
  43. data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +56 -0
  44. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +85 -0
  45. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
  46. data/lib/rubocop/cop/lint/mixed_case_range.rb +1 -1
  47. data/lib/rubocop/cop/lint/next_without_accumulator.rb +6 -21
  48. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +0 -1
  49. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -5
  50. data/lib/rubocop/cop/lint/number_conversion.rb +9 -4
  51. data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -0
  52. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +63 -4
  53. data/lib/rubocop/cop/lint/redundant_with_index.rb +2 -2
  54. data/lib/rubocop/cop/lint/redundant_with_object.rb +2 -2
  55. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +3 -4
  56. data/lib/rubocop/cop/lint/self_assignment.rb +38 -0
  57. data/lib/rubocop/cop/lint/shadowed_argument.rb +1 -0
  58. data/lib/rubocop/cop/lint/symbol_conversion.rb +7 -2
  59. data/lib/rubocop/cop/lint/syntax.rb +6 -3
  60. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -1
  61. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +2 -2
  62. data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
  63. data/lib/rubocop/cop/lint/useless_times.rb +1 -1
  64. data/lib/rubocop/cop/lint/void.rb +43 -12
  65. data/lib/rubocop/cop/metrics/abc_size.rb +3 -3
  66. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  67. data/lib/rubocop/cop/metrics/class_length.rb +8 -3
  68. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +2 -2
  69. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  70. data/lib/rubocop/cop/mixin/comments_help.rb +16 -12
  71. data/lib/rubocop/cop/mixin/configurable_formatting.rb +1 -0
  72. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +14 -11
  73. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +1 -1
  74. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  75. data/lib/rubocop/cop/naming/block_forwarding.rb +12 -4
  76. data/lib/rubocop/cop/naming/constant_name.rb +1 -2
  77. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  78. data/lib/rubocop/cop/security/open.rb +2 -2
  79. data/lib/rubocop/cop/style/access_modifier_declarations.rb +2 -2
  80. data/lib/rubocop/cop/style/accessor_grouping.rb +1 -1
  81. data/lib/rubocop/cop/style/arguments_forwarding.rb +120 -17
  82. data/lib/rubocop/cop/style/array_first_last.rb +64 -0
  83. data/lib/rubocop/cop/style/auto_resource_cleanup.rb +21 -14
  84. data/lib/rubocop/cop/style/bisected_attr_accessor.rb +2 -2
  85. data/lib/rubocop/cop/style/case_like_if.rb +4 -4
  86. data/lib/rubocop/cop/style/class_check.rb +1 -0
  87. data/lib/rubocop/cop/style/class_equality_comparison.rb +5 -0
  88. data/lib/rubocop/cop/style/collection_compact.rb +18 -8
  89. data/lib/rubocop/cop/style/combinable_loops.rb +13 -7
  90. data/lib/rubocop/cop/style/concat_array_literals.rb +1 -0
  91. data/lib/rubocop/cop/style/conditional_assignment.rb +2 -2
  92. data/lib/rubocop/cop/style/date_time.rb +5 -4
  93. data/lib/rubocop/cop/style/each_for_simple_loop.rb +7 -7
  94. data/lib/rubocop/cop/style/each_with_object.rb +2 -2
  95. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  96. data/lib/rubocop/cop/style/eval_with_location.rb +3 -14
  97. data/lib/rubocop/cop/style/exact_regexp_match.rb +2 -1
  98. data/lib/rubocop/cop/style/explicit_block_argument.rb +2 -2
  99. data/lib/rubocop/cop/style/format_string.rb +24 -3
  100. data/lib/rubocop/cop/style/guard_clause.rb +26 -0
  101. data/lib/rubocop/cop/style/hash_each_methods.rb +83 -10
  102. data/lib/rubocop/cop/style/hash_except.rb +2 -1
  103. data/lib/rubocop/cop/style/identical_conditional_branches.rb +28 -3
  104. data/lib/rubocop/cop/style/inverse_methods.rb +6 -5
  105. data/lib/rubocop/cop/style/invertible_unless_condition.rb +39 -2
  106. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +3 -2
  107. data/lib/rubocop/cop/style/map_to_hash.rb +17 -7
  108. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +14 -5
  109. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +2 -4
  110. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +20 -0
  111. data/lib/rubocop/cop/style/method_def_parentheses.rb +1 -1
  112. data/lib/rubocop/cop/style/missing_respond_to_missing.rb +2 -2
  113. data/lib/rubocop/cop/style/multiline_block_chain.rb +1 -1
  114. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -3
  115. data/lib/rubocop/cop/style/nested_ternary_operator.rb +3 -11
  116. data/lib/rubocop/cop/style/next.rb +1 -1
  117. data/lib/rubocop/cop/style/numeric_literal_prefix.rb +1 -1
  118. data/lib/rubocop/cop/style/operator_method_call.rb +2 -2
  119. data/lib/rubocop/cop/style/parallel_assignment.rb +2 -2
  120. data/lib/rubocop/cop/style/parentheses_around_condition.rb +8 -0
  121. data/lib/rubocop/cop/style/redundant_argument.rb +3 -2
  122. data/lib/rubocop/cop/style/redundant_begin.rb +9 -1
  123. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +93 -5
  124. data/lib/rubocop/cop/style/redundant_each.rb +7 -4
  125. data/lib/rubocop/cop/style/redundant_exception.rb +32 -12
  126. data/lib/rubocop/cop/style/redundant_fetch_block.rb +3 -3
  127. data/lib/rubocop/cop/style/redundant_filter_chain.rb +22 -5
  128. data/lib/rubocop/cop/style/redundant_line_continuation.rb +10 -1
  129. data/lib/rubocop/cop/style/redundant_parentheses.rb +68 -21
  130. data/lib/rubocop/cop/style/redundant_return.rb +1 -1
  131. data/lib/rubocop/cop/style/redundant_self.rb +17 -2
  132. data/lib/rubocop/cop/style/redundant_sort.rb +9 -8
  133. data/lib/rubocop/cop/style/redundant_sort_by.rb +2 -2
  134. data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -1
  135. data/lib/rubocop/cop/style/sample.rb +2 -1
  136. data/lib/rubocop/cop/style/select_by_regexp.rb +7 -6
  137. data/lib/rubocop/cop/style/self_assignment.rb +1 -1
  138. data/lib/rubocop/cop/style/semicolon.rb +8 -0
  139. data/lib/rubocop/cop/style/single_argument_dig.rb +7 -3
  140. data/lib/rubocop/cop/style/single_line_do_end_block.rb +67 -0
  141. data/lib/rubocop/cop/style/slicing_with_range.rb +76 -10
  142. data/lib/rubocop/cop/style/string_chars.rb +1 -0
  143. data/lib/rubocop/cop/style/strip.rb +7 -4
  144. data/lib/rubocop/cop/style/super_with_args_parentheses.rb +35 -0
  145. data/lib/rubocop/cop/style/symbol_proc.rb +36 -0
  146. data/lib/rubocop/cop/style/unpack_first.rb +11 -14
  147. data/lib/rubocop/cops_documentation_generator.rb +11 -1
  148. data/lib/rubocop/ext/regexp_node.rb +9 -4
  149. data/lib/rubocop/formatter/disabled_config_formatter.rb +17 -6
  150. data/lib/rubocop/formatter/html_formatter.rb +5 -4
  151. data/lib/rubocop/formatter/json_formatter.rb +0 -1
  152. data/lib/rubocop/formatter.rb +1 -1
  153. data/lib/rubocop/lsp/routes.rb +1 -1
  154. data/lib/rubocop/options.rb +0 -8
  155. data/lib/rubocop/result_cache.rb +0 -1
  156. data/lib/rubocop/rspec/shared_contexts.rb +6 -0
  157. data/lib/rubocop/rspec/support.rb +1 -0
  158. data/lib/rubocop/runner.rb +1 -1
  159. data/lib/rubocop/server/cache.rb +1 -1
  160. data/lib/rubocop/version.rb +1 -1
  161. data/lib/rubocop.rb +5 -0
  162. metadata +16 -24
  163. /data/lib/rubocop/formatter/{git_hub_actions_formatter.rb → github_actions_formatter.rb} +0 -0
@@ -10,7 +10,7 @@ module RuboCop
10
10
  include Util
11
11
 
12
12
  FOLDABLE_TYPES = %i[array hash heredoc send csend].freeze
13
- CLASSLIKE_TYPES = %i[class module sclass].freeze
13
+ CLASSLIKE_TYPES = %i[class module].freeze
14
14
  private_constant :FOLDABLE_TYPES, :CLASSLIKE_TYPES
15
15
 
16
16
  def initialize(node, processed_source, count_comments: false, foldable_types: [])
@@ -145,7 +145,7 @@ module RuboCop
145
145
 
146
146
  def extract_body(node)
147
147
  case node.type
148
- when :class, :module, :block, :numblock, :def, :defs
148
+ when :class, :module, :sclass, :block, :numblock, :def, :defs
149
149
  node.body
150
150
  when :casgn
151
151
  _scope, _name, value = *node
@@ -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
@@ -62,25 +62,29 @@ module RuboCop
62
62
  # Returns the end line of a node, which might be a comment and not part of the AST
63
63
  # End line is considered either the line at which another node starts, or
64
64
  # the line at which the parent node ends.
65
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Lint/DuplicateBranch
65
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
66
66
  def find_end_line(node)
67
- if node.if_type? && node.else?
68
- node.loc.else.line
69
- elsif node.if_type? && node.ternary?
70
- node.else_branch.loc.line
71
- elsif node.if_type? && node.elsif?
72
- node.each_ancestor(:if).find(&:if?).loc.end.line
67
+ if node.if_type?
68
+ if node.else?
69
+ node.loc.else.line
70
+ elsif node.ternary?
71
+ node.else_branch.loc.line
72
+ elsif node.elsif?
73
+ node.each_ancestor(:if).find(&:if?).loc.end.line
74
+ end
73
75
  elsif node.block_type? || node.numblock_type?
74
76
  node.loc.end.line
75
77
  elsif (next_sibling = node.right_sibling) && next_sibling.is_a?(AST::Node)
76
78
  next_sibling.loc.line
77
79
  elsif (parent = node.parent)
78
- parent.loc.respond_to?(:end) && parent.loc.end ? parent.loc.end.line : parent.loc.line
79
- else
80
- node.loc.end.line
81
- end
80
+ if parent.loc.respond_to?(:end) && parent.loc.end
81
+ parent.loc.end.line
82
+ else
83
+ parent.loc.line
84
+ end
85
+ end || node.loc.end.line
82
86
  end
83
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Lint/DuplicateBranch
87
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
84
88
  end
85
89
  end
86
90
  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)
@@ -48,18 +48,21 @@ module RuboCop
48
48
 
49
49
  def register_offense(node, message, replacement) # rubocop:disable Metrics/AbcSize
50
50
  add_offense(node.value, message: message) do |corrector|
51
- if (def_node = def_node_that_require_parentheses(node))
52
- last_argument = def_node.last_argument
53
- if last_argument.nil? || !last_argument.hash_type?
54
- next corrector.replace(node, replacement)
55
- end
56
-
57
- white_spaces = range_between(def_node.selector.end_pos,
58
- def_node.first_argument.source_range.begin_pos)
59
- corrector.replace(white_spaces, '(')
60
- corrector.insert_after(last_argument, ')') if node == last_argument.pairs.last
61
- end
62
51
  corrector.replace(node, replacement)
52
+
53
+ next unless (def_node = def_node_that_require_parentheses(node))
54
+
55
+ last_argument = def_node.last_argument
56
+ if last_argument.nil? || !last_argument.hash_type?
57
+ next corrector.replace(node, replacement)
58
+ end
59
+
60
+ white_spaces = range_between(def_node.selector.end_pos,
61
+ def_node.first_argument.source_range.begin_pos)
62
+ next if node.parent.braces?
63
+
64
+ corrector.replace(white_spaces, '(')
65
+ corrector.insert_after(last_argument, ')') if node == last_argument.pairs.last
63
66
  end
64
67
  end
65
68
 
@@ -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
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Identifies usages of `arr[0]` and `arr[-1]` and suggests to change
7
+ # them to use `arr.first` and `arr.last` instead.
8
+ #
9
+ # The cop is disabled by default due to safety concerns.
10
+ #
11
+ # @safety
12
+ # This cop is unsafe because `[0]` or `[-1]` can be called on a Hash,
13
+ # which returns a value for `0` or `-1` key, but changing these to use
14
+ # `.first` or `.last` will return first/last tuple instead. Also, String
15
+ # does not implement `first`/`last` methods.
16
+ #
17
+ # @example
18
+ # # bad
19
+ # arr[0]
20
+ # arr[-1]
21
+ #
22
+ # # good
23
+ # arr.first
24
+ # arr.last
25
+ # arr[0] = 2
26
+ # arr[0][-2]
27
+ #
28
+ class ArrayFirstLast < Base
29
+ extend AutoCorrector
30
+
31
+ MSG = 'Use `%<preferred>s`.'
32
+ RESTRICT_ON_SEND = %i[[]].freeze
33
+
34
+ # rubocop:disable Metrics/AbcSize
35
+ def on_send(node)
36
+ return unless node.arguments.size == 1 && node.first_argument.int_type?
37
+
38
+ value = node.first_argument.value
39
+ return unless [0, -1].include?(value)
40
+
41
+ node = innermost_braces_node(node)
42
+ return if node.parent && brace_method?(node.parent)
43
+
44
+ preferred = (value.zero? ? 'first' : 'last')
45
+ add_offense(node.loc.selector, message: format(MSG, preferred: preferred)) do |corrector|
46
+ corrector.replace(node.loc.selector, ".#{preferred}")
47
+ end
48
+ end
49
+ # rubocop:enable Metrics/AbcSize
50
+
51
+ private
52
+
53
+ def innermost_braces_node(node)
54
+ node = node.receiver while node.receiver.send_type? && node.receiver.method?(:[])
55
+ node
56
+ end
57
+
58
+ def brace_method?(node)
59
+ node.send_type? && (node.method?(:[]) || node.method?(:[]=))
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -16,31 +16,38 @@ module RuboCop
16
16
  # File.open('file') do |f|
17
17
  # # ...
18
18
  # end
19
+ #
20
+ # # bad
21
+ # f = Tempfile.open('temp')
22
+ #
23
+ # # good
24
+ # Tempfile.open('temp') do |f|
25
+ # # ...
26
+ # end
19
27
  class AutoResourceCleanup < Base
20
- MSG = 'Use the block version of `%<class>s.%<method>s`.'
21
-
22
- TARGET_METHODS = { File: :open }.freeze
28
+ MSG = 'Use the block version of `%<current>s`.'
29
+ RESTRICT_ON_SEND = %i[open].freeze
23
30
 
24
- RESTRICT_ON_SEND = TARGET_METHODS.values.freeze
31
+ # @!method file_open_method?(node)
32
+ def_node_matcher :file_open_method?, <<~PATTERN
33
+ (send (const {nil? cbase} {:File :Tempfile}) :open ...)
34
+ PATTERN
25
35
 
26
36
  def on_send(node)
27
- TARGET_METHODS.each do |target_class, target_method|
28
- next if node.method_name != target_method
37
+ return if !file_open_method?(node) || cleanup?(node)
29
38
 
30
- target_receiver = s(:const, nil, target_class)
31
- next if node.receiver != target_receiver
39
+ current = node.receiver.source_range.begin.join(node.selector.end).source
32
40
 
33
- next if cleanup?(node)
34
-
35
- add_offense(node, message: format(MSG, class: target_class, method: target_method))
36
- end
41
+ add_offense(node, message: format(MSG, current: current))
37
42
  end
38
43
 
39
44
  private
40
45
 
41
46
  def cleanup?(node)
42
- parent = node.parent
43
- node.block_argument? || (parent && (parent.block_type? || !parent.lvasgn_type?))
47
+ return true if node.block_argument?
48
+ return false unless (parent = node.parent)
49
+
50
+ parent.block_type? || !parent.lvasgn_type?
44
51
  end
45
52
  end
46
53
  end
@@ -33,7 +33,7 @@ module RuboCop
33
33
  def on_class(class_node)
34
34
  @macros_to_rewrite[class_node] = Set.new
35
35
 
36
- find_macros(class_node.body).each do |_visibility, macros|
36
+ find_macros(class_node.body).each_value do |macros|
37
37
  bisected = find_bisection(macros)
38
38
  next unless bisected.any?
39
39
 
@@ -74,7 +74,7 @@ module RuboCop
74
74
  def find_macros(class_def)
75
75
  # Find all the macros (`attr_reader`, `attr_writer`, etc.) in the class body
76
76
  # and turn them into `Macro` objects so that they can be processed.
77
- return [] if !class_def || class_def.def_type?
77
+ return {} if !class_def || class_def.def_type?
78
78
 
79
79
  send_nodes =
80
80
  if class_def.send_type?
@@ -125,7 +125,7 @@ module RuboCop
125
125
  when :==, :eql?, :equal?
126
126
  find_target_in_equality_node(node)
127
127
  when :===
128
- node.arguments.first
128
+ node.first_argument
129
129
  when :include?, :cover?
130
130
  find_target_in_include_or_cover_node(node)
131
131
  when :match, :match?, :=~
@@ -134,7 +134,7 @@ module RuboCop
134
134
  end
135
135
 
136
136
  def find_target_in_equality_node(node)
137
- argument = node.arguments.first
137
+ argument = node.first_argument
138
138
  receiver = node.receiver
139
139
  return unless argument && receiver
140
140
 
@@ -152,7 +152,7 @@ module RuboCop
152
152
  end
153
153
 
154
154
  def find_target_in_match_node(node)
155
- argument = node.arguments.first
155
+ argument = node.first_argument
156
156
  receiver = node.receiver
157
157
  return unless receiver
158
158
 
@@ -185,7 +185,7 @@ module RuboCop
185
185
  def condition_from_send_node(node, target)
186
186
  case node.method_name
187
187
  when :is_a?
188
- node.arguments.first if node.receiver == target
188
+ node.first_argument if node.receiver == target
189
189
  when :==, :eql?, :equal?
190
190
  condition_from_equality_node(node, target)
191
191
  when :=~, :match, :match?
@@ -40,6 +40,7 @@ module RuboCop
40
40
  corrector.replace(node.loc.selector, replacement)
41
41
  end
42
42
  end
43
+ alias on_csend on_send
43
44
 
44
45
  def message(node)
45
46
  if node.method?(:is_a?)
@@ -8,6 +8,11 @@ module RuboCop
8
8
  # `==`, `equal?`, and `eql?` custom method definitions are allowed by default.
9
9
  # These are customizable with `AllowedMethods` option.
10
10
  #
11
+ # @safety
12
+ # This cop's autocorrection is unsafe because there is no guarantee that
13
+ # the constant `Foo` exists when autocorrecting `var.class.name == 'Foo'` to
14
+ # `var.instance_of?(Foo)`.
15
+ #
11
16
  # @example
12
17
  # # bad
13
18
  # var.class == Date