rubocop-performance 1.18.0 → 1.20.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -0
  3. data/config/default.yml +22 -16
  4. data/lib/rubocop/cop/mixin/sort_block.rb +2 -2
  5. data/lib/rubocop/cop/performance/array_semi_infinite_range_slice.rb +2 -1
  6. data/lib/rubocop/cop/performance/block_given_with_explicit_block.rb +4 -0
  7. data/lib/rubocop/cop/performance/count.rb +5 -5
  8. data/lib/rubocop/cop/performance/delete_prefix.rb +5 -2
  9. data/lib/rubocop/cop/performance/delete_suffix.rb +5 -2
  10. data/lib/rubocop/cop/performance/detect.rb +3 -2
  11. data/lib/rubocop/cop/performance/end_with.rb +4 -2
  12. data/lib/rubocop/cop/performance/fixed_size.rb +4 -3
  13. data/lib/rubocop/cop/performance/flat_map.rb +4 -3
  14. data/lib/rubocop/cop/performance/inefficient_hash_search.rb +16 -10
  15. data/lib/rubocop/cop/performance/map_compact.rb +5 -4
  16. data/lib/rubocop/cop/performance/map_method_chain.rb +89 -0
  17. data/lib/rubocop/cop/performance/range_include.rb +8 -6
  18. data/lib/rubocop/cop/performance/redundant_equality_comparison_block.rb +1 -1
  19. data/lib/rubocop/cop/performance/redundant_match.rb +30 -1
  20. data/lib/rubocop/cop/performance/redundant_merge.rb +5 -3
  21. data/lib/rubocop/cop/performance/redundant_split_regexp_argument.rb +2 -1
  22. data/lib/rubocop/cop/performance/reverse_each.rb +2 -1
  23. data/lib/rubocop/cop/performance/reverse_first.rb +6 -13
  24. data/lib/rubocop/cop/performance/select_map.rb +3 -6
  25. data/lib/rubocop/cop/performance/size.rb +4 -3
  26. data/lib/rubocop/cop/performance/sort_reverse.rb +5 -4
  27. data/lib/rubocop/cop/performance/squeeze.rb +5 -2
  28. data/lib/rubocop/cop/performance/start_with.rb +4 -2
  29. data/lib/rubocop/cop/performance/string_identifier_argument.rb +58 -10
  30. data/lib/rubocop/cop/performance/string_include.rb +9 -4
  31. data/lib/rubocop/cop/performance/string_replacement.rb +2 -1
  32. data/lib/rubocop/cop/performance/sum.rb +11 -9
  33. data/lib/rubocop/cop/performance/times_map.rb +14 -2
  34. data/lib/rubocop/cop/performance/unfreeze_string.rb +6 -3
  35. data/lib/rubocop/cop/performance/uri_default_parser.rb +1 -1
  36. data/lib/rubocop/cop/performance_cops.rb +1 -0
  37. data/lib/rubocop/performance/version.rb +1 -1
  38. data/lib/rubocop-performance.rb +8 -0
  39. metadata +15 -8
@@ -23,6 +23,8 @@ module RuboCop
23
23
  MSG = 'Use `=~` in places where the `MatchData` returned by `#match` will not be used.'
24
24
  RESTRICT_ON_SEND = %i[match].freeze
25
25
 
26
+ HIGHER_PRECEDENCE_OPERATOR_METHODS = %i[| ^ & + - * / % ** > >= < <= << >>].freeze
27
+
26
28
  # 'match' is a fairly generic name, so we don't flag it unless we see
27
29
  # a string or regexp literal on one side or the other
28
30
  def_node_matcher :match_call?, <<~PATTERN
@@ -47,7 +49,7 @@ module RuboCop
47
49
  private
48
50
 
49
51
  def autocorrect(corrector, node)
50
- new_source = "#{node.receiver.source} =~ #{node.first_argument.source}"
52
+ new_source = "#{node.receiver.source} =~ #{replacement(node)}"
51
53
 
52
54
  corrector.replace(node, new_source)
53
55
  end
@@ -57,6 +59,33 @@ module RuboCop
57
59
  # register an offense in that case
58
60
  node.receiver.regexp_type? || node.first_argument.regexp_type?
59
61
  end
62
+
63
+ def replacement(node)
64
+ arg = node.first_argument
65
+
66
+ if requires_parentheses?(arg)
67
+ "(#{arg.source})"
68
+ else
69
+ arg.source
70
+ end
71
+ end
72
+
73
+ def requires_parentheses?(arg)
74
+ return true if arg.if_type? && arg.ternary?
75
+ return true if arg.and_type? || arg.or_type? || arg.range_type?
76
+
77
+ call_like?(arg) && requires_parentheses_for_call_like?(arg)
78
+ end
79
+
80
+ def requires_parentheses_for_call_like?(arg)
81
+ return false if arg.parenthesized? || !arg.arguments?
82
+
83
+ !HIGHER_PRECEDENCE_OPERATOR_METHODS.include?(arg.method_name)
84
+ end
85
+
86
+ def call_like?(arg)
87
+ arg.call_type? || arg.yield_type? || arg.super_type?
88
+ end
60
89
  end
61
90
  end
62
91
  end
@@ -109,7 +109,7 @@ module RuboCop
109
109
  node = parent
110
110
  else
111
111
  padding = "\n#{leading_spaces(node)}"
112
- new_source.gsub!(/\n/, padding)
112
+ new_source.gsub!("\n", padding)
113
113
  end
114
114
 
115
115
  corrector.replace(node, new_source)
@@ -130,9 +130,11 @@ module RuboCop
130
130
  end
131
131
 
132
132
  def rewrite_with_modifier(node, parent, new_source)
133
- indent = ' ' * configured_indentation_width
133
+ # FIXME: `|| 2` can be removed when support is limited to RuboCop 1.44 or higher.
134
+ # https://github.com/rubocop/rubocop/commit/02d1e5b
135
+ indent = ' ' * (configured_indentation_width || 2)
134
136
  padding = "\n#{indent + leading_spaces(node)}"
135
- new_source.gsub!(/\n/, padding)
137
+ new_source.gsub!("\n", padding)
136
138
 
137
139
  format(WITH_MODIFIER_CORRECTION, keyword: parent.loc.keyword.source,
138
140
  condition: parent.condition.source,
@@ -21,7 +21,7 @@ module RuboCop
21
21
  STR_SPECIAL_CHARS = %w[\n \" \' \\\\ \t \b \f \r].freeze
22
22
 
23
23
  def_node_matcher :split_call_with_regexp?, <<~PATTERN
24
- {(send !nil? :split $regexp)}
24
+ {(call !nil? :split $regexp)}
25
25
  PATTERN
26
26
 
27
27
  def on_send(node)
@@ -35,6 +35,7 @@ module RuboCop
35
35
  corrector.replace(regexp_node, "\"#{new_argument}\"")
36
36
  end
37
37
  end
38
+ alias on_csend on_send
38
39
 
39
40
  private
40
41
 
@@ -27,7 +27,7 @@ module RuboCop
27
27
  RESTRICT_ON_SEND = %i[each].freeze
28
28
 
29
29
  def_node_matcher :reverse_each?, <<~MATCHER
30
- (send (send _ :reverse) :each)
30
+ (call (call _ :reverse) :each)
31
31
  MATCHER
32
32
 
33
33
  def on_send(node)
@@ -41,6 +41,7 @@ module RuboCop
41
41
  end
42
42
  end
43
43
  end
44
+ alias on_csend on_send
44
45
 
45
46
  private
46
47
 
@@ -24,13 +24,13 @@ module RuboCop
24
24
  RESTRICT_ON_SEND = %i[first].freeze
25
25
 
26
26
  def_node_matcher :reverse_first_candidate?, <<~PATTERN
27
- (send $(send _ :reverse) :first (int _)?)
27
+ (call $(call _ :reverse) :first (int _)?)
28
28
  PATTERN
29
29
 
30
30
  def on_send(node)
31
31
  reverse_first_candidate?(node) do |receiver|
32
32
  range = correction_range(receiver, node)
33
- message = build_message(node)
33
+ message = build_message(node, range)
34
34
 
35
35
  add_offense(range, message: message) do |corrector|
36
36
  replacement = build_good_method(node)
@@ -39,6 +39,7 @@ module RuboCop
39
39
  end
40
40
  end
41
41
  end
42
+ alias on_csend on_send
42
43
 
43
44
  private
44
45
 
@@ -46,27 +47,19 @@ module RuboCop
46
47
  range_between(receiver.loc.selector.begin_pos, node.source_range.end_pos)
47
48
  end
48
49
 
49
- def build_message(node)
50
+ def build_message(node, range)
50
51
  good_method = build_good_method(node)
51
- bad_method = build_bad_method(node)
52
+ bad_method = range.source
52
53
  format(MSG, good_method: good_method, bad_method: bad_method)
53
54
  end
54
55
 
55
56
  def build_good_method(node)
56
57
  if node.arguments?
57
- "last(#{node.arguments.first.source}).reverse"
58
+ "last(#{node.first_argument.source})#{node.loc.dot.source}reverse"
58
59
  else
59
60
  'last'
60
61
  end
61
62
  end
62
-
63
- def build_bad_method(node)
64
- if node.arguments?
65
- "reverse.first(#{node.arguments.first.source})"
66
- else
67
- 'reverse.first'
68
- end
69
- end
70
63
  end
71
64
  end
72
65
  end
@@ -24,10 +24,6 @@ module RuboCop
24
24
  MSG = 'Use `filter_map` instead of `%<method_name>s.map`.'
25
25
  RESTRICT_ON_SEND = %i[select filter].freeze
26
26
 
27
- def_node_matcher :bad_method?, <<~PATTERN
28
- (send nil? :bad_method ...)
29
- PATTERN
30
-
31
27
  def on_send(node)
32
28
  return if (first_argument = node.first_argument) && !first_argument.block_pass_type?
33
29
  return unless (send_node = map_method_candidate(node))
@@ -38,15 +34,16 @@ module RuboCop
38
34
  range = offense_range(node, map_method)
39
35
  add_offense(range, message: format(MSG, method_name: node.method_name))
40
36
  end
37
+ alias on_csend on_send
41
38
 
42
39
  private
43
40
 
44
41
  def map_method_candidate(node)
45
42
  return unless (parent = node.parent)
46
43
 
47
- if parent.block_type? && parent.parent&.send_type?
44
+ if parent.block_type? && parent.parent&.call_type?
48
45
  parent.parent
49
- elsif parent.send_type?
46
+ elsif parent.call_type?
50
47
  parent
51
48
  end
52
49
  end
@@ -43,7 +43,7 @@ module RuboCop
43
43
  def_node_matcher :array?, <<~PATTERN
44
44
  {
45
45
  [!nil? array_type?]
46
- (send _ :to_a)
46
+ (call _ :to_a)
47
47
  (send (const nil? :Array) :[] _)
48
48
  (send nil? :Array _)
49
49
  }
@@ -52,14 +52,14 @@ module RuboCop
52
52
  def_node_matcher :hash?, <<~PATTERN
53
53
  {
54
54
  [!nil? hash_type?]
55
- (send _ :to_h)
55
+ (call _ :to_h)
56
56
  (send (const nil? :Hash) :[] _)
57
57
  (send nil? :Hash _)
58
58
  }
59
59
  PATTERN
60
60
 
61
61
  def_node_matcher :count?, <<~PATTERN
62
- (send {#array? #hash?} :count)
62
+ (call {#array? #hash?} :count)
63
63
  PATTERN
64
64
 
65
65
  def on_send(node)
@@ -69,6 +69,7 @@ module RuboCop
69
69
  corrector.replace(node.loc.selector, 'size')
70
70
  end
71
71
  end
72
+ alias on_csend on_send
72
73
  end
73
74
  end
74
75
  end
@@ -17,7 +17,7 @@ module RuboCop
17
17
  include SortBlock
18
18
  extend AutoCorrector
19
19
 
20
- MSG = 'Use `sort.reverse` instead.'
20
+ MSG = 'Use `%<prefer>s` instead.'
21
21
 
22
22
  def on_block(node)
23
23
  sort_with_block?(node) do |send, var_a, var_b, body|
@@ -41,11 +41,12 @@ module RuboCop
41
41
 
42
42
  def register_offense(send, node)
43
43
  range = sort_range(send, node)
44
+ prefer = "sort#{send.loc.dot.source}reverse"
44
45
 
45
- add_offense(range) do |corrector|
46
- replacement = 'sort.reverse'
46
+ message = format(MSG, prefer: prefer)
47
47
 
48
- corrector.replace(range, replacement)
48
+ add_offense(range, message: message) do |corrector|
49
+ corrector.replace(range, prefer)
49
50
  end
50
51
  end
51
52
  end
@@ -27,7 +27,7 @@ module RuboCop
27
27
  PREFERRED_METHODS = { gsub: :squeeze, gsub!: :squeeze! }.freeze
28
28
 
29
29
  def_node_matcher :squeeze_candidate?, <<~PATTERN
30
- (send
30
+ (call
31
31
  $!nil? ${:gsub :gsub!}
32
32
  (regexp
33
33
  (str $#repeating_literal?)
@@ -35,6 +35,7 @@ module RuboCop
35
35
  (str $_))
36
36
  PATTERN
37
37
 
38
+ # rubocop:disable Metrics/AbcSize
38
39
  def on_send(node)
39
40
  squeeze_candidate?(node) do |receiver, bad_method, regexp_str, replace_str|
40
41
  regexp_str = regexp_str[0..-2] # delete '+' from the end
@@ -46,12 +47,14 @@ module RuboCop
46
47
 
47
48
  add_offense(node.loc.selector, message: message) do |corrector|
48
49
  string_literal = to_string_literal(replace_str)
49
- new_code = "#{receiver.source}.#{good_method}(#{string_literal})"
50
+ new_code = "#{receiver.source}#{node.loc.dot.source}#{good_method}(#{string_literal})"
50
51
 
51
52
  corrector.replace(node, new_code)
52
53
  end
53
54
  end
54
55
  end
56
+ # rubocop:enable Metrics/AbcSize
57
+ alias on_csend on_send
55
58
 
56
59
  private
57
60
 
@@ -54,7 +54,7 @@ module RuboCop
54
54
  RESTRICT_ON_SEND = %i[match =~ match?].freeze
55
55
 
56
56
  def_node_matcher :redundant_regex?, <<~PATTERN
57
- {(send $!nil? {:match :=~ :match?} (regexp (str $#literal_at_start?) (regopt)))
57
+ {(call $!nil? {:match :=~ :match?} (regexp (str $#literal_at_start?) (regopt)))
58
58
  (send (regexp (str $#literal_at_start?) (regopt)) {:match :match?} $_)
59
59
  (match-with-lvasgn (regexp (str $#literal_at_start?) (regopt)) $_)}
60
60
  PATTERN
@@ -66,12 +66,14 @@ module RuboCop
66
66
  receiver, regex_str = regex_str, receiver if receiver.is_a?(String)
67
67
  regex_str = drop_start_metacharacter(regex_str)
68
68
  regex_str = interpret_string_escapes(regex_str)
69
+ dot = node.loc.dot ? node.loc.dot.source : '.'
69
70
 
70
- new_source = "#{receiver.source}.start_with?(#{to_string_literal(regex_str)})"
71
+ new_source = "#{receiver.source}#{dot}start_with?(#{to_string_literal(regex_str)})"
71
72
 
72
73
  corrector.replace(node, new_source)
73
74
  end
74
75
  end
76
+ alias on_csend on_send
75
77
  alias on_match_with_lvasgn on_send
76
78
  end
77
79
  end
@@ -16,11 +16,19 @@ module RuboCop
16
16
  # send('do_something')
17
17
  # attr_accessor 'do_something'
18
18
  # instance_variable_get('@ivar')
19
+ # respond_to?("string_#{interpolation}")
19
20
  #
20
21
  # # good
21
22
  # send(:do_something)
22
23
  # attr_accessor :do_something
23
24
  # instance_variable_get(:@ivar)
25
+ # respond_to?(:"string_#{interpolation}")
26
+ #
27
+ # # good - these methods don't support namespaced symbols
28
+ # const_get("#{module_path}::Base")
29
+ # const_source_location("#{module_path}::Base")
30
+ # const_defined?("#{module_path}::Base")
31
+ #
24
32
  #
25
33
  class StringIdentifierArgument < Base
26
34
  extend AutoCorrector
@@ -32,33 +40,73 @@ module RuboCop
32
40
  protected public public_constant module_function
33
41
  ].freeze
34
42
 
43
+ INTERPOLATION_IGNORE_METHODS = %i[const_get const_source_location const_defined?].freeze
44
+
45
+ TWO_ARGUMENTS_METHOD = :alias_method
46
+ MULTIPLE_ARGUMENTS_METHODS = %i[
47
+ attr_accessor attr_reader attr_writer private private_constant
48
+ protected public public_constant module_function
49
+ ].freeze
50
+
35
51
  # NOTE: `attr` method is not included in this list as it can cause false positives in Nokogiri API.
36
52
  # And `attr` may not be used because `Style/Attr` registers an offense.
37
53
  # https://github.com/rubocop/rubocop-performance/issues/278
38
54
  RESTRICT_ON_SEND = (%i[
39
- class_variable_defined? const_defined? const_get const_set const_source_location
55
+ class_variable_defined? const_set
40
56
  define_method instance_method method_defined? private_class_method? private_method_defined?
41
57
  protected_method_defined? public_class_method public_instance_method public_method_defined?
42
58
  remove_class_variable remove_method undef_method class_variable_get class_variable_set
43
59
  deprecate_constant remove_const ruby2_keywords define_singleton_method instance_variable_defined?
44
60
  instance_variable_get instance_variable_set method public_method public_send remove_instance_variable
45
61
  respond_to? send singleton_method __send__
46
- ] + COMMAND_METHODS).freeze
62
+ ] + COMMAND_METHODS + INTERPOLATION_IGNORE_METHODS).freeze
47
63
 
48
64
  def on_send(node)
49
65
  return if COMMAND_METHODS.include?(node.method_name) && node.receiver
50
- return unless (first_argument = node.first_argument)
51
- return unless first_argument.str_type?
52
66
 
53
- first_argument_value = first_argument.value
54
- return if first_argument_value.include?(' ') || first_argument_value.include?('::')
67
+ string_arguments(node).each do |string_argument|
68
+ string_argument_value = string_argument.value
69
+ next if string_argument_value.include?(' ') || string_argument_value.include?('::')
70
+
71
+ register_offense(string_argument, string_argument_value)
72
+ end
73
+ end
74
+
75
+ private
55
76
 
56
- replacement = first_argument_value.to_sym.inspect
77
+ def string_arguments(node)
78
+ arguments = if node.method?(TWO_ARGUMENTS_METHOD)
79
+ [node.first_argument, node.arguments[1]]
80
+ elsif MULTIPLE_ARGUMENTS_METHODS.include?(node.method_name)
81
+ node.arguments
82
+ else
83
+ [node.first_argument]
84
+ end
57
85
 
58
- message = format(MSG, symbol_arg: replacement, string_arg: first_argument.source)
86
+ arguments.compact.filter { |argument| string_argument_compatible?(argument, node) }
87
+ end
88
+
89
+ def string_argument_compatible?(argument, node)
90
+ return true if argument.str_type?
91
+
92
+ argument.dstr_type? && INTERPOLATION_IGNORE_METHODS.none? { |method| node.method?(method) }
93
+ end
94
+
95
+ def register_offense(argument, argument_value)
96
+ replacement = argument_replacement(argument, argument_value)
97
+
98
+ message = format(MSG, symbol_arg: replacement, string_arg: argument.source)
99
+
100
+ add_offense(argument, message: message) do |corrector|
101
+ corrector.replace(argument, replacement)
102
+ end
103
+ end
59
104
 
60
- add_offense(first_argument, message: message) do |corrector|
61
- corrector.replace(first_argument, replacement)
105
+ def argument_replacement(node, value)
106
+ if node.str_type?
107
+ value.to_sym.inspect
108
+ else
109
+ ":\"#{value.to_sym}\""
62
110
  end
63
111
  end
64
112
  end
@@ -16,6 +16,7 @@ module RuboCop
16
16
  # /ab/ =~ str
17
17
  # str.match(/ab/)
18
18
  # /ab/.match(str)
19
+ # /ab/ === str
19
20
  #
20
21
  # # good
21
22
  # str.include?('ab')
@@ -23,14 +24,15 @@ module RuboCop
23
24
  extend AutoCorrector
24
25
 
25
26
  MSG = 'Use `%<negation>sString#include?` instead of a regex match with literal-only pattern.'
26
- RESTRICT_ON_SEND = %i[match =~ !~ match?].freeze
27
+ RESTRICT_ON_SEND = %i[match =~ !~ match? ===].freeze
27
28
 
28
29
  def_node_matcher :redundant_regex?, <<~PATTERN
29
- {(send $!nil? {:match :=~ :!~ :match?} (regexp (str $#literal?) (regopt)))
30
- (send (regexp (str $#literal?) (regopt)) {:match :match?} $_)
30
+ {(call $!nil? {:match :=~ :!~ :match?} (regexp (str $#literal?) (regopt)))
31
+ (send (regexp (str $#literal?) (regopt)) {:match :match? :===} $_)
31
32
  (match-with-lvasgn (regexp (str $#literal?) (regopt)) $_)}
32
33
  PATTERN
33
34
 
35
+ # rubocop:disable Metrics/AbcSize
34
36
  def on_send(node)
35
37
  return unless (receiver, regex_str = redundant_regex?(node))
36
38
 
@@ -40,12 +42,15 @@ module RuboCop
40
42
  add_offense(node, message: message) do |corrector|
41
43
  receiver, regex_str = regex_str, receiver if receiver.is_a?(String)
42
44
  regex_str = interpret_string_escapes(regex_str)
45
+ dot = node.loc.dot ? node.loc.dot.source : '.'
43
46
 
44
- new_source = "#{'!' if negation}#{receiver.source}.include?(#{to_string_literal(regex_str)})"
47
+ new_source = "#{'!' if negation}#{receiver.source}#{dot}include?(#{to_string_literal(regex_str)})"
45
48
 
46
49
  corrector.replace(node, new_source)
47
50
  end
48
51
  end
52
+ # rubocop:enable Metrics/AbcSize
53
+ alias on_csend on_send
49
54
  alias on_match_with_lvasgn on_send
50
55
 
51
56
  private
@@ -29,7 +29,7 @@ module RuboCop
29
29
  BANG = '!'
30
30
 
31
31
  def_node_matcher :string_replacement?, <<~PATTERN
32
- (send _ {:gsub :gsub!}
32
+ (call _ {:gsub :gsub!}
33
33
  ${regexp str (send (const nil? :Regexp) {:new :compile} _)}
34
34
  $str)
35
35
  PATTERN
@@ -42,6 +42,7 @@ module RuboCop
42
42
  offense(node, first_param, second_param)
43
43
  end
44
44
  end
45
+ alias on_csend on_send
45
46
 
46
47
  private
47
48
 
@@ -80,21 +80,21 @@ module RuboCop
80
80
  RESTRICT_ON_SEND = %i[inject reduce sum].freeze
81
81
 
82
82
  def_node_matcher :sum_candidate?, <<~PATTERN
83
- (send _ ${:inject :reduce} $_init ? ${(sym :+) (block_pass (sym :+))})
83
+ (call _ ${:inject :reduce} $_init ? ${(sym :+) (block_pass (sym :+))})
84
84
  PATTERN
85
85
 
86
86
  def_node_matcher :sum_map_candidate?, <<~PATTERN
87
- (send
87
+ (call
88
88
  {
89
- (block $(send _ {:map :collect}) ...)
90
- $(send _ {:map :collect} (block_pass _))
89
+ (block $(call _ {:map :collect}) ...)
90
+ $(call _ {:map :collect} (block_pass _))
91
91
  }
92
92
  :sum $_init ?)
93
93
  PATTERN
94
94
 
95
95
  def_node_matcher :sum_with_block_candidate?, <<~PATTERN
96
96
  (block
97
- $(send _ {:inject :reduce} $_init ?)
97
+ $(call _ {:inject :reduce} $_init ?)
98
98
  (args (arg $_acc) (arg $_elem))
99
99
  $send)
100
100
  PATTERN
@@ -110,6 +110,7 @@ module RuboCop
110
110
  handle_sum_candidate(node)
111
111
  handle_sum_map_candidate(node)
112
112
  end
113
+ alias on_csend on_send
113
114
 
114
115
  def on_block(node)
115
116
  sum_with_block_candidate?(node) do |send, init, var_acc, var_elem, body|
@@ -143,7 +144,7 @@ module RuboCop
143
144
  sum_map_candidate?(node) do |map, init|
144
145
  next if node.block_literal? || node.block_argument?
145
146
 
146
- message = build_sum_map_message(map.method_name, init)
147
+ message = build_sum_map_message(map, init)
147
148
 
148
149
  add_offense(sum_map_range(map, node), message: message) do |corrector|
149
150
  autocorrect_sum_map(corrector, node, map, init)
@@ -178,7 +179,7 @@ module RuboCop
178
179
 
179
180
  corrector.remove(sum_range)
180
181
 
181
- dot = '.' if map.receiver
182
+ dot = map.loc.dot&.source || ''
182
183
  corrector.replace(map_range, "#{dot}#{replacement}")
183
184
  end
184
185
 
@@ -205,10 +206,11 @@ module RuboCop
205
206
  format(msg, good_method: good_method, bad_method: bad_method)
206
207
  end
207
208
 
208
- def build_sum_map_message(method, init)
209
+ def build_sum_map_message(send_node, init)
209
210
  sum_method = build_good_method(init)
210
211
  good_method = "#{sum_method} { ... }"
211
- bad_method = "#{method} { ... }.#{sum_method}"
212
+ dot = send_node.loc.dot&.source || '.'
213
+ bad_method = "#{send_node.method_name} { ... }#{dot}#{sum_method}"
212
214
  format(MSG, good_method: good_method, bad_method: bad_method)
213
215
  end
214
216
 
@@ -39,6 +39,7 @@ module RuboCop
39
39
  def on_send(node)
40
40
  check(node)
41
41
  end
42
+ alias on_csend on_send
42
43
 
43
44
  def on_block(node)
44
45
  check(node)
@@ -49,6 +50,8 @@ module RuboCop
49
50
 
50
51
  def check(node)
51
52
  times_map_call(node) do |map_or_collect, count|
53
+ next unless handleable_receiver?(node)
54
+
52
55
  add_offense(node, message: message(map_or_collect, count)) do |corrector|
53
56
  replacement = "Array.new(#{count.source}#{map_or_collect.arguments.map { |arg| ", #{arg.source}" }.join})"
54
57
 
@@ -57,6 +60,13 @@ module RuboCop
57
60
  end
58
61
  end
59
62
 
63
+ def handleable_receiver?(node)
64
+ receiver = node.receiver.receiver
65
+ return true if receiver.literal? && (receiver.int_type? || receiver.float_type?)
66
+
67
+ node.receiver.dot?
68
+ end
69
+
60
70
  def message(map_or_collect, count)
61
71
  template = if count.literal?
62
72
  "#{MESSAGE}."
@@ -67,8 +77,10 @@ module RuboCop
67
77
  end
68
78
 
69
79
  def_node_matcher :times_map_call, <<~PATTERN
70
- {({block numblock} $(send (send $!nil? :times) {:map :collect}) ...)
71
- $(send (send $!nil? :times) {:map :collect} (block_pass ...))}
80
+ {
81
+ ({block numblock} $(call (call $!nil? :times) {:map :collect}) ...)
82
+ $(call (call $!nil? :times) {:map :collect} (block_pass ...))
83
+ }
72
84
  PATTERN
73
85
  end
74
86
  end
@@ -15,8 +15,8 @@ module RuboCop
15
15
  #
16
16
  # @example
17
17
  # # bad
18
- # ''.dup
19
- # "something".dup
18
+ # ''.dup # when Ruby 3.2 or lower
19
+ # "something".dup # when Ruby 3.2 or lower
20
20
  # String.new
21
21
  # String.new('')
22
22
  # String.new('something')
@@ -26,6 +26,9 @@ module RuboCop
26
26
  # +''
27
27
  class UnfreezeString < Base
28
28
  extend AutoCorrector
29
+ extend TargetRubyVersion
30
+
31
+ minimum_target_ruby_version 2.3
29
32
 
30
33
  MSG = 'Use unary plus to get an unfrozen string literal.'
31
34
  RESTRICT_ON_SEND = %i[dup new].freeze
@@ -42,7 +45,7 @@ module RuboCop
42
45
  PATTERN
43
46
 
44
47
  def on_send(node)
45
- return unless dup_string?(node) || string_new?(node)
48
+ return unless (dup_string?(node) && target_ruby_version <= 3.2) || string_new?(node)
46
49
 
47
50
  add_offense(node) do |corrector|
48
51
  string_value = "+#{string_value(node)}"
@@ -25,7 +25,7 @@ module RuboCop
25
25
  PATTERN
26
26
 
27
27
  def on_send(node)
28
- return unless uri_parser_new?(node) do |captured_value|
28
+ uri_parser_new?(node) do |captured_value|
29
29
  double_colon = captured_value ? '::' : ''
30
30
  message = format(MSG, double_colon: double_colon)
31
31
 
@@ -25,6 +25,7 @@ require_relative 'performance/fixed_size'
25
25
  require_relative 'performance/flat_map'
26
26
  require_relative 'performance/inefficient_hash_search'
27
27
  require_relative 'performance/map_compact'
28
+ require_relative 'performance/map_method_chain'
28
29
  require_relative 'performance/method_object_as_block'
29
30
  require_relative 'performance/open_struct'
30
31
  require_relative 'performance/range_include'
@@ -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.18.0'
7
+ STRING = '1.20.2'
8
8
 
9
9
  def self.document_version
10
10
  STRING.match('\d+\.\d+').to_s
@@ -9,3 +9,11 @@ require_relative 'rubocop/performance/inject'
9
9
  RuboCop::Performance::Inject.defaults!
10
10
 
11
11
  require_relative 'rubocop/cop/performance_cops'
12
+
13
+ RuboCop::Cop::Lint::UnusedMethodArgument.singleton_class.prepend(
14
+ Module.new do
15
+ def autocorrect_incompatible_with
16
+ super.push(RuboCop::Cop::Performance::BlockGivenWithExplicitBlock)
17
+ end
18
+ end
19
+ )