rubocop 1.75.8 → 1.79.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 (109) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -16
  3. data/config/default.yml +107 -26
  4. data/config/obsoletion.yml +6 -3
  5. data/lib/rubocop/cli.rb +12 -1
  6. data/lib/rubocop/config_loader.rb +1 -38
  7. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -1
  8. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +5 -2
  9. data/lib/rubocop/cop/gemspec/attribute_assignment.rb +91 -0
  10. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +0 -22
  11. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  12. data/lib/rubocop/cop/gemspec/require_mfa.rb +15 -1
  13. data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
  14. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +4 -4
  15. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +1 -0
  16. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +3 -2
  17. data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +1 -1
  18. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +1 -1
  19. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +99 -0
  20. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -1
  21. data/lib/rubocop/cop/layout/line_length.rb +26 -5
  22. data/lib/rubocop/cop/layout/space_around_keyword.rb +6 -1
  23. data/lib/rubocop/cop/layout/space_around_operators.rb +8 -0
  24. data/lib/rubocop/cop/layout/space_before_brackets.rb +2 -9
  25. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +7 -2
  26. data/lib/rubocop/cop/lint/ambiguous_range.rb +5 -0
  27. data/lib/rubocop/cop/lint/duplicate_methods.rb +25 -4
  28. data/lib/rubocop/cop/lint/empty_interpolation.rb +3 -1
  29. data/lib/rubocop/cop/lint/float_comparison.rb +4 -4
  30. data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
  31. data/lib/rubocop/cop/lint/literal_as_condition.rb +34 -28
  32. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +1 -0
  33. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
  34. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +101 -2
  35. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +4 -4
  36. data/lib/rubocop/cop/lint/require_range_parentheses.rb +1 -1
  37. data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
  38. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +4 -4
  39. data/lib/rubocop/cop/lint/self_assignment.rb +25 -0
  40. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -0
  41. data/lib/rubocop/cop/lint/useless_access_modifier.rb +29 -4
  42. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +90 -0
  43. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +1 -0
  44. data/lib/rubocop/cop/lint/useless_or.rb +98 -0
  45. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +3 -3
  46. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +121 -0
  47. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  48. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
  49. data/lib/rubocop/cop/mixin/gemspec_help.rb +22 -0
  50. data/lib/rubocop/cop/mixin/line_length_help.rb +24 -8
  51. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  52. data/lib/rubocop/cop/naming/file_name.rb +2 -2
  53. data/lib/rubocop/cop/naming/method_name.rb +127 -13
  54. data/lib/rubocop/cop/naming/predicate_method.rb +306 -0
  55. data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +4 -4
  56. data/lib/rubocop/cop/security/eval.rb +2 -1
  57. data/lib/rubocop/cop/security/open.rb +1 -0
  58. data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -1
  59. data/lib/rubocop/cop/style/accessor_grouping.rb +13 -1
  60. data/lib/rubocop/cop/style/arguments_forwarding.rb +11 -17
  61. data/lib/rubocop/cop/style/array_intersect.rb +53 -23
  62. data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
  63. data/lib/rubocop/cop/style/case_like_if.rb +1 -1
  64. data/lib/rubocop/cop/style/collection_querying.rb +167 -0
  65. data/lib/rubocop/cop/style/conditional_assignment.rb +4 -2
  66. data/lib/rubocop/cop/style/dig_chain.rb +1 -1
  67. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
  68. data/lib/rubocop/cop/style/exponential_notation.rb +3 -2
  69. data/lib/rubocop/cop/style/fetch_env_var.rb +32 -6
  70. data/lib/rubocop/cop/style/hash_conversion.rb +16 -8
  71. data/lib/rubocop/cop/style/if_unless_modifier.rb +13 -6
  72. data/lib/rubocop/cop/style/inverse_methods.rb +1 -1
  73. data/lib/rubocop/cop/style/it_assignment.rb +69 -12
  74. data/lib/rubocop/cop/style/it_block_parameter.rb +36 -15
  75. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +4 -6
  76. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +16 -0
  77. data/lib/rubocop/cop/style/min_max_comparison.rb +13 -5
  78. data/lib/rubocop/cop/style/parallel_assignment.rb +32 -20
  79. data/lib/rubocop/cop/style/redundant_array_flatten.rb +50 -0
  80. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -9
  81. data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
  82. data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
  83. data/lib/rubocop/cop/style/redundant_line_continuation.rb +1 -1
  84. data/lib/rubocop/cop/style/redundant_parentheses.rb +35 -5
  85. data/lib/rubocop/cop/style/redundant_self.rb +8 -5
  86. data/lib/rubocop/cop/style/safe_navigation.rb +24 -11
  87. data/lib/rubocop/cop/style/single_line_methods.rb +7 -4
  88. data/lib/rubocop/cop/style/sole_nested_conditional.rb +32 -2
  89. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -1
  90. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  91. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  92. data/lib/rubocop/cop/variable_force.rb +18 -7
  93. data/lib/rubocop/cops_documentation_generator.rb +1 -0
  94. data/lib/rubocop/formatter/fuubar_style_formatter.rb +1 -1
  95. data/lib/rubocop/formatter/markdown_formatter.rb +1 -0
  96. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  97. data/lib/rubocop/formatter/pacman_formatter.rb +1 -0
  98. data/lib/rubocop/lsp/diagnostic.rb +4 -4
  99. data/lib/rubocop/lsp/routes.rb +4 -4
  100. data/lib/rubocop/pending_cops_reporter.rb +56 -0
  101. data/lib/rubocop/rspec/expect_offense.rb +9 -3
  102. data/lib/rubocop/server/cache.rb +4 -2
  103. data/lib/rubocop/server/client_command/base.rb +10 -0
  104. data/lib/rubocop/server/client_command/exec.rb +2 -1
  105. data/lib/rubocop/server/client_command/start.rb +11 -1
  106. data/lib/rubocop/version.rb +1 -1
  107. data/lib/rubocop.rb +11 -1
  108. data/lib/ruby_lsp/rubocop/addon.rb +2 -2
  109. metadata +21 -8
@@ -11,39 +11,43 @@ module RuboCop
11
11
  # @example
12
12
  # # bad
13
13
  # foo.object_id == bar.object_id
14
+ # foo.object_id != baz.object_id
14
15
  #
15
16
  # # good
16
17
  # foo.equal?(bar)
18
+ # !foo.equal?(baz)
17
19
  #
18
20
  class IdentityComparison < Base
19
21
  extend AutoCorrector
20
22
 
21
- MSG = 'Use `equal?` instead `==` when comparing `object_id`.'
22
- RESTRICT_ON_SEND = %i[==].freeze
23
+ MSG = 'Use `%<bang>sequal?` instead of `%<comparison_method>s` when comparing `object_id`.'
24
+ RESTRICT_ON_SEND = %i[== !=].freeze
25
+
26
+ # @!method object_id_comparison(node)
27
+ def_node_matcher :object_id_comparison, <<~PATTERN
28
+ (send
29
+ (send
30
+ _lhs_receiver :object_id) ${:== :!=}
31
+ (send
32
+ _rhs_receiver :object_id))
33
+ PATTERN
23
34
 
24
35
  def on_send(node)
25
- return unless compare_between_object_id_by_double_equal?(node)
36
+ return unless (comparison_method = object_id_comparison(node))
26
37
 
27
- add_offense(node) do |corrector|
38
+ bang = comparison_method == :== ? '' : '!'
39
+ add_offense(node,
40
+ message: format(MSG, comparison_method: comparison_method,
41
+ bang: bang)) do |corrector|
28
42
  receiver = node.receiver.receiver
29
43
  argument = node.first_argument.receiver
30
44
  return unless receiver && argument
31
45
 
32
- replacement = "#{receiver.source}.equal?(#{argument.source})"
46
+ replacement = "#{bang}#{receiver.source}.equal?(#{argument.source})"
33
47
 
34
48
  corrector.replace(node, replacement)
35
49
  end
36
50
  end
37
-
38
- private
39
-
40
- def compare_between_object_id_by_double_equal?(node)
41
- object_id_method?(node.receiver) && object_id_method?(node.first_argument)
42
- end
43
-
44
- def object_id_method?(node)
45
- node.send_type? && node.method?(:object_id)
46
- end
47
51
  end
48
52
  end
49
53
  end
@@ -54,6 +54,18 @@ module RuboCop
54
54
  end
55
55
  end
56
56
 
57
+ def on_or(node)
58
+ return unless node.lhs.falsey_literal?
59
+
60
+ add_offense(node.lhs) do |corrector|
61
+ # Don't autocorrect `'foo' && return` because having `return` as
62
+ # the leftmost node can lead to a void value expression syntax error.
63
+ next if node.rhs.type?(:return, :break, :next)
64
+
65
+ corrector.replace(node, node.rhs.source)
66
+ end
67
+ end
68
+
57
69
  def on_if(node)
58
70
  cond = condition(node)
59
71
 
@@ -123,7 +135,9 @@ module RuboCop
123
135
  # rubocop:enable Metrics/AbcSize
124
136
 
125
137
  def on_case(case_node)
126
- if case_node.condition
138
+ if (cond = case_node.condition)
139
+ return if !cond.falsey_literal? && !cond.truthy_literal?
140
+
127
141
  check_case(case_node)
128
142
  else
129
143
  case_node.when_branches.each do |when_node|
@@ -228,7 +242,7 @@ module RuboCop
228
242
  )
229
243
  end
230
244
 
231
- def condition_evaluation(node, cond)
245
+ def condition_evaluation?(node, cond)
232
246
  if node.unless?
233
247
  cond.falsey_literal?
234
248
  else
@@ -238,32 +252,24 @@ module RuboCop
238
252
 
239
253
  # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
240
254
  def correct_if_node(node, cond)
241
- result = condition_evaluation(node, cond)
242
-
243
- if node.elsif? && result
244
- add_offense(cond) do |corrector|
245
- corrector.replace(node, "else\n #{node.if_branch.source}")
246
- end
247
- elsif node.elsif? && !result
248
- add_offense(cond) do |corrector|
249
- corrector.replace(node, "else\n #{node.else_branch.source}")
250
- end
251
- elsif node.if_branch && result
252
- add_offense(cond) do |corrector|
253
- corrector.replace(node, node.if_branch.source)
254
- end
255
- elsif node.elsif_conditional?
256
- add_offense(cond) do |corrector|
257
- corrector.replace(node, "#{node.else_branch.source.sub('elsif', 'if')}\nend")
258
- end
259
- elsif node.else? || node.ternary?
260
- add_offense(cond) do |corrector|
261
- corrector.replace(node, node.else_branch.source)
262
- end
263
- else
264
- add_offense(cond) do |corrector|
265
- corrector.remove(node)
266
- end
255
+ result = condition_evaluation?(node, cond)
256
+
257
+ new_node = if node.elsif? && result
258
+ "else\n #{range_with_comments(node.if_branch).source}"
259
+ elsif node.elsif? && !result
260
+ "else\n #{node.else_branch.source}"
261
+ elsif node.if_branch && result
262
+ node.if_branch.source
263
+ elsif node.elsif_conditional?
264
+ "#{node.else_branch.source.sub('elsif', 'if')}\nend"
265
+ elsif node.else? || node.ternary?
266
+ node.else_branch.source
267
+ else
268
+ '' # Equivalent to removing the node
269
+ end
270
+
271
+ add_offense(cond) do |corrector|
272
+ corrector.replace(node, new_node)
267
273
  end
268
274
  end
269
275
  # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
@@ -45,6 +45,7 @@ module RuboCop
45
45
  #
46
46
  class NumericOperationWithConstantResult < Base
47
47
  extend AutoCorrector
48
+
48
49
  MSG = 'Numeric operation with a constant result detected.'
49
50
  RESTRICT_ON_SEND = %i[* / **].freeze
50
51
 
@@ -73,7 +73,7 @@ module RuboCop
73
73
  end
74
74
 
75
75
  def redundant_group?(expr)
76
- expr.is?(:passive, :group) && expr.count { |child| child.type != :free_space } == 1
76
+ expr.is?(:passive, :group) && expr.one? { |child| child.type != :free_space }
77
77
  end
78
78
 
79
79
  def redundantly_quantifiable?(node)
@@ -20,6 +20,15 @@ module RuboCop
20
20
  # In the example below, the safe navigation operator (`&.`) is unnecessary
21
21
  # because `NilClass` has methods like `respond_to?` and `is_a?`.
22
22
  #
23
+ # The `InferNonNilReceiver` option specifies whether to look into previous code
24
+ # paths to infer if the receiver can't be nil. This check is unsafe because the receiver
25
+ # can be redefined between the safe navigation call and previous regular method call.
26
+ # It does the inference only in the current scope, e.g. within the same method definition etc.
27
+ #
28
+ # The `AdditionalNilMethods` option specifies additional custom methods which are
29
+ # defined on `NilClass`. When `InferNonNilReceiver` is set, they are used to determine
30
+ # whether the receiver can be nil.
31
+ #
23
32
  # @safety
24
33
  # This cop is unsafe, because autocorrection can change the return type of
25
34
  # the expression. An offending expression that previously could return `nil`
@@ -33,6 +42,20 @@ module RuboCop
33
42
  # CamelCaseConst.do_something
34
43
  #
35
44
  # # bad
45
+ # foo.to_s&.strip
46
+ # foo.to_i&.zero?
47
+ # foo.to_f&.zero?
48
+ # foo.to_a&.size
49
+ # foo.to_h&.size
50
+ #
51
+ # # good
52
+ # foo.to_s.strip
53
+ # foo.to_i.zero?
54
+ # foo.to_f.zero?
55
+ # foo.to_a.size
56
+ # foo.to_h.size
57
+ #
58
+ # # bad
36
59
  # do_something if attrs&.respond_to?(:[])
37
60
  #
38
61
  # # good
@@ -81,17 +104,59 @@ module RuboCop
81
104
  # do_something if attrs.nil_safe_method(:[])
82
105
  # do_something if attrs&.not_nil_safe_method(:[])
83
106
  #
107
+ # @example InferNonNilReceiver: false (default)
108
+ # # good
109
+ # foo.bar
110
+ # foo&.baz
111
+ #
112
+ # @example InferNonNilReceiver: true
113
+ # # bad
114
+ # foo.bar
115
+ # foo&.baz # would raise on previous line if `foo` is nil
116
+ #
117
+ # # good
118
+ # foo.bar
119
+ # foo.baz
120
+ #
121
+ # # bad
122
+ # if foo.condition?
123
+ # foo&.bar
124
+ # end
125
+ #
126
+ # # good
127
+ # if foo.condition?
128
+ # foo.bar
129
+ # end
130
+ #
131
+ # # good (different scopes)
132
+ # def method1
133
+ # foo.bar
134
+ # end
135
+ #
136
+ # def method2
137
+ # foo&.bar
138
+ # end
139
+ #
140
+ # @example AdditionalNilMethods: [present?]
141
+ # # good
142
+ # foo.present?
143
+ # foo&.bar
144
+ #
84
145
  class RedundantSafeNavigation < Base
85
146
  include AllowedMethods
86
147
  extend AutoCorrector
87
148
 
88
149
  MSG = 'Redundant safe navigation detected, use `.` instead.'
89
150
  MSG_LITERAL = 'Redundant safe navigation with default literal detected.'
151
+ MSG_NON_NIL = 'Redundant safe navigation on non-nil receiver (detected by analyzing ' \
152
+ 'previous code/method invocations).'
90
153
 
91
154
  NIL_SPECIFIC_METHODS = (nil.methods - Object.new.methods).to_set.freeze
92
155
 
93
156
  SNAKE_CASE = /\A[[:digit:][:upper:]_]+\z/.freeze
94
157
 
158
+ GUARANTEED_INSTANCE_METHODS = %i[to_s to_i to_f to_a to_h].freeze
159
+
95
160
  # @!method respond_to_nil_specific_method?(node)
96
161
  def_node_matcher :respond_to_nil_specific_method?, <<~PATTERN
97
162
  (csend _ :respond_to? (sym %NIL_SPECIFIC_METHODS))
@@ -111,15 +176,27 @@ module RuboCop
111
176
 
112
177
  # rubocop:disable Metrics/AbcSize
113
178
  def on_csend(node)
179
+ range = node.loc.dot
180
+
181
+ if infer_non_nil_receiver?
182
+ checker = Lint::Utils::NilReceiverChecker.new(node.receiver, additional_nil_methods)
183
+
184
+ if checker.cant_be_nil?
185
+ add_offense(range, message: MSG_NON_NIL) { |corrector| corrector.replace(range, '.') }
186
+ return
187
+ end
188
+ end
189
+
114
190
  unless assume_receiver_instance_exists?(node.receiver)
115
- return unless check?(node) && allowed_method?(node.method_name)
191
+ return if !guaranteed_instance?(node.receiver) && !check?(node)
116
192
  return if respond_to_nil_specific_method?(node)
117
193
  end
118
194
 
119
- range = node.loc.dot
120
195
  add_offense(range) { |corrector| corrector.replace(range, '.') }
121
196
  end
197
+ # rubocop:enable Metrics/AbcSize
122
198
 
199
+ # rubocop:disable Metrics/AbcSize
123
200
  def on_or(node)
124
201
  conversion_with_default?(node) do |send_node|
125
202
  range = send_node.loc.dot.begin.join(node.source_range.end)
@@ -142,7 +219,20 @@ module RuboCop
142
219
  receiver.self_type? || (receiver.literal? && !receiver.nil_type?)
143
220
  end
144
221
 
222
+ def guaranteed_instance?(node)
223
+ receiver = if node.any_block_type?
224
+ node.send_node
225
+ else
226
+ node
227
+ end
228
+ return false unless receiver.send_type?
229
+
230
+ GUARANTEED_INSTANCE_METHODS.include?(receiver.method_name)
231
+ end
232
+
145
233
  def check?(node)
234
+ return false unless allowed_method?(node.method_name)
235
+
146
236
  parent = node.parent
147
237
  return false unless parent
148
238
 
@@ -154,6 +244,15 @@ module RuboCop
154
244
  def condition?(parent, node)
155
245
  (parent.conditional? || parent.post_condition_loop?) && parent.condition == node
156
246
  end
247
+
248
+ def infer_non_nil_receiver?
249
+ cop_config['InferNonNilReceiver']
250
+ end
251
+
252
+ def additional_nil_methods
253
+ @additional_nil_methods ||=
254
+ Array(cop_config.fetch('AdditionalNilMethods', []).map(&:to_sym))
255
+ end
157
256
  end
158
257
  end
159
258
  end
@@ -185,9 +185,9 @@ module RuboCop
185
185
  (hash (pair (sym :exception) false))
186
186
  PATTERN
187
187
 
188
- # rubocop:disable Metrics/AbcSize
188
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
189
189
  def on_send(node)
190
- return if hash_or_set_with_block?(node)
190
+ return if node.arguments.any? || hash_or_set_with_block?(node)
191
191
 
192
192
  receiver = find_receiver(node)
193
193
  return unless literal_receiver?(node, receiver) ||
@@ -198,10 +198,10 @@ module RuboCop
198
198
  message = format(MSG, method: node.method_name)
199
199
 
200
200
  add_offense(node.loc.selector, message: message) do |corrector|
201
- corrector.remove(node.loc.dot.join(node.loc.selector))
201
+ corrector.remove(node.loc.dot.join(node.loc.end || node.loc.selector))
202
202
  end
203
203
  end
204
- # rubocop:enable Metrics/AbcSize
204
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
205
205
  alias on_csend on_send
206
206
 
207
207
  private
@@ -43,7 +43,7 @@ module RuboCop
43
43
  def on_irange(node)
44
44
  return if node.parent&.begin_type?
45
45
  return unless node.begin && node.end
46
- return if same_line?(node.begin, node.end)
46
+ return if same_line?(node.loc.operator, node.end)
47
47
 
48
48
  message = format(MSG, range: "#{node.begin.source}#{node.loc.operator.source}")
49
49
 
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # Check for arguments to `rescue` that will result in a `TypeError`
6
+ # Checks for arguments to `rescue` that will result in a `TypeError`
7
7
  # if an exception is raised.
8
8
  #
9
9
  # @example
@@ -97,7 +97,7 @@ module RuboCop
97
97
  end
98
98
 
99
99
  def require_parentheses?(send_node)
100
- return true if operator_inside_hash?(send_node)
100
+ return true if operator_inside_collection_literal?(send_node)
101
101
  return false unless send_node.comparison_method?
102
102
  return false unless (node = send_node.parent)
103
103
 
@@ -105,10 +105,10 @@ module RuboCop
105
105
  (node.respond_to?(:comparison_method?) && node.comparison_method?)
106
106
  end
107
107
 
108
- def operator_inside_hash?(send_node)
109
- # If an operator call (without a dot) is inside a hash, it needs
108
+ def operator_inside_collection_literal?(send_node)
109
+ # If an operator call (without a dot) is inside an array or a hash, it needs
110
110
  # to be parenthesized when converted to safe navigation.
111
- send_node.parent&.pair_type? && !send_node.loc.dot
111
+ send_node.parent&.type?(:array, :pair) && !send_node.loc.dot
112
112
  end
113
113
  end
114
114
  end
@@ -23,6 +23,14 @@ module RuboCop
23
23
  # # good (method calls possibly can return different results)
24
24
  # hash[foo] = hash[foo]
25
25
  #
26
+ # @example AllowRBSInlineAnnotation:true
27
+ # # good
28
+ # foo = foo #: Integer
29
+ # foo, bar = foo, bar #: Integer
30
+ # Foo = Foo #: Integer
31
+ # hash['foo'] = hash['foo'] #: Integer
32
+ # obj.attr = obj.attr #: Integer
33
+ #
26
34
  class SelfAssignment < Base
27
35
  MSG = 'Self-assignment detected.'
28
36
 
@@ -34,6 +42,8 @@ module RuboCop
34
42
  }.freeze
35
43
 
36
44
  def on_send(node)
45
+ return if allow_rbs_inline_annotation? && rbs_inline_annotation?(node.receiver)
46
+
37
47
  if node.method?(:[]=)
38
48
  handle_key_assignment(node) if node.arguments.size == 2
39
49
  elsif node.assignment_method?
@@ -44,6 +54,7 @@ module RuboCop
44
54
 
45
55
  def on_lvasgn(node)
46
56
  return unless node.rhs
57
+ return if allow_rbs_inline_annotation? && rbs_inline_annotation?(node.rhs)
47
58
 
48
59
  rhs_type = ASSIGNMENT_TYPE_TO_RHS_TYPE[node.type]
49
60
 
@@ -55,16 +66,22 @@ module RuboCop
55
66
 
56
67
  def on_casgn(node)
57
68
  return unless node.rhs&.const_type?
69
+ return if allow_rbs_inline_annotation? && rbs_inline_annotation?(node.rhs)
58
70
 
59
71
  add_offense(node) if node.namespace == node.rhs.namespace &&
60
72
  node.short_name == node.rhs.short_name
61
73
  end
62
74
 
63
75
  def on_masgn(node)
76
+ first_lhs = node.lhs.assignments.first
77
+ return if allow_rbs_inline_annotation? && rbs_inline_annotation?(first_lhs)
78
+
64
79
  add_offense(node) if multiple_self_assignment?(node)
65
80
  end
66
81
 
67
82
  def on_or_asgn(node)
83
+ return if allow_rbs_inline_annotation? && rbs_inline_annotation?(node.lhs)
84
+
68
85
  add_offense(node) if rhs_matches_lhs?(node.rhs, node.lhs)
69
86
  end
70
87
  alias on_and_asgn on_or_asgn
@@ -108,6 +125,14 @@ module RuboCop
108
125
  add_offense(node)
109
126
  end
110
127
  end
128
+
129
+ def rbs_inline_annotation?(node)
130
+ processed_source.ast_with_comments[node].any? { |comment| comment.text.start_with?('#:') }
131
+ end
132
+
133
+ def allow_rbs_inline_annotation?
134
+ cop_config['AllowRBSInlineAnnotation']
135
+ end
111
136
  end
112
137
  end
113
138
  end
@@ -8,6 +8,11 @@ module RuboCop
8
8
  # given by `ruby -cw` prior to Ruby 2.6:
9
9
  # "shadowing outer local variable - foo".
10
10
  #
11
+ # The cop is now disabled by default to match the upstream Ruby behavior.
12
+ # It's useful, however, if you'd like to avoid shadowing variables from outer
13
+ # scopes, which some people consider an anti-pattern that makes it harder
14
+ # to keep track of what's going on in a program.
15
+ #
11
16
  # NOTE: Shadowing of variables in block passed to `Ractor.new` is allowed
12
17
  # because `Ractor` should not access outer variables.
13
18
  # eg. following style is encouraged:
@@ -4,10 +4,10 @@ module RuboCop
4
4
  module Cop
5
5
  module Lint
6
6
  # Checks for redundant access modifiers, including those with no
7
- # code, those which are repeated, and leading `public` modifiers in a
8
- # class or module body. Conditionally-defined methods are considered as
9
- # always being defined, and thus access modifiers guarding such methods
10
- # are not redundant.
7
+ # code, those which are repeated, those which are on top-level, and
8
+ # leading `public` modifiers in a class or module body.
9
+ # Conditionally-defined methods are considered as always being defined,
10
+ # and thus access modifiers guarding such methods are not redundant.
11
11
  #
12
12
  # This cop has `ContextCreatingMethods` option. The default setting value
13
13
  # is an empty array that means no method is specified.
@@ -58,6 +58,12 @@ module RuboCop
58
58
  # private # this is redundant (no following methods are defined)
59
59
  # end
60
60
  #
61
+ # # bad
62
+ # private # this is useless (access modifiers have no effect on top-level)
63
+ #
64
+ # def method
65
+ # end
66
+ #
61
67
  # # good
62
68
  # class Foo
63
69
  # private # this is not redundant (a method is defined)
@@ -145,6 +151,17 @@ module RuboCop
145
151
  alias on_numblock on_block
146
152
  alias on_itblock on_block
147
153
 
154
+ def on_begin(node)
155
+ return if node.parent
156
+
157
+ node.child_nodes.each do |child|
158
+ next unless child.send_type? && access_modifier?(child)
159
+
160
+ # This call always registers an offense for access modifier `child.method_name`
161
+ check_send_node(child, child.method_name, true)
162
+ end
163
+ end
164
+
148
165
  private
149
166
 
150
167
  def autocorrect(corrector, node)
@@ -257,6 +274,10 @@ module RuboCop
257
274
 
258
275
  def any_method_definition?(child)
259
276
  cop_config.fetch('MethodCreatingMethods', []).any? do |m|
277
+ # Some users still have `"included"` in their `MethodCreatingMethods` configurations,
278
+ # so to prevent Ruby method redefinition warnings let's just skip this value.
279
+ next if m == 'included'
280
+
260
281
  matcher_name = :"#{m}_method?"
261
282
  unless respond_to?(matcher_name)
262
283
  self.class.def_node_matcher matcher_name, <<~PATTERN
@@ -279,7 +300,11 @@ module RuboCop
279
300
  end
280
301
 
281
302
  def any_context_creating_methods?(child)
303
+ # Some users still have `"included"` in their `ContextCreatingMethods` configurations,
304
+ # so to prevent Ruby method redefinition warnings let's just skip this value.
282
305
  cop_config.fetch('ContextCreatingMethods', []).any? do |m|
306
+ next if m == 'included'
307
+
283
308
  matcher_name = :"#{m}_block?"
284
309
  unless respond_to?(matcher_name)
285
310
  self.class.def_node_matcher matcher_name, <<~PATTERN
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Checks for usage of method `fetch` or `Array.new` with default value argument
7
+ # and block. In such cases, block will always be used as default value.
8
+ #
9
+ # This cop emulates Ruby warning "block supersedes default value argument" which
10
+ # applies to `Array.new`, `Array#fetch`, `Hash#fetch`, `ENV.fetch` and
11
+ # `Thread#fetch`.
12
+ #
13
+ # A `fetch` call without a receiver is considered a custom method and does not register
14
+ # an offense.
15
+ #
16
+ # @safety
17
+ # This cop is unsafe because the receiver could have nonstandard implementation
18
+ # of `fetch`, or be a class other than the one listed above.
19
+ #
20
+ # It is also unsafe because default value argument could have side effects:
21
+ #
22
+ # [source,ruby]
23
+ # ----
24
+ # def x(a) = puts "side effect"
25
+ # Array.new(5, x(1)) { 2 }
26
+ # ----
27
+ #
28
+ # so removing it would change behavior.
29
+ #
30
+ # @example
31
+ # # bad
32
+ # x.fetch(key, default_value) { block_value }
33
+ # Array.new(size, default_value) { block_value }
34
+ #
35
+ # # good
36
+ # x.fetch(key) { block_value }
37
+ # Array.new(size) { block_value }
38
+ #
39
+ # # also good - in case default value argument is desired instead
40
+ # x.fetch(key, default_value)
41
+ # Array.new(size, default_value)
42
+ #
43
+ # # good - keyword arguments aren't registered as offenses
44
+ # x.fetch(key, keyword: :arg) { block_value }
45
+ #
46
+ # @example AllowedReceivers: ['Rails.cache']
47
+ # # good
48
+ # Rails.cache.fetch(name, options) { block }
49
+ #
50
+ class UselessDefaultValueArgument < Base
51
+ include AllowedReceivers
52
+ extend AutoCorrector
53
+
54
+ MSG = 'Block supersedes default value argument.'
55
+
56
+ RESTRICT_ON_SEND = %i[fetch new].freeze
57
+
58
+ # @!method default_value_argument_and_block(node)
59
+ def_node_matcher :default_value_argument_and_block, <<~PATTERN
60
+ (any_block
61
+ {
62
+ (call !nil? :fetch $_key $_default_value)
63
+ (send (const _ :Array) :new $_size $_default_value)
64
+ }
65
+ _args
66
+ _block_body)
67
+ PATTERN
68
+
69
+ def on_send(node)
70
+ unless (prev_arg_node, default_value_node = default_value_argument_and_block(node.parent))
71
+ return
72
+ end
73
+ return if allowed_receiver?(node.receiver)
74
+ return if hash_without_braces?(default_value_node)
75
+
76
+ add_offense(default_value_node) do |corrector|
77
+ corrector.remove(prev_arg_node.source_range.end.join(default_value_node.source_range))
78
+ end
79
+ end
80
+ alias on_csend on_send
81
+
82
+ private
83
+
84
+ def hash_without_braces?(node)
85
+ node.hash_type? && !node.braces?
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -31,6 +31,7 @@ module RuboCop
31
31
  #
32
32
  class UselessNumericOperation < Base
33
33
  extend AutoCorrector
34
+
34
35
  MSG = 'Do not apply inconsequential numeric operations to variables.'
35
36
  RESTRICT_ON_SEND = %i[+ - * / **].freeze
36
37