rubocop 1.57.2 → 1.59.0

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 (107) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/config/default.yml +43 -2
  4. data/lib/rubocop/config_obsoletion.rb +11 -8
  5. data/lib/rubocop/cop/bundler/gem_comment.rb +2 -2
  6. data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +2 -2
  7. data/lib/rubocop/cop/internal_affairs/method_name_equal.rb +19 -20
  8. data/lib/rubocop/cop/internal_affairs/node_first_or_last_argument.rb +53 -0
  9. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -2
  10. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  11. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  12. data/lib/rubocop/cop/layout/extra_spacing.rb +4 -10
  13. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +6 -6
  14. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
  15. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  16. data/lib/rubocop/cop/layout/heredoc_indentation.rb +1 -1
  17. data/lib/rubocop/cop/layout/redundant_line_break.rb +2 -1
  18. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +4 -4
  19. data/lib/rubocop/cop/layout/single_line_block_chain.rb +5 -0
  20. data/lib/rubocop/cop/layout/space_around_operators.rb +50 -20
  21. data/lib/rubocop/cop/lint/assignment_in_condition.rb +4 -4
  22. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +2 -2
  23. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +1 -1
  24. data/lib/rubocop/cop/lint/debugger.rb +2 -1
  25. data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -1
  26. data/lib/rubocop/cop/lint/erb_new_arguments.rb +3 -3
  27. data/lib/rubocop/cop/lint/float_comparison.rb +10 -0
  28. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +2 -1
  29. data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +56 -0
  30. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +78 -0
  31. data/lib/rubocop/cop/lint/next_without_accumulator.rb +6 -21
  32. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -5
  33. data/lib/rubocop/cop/lint/number_conversion.rb +9 -4
  34. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +43 -0
  35. data/lib/rubocop/cop/lint/redundant_with_index.rb +2 -2
  36. data/lib/rubocop/cop/lint/redundant_with_object.rb +2 -2
  37. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +3 -4
  38. data/lib/rubocop/cop/lint/self_assignment.rb +38 -0
  39. data/lib/rubocop/cop/lint/symbol_conversion.rb +7 -2
  40. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -1
  41. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +2 -2
  42. data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
  43. data/lib/rubocop/cop/lint/useless_times.rb +1 -1
  44. data/lib/rubocop/cop/lint/void.rb +14 -1
  45. data/lib/rubocop/cop/metrics/abc_size.rb +3 -3
  46. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  47. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +1 -1
  48. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  49. data/lib/rubocop/cop/naming/block_forwarding.rb +2 -2
  50. data/lib/rubocop/cop/naming/constant_name.rb +1 -2
  51. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  52. data/lib/rubocop/cop/style/access_modifier_declarations.rb +2 -2
  53. data/lib/rubocop/cop/style/accessor_grouping.rb +1 -1
  54. data/lib/rubocop/cop/style/arguments_forwarding.rb +68 -6
  55. data/lib/rubocop/cop/style/array_first_last.rb +64 -0
  56. data/lib/rubocop/cop/style/auto_resource_cleanup.rb +21 -14
  57. data/lib/rubocop/cop/style/bisected_attr_accessor.rb +2 -2
  58. data/lib/rubocop/cop/style/case_like_if.rb +4 -4
  59. data/lib/rubocop/cop/style/class_check.rb +1 -0
  60. data/lib/rubocop/cop/style/collection_compact.rb +7 -6
  61. data/lib/rubocop/cop/style/combinable_loops.rb +13 -7
  62. data/lib/rubocop/cop/style/concat_array_literals.rb +1 -0
  63. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
  64. data/lib/rubocop/cop/style/date_time.rb +5 -4
  65. data/lib/rubocop/cop/style/each_with_object.rb +2 -2
  66. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  67. data/lib/rubocop/cop/style/eval_with_location.rb +3 -3
  68. data/lib/rubocop/cop/style/exact_regexp_match.rb +2 -1
  69. data/lib/rubocop/cop/style/explicit_block_argument.rb +2 -2
  70. data/lib/rubocop/cop/style/hash_each_methods.rb +83 -10
  71. data/lib/rubocop/cop/style/hash_except.rb +2 -1
  72. data/lib/rubocop/cop/style/inverse_methods.rb +6 -5
  73. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +3 -2
  74. data/lib/rubocop/cop/style/map_to_hash.rb +10 -4
  75. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +1 -0
  76. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +1 -1
  77. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +20 -0
  78. data/lib/rubocop/cop/style/method_def_parentheses.rb +1 -1
  79. data/lib/rubocop/cop/style/missing_respond_to_missing.rb +2 -2
  80. data/lib/rubocop/cop/style/next.rb +1 -1
  81. data/lib/rubocop/cop/style/operator_method_call.rb +2 -2
  82. data/lib/rubocop/cop/style/redundant_argument.rb +3 -2
  83. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +3 -3
  84. data/lib/rubocop/cop/style/redundant_fetch_block.rb +3 -3
  85. data/lib/rubocop/cop/style/redundant_line_continuation.rb +2 -0
  86. data/lib/rubocop/cop/style/redundant_parentheses.rb +15 -8
  87. data/lib/rubocop/cop/style/redundant_return.rb +1 -1
  88. data/lib/rubocop/cop/style/redundant_self.rb +17 -2
  89. data/lib/rubocop/cop/style/redundant_sort.rb +9 -8
  90. data/lib/rubocop/cop/style/redundant_sort_by.rb +2 -2
  91. data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -1
  92. data/lib/rubocop/cop/style/sample.rb +2 -1
  93. data/lib/rubocop/cop/style/select_by_regexp.rb +7 -6
  94. data/lib/rubocop/cop/style/self_assignment.rb +1 -1
  95. data/lib/rubocop/cop/style/semicolon.rb +8 -0
  96. data/lib/rubocop/cop/style/single_argument_dig.rb +5 -2
  97. data/lib/rubocop/cop/style/slicing_with_range.rb +1 -1
  98. data/lib/rubocop/cop/style/string_chars.rb +1 -0
  99. data/lib/rubocop/cop/style/strip.rb +7 -4
  100. data/lib/rubocop/cop/style/super_with_args_parentheses.rb +35 -0
  101. data/lib/rubocop/cop/style/unpack_first.rb +11 -14
  102. data/lib/rubocop/formatter/html_formatter.rb +1 -2
  103. data/lib/rubocop/result_cache.rb +0 -1
  104. data/lib/rubocop/runner.rb +1 -1
  105. data/lib/rubocop/version.rb +1 -1
  106. data/lib/rubocop.rb +4 -0
  107. metadata +14 -9
@@ -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
@@ -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)
@@ -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
@@ -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,13 +48,13 @@ 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
54
  def on_def(node)
55
55
  return if node.arguments.empty?
56
56
 
57
- last_argument = node.arguments.last
57
+ last_argument = node.last_argument
58
58
  return if expected_block_forwarding_style?(node, last_argument)
59
59
 
60
60
  register_offense(last_argument, node)
@@ -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)
@@ -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
@@ -86,16 +127,19 @@ module RuboCop
86
127
  ARGS_MSG = 'Use anonymous positional arguments forwarding (`*`).'
87
128
  KWARGS_MSG = 'Use anonymous keyword arguments forwarding (`**`).'
88
129
 
130
+ def self.autocorrect_incompatible_with
131
+ [Naming::BlockForwarding]
132
+ end
133
+
89
134
  def on_def(node)
90
135
  return unless node.body
91
136
 
92
- forwardable_args = extract_forwardable_args(node.arguments)
137
+ restarg, kwrestarg, blockarg = extract_forwardable_args(node.arguments)
138
+ forwardable_args = redundant_forwardable_named_args(restarg, kwrestarg, blockarg)
139
+ send_nodes = node.each_descendant(:send).to_a
93
140
 
94
141
  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
142
+ node, send_nodes, non_splat_or_block_pass_lvar_references(node.body), forwardable_args
99
143
  )
100
144
 
101
145
  return if send_classifications.empty?
@@ -115,6 +159,14 @@ module RuboCop
115
159
  [args.find(&:restarg_type?), args.find(&:kwrestarg_type?), args.find(&:blockarg_type?)]
116
160
  end
117
161
 
162
+ def redundant_forwardable_named_args(restarg, kwrestarg, blockarg)
163
+ restarg_node = redundant_named_arg(restarg, 'RedundantRestArgumentNames', '*')
164
+ kwrestarg_node = redundant_named_arg(kwrestarg, 'RedundantKeywordRestArgumentNames', '**')
165
+ blockarg_node = redundant_named_arg(blockarg, 'RedundantBlockArgumentNames', '&')
166
+
167
+ [restarg_node, kwrestarg_node, blockarg_node]
168
+ end
169
+
118
170
  def only_forwards_all?(send_classifications)
119
171
  send_classifications.all? { |_, c, _, _| c == :all }
120
172
  end
@@ -188,6 +240,16 @@ module RuboCop
188
240
  [classification, classifier.forwarded_rest_arg, classifier.forwarded_kwrest_arg]
189
241
  end
190
242
 
243
+ def redundant_named_arg(arg, config_name, keyword)
244
+ return nil unless arg
245
+
246
+ redundant_arg_names = cop_config.fetch(config_name, []).map do |redundant_arg_name|
247
+ "#{keyword}#{redundant_arg_name}"
248
+ end << keyword
249
+
250
+ redundant_arg_names.include?(arg.source) ? arg : nil
251
+ end
252
+
191
253
  def register_forward_args_offense(def_arguments_or_send, rest_arg_or_splat)
192
254
  add_offense(rest_arg_or_splat, message: ARGS_MSG) do |corrector|
193
255
  add_parens_if_missing(def_arguments_or_send, corrector)
@@ -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?)
@@ -53,7 +53,7 @@ module RuboCop
53
53
 
54
54
  # @!method reject_method_with_block_pass?(node)
55
55
  def_node_matcher :reject_method_with_block_pass?, <<~PATTERN
56
- (send !nil? {:reject :delete_if :reject!}
56
+ (call !nil? {:reject :delete_if :reject!}
57
57
  (block_pass
58
58
  (sym :nil?)))
59
59
  PATTERN
@@ -61,21 +61,21 @@ module RuboCop
61
61
  # @!method reject_method?(node)
62
62
  def_node_matcher :reject_method?, <<~PATTERN
63
63
  (block
64
- (send
64
+ (call
65
65
  !nil? {:reject :delete_if :reject!})
66
66
  $(args ...)
67
- (send
67
+ (call
68
68
  $(lvar _) :nil?))
69
69
  PATTERN
70
70
 
71
71
  # @!method select_method?(node)
72
72
  def_node_matcher :select_method?, <<~PATTERN
73
73
  (block
74
- (send
74
+ (call
75
75
  !nil? {:select :select!})
76
76
  $(args ...)
77
- (send
78
- (send
77
+ (call
78
+ (call
79
79
  $(lvar _) :nil?) :!))
80
80
  PATTERN
81
81
 
@@ -91,6 +91,7 @@ module RuboCop
91
91
 
92
92
  add_offense(range, message: message) { |corrector| corrector.replace(range, good) }
93
93
  end
94
+ alias on_csend on_send
94
95
 
95
96
  private
96
97
 
@@ -59,8 +59,6 @@ module RuboCop
59
59
  class CombinableLoops < Base
60
60
  extend AutoCorrector
61
61
 
62
- include RangeHelp
63
-
64
62
  MSG = 'Combine this loop with the previous loop.'
65
63
 
66
64
  def on_block(node)
@@ -105,11 +103,19 @@ module RuboCop
105
103
  end
106
104
 
107
105
  def combine_with_left_sibling(corrector, node)
108
- corrector.replace(
109
- node.left_sibling.body,
110
- "#{node.left_sibling.body.source}\n#{node.body.source}"
111
- )
112
- corrector.remove(range_with_surrounding_space(range: node.source_range, side: :left))
106
+ corrector.remove(node.left_sibling.body.source_range.end.join(node.left_sibling.loc.end))
107
+ corrector.remove(node.source_range.begin.join(node.body.source_range.begin))
108
+
109
+ correct_end_of_block(corrector, node)
110
+ end
111
+
112
+ def correct_end_of_block(corrector, node)
113
+ return unless node.left_sibling.respond_to?(:braces?)
114
+ return if node.right_sibling&.block_type? || node.right_sibling&.numblock_type?
115
+
116
+ end_of_block = node.left_sibling.braces? ? '}' : ' end'
117
+ corrector.remove(node.loc.end)
118
+ corrector.insert_before(node.source_range.end, end_of_block)
113
119
  end
114
120
  end
115
121
  end