rubocop-performance 1.13.3 → 1.19.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.
- 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
|