rubocop-performance 1.5.1 → 1.7.1

Sign up to get free protection for your applications and to get access to all the features.
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