rubocop-performance 1.12.0 → 1.13.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: c98065b3a22d03c0e9290990288d6b8a82a87d956bdf4165d94d3cd326ae7bf5
4
- data.tar.gz: a9ee1493235e6584ad381941423c0cac234bfc36fb4a2417433e667a16bec054
3
+ metadata.gz: 874ce36b942ed539ead8c3329a7dd6831224459a286c2b3189605775e725fc60
4
+ data.tar.gz: 584a851432d98e548151baa8afc1f9a30d5dcca05f8eae0d86c68f17bf933f34
5
5
  SHA512:
6
- metadata.gz: 35f36bff7bfe1b19496bfea773e7d0f7892731e57b06bb523ac192324a817f401207750abab20892ea58ec54595d1bdfd454bf250a4a00baf6ef0f4ac3f7b030
7
- data.tar.gz: 84a6ebc3e13d7c24aa6f1219443ce2c6cf6cd6c6253d4f09c78056fb3906f08bcb44db5365b03231aae55170a653e504b4a3433b4ed4a4dea1ee21b23d7a1387
6
+ metadata.gz: 102cccbb3be2ba55702a2e7aff3c56c6325c80481d269f87921ca1d16a79fa12830c6f080f49bacdebf4b5726e4f4c42af2527c13a4abac5aae0cf4544fb018e
7
+ data.tar.gz: 23c8beebceef175caf7e226a136bb3eaff500b71f5e3e26cba9fee9a84e36aa7a3b315b11216844b7980a421a82fff7a5346560ef1418f58fbe5261599fe4dec
data/config/default.yml CHANGED
@@ -17,7 +17,7 @@ Performance/ArraySemiInfiniteRangeSlice:
17
17
  VersionAdded: '1.9'
18
18
 
19
19
  Performance/BigDecimalWithNumericArgument:
20
- Description: 'Convert numeric argument to string before passing to BigDecimal.'
20
+ Description: 'Convert numeric literal to string and pass it to `BigDecimal`.'
21
21
  Enabled: 'pending'
22
22
  VersionAdded: '1.7'
23
23
 
@@ -43,10 +43,9 @@ Performance/CaseWhenSplat:
43
43
  Reordering `when` conditions with a splat to the end
44
44
  of the `when` branches can improve performance.
45
45
  Enabled: false
46
- AutoCorrect: false
47
46
  SafeAutoCorrect: false
48
47
  VersionAdded: '0.34'
49
- VersionChanged: '0.59'
48
+ VersionChanged: '1.13'
50
49
 
51
50
  Performance/Casecmp:
52
51
  Description: >-
@@ -137,7 +136,7 @@ Performance/DoubleStartEndWith:
137
136
  VersionAdded: '0.36'
138
137
  VersionChanged: '0.48'
139
138
  # Used to check for `starts_with?` and `ends_with?`.
140
- # These methods are defined by `ActiveSupport`.
139
+ # These methods are defined by Active Support.
141
140
  IncludeActiveSupportAliases: false
142
141
 
143
142
  Performance/EndWith:
@@ -312,6 +311,11 @@ Performance/StartWith:
312
311
  VersionAdded: '0.36'
313
312
  VersionChanged: '1.10'
314
313
 
314
+ Performance/StringIdentifierArgument:
315
+ Description: 'Use symbol identifier argument instead of string identifier argument.'
316
+ Enabled: pending
317
+ VersionAdded: '1.13'
318
+
315
319
  Performance/StringInclude:
316
320
  Description: 'Use `String#include?` instead of a regex match with literal-only pattern.'
317
321
  Enabled: 'pending'
@@ -330,17 +334,20 @@ Performance/StringReplacement:
330
334
 
331
335
  Performance/Sum:
332
336
  Description: 'Use `sum` instead of a custom array summation.'
337
+ SafeAutoCorrect: false
333
338
  Reference: 'https://blog.bigbinary.com/2016/11/02/ruby-2-4-introduces-enumerable-sum.html'
334
339
  Enabled: 'pending'
335
340
  VersionAdded: '1.8'
341
+ VersionChanged: '1.13'
342
+ OnlySumOrWithInitialValue: false
336
343
 
337
344
  Performance/TimesMap:
338
345
  Description: 'Checks for .times.map calls.'
339
- AutoCorrect: false
340
346
  Enabled: true
347
+ # See https://github.com/rubocop/rubocop/issues/4658
348
+ SafeAutoCorrect: false
341
349
  VersionAdded: '0.36'
342
- VersionChanged: '0.50'
343
- SafeAutoCorrect: false # see https://github.com/rubocop/rubocop/issues/4658
350
+ VersionChanged: '1.13'
344
351
 
345
352
  Performance/UnfreezeString:
346
353
  Description: 'Use unary plus to get an unfrozen string literal.'
@@ -10,36 +10,47 @@ module RuboCop
10
10
  # @example
11
11
  # # bad
12
12
  # BigDecimal(1, 2)
13
+ # 4.to_d(6)
13
14
  # BigDecimal(1.2, 3, exception: true)
15
+ # 4.5.to_d(6, exception: true)
14
16
  #
15
17
  # # good
16
18
  # BigDecimal('1', 2)
19
+ # BigDecimal('4', 6)
17
20
  # BigDecimal('1.2', 3, exception: true)
21
+ # BigDecimal('4.5', 6, exception: true)
18
22
  #
19
23
  class BigDecimalWithNumericArgument < Base
20
24
  extend AutoCorrector
21
25
 
22
- MSG = 'Convert numeric argument to string before passing to `BigDecimal`.'
23
- RESTRICT_ON_SEND = %i[BigDecimal].freeze
26
+ MSG = 'Convert numeric literal to string and pass it to `BigDecimal`.'
27
+ RESTRICT_ON_SEND = %i[BigDecimal to_d].freeze
24
28
 
25
29
  def_node_matcher :big_decimal_with_numeric_argument?, <<~PATTERN
26
30
  (send nil? :BigDecimal $numeric_type? ...)
27
31
  PATTERN
28
32
 
33
+ def_node_matcher :to_d?, <<~PATTERN
34
+ (send [!nil? $numeric_type?] :to_d ...)
35
+ PATTERN
36
+
29
37
  def on_send(node)
30
- return unless (numeric = big_decimal_with_numeric_argument?(node))
31
- return if numeric.float_type? && specifies_precision?(node)
38
+ if (numeric = big_decimal_with_numeric_argument?(node))
39
+ add_offense(numeric.source_range) do |corrector|
40
+ corrector.wrap(numeric, "'", "'")
41
+ end
42
+ elsif (numeric_to_d = to_d?(node))
43
+ add_offense(numeric_to_d.source_range) do |corrector|
44
+ big_decimal_args = node
45
+ .arguments
46
+ .map(&:source)
47
+ .unshift("'#{numeric_to_d.source}'")
48
+ .join(', ')
32
49
 
33
- add_offense(numeric.source_range) do |corrector|
34
- corrector.wrap(numeric, "'", "'")
50
+ corrector.replace(node, "BigDecimal(#{big_decimal_args})")
51
+ end
35
52
  end
36
53
  end
37
-
38
- private
39
-
40
- def specifies_precision?(node)
41
- node.arguments.size > 1 && !node.arguments[1].hash_type?
42
- end
43
54
  end
44
55
  end
45
56
  end
@@ -27,10 +27,6 @@ module RuboCop
27
27
  # [].detect { |item| true }
28
28
  # [].reverse.detect { |item| true }
29
29
  #
30
- # `ActiveRecord` compatibility:
31
- # `ActiveRecord` does not implement a `detect` method and `find` has its
32
- # own meaning. Correcting ActiveRecord methods with this cop should be
33
- # considered unsafe.
34
30
  class Detect < Base
35
31
  extend AutoCorrector
36
32
 
@@ -7,6 +7,9 @@ module RuboCop
7
7
  # separated by `||`. In some cases such calls can be replaced
8
8
  # with an single `#start_with?`/`#end_with?` call.
9
9
  #
10
+ # `IncludeActiveSupportAliases` configuration option is used to check for
11
+ # `starts_with?` and `ends_with?`. These methods are defined by Active Support.
12
+ #
10
13
  # @example
11
14
  # # bad
12
15
  # str.start_with?("a") || str.start_with?(Some::CONST)
@@ -17,6 +20,24 @@ module RuboCop
17
20
  # str.start_with?("a", Some::CONST)
18
21
  # str.start_with?("a", "b", "c")
19
22
  # str.end_with?(var1, var2)
23
+ #
24
+ # @example IncludeActiveSupportAliases: false (default)
25
+ # # good
26
+ # str.starts_with?("a", "b") || str.starts_with?("c")
27
+ # str.ends_with?(var1) || str.ends_with?(var2)
28
+ #
29
+ # str.starts_with?("a", "b", "c")
30
+ # str.ends_with?(var1, var2)
31
+ #
32
+ # @example IncludeActiveSupportAliases: true
33
+ # # bad
34
+ # str.starts_with?("a", "b") || str.starts_with?("c")
35
+ # str.ends_with?(var1) || str.ends_with?(var2)
36
+ #
37
+ # # good
38
+ # str.starts_with?("a", "b", "c")
39
+ # str.ends_with?(var1, var2)
40
+ #
20
41
  class DoubleStartEndWith < Base
21
42
  extend AutoCorrector
22
43
 
@@ -58,19 +58,19 @@ module RuboCop
58
58
 
59
59
  add_offense(range) do |corrector|
60
60
  corrector.replace(map_node.loc.selector, 'filter_map')
61
- remove_compact_method(corrector, node)
61
+ remove_compact_method(corrector, node, node.parent)
62
62
  end
63
63
  end
64
64
 
65
65
  private
66
66
 
67
- def remove_compact_method(corrector, compact_node)
68
- chained_method = compact_node.parent
67
+ def remove_compact_method(corrector, compact_node, chained_method)
69
68
  compact_method_range = compact_node.loc.selector
70
69
 
71
70
  if compact_node.multiline? && chained_method&.loc.respond_to?(:selector) && chained_method.dot? &&
71
+ !map_method_and_compact_method_on_same_line?(compact_node) &&
72
72
  !invoke_method_after_map_compact_on_same_line?(compact_node, chained_method)
73
- compact_method_range = range_by_whole_lines(compact_method_range, include_final_newline: true)
73
+ compact_method_range = compact_method_with_final_newline_range(compact_method_range)
74
74
  else
75
75
  corrector.remove(compact_node.loc.dot)
76
76
  end
@@ -78,9 +78,21 @@ module RuboCop
78
78
  corrector.remove(compact_method_range)
79
79
  end
80
80
 
81
+ def map_method_and_compact_method_on_same_line?(compact_node)
82
+ return false unless compact_node.children.first.respond_to?(:send_node)
83
+
84
+ map_node = compact_node.children.first.send_node
85
+
86
+ compact_node.loc.selector.line == map_node.loc.selector.line
87
+ end
88
+
81
89
  def invoke_method_after_map_compact_on_same_line?(compact_node, chained_method)
82
90
  compact_node.loc.selector.line == chained_method.loc.selector.line
83
91
  end
92
+
93
+ def compact_method_with_final_newline_range(compact_method_range)
94
+ range_by_whole_lines(compact_method_range, include_final_newline: true)
95
+ end
84
96
  end
85
97
  end
86
98
  end
@@ -82,14 +82,10 @@ module RuboCop
82
82
 
83
83
  def build_good_method(method, args)
84
84
  case method
85
- when :[], :slice
85
+ when :slice
86
86
  "[#{build_call_args(args)}].chars"
87
- when :first
88
- if args.any?
89
- "[0...#{args.first.source}].chars"
90
- else
91
- '[0]'
92
- end
87
+ when :[], :first
88
+ build_good_method_for_brackets_or_first_method(method, args)
93
89
  when :take
94
90
  "[0...#{args.first.source}].chars"
95
91
  else
@@ -97,6 +93,18 @@ module RuboCop
97
93
  end
98
94
  end
99
95
 
96
+ def build_good_method_for_brackets_or_first_method(method, args)
97
+ first_arg = args.first
98
+
99
+ if first_arg&.range_type?
100
+ "[#{build_call_args(args)}].chars"
101
+ elsif method == :first && args.any?
102
+ "[0...#{args.first.source}].chars"
103
+ else
104
+ first_arg ? "[#{first_arg.source}]" : '[0]'
105
+ end
106
+ end
107
+
100
108
  def build_bad_method(method, args)
101
109
  case method
102
110
  when :[]
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # This cop identifies places where string identifier argument can be replaced
7
+ # by symbol identifier argument.
8
+ # It prevents the redundancy of the internal string-to-symbol conversion.
9
+ #
10
+ # This cop targets methods that take identifier (e.g. method name) argument
11
+ # and the following examples are parts of it.
12
+ #
13
+ # @example
14
+ #
15
+ # # bad
16
+ # send('do_something')
17
+ # attr_accessor 'do_something'
18
+ # instance_variable_get('@ivar')
19
+ #
20
+ # # good
21
+ # send(:do_something)
22
+ # attr_accessor :do_something
23
+ # instance_variable_get(:@ivar)
24
+ #
25
+ class StringIdentifierArgument < Base
26
+ extend AutoCorrector
27
+
28
+ MSG = 'Use `%<symbol_arg>s` instead of `%<string_arg>s`.'
29
+
30
+ RESTRICT_ON_SEND = %i[
31
+ alias_method attr attr_accessor attr_reader attr_writer autoload autoload?
32
+ class_variable_defined? const_defined? const_get const_set const_source_location
33
+ define_method instance_method method_defined? private_class_method? private_method_defined?
34
+ protected_method_defined? public_class_method public_instance_method public_method_defined?
35
+ remove_class_variable remove_method undef_method class_variable_get class_variable_set
36
+ deprecate_constant module_function private private_constant protected public public_constant
37
+ remove_const ruby2_keywords
38
+ define_singleton_method instance_variable_defined instance_variable_get instance_variable_set
39
+ method public_method public_send remove_instance_variable respond_to? send singleton_method
40
+ __send__
41
+ ].freeze
42
+
43
+ def on_send(node)
44
+ return unless (first_argument = node.first_argument)
45
+ return unless first_argument.str_type?
46
+ return if first_argument.value.include?(' ')
47
+
48
+ replacement = first_argument.value.to_sym.inspect
49
+
50
+ message = format(MSG, symbol_arg: replacement, string_arg: first_argument.source)
51
+
52
+ add_offense(first_argument, message: message) do |corrector|
53
+ corrector.replace(first_argument, replacement)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -6,35 +6,46 @@ module RuboCop
6
6
  # This cop identifies places where custom code finding the sum of elements
7
7
  # in some Enumerable object can be replaced by `Enumerable#sum` method.
8
8
  #
9
- # This cop can change auto-correction scope depending on the value of
10
- # `SafeAutoCorrect`.
11
- # Its auto-correction is marked as safe by default (`SafeAutoCorrect: true`)
12
- # to prevent `TypeError` in auto-correced code when initial value is not
13
- # specified as shown below:
9
+ # @safety
10
+ # Auto-corrections are unproblematic wherever an initial value is provided explicitly:
14
11
  #
15
- # [source,ruby]
16
- # ----
17
- # ['a', 'b'].sum # => (String can't be coerced into Integer)
18
- # ----
12
+ # [source,ruby]
13
+ # ----
14
+ # [1, 2, 3].reduce(4, :+) # => 10
15
+ # [1, 2, 3].sum(4) # => 10
19
16
  #
20
- # Therefore if initial value is not specified, unsafe auto-corrected will not occur.
17
+ # [].reduce(4, :+) # => 4
18
+ # [].sum(4) # => 4
19
+ # ----
21
20
  #
22
- # If you always want to enable auto-correction, you can set `SafeAutoCorrect: false`.
21
+ # This also holds true for non-numeric types which implement a `:+` method:
23
22
  #
24
- # [source,yaml]
25
- # ----
26
- # Performance/Sum:
27
- # SafeAutoCorrect: false
28
- # ----
23
+ # [source,ruby]
24
+ # ----
25
+ # ['l', 'o'].reduce('Hel', :+) # => "Hello"
26
+ # ['l', 'o'].sum('Hel') # => "Hello"
27
+ # ----
29
28
  #
30
- # Please note that the auto-correction command line option will be changed from
31
- # `rubocop -a` to `rubocop -A`, which includes unsafe auto-correction.
29
+ # When no initial value is provided though, `Enumerable#reduce` will pick the first enumerated value
30
+ # as initial value and successively add all following values to it, whereas
31
+ # `Enumerable#sum` will set an initial value of `0` (`Integer`) which can lead to a `TypeError`:
32
32
  #
33
- # @example
33
+ # [source,ruby]
34
+ # ----
35
+ # [].reduce(:+) # => nil
36
+ # [1, 2, 3].reduce(:+) # => 6
37
+ # ['H', 'e', 'l', 'l', 'o'].reduce(:+) # => "Hello"
38
+ #
39
+ # [].sum # => 0
40
+ # [1, 2, 3].sum # => 6
41
+ # ['H', 'e', 'l', 'l', 'o'].sum # => in `+': String can't be coerced into Integer (TypeError)
42
+ # ----
43
+ #
44
+ # @example OnlySumOrWithInitialValue: false (default)
34
45
  # # bad
35
- # [1, 2, 3].inject(:+) # These bad cases with no initial value are unsafe and
36
- # [1, 2, 3].inject(&:+) # will not be auto-correced by default. If you want to
37
- # [1, 2, 3].reduce { |acc, elem| acc + elem } # auto-corrected, you can set `SafeAutoCorrect: false`.
46
+ # [1, 2, 3].inject(:+) # Auto-corrections for cases without initial value are unsafe
47
+ # [1, 2, 3].inject(&:+) # and will only be performed when using the `-A` option.
48
+ # [1, 2, 3].reduce { |acc, elem| acc + elem } # They can be prohibited completely using `SafeAutoCorrect: true`.
38
49
  # [1, 2, 3].reduce(10, :+)
39
50
  # [1, 2, 3].map { |elem| elem ** 2 }.sum
40
51
  # [1, 2, 3].collect(&:count).sum(10)
@@ -45,6 +56,17 @@ module RuboCop
45
56
  # [1, 2, 3].sum { |elem| elem ** 2 }
46
57
  # [1, 2, 3].sum(10, &:count)
47
58
  #
59
+ # @example OnlySumOrWithInitialValue: true
60
+ # # bad
61
+ # [1, 2, 3].reduce(10, :+)
62
+ # [1, 2, 3].map { |elem| elem ** 2 }.sum
63
+ # [1, 2, 3].collect(&:count).sum(10)
64
+ #
65
+ # # good
66
+ # [1, 2, 3].sum(10)
67
+ # [1, 2, 3].sum { |elem| elem ** 2 }
68
+ # [1, 2, 3].sum(10, &:count)
69
+ #
48
70
  class Sum < Base
49
71
  include RangeHelp
50
72
  extend AutoCorrector
@@ -103,6 +125,8 @@ module RuboCop
103
125
 
104
126
  def handle_sum_candidate(node)
105
127
  sum_candidate?(node) do |method, init, operation|
128
+ next if cop_config['OnlySumOrWithInitialValue'] && init.empty?
129
+
106
130
  range = sum_method_range(node)
107
131
  message = build_method_message(node, method, init, operation)
108
132
 
@@ -44,6 +44,7 @@ require_relative 'performance/size'
44
44
  require_relative 'performance/sort_reverse'
45
45
  require_relative 'performance/squeeze'
46
46
  require_relative 'performance/start_with'
47
+ require_relative 'performance/string_identifier_argument'
47
48
  require_relative 'performance/string_include'
48
49
  require_relative 'performance/string_replacement'
49
50
  require_relative 'performance/sum'
@@ -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.12.0'
7
+ STRING = '1.13.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.12.0
4
+ version: 1.13.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: 2021-10-31 00:00:00.000000000 Z
13
+ date: 2021-12-25 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rubocop
@@ -105,6 +105,7 @@ files:
105
105
  - lib/rubocop/cop/performance/sort_reverse.rb
106
106
  - lib/rubocop/cop/performance/squeeze.rb
107
107
  - lib/rubocop/cop/performance/start_with.rb
108
+ - lib/rubocop/cop/performance/string_identifier_argument.rb
108
109
  - lib/rubocop/cop/performance/string_include.rb
109
110
  - lib/rubocop/cop/performance/string_replacement.rb
110
111
  - lib/rubocop/cop/performance/sum.rb
@@ -122,8 +123,9 @@ metadata:
122
123
  homepage_uri: https://docs.rubocop.org/rubocop-performance/
123
124
  changelog_uri: https://github.com/rubocop/rubocop-performance/blob/master/CHANGELOG.md
124
125
  source_code_uri: https://github.com/rubocop/rubocop-performance/
125
- documentation_uri: https://docs.rubocop.org/rubocop-performance/1.12/
126
+ documentation_uri: https://docs.rubocop.org/rubocop-performance/1.13/
126
127
  bug_tracker_uri: https://github.com/rubocop/rubocop-performance/issues
128
+ rubygems_mfa_required: 'true'
127
129
  post_install_message:
128
130
  rdoc_options: []
129
131
  require_paths:
@@ -139,7 +141,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
139
141
  - !ruby/object:Gem::Version
140
142
  version: '0'
141
143
  requirements: []
142
- rubygems_version: 3.3.0.dev
144
+ rubygems_version: 3.3.0
143
145
  signing_key:
144
146
  specification_version: 4
145
147
  summary: Automatic performance checking tool for Ruby code.