rubocop-performance 1.24.0 → 1.26.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: 1b4cdc2536f76691be677a1e203a886fae5e170f550424bed14d579f39a87e75
4
- data.tar.gz: 6ea02935a50fcdeb0b07d1a5d67c4414c71115019cb02dae4746c207f798cad6
3
+ metadata.gz: 65d9d36b4437bd92d054b743117d77b7fdb4f5391c012ae656b01dcd2ca2fa51
4
+ data.tar.gz: 8678a0f7fac0c650fa4c51a25e8c3e06b049c67b5fb369719fe7eee2c04a8820
5
5
  SHA512:
6
- metadata.gz: 7bc4f69ef0a5ca779626b29d0a7c5333bf135b29d06d1be009acd4e9ffaa368873cb9977a94e9c5d5f50cb1c000d9fa243b8557badbab00756469a0b6a366662
7
- data.tar.gz: 47e7f03cf61d57a3f9bd8e3cdb79f722fdaa279d300d2de5532435c1231fa06c28b1a148ab31c7b62775d1449a284fc43cee4bd36bbd16b1065145f022c8f729
6
+ metadata.gz: d0efc235b893328de9dfb9a58a26bb5d4838b9919a2b0a06e77f1f34ad1b53d39433793b1dd24dc04d34e79047aa0ff5e4863d407a48160da6c35fc45d91906f
7
+ data.tar.gz: 4aafa85fa2c326f532d16b338f55df55e67d9d52ebbd52196b53ae3520e7b30fdce7656081f716c91f6fe6ec3833915a6ac1bc0838d2c3016bfde6cb133689c0
data/README.md CHANGED
@@ -84,7 +84,7 @@ You can read a lot more about RuboCop Performance in its [official docs](https:/
84
84
 
85
85
  ## Compatibility
86
86
 
87
- RuboCop Performance complies with the RuboCop core compatibility.
87
+ RuboCop Performance is compatible with RuboCop core.
88
88
 
89
89
  See the [compatibility documentation](https://docs.rubocop.org/rubocop/compatibility.html) for further details.
90
90
 
data/config/default.yml CHANGED
@@ -67,7 +67,6 @@ Performance/ChainArrayAllocation:
67
67
  Description: >-
68
68
  Instead of chaining array methods that allocate new arrays, mutate an
69
69
  existing array.
70
- Reference: 'https://twitter.com/schneems/status/1034123879978029057'
71
70
  Enabled: false
72
71
  VersionAdded: '0.59'
73
72
 
@@ -187,14 +186,14 @@ Performance/InefficientHashSearch:
187
186
 
188
187
  Performance/IoReadlines:
189
188
  Description: 'Use `IO.each_line` (`IO#each_line`) instead of `IO.readlines` (`IO#readlines`).'
190
- Reference: 'https://docs.gitlab.com/ee/development/performance.html#reading-from-files-and-other-data-sources'
189
+ Reference: 'https://docs.gitlab.com/development/performance/#reading-from-files-and-other-data-sources'
191
190
  Enabled: false
192
191
  VersionAdded: '1.7'
193
192
 
194
193
  Performance/MapCompact:
195
194
  Description: 'Use `filter_map` instead of `collection.map(&:do_something).compact`.'
196
195
  Enabled: pending
197
- SafeAutoCorrect: false
196
+ Safe: false
198
197
  VersionAdded: '1.11'
199
198
 
200
199
  Performance/MapMethodChain:
@@ -356,7 +355,7 @@ Performance/StringReplacement:
356
355
  Performance/Sum:
357
356
  Description: 'Use `sum` instead of a custom array summation.'
358
357
  SafeAutoCorrect: false
359
- Reference: 'https://blog.bigbinary.com/2016/11/02/ruby-2-4-introduces-enumerable-sum.html'
358
+ Reference: 'https://www.bigbinary.com/blog/ruby-2-4-introduces-enumerable-sum'
360
359
  Enabled: 'pending'
361
360
  VersionAdded: '1.8'
362
361
  VersionChanged: '1.13'
@@ -36,7 +36,7 @@ module RuboCop
36
36
  def_node_matcher :reassigns_block_arg?, '`(lvasgn %1 ...)'
37
37
 
38
38
  def on_send(node)
39
- def_node = node.each_ancestor(:def, :defs).first
39
+ def_node = node.each_ancestor(:any_def).first
40
40
  return unless def_node
41
41
 
42
42
  block_arg = def_node.arguments.find(&:blockarg_type?)
@@ -52,7 +52,7 @@ module RuboCop
52
52
  end
53
53
 
54
54
  def self.autocorrect_incompatible_with
55
- [Lint::UnusedMethodArgument]
55
+ [Lint::UnusedMethodArgument, Naming::BlockForwarding]
56
56
  end
57
57
  end
58
58
  end
@@ -12,6 +12,14 @@ module RuboCop
12
12
  # You can set the minimum number of elements to consider
13
13
  # an offense with `MinSize`.
14
14
  #
15
+ # NOTE: Since Ruby 3.4, certain simple arguments to `Array#include?` are
16
+ # optimized directly in Ruby. This avoids allocations without changing the
17
+ # code, as such no offense will be registered in those cases. Currently that
18
+ # includes: strings, `self`, local variables, instance variables, and method
19
+ # calls without arguments. Additionally, any number of methods can be chained:
20
+ # `[1, 2, 3].include?(@foo)` and `[1, 2, 3].include?(@foo.bar.baz)` both avoid
21
+ # the array allocation.
22
+ #
15
23
  # @example
16
24
  # # bad
17
25
  # users.select do |user|
@@ -55,6 +63,8 @@ module RuboCop
55
63
 
56
64
  ARRAY_METHODS = (ENUMERABLE_METHOD_NAMES | NONMUTATING_ARRAY_METHODS).to_set.freeze
57
65
 
66
+ ARRAY_INCLUDE_OPTIMIZED_TYPES = %i[str self lvar ivar send].freeze
67
+
58
68
  NONMUTATING_HASH_METHODS = %i[< <= == > >= [] any? assoc compact dig
59
69
  each each_key each_pair each_value empty?
60
70
  eql? fetch fetch_values filter flatten has_key?
@@ -80,8 +90,8 @@ module RuboCop
80
90
  PATTERN
81
91
 
82
92
  def on_send(node)
83
- receiver, method, = *node.children
84
- return unless check_literal?(receiver, method) && parent_is_loop?(receiver)
93
+ receiver, method, *arguments = *node.children
94
+ return unless check_literal?(receiver, method, arguments) && parent_is_loop?(receiver)
85
95
 
86
96
  message = format(MSG, literal_class: literal_class(receiver))
87
97
  add_offense(receiver, message: message)
@@ -89,12 +99,33 @@ module RuboCop
89
99
 
90
100
  private
91
101
 
92
- def check_literal?(node, method)
102
+ def check_literal?(node, method, arguments)
93
103
  !node.nil? &&
94
104
  nonmutable_method_of_array_or_hash?(node, method) &&
95
105
  node.children.size >= min_size &&
96
- node.recursive_basic_literal?
106
+ node.recursive_basic_literal? &&
107
+ !optimized_array_include?(node, method, arguments)
108
+ end
109
+
110
+ # Since Ruby 3.4, simple arguments to Array#include? are optimized.
111
+ # See https://github.com/ruby/ruby/pull/12123 for more details.
112
+ # rubocop:disable Metrics/CyclomaticComplexity
113
+ def optimized_array_include?(node, method, arguments)
114
+ return false unless target_ruby_version >= 3.4 && node.array_type? && method == :include?
115
+ # Disallow include?(1, 2)
116
+ return false if arguments.count != 1
117
+
118
+ arg = arguments.first
119
+ # Allow `include?(foo.bar.baz.bat)`
120
+ while arg.send_type?
121
+ return false if arg.arguments.any? # Disallow include?(foo(bar))
122
+ break unless arg.receiver
123
+
124
+ arg = arg.receiver
125
+ end
126
+ ARRAY_INCLUDE_OPTIMIZED_TYPES.include?(arg.type)
97
127
  end
128
+ # rubocop:enable Metrics/CyclomaticComplexity
98
129
 
99
130
  def nonmutable_method_of_array_or_hash?(node, method)
100
131
  (node.array_type? && ARRAY_METHODS.include?(method)) ||
@@ -81,13 +81,15 @@ module RuboCop
81
81
 
82
82
  range = source_starting_at(node) { |n| n.loc.dot.begin_pos }
83
83
 
84
- corrector.remove(range)
84
+ corrector.remove(range_with_surrounding_space(range, side: :left))
85
85
  corrector.replace(selector_loc, 'count')
86
86
  negate_reject(corrector, node) if selector == :reject
87
87
  end
88
88
 
89
89
  def eligible_node?(node)
90
- !(node.parent && node.parent.block_type?)
90
+ return false if node.parent&.block_type?
91
+
92
+ node.receiver.call_type? || node.receiver.body
91
93
  end
92
94
 
93
95
  def source_starting_at(node)
@@ -3,9 +3,9 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Performance
6
- # Checks for double `#start_with?` or `#end_with?` calls
7
- # separated by `||`. In some cases such calls can be replaced
8
- # with an single `#start_with?`/`#end_with?` call.
6
+ # Checks for consecutive `#start_with?` or `#end_with?` calls.
7
+ # These methods accept multiple arguments, so in some cases like when
8
+ # they are separated by `||`, they can be combined into a single method call.
9
9
  #
10
10
  # `IncludeActiveSupportAliases` configuration option is used to check for
11
11
  # `starts_with?` and `ends_with?`. These methods are defined by Active Support.
@@ -14,11 +14,13 @@ module RuboCop
14
14
  # # bad
15
15
  # str.start_with?("a") || str.start_with?(Some::CONST)
16
16
  # str.start_with?("a", "b") || str.start_with?("c")
17
+ # !str.start_with?(foo) && !str.start_with?(bar)
17
18
  # str.end_with?(var1) || str.end_with?(var2)
18
19
  #
19
20
  # # good
20
21
  # str.start_with?("a", Some::CONST)
21
22
  # str.start_with?("a", "b", "c")
23
+ # !str.start_with?(foo, bar)
22
24
  # str.end_with?(var1, var2)
23
25
  #
24
26
  # @example IncludeActiveSupportAliases: false (default)
@@ -43,20 +45,33 @@ module RuboCop
43
45
 
44
46
  MSG = 'Use `%<replacement>s` instead of `%<original_code>s`.'
45
47
 
48
+ METHODS = %i[start_with? end_with?].to_set
49
+ METHODS_WITH_ACTIVE_SUPPORT = %i[start_with? starts_with? end_with? ends_with?].to_set
50
+
46
51
  def on_or(node)
47
- receiver, method, first_call_args, second_call_args = process_source(node)
52
+ two_start_end_with_calls(node, methods_to_check: methods) do |*matched|
53
+ check(node, *matched)
54
+ end
55
+ end
56
+
57
+ def on_and(node)
58
+ two_start_end_with_calls_negated(node, methods_to_check: methods) do |*matched|
59
+ check(node, *matched)
60
+ end
61
+ end
48
62
 
63
+ private
64
+
65
+ def check(node, receiver, method, first_call_args, second_call_args)
49
66
  return unless receiver && second_call_args.all?(&:pure?)
50
67
 
51
68
  combined_args = combine_args(first_call_args, second_call_args)
52
69
 
53
- add_offense(node, message: message(node, receiver, first_call_args, method, combined_args)) do |corrector|
70
+ add_offense(node, message: message(node, receiver, method, combined_args)) do |corrector|
54
71
  autocorrect(corrector, first_call_args, second_call_args, combined_args)
55
72
  end
56
73
  end
57
74
 
58
- private
59
-
60
75
  def autocorrect(corrector, first_call_args, second_call_args, combined_args)
61
76
  first_argument = first_call_args.first.source_range
62
77
  last_argument = second_call_args.last.source_range
@@ -65,17 +80,20 @@ module RuboCop
65
80
  corrector.replace(range, combined_args)
66
81
  end
67
82
 
68
- def process_source(node)
83
+ def methods
69
84
  if check_for_active_support_aliases?
70
- check_with_active_support_aliases(node)
85
+ METHODS_WITH_ACTIVE_SUPPORT
71
86
  else
72
- two_start_end_with_calls(node)
87
+ METHODS
73
88
  end
74
89
  end
75
90
 
76
- def message(node, receiver, first_call_args, method, combined_args)
77
- dot = first_call_args.first.parent.send_type? ? '.' : '&.'
78
- replacement = "#{receiver.source}#{dot}#{method}(#{combined_args})"
91
+ def message(node, receiver, method, combined_args)
92
+ parent = receiver.parent
93
+ grandparent = parent.parent
94
+ dot = parent.send_type? ? '.' : '&.'
95
+ bang = grandparent.send_type? && grandparent.prefix_bang? ? '!' : ''
96
+ replacement = "#{bang}#{receiver.source}#{dot}#{method}(#{combined_args})"
79
97
  format(MSG, replacement: replacement, original_code: node.source)
80
98
  end
81
99
 
@@ -89,16 +107,14 @@ module RuboCop
89
107
 
90
108
  def_node_matcher :two_start_end_with_calls, <<~PATTERN
91
109
  (or
92
- (call $_recv [{:start_with? :end_with?} $_method] $...)
110
+ (call $_recv [%methods_to_check $_method] $...)
93
111
  (call _recv _method $...))
94
112
  PATTERN
95
113
 
96
- def_node_matcher :check_with_active_support_aliases, <<~PATTERN
97
- (or
98
- (call $_recv
99
- [{:start_with? :starts_with? :end_with? :ends_with?} $_method]
100
- $...)
101
- (call _recv _method $...))
114
+ def_node_matcher :two_start_end_with_calls_negated, <<~PATTERN
115
+ (and
116
+ (send (call $_recv [%methods_to_check $_method] $...) :!)
117
+ (send (call _recv _method $...) :!))
102
118
  PATTERN
103
119
  end
104
120
  end
@@ -75,7 +75,7 @@ module RuboCop
75
75
  end
76
76
 
77
77
  def allowed_parent?(node)
78
- node&.type?(:casgn, :block)
78
+ node&.type?(:casgn, :any_block)
79
79
  end
80
80
 
81
81
  def contains_splat?(node)
@@ -74,8 +74,8 @@ module RuboCop
74
74
 
75
75
  def correct_method(node)
76
76
  case current_method(node)
77
- when :keys then use_long_method ? 'has_key?' : 'key?'
78
- when :values then use_long_method ? 'has_value?' : 'value?'
77
+ when :keys then use_long_method? ? 'has_key?' : 'key?'
78
+ when :values then use_long_method? ? 'has_value?' : 'value?'
79
79
  end
80
80
  end
81
81
 
@@ -83,9 +83,8 @@ module RuboCop
83
83
  node.receiver.method_name
84
84
  end
85
85
 
86
- def use_long_method
87
- preferred_config = config.for_all_cops['Style/PreferredHashMethods']
88
- preferred_config && preferred_config['EnforcedStyle'] == 'long' && preferred_config['Enabled']
86
+ def use_long_method?
87
+ config.for_enabled_cop('Style/PreferredHashMethods')['EnforcedStyle'] == 'long'
89
88
  end
90
89
 
91
90
  def correct_argument(node)
@@ -8,9 +8,9 @@ module RuboCop
8
8
  # This cop identifies places where `map { ... }.compact` can be replaced by `filter_map`.
9
9
  #
10
10
  # @safety
11
- # This cop's autocorrection is unsafe because `map { ... }.compact` might yield
11
+ # This cop is unsafe because `map { ... }.compact` might yield
12
12
  # different results than `filter_map`. As illustrated in the example, `filter_map`
13
- # also filters out falsy values, while `compact` only gets rid of `nil`.
13
+ # also filters out falsey values, while `compact` only gets rid of `nil`.
14
14
  #
15
15
  # [source,ruby]
16
16
  # ----
@@ -239,7 +239,7 @@ module RuboCop
239
239
 
240
240
  def scope_root(node)
241
241
  node.each_ancestor.find do |ancestor|
242
- ancestor.type?(:def, :defs, :class, :module)
242
+ ancestor.type?(:any_def, :class, :module)
243
243
  end
244
244
  end
245
245
 
@@ -16,19 +16,19 @@ module RuboCop
16
16
  # send('do_something')
17
17
  # attr_accessor 'do_something'
18
18
  # instance_variable_get('@ivar')
19
- # respond_to?("string_#{interpolation}")
20
19
  #
21
20
  # # good
22
21
  # send(:do_something)
23
22
  # attr_accessor :do_something
24
23
  # instance_variable_get(:@ivar)
25
- # respond_to?(:"string_#{interpolation}")
26
24
  #
27
25
  # # good - these methods don't support namespaced symbols
28
26
  # const_get("#{module_path}::Base")
29
27
  # const_source_location("#{module_path}::Base")
30
28
  # const_defined?("#{module_path}::Base")
31
29
  #
30
+ # # good - using a symbol when string interpolation is involved causes a performance regression.
31
+ # respond_to?("string_#{interpolation}")
32
32
  #
33
33
  class StringIdentifierArgument < Base
34
34
  extend AutoCorrector
@@ -40,8 +40,6 @@ module RuboCop
40
40
  protected public public_constant module_function
41
41
  ].freeze
42
42
 
43
- INTERPOLATION_IGNORE_METHODS = %i[const_get const_source_location const_defined?].freeze
44
-
45
43
  TWO_ARGUMENTS_METHOD = :alias_method
46
44
  MULTIPLE_ARGUMENTS_METHODS = %i[
47
45
  attr_accessor attr_reader attr_writer private private_constant
@@ -59,7 +57,7 @@ module RuboCop
59
57
  deprecate_constant remove_const ruby2_keywords define_singleton_method instance_variable_defined?
60
58
  instance_variable_get instance_variable_set method public_method public_send remove_instance_variable
61
59
  respond_to? send singleton_method __send__
62
- ] + COMMAND_METHODS + INTERPOLATION_IGNORE_METHODS).freeze
60
+ ] + COMMAND_METHODS).freeze
63
61
 
64
62
  def on_send(node)
65
63
  return if COMMAND_METHODS.include?(node.method_name) && node.receiver
@@ -83,13 +81,7 @@ module RuboCop
83
81
  [node.first_argument]
84
82
  end
85
83
 
86
- arguments.compact.filter { |argument| string_argument_compatible?(argument, node) }
87
- end
88
-
89
- def string_argument_compatible?(argument, node)
90
- return true if argument.str_type?
91
-
92
- argument.dstr_type? && INTERPOLATION_IGNORE_METHODS.none? { |method| node.method?(method) }
84
+ arguments.compact.filter(&:str_type?)
93
85
  end
94
86
 
95
87
  def register_offense(argument, argument_value)
@@ -52,6 +52,7 @@ module RuboCop
52
52
  check(node)
53
53
  end
54
54
  alias on_numblock on_block
55
+ alias on_itblock on_block
55
56
 
56
57
  private
57
58
 
@@ -28,6 +28,7 @@ module RuboCop
28
28
  {
29
29
  (block (call !nil? RESTRICT_ON_SEND) (args (arg _)) (array (lvar _)))
30
30
  (numblock (call !nil? RESTRICT_ON_SEND) 1 (array (lvar _)))
31
+ (itblock (call !nil? RESTRICT_ON_SEND) :it (array (lvar _)))
31
32
  }
32
33
  PATTERN
33
34
 
@@ -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.24.0'
7
+ STRING = '1.26.0'
8
8
 
9
9
  def self.document_version
10
10
  STRING.match('\d+\.\d+').to_s
@@ -7,10 +7,16 @@ require_relative 'rubocop/performance/version'
7
7
  require_relative 'rubocop/performance/plugin'
8
8
  require_relative 'rubocop/cop/performance_cops'
9
9
 
10
- RuboCop::Cop::Lint::UnusedMethodArgument.singleton_class.prepend(
11
- Module.new do
12
- def autocorrect_incompatible_with
13
- super.push(RuboCop::Cop::Performance::BlockGivenWithExplicitBlock)
14
- end
10
+ autocorrect_incompatible_with_block_given_with_explicit_block = Module.new do
11
+ def autocorrect_incompatible_with
12
+ super.push(RuboCop::Cop::Performance::BlockGivenWithExplicitBlock)
15
13
  end
14
+ end
15
+
16
+ RuboCop::Cop::Lint::UnusedMethodArgument.singleton_class.prepend(
17
+ autocorrect_incompatible_with_block_given_with_explicit_block
18
+ )
19
+
20
+ RuboCop::Cop::Naming::BlockForwarding.singleton_class.prepend(
21
+ autocorrect_incompatible_with_block_given_with_explicit_block
16
22
  )
metadata CHANGED
@@ -1,16 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-performance
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.24.0
4
+ version: 1.26.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
8
8
  - Jonas Arvidsson
9
9
  - Yuji Nakayama
10
- autorequire:
11
10
  bindir: bin
12
11
  cert_chain: []
13
- date: 2025-02-15 00:00:00.000000000 Z
12
+ date: 1980-01-02 00:00:00.000000000 Z
14
13
  dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
15
  name: lint_roller
@@ -32,7 +31,7 @@ dependencies:
32
31
  requirements:
33
32
  - - ">="
34
33
  - !ruby/object:Gem::Version
35
- version: 1.72.1
34
+ version: 1.75.0
36
35
  - - "<"
37
36
  - !ruby/object:Gem::Version
38
37
  version: '2.0'
@@ -42,7 +41,7 @@ dependencies:
42
41
  requirements:
43
42
  - - ">="
44
43
  - !ruby/object:Gem::Version
45
- version: 1.72.1
44
+ version: 1.75.0
46
45
  - - "<"
47
46
  - !ruby/object:Gem::Version
48
47
  version: '2.0'
@@ -52,7 +51,7 @@ dependencies:
52
51
  requirements:
53
52
  - - ">="
54
53
  - !ruby/object:Gem::Version
55
- version: 1.38.0
54
+ version: 1.44.0
56
55
  - - "<"
57
56
  - !ruby/object:Gem::Version
58
57
  version: '2.0'
@@ -62,7 +61,7 @@ dependencies:
62
61
  requirements:
63
62
  - - ">="
64
63
  - !ruby/object:Gem::Version
65
- version: 1.38.0
64
+ version: 1.44.0
66
65
  - - "<"
67
66
  - !ruby/object:Gem::Version
68
67
  version: '2.0'
@@ -146,11 +145,10 @@ metadata:
146
145
  homepage_uri: https://docs.rubocop.org/rubocop-performance/
147
146
  changelog_uri: https://github.com/rubocop/rubocop-performance/blob/master/CHANGELOG.md
148
147
  source_code_uri: https://github.com/rubocop/rubocop-performance/
149
- documentation_uri: https://docs.rubocop.org/rubocop-performance/1.24/
148
+ documentation_uri: https://docs.rubocop.org/rubocop-performance/1.26/
150
149
  bug_tracker_uri: https://github.com/rubocop/rubocop-performance/issues
151
150
  rubygems_mfa_required: 'true'
152
151
  default_lint_roller_plugin: RuboCop::Performance::Plugin
153
- post_install_message:
154
152
  rdoc_options: []
155
153
  require_paths:
156
154
  - lib
@@ -165,8 +163,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
165
163
  - !ruby/object:Gem::Version
166
164
  version: '0'
167
165
  requirements: []
168
- rubygems_version: 3.1.6
169
- signing_key:
166
+ rubygems_version: 3.6.9
170
167
  specification_version: 4
171
168
  summary: Automatic performance checking tool for Ruby code.
172
169
  test_files: []