rubocop-performance 1.10.0 → 1.11.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c936c552e05f2364a4256d5f0975973ee3a332270bc1af524db3420e9fc70ba5
4
- data.tar.gz: 3cd0086460c2db1c5926caef0737a1d9ae4038808b58b7c91f97e349cfe8f1b6
3
+ metadata.gz: a26b6360b5df05f3a7a8dfe957e5ecad0922105024a2e49dae8c39a566f10d88
4
+ data.tar.gz: 7591fd4d805f4d6dad68b83c8fbffff8e563bfa27bd58030efa48f332c392b2f
5
5
  SHA512:
6
- metadata.gz: cdc03f8c28e3ea1f63a8477fe064cf1df766f6334fc017d5f8bc7a830f189ad11c1f5a65a33008469b4fe8438522f64b56e6b026fdadb208abf18181d2a6d261
7
- data.tar.gz: 04034d2353dc721670ac494d40fae7383ba3800923feea6140c8ea9d86ee080028bc8fb05e46deeb475f1365705114d6a3848520e01248076f38f6e4ef18282f
6
+ metadata.gz: c8fb234710212fd000d0d695da71a1b1a36687550cf3738ce987264dad85f015d2f1f1da9b0f511c2147532ff7d6d444b35efb472b399646171bbb35e0ff79ad
7
+ data.tar.gz: 8447f79bb69e546ff684ece819b626fbfc68db8f7154b1b839c22167c87dbbf58821f1abe626a2b6d486ccfc81601e0844e416a312703ce0be7bc5552f1160ab
data/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/rubocop-performance.svg)](https://badge.fury.io/rb/rubocop-performance)
4
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
7
  Performance optimization analysis for your projects, as an extension to [RuboCop](https://github.com/rubocop/rubocop).
7
8
 
data/config/default.yml CHANGED
@@ -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'
@@ -220,7 +226,9 @@ Performance/RedundantMerge:
220
226
  Description: 'Use Hash#[]=, rather than Hash#merge! with a single key-value pair.'
221
227
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#hashmerge-vs-hash-code'
222
228
  Enabled: true
229
+ Safe: false
223
230
  VersionAdded: '0.36'
231
+ VersionChanged: '1.11'
224
232
  # Max number of key-value pairs to consider an offense
225
233
  MaxKeyValuePairs: 2
226
234
 
@@ -258,6 +266,11 @@ Performance/ReverseFirst:
258
266
  Enabled: 'pending'
259
267
  VersionAdded: '1.7'
260
268
 
269
+ Performance/SelectMap:
270
+ Description: 'Use `filter_map` instead of `ary.select(&:foo).map(&:bar)`.'
271
+ Enabled: false
272
+ VersionAdded: '1.11'
273
+
261
274
  Performance/Size:
262
275
  Description: >-
263
276
  Use `size` instead of `count` for counting
@@ -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/*: ~
@@ -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))
@@ -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,83 @@
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_method_range(node))
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ def compact_method_range(compact_node)
66
+ chained_method = compact_node.parent
67
+ compact_method_range = compact_node.loc.selector
68
+
69
+ if compact_node.multiline? &&
70
+ chained_method && !invoke_method_after_map_compact_on_same_line?(compact_node, chained_method)
71
+ range_by_whole_lines(compact_method_range, include_final_newline: true)
72
+ else
73
+ compact_method_range
74
+ end
75
+ end
76
+
77
+ def invoke_method_after_map_compact_on_same_line?(compact_node, chained_method)
78
+ compact_node.loc.selector.line == chained_method.loc.selector.line
79
+ end
80
+ end
81
+ end
82
+ end
83
+ 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
 
@@ -32,13 +32,16 @@ module RuboCop
32
32
 
33
33
  TARGET_METHODS = %i[all? any? one? none?].freeze
34
34
  COMPARISON_METHODS = %i[== === is_a? kind_of?].freeze
35
+ IS_A_METHODS = %i[is_a? kind_of?].freeze
35
36
 
36
37
  def on_block(node)
37
38
  return unless TARGET_METHODS.include?(node.method_name)
39
+ return unless one_block_argument?(node.arguments)
38
40
 
39
41
  block_argument = node.arguments.first
40
42
  block_body = node.body
41
43
  return unless use_equality_comparison_block?(block_body)
44
+ return if same_block_argument_and_is_a_argument?(block_body, block_argument)
42
45
  return unless (new_argument = new_argument(block_argument, block_body))
43
46
 
44
47
  range = offense_range(node)
@@ -51,10 +54,24 @@ module RuboCop
51
54
 
52
55
  private
53
56
 
57
+ def one_block_argument?(block_arguments)
58
+ block_arguments.one? && !block_arguments.source.include?(',')
59
+ end
60
+
54
61
  def use_equality_comparison_block?(block_body)
55
62
  block_body.send_type? && COMPARISON_METHODS.include?(block_body.method_name)
56
63
  end
57
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
+
58
75
  def new_argument(block_argument, block_body)
59
76
  if block_argument.source == block_body.receiver.source
60
77
  block_body.first_argument.source
@@ -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)
@@ -21,32 +21,29 @@ module RuboCop
21
21
  STR_SPECIAL_CHARS = %w[\n \" \' \\\\ \t \b \f \r].freeze
22
22
 
23
23
  def_node_matcher :split_call_with_regexp?, <<~PATTERN
24
- {(send !nil? :split {regexp})}
24
+ {(send !nil? :split $regexp)}
25
25
  PATTERN
26
26
 
27
27
  def on_send(node)
28
- return unless split_call_with_regexp?(node)
29
- return unless determinist_regexp?(node.first_argument)
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)
30
31
 
31
- add_offense(node.first_argument) do |corrector|
32
- autocorrect(corrector, node)
32
+ add_offense(regexp_node) do |corrector|
33
+ new_argument = replacement(regexp_node)
34
+
35
+ corrector.replace(regexp_node, "\"#{new_argument}\"")
33
36
  end
34
37
  end
35
38
 
36
39
  private
37
40
 
38
- def determinist_regexp?(first_argument)
39
- DETERMINISTIC_REGEX.match?(first_argument.source)
40
- end
41
-
42
- def autocorrect(corrector, node)
43
- new_argument = replacement(node)
44
-
45
- corrector.replace(node.first_argument, "\"#{new_argument}\"")
41
+ def determinist_regexp?(regexp_node)
42
+ DETERMINISTIC_REGEX.match?(regexp_node.source)
46
43
  end
47
44
 
48
- def replacement(node)
49
- regexp_content = node.first_argument.content
45
+ def replacement(regexp_node)
46
+ regexp_content = regexp_node.content
50
47
  stack = []
51
48
  chars = regexp_content.chars.each_with_object([]) do |char, strings|
52
49
  if stack.empty? && char == '\\'
@@ -6,12 +6,20 @@ 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
@@ -24,6 +32,8 @@ module RuboCop
24
32
  MATCHER
25
33
 
26
34
  def on_send(node)
35
+ return if use_return_value?(node)
36
+
27
37
  reverse_each?(node) do
28
38
  range = offense_range(node)
29
39
 
@@ -35,6 +45,12 @@ module RuboCop
35
45
 
36
46
  private
37
47
 
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
+
38
54
  def offense_range(node)
39
55
  range_between(node.children.first.loc.selector.begin_pos, node.loc.selector.end_pos)
40
56
  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
@@ -23,6 +23,7 @@ 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'
@@ -37,6 +38,7 @@ require_relative 'performance/redundant_string_chars'
37
38
  require_relative 'performance/regexp_match'
38
39
  require_relative 'performance/reverse_each'
39
40
  require_relative 'performance/reverse_first'
41
+ require_relative 'performance/select_map'
40
42
  require_relative 'performance/size'
41
43
  require_relative 'performance/sort_reverse'
42
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.10.0'
7
+ STRING = '1.11.2'
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.10.0
4
+ version: 1.11.2
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: 2021-02-28 00:00:00.000000000 Z
13
+ date: 2021-05-05 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,6 +85,7 @@ 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
@@ -97,6 +99,7 @@ files:
97
99
  - lib/rubocop/cop/performance/regexp_match.rb
98
100
  - lib/rubocop/cop/performance/reverse_each.rb
99
101
  - lib/rubocop/cop/performance/reverse_first.rb
102
+ - lib/rubocop/cop/performance/select_map.rb
100
103
  - lib/rubocop/cop/performance/size.rb
101
104
  - lib/rubocop/cop/performance/sort_reverse.rb
102
105
  - lib/rubocop/cop/performance/squeeze.rb
@@ -118,7 +121,7 @@ metadata:
118
121
  homepage_uri: https://docs.rubocop.org/rubocop-performance/
119
122
  changelog_uri: https://github.com/rubocop/rubocop-performance/blob/master/CHANGELOG.md
120
123
  source_code_uri: https://github.com/rubocop/rubocop-performance/
121
- documentation_uri: https://docs.rubocop.org/rubocop-performance/1.10/
124
+ documentation_uri: https://docs.rubocop.org/rubocop-performance/1.11/
122
125
  bug_tracker_uri: https://github.com/rubocop/rubocop-performance/issues
123
126
  post_install_message:
124
127
  rdoc_options: []
@@ -128,14 +131,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
128
131
  requirements:
129
132
  - - ">="
130
133
  - !ruby/object:Gem::Version
131
- version: 2.4.0
134
+ version: 2.5.0
132
135
  required_rubygems_version: !ruby/object:Gem::Requirement
133
136
  requirements:
134
137
  - - ">="
135
138
  - !ruby/object:Gem::Version
136
139
  version: '0'
137
140
  requirements: []
138
- rubygems_version: 3.2.9
141
+ rubygems_version: 3.2.12
139
142
  signing_key:
140
143
  specification_version: 4
141
144
  summary: Automatic performance checking tool for Ruby code.