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
@@ -23,8 +23,9 @@ module RuboCop
23
23
  # array.max_by(&:foo)
24
24
  # array.min_by(&:foo)
25
25
  # array.sort_by { |a| a[:foo] }
26
- class CompareWithBlock < Cop
26
+ class CompareWithBlock < Base
27
27
  include RangeHelp
28
+ extend AutoCorrector
28
29
 
29
30
  MSG = 'Use `%<compare_method>s_by%<instead>s` instead of ' \
30
31
  '`%<compare_method>s { |%<var_a>s, %<var_b>s| %<str_a>s ' \
@@ -51,27 +52,15 @@ module RuboCop
51
52
 
52
53
  range = compare_range(send, node)
53
54
 
54
- add_offense(
55
- node,
56
- location: range,
57
- message: message(send, method, var_a, var_b, args_a)
58
- )
59
- end
60
- end
61
- end
62
-
63
- def autocorrect(node)
64
- lambda do |corrector|
65
- send, var_a, var_b, body = compare?(node)
66
- method, arg, = replaceable_body?(body, var_a, var_b)
67
- replacement =
68
- if method == :[]
69
- "#{send.method_name}_by { |a| a[#{arg.first.source}] }"
70
- else
71
- "#{send.method_name}_by(&:#{method})"
55
+ add_offense(range, message: message(send, method, var_a, var_b, args_a)) do |corrector|
56
+ replacement = if method == :[]
57
+ "#{send.method_name}_by { |a| a[#{args_a.first.source}] }"
58
+ else
59
+ "#{send.method_name}_by(&:#{method})"
60
+ end
61
+ corrector.replace(range, replacement)
72
62
  end
73
- corrector.replace(compare_range(send, node),
74
- replacement)
63
+ end
75
64
  end
76
65
  end
77
66
 
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Cop
5
5
  module Performance
6
6
  # This cop is used to identify usages of `count` on an `Enumerable` that
7
- # follow calls to `select` or `reject`. Querying logic can instead be
7
+ # follow calls to `select`, `find_all`, `filter` or `reject`. Querying logic can instead be
8
8
  # passed to the `count` call.
9
9
  #
10
10
  # @example
@@ -37,15 +37,16 @@ module RuboCop
37
37
  # becomes:
38
38
  #
39
39
  # `Model.where(id: [1, 2, 3]).to_a.count { |m| m.method == true }`
40
- class Count < Cop
40
+ class Count < Base
41
41
  include RangeHelp
42
+ extend AutoCorrector
42
43
 
43
44
  MSG = 'Use `count` instead of `%<selector>s...%<counter>s`.'
44
45
 
45
46
  def_node_matcher :count_candidate?, <<~PATTERN
46
47
  {
47
- (send (block $(send _ ${:select :reject}) ...) ${:count :length :size})
48
- (send $(send _ ${:select :reject} (:block_pass _)) ${:count :length :size})
48
+ (send (block $(send _ ${:select :filter :find_all :reject}) ...) ${:count :length :size})
49
+ (send $(send _ ${:select :filter :find_all :reject} (:block_pass _)) ${:count :length :size})
49
50
  }
50
51
  PATTERN
51
52
 
@@ -57,29 +58,25 @@ module RuboCop
57
58
  selector_node.loc.selector.begin_pos
58
59
  end
59
60
 
60
- add_offense(node,
61
- location: range,
62
- message: format(MSG, selector: selector,
63
- counter: counter))
61
+ add_offense(range, message: format(MSG, selector: selector, counter: counter)) do |corrector|
62
+ autocorrect(corrector, node, selector_node, selector)
63
+ end
64
64
  end
65
65
  end
66
66
 
67
- def autocorrect(node)
68
- selector_node, selector, _counter = count_candidate?(node)
67
+ private
68
+
69
+ def autocorrect(corrector, node, selector_node, selector)
69
70
  selector_loc = selector_node.loc.selector
70
71
 
71
72
  return if selector == :reject
72
73
 
73
74
  range = source_starting_at(node) { |n| n.loc.dot.begin_pos }
74
75
 
75
- lambda do |corrector|
76
- corrector.remove(range)
77
- corrector.replace(selector_loc, 'count')
78
- end
76
+ corrector.remove(range)
77
+ corrector.replace(selector_loc, 'count')
79
78
  end
80
79
 
81
- private
82
-
83
80
  def eligible_node?(node)
84
81
  !(node.parent && node.parent.block_type?)
85
82
  end
@@ -43,9 +43,10 @@ module RuboCop
43
43
  # str.sub(/^prefix/, '')
44
44
  # str.sub!(/^prefix/, '')
45
45
  #
46
- class DeletePrefix < Cop
47
- extend TargetRubyVersion
46
+ class DeletePrefix < Base
48
47
  include RegexpMetacharacter
48
+ extend AutoCorrector
49
+ extend TargetRubyVersion
49
50
 
50
51
  minimum_target_ruby_version 2.5
51
52
 
@@ -63,31 +64,21 @@ module RuboCop
63
64
  PATTERN
64
65
 
65
66
  def on_send(node)
66
- delete_prefix_candidate?(node) do |_, bad_method, _, replace_string|
67
- return unless replace_string.blank?
68
-
69
- good_method = PREFERRED_METHODS[bad_method]
67
+ return unless (receiver, bad_method, regexp_str, replace_string = delete_prefix_candidate?(node))
68
+ return unless replace_string.blank?
70
69
 
71
- message = format(MSG, current: bad_method, prefer: good_method)
70
+ good_method = PREFERRED_METHODS[bad_method]
72
71
 
73
- add_offense(node, location: :selector, message: message)
74
- end
75
- end
72
+ message = format(MSG, current: bad_method, prefer: good_method)
76
73
 
77
- def autocorrect(node)
78
- delete_prefix_candidate?(node) do |receiver, bad_method, regexp_str, _|
79
- lambda do |corrector|
80
- good_method = PREFERRED_METHODS[bad_method]
81
- regexp_str = drop_start_metacharacter(regexp_str)
82
- regexp_str = interpret_string_escapes(regexp_str)
83
- string_literal = to_string_literal(regexp_str)
74
+ add_offense(node.loc.selector, message: message) do |corrector|
75
+ regexp_str = drop_start_metacharacter(regexp_str)
76
+ regexp_str = interpret_string_escapes(regexp_str)
77
+ string_literal = to_string_literal(regexp_str)
84
78
 
85
- new_code = "#{receiver.source}.#{good_method}(#{string_literal})"
79
+ new_code = "#{receiver.source}.#{good_method}(#{string_literal})"
86
80
 
87
- # TODO: `source_range` is no longer required when RuboCop 0.81 or lower support will be dropped.
88
- # https://github.com/rubocop-hq/rubocop/commit/82eb350d2cba16
89
- corrector.replace(node.source_range, new_code)
90
- end
81
+ corrector.replace(node, new_code)
91
82
  end
92
83
  end
93
84
  end
@@ -43,9 +43,10 @@ module RuboCop
43
43
  # str.sub(/suffix$/, '')
44
44
  # str.sub!(/suffix$/, '')
45
45
  #
46
- class DeleteSuffix < Cop
47
- extend TargetRubyVersion
46
+ class DeleteSuffix < Base
48
47
  include RegexpMetacharacter
48
+ extend AutoCorrector
49
+ extend TargetRubyVersion
49
50
 
50
51
  minimum_target_ruby_version 2.5
51
52
 
@@ -63,31 +64,21 @@ module RuboCop
63
64
  PATTERN
64
65
 
65
66
  def on_send(node)
66
- delete_suffix_candidate?(node) do |_, bad_method, _, replace_string|
67
- return unless replace_string.blank?
68
-
69
- good_method = PREFERRED_METHODS[bad_method]
67
+ return unless (receiver, bad_method, regexp_str, replace_string = delete_suffix_candidate?(node))
68
+ return unless replace_string.blank?
70
69
 
71
- message = format(MSG, current: bad_method, prefer: good_method)
70
+ good_method = PREFERRED_METHODS[bad_method]
72
71
 
73
- add_offense(node, location: :selector, message: message)
74
- end
75
- end
72
+ message = format(MSG, current: bad_method, prefer: good_method)
76
73
 
77
- def autocorrect(node)
78
- delete_suffix_candidate?(node) do |receiver, bad_method, regexp_str, _|
79
- lambda do |corrector|
80
- good_method = PREFERRED_METHODS[bad_method]
81
- regexp_str = drop_end_metacharacter(regexp_str)
82
- regexp_str = interpret_string_escapes(regexp_str)
83
- string_literal = to_string_literal(regexp_str)
74
+ add_offense(node.loc.selector, message: message) do |corrector|
75
+ regexp_str = drop_end_metacharacter(regexp_str)
76
+ regexp_str = interpret_string_escapes(regexp_str)
77
+ string_literal = to_string_literal(regexp_str)
84
78
 
85
- new_code = "#{receiver.source}.#{good_method}(#{string_literal})"
79
+ new_code = "#{receiver.source}.#{good_method}(#{string_literal})"
86
80
 
87
- # TODO: `source_range` is no longer required when RuboCop 0.81 or lower support will be dropped.
88
- # https://github.com/rubocop-hq/rubocop/commit/82eb350d2cba16
89
- corrector.replace(node.source_range, new_code)
90
- end
81
+ corrector.replace(node, new_code)
91
82
  end
92
83
  end
93
84
  end
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Cop
5
5
  module Performance
6
6
  # This cop is used to identify usages of
7
- # `select.first`, `select.last`, `find_all.first`, and `find_all.last`
7
+ # `select.first`, `select.last`, `find_all.first`, `find_all.last`, `filter.first`, and `filter.last`
8
8
  # and change them to use `detect` instead.
9
9
  #
10
10
  # @example
@@ -13,6 +13,8 @@ module RuboCop
13
13
  # [].select { |item| true }.last
14
14
  # [].find_all { |item| true }.first
15
15
  # [].find_all { |item| true }.last
16
+ # [].filter { |item| true }.first
17
+ # [].filter { |item| true }.last
16
18
  #
17
19
  # # good
18
20
  # [].detect { |item| true }
@@ -22,7 +24,9 @@ module RuboCop
22
24
  # `ActiveRecord` does not implement a `detect` method and `find` has its
23
25
  # own meaning. Correcting ActiveRecord methods with this cop should be
24
26
  # considered unsafe.
25
- class Detect < Cop
27
+ class Detect < Base
28
+ extend AutoCorrector
29
+
26
30
  MSG = 'Use `%<prefer>s` instead of ' \
27
31
  '`%<first_method>s.%<second_method>s`.'
28
32
  REVERSE_MSG = 'Use `reverse.%<prefer>s` instead of ' \
@@ -30,8 +34,8 @@ module RuboCop
30
34
 
31
35
  def_node_matcher :detect_candidate?, <<~PATTERN
32
36
  {
33
- (send $(block (send _ {:select :find_all}) ...) ${:first :last} $...)
34
- (send $(send _ {:select :find_all} ...) ${:first :last} $...)
37
+ (send $(block (send _ {:select :find_all :filter}) ...) ${:first :last} $...)
38
+ (send $(send _ {:select :find_all :filter} ...) ${:first :last} $...)
35
39
  }
36
40
  PATTERN
37
41
 
@@ -47,25 +51,6 @@ module RuboCop
47
51
  end
48
52
  end
49
53
 
50
- def autocorrect(node)
51
- receiver, first_method = *node
52
-
53
- replacement = if first_method == :last
54
- "reverse.#{preferred_method}"
55
- else
56
- preferred_method
57
- end
58
-
59
- first_range = receiver.source_range.end.join(node.loc.selector)
60
-
61
- receiver, _args, _body = *receiver if receiver.block_type?
62
-
63
- lambda do |corrector|
64
- corrector.remove(first_range)
65
- corrector.replace(receiver.loc.selector, replacement)
66
- end
67
- end
68
-
69
54
  private
70
55
 
71
56
  def accept_first_call?(receiver, body)
@@ -86,12 +71,30 @@ module RuboCop
86
71
  first_method: first_method,
87
72
  second_method: second_method)
88
73
 
89
- add_offense(node, location: range, message: formatted_message)
74
+ add_offense(range, message: formatted_message) do |corrector|
75
+ autocorrect(corrector, node)
76
+ end
77
+ end
78
+
79
+ def autocorrect(corrector, node)
80
+ receiver, first_method = *node
81
+
82
+ replacement = if first_method == :last
83
+ "reverse.#{preferred_method}"
84
+ else
85
+ preferred_method
86
+ end
87
+
88
+ first_range = receiver.source_range.end.join(node.loc.selector)
89
+
90
+ receiver, _args, _body = *receiver if receiver.block_type?
91
+
92
+ corrector.remove(first_range)
93
+ corrector.replace(receiver.loc.selector, replacement)
90
94
  end
91
95
 
92
96
  def preferred_method
93
- config.for_cop('Style/CollectionMethods') \
94
- ['PreferredMethods']['detect'] || 'detect'
97
+ config.for_cop('Style/CollectionMethods')['PreferredMethods']['detect'] || 'detect'
95
98
  end
96
99
 
97
100
  def lazy?(node)
@@ -17,39 +17,34 @@ module RuboCop
17
17
  # str.start_with?("a", Some::CONST)
18
18
  # str.start_with?("a", "b", "c")
19
19
  # str.end_with?(var1, var2)
20
- class DoubleStartEndWith < Cop
20
+ class DoubleStartEndWith < Base
21
+ extend AutoCorrector
22
+
21
23
  MSG = 'Use `%<receiver>s.%<method>s(%<combined_args>s)` ' \
22
24
  'instead of `%<original_code>s`.'
23
25
 
24
26
  def on_or(node)
25
- receiver,
26
- method,
27
- first_call_args,
28
- second_call_args = process_source(node)
27
+ receiver, method, first_call_args, second_call_args = process_source(node)
29
28
 
30
29
  return unless receiver && second_call_args.all?(&:pure?)
31
30
 
32
31
  combined_args = combine_args(first_call_args, second_call_args)
33
32
 
34
- add_offense_for_double_call(node, receiver, method, combined_args)
33
+ add_offense(node, message: message(node, receiver, method, combined_args)) do |corrector|
34
+ autocorrect(corrector, first_call_args, second_call_args, combined_args)
35
+ end
35
36
  end
36
37
 
37
- def autocorrect(node)
38
- _receiver, _method,
39
- first_call_args, second_call_args = process_source(node)
38
+ private
40
39
 
41
- combined_args = combine_args(first_call_args, second_call_args)
40
+ def autocorrect(corrector, first_call_args, second_call_args, combined_args)
42
41
  first_argument = first_call_args.first.loc.expression
43
42
  last_argument = second_call_args.last.loc.expression
44
43
  range = first_argument.join(last_argument)
45
44
 
46
- lambda do |corrector|
47
- corrector.replace(range, combined_args)
48
- end
45
+ corrector.replace(range, combined_args)
49
46
  end
50
47
 
51
- private
52
-
53
48
  def process_source(node)
54
49
  if check_for_active_support_aliases?
55
50
  check_with_active_support_aliases(node)
@@ -58,17 +53,14 @@ module RuboCop
58
53
  end
59
54
  end
60
55
 
61
- def combine_args(first_call_args, second_call_args)
62
- (first_call_args + second_call_args).map(&:source).join(', ')
56
+ def message(node, receiver, method, combined_args)
57
+ format(
58
+ MSG, receiver: receiver.source, method: method, combined_args: combined_args, original_code: node.source
59
+ )
63
60
  end
64
61
 
65
- def add_offense_for_double_call(node, receiver, method, combined_args)
66
- msg = format(MSG, receiver: receiver.source,
67
- method: method,
68
- combined_args: combined_args,
69
- original_code: node.source)
70
-
71
- add_offense(node, message: msg)
62
+ def combine_args(first_call_args, second_call_args)
63
+ (first_call_args + second_call_args).map(&:source).join(', ')
72
64
  end
73
65
 
74
66
  def check_for_active_support_aliases?
@@ -41,8 +41,9 @@ module RuboCop
41
41
  # 'abc'.match(/bc$/)
42
42
  # /bc$/.match('abc')
43
43
  #
44
- class EndWith < Cop
44
+ class EndWith < Base
45
45
  include RegexpMetacharacter
46
+ extend AutoCorrector
46
47
 
47
48
  MSG = 'Use `String#end_with?` instead of a regex match anchored to ' \
48
49
  'the end of the string.'
@@ -54,25 +55,19 @@ module RuboCop
54
55
  PATTERN
55
56
 
56
57
  def on_send(node)
57
- return unless redundant_regex?(node)
58
+ return unless (receiver, regex_str = redundant_regex?(node))
58
59
 
59
- add_offense(node)
60
- end
61
- alias on_match_with_lvasgn on_send
62
-
63
- def autocorrect(node)
64
- redundant_regex?(node) do |receiver, regex_str|
60
+ add_offense(node) do |corrector|
65
61
  receiver, regex_str = regex_str, receiver if receiver.is_a?(String)
66
62
  regex_str = drop_end_metacharacter(regex_str)
67
63
  regex_str = interpret_string_escapes(regex_str)
68
64
 
69
- lambda do |corrector|
70
- new_source = receiver.source + '.end_with?(' +
71
- to_string_literal(regex_str) + ')'
72
- corrector.replace(node.source_range, new_source)
73
- end
65
+ new_source = "#{receiver.source}.end_with?(#{to_string_literal(regex_str)})"
66
+
67
+ corrector.replace(node.source_range, new_source)
74
68
  end
75
69
  end
70
+ alias on_match_with_lvasgn on_send
76
71
  end
77
72
  end
78
73
  end
@@ -45,7 +45,7 @@ module RuboCop
45
45
  # waldo = { a: corge, b: grault }
46
46
  # waldo.size
47
47
  #
48
- class FixedSize < Cop
48
+ class FixedSize < Base
49
49
  MSG = 'Do not compute the size of statically sized objects.'
50
50
 
51
51
  def_node_matcher :counter, <<~MATCHER
@@ -14,8 +14,9 @@ module RuboCop
14
14
  # [1, 2, 3, 4].flat_map { |e| [e, e] }
15
15
  # [1, 2, 3, 4].map { |e| [e, e] }.flatten
16
16
  # [1, 2, 3, 4].collect { |e| [e, e] }.flatten
17
- class FlatMap < Cop
17
+ class FlatMap < Base
18
18
  include RangeHelp
19
+ extend AutoCorrector
19
20
 
20
21
  MSG = 'Use `flat_map` instead of `%<method>s...%<flatten>s`.'
21
22
  FLATTEN_MULTIPLE_LEVELS = ' Beware, `flat_map` only flattens 1 level ' \
@@ -44,25 +45,11 @@ module RuboCop
44
45
  end
45
46
  end
46
47
 
47
- def autocorrect(node)
48
- map_node, _first_method, _flatten, params = flat_map_candidate?(node)
49
- flatten_level, = *params.first
50
-
51
- return unless flatten_level
52
-
53
- range = range_between(node.loc.dot.begin_pos,
54
- node.source_range.end_pos)
55
-
56
- lambda do |corrector|
57
- corrector.remove(range)
58
- corrector.replace(map_node.loc.selector, 'flat_map')
59
- end
60
- end
61
-
62
48
  private
63
49
 
64
50
  def offense_for_levels(node, map_node, first_method, flatten)
65
51
  message = MSG + FLATTEN_MULTIPLE_LEVELS
52
+
66
53
  register_offense(node, map_node, first_method, flatten, message)
67
54
  end
68
55
 
@@ -71,13 +58,24 @@ module RuboCop
71
58
  end
72
59
 
73
60
  def register_offense(node, map_node, first_method, flatten, message)
74
- range = range_between(map_node.loc.selector.begin_pos,
75
- node.loc.expression.end_pos)
61
+ range = range_between(map_node.loc.selector.begin_pos, node.loc.expression.end_pos)
62
+ message = format(message, method: first_method, flatten: flatten)
63
+
64
+ add_offense(range, message: message) do |corrector|
65
+ autocorrect(corrector, node)
66
+ end
67
+ end
68
+
69
+ def autocorrect(corrector, node)
70
+ map_node, _first_method, _flatten, params = flat_map_candidate?(node)
71
+ flatten_level, = *params.first
72
+
73
+ return unless flatten_level
74
+
75
+ range = range_between(node.loc.dot.begin_pos, node.source_range.end_pos)
76
76
 
77
- add_offense(node,
78
- location: range,
79
- message: format(message, method: first_method,
80
- flatten: flatten))
77
+ corrector.remove(range)
78
+ corrector.replace(map_node.loc.selector, 'flat_map')
81
79
  end
82
80
  end
83
81
  end