rubocop 1.76.1 → 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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/config/default.yml +22 -0
  4. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +5 -2
  5. data/lib/rubocop/cop/gemspec/attribute_assignment.rb +91 -0
  6. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +0 -22
  7. data/lib/rubocop/cop/gemspec/require_mfa.rb +15 -1
  8. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +4 -4
  9. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +1 -1
  10. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -1
  11. data/lib/rubocop/cop/layout/line_length.rb +26 -5
  12. data/lib/rubocop/cop/layout/space_before_brackets.rb +2 -9
  13. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +7 -2
  14. data/lib/rubocop/cop/lint/ambiguous_range.rb +5 -0
  15. data/lib/rubocop/cop/lint/empty_interpolation.rb +1 -1
  16. data/lib/rubocop/cop/lint/float_comparison.rb +4 -4
  17. data/lib/rubocop/cop/lint/literal_as_condition.rb +2 -2
  18. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
  19. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +4 -4
  20. data/lib/rubocop/cop/lint/self_assignment.rb +25 -0
  21. data/lib/rubocop/cop/lint/useless_access_modifier.rb +29 -4
  22. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +4 -1
  23. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +3 -3
  24. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  25. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
  26. data/lib/rubocop/cop/mixin/gemspec_help.rb +22 -0
  27. data/lib/rubocop/cop/mixin/line_length_help.rb +24 -8
  28. data/lib/rubocop/cop/naming/file_name.rb +2 -2
  29. data/lib/rubocop/cop/naming/predicate_method.rb +73 -8
  30. data/lib/rubocop/cop/naming/predicate_prefix.rb +2 -2
  31. data/lib/rubocop/cop/style/case_like_if.rb +1 -1
  32. data/lib/rubocop/cop/style/collection_querying.rb +167 -0
  33. data/lib/rubocop/cop/style/conditional_assignment.rb +3 -1
  34. data/lib/rubocop/cop/style/exponential_notation.rb +2 -2
  35. data/lib/rubocop/cop/style/fetch_env_var.rb +32 -6
  36. data/lib/rubocop/cop/style/hash_conversion.rb +12 -3
  37. data/lib/rubocop/cop/style/if_unless_modifier.rb +11 -2
  38. data/lib/rubocop/cop/style/it_block_parameter.rb +2 -2
  39. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +1 -1
  40. data/lib/rubocop/cop/style/min_max_comparison.rb +13 -5
  41. data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
  42. data/lib/rubocop/cop/style/redundant_parentheses.rb +10 -1
  43. data/lib/rubocop/cop/style/redundant_self.rb +8 -5
  44. data/lib/rubocop/cop/style/sole_nested_conditional.rb +2 -1
  45. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  46. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  47. data/lib/rubocop/formatter/fuubar_style_formatter.rb +1 -1
  48. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  49. data/lib/rubocop/lsp/diagnostic.rb +4 -4
  50. data/lib/rubocop/version.rb +1 -1
  51. data/lib/rubocop.rb +2 -0
  52. data/lib/ruby_lsp/rubocop/addon.rb +2 -2
  53. metadata +8 -6
@@ -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
@@ -10,6 +10,9 @@ module RuboCop
10
10
  # applies to `Array.new`, `Array#fetch`, `Hash#fetch`, `ENV.fetch` and
11
11
  # `Thread#fetch`.
12
12
  #
13
+ # A `fetch` call without a receiver is considered a custom method and does not register
14
+ # an offense.
15
+ #
13
16
  # @safety
14
17
  # This cop is unsafe because the receiver could have nonstandard implementation
15
18
  # of `fetch`, or be a class other than the one listed above.
@@ -56,7 +59,7 @@ module RuboCop
56
59
  def_node_matcher :default_value_argument_and_block, <<~PATTERN
57
60
  (any_block
58
61
  {
59
- (call _receiver :fetch $_key $_default_value)
62
+ (call !nil? :fetch $_key $_default_value)
60
63
  (send (const _ :Array) :new $_size $_default_value)
61
64
  }
62
65
  _args
@@ -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
@@ -25,20 +25,24 @@ module RuboCop
25
25
  config.for_cop('Layout/LineLength')['AllowURI']
26
26
  end
27
27
 
28
- def allowed_uri_position?(line, uri_range)
29
- uri_range.begin < max_line_length && uri_range.end == line_length(line)
28
+ def allow_qualified_name?
29
+ config.for_cop('Layout/LineLength')['AllowQualifiedName']
30
+ end
31
+
32
+ def allowed_position?(line, range)
33
+ range.begin < max_line_length && range.end == line_length(line)
30
34
  end
31
35
 
32
36
  def line_length(line)
33
37
  line.length + indentation_difference(line)
34
38
  end
35
39
 
36
- def find_excessive_uri_range(line)
37
- last_uri_match = match_uris(line).last
38
- return nil unless last_uri_match
40
+ def find_excessive_range(line, type)
41
+ last_match = (type == :uri ? match_uris(line) : match_qualified_names(line)).last
42
+ return nil unless last_match
39
43
 
40
- begin_position, end_position = last_uri_match.offset(0)
41
- end_position = extend_uri_end_position(line, end_position)
44
+ begin_position, end_position = last_match.offset(0)
45
+ end_position = extend_end_position(line, end_position)
42
46
 
43
47
  line_indentation_difference = indentation_difference(line)
44
48
  begin_position += line_indentation_difference
@@ -57,6 +61,14 @@ module RuboCop
57
61
  matches
58
62
  end
59
63
 
64
+ def match_qualified_names(string)
65
+ matches = []
66
+ string.scan(qualified_name_regexp) do
67
+ matches << $LAST_MATCH_INFO
68
+ end
69
+ matches
70
+ end
71
+
60
72
  def indentation_difference(line)
61
73
  return 0 unless tab_indentation_width
62
74
 
@@ -70,7 +82,7 @@ module RuboCop
70
82
  index * (tab_indentation_width - 1)
71
83
  end
72
84
 
73
- def extend_uri_end_position(line, end_position)
85
+ def extend_end_position(line, end_position)
74
86
  # Extend the end position YARD comments with linked URLs of the form {<uri> <title>}
75
87
  if line&.match(/{(\s|\S)*}$/)
76
88
  match = line[end_position..line_length(line)]&.match(/(\s|\S)*}/)
@@ -101,6 +113,10 @@ module RuboCop
101
113
  end
102
114
  end
103
115
 
116
+ def qualified_name_regexp
117
+ /\b(?:[A-Z][A-Za-z0-9_]*::)+[A-Za-z_][A-Za-z0-9_]*\b/
118
+ end
119
+
104
120
  def valid_uri?(uri_ish_string)
105
121
  URI.parse(uri_ish_string)
106
122
  true
@@ -152,7 +152,7 @@ module RuboCop
152
152
 
153
153
  const_namespace, const_name = *const
154
154
  next if name != const_name && !match_acronym?(name, const_name)
155
- next unless namespace.empty? || match_namespace(child, const_namespace, namespace)
155
+ next unless namespace.empty? || namespace_matches?(child, const_namespace, namespace)
156
156
 
157
157
  return node
158
158
  end
@@ -169,7 +169,7 @@ module RuboCop
169
169
  s(:const, namespace, name) if name
170
170
  end
171
171
 
172
- def match_namespace(node, namespace, expected)
172
+ def namespace_matches?(node, namespace, expected)
173
173
  match_partial = partial_matcher!(expected)
174
174
 
175
175
  match_partial.call(namespace)
@@ -6,12 +6,13 @@ module RuboCop
6
6
  # Checks that predicate methods end with `?` and non-predicate methods do not.
7
7
  #
8
8
  # The names of predicate methods (methods that return a boolean value) should end
9
- # in a question mark. Methods that dont return a boolean, shouldnt
9
+ # in a question mark. Methods that don't return a boolean, shouldn't
10
10
  # end in a question mark.
11
11
  #
12
12
  # The cop assesses a predicate method as one that returns boolean values. Likewise,
13
- # a method that only returns literal values is assessed as non-predicate. The cop does
14
- # not make an assessment if the return type is unknown (method calls, variables, etc.).
13
+ # a method that only returns literal values is assessed as non-predicate. Other predicate
14
+ # method calls are assumed to return boolean values. The cop does not make an assessment
15
+ # if the return type is unknown (non-predicate method calls, variables, etc.).
15
16
  #
16
17
  # NOTE: Operator methods (`def ==`, etc.) are ignored.
17
18
  #
@@ -22,7 +23,11 @@ module RuboCop
22
23
  #
23
24
  # The cop also has `AllowedMethods` configuration in order to prevent the cop from
24
25
  # registering an offense from a method name that does not confirm to the naming
25
- # guidelines. By default, `call` is allowed.
26
+ # guidelines. By default, `call` is allowed. The cop also has `AllowedPatterns`
27
+ # configuration to allow method names by regular expression.
28
+ #
29
+ # The cop can furthermore be configured to allow all bang methods (method names
30
+ # ending with `!`), with `AllowBangMethods: true` (default false).
26
31
  #
27
32
  # @example Mode: conservative (default)
28
33
  # # bad
@@ -45,6 +50,36 @@ module RuboCop
45
50
  # 5
46
51
  # end
47
52
  #
53
+ # # bad
54
+ # def foo
55
+ # x == y
56
+ # end
57
+ #
58
+ # # good
59
+ # def foo?
60
+ # x == y
61
+ # end
62
+ #
63
+ # # bad
64
+ # def foo
65
+ # !x
66
+ # end
67
+ #
68
+ # # good
69
+ # def foo?
70
+ # !x
71
+ # end
72
+ #
73
+ # # bad - returns the value of another predicate method
74
+ # def foo
75
+ # bar?
76
+ # end
77
+ #
78
+ # # good
79
+ # def foo?
80
+ # bar?
81
+ # end
82
+ #
48
83
  # # good - operator method
49
84
  # def ==(other)
50
85
  # hash == other.hash
@@ -73,8 +108,21 @@ module RuboCop
73
108
  # true
74
109
  # end
75
110
  #
111
+ # @example AllowBangMethods: false (default)
112
+ # # bad
113
+ # def save!
114
+ # true
115
+ # end
116
+ #
117
+ # @example AllowBangMethods: true
118
+ # # good
119
+ # def save!
120
+ # true
121
+ # end
122
+ #
76
123
  class PredicateMethod < Base
77
124
  include AllowedMethods
125
+ include AllowedPattern
78
126
 
79
127
  MSG_PREDICATE = 'Predicate method names should end with `?`.'
80
128
  MSG_NON_PREDICATE = 'Non-predicate method names should not end with `?`.'
@@ -97,6 +145,8 @@ module RuboCop
97
145
 
98
146
  def allowed?(node)
99
147
  allowed_method?(node.method_name) ||
148
+ matches_allowed_pattern?(node.method_name) ||
149
+ allowed_bang_method?(node) ||
100
150
  node.operator_method? ||
101
151
  node.body.nil?
102
152
  end
@@ -107,12 +157,14 @@ module RuboCop
107
157
  return false unless conservative?
108
158
 
109
159
  return_values.any? do |value|
110
- value.type?(:super, :zsuper) || non_comparison_call?(value)
160
+ value.type?(:super, :zsuper) || unknown_method_call?(value)
111
161
  end
112
162
  end
113
163
 
114
- def non_comparison_call?(value)
115
- value.call_type? && !value.comparison_method?
164
+ def unknown_method_call?(value)
165
+ return false unless value.call_type?
166
+
167
+ !value.comparison_method? && !value.predicate_method? && !value.negation_method?
116
168
  end
117
169
 
118
170
  def return_values(node)
@@ -137,7 +189,10 @@ module RuboCop
137
189
  end
138
190
 
139
191
  def boolean_return?(value)
140
- value.boolean_type? || (value.call_type? && value.comparison_method?)
192
+ return true if value.boolean_type?
193
+ return false unless value.call_type?
194
+
195
+ value.comparison_method? || value.predicate_method? || value.negation_method?
141
196
  end
142
197
 
143
198
  def potential_non_predicate?(return_values)
@@ -210,6 +265,16 @@ module RuboCop
210
265
  def conservative?
211
266
  cop_config.fetch('Mode', :conservative).to_sym == :conservative
212
267
  end
268
+
269
+ def allowed_bang_method?(node)
270
+ return false unless allow_bang_methods?
271
+
272
+ node.bang_method?
273
+ end
274
+
275
+ def allow_bang_methods?
276
+ cop_config.fetch('AllowBangMethods', false)
277
+ end
213
278
  end
214
279
  end
215
280
  end
@@ -105,7 +105,7 @@ module RuboCop
105
105
 
106
106
  # @!method dynamic_method_define(node)
107
107
  def_node_matcher :dynamic_method_define, <<~PATTERN
108
- (send nil? #method_definition_macros
108
+ (send nil? #method_definition_macro?
109
109
  (sym $_)
110
110
  ...)
111
111
  PATTERN
@@ -195,7 +195,7 @@ module RuboCop
195
195
  cop_config['UseSorbetSigs']
196
196
  end
197
197
 
198
- def method_definition_macros(macro_name)
198
+ def method_definition_macro?(macro_name)
199
199
  cop_config['MethodDefinitionMacros'].include?(macro_name.to_s)
200
200
  end
201
201
  end
@@ -269,7 +269,7 @@ module RuboCop
269
269
  end
270
270
 
271
271
  def regexp_with_named_captures?(node)
272
- node.regexp_type? && node.each_capture(named: true).count.positive?
272
+ node.regexp_type? && node.each_capture(named: true).any?
273
273
  end
274
274
  end
275
275
  end
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Prefer `Enumerable` predicate methods over expressions with `count`.
7
+ #
8
+ # The cop checks calls to `count` without arguments, or with a
9
+ # block. It doesn't register offenses for `count` with a positional
10
+ # argument because its behavior differs from predicate methods (`count`
11
+ # matches the argument using `==`, while `any?`, `none?` and `one?` use
12
+ # `===`).
13
+ #
14
+ # NOTE: This cop doesn't check `length` and `size` methods because they
15
+ # would yield false positives. For example, `String` implements `length`
16
+ # and `size`, but it doesn't include `Enumerable`.
17
+ #
18
+ # @safety
19
+ # The cop is unsafe because receiver might not include `Enumerable`, or
20
+ # it has nonstandard implementation of `count` or any replacement
21
+ # methods.
22
+ #
23
+ # It's also unsafe because for collections with falsey values, expressions
24
+ # with `count` without a block return a different result than methods `any?`,
25
+ # `none?` and `one?`:
26
+ #
27
+ # [source,ruby]
28
+ # ----
29
+ # [nil, false].count.positive?
30
+ # [nil].count == 1
31
+ # # => true
32
+ #
33
+ # [nil, false].any?
34
+ # [nil].one?
35
+ # # => false
36
+ #
37
+ # [nil].count == 0
38
+ # # => false
39
+ #
40
+ # [nil].none?
41
+ # # => true
42
+ # ----
43
+ #
44
+ # Autocorrection is unsafe when replacement methods don't iterate over
45
+ # every element in collection and the given block runs side effects:
46
+ #
47
+ # [source,ruby]
48
+ # ----
49
+ # x.count(&:method_with_side_effects).positive?
50
+ # # calls `method_with_side_effects` on every element
51
+ #
52
+ # x.any?(&:method_with_side_effects)
53
+ # # calls `method_with_side_effects` until first element returns a truthy value
54
+ # ----
55
+ #
56
+ # @example
57
+ #
58
+ # # bad
59
+ # x.count.positive?
60
+ # x.count > 0
61
+ # x.count != 0
62
+ #
63
+ # x.count(&:foo?).positive?
64
+ # x.count { |item| item.foo? }.positive?
65
+ #
66
+ # # good
67
+ # x.any?
68
+ #
69
+ # x.any?(&:foo?)
70
+ # x.any? { |item| item.foo? }
71
+ #
72
+ # # bad
73
+ # x.count.zero?
74
+ # x.count == 0
75
+ #
76
+ # # good
77
+ # x.none?
78
+ #
79
+ # # bad
80
+ # x.count == 1
81
+ # x.one?
82
+ #
83
+ # @example AllCops:ActiveSupportExtensionsEnabled: false (default)
84
+ #
85
+ # # good
86
+ # x.count > 1
87
+ #
88
+ # @example AllCops:ActiveSupportExtensionsEnabled: true
89
+ #
90
+ # # bad
91
+ # x.count > 1
92
+ #
93
+ # # good
94
+ # x.many?
95
+ #
96
+ class CollectionQuerying < Base
97
+ include RangeHelp
98
+ extend AutoCorrector
99
+
100
+ MSG = 'Use `%<prefer>s` instead.'
101
+
102
+ RESTRICT_ON_SEND = %i[positive? > != zero? ==].freeze
103
+
104
+ REPLACEMENTS = {
105
+ [:positive?, nil] => :any?,
106
+ [:>, 0] => :any?,
107
+ [:!=, 0] => :any?,
108
+ [:zero?, nil] => :none?,
109
+ [:==, 0] => :none?,
110
+ [:==, 1] => :one?,
111
+ [:>, 1] => :many?
112
+ }.freeze
113
+
114
+ # @!method count_predicate(node)
115
+ def_node_matcher :count_predicate, <<~PATTERN
116
+ (send
117
+ {
118
+ (any_block $(call !nil? :count) _ _)
119
+ $(call !nil? :count (block-pass _)?)
120
+ }
121
+ {
122
+ :positive? |
123
+ :> (int 0) |
124
+ :!= (int 0) |
125
+ :zero? |
126
+ :== (int 0) |
127
+ :== (int 1) |
128
+ :> (int 1)
129
+ })
130
+ PATTERN
131
+
132
+ def on_send(node)
133
+ return unless (count_node = count_predicate(node))
134
+
135
+ replacement_method = replacement_method(node)
136
+
137
+ return unless replacement_supported?(replacement_method)
138
+
139
+ offense_range = count_node.loc.selector.join(node.source_range.end)
140
+ add_offense(offense_range,
141
+ message: format(MSG, prefer: replacement_method)) do |corrector|
142
+ corrector.replace(count_node.loc.selector, replacement_method)
143
+ corrector.remove(removal_range(node))
144
+ end
145
+ end
146
+
147
+ private
148
+
149
+ def replacement_method(node)
150
+ REPLACEMENTS.fetch([node.method_name, node.first_argument&.value])
151
+ end
152
+
153
+ def replacement_supported?(method_name)
154
+ return true if active_support_extensions_enabled?
155
+
156
+ method_name != :many?
157
+ end
158
+
159
+ def removal_range(node)
160
+ range = (node.loc.dot || node.loc.selector).join(node.source_range.end)
161
+
162
+ range_with_surrounding_space(range, side: :left)
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
@@ -451,7 +451,9 @@ module RuboCop
451
451
  corrector.remove_preceding(condition.loc.else, condition.loc.else.column - column)
452
452
  end
453
453
 
454
- return unless condition.loc.end && !same_line?(condition.loc.end, condition)
454
+ return unless condition.loc.end && !same_line?(
455
+ condition.branches.last.parent.else_branch, condition.loc.end
456
+ )
455
457
 
456
458
  corrector.remove_preceding(condition.loc.end, condition.loc.end.column - column)
457
459
  end
@@ -87,7 +87,7 @@ module RuboCop
87
87
  true
88
88
  end
89
89
 
90
- def integral(node)
90
+ def integral?(node)
91
91
  mantissa, = node.source.split('e')
92
92
  /^-?[1-9](\d*[1-9])?$/.match?(mantissa)
93
93
  end
@@ -101,7 +101,7 @@ module RuboCop
101
101
  when :engineering
102
102
  !engineering?(node)
103
103
  when :integral
104
- !integral(node)
104
+ !integral?(node)
105
105
  else
106
106
  false
107
107
  end