rubocop-performance 1.5.0 → 1.7.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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +5 -1
  4. data/config/default.yml +75 -6
  5. data/lib/rubocop/cop/mixin/regexp_metacharacter.rb +76 -0
  6. data/lib/rubocop/cop/mixin/sort_block.rb +28 -0
  7. data/lib/rubocop/cop/performance/ancestors_include.rb +45 -0
  8. data/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb +43 -0
  9. data/lib/rubocop/cop/performance/bind_call.rb +87 -0
  10. data/lib/rubocop/cop/performance/caller.rb +3 -3
  11. data/lib/rubocop/cop/performance/casecmp.rb +5 -3
  12. data/lib/rubocop/cop/performance/chain_array_allocation.rb +1 -1
  13. data/lib/rubocop/cop/performance/compare_with_block.rb +2 -2
  14. data/lib/rubocop/cop/performance/count.rb +3 -3
  15. data/lib/rubocop/cop/performance/delete_prefix.rb +96 -0
  16. data/lib/rubocop/cop/performance/delete_suffix.rb +96 -0
  17. data/lib/rubocop/cop/performance/detect.rb +1 -1
  18. data/lib/rubocop/cop/performance/double_start_end_with.rb +2 -2
  19. data/lib/rubocop/cop/performance/end_with.rb +36 -13
  20. data/lib/rubocop/cop/performance/fixed_size.rb +1 -1
  21. data/lib/rubocop/cop/performance/flat_map.rb +1 -1
  22. data/lib/rubocop/cop/performance/inefficient_hash_search.rb +1 -1
  23. data/lib/rubocop/cop/performance/io_readlines.rb +127 -0
  24. data/lib/rubocop/cop/performance/open_struct.rb +1 -1
  25. data/lib/rubocop/cop/performance/range_include.rb +10 -8
  26. data/lib/rubocop/cop/performance/redundant_block_call.rb +3 -3
  27. data/lib/rubocop/cop/performance/redundant_match.rb +2 -2
  28. data/lib/rubocop/cop/performance/redundant_merge.rb +21 -8
  29. data/lib/rubocop/cop/performance/redundant_sort_block.rb +53 -0
  30. data/lib/rubocop/cop/performance/redundant_string_chars.rb +137 -0
  31. data/lib/rubocop/cop/performance/regexp_match.rb +13 -13
  32. data/lib/rubocop/cop/performance/reverse_each.rb +3 -2
  33. data/lib/rubocop/cop/performance/reverse_first.rb +78 -0
  34. data/lib/rubocop/cop/performance/size.rb +35 -37
  35. data/lib/rubocop/cop/performance/sort_reverse.rb +54 -0
  36. data/lib/rubocop/cop/performance/squeeze.rb +70 -0
  37. data/lib/rubocop/cop/performance/start_with.rb +36 -16
  38. data/lib/rubocop/cop/performance/string_include.rb +57 -0
  39. data/lib/rubocop/cop/performance/string_replacement.rb +4 -11
  40. data/lib/rubocop/cop/performance/times_map.rb +1 -1
  41. data/lib/rubocop/cop/performance/unfreeze_string.rb +3 -7
  42. data/lib/rubocop/cop/performance/uri_default_parser.rb +1 -1
  43. data/lib/rubocop/cop/performance_cops.rb +15 -0
  44. data/lib/rubocop/performance/inject.rb +1 -1
  45. data/lib/rubocop/performance/version.rb +1 -1
  46. metadata +25 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2aa98e8177fd7ce04a6689bc8e25d5aa282feac4515b77eaa0971552d3436591
4
- data.tar.gz: a3f2e4ae23b70b9a30418b9e207f05c626c9f998254d22f70cec3ab5018acfe9
3
+ metadata.gz: 5be372d62d20424e05201d4b78bb06bf19fc707d845d6b799599ba72958f72b7
4
+ data.tar.gz: 87c59d78e37de238add70195fa26c95d31b23eb3065695f443a1694d3acf9145
5
5
  SHA512:
6
- metadata.gz: 19e52e2ff3845ed2b85574d5ec4b675492e7d92ef3713c879f3524800ab29446f278e1dd0f0a4fe82a92c3d746fce9cd352122bba478671c8bccc0a4fe10c993
7
- data.tar.gz: 9b4657c5085913cc35d502431ae040f164b1844e0e995814bb3b23694ec157c765a385b5b69b0ee70fd0a14dfa7c2152ad715fbde9c21d4f7d6c40dce9f4ab06
6
+ metadata.gz: d6caf119ca1a8b11d829ed555f3a0653a1087d3a3ee3be078460c9ecfb5a5fb236a7abf2c058f57647d4f74dffccc66aebcf4d330f5a6a5bd5deaa12a8fea480
7
+ data.tar.gz: d8a6e634d4453300d24e5869bffa690457d07048b1e2978cc00dbc930c40e2dc80789a6faa95823e63414e5c579f8784cbe4f743607e2fd9598cf478aa1801ed
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012-18 Bozhidar Batsov
1
+ Copyright (c) 2012-20 Bozhidar Batsov
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -16,7 +16,7 @@ gem install rubocop-performance
16
16
  or if you use bundler put this in your `Gemfile`
17
17
 
18
18
  ```ruby
19
- gem 'rubocop-performance'
19
+ gem 'rubocop-performance', require: false
20
20
  ```
21
21
 
22
22
  ## Usage
@@ -72,6 +72,10 @@ Performance/Size:
72
72
  - lib/example.rb
73
73
  ```
74
74
 
75
+ ## Documentation
76
+
77
+ You can read a lot more about RuboCop Performance in its [official docs](https://docs.rubocop.org/rubocop-performance/).
78
+
75
79
  ## Contributing
76
80
 
77
81
  Checkout the [contribution guidelines](CONTRIBUTING.md).
@@ -1,5 +1,21 @@
1
1
  # This is the default configuration file.
2
2
 
3
+ Performance/AncestorsInclude:
4
+ Description: 'Use `A <= B` instead of `A.ancestors.include?(B)`.'
5
+ Reference: 'https://github.com/JuanitoFatas/fast-ruby#ancestorsinclude-vs--code'
6
+ Enabled: 'pending'
7
+ VersionAdded: '1.7'
8
+
9
+ Performance/BigDecimalWithNumericArgument:
10
+ Description: 'Convert numeric argument to string before passing to BigDecimal.'
11
+ Enabled: 'pending'
12
+ VersionAdded: '1.7'
13
+
14
+ Performance/BindCall:
15
+ Description: 'Use `bind_call(obj, args, ...)` instead of `bind(obj).call(args, ...)`.'
16
+ Enabled: true
17
+ VersionAdded: '1.6'
18
+
3
19
  Performance/Caller:
4
20
  Description: >-
5
21
  Use `caller(n..n)` instead of `caller`.
@@ -21,6 +37,7 @@ Performance/Casecmp:
21
37
  Use `casecmp` rather than `downcase ==`, `upcase ==`, `== downcase`, or `== upcase`..
22
38
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringcasecmp-vs-stringdowncase---code'
23
39
  Enabled: true
40
+ Safe: false
24
41
  VersionAdded: '0.36'
25
42
 
26
43
  Performance/ChainArrayAllocation:
@@ -49,6 +66,18 @@ Performance/Count:
49
66
  VersionAdded: '0.31'
50
67
  VersionChanged: '1.5'
51
68
 
69
+ Performance/DeletePrefix:
70
+ Description: 'Use `delete_prefix` instead of `gsub`.'
71
+ Enabled: true
72
+ SafeMultiline: true
73
+ VersionAdded: '1.6'
74
+
75
+ Performance/DeleteSuffix:
76
+ Description: 'Use `delete_suffix` instead of `gsub`.'
77
+ Enabled: true
78
+ SafeMultiline: true
79
+ VersionAdded: '1.6'
80
+
52
81
  Performance/Detect:
53
82
  Description: >-
54
83
  Use `detect` instead of `select.first`, `find_all.first`,
@@ -83,11 +112,12 @@ Performance/EndWith:
83
112
  SafeAutoCorrect: false
84
113
  AutoCorrect: false
85
114
  Enabled: true
115
+ SafeMultiline: true
86
116
  VersionAdded: '0.36'
87
- VersionChanged: '0.44'
117
+ VersionChanged: '1.6'
88
118
 
89
119
  Performance/FixedSize:
90
- Description: 'Do not compute the size of statically sized objects except in constants'
120
+ Description: 'Do not compute the size of statically sized objects except in constants.'
91
121
  Enabled: true
92
122
  VersionAdded: '0.35'
93
123
 
@@ -95,7 +125,7 @@ Performance/FlatMap:
95
125
  Description: >-
96
126
  Use `Enumerable#flat_map`
97
127
  instead of `Enumerable#map...Array#flatten(1)`
98
- or `Enumberable#collect..Array#flatten(1)`
128
+ or `Enumberable#collect..Array#flatten(1)`.
99
129
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code'
100
130
  Enabled: true
101
131
  VersionAdded: '0.30'
@@ -106,12 +136,18 @@ Performance/FlatMap:
106
136
  # `flatten` without any parameters can flatten multiple levels.
107
137
 
108
138
  Performance/InefficientHashSearch:
109
- Description: 'Use `key?` or `value?` instead of `keys.include?` or `values.include?`'
139
+ Description: 'Use `key?` or `value?` instead of `keys.include?` or `values.include?`.'
110
140
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#hashkey-instead-of-hashkeysinclude-code'
111
141
  Enabled: true
112
142
  VersionAdded: '0.56'
113
143
  Safe: false
114
144
 
145
+ Performance/IoReadlines:
146
+ Description: 'Use `IO.each_line` (`IO#each_line`) instead of `IO.readlines` (`IO#readlines`).'
147
+ Reference: 'https://docs.gitlab.com/ee/development/performance.html#reading-from-files-and-other-data-sources'
148
+ Enabled: false
149
+ VersionAdded: '1.7'
150
+
115
151
  Performance/OpenStruct:
116
152
  Description: 'Use `Struct` instead of `OpenStruct`.'
117
153
  Enabled: false
@@ -119,10 +155,11 @@ Performance/OpenStruct:
119
155
  Safe: false
120
156
 
121
157
  Performance/RangeInclude:
122
- Description: 'Use `Range#cover?` instead of `Range#include?`.'
158
+ Description: 'Use `Range#cover?` instead of `Range#include?` (or `Range#member?`).'
123
159
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#cover-vs-include-code'
124
160
  Enabled: true
125
161
  VersionAdded: '0.36'
162
+ VersionChanged: '1.7'
126
163
  Safe: false
127
164
 
128
165
  Performance/RedundantBlockCall:
@@ -146,6 +183,16 @@ Performance/RedundantMerge:
146
183
  # Max number of key-value pairs to consider an offense
147
184
  MaxKeyValuePairs: 2
148
185
 
186
+ Performance/RedundantSortBlock:
187
+ Description: 'Use `sort` instead of `sort { |a, b| a <=> b }`.'
188
+ Enabled: 'pending'
189
+ VersionAdded: '1.7'
190
+
191
+ Performance/RedundantStringChars:
192
+ Description: 'Checks for redundant `String#chars`.'
193
+ Enabled: 'pending'
194
+ VersionAdded: '1.7'
195
+
149
196
  Performance/RegexpMatch:
150
197
  Description: >-
151
198
  Use `match?` instead of `Regexp#match`, `String#match`, `Symbol#match`,
@@ -160,6 +207,11 @@ Performance/ReverseEach:
160
207
  Enabled: true
161
208
  VersionAdded: '0.30'
162
209
 
210
+ Performance/ReverseFirst:
211
+ Description: 'Use `last(n).reverse` instead of `reverse.first(n)`.'
212
+ Enabled: 'pending'
213
+ VersionAdded: '1.7'
214
+
163
215
  Performance/Size:
164
216
  Description: >-
165
217
  Use `size` instead of `count` for counting
@@ -168,6 +220,17 @@ Performance/Size:
168
220
  Enabled: true
169
221
  VersionAdded: '0.30'
170
222
 
223
+ Performance/SortReverse:
224
+ Description: 'Use `sort.reverse` instead of `sort { |a, b| b <=> a }`.'
225
+ Enabled: 'pending'
226
+ VersionAdded: '1.7'
227
+
228
+ Performance/Squeeze:
229
+ Description: "Use `squeeze('a')` instead of `gsub(/a+/, 'a')`."
230
+ Reference: 'https://github.com/JuanitoFatas/fast-ruby#remove-extra-spaces-or-other-contiguous-characters-code'
231
+ Enabled: 'pending'
232
+ VersionAdded: '1.7'
233
+
171
234
  Performance/StartWith:
172
235
  Description: 'Use `start_with?` instead of a regex match anchored to the beginning of a string.'
173
236
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringmatch-vs-stringstart_withstringend_with-code-start-code-end'
@@ -177,8 +240,14 @@ Performance/StartWith:
177
240
  SafeAutoCorrect: false
178
241
  AutoCorrect: false
179
242
  Enabled: true
243
+ SafeMultiline: true
180
244
  VersionAdded: '0.36'
181
- VersionChanged: '0.44'
245
+ VersionChanged: '1.6'
246
+
247
+ Performance/StringInclude:
248
+ Description: 'Use `String#include?` instead of a regex match with literal-only pattern.'
249
+ Enabled: 'pending'
250
+ VersionAdded: '1.7'
182
251
 
183
252
  Performance/StringReplacement:
184
253
  Description: >-
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Common functionality for handling regexp metacharacters.
6
+ module RegexpMetacharacter
7
+ private
8
+
9
+ def literal_at_start?(regexp)
10
+ return true if literal_at_start_with_backslash_a?(regexp)
11
+
12
+ !safe_multiline? && literal_at_start_with_caret?(regexp)
13
+ end
14
+
15
+ def literal_at_end?(regexp)
16
+ return true if literal_at_end_with_backslash_z?(regexp)
17
+
18
+ !safe_multiline? && literal_at_end_with_dollar?(regexp)
19
+ end
20
+
21
+ def literal_at_start_with_backslash_a?(regex_str)
22
+ # is this regexp 'literal' in the sense of only matching literal
23
+ # chars, rather than using metachars like `.` and `*` and so on?
24
+ # also, is it anchored at the start of the string?
25
+ # (tricky: \s, \d, and so on are metacharacters, but other characters
26
+ # escaped with a slash are just literals. LITERAL_REGEX takes all
27
+ # that into account.)
28
+ /\A\\A(?:#{Util::LITERAL_REGEX})+\z/.match?(regex_str)
29
+ end
30
+
31
+ def literal_at_start_with_caret?(regex_str)
32
+ # is this regexp 'literal' in the sense of only matching literal
33
+ # chars, rather than using metachars like `.` and `*` and so on?
34
+ # also, is it anchored at the start of the string?
35
+ # (tricky: \s, \d, and so on are metacharacters, but other characters
36
+ # escaped with a slash are just literals. LITERAL_REGEX takes all
37
+ # that into account.)
38
+ /\A\^(?:#{Util::LITERAL_REGEX})+\z/.match?(regex_str)
39
+ end
40
+
41
+ def literal_at_end_with_backslash_z?(regex_str)
42
+ # is this regexp 'literal' in the sense of only matching literal
43
+ # chars, rather than using metachars like . and * and so on?
44
+ # also, is it anchored at the end of the string?
45
+ /\A(?:#{Util::LITERAL_REGEX})+\\z\z/.match?(regex_str)
46
+ end
47
+
48
+ def literal_at_end_with_dollar?(regex_str)
49
+ # is this regexp 'literal' in the sense of only matching literal
50
+ # chars, rather than using metachars like . and * and so on?
51
+ # also, is it anchored at the end of the string?
52
+ /\A(?:#{Util::LITERAL_REGEX})+\$\z/.match?(regex_str)
53
+ end
54
+
55
+ def drop_start_metacharacter(regexp_string)
56
+ if regexp_string.start_with?('\\A')
57
+ regexp_string[2..-1] # drop `\A` anchor
58
+ else
59
+ regexp_string[1..-1] # drop `^` anchor
60
+ end
61
+ end
62
+
63
+ def drop_end_metacharacter(regexp_string)
64
+ if regexp_string.end_with?('\\z')
65
+ regexp_string.chomp('\z') # drop `\z` anchor
66
+ else
67
+ regexp_string.chop # drop `$` anchor
68
+ end
69
+ end
70
+
71
+ def safe_multiline?
72
+ cop_config.fetch('SafeMultiline', true)
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Common functionality for cops checking `Enumerable#sort` blocks.
6
+ module SortBlock
7
+ extend NodePattern::Macros
8
+ include RangeHelp
9
+
10
+ def_node_matcher :sort_with_block?, <<~PATTERN
11
+ (block
12
+ $(send _ :sort)
13
+ (args (arg $_a) (arg $_b))
14
+ $send)
15
+ PATTERN
16
+
17
+ def_node_matcher :replaceable_body?, <<~PATTERN
18
+ (send (lvar %1) :<=> (lvar %2))
19
+ PATTERN
20
+
21
+ private
22
+
23
+ def sort_range(send, node)
24
+ range_between(send.loc.selector.begin_pos, node.loc.end.end_pos)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # This cop is used to identify usages of `ancestors.include?` and
7
+ # change them to use `<=` instead.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # A.ancestors.include?(B)
12
+ #
13
+ # # good
14
+ # A <= B
15
+ #
16
+ class AncestorsInclude < Cop
17
+ include RangeHelp
18
+
19
+ MSG = 'Use `<=` instead of `ancestors.include?`.'
20
+
21
+ def_node_matcher :ancestors_include_candidate?, <<~PATTERN
22
+ (send (send $_subclass :ancestors) :include? $_superclass)
23
+ PATTERN
24
+
25
+ def on_send(node)
26
+ return unless ancestors_include_candidate?(node)
27
+
28
+ location_of_ancestors = node.children[0].loc.selector.begin_pos
29
+ end_location = node.loc.selector.end_pos
30
+ range = range_between(location_of_ancestors, end_location)
31
+
32
+ add_offense(node, location: range)
33
+ end
34
+
35
+ def autocorrect(node)
36
+ ancestors_include_candidate?(node) do |subclass, superclass|
37
+ lambda do |corrector|
38
+ corrector.replace(node, "#{subclass.source} <= #{superclass.source}")
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # This cop identifies places where numeric argument to BigDecimal should be
7
+ # converted to string. Initializing from String is faster
8
+ # than from Numeric for BigDecimal.
9
+ #
10
+ # @example
11
+ #
12
+ # # bad
13
+ # BigDecimal(1, 2)
14
+ # BigDecimal(1.2, 3, exception: true)
15
+ #
16
+ # # good
17
+ # BigDecimal('1', 2)
18
+ # BigDecimal('1.2', 3, exception: true)
19
+ #
20
+ class BigDecimalWithNumericArgument < Cop
21
+ MSG = 'Convert numeric argument to string before passing to `BigDecimal`.'
22
+
23
+ def_node_matcher :big_decimal_with_numeric_argument?, <<~PATTERN
24
+ (send nil? :BigDecimal $numeric_type? ...)
25
+ PATTERN
26
+
27
+ def on_send(node)
28
+ big_decimal_with_numeric_argument?(node) do |numeric|
29
+ add_offense(node, location: numeric.source_range)
30
+ end
31
+ end
32
+
33
+ def autocorrect(node)
34
+ big_decimal_with_numeric_argument?(node) do |numeric|
35
+ lambda do |corrector|
36
+ corrector.wrap(numeric, "'", "'")
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # In Ruby 2.7, `UnboundMethod#bind_call` has been added.
7
+ #
8
+ # This cop identifies places where `bind(obj).call(args, ...)`
9
+ # can be replaced by `bind_call(obj, args, ...)`.
10
+ #
11
+ # The `bind_call(obj, args, ...)` method is faster than
12
+ # `bind(obj).call(args, ...)`.
13
+ #
14
+ # @example
15
+ # # bad
16
+ # umethod.bind(obj).call(foo, bar)
17
+ # umethod.bind(obj).(foo, bar)
18
+ #
19
+ # # good
20
+ # umethod.bind_call(obj, foo, bar)
21
+ #
22
+ class BindCall < Cop
23
+ include RangeHelp
24
+ extend TargetRubyVersion
25
+
26
+ minimum_target_ruby_version 2.7
27
+
28
+ MSG = 'Use `bind_call(%<bind_arg>s%<comma>s%<call_args>s)` ' \
29
+ 'instead of `bind(%<bind_arg>s).call(%<call_args>s)`.'
30
+
31
+ def_node_matcher :bind_with_call_method?, <<~PATTERN
32
+ (send
33
+ $(send
34
+ (send nil? _) :bind
35
+ $(...)) :call
36
+ $...)
37
+ PATTERN
38
+
39
+ def on_send(node)
40
+ bind_with_call_method?(node) do |receiver, bind_arg, call_args_node|
41
+ range = correction_range(receiver, node)
42
+
43
+ call_args = build_call_args(call_args_node)
44
+
45
+ message = message(bind_arg.source, call_args)
46
+
47
+ add_offense(node, location: range, message: message)
48
+ end
49
+ end
50
+
51
+ def autocorrect(node)
52
+ receiver, bind_arg, call_args_node = bind_with_call_method?(node)
53
+
54
+ range = correction_range(receiver, node)
55
+
56
+ call_args = build_call_args(call_args_node)
57
+ call_args = ", #{call_args}" unless call_args.empty?
58
+
59
+ replacement_method = "bind_call(#{bind_arg.source}#{call_args})"
60
+
61
+ lambda do |corrector|
62
+ corrector.replace(range, replacement_method)
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ def message(bind_arg, call_args)
69
+ comma = call_args.empty? ? '' : ', '
70
+
71
+ format(MSG, bind_arg: bind_arg, comma: comma, call_args: call_args)
72
+ end
73
+
74
+ def correction_range(receiver, node)
75
+ location_of_bind = receiver.loc.selector.begin_pos
76
+ location_of_call = node.loc.end.end_pos
77
+
78
+ range_between(location_of_bind, location_of_call)
79
+ end
80
+
81
+ def build_call_args(call_args_node)
82
+ call_args_node.map(&:source).join(', ')
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end