rubocop-performance 1.6.1 → 1.9.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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -0
  3. data/config/default.yml +95 -8
  4. data/lib/rubocop/cop/mixin/regexp_metacharacter.rb +4 -4
  5. data/lib/rubocop/cop/mixin/sort_block.rb +28 -0
  6. data/lib/rubocop/cop/performance/ancestors_include.rb +49 -0
  7. data/lib/rubocop/cop/performance/array_semi_infinite_range_slice.rb +74 -0
  8. data/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb +46 -0
  9. data/lib/rubocop/cop/performance/bind_call.rb +9 -18
  10. data/lib/rubocop/cop/performance/block_given_with_explicit_block.rb +52 -0
  11. data/lib/rubocop/cop/performance/caller.rb +14 -15
  12. data/lib/rubocop/cop/performance/case_when_splat.rb +18 -11
  13. data/lib/rubocop/cop/performance/casecmp.rb +13 -20
  14. data/lib/rubocop/cop/performance/chain_array_allocation.rb +4 -10
  15. data/lib/rubocop/cop/performance/collection_literal_in_loop.rb +140 -0
  16. data/lib/rubocop/cop/performance/compare_with_block.rb +10 -21
  17. data/lib/rubocop/cop/performance/constant_regexp.rb +68 -0
  18. data/lib/rubocop/cop/performance/count.rb +14 -16
  19. data/lib/rubocop/cop/performance/delete_prefix.rb +14 -22
  20. data/lib/rubocop/cop/performance/delete_suffix.rb +14 -22
  21. data/lib/rubocop/cop/performance/detect.rb +65 -32
  22. data/lib/rubocop/cop/performance/double_start_end_with.rb +16 -24
  23. data/lib/rubocop/cop/performance/end_with.rb +9 -13
  24. data/lib/rubocop/cop/performance/fixed_size.rb +2 -1
  25. data/lib/rubocop/cop/performance/flat_map.rb +21 -22
  26. data/lib/rubocop/cop/performance/inefficient_hash_search.rb +15 -14
  27. data/lib/rubocop/cop/performance/io_readlines.rb +112 -0
  28. data/lib/rubocop/cop/performance/method_object_as_block.rb +32 -0
  29. data/lib/rubocop/cop/performance/open_struct.rb +3 -2
  30. data/lib/rubocop/cop/performance/range_include.rb +15 -11
  31. data/lib/rubocop/cop/performance/redundant_block_call.rb +15 -10
  32. data/lib/rubocop/cop/performance/redundant_match.rb +12 -6
  33. data/lib/rubocop/cop/performance/redundant_merge.rb +19 -17
  34. data/lib/rubocop/cop/performance/redundant_sort_block.rb +43 -0
  35. data/lib/rubocop/cop/performance/redundant_string_chars.rb +129 -0
  36. data/lib/rubocop/cop/performance/regexp_match.rb +20 -20
  37. data/lib/rubocop/cop/performance/reverse_each.rb +10 -5
  38. data/lib/rubocop/cop/performance/reverse_first.rb +73 -0
  39. data/lib/rubocop/cop/performance/size.rb +42 -43
  40. data/lib/rubocop/cop/performance/sort_reverse.rb +45 -0
  41. data/lib/rubocop/cop/performance/squeeze.rb +67 -0
  42. data/lib/rubocop/cop/performance/start_with.rb +9 -13
  43. data/lib/rubocop/cop/performance/string_include.rb +56 -0
  44. data/lib/rubocop/cop/performance/string_replacement.rb +24 -27
  45. data/lib/rubocop/cop/performance/sum.rb +236 -0
  46. data/lib/rubocop/cop/performance/times_map.rb +12 -18
  47. data/lib/rubocop/cop/performance/unfreeze_string.rb +20 -2
  48. data/lib/rubocop/cop/performance/uri_default_parser.rb +7 -12
  49. data/lib/rubocop/cop/performance_cops.rb +16 -0
  50. data/lib/rubocop/performance/version.rb +6 -1
  51. metadata +35 -13
@@ -9,67 +9,66 @@ module RuboCop
9
9
  # @example
10
10
  # # bad
11
11
  # [1, 2, 3].count
12
+ # (1..3).to_a.count
13
+ # Array[*1..3].count
14
+ # Array(1..3).count
12
15
  #
13
16
  # # bad
14
17
  # {a: 1, b: 2, c: 3}.count
18
+ # [[:foo, :bar], [1, 2]].to_h.count
19
+ # Hash[*('a'..'z')].count
20
+ # Hash(key: :value).count
15
21
  #
16
22
  # # good
17
23
  # [1, 2, 3].size
24
+ # (1..3).to_a.size
25
+ # Array[*1..3].size
26
+ # Array(1..3).size
18
27
  #
19
28
  # # good
20
29
  # {a: 1, b: 2, c: 3}.size
30
+ # [[:foo, :bar], [1, 2]].to_h.size
31
+ # Hash[*('a'..'z')].size
32
+ # Hash(key: :value).size
21
33
  #
22
34
  # # good
23
35
  # [1, 2, 3].count { |e| e > 2 }
24
36
  # TODO: Add advanced detection of variables that could
25
37
  # have been assigned to an array or a hash.
26
- class Size < Cop
38
+ class Size < Base
39
+ extend AutoCorrector
40
+
27
41
  MSG = 'Use `size` instead of `count`.'
42
+ RESTRICT_ON_SEND = %i[count].freeze
43
+
44
+ def_node_matcher :array?, <<~PATTERN
45
+ {
46
+ [!nil? array_type?]
47
+ (send _ :to_a)
48
+ (send (const nil? :Array) :[] _)
49
+ (send nil? :Array _)
50
+ }
51
+ PATTERN
52
+
53
+ def_node_matcher :hash?, <<~PATTERN
54
+ {
55
+ [!nil? hash_type?]
56
+ (send _ :to_h)
57
+ (send (const nil? :Hash) :[] _)
58
+ (send nil? :Hash _)
59
+ }
60
+ PATTERN
61
+
62
+ def_node_matcher :count?, <<~PATTERN
63
+ (send {#array? #hash?} :count)
64
+ PATTERN
28
65
 
29
66
  def on_send(node)
30
- return unless eligible_node?(node)
31
-
32
- add_offense(node, location: :selector)
33
- end
34
-
35
- def autocorrect(node)
36
- ->(corrector) { corrector.replace(node.loc.selector, 'size') }
37
- end
38
-
39
- private
40
-
41
- def eligible_node?(node)
42
- return false unless node.method?(:count) && !node.arguments?
43
-
44
- eligible_receiver?(node.receiver) && !allowed_parent?(node.parent)
45
- end
46
-
47
- def eligible_receiver?(node)
48
- return false unless node
49
-
50
- array?(node) || hash?(node)
51
- end
52
-
53
- def allowed_parent?(node)
54
- node&.block_type?
55
- end
56
-
57
- def array?(node)
58
- return true if node.array_type?
59
- return false unless node.send_type?
60
-
61
- _, constant = *node.receiver
62
-
63
- constant == :Array || node.method?(:to_a)
64
- end
65
-
66
- def hash?(node)
67
- return true if node.hash_type?
68
- return false unless node.send_type?
69
-
70
- _, constant = *node.receiver
67
+ return if node.parent&.block_type? || !count?(node)
71
68
 
72
- constant == :Hash || node.method?(:to_h)
69
+ add_offense(node.loc.selector) do |corrector|
70
+ corrector.replace(node.loc.selector, 'size')
71
+ end
73
72
  end
74
73
  end
75
74
  end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # This cop identifies places where `sort { |a, b| b <=> a }`
7
+ # can be replaced by a faster `sort.reverse`.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # array.sort { |a, b| b <=> a }
12
+ #
13
+ # # good
14
+ # array.sort.reverse
15
+ #
16
+ class SortReverse < Base
17
+ include SortBlock
18
+ extend AutoCorrector
19
+
20
+ MSG = 'Use `sort.reverse` instead of `%<bad_method>s`.'
21
+
22
+ def on_block(node)
23
+ sort_with_block?(node) do |send, var_a, var_b, body|
24
+ replaceable_body?(body, var_b, var_a) do
25
+ range = sort_range(send, node)
26
+
27
+ add_offense(range, message: message(var_a, var_b)) do |corrector|
28
+ replacement = 'sort.reverse'
29
+
30
+ corrector.replace(range, replacement)
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ private
37
+
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)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # This cop identifies places where `gsub(/a+/, 'a')` and `gsub!(/a+/, 'a')`
7
+ # can be replaced by `squeeze('a')` and `squeeze!('a')`.
8
+ #
9
+ # The `squeeze('a')` method is faster than `gsub(/a+/, 'a')`.
10
+ #
11
+ # @example
12
+ #
13
+ # # bad
14
+ # str.gsub(/a+/, 'a')
15
+ # str.gsub!(/a+/, 'a')
16
+ #
17
+ # # good
18
+ # str.squeeze('a')
19
+ # str.squeeze!('a')
20
+ #
21
+ class Squeeze < Base
22
+ extend AutoCorrector
23
+
24
+ MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
25
+ RESTRICT_ON_SEND = %i[gsub gsub!].freeze
26
+
27
+ PREFERRED_METHODS = {
28
+ gsub: :squeeze,
29
+ gsub!: :squeeze!
30
+ }.freeze
31
+
32
+ def_node_matcher :squeeze_candidate?, <<~PATTERN
33
+ (send
34
+ $!nil? ${:gsub :gsub!}
35
+ (regexp
36
+ (str $#repeating_literal?)
37
+ (regopt))
38
+ (str $_))
39
+ PATTERN
40
+
41
+ def on_send(node)
42
+ squeeze_candidate?(node) do |receiver, bad_method, regexp_str, replace_str|
43
+ regexp_str = regexp_str[0..-2] # delete '+' from the end
44
+ regexp_str = interpret_string_escapes(regexp_str)
45
+ return unless replace_str == regexp_str
46
+
47
+ good_method = PREFERRED_METHODS[bad_method]
48
+ message = format(MSG, current: bad_method, prefer: good_method)
49
+
50
+ add_offense(node.loc.selector, message: message) do |corrector|
51
+ string_literal = to_string_literal(replace_str)
52
+ new_code = "#{receiver.source}.#{good_method}(#{string_literal})"
53
+
54
+ corrector.replace(node.source_range, new_code)
55
+ end
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def repeating_literal?(regex_str)
62
+ regex_str.match?(/\A(?:#{Util::LITERAL_REGEX})\+\z/o)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -41,11 +41,13 @@ module RuboCop
41
41
  # 'abc'.match(/^ab/)
42
42
  # /^ab/.match('abc')
43
43
  #
44
- class StartWith < Cop
44
+ class StartWith < Base
45
45
  include RegexpMetacharacter
46
+ extend AutoCorrector
46
47
 
47
48
  MSG = 'Use `String#start_with?` instead of a regex match anchored to ' \
48
49
  'the beginning of the string.'
50
+ RESTRICT_ON_SEND = %i[match =~ match?].freeze
49
51
 
50
52
  def_node_matcher :redundant_regex?, <<~PATTERN
51
53
  {(send $!nil? {:match :=~ :match?} (regexp (str $#literal_at_start?) (regopt)))
@@ -54,25 +56,19 @@ module RuboCop
54
56
  PATTERN
55
57
 
56
58
  def on_send(node)
57
- return unless redundant_regex?(node)
59
+ return unless (receiver, regex_str = redundant_regex?(node))
58
60
 
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|
61
+ add_offense(node) do |corrector|
65
62
  receiver, regex_str = regex_str, receiver if receiver.is_a?(String)
66
63
  regex_str = drop_start_metacharacter(regex_str)
67
64
  regex_str = interpret_string_escapes(regex_str)
68
65
 
69
- lambda do |corrector|
70
- new_source = receiver.source + '.start_with?(' +
71
- to_string_literal(regex_str) + ')'
72
- corrector.replace(node.source_range, new_source)
73
- end
66
+ new_source = "#{receiver.source}.start_with?(#{to_string_literal(regex_str)})"
67
+
68
+ corrector.replace(node.source_range, new_source)
74
69
  end
75
70
  end
71
+ alias on_match_with_lvasgn on_send
76
72
  end
77
73
  end
78
74
  end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # This cop identifies unnecessary use of a regex where
7
+ # `String#include?` would suffice.
8
+ #
9
+ # This cop's offenses are not safe to auto-correct if a receiver is nil.
10
+ #
11
+ # @example
12
+ # # bad
13
+ # 'abc'.match?(/ab/)
14
+ # /ab/.match?('abc')
15
+ # 'abc' =~ /ab/
16
+ # /ab/ =~ 'abc'
17
+ # 'abc'.match(/ab/)
18
+ # /ab/.match('abc')
19
+ #
20
+ # # good
21
+ # 'abc'.include?('ab')
22
+ class StringInclude < Base
23
+ extend AutoCorrector
24
+
25
+ MSG = 'Use `String#include?` instead of a regex match with literal-only pattern.'
26
+ RESTRICT_ON_SEND = %i[match =~ match?].freeze
27
+
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)
31
+ (match-with-lvasgn (regexp (str $#literal?) (regopt)) $_)}
32
+ PATTERN
33
+
34
+ def on_send(node)
35
+ return unless (receiver, regex_str = redundant_regex?(node))
36
+
37
+ add_offense(node) do |corrector|
38
+ receiver, regex_str = regex_str, receiver if receiver.is_a?(String)
39
+ regex_str = interpret_string_escapes(regex_str)
40
+
41
+ new_source = "#{receiver.source}.include?(#{to_string_literal(regex_str)})"
42
+
43
+ corrector.replace(node.source_range, new_source)
44
+ end
45
+ end
46
+ alias on_match_with_lvasgn on_send
47
+
48
+ private
49
+
50
+ def literal?(regex_str)
51
+ regex_str.match?(/\A#{Util::LITERAL_REGEX}+\z/o)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -18,10 +18,12 @@ module RuboCop
18
18
  # 'abc'.gsub(/a+/, 'd')
19
19
  # 'abc'.tr('b', 'd')
20
20
  # 'a b c'.delete(' ')
21
- class StringReplacement < Cop
21
+ class StringReplacement < Base
22
22
  include RangeHelp
23
+ extend AutoCorrector
23
24
 
24
25
  MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
26
+ RESTRICT_ON_SEND = %i[gsub gsub!].freeze
25
27
  DETERMINISTIC_REGEX = /\A(?:#{LITERAL_REGEX})+\Z/.freeze
26
28
  DELETE = 'delete'
27
29
  TR = 'tr'
@@ -42,33 +44,37 @@ module RuboCop
42
44
  end
43
45
  end
44
46
 
45
- def autocorrect(node)
47
+ private
48
+
49
+ def offense(node, first_param, second_param)
50
+ first_source, = first_source(first_param)
51
+ first_source = interpret_string_escapes(first_source) unless first_param.str_type?
52
+ second_source, = *second_param
53
+ message = message(node, first_source, second_source)
54
+
55
+ add_offense(range(node), message: message) do |corrector|
56
+ autocorrect(corrector, node)
57
+ end
58
+ end
59
+
60
+ def autocorrect(corrector, node)
46
61
  _string, _method, first_param, second_param = *node
47
62
  first_source, = first_source(first_param)
48
63
  second_source, = *second_param
49
64
 
50
65
  first_source = interpret_string_escapes(first_source) unless first_param.str_type?
51
66
 
52
- replacement_method =
53
- replacement_method(node, first_source, second_source)
54
-
55
- replace_method(node, first_source, second_source, first_param,
56
- replacement_method)
67
+ replace_method(corrector, node, first_source, second_source, first_param)
57
68
  end
58
69
 
59
- def replace_method(node, first, second, first_param, replacement)
60
- lambda do |corrector|
61
- corrector.replace(node.loc.selector, replacement)
62
- unless first_param.str_type?
63
- corrector.replace(first_param.source_range,
64
- to_string_literal(first))
65
- end
70
+ def replace_method(corrector, node, first_source, second_source, first_param)
71
+ replacement_method = replacement_method(node, first_source, second_source)
66
72
 
67
- remove_second_param(corrector, node, first_param) if second.empty? && first.length == 1
68
- end
69
- end
73
+ corrector.replace(node.loc.selector, replacement_method)
74
+ corrector.replace(first_param.source_range, to_string_literal(first_source)) unless first_param.str_type?
70
75
 
71
- private
76
+ remove_second_param(corrector, node, first_param) if second_source.empty? && first_source.length == 1
77
+ end
72
78
 
73
79
  def accept_second_param?(second_param)
74
80
  second_source, = *second_param
@@ -92,15 +98,6 @@ module RuboCop
92
98
  first_source.length != 1
93
99
  end
94
100
 
95
- def offense(node, first_param, second_param)
96
- first_source, = first_source(first_param)
97
- first_source = interpret_string_escapes(first_source) unless first_param.str_type?
98
- second_source, = *second_param
99
- message = message(node, first_source, second_source)
100
-
101
- add_offense(node, location: range(node), message: message)
102
- end
103
-
104
101
  def first_source(first_param)
105
102
  case first_param.type
106
103
  when :regexp
@@ -0,0 +1,236 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # This cop identifies places where custom code finding the sum of elements
7
+ # in some Enumerable object can be replaced by `Enumerable#sum` method.
8
+ #
9
+ # This cop can change auto-correction scope depending on the value of
10
+ # `SafeAutoCorrect`.
11
+ # Its auto-correction is marked as safe by default (`SafeAutoCorrect: true`)
12
+ # to prevent `TypeError` in auto-correced code when initial value is not
13
+ # specified as shown below:
14
+ #
15
+ # [source,ruby]
16
+ # ----
17
+ # ['a', 'b'].sum # => (String can't be coerced into Integer)
18
+ # ----
19
+ #
20
+ # Therefore if initial value is not specified, unsafe auto-corrected will not occur.
21
+ #
22
+ # If you always want to enable auto-correction, you can set `SafeAutoCorrect: false`.
23
+ #
24
+ # [source,yaml]
25
+ # ----
26
+ # Performance/Sum:
27
+ # SafeAutoCorrect: false
28
+ # ----
29
+ #
30
+ # Please note that the auto-correction command line option will be changed from
31
+ # `rubocop -a` to `rubocop -A`, which includes unsafe auto-correction.
32
+ #
33
+ # @example
34
+ # # bad
35
+ # [1, 2, 3].inject(:+) # These bad cases with no initial value are unsafe and
36
+ # [1, 2, 3].inject(&:+) # will not be auto-correced by default. If you want to
37
+ # [1, 2, 3].reduce { |acc, elem| acc + elem } # auto-corrected, you can set `SafeAutoCorrect: false`.
38
+ # [1, 2, 3].reduce(10, :+)
39
+ # [1, 2, 3].map { |elem| elem ** 2 }.sum
40
+ # [1, 2, 3].collect(&:count).sum(10)
41
+ #
42
+ # # good
43
+ # [1, 2, 3].sum
44
+ # [1, 2, 3].sum(10)
45
+ # [1, 2, 3].sum { |elem| elem ** 2 }
46
+ # [1, 2, 3].sum(10, &:count)
47
+ #
48
+ class Sum < Base
49
+ include RangeHelp
50
+ extend AutoCorrector
51
+
52
+ MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'
53
+ MSG_IF_NO_INIT_VALUE =
54
+ 'Use `%<good_method>s` instead of `%<bad_method>s`, unless calling `%<bad_method>s` on an empty array.'
55
+ RESTRICT_ON_SEND = %i[inject reduce sum].freeze
56
+
57
+ def_node_matcher :sum_candidate?, <<~PATTERN
58
+ (send _ ${:inject :reduce} $_init ? ${(sym :+) (block_pass (sym :+))})
59
+ PATTERN
60
+
61
+ def_node_matcher :sum_map_candidate?, <<~PATTERN
62
+ (send
63
+ {
64
+ (block $(send _ {:map :collect}) ...)
65
+ $(send _ {:map :collect} (block_pass _))
66
+ }
67
+ :sum $_init ?)
68
+ PATTERN
69
+
70
+ def_node_matcher :sum_with_block_candidate?, <<~PATTERN
71
+ (block
72
+ $(send _ {:inject :reduce} $_init ?)
73
+ (args (arg $_acc) (arg $_elem))
74
+ $send)
75
+ PATTERN
76
+
77
+ def_node_matcher :acc_plus_elem?, <<~PATTERN
78
+ (send (lvar %1) :+ (lvar %2))
79
+ PATTERN
80
+ alias elem_plus_acc? acc_plus_elem?
81
+
82
+ def on_send(node)
83
+ return if empty_array_literal?(node)
84
+
85
+ handle_sum_candidate(node)
86
+ handle_sum_map_candidate(node)
87
+ end
88
+
89
+ def on_block(node)
90
+ sum_with_block_candidate?(node) do |send, init, var_acc, var_elem, body|
91
+ if acc_plus_elem?(body, var_acc, var_elem) || elem_plus_acc?(body, var_elem, var_acc)
92
+ range = sum_block_range(send, node)
93
+ message = build_block_message(send, init, var_acc, var_elem, body)
94
+
95
+ add_offense(range, message: message) do |corrector|
96
+ autocorrect(corrector, init, range)
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ private
103
+
104
+ def handle_sum_candidate(node)
105
+ sum_candidate?(node) do |method, init, operation|
106
+ range = sum_method_range(node)
107
+ message = build_method_message(node, method, init, operation)
108
+
109
+ add_offense(range, message: message) do |corrector|
110
+ autocorrect(corrector, init, range)
111
+ end
112
+ end
113
+ end
114
+
115
+ def handle_sum_map_candidate(node)
116
+ sum_map_candidate?(node) do |map, init|
117
+ next if node.block_literal? || node.block_argument?
118
+
119
+ message = build_sum_map_message(map.method_name, init)
120
+
121
+ add_offense(sum_map_range(map, node), message: message) do |corrector|
122
+ autocorrect_sum_map(corrector, node, map, init)
123
+ end
124
+ end
125
+ end
126
+
127
+ def empty_array_literal?(node)
128
+ receiver = node.children.first
129
+ array_literal?(node) && receiver && receiver.children.empty?
130
+ end
131
+
132
+ def array_literal?(node)
133
+ receiver = node.children.first
134
+ receiver&.literal? && receiver&.array_type?
135
+ end
136
+
137
+ def autocorrect(corrector, init, range)
138
+ return if init.empty? && safe_autocorrect?
139
+
140
+ replacement = build_good_method(init)
141
+
142
+ corrector.replace(range, replacement)
143
+ end
144
+
145
+ def autocorrect_sum_map(corrector, sum, map, init)
146
+ sum_range = method_call_with_args_range(sum)
147
+ map_range = method_call_with_args_range(map)
148
+
149
+ block_pass = map.last_argument if map.last_argument&.block_pass_type?
150
+ replacement = build_good_method(init, block_pass)
151
+
152
+ corrector.remove(sum_range)
153
+ corrector.replace(map_range, ".#{replacement}")
154
+ end
155
+
156
+ def sum_method_range(node)
157
+ range_between(node.loc.selector.begin_pos, node.loc.end.end_pos)
158
+ end
159
+
160
+ def sum_map_range(map, sum)
161
+ range_between(map.loc.selector.begin_pos, sum.source_range.end.end_pos)
162
+ end
163
+
164
+ def sum_block_range(send, node)
165
+ range_between(send.loc.selector.begin_pos, node.loc.end.end_pos)
166
+ end
167
+
168
+ def build_method_message(node, method, init, operation)
169
+ good_method = build_good_method(init)
170
+ bad_method = build_method_bad_method(init, method, operation)
171
+ msg = if init.empty? && !array_literal?(node)
172
+ MSG_IF_NO_INIT_VALUE
173
+ else
174
+ MSG
175
+ end
176
+ format(msg, good_method: good_method, bad_method: bad_method)
177
+ end
178
+
179
+ def build_sum_map_message(method, init)
180
+ sum_method = build_good_method(init)
181
+ good_method = "#{sum_method} { ... }"
182
+ bad_method = "#{method} { ... }.#{sum_method}"
183
+ format(MSG, good_method: good_method, bad_method: bad_method)
184
+ end
185
+
186
+ def build_block_message(send, init, var_acc, var_elem, body)
187
+ good_method = build_good_method(init)
188
+ bad_method = build_block_bad_method(send.method_name, init, var_acc, var_elem, body)
189
+ format(MSG, good_method: good_method, bad_method: bad_method)
190
+ end
191
+
192
+ def build_good_method(init, block_pass = nil)
193
+ good_method = 'sum'
194
+
195
+ args = []
196
+ unless init.empty?
197
+ init = init.first
198
+ args << init.source unless init.int_type? && init.value.zero?
199
+ end
200
+ args << block_pass.source if block_pass
201
+ good_method += "(#{args.join(', ')})" unless args.empty?
202
+ good_method
203
+ end
204
+
205
+ def build_method_bad_method(init, method, operation)
206
+ bad_method = "#{method}("
207
+ unless init.empty?
208
+ init = init.first
209
+ bad_method += "#{init.source}, "
210
+ end
211
+ bad_method += if operation.block_pass_type?
212
+ '&:+)'
213
+ else
214
+ ':+)'
215
+ end
216
+ bad_method
217
+ end
218
+
219
+ def build_block_bad_method(method, init, var_acc, var_elem, body)
220
+ bad_method = method.to_s
221
+
222
+ unless init.empty?
223
+ init = init.first
224
+ bad_method += "(#{init.source})"
225
+ end
226
+ bad_method += " { |#{var_acc}, #{var_elem}| #{body.source} }"
227
+ bad_method
228
+ end
229
+
230
+ def method_call_with_args_range(node)
231
+ node.receiver.source_range.end.join(node.source_range.end)
232
+ end
233
+ end
234
+ end
235
+ end
236
+ end