rubocop-performance 1.14.3 → 1.16.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 (32) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/config/default.yml +4 -0
  4. data/lib/rubocop/cop/mixin/sort_block.rb +7 -0
  5. data/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb +1 -5
  6. data/lib/rubocop/cop/performance/bind_call.rb +1 -2
  7. data/lib/rubocop/cop/performance/case_when_splat.rb +6 -12
  8. data/lib/rubocop/cop/performance/chain_array_allocation.rb +1 -1
  9. data/lib/rubocop/cop/performance/collection_literal_in_loop.rb +1 -3
  10. data/lib/rubocop/cop/performance/constant_regexp.rb +1 -3
  11. data/lib/rubocop/cop/performance/count.rb +38 -2
  12. data/lib/rubocop/cop/performance/detect.rb +6 -3
  13. data/lib/rubocop/cop/performance/double_start_end_with.rb +1 -2
  14. data/lib/rubocop/cop/performance/end_with.rb +1 -2
  15. data/lib/rubocop/cop/performance/inefficient_hash_search.rb +1 -3
  16. data/lib/rubocop/cop/performance/map_compact.rb +5 -1
  17. data/lib/rubocop/cop/performance/open_struct.rb +1 -2
  18. data/lib/rubocop/cop/performance/redundant_match.rb +8 -7
  19. data/lib/rubocop/cop/performance/redundant_merge.rb +4 -10
  20. data/lib/rubocop/cop/performance/redundant_sort_block.rb +16 -8
  21. data/lib/rubocop/cop/performance/redundant_string_chars.rb +7 -3
  22. data/lib/rubocop/cop/performance/regexp_match.rb +4 -12
  23. data/lib/rubocop/cop/performance/sort_reverse.rb +18 -9
  24. data/lib/rubocop/cop/performance/squeeze.rb +1 -4
  25. data/lib/rubocop/cop/performance/start_with.rb +1 -2
  26. data/lib/rubocop/cop/performance/string_include.rb +17 -14
  27. data/lib/rubocop/cop/performance/string_replacement.rb +3 -6
  28. data/lib/rubocop/cop/performance/sum.rb +3 -0
  29. data/lib/rubocop/cop/performance/times_map.rb +4 -5
  30. data/lib/rubocop/cop/performance/uri_default_parser.rb +1 -2
  31. data/lib/rubocop/performance/version.rb +1 -1
  32. metadata +4 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7716dc36d8facd9d8604d89d117f85db5fed60efcad66ad8354d673492800140
4
- data.tar.gz: 59f4dd4e4df5afef36700a8951e8e91e18567ba978506d9aa02884abafc6d639
3
+ metadata.gz: 7b0361feae763e4c8cbea3bd0160a1e4b0646b2e3b3246fb8dd55a5f21a53acd
4
+ data.tar.gz: 152d09678555d309441fb809f60b07b69b04289b3949bf1eaca3e9b70b3afe13
5
5
  SHA512:
6
- metadata.gz: 748977ac053ddab36afda43c709eee6424957a81ce18daf1048cf42399226e0d2d67f9964ba544594a03862d5650eff369a34b407d49892d72957ab3acf2ca11
7
- data.tar.gz: 9a785cd79f0e781643720fd19e2454d6b1cac890c215d5a44762e4173d69e9f6ec6adc57fe43646dba7b3e48bf86663842f595f54db1f51c7e251af75113ecee
6
+ metadata.gz: 9f08bd2f25c0e0e537bf8fa5378e701d6b6b6794a45fe534229e73a0dd4bd623dd415965966655a802cc8e93a3776880d99c89245947aae090ba2c49f0bb183f
7
+ data.tar.gz: 34f697ee70070d32d17b3684712cec5628e71cbdb393722fec3bb0dbc541cf65af92a762907dbbb0c5e6c3023f8dd4738e06a131a5a28014749dac440e15bc92
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012-22 Bozhidar Batsov
1
+ Copyright (c) 2012-23 Bozhidar Batsov
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/config/default.yml CHANGED
@@ -1,5 +1,9 @@
1
1
  # This is the default configuration file.
2
2
 
3
+ Performance:
4
+ Enabled: true
5
+ DocumentationBaseURL: https://docs.rubocop.org/rubocop-performance
6
+
3
7
  Performance/AncestorsInclude:
4
8
  Description: 'Use `A <= B` instead of `A.ancestors.include?(B)`.'
5
9
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#ancestorsinclude-vs--code'
@@ -14,6 +14,13 @@ module RuboCop
14
14
  $send)
15
15
  PATTERN
16
16
 
17
+ def_node_matcher :sort_with_numblock?, <<~PATTERN
18
+ (numblock
19
+ $(send _ :sort)
20
+ $_arg_count
21
+ $send)
22
+ PATTERN
23
+
17
24
  def_node_matcher :replaceable_body?, <<~PATTERN
18
25
  (send (lvar %1) :<=> (lvar %2))
19
26
  PATTERN
@@ -41,11 +41,7 @@ module RuboCop
41
41
  end
42
42
  elsif (numeric_to_d = to_d?(node))
43
43
  add_offense(numeric_to_d.source_range) do |corrector|
44
- big_decimal_args = node
45
- .arguments
46
- .map(&:source)
47
- .unshift("'#{numeric_to_d.source}'")
48
- .join(', ')
44
+ big_decimal_args = node.arguments.map(&:source).unshift("'#{numeric_to_d.source}'").join(', ')
49
45
 
50
46
  corrector.replace(node, "BigDecimal(#{big_decimal_args})")
51
47
  end
@@ -26,8 +26,7 @@ module RuboCop
26
26
 
27
27
  minimum_target_ruby_version 2.7
28
28
 
29
- MSG = 'Use `bind_call(%<bind_arg>s%<comma>s%<call_args>s)` ' \
30
- 'instead of `bind(%<bind_arg>s).call(%<call_args>s)`.'
29
+ MSG = 'Use `bind_call(%<bind_arg>s%<comma>s%<call_args>s)` instead of `bind(%<bind_arg>s).call(%<call_args>s)`.'
31
30
  RESTRICT_ON_SEND = %i[call].freeze
32
31
 
33
32
  def_node_matcher :bind_with_call_method?, <<~PATTERN
@@ -99,8 +99,7 @@ module RuboCop
99
99
 
100
100
  def inline_fix_branch(corrector, when_node)
101
101
  conditions = when_node.conditions
102
- range = range_between(conditions[0].loc.expression.begin_pos,
103
- conditions[-1].loc.expression.end_pos)
102
+ range = range_between(conditions[0].loc.expression.begin_pos, conditions[-1].loc.expression.end_pos)
104
103
 
105
104
  corrector.replace(range, replacement(conditions))
106
105
  end
@@ -111,8 +110,7 @@ module RuboCop
111
110
  return if when_branches.one?
112
111
 
113
112
  corrector.remove(when_branch_range(when_node))
114
- corrector.insert_after(when_branches.last.source_range,
115
- reordering_correction(when_node))
113
+ corrector.insert_after(when_branches.last.source_range, reordering_correction(when_node))
116
114
  end
117
115
 
118
116
  def reordering_correction(when_node)
@@ -126,11 +124,9 @@ module RuboCop
126
124
  end
127
125
 
128
126
  def when_branch_range(when_node)
129
- next_branch =
130
- when_node.parent.when_branches[when_node.branch_index + 1]
127
+ next_branch = when_node.parent.when_branches[when_node.branch_index + 1]
131
128
 
132
- range_between(when_node.source_range.begin_pos,
133
- next_branch.source_range.begin_pos)
129
+ range_between(when_node.source_range.begin_pos, next_branch.source_range.begin_pos)
134
130
  end
135
131
 
136
132
  def new_condition_with_then(node, new_condition)
@@ -162,13 +158,11 @@ module RuboCop
162
158
  def non_splat?(condition)
163
159
  variable, = *condition
164
160
 
165
- (condition.splat_type? && variable.array_type?) ||
166
- !condition.splat_type?
161
+ (condition.splat_type? && variable.array_type?) || !condition.splat_type?
167
162
  end
168
163
 
169
164
  def needs_reorder?(when_node)
170
- following_branches =
171
- when_node.parent.when_branches[(when_node.branch_index + 1)..]
165
+ following_branches = when_node.parent.when_branches[(when_node.branch_index + 1)..]
172
166
 
173
167
  following_branches.any? do |when_branch|
174
168
  when_branch.conditions.any? do |condition|
@@ -33,7 +33,7 @@ module RuboCop
33
33
  RETURNS_NEW_ARRAY_WHEN_NO_BLOCK = %i[zip product].to_set.freeze
34
34
 
35
35
  # These methods ALWAYS return a new array
36
- # after they're called it's safe to mutate the the resulting array
36
+ # after they're called it's safe to mutate the resulting array
37
37
  ALWAYS_RETURNS_NEW_ARRAY = %i[* + - collect compact drop
38
38
  drop_while flatten map reject
39
39
  reverse rotate select shuffle sort
@@ -104,9 +104,7 @@ module RuboCop
104
104
  end
105
105
 
106
106
  def loop?(ancestor, node)
107
- keyword_loop?(ancestor.type) ||
108
- kernel_loop?(ancestor) ||
109
- node_within_enumerable_loop?(node, ancestor)
107
+ keyword_loop?(ancestor.type) || kernel_loop?(ancestor) || node_within_enumerable_loop?(node, ancestor)
110
108
  end
111
109
 
112
110
  def keyword_loop?(type)
@@ -39,9 +39,7 @@ module RuboCop
39
39
  MSG = 'Extract this regexp into a constant, memoize it, or append an `/o` option to its options.'
40
40
 
41
41
  def on_regexp(node)
42
- return if within_allowed_assignment?(node) ||
43
- !include_interpolated_const?(node) ||
44
- node.single_interpolation?
42
+ return if within_allowed_assignment?(node) || !include_interpolated_const?(node) || node.single_interpolation?
45
43
 
46
44
  add_offense(node) do |corrector|
47
45
  corrector.insert_after(node, 'o')
@@ -79,12 +79,11 @@ module RuboCop
79
79
  def autocorrect(corrector, node, selector_node, selector)
80
80
  selector_loc = selector_node.loc.selector
81
81
 
82
- return if selector == :reject
83
-
84
82
  range = source_starting_at(node) { |n| n.loc.dot.begin_pos }
85
83
 
86
84
  corrector.remove(range)
87
85
  corrector.replace(selector_loc, 'count')
86
+ negate_reject(corrector, node) if selector == :reject
88
87
  end
89
88
 
90
89
  def eligible_node?(node)
@@ -100,6 +99,43 @@ module RuboCop
100
99
 
101
100
  range_between(begin_pos, node.source_range.end_pos)
102
101
  end
102
+
103
+ def negate_reject(corrector, node)
104
+ if node.receiver.send_type?
105
+ negate_block_pass_reject(corrector, node)
106
+ else
107
+ negate_block_reject(corrector, node)
108
+ end
109
+ end
110
+
111
+ def negate_block_pass_reject(corrector, node)
112
+ corrector.replace(
113
+ node.receiver.loc.expression.with(begin_pos: node.receiver.loc.begin.begin_pos),
114
+ negate_block_pass_as_inline_block(node.receiver)
115
+ )
116
+ end
117
+
118
+ def negate_block_reject(corrector, node)
119
+ target =
120
+ if node.receiver.body.begin_type?
121
+ node.receiver.body.children.last
122
+ else
123
+ node.receiver.body
124
+ end
125
+ corrector.replace(target, negate_expression(target))
126
+ end
127
+
128
+ def negate_expression(node)
129
+ "!(#{node.source})"
130
+ end
131
+
132
+ def negate_block_pass_as_inline_block(node)
133
+ if node.last_argument.children.first.sym_type?
134
+ " { |element| !element.#{node.last_argument.children.first.value} }"
135
+ else
136
+ " { !#{node.last_argument.children.first.source}.call }"
137
+ end
138
+ end
103
139
  end
104
140
  end
105
141
  end
@@ -8,9 +8,12 @@ module RuboCop
8
8
  # `detect` instead.
9
9
  #
10
10
  # @safety
11
- # This cop is unsafe because is has known compatibility issues with `ActiveRecord` and other
12
- # frameworks. `ActiveRecord` does not implement a `detect` method and `find` has its own
13
- # meaning. Correcting `ActiveRecord` methods with this cop should be considered unsafe.
11
+ # This cop is unsafe because is assumes the class implements the
12
+ # `Enumerable` interface, but can't reliably detect this. This creates
13
+ # known compatibility issues with `Hash`, `ActiveRecord` and other
14
+ # frameworks. `Hash` and `ActiveRecord` do not implement a `detect`
15
+ # method and `find` has its own meaning. Correcting `Hash` and
16
+ # `ActiveRecord` methods with this cop should be considered unsafe.
14
17
  #
15
18
  # @example
16
19
  # # bad
@@ -41,8 +41,7 @@ module RuboCop
41
41
  class DoubleStartEndWith < Base
42
42
  extend AutoCorrector
43
43
 
44
- MSG = 'Use `%<receiver>s.%<method>s(%<combined_args>s)` ' \
45
- 'instead of `%<original_code>s`.'
44
+ MSG = 'Use `%<receiver>s.%<method>s(%<combined_args>s)` instead of `%<original_code>s`.'
46
45
 
47
46
  def on_or(node)
48
47
  receiver, method, first_call_args, second_call_args = process_source(node)
@@ -50,8 +50,7 @@ module RuboCop
50
50
  include RegexpMetacharacter
51
51
  extend AutoCorrector
52
52
 
53
- MSG = 'Use `String#end_with?` instead of a regex match anchored to ' \
54
- 'the end of the string.'
53
+ MSG = 'Use `String#end_with?` instead of a regex match anchored to the end of the string.'
55
54
  RESTRICT_ON_SEND = %i[match =~ match?].freeze
56
55
 
57
56
  def_node_matcher :redundant_regex?, <<~PATTERN
@@ -83,9 +83,7 @@ module RuboCop
83
83
 
84
84
  def use_long_method
85
85
  preferred_config = config.for_all_cops['Style/PreferredHashMethods']
86
- preferred_config &&
87
- preferred_config['EnforcedStyle'] == 'long' &&
88
- preferred_config['Enabled']
86
+ preferred_config && preferred_config['EnforcedStyle'] == 'long' && preferred_config['Enabled']
89
87
  end
90
88
 
91
89
  def autocorrect_argument(node)
@@ -67,7 +67,7 @@ module RuboCop
67
67
  def remove_compact_method(corrector, map_node, compact_node, chained_method)
68
68
  compact_method_range = compact_node.loc.selector
69
69
 
70
- if compact_node.multiline? && chained_method&.loc.respond_to?(:selector) && chained_method.dot? &&
70
+ if compact_node.multiline? && chained_method&.loc.respond_to?(:selector) && use_dot?(chained_method) &&
71
71
  !map_method_and_compact_method_on_same_line?(map_node, compact_node) &&
72
72
  !invoke_method_after_map_compact_on_same_line?(compact_node, chained_method)
73
73
  compact_method_range = compact_method_with_final_newline_range(compact_method_range)
@@ -78,6 +78,10 @@ module RuboCop
78
78
  corrector.remove(compact_method_range)
79
79
  end
80
80
 
81
+ def use_dot?(node)
82
+ node.respond_to?(:dot?) && node.dot?
83
+ end
84
+
81
85
  def map_method_and_compact_method_on_same_line?(map_node, compact_node)
82
86
  compact_node.loc.selector.line == map_node.loc.selector.line
83
87
  end
@@ -32,8 +32,7 @@ module RuboCop
32
32
  # end
33
33
  #
34
34
  class OpenStruct < Base
35
- MSG = 'Consider using `Struct` over `OpenStruct` ' \
36
- 'to optimize the performance.'
35
+ MSG = 'Consider using `Struct` over `OpenStruct` to optimize the performance.'
37
36
  RESTRICT_ON_SEND = %i[new].freeze
38
37
 
39
38
  def_node_matcher :open_struct, <<~PATTERN
@@ -20,8 +20,7 @@ module RuboCop
20
20
  class RedundantMatch < Base
21
21
  extend AutoCorrector
22
22
 
23
- MSG = 'Use `=~` in places where the `MatchData` returned by ' \
24
- '`#match` will not be used.'
23
+ MSG = 'Use `=~` in places where the `MatchData` returned by `#match` will not be used.'
25
24
  RESTRICT_ON_SEND = %i[match].freeze
26
25
 
27
26
  # 'match' is a fairly generic name, so we don't flag it unless we see
@@ -41,21 +40,23 @@ module RuboCop
41
40
  !(node.parent && node.parent.block_type?)
42
41
 
43
42
  add_offense(node) do |corrector|
44
- autocorrect(corrector, node)
43
+ autocorrect(corrector, node) if autocorrectable?(node)
45
44
  end
46
45
  end
47
46
 
48
47
  private
49
48
 
50
49
  def autocorrect(corrector, node)
51
- # Regexp#match can take a second argument, but this cop doesn't
52
- # register an offense in that case
53
- return unless node.first_argument.regexp_type?
54
-
55
50
  new_source = "#{node.receiver.source} =~ #{node.first_argument.source}"
56
51
 
57
52
  corrector.replace(node.source_range, new_source)
58
53
  end
54
+
55
+ def autocorrectable?(node)
56
+ # Regexp#match can take a second argument, but this cop doesn't
57
+ # register an offense in that case
58
+ node.receiver.regexp_type? || node.first_argument.regexp_type?
59
+ end
59
60
  end
60
61
  end
61
62
  end
@@ -28,6 +28,7 @@ module RuboCop
28
28
  # hash[:a] = 1
29
29
  # hash[:b] = 2
30
30
  class RedundantMerge < Base
31
+ include Alignment
31
32
  extend AutoCorrector
32
33
 
33
34
  AREF_ASGN = '%<receiver>s[%<key>s] = %<value>s'
@@ -99,8 +100,7 @@ module RuboCop
99
100
  end
100
101
 
101
102
  def non_redundant_value_used?(receiver, node)
102
- node.value_used? &&
103
- !EachWithObjectInspector.new(node, receiver).value_used?
103
+ node.value_used? && !EachWithObjectInspector.new(node, receiver).value_used?
104
104
  end
105
105
 
106
106
  def correct_multiple_elements(corrector, node, parent, new_source)
@@ -125,14 +125,12 @@ module RuboCop
125
125
 
126
126
  key = key.sym_type? && pair.colon? ? ":#{key.source}" : key.source
127
127
 
128
- format(AREF_ASGN, receiver: receiver.source,
129
- key: key,
130
- value: value.source)
128
+ format(AREF_ASGN, receiver: receiver.source, key: key, value: value.source)
131
129
  end
132
130
  end
133
131
 
134
132
  def rewrite_with_modifier(node, parent, new_source)
135
- indent = ' ' * indent_width
133
+ indent = ' ' * configured_indentation_width
136
134
  padding = "\n#{indent + leading_spaces(node)}"
137
135
  new_source.gsub!(/\n/, padding)
138
136
 
@@ -147,10 +145,6 @@ module RuboCop
147
145
  node.source_range.source_line[/\A\s*/]
148
146
  end
149
147
 
150
- def indent_width
151
- @config.for_cop('Layout/IndentationWidth')['Width'] || 2
152
- end
153
-
154
148
  def max_key_value_pairs
155
149
  Integer(cop_config['MaxKeyValuePairs'] || 2)
156
150
  end
@@ -16,25 +16,33 @@ module RuboCop
16
16
  include SortBlock
17
17
  extend AutoCorrector
18
18
 
19
- MSG = 'Use `sort` instead of `%<bad_method>s`.'
19
+ MSG = 'Use `sort` without block.'
20
20
 
21
21
  def on_block(node)
22
22
  return unless (send, var_a, var_b, body = sort_with_block?(node))
23
23
 
24
24
  replaceable_body?(body, var_a, var_b) do
25
- range = sort_range(send, node)
25
+ register_offense(send, node)
26
+ end
27
+ end
28
+
29
+ def on_numblock(node)
30
+ return unless (send, arg_count, body = sort_with_numblock?(node))
31
+ return unless arg_count == 2
26
32
 
27
- add_offense(range, message: message(var_a, var_b)) do |corrector|
28
- corrector.replace(range, 'sort')
29
- end
33
+ replaceable_body?(body, :_1, :_2) do
34
+ register_offense(send, node)
30
35
  end
31
36
  end
32
37
 
33
38
  private
34
39
 
35
- def message(var_a, var_b)
36
- bad_method = "sort { |#{var_a}, #{var_b}| #{var_a} <=> #{var_b} }"
37
- format(MSG, bad_method: bad_method)
40
+ def register_offense(send, node)
41
+ range = sort_range(send, node)
42
+
43
+ add_offense(range) do |corrector|
44
+ corrector.replace(range, 'sort')
45
+ end
38
46
  end
39
47
  end
40
48
  end
@@ -9,6 +9,7 @@ module RuboCop
9
9
  # # bad
10
10
  # str.chars[0..2]
11
11
  # str.chars.slice(0..2)
12
+ # str.chars.last
12
13
  #
13
14
  # # good
14
15
  # str[0..2].chars
@@ -20,6 +21,7 @@ module RuboCop
20
21
  # # good
21
22
  # str[0]
22
23
  # str[0...2].chars
24
+ # str[-1]
23
25
  #
24
26
  # # bad
25
27
  # str.chars.take(2)
@@ -33,9 +35,8 @@ module RuboCop
33
35
  # str.size
34
36
  # str.empty?
35
37
  #
36
- # # For example, if the receiver is a blank string, it will be incompatible.
38
+ # # For example, if the receiver is an empty string, it will be incompatible.
37
39
  # # If a negative value is specified for the receiver, `nil` is returned.
38
- # str.chars.last # Incompatible with `str[-1]`.
39
40
  # str.chars.last(2) # Incompatible with `str[-2..-1].chars`.
40
41
  # str.chars.drop(2) # Incompatible with `str[2..-1].chars`.
41
42
  #
@@ -44,7 +45,7 @@ module RuboCop
44
45
  extend AutoCorrector
45
46
 
46
47
  MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'
47
- RESTRICT_ON_SEND = %i[[] slice first take length size empty?].freeze
48
+ RESTRICT_ON_SEND = %i[[] slice first last take length size empty?].freeze
48
49
 
49
50
  def_node_matcher :redundant_chars_call?, <<~PATTERN
50
51
  (send $(send _ :chars) $_ $...)
@@ -52,6 +53,7 @@ module RuboCop
52
53
 
53
54
  def on_send(node)
54
55
  return unless (receiver, method, args = redundant_chars_call?(node))
56
+ return if method == :last && !args.empty?
55
57
 
56
58
  range = offense_range(receiver, node)
57
59
  message = build_message(method, args)
@@ -86,6 +88,8 @@ module RuboCop
86
88
  "[#{build_call_args(args)}].chars"
87
89
  when :[], :first
88
90
  build_good_method_for_brackets_or_first_method(method, args)
91
+ when :last
92
+ '[-1]'
89
93
  when :take
90
94
  "[0...#{args.first.source}].chars"
91
95
  else
@@ -124,8 +124,8 @@ module RuboCop
124
124
 
125
125
  def_node_search :last_matches, <<~PATTERN
126
126
  {
127
- (send (const nil? :Regexp) :last_match)
128
- (send (const nil? :Regexp) :last_match _)
127
+ (send (const {nil? cbase} :Regexp) :last_match)
128
+ (send (const {nil? cbase} :Regexp) :last_match _)
129
129
  ({back_ref nth_ref} _)
130
130
  (gvar #match_gvar?)
131
131
  }
@@ -217,8 +217,7 @@ module RuboCop
217
217
  def find_last_match(body, range, scope_root)
218
218
  last_matches(body).find do |ref|
219
219
  ref_pos = ref.loc.expression.begin_pos
220
- range.cover?(ref_pos) &&
221
- scope_root(ref) == scope_root
220
+ range.cover?(ref_pos) && scope_root(ref) == scope_root
222
221
  end
223
222
  end
224
223
 
@@ -241,14 +240,7 @@ module RuboCop
241
240
  end
242
241
 
243
242
  def match_gvar?(sym)
244
- %i[
245
- $~
246
- $MATCH
247
- $PREMATCH
248
- $POSTMATCH
249
- $LAST_PAREN_MATCH
250
- $LAST_MATCH_INFO
251
- ].include?(sym)
243
+ %i[$~ $MATCH $PREMATCH $POSTMATCH $LAST_PAREN_MATCH $LAST_MATCH_INFO].include?(sym)
252
244
  end
253
245
 
254
246
  def correct_operator(corrector, recv, arg, oper = nil)
@@ -17,27 +17,36 @@ module RuboCop
17
17
  include SortBlock
18
18
  extend AutoCorrector
19
19
 
20
- MSG = 'Use `sort.reverse` instead of `%<bad_method>s`.'
20
+ MSG = 'Use `sort.reverse` instead.'
21
21
 
22
22
  def on_block(node)
23
23
  sort_with_block?(node) do |send, var_a, var_b, body|
24
24
  replaceable_body?(body, var_b, var_a) do
25
- range = sort_range(send, node)
25
+ register_offense(send, node)
26
+ end
27
+ end
28
+ end
26
29
 
27
- add_offense(range, message: message(var_a, var_b)) do |corrector|
28
- replacement = 'sort.reverse'
30
+ def on_numblock(node)
31
+ sort_with_numblock?(node) do |send, arg_count, body|
32
+ next unless arg_count == 2
29
33
 
30
- corrector.replace(range, replacement)
31
- end
34
+ replaceable_body?(body, :_2, :_1) do
35
+ register_offense(send, node)
32
36
  end
33
37
  end
34
38
  end
35
39
 
36
40
  private
37
41
 
38
- def message(var_a, var_b)
39
- bad_method = "sort { |#{var_a}, #{var_b}| #{var_b} <=> #{var_a} }"
40
- format(MSG, bad_method: bad_method)
42
+ def register_offense(send, node)
43
+ range = sort_range(send, node)
44
+
45
+ add_offense(range) do |corrector|
46
+ replacement = 'sort.reverse'
47
+
48
+ corrector.replace(range, replacement)
49
+ end
41
50
  end
42
51
  end
43
52
  end
@@ -24,10 +24,7 @@ module RuboCop
24
24
  MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
25
25
  RESTRICT_ON_SEND = %i[gsub gsub!].freeze
26
26
 
27
- PREFERRED_METHODS = {
28
- gsub: :squeeze,
29
- gsub!: :squeeze!
30
- }.freeze
27
+ PREFERRED_METHODS = { gsub: :squeeze, gsub!: :squeeze! }.freeze
31
28
 
32
29
  def_node_matcher :squeeze_candidate?, <<~PATTERN
33
30
  (send
@@ -50,8 +50,7 @@ module RuboCop
50
50
  include RegexpMetacharacter
51
51
  extend AutoCorrector
52
52
 
53
- MSG = 'Use `String#start_with?` instead of a regex match anchored to ' \
54
- 'the beginning of the string.'
53
+ MSG = 'Use `String#start_with?` instead of a regex match anchored to the beginning of the string.'
55
54
  RESTRICT_ON_SEND = %i[match =~ match?].freeze
56
55
 
57
56
  def_node_matcher :redundant_regex?, <<~PATTERN
@@ -6,39 +6,42 @@ module RuboCop
6
6
  # Identifies unnecessary use of a regex where `String#include?` would suffice.
7
7
  #
8
8
  # @safety
9
- # This cop's offenses are not safe to autocorrect if a receiver is nil.
9
+ # This cop's offenses are not safe to autocorrect if a receiver is nil or a Symbol.
10
10
  #
11
11
  # @example
12
12
  # # bad
13
- # 'abc'.match?(/ab/)
14
- # /ab/.match?('abc')
15
- # 'abc' =~ /ab/
16
- # /ab/ =~ 'abc'
17
- # 'abc'.match(/ab/)
18
- # /ab/.match('abc')
13
+ # str.match?(/ab/)
14
+ # /ab/.match?(str)
15
+ # str =~ /ab/
16
+ # /ab/ =~ str
17
+ # str.match(/ab/)
18
+ # /ab/.match(str)
19
19
  #
20
20
  # # good
21
- # 'abc'.include?('ab')
21
+ # str.include?('ab')
22
22
  class StringInclude < Base
23
23
  extend AutoCorrector
24
24
 
25
- MSG = 'Use `String#include?` instead of a regex match with literal-only pattern.'
26
- RESTRICT_ON_SEND = %i[match =~ match?].freeze
25
+ MSG = 'Use `%<negation>sString#include?` instead of a regex match with literal-only pattern.'
26
+ RESTRICT_ON_SEND = %i[match =~ !~ match?].freeze
27
27
 
28
28
  def_node_matcher :redundant_regex?, <<~PATTERN
29
- {(send $!nil? {:match :=~ :match?} (regexp (str $#literal?) (regopt)))
30
- (send (regexp (str $#literal?) (regopt)) {:match :match?} $str)
29
+ {(send $!nil? {:match :=~ :!~ :match?} (regexp (str $#literal?) (regopt)))
30
+ (send (regexp (str $#literal?) (regopt)) {:match :match?} $_)
31
31
  (match-with-lvasgn (regexp (str $#literal?) (regopt)) $_)}
32
32
  PATTERN
33
33
 
34
34
  def on_send(node)
35
35
  return unless (receiver, regex_str = redundant_regex?(node))
36
36
 
37
- add_offense(node) do |corrector|
37
+ negation = node.send_type? && node.method?(:!~)
38
+ message = format(MSG, negation: ('!' if negation))
39
+
40
+ add_offense(node, message: message) do |corrector|
38
41
  receiver, regex_str = regex_str, receiver if receiver.is_a?(String)
39
42
  regex_str = interpret_string_escapes(regex_str)
40
43
 
41
- new_source = "#{receiver.source}.include?(#{to_string_literal(regex_str)})"
44
+ new_source = "#{'!' if negation}#{receiver.source}.include?(#{to_string_literal(regex_str)})"
42
45
 
43
46
  corrector.replace(node.source_range, new_source)
44
47
  end
@@ -86,8 +86,7 @@ module RuboCop
86
86
 
87
87
  unless first_param.str_type?
88
88
  return true if options
89
- return true unless first_source.is_a?(String) &&
90
- first_source =~ DETERMINISTIC_REGEX
89
+ return true unless first_source.is_a?(String) && first_source =~ DETERMINISTIC_REGEX
91
90
 
92
91
  # This must be done after checking DETERMINISTIC_REGEX
93
92
  # Otherwise things like \s will trip us up
@@ -141,8 +140,7 @@ module RuboCop
141
140
  end
142
141
 
143
142
  def message(node, first_source, second_source)
144
- replacement_method =
145
- replacement_method(node, first_source, second_source)
143
+ replacement_method = replacement_method(node, first_source, second_source)
146
144
 
147
145
  format(MSG, prefer: replacement_method, current: node.method_name)
148
146
  end
@@ -152,8 +150,7 @@ module RuboCop
152
150
  end
153
151
 
154
152
  def remove_second_param(corrector, node, first_param)
155
- end_range = range_between(first_param.source_range.end_pos,
156
- node.source_range.end_pos)
153
+ end_range = range_between(first_param.source_range.end_pos, node.source_range.end_pos)
157
154
 
158
155
  corrector.replace(end_range, method_suffix(node))
159
156
  end
@@ -70,6 +70,9 @@ module RuboCop
70
70
  class Sum < Base
71
71
  include RangeHelp
72
72
  extend AutoCorrector
73
+ extend TargetRubyVersion
74
+
75
+ minimum_target_ruby_version 2.4
73
76
 
74
77
  MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'
75
78
  MSG_IF_NO_INIT_VALUE =
@@ -32,8 +32,7 @@ module RuboCop
32
32
  class TimesMap < Base
33
33
  extend AutoCorrector
34
34
 
35
- MESSAGE = 'Use `Array.new(%<count>s)` with a block ' \
36
- 'instead of `.times.%<map_or_collect>s`'
35
+ MESSAGE = 'Use `Array.new(%<count>s)` with a block instead of `.times.%<map_or_collect>s`'
37
36
  MESSAGE_ONLY_IF = 'only if `%<count>s` is always 0 or more'
38
37
  RESTRICT_ON_SEND = %i[map collect].freeze
39
38
 
@@ -44,14 +43,14 @@ module RuboCop
44
43
  def on_block(node)
45
44
  check(node)
46
45
  end
46
+ alias on_numblock on_block
47
47
 
48
48
  private
49
49
 
50
50
  def check(node)
51
51
  times_map_call(node) do |map_or_collect, count|
52
52
  add_offense(node, message: message(map_or_collect, count)) do |corrector|
53
- replacement = "Array.new(#{count.source}" \
54
- "#{map_or_collect.arguments.map { |arg| ", #{arg.source}" }.join})"
53
+ replacement = "Array.new(#{count.source}#{map_or_collect.arguments.map { |arg| ", #{arg.source}" }.join})"
55
54
 
56
55
  corrector.replace(map_or_collect.loc.expression, replacement)
57
56
  end
@@ -68,7 +67,7 @@ module RuboCop
68
67
  end
69
68
 
70
69
  def_node_matcher :times_map_call, <<~PATTERN
71
- {(block $(send (send $!nil? :times) {:map :collect}) ...)
70
+ {({block numblock} $(send (send $!nil? :times) {:map :collect}) ...)
72
71
  $(send (send $!nil? :times) {:map :collect} (block_pass ...))}
73
72
  PATTERN
74
73
  end
@@ -15,8 +15,7 @@ module RuboCop
15
15
  class UriDefaultParser < Base
16
16
  extend AutoCorrector
17
17
 
18
- MSG = 'Use `%<double_colon>sURI::DEFAULT_PARSER` instead of ' \
19
- '`%<double_colon>sURI::Parser.new`.'
18
+ MSG = 'Use `%<double_colon>sURI::DEFAULT_PARSER` instead of `%<double_colon>sURI::Parser.new`.'
20
19
  RESTRICT_ON_SEND = %i[new].freeze
21
20
 
22
21
  def_node_matcher :uri_parser_new?, <<~PATTERN
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Performance
5
5
  # This module holds the RuboCop Performance version information.
6
6
  module Version
7
- STRING = '1.14.3'
7
+ STRING = '1.16.0'
8
8
 
9
9
  def self.document_version
10
10
  STRING.match('\d+\.\d+').to_s
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-performance
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.14.3
4
+ version: 1.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2022-07-17 00:00:00.000000000 Z
13
+ date: 2023-02-06 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rubocop
@@ -123,7 +123,7 @@ metadata:
123
123
  homepage_uri: https://docs.rubocop.org/rubocop-performance/
124
124
  changelog_uri: https://github.com/rubocop/rubocop-performance/blob/master/CHANGELOG.md
125
125
  source_code_uri: https://github.com/rubocop/rubocop-performance/
126
- documentation_uri: https://docs.rubocop.org/rubocop-performance/1.14/
126
+ documentation_uri: https://docs.rubocop.org/rubocop-performance/1.16/
127
127
  bug_tracker_uri: https://github.com/rubocop/rubocop-performance/issues
128
128
  rubygems_mfa_required: 'true'
129
129
  post_install_message:
@@ -141,7 +141,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
141
141
  - !ruby/object:Gem::Version
142
142
  version: '0'
143
143
  requirements: []
144
- rubygems_version: 3.3.3
144
+ rubygems_version: 3.3.26
145
145
  signing_key:
146
146
  specification_version: 4
147
147
  summary: Automatic performance checking tool for Ruby code.