rubocop 1.75.8 → 1.77.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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -14
  3. data/config/default.yml +72 -7
  4. data/config/obsoletion.yml +6 -3
  5. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -1
  6. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +5 -2
  7. data/lib/rubocop/cop/gemspec/attribute_assignment.rb +91 -0
  8. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +0 -22
  9. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  10. data/lib/rubocop/cop/gemspec/require_mfa.rb +15 -1
  11. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +4 -4
  12. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +1 -0
  13. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +1 -1
  14. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -1
  15. data/lib/rubocop/cop/layout/line_length.rb +26 -5
  16. data/lib/rubocop/cop/layout/space_before_brackets.rb +2 -9
  17. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +7 -2
  18. data/lib/rubocop/cop/lint/ambiguous_range.rb +5 -0
  19. data/lib/rubocop/cop/lint/empty_interpolation.rb +3 -1
  20. data/lib/rubocop/cop/lint/float_comparison.rb +4 -4
  21. data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
  22. data/lib/rubocop/cop/lint/literal_as_condition.rb +19 -27
  23. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
  24. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +4 -4
  25. data/lib/rubocop/cop/lint/self_assignment.rb +25 -0
  26. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -0
  27. data/lib/rubocop/cop/lint/useless_access_modifier.rb +29 -4
  28. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +90 -0
  29. data/lib/rubocop/cop/lint/useless_or.rb +98 -0
  30. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +3 -3
  31. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  32. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
  33. data/lib/rubocop/cop/mixin/gemspec_help.rb +22 -0
  34. data/lib/rubocop/cop/mixin/line_length_help.rb +24 -8
  35. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  36. data/lib/rubocop/cop/naming/file_name.rb +2 -2
  37. data/lib/rubocop/cop/naming/predicate_method.rb +281 -0
  38. data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +4 -4
  39. data/lib/rubocop/cop/style/case_like_if.rb +1 -1
  40. data/lib/rubocop/cop/style/collection_querying.rb +167 -0
  41. data/lib/rubocop/cop/style/conditional_assignment.rb +3 -1
  42. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
  43. data/lib/rubocop/cop/style/exponential_notation.rb +2 -2
  44. data/lib/rubocop/cop/style/fetch_env_var.rb +32 -6
  45. data/lib/rubocop/cop/style/hash_conversion.rb +12 -3
  46. data/lib/rubocop/cop/style/if_unless_modifier.rb +13 -6
  47. data/lib/rubocop/cop/style/it_block_parameter.rb +33 -14
  48. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -2
  49. data/lib/rubocop/cop/style/min_max_comparison.rb +13 -5
  50. data/lib/rubocop/cop/style/redundant_array_flatten.rb +50 -0
  51. data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
  52. data/lib/rubocop/cop/style/redundant_parentheses.rb +26 -5
  53. data/lib/rubocop/cop/style/redundant_self.rb +8 -5
  54. data/lib/rubocop/cop/style/safe_navigation.rb +24 -11
  55. data/lib/rubocop/cop/style/sole_nested_conditional.rb +2 -1
  56. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  57. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  58. data/lib/rubocop/formatter/fuubar_style_formatter.rb +1 -1
  59. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  60. data/lib/rubocop/lsp/diagnostic.rb +4 -4
  61. data/lib/rubocop/rspec/expect_offense.rb +9 -3
  62. data/lib/rubocop/version.rb +1 -1
  63. data/lib/rubocop.rb +8 -1
  64. data/lib/ruby_lsp/rubocop/addon.rb +2 -2
  65. metadata +14 -7
@@ -86,6 +86,7 @@ module RuboCop
86
86
  def on_array(node)
87
87
  return if node.array_type? && !node.square_brackets?
88
88
 
89
+ node = find_node_with_brackets(node)
89
90
  tokens, left, right = array_brackets(node)
90
91
  return unless left && right
91
92
 
@@ -102,6 +103,10 @@ module RuboCop
102
103
 
103
104
  private
104
105
 
106
+ def find_node_with_brackets(node)
107
+ node.ancestors.find(&:const_pattern_type?) || node
108
+ end
109
+
105
110
  def autocorrect(corrector, node)
106
111
  tokens, left, right = array_brackets(node)
107
112
 
@@ -119,7 +124,7 @@ module RuboCop
119
124
  def array_brackets(node)
120
125
  tokens = processed_source.tokens_within(node)
121
126
 
122
- left = tokens.find(&:left_array_bracket?)
127
+ left = tokens.find(&:left_bracket?)
123
128
  right = tokens.reverse_each.find(&:right_bracket?)
124
129
 
125
130
  [tokens, left, right]
@@ -192,7 +197,7 @@ module RuboCop
192
197
  if side == :right
193
198
  processed_source.tokens_within(node)[i].right_bracket?
194
199
  else
195
- processed_source.tokens_within(node)[i].left_array_bracket?
200
+ processed_source.tokens_within(node)[i].left_bracket?
196
201
  end
197
202
  end
198
203
 
@@ -27,7 +27,9 @@ module RuboCop
27
27
  # @example
28
28
  # # bad
29
29
  # x || 1..2
30
+ # x - 1..2
30
31
  # (x || 1..2)
32
+ # x || 1..y || 2
31
33
  # 1..2.to_a
32
34
  #
33
35
  # # good, unambiguous
@@ -41,6 +43,7 @@ module RuboCop
41
43
  #
42
44
  # # good, ambiguity removed
43
45
  # x || (1..2)
46
+ # (x - 1)..2
44
47
  # (x || 1)..2
45
48
  # (x || 1)..(y || 2)
46
49
  # (1..2).to_a
@@ -96,6 +99,8 @@ module RuboCop
96
99
  # to avoid the ambiguity of `1..2.to_a`.
97
100
  return false if node.receiver&.basic_literal?
98
101
 
102
+ return false if node.operator_method? && !node.method?(:[])
103
+
99
104
  require_parentheses_for_method_chain? || node.receiver.nil?
100
105
  end
101
106
 
@@ -19,7 +19,9 @@ module RuboCop
19
19
  MSG = 'Empty interpolation detected.'
20
20
 
21
21
  def on_interpolation(begin_node)
22
- return unless begin_node.children.empty?
22
+ node_children = begin_node.children.dup
23
+ node_children.delete_if { |e| e.nil_type? || (e.basic_literal? && e.str_content&.empty?) }
24
+ return unless node_children.empty?
23
25
 
24
26
  add_offense(begin_node) { |corrector| corrector.remove(begin_node) }
25
27
  end
@@ -94,7 +94,7 @@ module RuboCop
94
94
  when :float
95
95
  true
96
96
  when :send
97
- check_send(node)
97
+ float_send?(node)
98
98
  when :begin
99
99
  float?(node.children.first)
100
100
  else
@@ -108,18 +108,18 @@ module RuboCop
108
108
  (node.numeric_type? && node.value.zero?) || node.nil_type?
109
109
  end
110
110
 
111
- def check_send(node)
111
+ def float_send?(node)
112
112
  if node.arithmetic_operation?
113
113
  float?(node.receiver) || float?(node.first_argument)
114
114
  elsif FLOAT_RETURNING_METHODS.include?(node.method_name)
115
115
  true
116
116
  elsif node.receiver&.float_type?
117
117
  FLOAT_INSTANCE_METHODS.include?(node.method_name) ||
118
- check_numeric_returning_method(node)
118
+ numeric_returning_method?(node)
119
119
  end
120
120
  end
121
121
 
122
- def check_numeric_returning_method(node)
122
+ def numeric_returning_method?(node)
123
123
  return false unless node.receiver
124
124
 
125
125
  case node.method_name
@@ -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
@@ -228,7 +228,7 @@ module RuboCop
228
228
  )
229
229
  end
230
230
 
231
- def condition_evaluation(node, cond)
231
+ def condition_evaluation?(node, cond)
232
232
  if node.unless?
233
233
  cond.falsey_literal?
234
234
  else
@@ -238,32 +238,24 @@ module RuboCop
238
238
 
239
239
  # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
240
240
  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
241
+ result = condition_evaluation?(node, cond)
242
+
243
+ new_node = if node.elsif? && result
244
+ "else\n #{range_with_comments(node.if_branch).source}"
245
+ elsif node.elsif? && !result
246
+ "else\n #{node.else_branch.source}"
247
+ elsif node.if_branch && result
248
+ node.if_branch.source
249
+ elsif node.elsif_conditional?
250
+ "#{node.else_branch.source.sub('elsif', 'if')}\nend"
251
+ elsif node.else? || node.ternary?
252
+ node.else_branch.source
253
+ else
254
+ '' # Equivalent to removing the node
255
+ end
256
+
257
+ add_offense(cond) do |corrector|
258
+ corrector.replace(node, new_node)
267
259
  end
268
260
  end
269
261
  # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
@@ -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)
@@ -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
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Checks for useless OR (`||` and `or`) expressions.
7
+ #
8
+ # Some methods always return a truthy value, even when called
9
+ # on `nil` (e.g. `nil.to_i` evaluates to `0`). Therefore, OR expressions
10
+ # appended after these methods will never evaluate.
11
+ #
12
+ # @example
13
+ #
14
+ # # bad
15
+ # x.to_a || fallback
16
+ # x.to_c || fallback
17
+ # x.to_d || fallback
18
+ # x.to_i || fallback
19
+ # x.to_f || fallback
20
+ # x.to_h || fallback
21
+ # x.to_r || fallback
22
+ # x.to_s || fallback
23
+ # x.to_sym || fallback
24
+ # x.intern || fallback
25
+ # x.inspect || fallback
26
+ # x.hash || fallback
27
+ # x.object_id || fallback
28
+ # x.__id__ || fallback
29
+ #
30
+ # x.to_s or fallback
31
+ #
32
+ # # good - if fallback is same as return value of method called on nil
33
+ # x.to_a # nil.to_a returns []
34
+ # x.to_c # nil.to_c returns (0+0i)
35
+ # x.to_d # nil.to_d returns 0.0
36
+ # x.to_i # nil.to_i returns 0
37
+ # x.to_f # nil.to_f returns 0.0
38
+ # x.to_h # nil.to_h returns {}
39
+ # x.to_r # nil.to_r returns (0/1)
40
+ # x.to_s # nil.to_s returns ''
41
+ # x.to_sym # nil.to_sym raises an error
42
+ # x.intern # nil.intern raises an error
43
+ # x.inspect # nil.inspect returns "nil"
44
+ # x.hash # nil.hash returns an Integer
45
+ # x.object_id # nil.object_id returns an Integer
46
+ # x.__id__ # nil.object_id returns an Integer
47
+ #
48
+ # # good - if the intention is not to call the method on nil
49
+ # x&.to_a || fallback
50
+ # x&.to_c || fallback
51
+ # x&.to_d || fallback
52
+ # x&.to_i || fallback
53
+ # x&.to_f || fallback
54
+ # x&.to_h || fallback
55
+ # x&.to_r || fallback
56
+ # x&.to_s || fallback
57
+ # x&.to_sym || fallback
58
+ # x&.intern || fallback
59
+ # x&.inspect || fallback
60
+ # x&.hash || fallback
61
+ # x&.object_id || fallback
62
+ # x&.__id__ || fallback
63
+ #
64
+ # x&.to_s or fallback
65
+ #
66
+ class UselessOr < Base
67
+ MSG = '`%<rhs>s` will never evaluate because `%<lhs>s` always returns a truthy value.'
68
+
69
+ TRUTHY_RETURN_VALUE_METHODS = Set[:to_a, :to_c, :to_d, :to_i, :to_f, :to_h, :to_r,
70
+ :to_s, :to_sym, :intern, :inspect, :hash, :object_id,
71
+ :__id__].freeze
72
+
73
+ # @!method truthy_return_value_method?(node)
74
+ def_node_matcher :truthy_return_value_method?, <<~PATTERN
75
+ (send _ %TRUTHY_RETURN_VALUE_METHODS)
76
+ PATTERN
77
+
78
+ def on_or(node)
79
+ if truthy_return_value_method?(node.lhs)
80
+ report_offense(node, node.lhs)
81
+ elsif truthy_return_value_method?(node.rhs)
82
+ parent = node.parent
83
+ parent = parent.parent if parent&.begin_type?
84
+
85
+ report_offense(parent, node.rhs) if parent&.or_type?
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ def report_offense(or_node, truthy_node)
92
+ add_offense(or_node.loc.operator.join(or_node.rhs.source_range),
93
+ message: format(MSG, lhs: truthy_node.source, rhs: or_node.rhs.source))
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -89,7 +89,7 @@ module RuboCop
89
89
  private
90
90
 
91
91
  def inspect_def(node, def_node)
92
- return if allowed_arguments(def_node.arguments)
92
+ return if allowed_arguments?(def_node.arguments)
93
93
 
94
94
  add_offense(node.loc.selector, message: format(MSG, method_name: def_node.method_name))
95
95
  end
@@ -101,7 +101,7 @@ module RuboCop
101
101
  definition = find_method_definition(node, method_name)
102
102
 
103
103
  return unless definition
104
- return if allowed_arguments(definition.arguments)
104
+ return if allowed_arguments?(definition.arguments)
105
105
 
106
106
  add_offense(node, message: format(MSG, method_name: method_name))
107
107
  end
@@ -115,7 +115,7 @@ module RuboCop
115
115
  end
116
116
 
117
117
  # `ruby2_keywords` is only allowed if there's a `restarg` and no keyword arguments
118
- def allowed_arguments(arguments)
118
+ def allowed_arguments?(arguments)
119
119
  return false if arguments.empty?
120
120
 
121
121
  arguments.each_child_node(:restarg).any? &&
@@ -66,7 +66,7 @@ module RuboCop
66
66
  end
67
67
 
68
68
  # @deprecated Use processed_source.line_with_comment?(line)
69
- def end_of_line_comment(line)
69
+ def end_of_line_comment(line) # rubocop:disable Naming/PredicateMethod
70
70
  warn Rainbow(<<~WARNING).yellow, uplevel: 1
71
71
  `end_of_line_comment` is deprecated. Use `processed_source.line_with_comment?` instead.
72
72
  WARNING
@@ -89,7 +89,7 @@ module RuboCop
89
89
 
90
90
  if first_non_comment_token
91
91
  # `line` is 1-indexed so we need to subtract 1 to get the array index
92
- processed_source.lines[0...first_non_comment_token.line - 1]
92
+ processed_source.lines[0...(first_non_comment_token.line - 1)]
93
93
  else
94
94
  processed_source.lines
95
95
  end
@@ -25,6 +25,28 @@ module RuboCop
25
25
  (args
26
26
  (arg $_)) ...)
27
27
  PATTERN
28
+
29
+ # @!method assignment_method_declarations(node)
30
+ def_node_search :assignment_method_declarations, <<~PATTERN
31
+ (send
32
+ (lvar {#match_block_variable_name? :_1 :it}) _ ...)
33
+ PATTERN
34
+
35
+ # @!method indexed_assignment_method_declarations(node)
36
+ def_node_search :indexed_assignment_method_declarations, <<~PATTERN
37
+ (send
38
+ (send (lvar {#match_block_variable_name? :_1 :it}) _)
39
+ :[]=
40
+ literal?
41
+ _
42
+ )
43
+ PATTERN
44
+
45
+ def match_block_variable_name?(receiver_name)
46
+ gem_specification(processed_source.ast) do |block_variable_name|
47
+ return block_variable_name == receiver_name
48
+ end
49
+ end
28
50
  end
29
51
  end
30
52
  end