rubocop-performance 1.9.1 → 1.11.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c90f0ddd451c37a5c2187423524462aaa583fc0a7d4fb0342077eb792f03bfcc
4
- data.tar.gz: 7606efa668ec94c24a35b93df1062a5a6f39e8c787e013d2707f69448841b7e9
3
+ metadata.gz: f7398f6e66937ee06d17e56c52ebe2858d26fcaf0a91d00d0c90caccbd1722b6
4
+ data.tar.gz: ab180bb4e81913bdabd470b04264e91b9687edde72f6f6d126e9918a11f361ca
5
5
  SHA512:
6
- metadata.gz: a2a0094a2edfa59ee9a517cbf5d71ae1fa0fc67042bf0c35b129872a1b6cb6f892c182a0777da2a4e34f1a4c4b4292c30d19a34083b65844802e75b021f75322
7
- data.tar.gz: e24dab2beb33688d61cf110a748d5295921c185324df366c0e02478ff44c34acb0857d8b603ca308021c7eaaf6e13d9ecbaeda1f80572b180d499c1b4e5fec5d
6
+ metadata.gz: 5ae6caa06f44418aab93a55378a7727e48dde52eaf49c0e70e6e84ed01c62210048dc9c5cbe826bffbc0b7b85a779084517828350f50f76f88759f291385bdef
7
+ data.tar.gz: e9a63b027c140d453648376871ab24f5192004971239aa8b06ad5f73b3da917a7b70923d8ac161fc3a10d352a167ac55577136c05f8dfd12a78e4c0c08acfbc3
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012-20 Bozhidar Batsov
1
+ Copyright (c) 2012-21 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
@@ -1,9 +1,10 @@
1
1
  # RuboCop Performance
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/rubocop-performance.svg)](https://badge.fury.io/rb/rubocop-performance)
4
- [![CircleCI](https://circleci.com/gh/rubocop-hq/rubocop-performance.svg?style=svg)](https://circleci.com/gh/rubocop-hq/rubocop-performance)
4
+ [![CircleCI](https://circleci.com/gh/rubocop/rubocop-performance.svg?style=svg)](https://circleci.com/gh/rubocop/rubocop-performance)
5
+ [![Discord](https://img.shields.io/badge/chat-on%20discord-7289da.svg?sanitize=true)](https://discord.gg/wJjWvGRDmm)
5
6
 
6
- Performance optimization analysis for your projects, as an extension to [RuboCop](https://github.com/rubocop-hq/rubocop).
7
+ Performance optimization analysis for your projects, as an extension to [RuboCop](https://github.com/rubocop/rubocop).
7
8
 
8
9
  ## Installation
9
10
 
data/config/default.yml CHANGED
@@ -10,7 +10,7 @@ Performance/AncestorsInclude:
10
10
  Performance/ArraySemiInfiniteRangeSlice:
11
11
  Description: 'Identifies places where slicing arrays with semi-infinite ranges can be replaced by `Array#take` and `Array#drop`.'
12
12
  # This cop was created due to a mistake in microbenchmark.
13
- # Refer https://github.com/rubocop-hq/rubocop-performance/pull/175#issuecomment-731892717
13
+ # Refer https://github.com/rubocop/rubocop-performance/pull/175#issuecomment-731892717
14
14
  Enabled: false
15
15
  # Unsafe for string slices because strings do not have `#take` and `#drop` methods.
16
16
  Safe: false
@@ -80,6 +80,7 @@ Performance/ConstantRegexp:
80
80
  Description: 'Finds regular expressions with dynamic components that are all constants.'
81
81
  Enabled: pending
82
82
  VersionAdded: '1.9'
83
+ VersionChanged: '1.10'
83
84
 
84
85
  Performance/Count:
85
86
  Description: >-
@@ -136,11 +137,10 @@ Performance/EndWith:
136
137
  # object. Switching these methods has to be done with knowledge of the types
137
138
  # of the variables which rubocop doesn't have.
138
139
  SafeAutoCorrect: false
139
- AutoCorrect: false
140
140
  Enabled: true
141
141
  SafeMultiline: true
142
142
  VersionAdded: '0.36'
143
- VersionChanged: '1.6'
143
+ VersionChanged: '1.10'
144
144
 
145
145
  Performance/FixedSize:
146
146
  Description: 'Do not compute the size of statically sized objects except in constants.'
@@ -174,6 +174,12 @@ Performance/IoReadlines:
174
174
  Enabled: false
175
175
  VersionAdded: '1.7'
176
176
 
177
+ Performance/MapCompact:
178
+ Description: 'Use `filter_map` instead of `collection.map(&:do_something).compact`.'
179
+ Enabled: pending
180
+ SafeAutoCorrect: false
181
+ VersionAdded: '1.11'
182
+
177
183
  Performance/MethodObjectAsBlock:
178
184
  Description: 'Use block explicitly instead of block-passing a method object.'
179
185
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#normal-way-to-apply-method-vs-method-code'
@@ -200,6 +206,15 @@ Performance/RedundantBlockCall:
200
206
  Enabled: true
201
207
  VersionAdded: '0.36'
202
208
 
209
+ Performance/RedundantEqualityComparisonBlock:
210
+ Description: >-
211
+ Checks for uses `Enumerable#all?`, `Enumerable#any?`, `Enumerable#one?`,
212
+ or `Enumerable#none?` are compared with `===` or similar methods in block.
213
+ Reference: 'https://github.com/rails/rails/pull/41363'
214
+ Enabled: pending
215
+ Safe: false
216
+ VersionAdded: '1.10'
217
+
203
218
  Performance/RedundantMatch:
204
219
  Description: >-
205
220
  Use `=~` instead of `String#match` or `Regexp#match` in a context where the
@@ -211,7 +226,9 @@ Performance/RedundantMerge:
211
226
  Description: 'Use Hash#[]=, rather than Hash#merge! with a single key-value pair.'
212
227
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#hashmerge-vs-hash-code'
213
228
  Enabled: true
229
+ Safe: false
214
230
  VersionAdded: '0.36'
231
+ VersionChanged: '1.11'
215
232
  # Max number of key-value pairs to consider an offense
216
233
  MaxKeyValuePairs: 2
217
234
 
@@ -220,6 +237,11 @@ Performance/RedundantSortBlock:
220
237
  Enabled: 'pending'
221
238
  VersionAdded: '1.7'
222
239
 
240
+ Performance/RedundantSplitRegexpArgument:
241
+ Description: 'This cop identifies places where `split` argument can be replaced from a deterministic regexp to a string.'
242
+ Enabled: pending
243
+ VersionAdded: '1.10'
244
+
223
245
  Performance/RedundantStringChars:
224
246
  Description: 'Checks for redundant `String#chars`.'
225
247
  Enabled: 'pending'
@@ -244,6 +266,11 @@ Performance/ReverseFirst:
244
266
  Enabled: 'pending'
245
267
  VersionAdded: '1.7'
246
268
 
269
+ Performance/SelectMap:
270
+ Description: 'Use `filter_map` instead of `ary.select(&:foo).map(&:bar)`.'
271
+ Enabled: false
272
+ VersionAdded: '1.11'
273
+
247
274
  Performance/Size:
248
275
  Description: >-
249
276
  Use `size` instead of `count` for counting
@@ -270,11 +297,10 @@ Performance/StartWith:
270
297
  # object. Switching these methods has to be done with knowledge of the types
271
298
  # of the variables which rubocop doesn't have.
272
299
  SafeAutoCorrect: false
273
- AutoCorrect: false
274
300
  Enabled: true
275
301
  SafeMultiline: true
276
302
  VersionAdded: '0.36'
277
- VersionChanged: '1.6'
303
+ VersionChanged: '1.10'
278
304
 
279
305
  Performance/StringInclude:
280
306
  Description: 'Use `String#include?` instead of a regex match with literal-only pattern.'
@@ -304,7 +330,7 @@ Performance/TimesMap:
304
330
  Enabled: true
305
331
  VersionAdded: '0.36'
306
332
  VersionChanged: '0.50'
307
- SafeAutoCorrect: false # see https://github.com/rubocop-hq/rubocop/issues/4658
333
+ SafeAutoCorrect: false # see https://github.com/rubocop/rubocop/issues/4658
308
334
 
309
335
  Performance/UnfreezeString:
310
336
  Description: 'Use unary plus to get an unfrozen string literal.'
@@ -0,0 +1,7 @@
1
+ #
2
+ # Configuration for obsoletion.
3
+ #
4
+ # See: https://docs.rubocop.org/rubocop/extensions.html#config-obsoletions
5
+ #
6
+ extracted:
7
+ Performance/*: ~
@@ -6,7 +6,7 @@ module RuboCop
6
6
  # This cop identifies places where slicing arrays with semi-infinite ranges
7
7
  # can be replaced by `Array#take` and `Array#drop`.
8
8
  # This cop was created due to a mistake in microbenchmark and hence is disabled by default.
9
- # Refer https://github.com/rubocop-hq/rubocop-performance/pull/175#issuecomment-731892717
9
+ # Refer https://github.com/rubocop/rubocop-performance/pull/175#issuecomment-731892717
10
10
  # This cop is also unsafe for string slices because strings do not have `#take` and `#drop` methods.
11
11
  #
12
12
  # @example
@@ -33,7 +33,7 @@ module RuboCop
33
33
  def_node_matcher :bind_with_call_method?, <<~PATTERN
34
34
  (send
35
35
  $(send
36
- (send nil? _) :bind
36
+ _ :bind
37
37
  $(...)) :call
38
38
  $...)
39
39
  PATTERN
@@ -64,7 +64,7 @@ module RuboCop
64
64
 
65
65
  def correction_range(receiver, node)
66
66
  location_of_bind = receiver.loc.selector.begin_pos
67
- location_of_call = node.loc.end.end_pos
67
+ location_of_call = node.source_range.end.end_pos
68
68
 
69
69
  range_between(location_of_bind, location_of_call)
70
70
  end
@@ -63,6 +63,8 @@ module RuboCop
63
63
 
64
64
  def on_send(node)
65
65
  chain_array_allocation?(node) do |fm, sm|
66
+ return if node.each_descendant(:send).any? { |descendant| descendant.method?(:lazy) }
67
+
66
68
  range = range_between(node.loc.dot.begin_pos, node.source_range.end_pos)
67
69
 
68
70
  add_offense(range, message: format(MSG, method: fm, second_method: sm))
@@ -6,9 +6,9 @@ module RuboCop
6
6
  # This cop finds regular expressions with dynamic components that are all constants.
7
7
  #
8
8
  # Ruby allocates a new Regexp object every time it executes a code containing such
9
- # a regular expression. It is more efficient to extract it into a constant
10
- # or add an `/o` option to perform `#{}` interpolation only once and reuse that
11
- # Regexp object.
9
+ # a regular expression. It is more efficient to extract it into a constant,
10
+ # memoize it, or add an `/o` option to perform `#{}` interpolation only once and
11
+ # reuse that Regexp object.
12
12
  #
13
13
  # @example
14
14
  #
@@ -28,13 +28,18 @@ module RuboCop
28
28
  # pattern.scan(TOKEN).reject { |token| token.match?(/\A#{SEPARATORS}\Z/o) }
29
29
  # end
30
30
  #
31
+ # # good
32
+ # def separators
33
+ # @separators ||= /\A#{SEPARATORS}\Z/
34
+ # end
35
+ #
31
36
  class ConstantRegexp < Base
32
37
  extend AutoCorrector
33
38
 
34
- MSG = 'Extract this regexp into a constant or append an `/o` option to its options.'
39
+ MSG = 'Extract this regexp into a constant, memoize it, or append an `/o` option to its options.'
35
40
 
36
41
  def on_regexp(node)
37
- return if within_const_assignment?(node) ||
42
+ return if within_allowed_assignment?(node) ||
38
43
  !include_interpolated_const?(node) ||
39
44
  node.single_interpolation?
40
45
 
@@ -45,8 +50,8 @@ module RuboCop
45
50
 
46
51
  private
47
52
 
48
- def within_const_assignment?(node)
49
- node.each_ancestor(:casgn).any?
53
+ def within_allowed_assignment?(node)
54
+ node.each_ancestor(:casgn, :or_asgn).any?
50
55
  end
51
56
 
52
57
  def_node_matcher :regexp_escape?, <<~PATTERN
@@ -3,7 +3,8 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Performance
6
- # This cop is used to identify usages of
6
+ # This cop is used to identify usages of `map { ... }.flatten` and
7
+ # change them to use `flat_map { ... }` instead.
7
8
  #
8
9
  # @example
9
10
  # # bad
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # In Ruby 2.7, `Enumerable#filter_map` has been added.
7
+ #
8
+ # This cop identifies places where `map { ... }.compact` can be replaced by `filter_map`.
9
+ # It is marked as unsafe auto-correction by default because `map { ... }.compact`
10
+ # that is not compatible with `filter_map`.
11
+ #
12
+ # [source,ruby]
13
+ # ----
14
+ # [true, false, nil].compact #=> [true, false]
15
+ # [true, false, nil].filter_map(&:itself) #=> [true]
16
+ # ----
17
+ #
18
+ # @example
19
+ # # bad
20
+ # ary.map(&:foo).compact
21
+ # ary.collect(&:foo).compact
22
+ #
23
+ # # good
24
+ # ary.filter_map(&:foo)
25
+ # ary.map(&:foo).compact!
26
+ #
27
+ class MapCompact < Base
28
+ include RangeHelp
29
+ extend AutoCorrector
30
+ extend TargetRubyVersion
31
+
32
+ MSG = 'Use `filter_map` instead.'
33
+ RESTRICT_ON_SEND = %i[compact].freeze
34
+
35
+ minimum_target_ruby_version 2.7
36
+
37
+ def_node_matcher :map_compact, <<~PATTERN
38
+ {
39
+ (send
40
+ $(send _ {:map :collect}
41
+ (block_pass
42
+ (sym _))) _)
43
+ (send
44
+ (block
45
+ $(send _ {:map :collect})
46
+ (args ...) _) _)
47
+ }
48
+ PATTERN
49
+
50
+ def on_send(node)
51
+ return unless (map_node = map_compact(node))
52
+
53
+ compact_loc = node.loc
54
+ range = range_between(map_node.loc.selector.begin_pos, compact_loc.selector.end_pos)
55
+
56
+ add_offense(range) do |corrector|
57
+ corrector.replace(map_node.loc.selector, 'filter_map')
58
+ corrector.remove(compact_loc.dot)
59
+ corrector.remove(compact_loc.selector)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -78,7 +78,7 @@ module RuboCop
78
78
  end
79
79
 
80
80
  def calls_to_report(argname, body)
81
- return [] if blockarg_assigned?(body, argname)
81
+ return [] if blockarg_assigned?(body, argname) || shadowed_block_argument?(body, argname)
82
82
 
83
83
  blockarg_calls(body, argname).map do |call|
84
84
  return [] if args_include_block_pass?(call)
@@ -87,6 +87,12 @@ module RuboCop
87
87
  end
88
88
  end
89
89
 
90
+ def shadowed_block_argument?(body, block_argument_of_method_signature)
91
+ return false unless body.block_type?
92
+
93
+ body.arguments.map(&:source).include?(block_argument_of_method_signature.to_s)
94
+ end
95
+
90
96
  def args_include_block_pass?(blockcall)
91
97
  _receiver, _call, *args = *blockcall
92
98
 
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # This cop checks for uses `Enumerable#all?`, `Enumerable#any?`, `Enumerable#one?`,
7
+ # and `Enumerable#none?` are compared with `===` or similar methods in block.
8
+ #
9
+ # By default, `Object#===` behaves the same as `Object#==`, but this
10
+ # behavior is appropriately overridden in subclass. For example,
11
+ # `Range#===` returns `true` when argument is within the range.
12
+ # Therefore, It is marked as unsafe by default because `===` and `==`
13
+ # do not always behave the same.
14
+ #
15
+ # @example
16
+ # # bad
17
+ # items.all? { |item| pattern === item }
18
+ # items.all? { |item| item == other }
19
+ # items.all? { |item| item.is_a?(Klass) }
20
+ # items.all? { |item| item.kind_of?(Klass) }
21
+ #
22
+ # # good
23
+ # items.all?(pattern)
24
+ #
25
+ class RedundantEqualityComparisonBlock < Base
26
+ extend AutoCorrector
27
+ extend TargetRubyVersion
28
+
29
+ minimum_target_ruby_version 2.5
30
+
31
+ MSG = 'Use `%<prefer>s` instead of block.'
32
+
33
+ TARGET_METHODS = %i[all? any? one? none?].freeze
34
+ COMPARISON_METHODS = %i[== === is_a? kind_of?].freeze
35
+ IS_A_METHODS = %i[is_a? kind_of?].freeze
36
+
37
+ def on_block(node)
38
+ return unless TARGET_METHODS.include?(node.method_name)
39
+ return unless one_block_argument?(node.arguments)
40
+
41
+ block_argument = node.arguments.first
42
+ block_body = node.body
43
+ return unless use_equality_comparison_block?(block_body)
44
+ return if same_block_argument_and_is_a_argument?(block_body, block_argument)
45
+ return unless (new_argument = new_argument(block_argument, block_body))
46
+
47
+ range = offense_range(node)
48
+ prefer = "#{node.method_name}(#{new_argument})"
49
+
50
+ add_offense(range, message: format(MSG, prefer: prefer)) do |corrector|
51
+ corrector.replace(range, prefer)
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def one_block_argument?(block_arguments)
58
+ block_arguments.one? && !block_arguments.source.include?(',')
59
+ end
60
+
61
+ def use_equality_comparison_block?(block_body)
62
+ block_body.send_type? && COMPARISON_METHODS.include?(block_body.method_name)
63
+ end
64
+
65
+ def same_block_argument_and_is_a_argument?(block_body, block_argument)
66
+ if block_body.method?(:===)
67
+ block_argument.source != block_body.children[2].source
68
+ elsif IS_A_METHODS.include?(block_body.method_name)
69
+ block_argument.source == block_body.first_argument.source
70
+ else
71
+ false
72
+ end
73
+ end
74
+
75
+ def new_argument(block_argument, block_body)
76
+ if block_argument.source == block_body.receiver.source
77
+ block_body.first_argument.source
78
+ elsif block_argument.source == block_body.first_argument.source
79
+ block_body.receiver.source
80
+ end
81
+ end
82
+
83
+ def offense_range(node)
84
+ node.send_node.loc.selector.join(node.source_range.end)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -8,6 +8,9 @@ module RuboCop
8
8
  # You can set the maximum number of key-value pairs to consider
9
9
  # an offense with `MaxKeyValuePairs`.
10
10
  #
11
+ # This cop is marked as unsafe because RuboCop cannot determine if the
12
+ # receiver of `merge!` is actually a hash or not.
13
+ #
11
14
  # @example
12
15
  # # bad
13
16
  # hash.merge!(a: 1)
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # This cop identifies places where `split` argument can be replaced from
7
+ # a deterministic regexp to a string.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # 'a,b,c'.split(/,/)
12
+ #
13
+ # # good
14
+ # 'a,b,c'.split(',')
15
+ class RedundantSplitRegexpArgument < Base
16
+ extend AutoCorrector
17
+
18
+ MSG = 'Use string as argument instead of regexp.'
19
+ RESTRICT_ON_SEND = %i[split].freeze
20
+ DETERMINISTIC_REGEX = /\A(?:#{LITERAL_REGEX})+\Z/.freeze
21
+ STR_SPECIAL_CHARS = %w[\n \" \' \\\\ \t \b \f \r].freeze
22
+
23
+ def_node_matcher :split_call_with_regexp?, <<~PATTERN
24
+ {(send !nil? :split $regexp)}
25
+ PATTERN
26
+
27
+ def on_send(node)
28
+ return unless (regexp_node = split_call_with_regexp?(node))
29
+ return if regexp_node.ignore_case? || regexp_node.content == ' '
30
+ return unless determinist_regexp?(regexp_node)
31
+
32
+ add_offense(regexp_node) do |corrector|
33
+ new_argument = replacement(regexp_node)
34
+
35
+ corrector.replace(regexp_node, "\"#{new_argument}\"")
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def determinist_regexp?(regexp_node)
42
+ DETERMINISTIC_REGEX.match?(regexp_node.source)
43
+ end
44
+
45
+ def replacement(regexp_node)
46
+ regexp_content = regexp_node.content
47
+ stack = []
48
+ chars = regexp_content.chars.each_with_object([]) do |char, strings|
49
+ if stack.empty? && char == '\\'
50
+ stack.push(char)
51
+ else
52
+ strings << "#{stack.pop}#{char}"
53
+ end
54
+ end
55
+ chars.map do |char|
56
+ char = char.dup
57
+ char.delete!('\\') unless STR_SPECIAL_CHARS.include?(char)
58
+ char
59
+ end.join
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -6,41 +6,53 @@ module RuboCop
6
6
  # This cop is used to identify usages of `reverse.each` and
7
7
  # change them to use `reverse_each` instead.
8
8
  #
9
+ # If the return value is used, it will not be detected because the result will be different.
10
+ #
11
+ # [source,ruby]
12
+ # ----
13
+ # [1, 2, 3].reverse.each {} #=> [3, 2, 1]
14
+ # [1, 2, 3].reverse_each {} #=> [1, 2, 3]
15
+ # ----
16
+ #
9
17
  # @example
10
18
  # # bad
11
- # [].reverse.each
19
+ # items.reverse.each
12
20
  #
13
21
  # # good
14
- # [].reverse_each
22
+ # items.reverse_each
15
23
  class ReverseEach < Base
16
24
  include RangeHelp
17
25
  extend AutoCorrector
18
26
 
19
27
  MSG = 'Use `reverse_each` instead of `reverse.each`.'
20
28
  RESTRICT_ON_SEND = %i[each].freeze
21
- UNDERSCORE = '_'
22
29
 
23
30
  def_node_matcher :reverse_each?, <<~MATCHER
24
- (send $(send _ :reverse) :each)
31
+ (send (send _ :reverse) :each)
25
32
  MATCHER
26
33
 
27
34
  def on_send(node)
28
- reverse_each?(node) do |receiver|
29
- location_of_reverse = receiver.loc.selector.begin_pos
30
- end_location = node.loc.selector.end_pos
35
+ return if use_return_value?(node)
31
36
 
32
- range = range_between(location_of_reverse, end_location)
37
+ reverse_each?(node) do
38
+ range = offense_range(node)
33
39
 
34
40
  add_offense(range) do |corrector|
35
- corrector.replace(replacement_range(node), UNDERSCORE)
41
+ corrector.replace(range, 'reverse_each')
36
42
  end
37
43
  end
38
44
  end
39
45
 
40
46
  private
41
47
 
42
- def replacement_range(node)
43
- range_between(node.loc.dot.begin_pos, node.loc.selector.begin_pos)
48
+ def use_return_value?(node)
49
+ !!node.ancestors.detect do |ancestor|
50
+ ancestor.assignment? || ancestor.send_type? || ancestor.return_type?
51
+ end
52
+ end
53
+
54
+ def offense_range(node)
55
+ range_between(node.children.first.loc.selector.begin_pos, node.loc.selector.end_pos)
44
56
  end
45
57
  end
46
58
  end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # In Ruby 2.7, `Enumerable#filter_map` has been added.
7
+ #
8
+ # This cop identifies places where `select.map` can be replaced by `filter_map`.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # ary.select(&:foo).map(&:bar)
13
+ # ary.filter(&:foo).map(&:bar)
14
+ #
15
+ # # good
16
+ # ary.filter_map { |o| o.bar if o.foo }
17
+ #
18
+ class SelectMap < Base
19
+ include RangeHelp
20
+ extend TargetRubyVersion
21
+
22
+ minimum_target_ruby_version 2.7
23
+
24
+ MSG = 'Use `filter_map` instead of `%<method_name>s.map`.'
25
+ RESTRICT_ON_SEND = %i[select filter].freeze
26
+
27
+ def_node_matcher :bad_method?, <<~PATTERN
28
+ (send nil? :bad_method ...)
29
+ PATTERN
30
+
31
+ def on_send(node)
32
+ return if (first_argument = node.first_argument) && !first_argument.block_pass_type?
33
+ return unless (send_node = map_method_candidate(node))
34
+ return unless send_node.method?(:map)
35
+
36
+ map_method = send_node.parent&.block_type? ? send_node.parent : send_node
37
+
38
+ range = offense_range(node, map_method)
39
+ add_offense(range, message: format(MSG, method_name: node.method_name))
40
+ end
41
+
42
+ private
43
+
44
+ def map_method_candidate(node)
45
+ return unless (parent = node.parent)
46
+
47
+ if parent.block_type? && parent.parent&.send_type?
48
+ parent.parent
49
+ elsif parent.send_type?
50
+ parent
51
+ end
52
+ end
53
+
54
+ def offense_range(node, map_method)
55
+ range_between(node.loc.selector.begin_pos, map_method.loc.expression.end_pos)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -150,7 +150,9 @@ module RuboCop
150
150
  replacement = build_good_method(init, block_pass)
151
151
 
152
152
  corrector.remove(sum_range)
153
- corrector.replace(map_range, ".#{replacement}")
153
+
154
+ dot = '.' if map.receiver
155
+ corrector.replace(map_range, "#{dot}#{replacement}")
154
156
  end
155
157
 
156
158
  def sum_method_range(node)
@@ -228,7 +230,11 @@ module RuboCop
228
230
  end
229
231
 
230
232
  def method_call_with_args_range(node)
231
- node.receiver.source_range.end.join(node.source_range.end)
233
+ if (receiver = node.receiver)
234
+ receiver.source_range.end.join(node.source_range.end)
235
+ else
236
+ node.source_range
237
+ end
232
238
  end
233
239
  end
234
240
  end
@@ -23,18 +23,22 @@ require_relative 'performance/end_with'
23
23
  require_relative 'performance/fixed_size'
24
24
  require_relative 'performance/flat_map'
25
25
  require_relative 'performance/inefficient_hash_search'
26
+ require_relative 'performance/map_compact'
26
27
  require_relative 'performance/method_object_as_block'
27
28
  require_relative 'performance/open_struct'
28
29
  require_relative 'performance/range_include'
29
30
  require_relative 'performance/io_readlines'
30
31
  require_relative 'performance/redundant_block_call'
32
+ require_relative 'performance/redundant_equality_comparison_block'
31
33
  require_relative 'performance/redundant_match'
32
34
  require_relative 'performance/redundant_merge'
33
35
  require_relative 'performance/redundant_sort_block'
36
+ require_relative 'performance/redundant_split_regexp_argument'
34
37
  require_relative 'performance/redundant_string_chars'
35
38
  require_relative 'performance/regexp_match'
36
39
  require_relative 'performance/reverse_each'
37
40
  require_relative 'performance/reverse_first'
41
+ require_relative 'performance/select_map'
38
42
  require_relative 'performance/size'
39
43
  require_relative 'performance/sort_reverse'
40
44
  require_relative 'performance/squeeze'
@@ -8,5 +8,7 @@ module RuboCop
8
8
  CONFIG = YAML.safe_load(CONFIG_DEFAULT.read).freeze
9
9
 
10
10
  private_constant(:CONFIG_DEFAULT, :PROJECT_ROOT)
11
+
12
+ ::RuboCop::ConfigObsoletion.files << PROJECT_ROOT.join('config', 'obsoletion.yml')
11
13
  end
12
14
  end
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Performance
5
5
  # This module holds the RuboCop Performance version information.
6
6
  module Version
7
- STRING = '1.9.1'
7
+ STRING = '1.11.0'
8
8
 
9
9
  def self.document_version
10
10
  STRING.match('\d+\.\d+').to_s
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-performance
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.1
4
+ version: 1.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2020-11-28 00:00:00.000000000 Z
13
+ date: 2021-04-21 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rubocop
@@ -18,7 +18,7 @@ dependencies:
18
18
  requirements:
19
19
  - - ">="
20
20
  - !ruby/object:Gem::Version
21
- version: 0.90.0
21
+ version: 1.7.0
22
22
  - - "<"
23
23
  - !ruby/object:Gem::Version
24
24
  version: '2.0'
@@ -28,7 +28,7 @@ dependencies:
28
28
  requirements:
29
29
  - - ">="
30
30
  - !ruby/object:Gem::Version
31
- version: 0.90.0
31
+ version: 1.7.0
32
32
  - - "<"
33
33
  - !ruby/object:Gem::Version
34
34
  version: '2.0'
@@ -59,6 +59,7 @@ files:
59
59
  - LICENSE.txt
60
60
  - README.md
61
61
  - config/default.yml
62
+ - config/obsoletion.yml
62
63
  - lib/rubocop-performance.rb
63
64
  - lib/rubocop/cop/mixin/regexp_metacharacter.rb
64
65
  - lib/rubocop/cop/mixin/sort_block.rb
@@ -84,17 +85,21 @@ files:
84
85
  - lib/rubocop/cop/performance/flat_map.rb
85
86
  - lib/rubocop/cop/performance/inefficient_hash_search.rb
86
87
  - lib/rubocop/cop/performance/io_readlines.rb
88
+ - lib/rubocop/cop/performance/map_compact.rb
87
89
  - lib/rubocop/cop/performance/method_object_as_block.rb
88
90
  - lib/rubocop/cop/performance/open_struct.rb
89
91
  - lib/rubocop/cop/performance/range_include.rb
90
92
  - lib/rubocop/cop/performance/redundant_block_call.rb
93
+ - lib/rubocop/cop/performance/redundant_equality_comparison_block.rb
91
94
  - lib/rubocop/cop/performance/redundant_match.rb
92
95
  - lib/rubocop/cop/performance/redundant_merge.rb
93
96
  - lib/rubocop/cop/performance/redundant_sort_block.rb
97
+ - lib/rubocop/cop/performance/redundant_split_regexp_argument.rb
94
98
  - lib/rubocop/cop/performance/redundant_string_chars.rb
95
99
  - lib/rubocop/cop/performance/regexp_match.rb
96
100
  - lib/rubocop/cop/performance/reverse_each.rb
97
101
  - lib/rubocop/cop/performance/reverse_first.rb
102
+ - lib/rubocop/cop/performance/select_map.rb
98
103
  - lib/rubocop/cop/performance/size.rb
99
104
  - lib/rubocop/cop/performance/sort_reverse.rb
100
105
  - lib/rubocop/cop/performance/squeeze.rb
@@ -109,15 +114,15 @@ files:
109
114
  - lib/rubocop/performance.rb
110
115
  - lib/rubocop/performance/inject.rb
111
116
  - lib/rubocop/performance/version.rb
112
- homepage: https://github.com/rubocop-hq/rubocop-performance
117
+ homepage: https://github.com/rubocop/rubocop-performance
113
118
  licenses:
114
119
  - MIT
115
120
  metadata:
116
121
  homepage_uri: https://docs.rubocop.org/rubocop-performance/
117
- changelog_uri: https://github.com/rubocop-hq/rubocop-performance/blob/master/CHANGELOG.md
118
- source_code_uri: https://github.com/rubocop-hq/rubocop-performance/
119
- documentation_uri: https://docs.rubocop.org/rubocop-performance/1.9/
120
- bug_tracker_uri: https://github.com/rubocop-hq/rubocop-performance/issues
122
+ changelog_uri: https://github.com/rubocop/rubocop-performance/blob/master/CHANGELOG.md
123
+ source_code_uri: https://github.com/rubocop/rubocop-performance/
124
+ documentation_uri: https://docs.rubocop.org/rubocop-performance/1.11/
125
+ bug_tracker_uri: https://github.com/rubocop/rubocop-performance/issues
121
126
  post_install_message:
122
127
  rdoc_options: []
123
128
  require_paths:
@@ -126,14 +131,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
126
131
  requirements:
127
132
  - - ">="
128
133
  - !ruby/object:Gem::Version
129
- version: 2.4.0
134
+ version: 2.5.0
130
135
  required_rubygems_version: !ruby/object:Gem::Requirement
131
136
  requirements:
132
137
  - - ">="
133
138
  - !ruby/object:Gem::Version
134
139
  version: '0'
135
140
  requirements: []
136
- rubygems_version: 3.2.0.rc.2
141
+ rubygems_version: 3.2.12
137
142
  signing_key:
138
143
  specification_version: 4
139
144
  summary: Automatic performance checking tool for Ruby code.