rubocop-performance 1.5.0 → 1.7.0

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 +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