rubocop-performance 1.7.1 → 1.8.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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +18 -7
  3. data/lib/rubocop/cop/performance/ancestors_include.rb +14 -13
  4. data/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb +7 -12
  5. data/lib/rubocop/cop/performance/bind_call.rb +8 -18
  6. data/lib/rubocop/cop/performance/caller.rb +3 -2
  7. data/lib/rubocop/cop/performance/case_when_splat.rb +18 -11
  8. data/lib/rubocop/cop/performance/casecmp.rb +12 -20
  9. data/lib/rubocop/cop/performance/chain_array_allocation.rb +4 -10
  10. data/lib/rubocop/cop/performance/collection_literal_in_loop.rb +140 -0
  11. data/lib/rubocop/cop/performance/compare_with_block.rb +10 -21
  12. data/lib/rubocop/cop/performance/count.rb +13 -16
  13. data/lib/rubocop/cop/performance/delete_prefix.rb +13 -22
  14. data/lib/rubocop/cop/performance/delete_suffix.rb +13 -22
  15. data/lib/rubocop/cop/performance/detect.rb +29 -26
  16. data/lib/rubocop/cop/performance/double_start_end_with.rb +16 -24
  17. data/lib/rubocop/cop/performance/end_with.rb +8 -13
  18. data/lib/rubocop/cop/performance/fixed_size.rb +1 -1
  19. data/lib/rubocop/cop/performance/flat_map.rb +20 -22
  20. data/lib/rubocop/cop/performance/inefficient_hash_search.rb +13 -14
  21. data/lib/rubocop/cop/performance/io_readlines.rb +25 -36
  22. data/lib/rubocop/cop/performance/open_struct.rb +2 -2
  23. data/lib/rubocop/cop/performance/range_include.rb +7 -6
  24. data/lib/rubocop/cop/performance/redundant_block_call.rb +11 -6
  25. data/lib/rubocop/cop/performance/redundant_match.rb +11 -6
  26. data/lib/rubocop/cop/performance/redundant_merge.rb +18 -17
  27. data/lib/rubocop/cop/performance/redundant_sort_block.rb +6 -16
  28. data/lib/rubocop/cop/performance/redundant_string_chars.rb +8 -12
  29. data/lib/rubocop/cop/performance/regexp_match.rb +20 -20
  30. data/lib/rubocop/cop/performance/reverse_each.rb +9 -5
  31. data/lib/rubocop/cop/performance/reverse_first.rb +4 -10
  32. data/lib/rubocop/cop/performance/size.rb +6 -6
  33. data/lib/rubocop/cop/performance/sort_reverse.rb +6 -15
  34. data/lib/rubocop/cop/performance/squeeze.rb +6 -10
  35. data/lib/rubocop/cop/performance/start_with.rb +8 -13
  36. data/lib/rubocop/cop/performance/string_include.rb +9 -13
  37. data/lib/rubocop/cop/performance/string_replacement.rb +23 -27
  38. data/lib/rubocop/cop/performance/sum.rb +129 -0
  39. data/lib/rubocop/cop/performance/times_map.rb +11 -18
  40. data/lib/rubocop/cop/performance/unfreeze_string.rb +1 -1
  41. data/lib/rubocop/cop/performance/uri_default_parser.rb +6 -12
  42. data/lib/rubocop/cop/performance_cops.rb +2 -0
  43. data/lib/rubocop/performance/version.rb +1 -1
  44. metadata +6 -4
@@ -36,7 +36,9 @@ module RuboCop
36
36
  # { a: 1, b: 2 }.has_value?('garbage')
37
37
  # h = { a: 1, b: 2 }; h.value?(nil)
38
38
  #
39
- class InefficientHashSearch < Cop
39
+ class InefficientHashSearch < Base
40
+ extend AutoCorrector
41
+
40
42
  def_node_matcher :inefficient_include?, <<~PATTERN
41
43
  (send (send $_ {:keys :values}) :include? _)
42
44
  PATTERN
@@ -45,19 +47,16 @@ module RuboCop
45
47
  inefficient_include?(node) do |receiver|
46
48
  return if receiver.nil?
47
49
 
48
- add_offense(node)
49
- end
50
- end
51
-
52
- def autocorrect(node)
53
- lambda do |corrector|
54
- # Replace `keys.include?` or `values.include?` with the appropriate
55
- # `key?`/`value?` method.
56
- corrector.replace(
57
- node.loc.expression,
58
- "#{autocorrect_hash_expression(node)}."\
59
- "#{autocorrect_method(node)}(#{autocorrect_argument(node)})"
60
- )
50
+ message = message(node)
51
+ add_offense(node, message: message) do |corrector|
52
+ # Replace `keys.include?` or `values.include?` with the appropriate
53
+ # `key?`/`value?` method.
54
+ corrector.replace(
55
+ node.loc.expression,
56
+ "#{autocorrect_hash_expression(node)}."\
57
+ "#{autocorrect_method(node)}(#{autocorrect_argument(node)})"
58
+ )
59
+ end
61
60
  end
62
61
  end
63
62
 
@@ -24,8 +24,9 @@ module RuboCop
24
24
  # file.each_line.find { |l| l.start_with?('#') }
25
25
  # file.each_line { |l| puts l }
26
26
  #
27
- class IoReadlines < Cop
27
+ class IoReadlines < Base
28
28
  include RangeHelp
29
+ extend AutoCorrector
29
30
 
30
31
  MSG = 'Use `%<good>s` instead of `%<bad>s`.'
31
32
  ENUMERABLE_METHODS = (Enumerable.instance_methods + [:each]).freeze
@@ -39,34 +40,16 @@ module RuboCop
39
40
  PATTERN
40
41
 
41
42
  def on_send(node)
42
- readlines_on_class?(node) do |enumerable_call, readlines_call|
43
- offense(node, enumerable_call, readlines_call)
44
- end
43
+ return unless (captured_values = readlines_on_class?(node) || readlines_on_instance?(node))
45
44
 
46
- readlines_on_instance?(node) do |enumerable_call, readlines_call, _|
47
- offense(node, enumerable_call, readlines_call)
48
- end
49
- end
45
+ enumerable_call, readlines_call, receiver = *captured_values
46
+
47
+ range = offense_range(enumerable_call, readlines_call)
48
+ good_method = build_good_method(enumerable_call)
49
+ bad_method = build_bad_method(enumerable_call)
50
50
 
51
- def autocorrect(node)
52
- readlines_on_instance?(node) do |enumerable_call, readlines_call, receiver|
53
- # We cannot safely correct `.readlines` method called on IO/File classes
54
- # due to its signature and we are not sure with implicit receiver
55
- # if it is called in the context of some instance or mentioned class.
56
- return if receiver.nil?
57
-
58
- lambda do |corrector|
59
- range = correction_range(enumerable_call, readlines_call)
60
-
61
- if readlines_call.arguments?
62
- call_args = build_call_args(readlines_call.arguments)
63
- replacement = "each_line(#{call_args})"
64
- else
65
- replacement = 'each_line'
66
- end
67
-
68
- corrector.replace(range, replacement)
69
- end
51
+ add_offense(range, message: format(MSG, good: good_method, bad: bad_method)) do |corrector|
52
+ autocorrect(corrector, enumerable_call, readlines_call, receiver)
70
53
  end
71
54
  end
72
55
 
@@ -76,16 +59,22 @@ module RuboCop
76
59
  ENUMERABLE_METHODS.include?(node.to_sym)
77
60
  end
78
61
 
79
- def offense(node, enumerable_call, readlines_call)
80
- range = offense_range(enumerable_call, readlines_call)
81
- good_method = build_good_method(enumerable_call)
82
- bad_method = build_bad_method(enumerable_call)
62
+ def autocorrect(corrector, enumerable_call, readlines_call, receiver)
63
+ # We cannot safely correct `.readlines` method called on IO/File classes
64
+ # due to its signature and we are not sure with implicit receiver
65
+ # if it is called in the context of some instance or mentioned class.
66
+ return if receiver.nil?
67
+
68
+ range = correction_range(enumerable_call, readlines_call)
69
+
70
+ if readlines_call.arguments?
71
+ call_args = build_call_args(readlines_call.arguments)
72
+ replacement = "each_line(#{call_args})"
73
+ else
74
+ replacement = 'each_line'
75
+ end
83
76
 
84
- add_offense(
85
- node,
86
- location: range,
87
- message: format(MSG, good: good_method, bad: bad_method)
88
- )
77
+ corrector.replace(range, replacement)
89
78
  end
90
79
 
91
80
  def offense_range(enumerable_call, readlines_call)
@@ -27,7 +27,7 @@ module RuboCop
27
27
  # end
28
28
  # end
29
29
  #
30
- class OpenStruct < Cop
30
+ class OpenStruct < Base
31
31
  MSG = 'Consider using `Struct` over `OpenStruct` ' \
32
32
  'to optimize the performance.'
33
33
 
@@ -37,7 +37,7 @@ module RuboCop
37
37
 
38
38
  def on_send(node)
39
39
  open_struct(node) do
40
- add_offense(node, location: :selector)
40
+ add_offense(node.loc.selector)
41
41
  end
42
42
  end
43
43
  end
@@ -24,7 +24,9 @@ module RuboCop
24
24
  # # the desired result:
25
25
  #
26
26
  # ('a'..'z').cover?('yellow') # => true
27
- class RangeInclude < Cop
27
+ class RangeInclude < Base
28
+ extend AutoCorrector
29
+
28
30
  MSG = 'Use `Range#cover?` instead of `Range#%<bad_method>s`.'
29
31
 
30
32
  # TODO: If we traced out assignments of variables to their uses, we
@@ -39,12 +41,11 @@ module RuboCop
39
41
  def on_send(node)
40
42
  range_include(node) do |bad_method|
41
43
  message = format(MSG, bad_method: bad_method)
42
- add_offense(node, location: :selector, message: message)
43
- end
44
- end
45
44
 
46
- def autocorrect(node)
47
- ->(corrector) { corrector.replace(node.loc.selector, 'cover?') }
45
+ add_offense(node.loc.selector, message: message) do |corrector|
46
+ corrector.replace(node.loc.selector, 'cover?')
47
+ end
48
+ end
48
49
  end
49
50
  end
50
51
  end
@@ -22,7 +22,9 @@ module RuboCop
22
22
  # def another
23
23
  # yield 1, 2, 3
24
24
  # end
25
- class RedundantBlockCall < Cop
25
+ class RedundantBlockCall < Base
26
+ extend AutoCorrector
27
+
26
28
  MSG = 'Use `yield` instead of `%<argname>s.call`.'
27
29
  YIELD = 'yield'
28
30
  OPEN_PAREN = '('
@@ -47,13 +49,17 @@ module RuboCop
47
49
  next unless body
48
50
 
49
51
  calls_to_report(argname, body).each do |blockcall|
50
- add_offense(blockcall, message: format(MSG, argname: argname))
52
+ add_offense(blockcall, message: format(MSG, argname: argname)) do |corrector|
53
+ autocorrect(corrector, blockcall)
54
+ end
51
55
  end
52
56
  end
53
57
  end
54
58
 
59
+ private
60
+
55
61
  # offenses are registered on the `block.call` nodes
56
- def autocorrect(node)
62
+ def autocorrect(corrector, node)
57
63
  _receiver, _method, *args = *node
58
64
  new_source = String.new(YIELD)
59
65
  unless args.empty?
@@ -67,10 +73,9 @@ module RuboCop
67
73
  end
68
74
 
69
75
  new_source << CLOSE_PAREN if parentheses?(node) && !args.empty?
70
- ->(corrector) { corrector.replace(node.source_range, new_source) }
71
- end
72
76
 
73
- private
77
+ corrector.replace(node.source_range, new_source)
78
+ end
74
79
 
75
80
  def calls_to_report(argname, body)
76
81
  return [] if blockarg_assigned?(body, argname)
@@ -17,7 +17,9 @@ module RuboCop
17
17
  # # good
18
18
  # method(str =~ /regex/)
19
19
  # return value unless regex =~ 'str'
20
- class RedundantMatch < Cop
20
+ class RedundantMatch < Base
21
+ extend AutoCorrector
22
+
21
23
  MSG = 'Use `=~` in places where the `MatchData` returned by ' \
22
24
  '`#match` will not be used.'
23
25
 
@@ -37,18 +39,21 @@ module RuboCop
37
39
  (!node.value_used? || only_truthiness_matters?(node)) &&
38
40
  !(node.parent && node.parent.block_type?)
39
41
 
40
- add_offense(node)
42
+ add_offense(node) do |corrector|
43
+ autocorrect(corrector, node)
44
+ end
41
45
  end
42
46
 
43
- def autocorrect(node)
47
+ private
48
+
49
+ def autocorrect(corrector, node)
44
50
  # Regexp#match can take a second argument, but this cop doesn't
45
51
  # register an offense in that case
46
52
  return unless node.first_argument.regexp_type?
47
53
 
48
- new_source =
49
- node.receiver.source + ' =~ ' + node.first_argument.source
54
+ new_source = "#{node.receiver.source} =~ #{node.first_argument.source}"
50
55
 
51
- ->(corrector) { corrector.replace(node.source_range, new_source) }
56
+ corrector.replace(node.source_range, new_source)
52
57
  end
53
58
  end
54
59
  end
@@ -24,7 +24,9 @@ module RuboCop
24
24
  # # good
25
25
  # hash[:a] = 1
26
26
  # hash[:b] = 2
27
- class RedundantMerge < Cop
27
+ class RedundantMerge < Base
28
+ extend AutoCorrector
29
+
28
30
  AREF_ASGN = '%<receiver>s[%<key>s] = %<value>s'
29
31
  MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
30
32
 
@@ -44,18 +46,17 @@ module RuboCop
44
46
 
45
47
  def on_send(node)
46
48
  each_redundant_merge(node) do |redundant_merge_node|
47
- add_offense(redundant_merge_node)
48
- end
49
- end
50
-
51
- def autocorrect(node)
52
- redundant_merge_candidate(node) do |receiver, pairs|
53
- new_source = to_assignments(receiver, pairs).join("\n")
54
-
55
- if node.parent && pairs.size > 1
56
- correct_multiple_elements(node, node.parent, new_source)
57
- else
58
- correct_single_element(node, new_source)
49
+ message = message(node)
50
+ add_offense(redundant_merge_node, message: message) do |corrector|
51
+ redundant_merge_candidate(node) do |receiver, pairs|
52
+ new_source = to_assignments(receiver, pairs).join("\n")
53
+
54
+ if node.parent && pairs.size > 1
55
+ correct_multiple_elements(corrector, node, node.parent, new_source)
56
+ else
57
+ correct_single_element(corrector, node, new_source)
58
+ end
59
+ end
59
60
  end
60
61
  end
61
62
  end
@@ -98,7 +99,7 @@ module RuboCop
98
99
  !EachWithObjectInspector.new(node, receiver).value_used?
99
100
  end
100
101
 
101
- def correct_multiple_elements(node, parent, new_source)
102
+ def correct_multiple_elements(corrector, node, parent, new_source)
102
103
  if modifier_flow_control?(parent)
103
104
  new_source = rewrite_with_modifier(node, parent, new_source)
104
105
  node = parent
@@ -107,11 +108,11 @@ module RuboCop
107
108
  new_source.gsub!(/\n/, padding)
108
109
  end
109
110
 
110
- ->(corrector) { corrector.replace(node.source_range, new_source) }
111
+ corrector.replace(node.source_range, new_source)
111
112
  end
112
113
 
113
- def correct_single_element(node, new_source)
114
- ->(corrector) { corrector.replace(node.source_range, new_source) }
114
+ def correct_single_element(corrector, node, new_source)
115
+ corrector.replace(node.source_range, new_source)
115
116
  end
116
117
 
117
118
  def to_assignments(receiver, pairs)
@@ -13,29 +13,19 @@ module RuboCop
13
13
  # # good
14
14
  # array.sort
15
15
  #
16
- class RedundantSortBlock < Cop
16
+ class RedundantSortBlock < Base
17
17
  include SortBlock
18
+ extend AutoCorrector
18
19
 
19
20
  MSG = 'Use `sort` instead of `%<bad_method>s`.'
20
21
 
21
22
  def on_block(node)
22
- sort_with_block?(node) do |send, var_a, var_b, body|
23
- replaceable_body?(body, var_a, var_b) do
24
- range = sort_range(send, node)
23
+ return unless (send, var_a, var_b, body = sort_with_block?(node))
25
24
 
26
- add_offense(
27
- node,
28
- location: range,
29
- message: message(var_a, var_b)
30
- )
31
- end
32
- end
33
- end
25
+ replaceable_body?(body, var_a, var_b) do
26
+ range = sort_range(send, node)
34
27
 
35
- def autocorrect(node)
36
- sort_with_block?(node) do |send, _var_a, _var_b, _body|
37
- lambda do |corrector|
38
- range = sort_range(send, node)
28
+ add_offense(range, message: message(var_a, var_b)) do |corrector|
39
29
  corrector.replace(range, 'sort')
40
30
  end
41
31
  end
@@ -39,8 +39,9 @@ module RuboCop
39
39
  # str.size
40
40
  # str.empty?
41
41
  #
42
- class RedundantStringChars < Cop
42
+ class RedundantStringChars < Base
43
43
  include RangeHelp
44
+ extend AutoCorrector
44
45
 
45
46
  MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'
46
47
  REPLACEABLE_METHODS = %i[[] slice first last take drop length size empty?].freeze
@@ -50,21 +51,16 @@ module RuboCop
50
51
  PATTERN
51
52
 
52
53
  def on_send(node)
53
- redundant_chars_call?(node) do |receiver, method, args|
54
- range = offense_range(receiver, node)
55
- message = build_message(method, args)
56
- add_offense(node, location: range, message: message)
57
- end
58
- end
54
+ return unless (receiver, method, args = redundant_chars_call?(node))
59
55
 
60
- def autocorrect(node)
61
- redundant_chars_call?(node) do |receiver, method, args|
56
+ range = offense_range(receiver, node)
57
+ message = build_message(method, args)
58
+
59
+ add_offense(range, message: message) do |corrector|
62
60
  range = correction_range(receiver, node)
63
61
  replacement = build_good_method(method, args)
64
62
 
65
- lambda do |corrector|
66
- corrector.replace(range, replacement)
67
- end
63
+ corrector.replace(range, replacement)
68
64
  end
69
65
  end
70
66
 
@@ -72,7 +72,9 @@ module RuboCop
72
72
  # do_something($~)
73
73
  # end
74
74
  # end
75
- class RegexpMatch < Cop
75
+ class RegexpMatch < Base
76
+ extend AutoCorrector
77
+
76
78
  # Constants are included in this list because it is unlikely that
77
79
  # someone will store `nil` as a constant and then use it for comparison
78
80
  TYPES_IMPLEMENTING_MATCH = %i[const regexp str sym].freeze
@@ -141,27 +143,28 @@ module RuboCop
141
143
  end
142
144
  end
143
145
 
144
- def autocorrect(node)
145
- lambda do |corrector|
146
- if match_method?(node) || match_with_int_arg_method?(node)
147
- corrector.replace(node.loc.selector, 'match?')
148
- elsif match_operator?(node) || match_threequals?(node)
149
- recv, oper, arg = *node
150
- correct_operator(corrector, recv, arg, oper)
151
- elsif match_with_lvasgn?(node)
152
- recv, arg = *node
153
- correct_operator(corrector, recv, arg)
154
- end
155
- end
156
- end
157
-
158
146
  private
159
147
 
160
148
  def check_condition(cond)
161
149
  match_node?(cond) do
162
150
  return if last_match_used?(cond)
163
151
 
164
- add_offense(cond)
152
+ message = message(cond)
153
+ add_offense(cond, message: message) do |corrector|
154
+ autocorrect(corrector, cond)
155
+ end
156
+ end
157
+ end
158
+
159
+ def autocorrect(corrector, node)
160
+ if match_method?(node) || match_with_int_arg_method?(node)
161
+ corrector.replace(node.loc.selector, 'match?')
162
+ elsif match_operator?(node) || match_threequals?(node)
163
+ recv, oper, arg = *node
164
+ correct_operator(corrector, recv, arg, oper)
165
+ elsif match_with_lvasgn?(node)
166
+ recv, arg = *node
167
+ correct_operator(corrector, recv, arg)
165
168
  end
166
169
  end
167
170
 
@@ -231,10 +234,7 @@ module RuboCop
231
234
 
232
235
  def scope_root(node)
233
236
  node.each_ancestor.find do |ancestor|
234
- ancestor.def_type? ||
235
- ancestor.defs_type? ||
236
- ancestor.class_type? ||
237
- ancestor.module_type?
237
+ ancestor.def_type? || ancestor.defs_type? || ancestor.class_type? || ancestor.module_type?
238
238
  end
239
239
  end
240
240