rubocop-performance 1.5.1 → 1.7.1

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 +78 -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 +47 -0
  8. data/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb +50 -0
  9. data/lib/rubocop/cop/performance/bind_call.rb +87 -0
  10. data/lib/rubocop/cop/performance/caller.rb +2 -2
  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 +30 -12
  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 +20 -7
  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 +30 -15
  38. data/lib/rubocop/cop/performance/string_include.rb +59 -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: 55bbd229a457fd01ff1b22b43e644f5399dd97160ae6e4b853ce83bd6ea32359
4
- data.tar.gz: 7106cb02fec6940d7d59672b84add493689910ea1046bdc69b93d7db6edbeeb3
3
+ metadata.gz: 34605a937c1a3e040428bee06e322220659c105b6e835423d9e0e73869fd83ee
4
+ data.tar.gz: e3fa0103bd2518cec45334bc162e144963eef9e3d09ab9fd6bd7b2d35b654e84
5
5
  SHA512:
6
- metadata.gz: b2840baf60f0906f80aa7a310bee2ac0591eb8763ff938334498d47f52df17782efbdcea0c50479a587a3d5b04f00bb2510c7779577b92a4caa6221944b55d85
7
- data.tar.gz: e89228cca25aefdceb216c530e928732d062f47971587e50403a79e67220904ff2b9e3601fbb997f529f1bd5b5fda066e727a4e2428026a44723edb959a3f066
6
+ metadata.gz: 5fe72ab5a0370aa8e0cc434080a27a38d8979084c53c50cd18e8cbc0896572ddc9d011130d56283f9d588268253ec0e2e44fc32b81483e1458689c60c17e835b
7
+ data.tar.gz: a60d627b0d1ded2fb6103a78fb4414fc1e1f1ff13ab9f4e82c399289c9a40c3c6e0385ee2509074175bd724755f768dd4625bb9791af993032a1f840a0533ef6
@@ -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,22 @@
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
+ Safe: false
8
+ VersionAdded: '1.7'
9
+
10
+ Performance/BigDecimalWithNumericArgument:
11
+ Description: 'Convert numeric argument to string before passing to BigDecimal.'
12
+ Enabled: 'pending'
13
+ VersionAdded: '1.7'
14
+
15
+ Performance/BindCall:
16
+ Description: 'Use `bind_call(obj, args, ...)` instead of `bind(obj).call(args, ...)`.'
17
+ Enabled: true
18
+ VersionAdded: '1.6'
19
+
3
20
  Performance/Caller:
4
21
  Description: >-
5
22
  Use `caller(n..n)` instead of `caller`.
@@ -21,6 +38,7 @@ Performance/Casecmp:
21
38
  Use `casecmp` rather than `downcase ==`, `upcase ==`, `== downcase`, or `== upcase`..
22
39
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringcasecmp-vs-stringdowncase---code'
23
40
  Enabled: true
41
+ Safe: false
24
42
  VersionAdded: '0.36'
25
43
 
26
44
  Performance/ChainArrayAllocation:
@@ -49,6 +67,18 @@ Performance/Count:
49
67
  VersionAdded: '0.31'
50
68
  VersionChanged: '1.5'
51
69
 
70
+ Performance/DeletePrefix:
71
+ Description: 'Use `delete_prefix` instead of `gsub`.'
72
+ Enabled: true
73
+ SafeMultiline: true
74
+ VersionAdded: '1.6'
75
+
76
+ Performance/DeleteSuffix:
77
+ Description: 'Use `delete_suffix` instead of `gsub`.'
78
+ Enabled: true
79
+ SafeMultiline: true
80
+ VersionAdded: '1.6'
81
+
52
82
  Performance/Detect:
53
83
  Description: >-
54
84
  Use `detect` instead of `select.first`, `find_all.first`,
@@ -83,11 +113,12 @@ Performance/EndWith:
83
113
  SafeAutoCorrect: false
84
114
  AutoCorrect: false
85
115
  Enabled: true
116
+ SafeMultiline: true
86
117
  VersionAdded: '0.36'
87
- VersionChanged: '0.44'
118
+ VersionChanged: '1.6'
88
119
 
89
120
  Performance/FixedSize:
90
- Description: 'Do not compute the size of statically sized objects except in constants'
121
+ Description: 'Do not compute the size of statically sized objects except in constants.'
91
122
  Enabled: true
92
123
  VersionAdded: '0.35'
93
124
 
@@ -95,7 +126,7 @@ Performance/FlatMap:
95
126
  Description: >-
96
127
  Use `Enumerable#flat_map`
97
128
  instead of `Enumerable#map...Array#flatten(1)`
98
- or `Enumberable#collect..Array#flatten(1)`
129
+ or `Enumberable#collect..Array#flatten(1)`.
99
130
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code'
100
131
  Enabled: true
101
132
  VersionAdded: '0.30'
@@ -106,12 +137,18 @@ Performance/FlatMap:
106
137
  # `flatten` without any parameters can flatten multiple levels.
107
138
 
108
139
  Performance/InefficientHashSearch:
109
- Description: 'Use `key?` or `value?` instead of `keys.include?` or `values.include?`'
140
+ Description: 'Use `key?` or `value?` instead of `keys.include?` or `values.include?`.'
110
141
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#hashkey-instead-of-hashkeysinclude-code'
111
142
  Enabled: true
112
143
  VersionAdded: '0.56'
113
144
  Safe: false
114
145
 
146
+ Performance/IoReadlines:
147
+ Description: 'Use `IO.each_line` (`IO#each_line`) instead of `IO.readlines` (`IO#readlines`).'
148
+ Reference: 'https://docs.gitlab.com/ee/development/performance.html#reading-from-files-and-other-data-sources'
149
+ Enabled: false
150
+ VersionAdded: '1.7'
151
+
115
152
  Performance/OpenStruct:
116
153
  Description: 'Use `Struct` instead of `OpenStruct`.'
117
154
  Enabled: false
@@ -119,10 +156,11 @@ Performance/OpenStruct:
119
156
  Safe: false
120
157
 
121
158
  Performance/RangeInclude:
122
- Description: 'Use `Range#cover?` instead of `Range#include?`.'
159
+ Description: 'Use `Range#cover?` instead of `Range#include?` (or `Range#member?`).'
123
160
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#cover-vs-include-code'
124
161
  Enabled: true
125
162
  VersionAdded: '0.36'
163
+ VersionChanged: '1.7'
126
164
  Safe: false
127
165
 
128
166
  Performance/RedundantBlockCall:
@@ -146,6 +184,16 @@ Performance/RedundantMerge:
146
184
  # Max number of key-value pairs to consider an offense
147
185
  MaxKeyValuePairs: 2
148
186
 
187
+ Performance/RedundantSortBlock:
188
+ Description: 'Use `sort` instead of `sort { |a, b| a <=> b }`.'
189
+ Enabled: 'pending'
190
+ VersionAdded: '1.7'
191
+
192
+ Performance/RedundantStringChars:
193
+ Description: 'Checks for redundant `String#chars`.'
194
+ Enabled: 'pending'
195
+ VersionAdded: '1.7'
196
+
149
197
  Performance/RegexpMatch:
150
198
  Description: >-
151
199
  Use `match?` instead of `Regexp#match`, `String#match`, `Symbol#match`,
@@ -160,6 +208,11 @@ Performance/ReverseEach:
160
208
  Enabled: true
161
209
  VersionAdded: '0.30'
162
210
 
211
+ Performance/ReverseFirst:
212
+ Description: 'Use `last(n).reverse` instead of `reverse.first(n)`.'
213
+ Enabled: 'pending'
214
+ VersionAdded: '1.7'
215
+
163
216
  Performance/Size:
164
217
  Description: >-
165
218
  Use `size` instead of `count` for counting
@@ -168,6 +221,17 @@ Performance/Size:
168
221
  Enabled: true
169
222
  VersionAdded: '0.30'
170
223
 
224
+ Performance/SortReverse:
225
+ Description: 'Use `sort.reverse` instead of `sort { |a, b| b <=> a }`.'
226
+ Enabled: 'pending'
227
+ VersionAdded: '1.7'
228
+
229
+ Performance/Squeeze:
230
+ Description: "Use `squeeze('a')` instead of `gsub(/a+/, 'a')`."
231
+ Reference: 'https://github.com/JuanitoFatas/fast-ruby#remove-extra-spaces-or-other-contiguous-characters-code'
232
+ Enabled: 'pending'
233
+ VersionAdded: '1.7'
234
+
171
235
  Performance/StartWith:
172
236
  Description: 'Use `start_with?` instead of a regex match anchored to the beginning of a string.'
173
237
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringmatch-vs-stringstart_withstringend_with-code-start-code-end'
@@ -177,8 +241,16 @@ Performance/StartWith:
177
241
  SafeAutoCorrect: false
178
242
  AutoCorrect: false
179
243
  Enabled: true
244
+ SafeMultiline: true
180
245
  VersionAdded: '0.36'
181
- VersionChanged: '0.44'
246
+ VersionChanged: '1.6'
247
+
248
+ Performance/StringInclude:
249
+ Description: 'Use `String#include?` instead of a regex match with literal-only pattern.'
250
+ Enabled: 'pending'
251
+ AutoCorrect: false
252
+ SafeAutoCorrect: false
253
+ VersionAdded: '1.7'
182
254
 
183
255
  Performance/StringReplacement:
184
256
  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,47 @@
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
+ subclass_source = subclass ? subclass.source : 'self'
39
+
40
+ corrector.replace(node, "#{subclass_source} <= #{superclass.source}")
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,50 @@
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
+ # # bad
12
+ # BigDecimal(1, 2)
13
+ # BigDecimal(1.2, 3, exception: true)
14
+ #
15
+ # # good
16
+ # BigDecimal('1', 2)
17
+ # BigDecimal('1.2', 3, exception: true)
18
+ #
19
+ class BigDecimalWithNumericArgument < Cop
20
+ MSG = 'Convert numeric argument to string before passing to `BigDecimal`.'
21
+
22
+ def_node_matcher :big_decimal_with_numeric_argument?, <<~PATTERN
23
+ (send nil? :BigDecimal $numeric_type? ...)
24
+ PATTERN
25
+
26
+ def on_send(node)
27
+ big_decimal_with_numeric_argument?(node) do |numeric|
28
+ next if numeric.float_type? && specifies_precision?(node)
29
+
30
+ add_offense(node, location: numeric.source_range)
31
+ end
32
+ end
33
+
34
+ def autocorrect(node)
35
+ big_decimal_with_numeric_argument?(node) do |numeric|
36
+ lambda do |corrector|
37
+ corrector.wrap(numeric, "'", "'")
38
+ end
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def specifies_precision?(node)
45
+ node.arguments.size > 1 && !node.arguments[1].hash_type?
46
+ end
47
+ end
48
+ end
49
+ end
50
+ 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