rubocop-performance 1.13.3 → 1.19.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +2 -2
- data/config/default.yml +13 -2
- data/lib/rubocop/cop/mixin/regexp_metacharacter.rb +2 -2
- data/lib/rubocop/cop/mixin/sort_block.rb +7 -0
- data/lib/rubocop/cop/performance/ancestors_include.rb +1 -2
- data/lib/rubocop/cop/performance/array_semi_infinite_range_slice.rb +3 -2
- data/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb +2 -6
- data/lib/rubocop/cop/performance/bind_call.rb +1 -2
- data/lib/rubocop/cop/performance/block_given_with_explicit_block.rb +1 -1
- data/lib/rubocop/cop/performance/caller.rb +1 -2
- data/lib/rubocop/cop/performance/case_when_splat.rb +7 -13
- data/lib/rubocop/cop/performance/casecmp.rb +10 -12
- data/lib/rubocop/cop/performance/chain_array_allocation.rb +5 -5
- data/lib/rubocop/cop/performance/collection_literal_in_loop.rb +4 -6
- data/lib/rubocop/cop/performance/compare_with_block.rb +20 -11
- data/lib/rubocop/cop/performance/concurrent_monotonic_time.rb +1 -1
- data/lib/rubocop/cop/performance/constant_regexp.rb +6 -4
- data/lib/rubocop/cop/performance/count.rb +39 -3
- data/lib/rubocop/cop/performance/delete_prefix.rb +8 -2
- data/lib/rubocop/cop/performance/delete_suffix.rb +8 -2
- data/lib/rubocop/cop/performance/detect.rb +7 -6
- data/lib/rubocop/cop/performance/double_start_end_with.rb +4 -5
- data/lib/rubocop/cop/performance/end_with.rb +7 -6
- data/lib/rubocop/cop/performance/fixed_size.rb +2 -2
- data/lib/rubocop/cop/performance/flat_map.rb +7 -5
- data/lib/rubocop/cop/performance/inefficient_hash_search.rb +17 -15
- data/lib/rubocop/cop/performance/io_readlines.rb +2 -2
- data/lib/rubocop/cop/performance/map_compact.rb +9 -4
- data/lib/rubocop/cop/performance/map_method_chain.rb +87 -0
- data/lib/rubocop/cop/performance/method_object_as_block.rb +1 -1
- data/lib/rubocop/cop/performance/open_struct.rb +2 -3
- data/lib/rubocop/cop/performance/range_include.rb +2 -2
- data/lib/rubocop/cop/performance/redundant_block_call.rb +2 -2
- data/lib/rubocop/cop/performance/redundant_equality_comparison_block.rb +38 -3
- data/lib/rubocop/cop/performance/redundant_match.rb +10 -9
- data/lib/rubocop/cop/performance/redundant_merge.rb +9 -16
- data/lib/rubocop/cop/performance/redundant_sort_block.rb +17 -10
- data/lib/rubocop/cop/performance/redundant_split_regexp_argument.rb +3 -2
- data/lib/rubocop/cop/performance/redundant_string_chars.rb +10 -6
- data/lib/rubocop/cop/performance/regexp_match.rb +23 -24
- data/lib/rubocop/cop/performance/reverse_each.rb +3 -3
- data/lib/rubocop/cop/performance/reverse_first.rb +4 -3
- data/lib/rubocop/cop/performance/select_map.rb +2 -1
- data/lib/rubocop/cop/performance/size.rb +1 -2
- data/lib/rubocop/cop/performance/sort_reverse.rb +19 -10
- data/lib/rubocop/cop/performance/squeeze.rb +8 -8
- data/lib/rubocop/cop/performance/start_with.rb +7 -6
- data/lib/rubocop/cop/performance/string_identifier_argument.rb +16 -11
- data/lib/rubocop/cop/performance/string_include.rb +23 -17
- data/lib/rubocop/cop/performance/string_replacement.rb +7 -10
- data/lib/rubocop/cop/performance/sum.rb +7 -4
- data/lib/rubocop/cop/performance/times_map.rb +6 -7
- data/lib/rubocop/cop/performance/uri_default_parser.rb +4 -6
- data/lib/rubocop/cop/performance_cops.rb +1 -0
- data/lib/rubocop/performance/version.rb +1 -1
- metadata +6 -5
@@ -3,14 +3,14 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Performance
|
6
|
-
#
|
6
|
+
# Identifies usages of `first`, `last`, `[0]` or `[-1]`
|
7
7
|
# chained to `select`, `find_all` or `filter` and change them to use
|
8
8
|
# `detect` instead.
|
9
9
|
#
|
10
10
|
# @safety
|
11
|
-
# This cop is unsafe because
|
12
|
-
#
|
13
|
-
#
|
11
|
+
# This cop is unsafe because it assumes that the receiver is an
|
12
|
+
# `Array` or equivalent, but can't reliably detect it. For example,
|
13
|
+
# if the receiver is a `Hash`, it may report a false positive.
|
14
14
|
#
|
15
15
|
# @example
|
16
16
|
# # bad
|
@@ -40,9 +40,9 @@ module RuboCop
|
|
40
40
|
|
41
41
|
def_node_matcher :detect_candidate?, <<~PATTERN
|
42
42
|
{
|
43
|
-
(send $(block (
|
43
|
+
(send $(block (call _ %CANDIDATE_METHODS) ...) ${:first :last} $...)
|
44
44
|
(send $(block (send _ %CANDIDATE_METHODS) ...) $:[] (int ${0 -1}))
|
45
|
-
(send $(
|
45
|
+
(send $(call _ %CANDIDATE_METHODS ...) ${:first :last} $...)
|
46
46
|
(send $(send _ %CANDIDATE_METHODS ...) $:[] (int ${0 -1}))
|
47
47
|
}
|
48
48
|
PATTERN
|
@@ -63,6 +63,7 @@ module RuboCop
|
|
63
63
|
register_offense(node, receiver, second_method, index)
|
64
64
|
end
|
65
65
|
end
|
66
|
+
alias on_csend on_send
|
66
67
|
|
67
68
|
private
|
68
69
|
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Performance
|
6
|
-
#
|
6
|
+
# Checks for double `#start_with?` or `#end_with?` calls
|
7
7
|
# separated by `||`. In some cases such calls can be replaced
|
8
8
|
# with an single `#start_with?`/`#end_with?` call.
|
9
9
|
#
|
@@ -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)
|
@@ -59,8 +58,8 @@ module RuboCop
|
|
59
58
|
private
|
60
59
|
|
61
60
|
def autocorrect(corrector, first_call_args, second_call_args, combined_args)
|
62
|
-
first_argument = first_call_args.first.
|
63
|
-
last_argument = second_call_args.last.
|
61
|
+
first_argument = first_call_args.first.source_range
|
62
|
+
last_argument = second_call_args.last.source_range
|
64
63
|
range = first_argument.join(last_argument)
|
65
64
|
|
66
65
|
corrector.replace(range, combined_args)
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Performance
|
6
|
-
#
|
6
|
+
# Identifies unnecessary use of a regex where `String#end_with?` would suffice.
|
7
7
|
#
|
8
8
|
# This cop has `SafeMultiline` configuration option that `true` by default because
|
9
9
|
# `end$` is unsafe as it will behave incompatible with `end_with?`
|
@@ -50,12 +50,11 @@ 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
|
58
|
-
{(
|
57
|
+
{(call $!nil? {:match :=~ :match?} (regexp (str $#literal_at_end?) (regopt)))
|
59
58
|
(send (regexp (str $#literal_at_end?) (regopt)) {:match :match?} $_)
|
60
59
|
(match-with-lvasgn (regexp (str $#literal_at_end?) (regopt)) $_)}
|
61
60
|
PATTERN
|
@@ -67,12 +66,14 @@ module RuboCop
|
|
67
66
|
receiver, regex_str = regex_str, receiver if receiver.is_a?(String)
|
68
67
|
regex_str = drop_end_metacharacter(regex_str)
|
69
68
|
regex_str = interpret_string_escapes(regex_str)
|
69
|
+
dot = node.loc.dot ? node.loc.dot.source : '.'
|
70
70
|
|
71
|
-
new_source = "#{receiver.source}
|
71
|
+
new_source = "#{receiver.source}#{dot}end_with?(#{to_string_literal(regex_str)})"
|
72
72
|
|
73
|
-
corrector.replace(node
|
73
|
+
corrector.replace(node, new_source)
|
74
74
|
end
|
75
75
|
end
|
76
|
+
alias on_csend on_send
|
76
77
|
alias on_match_with_lvasgn on_send
|
77
78
|
end
|
78
79
|
end
|
@@ -78,13 +78,13 @@ module RuboCop
|
|
78
78
|
end
|
79
79
|
|
80
80
|
def contains_splat?(node)
|
81
|
-
return unless node.array_type?
|
81
|
+
return false unless node.array_type?
|
82
82
|
|
83
83
|
node.each_child_node(:splat).any?
|
84
84
|
end
|
85
85
|
|
86
86
|
def contains_double_splat?(node)
|
87
|
-
return unless node.hash_type?
|
87
|
+
return false unless node.hash_type?
|
88
88
|
|
89
89
|
node.each_child_node(:kwsplat).any?
|
90
90
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Performance
|
6
|
-
#
|
6
|
+
# Identifies usages of `map { ... }.flatten` and
|
7
7
|
# change them to use `flat_map { ... }` instead.
|
8
8
|
#
|
9
9
|
# @example
|
@@ -28,7 +28,7 @@ module RuboCop
|
|
28
28
|
def_node_matcher :flat_map_candidate?, <<~PATTERN
|
29
29
|
(send
|
30
30
|
{
|
31
|
-
(block
|
31
|
+
$(block (send _ ${:collect :map}) ...)
|
32
32
|
$(send _ ${:collect :map} (block_pass _))
|
33
33
|
}
|
34
34
|
${:flatten :flatten!}
|
@@ -60,7 +60,8 @@ module RuboCop
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def register_offense(node, map_node, first_method, flatten, message)
|
63
|
-
|
63
|
+
map_send_node = map_node.block_type? ? map_node.send_node : map_node
|
64
|
+
range = range_between(map_send_node.loc.selector.begin_pos, node.source_range.end_pos)
|
64
65
|
message = format(message, method: first_method, flatten: flatten)
|
65
66
|
|
66
67
|
add_offense(range, message: message) do |corrector|
|
@@ -74,10 +75,11 @@ module RuboCop
|
|
74
75
|
|
75
76
|
return unless flatten_level
|
76
77
|
|
77
|
-
|
78
|
+
map_send_node = map_node.block_type? ? map_node.send_node : map_node
|
79
|
+
range = range_between(map_node.source_range.end_pos, node.source_range.end_pos)
|
78
80
|
|
79
81
|
corrector.remove(range)
|
80
|
-
corrector.replace(
|
82
|
+
corrector.replace(map_send_node.loc.selector, 'flat_map')
|
81
83
|
end
|
82
84
|
end
|
83
85
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Performance
|
6
|
-
#
|
6
|
+
# Checks for inefficient searching of keys and values within
|
7
7
|
# hashes.
|
8
8
|
#
|
9
9
|
# `Hash#keys.include?` is less efficient than `Hash#key?` because
|
@@ -45,7 +45,7 @@ module RuboCop
|
|
45
45
|
RESTRICT_ON_SEND = %i[include?].freeze
|
46
46
|
|
47
47
|
def_node_matcher :inefficient_include?, <<~PATTERN
|
48
|
-
(send (
|
48
|
+
(send (call $_ {:keys :values}) :include? _)
|
49
49
|
PATTERN
|
50
50
|
|
51
51
|
def on_send(node)
|
@@ -56,23 +56,23 @@ module RuboCop
|
|
56
56
|
add_offense(node, message: message) do |corrector|
|
57
57
|
# Replace `keys.include?` or `values.include?` with the appropriate
|
58
58
|
# `key?`/`value?` method.
|
59
|
-
corrector.replace(
|
60
|
-
node.loc.expression,
|
61
|
-
"#{autocorrect_hash_expression(node)}."\
|
62
|
-
"#{autocorrect_method(node)}(#{autocorrect_argument(node)})"
|
63
|
-
)
|
59
|
+
corrector.replace(node, replacement(node))
|
64
60
|
end
|
65
61
|
end
|
66
62
|
end
|
63
|
+
alias on_csend on_send
|
67
64
|
|
68
65
|
private
|
69
66
|
|
70
67
|
def message(node)
|
71
|
-
"Use `##{
|
72
|
-
"`##{current_method(node)}.include?`."
|
68
|
+
"Use `##{correct_method(node)}` instead of `##{current_method(node)}.include?`."
|
73
69
|
end
|
74
70
|
|
75
|
-
def
|
71
|
+
def replacement(node)
|
72
|
+
"#{correct_hash_expression(node)}#{correct_dot(node)}#{correct_method(node)}(#{correct_argument(node)})"
|
73
|
+
end
|
74
|
+
|
75
|
+
def correct_method(node)
|
76
76
|
case current_method(node)
|
77
77
|
when :keys then use_long_method ? 'has_key?' : 'key?'
|
78
78
|
when :values then use_long_method ? 'has_value?' : 'value?'
|
@@ -85,18 +85,20 @@ module RuboCop
|
|
85
85
|
|
86
86
|
def use_long_method
|
87
87
|
preferred_config = config.for_all_cops['Style/PreferredHashMethods']
|
88
|
-
preferred_config &&
|
89
|
-
preferred_config['EnforcedStyle'] == 'long' &&
|
90
|
-
preferred_config['Enabled']
|
88
|
+
preferred_config && preferred_config['EnforcedStyle'] == 'long' && preferred_config['Enabled']
|
91
89
|
end
|
92
90
|
|
93
|
-
def
|
91
|
+
def correct_argument(node)
|
94
92
|
node.arguments.first.source
|
95
93
|
end
|
96
94
|
|
97
|
-
def
|
95
|
+
def correct_hash_expression(node)
|
98
96
|
node.receiver.receiver.source
|
99
97
|
end
|
98
|
+
|
99
|
+
def correct_dot(node)
|
100
|
+
node.receiver.loc.dot.source
|
101
|
+
end
|
100
102
|
end
|
101
103
|
end
|
102
104
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Performance
|
6
|
-
#
|
6
|
+
# Identifies places where inefficient `readlines` method
|
7
7
|
# can be replaced by `each_line` to avoid fully loading file content into memory.
|
8
8
|
#
|
9
9
|
# @example
|
@@ -95,7 +95,7 @@ module RuboCop
|
|
95
95
|
begin_pos = readlines_call.loc.selector.begin_pos
|
96
96
|
|
97
97
|
end_pos = if enumerable_call.method?(:each)
|
98
|
-
enumerable_call.
|
98
|
+
enumerable_call.source_range.end_pos
|
99
99
|
else
|
100
100
|
enumerable_call.loc.dot.begin_pos
|
101
101
|
end
|
@@ -40,12 +40,12 @@ module RuboCop
|
|
40
40
|
def_node_matcher :map_compact, <<~PATTERN
|
41
41
|
{
|
42
42
|
(send
|
43
|
-
$(
|
43
|
+
$(call _ {:map :collect}
|
44
44
|
(block_pass
|
45
45
|
(sym _))) _)
|
46
46
|
(send
|
47
47
|
(block
|
48
|
-
$(
|
48
|
+
$(call _ {:map :collect})
|
49
49
|
(args ...) _) _)
|
50
50
|
}
|
51
51
|
PATTERN
|
@@ -61,13 +61,14 @@ module RuboCop
|
|
61
61
|
remove_compact_method(corrector, map_node, node, node.parent)
|
62
62
|
end
|
63
63
|
end
|
64
|
+
alias on_csend on_send
|
64
65
|
|
65
66
|
private
|
66
67
|
|
67
68
|
def remove_compact_method(corrector, map_node, compact_node, chained_method)
|
68
69
|
compact_method_range = compact_node.loc.selector
|
69
70
|
|
70
|
-
if compact_node.multiline? && chained_method&.loc.respond_to?(:selector) && chained_method
|
71
|
+
if compact_node.multiline? && chained_method&.loc.respond_to?(:selector) && use_dot?(chained_method) &&
|
71
72
|
!map_method_and_compact_method_on_same_line?(map_node, compact_node) &&
|
72
73
|
!invoke_method_after_map_compact_on_same_line?(compact_node, chained_method)
|
73
74
|
compact_method_range = compact_method_with_final_newline_range(compact_method_range)
|
@@ -78,12 +79,16 @@ module RuboCop
|
|
78
79
|
corrector.remove(compact_method_range)
|
79
80
|
end
|
80
81
|
|
82
|
+
def use_dot?(node)
|
83
|
+
node.respond_to?(:dot?) && node.dot?
|
84
|
+
end
|
85
|
+
|
81
86
|
def map_method_and_compact_method_on_same_line?(map_node, compact_node)
|
82
87
|
compact_node.loc.selector.line == map_node.loc.selector.line
|
83
88
|
end
|
84
89
|
|
85
90
|
def invoke_method_after_map_compact_on_same_line?(compact_node, chained_method)
|
86
|
-
compact_node.loc.selector.line == chained_method.loc.
|
91
|
+
compact_node.loc.selector.line == chained_method.loc.last_line
|
87
92
|
end
|
88
93
|
|
89
94
|
def compact_method_with_final_newline_range(compact_method_range)
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Performance
|
6
|
+
# Checks if the map method is used in a chain.
|
7
|
+
#
|
8
|
+
# Autocorrection is not supported because an appropriate block variable name cannot be determined automatically.
|
9
|
+
#
|
10
|
+
# @safety
|
11
|
+
# This cop is unsafe because false positives occur if the number of times the first method is executed
|
12
|
+
# affects the return value of subsequent methods.
|
13
|
+
#
|
14
|
+
# [source,ruby]
|
15
|
+
# ----
|
16
|
+
# class X
|
17
|
+
# def initialize
|
18
|
+
# @@num = 0
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# def foo
|
22
|
+
# @@num += 1
|
23
|
+
# self
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# def bar
|
27
|
+
# @@num * 2
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# [X.new, X.new].map(&:foo).map(&:bar) # => [4, 4]
|
32
|
+
# [X.new, X.new].map { |x| x.foo.bar } # => [2, 4]
|
33
|
+
# ----
|
34
|
+
#
|
35
|
+
# @example
|
36
|
+
#
|
37
|
+
# # bad
|
38
|
+
# array.map(&:foo).map(&:bar)
|
39
|
+
#
|
40
|
+
# # good
|
41
|
+
# array.map { |item| item.foo.bar }
|
42
|
+
#
|
43
|
+
class MapMethodChain < Base
|
44
|
+
include IgnoredNode
|
45
|
+
|
46
|
+
MSG = 'Use `%<method_name>s { |x| x.%<map_args>s }` instead of `%<method_name>s` method chain.'
|
47
|
+
RESTRICT_ON_SEND = %i[map collect].freeze
|
48
|
+
|
49
|
+
def_node_matcher :block_pass_with_symbol_arg?, <<~PATTERN
|
50
|
+
(:block_pass (:sym $_))
|
51
|
+
PATTERN
|
52
|
+
|
53
|
+
def on_send(node)
|
54
|
+
return if part_of_ignored_node?(node)
|
55
|
+
return unless (map_arg = block_pass_with_symbol_arg?(node.first_argument))
|
56
|
+
|
57
|
+
map_args = [map_arg]
|
58
|
+
|
59
|
+
return unless (begin_of_chained_map_method = find_begin_of_chained_map_method(node, map_args))
|
60
|
+
|
61
|
+
range = begin_of_chained_map_method.loc.selector.begin.join(node.source_range.end)
|
62
|
+
message = format(MSG, method_name: begin_of_chained_map_method.method_name, map_args: map_args.join('.'))
|
63
|
+
|
64
|
+
add_offense(range, message: message)
|
65
|
+
|
66
|
+
ignore_node(node)
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def find_begin_of_chained_map_method(node, map_args)
|
72
|
+
return unless (chained_map_method = node.receiver)
|
73
|
+
return if !chained_map_method.call_type? || !RESTRICT_ON_SEND.include?(chained_map_method.method_name)
|
74
|
+
return unless (map_arg = block_pass_with_symbol_arg?(chained_map_method.first_argument))
|
75
|
+
|
76
|
+
map_args.unshift(map_arg)
|
77
|
+
|
78
|
+
receiver = chained_map_method.receiver
|
79
|
+
|
80
|
+
return chained_map_method unless receiver.call_type? && block_pass_with_symbol_arg?(receiver.first_argument)
|
81
|
+
|
82
|
+
find_begin_of_chained_map_method(chained_map_method, map_args)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Performance
|
6
|
-
#
|
6
|
+
# Identifies places where methods are converted to blocks, with the
|
7
7
|
# use of `&method`, and passed as arguments to method calls.
|
8
8
|
# It is faster to replace those with explicit blocks, calling those methods inside.
|
9
9
|
#
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Performance
|
6
|
-
#
|
6
|
+
# Checks for `OpenStruct.new` calls.
|
7
7
|
# Instantiation of an `OpenStruct` invalidates
|
8
8
|
# Ruby global method cache as it causes dynamic method
|
9
9
|
# definition during program runtime.
|
@@ -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
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Performance
|
6
|
-
#
|
6
|
+
# Identifies uses of `Range#include?` and `Range#member?`, which iterates over each
|
7
7
|
# item in a `Range` to see if a specified item is there. In contrast,
|
8
8
|
# `Range#cover?` simply compares the target item with the beginning and
|
9
9
|
# end points of the `Range`. In a great majority of cases, this is what
|
@@ -11,7 +11,7 @@ module RuboCop
|
|
11
11
|
#
|
12
12
|
# @safety
|
13
13
|
# This cop is unsafe because `Range#include?` (or `Range#member?`) and `Range#cover?`
|
14
|
-
# are not equivalent
|
14
|
+
# are not equivalent behavior.
|
15
15
|
#
|
16
16
|
# @example
|
17
17
|
# # bad
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Performance
|
6
|
-
#
|
6
|
+
# Identifies the use of a `&block` parameter and `block.call`
|
7
7
|
# where `yield` would do just as well.
|
8
8
|
#
|
9
9
|
# @example
|
@@ -75,7 +75,7 @@ module RuboCop
|
|
75
75
|
|
76
76
|
new_source << CLOSE_PAREN if parentheses?(node) && !args.empty?
|
77
77
|
|
78
|
-
corrector.replace(node
|
78
|
+
corrector.replace(node, new_source)
|
79
79
|
end
|
80
80
|
|
81
81
|
def calls_to_report(argname, body)
|
@@ -3,13 +3,23 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Performance
|
6
|
-
#
|
6
|
+
# Checks for uses `Enumerable#all?`, `Enumerable#any?`, `Enumerable#one?`,
|
7
7
|
# and `Enumerable#none?` are compared with `===` or similar methods in block.
|
8
8
|
#
|
9
9
|
# By default, `Object#===` behaves the same as `Object#==`, but this
|
10
10
|
# behavior is appropriately overridden in subclass. For example,
|
11
11
|
# `Range#===` returns `true` when argument is within the range.
|
12
12
|
#
|
13
|
+
# This cop has `AllowRegexpMatch` option and it is true by default because
|
14
|
+
# `regexp.match?('string')` often used in block changes to the opposite result:
|
15
|
+
#
|
16
|
+
# [source,ruby]
|
17
|
+
# ----
|
18
|
+
# [/pattern/].all? { |regexp| regexp.match?('pattern') } # => true
|
19
|
+
# [/pattern/].all? { |regexp| regexp =~ 'pattern' } # => true
|
20
|
+
# [/pattern/].all?('pattern') # => false
|
21
|
+
# ----
|
22
|
+
#
|
13
23
|
# @safety
|
14
24
|
# This cop is unsafe because `===` and `==` do not always behave the same.
|
15
25
|
#
|
@@ -22,14 +32,31 @@ module RuboCop
|
|
22
32
|
#
|
23
33
|
# # good
|
24
34
|
# items.all?(pattern)
|
35
|
+
# items.all?(Klass)
|
36
|
+
#
|
37
|
+
# @example AllowRegexpMatch: true (default)
|
38
|
+
#
|
39
|
+
# # good
|
40
|
+
# items.all? { |item| item =~ pattern }
|
41
|
+
# items.all? { |item| item.match?(pattern) }
|
42
|
+
#
|
43
|
+
# @example AllowRegexpMatch: false
|
44
|
+
#
|
45
|
+
# # bad
|
46
|
+
# items.all? { |item| item =~ pattern }
|
47
|
+
# items.all? { |item| item.match?(pattern) }
|
25
48
|
#
|
26
49
|
class RedundantEqualityComparisonBlock < Base
|
27
50
|
extend AutoCorrector
|
51
|
+
extend TargetRubyVersion
|
52
|
+
|
53
|
+
minimum_target_ruby_version 2.5
|
28
54
|
|
29
55
|
MSG = 'Use `%<prefer>s` instead of block.'
|
30
56
|
|
31
57
|
TARGET_METHODS = %i[all? any? one? none?].freeze
|
32
58
|
COMPARISON_METHODS = %i[== === is_a? kind_of?].freeze
|
59
|
+
REGEXP_METHODS = %i[=~ match?].freeze
|
33
60
|
IS_A_METHODS = %i[is_a? kind_of?].freeze
|
34
61
|
|
35
62
|
def on_block(node)
|
@@ -57,7 +84,11 @@ module RuboCop
|
|
57
84
|
end
|
58
85
|
|
59
86
|
def use_equality_comparison_block?(block_body)
|
60
|
-
block_body.send_type?
|
87
|
+
return false unless block_body.send_type?
|
88
|
+
|
89
|
+
method_name = block_body.method_name
|
90
|
+
|
91
|
+
COMPARISON_METHODS.include?(method_name) || (!allow_regexp_match? && REGEXP_METHODS.include?(method_name))
|
61
92
|
end
|
62
93
|
|
63
94
|
def same_block_argument_and_is_a_argument?(block_body, block_argument)
|
@@ -66,7 +97,7 @@ module RuboCop
|
|
66
97
|
elsif IS_A_METHODS.include?(block_body.method_name)
|
67
98
|
block_argument.source == block_body.first_argument.source
|
68
99
|
else
|
69
|
-
|
100
|
+
block_body.receiver.source == block_body.first_argument.receiver&.source
|
70
101
|
end
|
71
102
|
end
|
72
103
|
|
@@ -96,6 +127,10 @@ module RuboCop
|
|
96
127
|
def offense_range(node)
|
97
128
|
node.send_node.loc.selector.join(node.source_range.end)
|
98
129
|
end
|
130
|
+
|
131
|
+
def allow_regexp_match?
|
132
|
+
cop_config.fetch('AllowRegexpMatch', true)
|
133
|
+
end
|
99
134
|
end
|
100
135
|
end
|
101
136
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Performance
|
6
|
-
#
|
6
|
+
# Identifies the use of `Regexp#match` or `String#match`, which
|
7
7
|
# returns `#<MatchData>`/`nil`. The return value of `=~` is an integral
|
8
8
|
# index/`nil` and is more performant.
|
9
9
|
#
|
@@ -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,20 +40,22 @@ 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
|
-
corrector.replace(node
|
52
|
+
corrector.replace(node, new_source)
|
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?
|
58
59
|
end
|
59
60
|
end
|
60
61
|
end
|