rubocop-performance 1.7.0 → 1.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -0
  3. data/config/default.yml +49 -7
  4. data/lib/rubocop/cop/mixin/regexp_metacharacter.rb +4 -4
  5. data/lib/rubocop/cop/performance/ancestors_include.rb +16 -12
  6. data/lib/rubocop/cop/performance/array_semi_infinite_range_slice.rb +77 -0
  7. data/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb +17 -14
  8. data/lib/rubocop/cop/performance/bind_call.rb +9 -18
  9. data/lib/rubocop/cop/performance/block_given_with_explicit_block.rb +52 -0
  10. data/lib/rubocop/cop/performance/caller.rb +14 -15
  11. data/lib/rubocop/cop/performance/case_when_splat.rb +18 -11
  12. data/lib/rubocop/cop/performance/casecmp.rb +13 -20
  13. data/lib/rubocop/cop/performance/chain_array_allocation.rb +24 -28
  14. data/lib/rubocop/cop/performance/collection_literal_in_loop.rb +140 -0
  15. data/lib/rubocop/cop/performance/compare_with_block.rb +10 -21
  16. data/lib/rubocop/cop/performance/constant_regexp.rb +68 -0
  17. data/lib/rubocop/cop/performance/count.rb +14 -16
  18. data/lib/rubocop/cop/performance/delete_prefix.rb +14 -22
  19. data/lib/rubocop/cop/performance/delete_suffix.rb +14 -22
  20. data/lib/rubocop/cop/performance/detect.rb +65 -32
  21. data/lib/rubocop/cop/performance/double_start_end_with.rb +16 -24
  22. data/lib/rubocop/cop/performance/end_with.rb +9 -13
  23. data/lib/rubocop/cop/performance/fixed_size.rb +2 -1
  24. data/lib/rubocop/cop/performance/flat_map.rb +21 -22
  25. data/lib/rubocop/cop/performance/inefficient_hash_search.rb +15 -14
  26. data/lib/rubocop/cop/performance/io_readlines.rb +27 -42
  27. data/lib/rubocop/cop/performance/method_object_as_block.rb +32 -0
  28. data/lib/rubocop/cop/performance/open_struct.rb +3 -2
  29. data/lib/rubocop/cop/performance/range_include.rb +8 -6
  30. data/lib/rubocop/cop/performance/redundant_block_call.rb +15 -10
  31. data/lib/rubocop/cop/performance/redundant_match.rb +12 -6
  32. data/lib/rubocop/cop/performance/redundant_merge.rb +19 -17
  33. data/lib/rubocop/cop/performance/redundant_sort_block.rb +6 -16
  34. data/lib/rubocop/cop/performance/redundant_string_chars.rb +10 -18
  35. data/lib/rubocop/cop/performance/regexp_match.rb +20 -20
  36. data/lib/rubocop/cop/performance/reverse_each.rb +10 -5
  37. data/lib/rubocop/cop/performance/reverse_first.rb +5 -10
  38. data/lib/rubocop/cop/performance/size.rb +7 -6
  39. data/lib/rubocop/cop/performance/sort_reverse.rb +6 -15
  40. data/lib/rubocop/cop/performance/squeeze.rb +8 -11
  41. data/lib/rubocop/cop/performance/start_with.rb +9 -13
  42. data/lib/rubocop/cop/performance/string_include.rb +13 -14
  43. data/lib/rubocop/cop/performance/string_replacement.rb +24 -27
  44. data/lib/rubocop/cop/performance/sum.rb +236 -0
  45. data/lib/rubocop/cop/performance/times_map.rb +12 -18
  46. data/lib/rubocop/cop/performance/unfreeze_string.rb +20 -2
  47. data/lib/rubocop/cop/performance/uri_default_parser.rb +7 -12
  48. data/lib/rubocop/cop/performance_cops.rb +6 -0
  49. data/lib/rubocop/performance/version.rb +6 -1
  50. metadata +25 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5be372d62d20424e05201d4b78bb06bf19fc707d845d6b799599ba72958f72b7
4
- data.tar.gz: 87c59d78e37de238add70195fa26c95d31b23eb3065695f443a1694d3acf9145
3
+ metadata.gz: c90f0ddd451c37a5c2187423524462aaa583fc0a7d4fb0342077eb792f03bfcc
4
+ data.tar.gz: 7606efa668ec94c24a35b93df1062a5a6f39e8c787e013d2707f69448841b7e9
5
5
  SHA512:
6
- metadata.gz: d6caf119ca1a8b11d829ed555f3a0653a1087d3a3ee3be078460c9ecfb5a5fb236a7abf2c058f57647d4f74dffccc66aebcf4d330f5a6a5bd5deaa12a8fea480
7
- data.tar.gz: d8a6e634d4453300d24e5869bffa690457d07048b1e2978cc00dbc930c40e2dc80789a6faa95823e63414e5c579f8784cbe4f743607e2fd9598cf478aa1801ed
6
+ metadata.gz: a2a0094a2edfa59ee9a517cbf5d71ae1fa0fc67042bf0c35b129872a1b6cb6f892c182a0777da2a4e34f1a4c4b4292c30d19a34083b65844802e75b021f75322
7
+ data.tar.gz: e24dab2beb33688d61cf110a748d5295921c185324df366c0e02478ff44c34acb0857d8b603ca308021c7eaaf6e13d9ecbaeda1f80572b180d499c1b4e5fec5d
data/README.md CHANGED
@@ -76,6 +76,14 @@ Performance/Size:
76
76
 
77
77
  You can read a lot more about RuboCop Performance in its [official docs](https://docs.rubocop.org/rubocop-performance/).
78
78
 
79
+ ## Compatibility
80
+
81
+ RuboCop Performance complies with the RuboCop core compatibility.
82
+
83
+ See the [compatibility documentation](https://docs.rubocop.org/rubocop/compatibility.html) for further details.
84
+
85
+ **Note:** Performance cops are all MRI focused and are highly dependent of the version of MRI you're using.
86
+
79
87
  ## Contributing
80
88
 
81
89
  Checkout the [contribution guidelines](CONTRIBUTING.md).
@@ -4,8 +4,18 @@ Performance/AncestorsInclude:
4
4
  Description: 'Use `A <= B` instead of `A.ancestors.include?(B)`.'
5
5
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#ancestorsinclude-vs--code'
6
6
  Enabled: 'pending'
7
+ Safe: false
7
8
  VersionAdded: '1.7'
8
9
 
10
+ Performance/ArraySemiInfiniteRangeSlice:
11
+ Description: 'Identifies places where slicing arrays with semi-infinite ranges can be replaced by `Array#take` and `Array#drop`.'
12
+ # This cop was created due to a mistake in microbenchmark.
13
+ # Refer https://github.com/rubocop-hq/rubocop-performance/pull/175#issuecomment-731892717
14
+ Enabled: false
15
+ # Unsafe for string slices because strings do not have `#take` and `#drop` methods.
16
+ Safe: false
17
+ VersionAdded: '1.9'
18
+
9
19
  Performance/BigDecimalWithNumericArgument:
10
20
  Description: 'Convert numeric argument to string before passing to BigDecimal.'
11
21
  Enabled: 'pending'
@@ -16,11 +26,17 @@ Performance/BindCall:
16
26
  Enabled: true
17
27
  VersionAdded: '1.6'
18
28
 
29
+ Performance/BlockGivenWithExplicitBlock:
30
+ Description: 'Check block argument explicitly instead of using `block_given?`.'
31
+ Enabled: pending
32
+ VersionAdded: '1.9'
33
+
19
34
  Performance/Caller:
20
35
  Description: >-
21
36
  Use `caller(n..n)` instead of `caller`.
22
37
  Enabled: true
23
38
  VersionAdded: '0.49'
39
+ VersionChanged: '1.9'
24
40
 
25
41
  Performance/CaseWhenSplat:
26
42
  Description: >-
@@ -48,23 +64,33 @@ Performance/ChainArrayAllocation:
48
64
  Enabled: false
49
65
  VersionAdded: '0.59'
50
66
 
67
+ Performance/CollectionLiteralInLoop:
68
+ Description: 'Extract Array and Hash literals outside of loops into local variables or constants.'
69
+ Enabled: 'pending'
70
+ VersionAdded: '1.8'
71
+ # Min number of elements to consider an offense
72
+ MinSize: 1
73
+
51
74
  Performance/CompareWithBlock:
52
75
  Description: 'Use `sort_by(&:foo)` instead of `sort { |a, b| a.foo <=> b.foo }`.'
53
76
  Enabled: true
54
77
  VersionAdded: '0.46'
55
78
 
79
+ Performance/ConstantRegexp:
80
+ Description: 'Finds regular expressions with dynamic components that are all constants.'
81
+ Enabled: pending
82
+ VersionAdded: '1.9'
83
+
56
84
  Performance/Count:
57
85
  Description: >-
58
- Use `count` instead of `select...size`, `reject...size`,
59
- `select...count`, `reject...count`, `select...length`,
60
- and `reject...length`.
86
+ Use `count` instead of `{select,find_all,filter,reject}...{size,count,length}`.
61
87
  # This cop has known compatibility issues with `ActiveRecord` and other
62
88
  # frameworks. ActiveRecord's `count` ignores the block that is passed to it.
63
89
  # For more information, see the documentation in the cop itself.
64
90
  SafeAutoCorrect: false
65
91
  Enabled: true
66
92
  VersionAdded: '0.31'
67
- VersionChanged: '1.5'
93
+ VersionChanged: '1.8'
68
94
 
69
95
  Performance/DeletePrefix:
70
96
  Description: 'Use `delete_prefix` instead of `gsub`.'
@@ -80,8 +106,8 @@ Performance/DeleteSuffix:
80
106
 
81
107
  Performance/Detect:
82
108
  Description: >-
83
- Use `detect` instead of `select.first`, `find_all.first`,
84
- `select.last`, and `find_all.last`.
109
+ Use `detect` instead of `select.first`, `find_all.first`, `filter.first`,
110
+ `select.last`, `find_all.last`, and `filter.last`.
85
111
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerabledetect-vs-enumerableselectfirst-code'
86
112
  # This cop has known compatibility issues with `ActiveRecord` and other
87
113
  # frameworks. `ActiveRecord` does not implement a `detect` method and `find`
@@ -90,7 +116,7 @@ Performance/Detect:
90
116
  SafeAutoCorrect: false
91
117
  Enabled: true
92
118
  VersionAdded: '0.30'
93
- VersionChanged: '1.5'
119
+ VersionChanged: '1.8'
94
120
 
95
121
  Performance/DoubleStartEndWith:
96
122
  Description: >-
@@ -148,6 +174,12 @@ Performance/IoReadlines:
148
174
  Enabled: false
149
175
  VersionAdded: '1.7'
150
176
 
177
+ Performance/MethodObjectAsBlock:
178
+ Description: 'Use block explicitly instead of block-passing a method object.'
179
+ Reference: 'https://github.com/JuanitoFatas/fast-ruby#normal-way-to-apply-method-vs-method-code'
180
+ Enabled: pending
181
+ VersionAdded: '1.9'
182
+
151
183
  Performance/OpenStruct:
152
184
  Description: 'Use `Struct` instead of `OpenStruct`.'
153
185
  Enabled: false
@@ -247,6 +279,8 @@ Performance/StartWith:
247
279
  Performance/StringInclude:
248
280
  Description: 'Use `String#include?` instead of a regex match with literal-only pattern.'
249
281
  Enabled: 'pending'
282
+ AutoCorrect: false
283
+ SafeAutoCorrect: false
250
284
  VersionAdded: '1.7'
251
285
 
252
286
  Performance/StringReplacement:
@@ -258,6 +292,12 @@ Performance/StringReplacement:
258
292
  Enabled: true
259
293
  VersionAdded: '0.33'
260
294
 
295
+ Performance/Sum:
296
+ Description: 'Use `sum` instead of a custom array summation.'
297
+ Reference: 'https://blog.bigbinary.com/2016/11/02/ruby-2-4-introduces-enumerable-sum.html'
298
+ Enabled: 'pending'
299
+ VersionAdded: '1.8'
300
+
261
301
  Performance/TimesMap:
262
302
  Description: 'Checks for .times.map calls.'
263
303
  AutoCorrect: false
@@ -269,7 +309,9 @@ Performance/TimesMap:
269
309
  Performance/UnfreezeString:
270
310
  Description: 'Use unary plus to get an unfrozen string literal.'
271
311
  Enabled: true
312
+ SafeAutoCorrect: false
272
313
  VersionAdded: '0.50'
314
+ VersionChanged: '1.9'
273
315
 
274
316
  Performance/UriDefaultParser:
275
317
  Description: 'Use `URI::DEFAULT_PARSER` instead of `URI::Parser.new`.'
@@ -25,7 +25,7 @@ module RuboCop
25
25
  # (tricky: \s, \d, and so on are metacharacters, but other characters
26
26
  # escaped with a slash are just literals. LITERAL_REGEX takes all
27
27
  # that into account.)
28
- /\A\\A(?:#{Util::LITERAL_REGEX})+\z/.match?(regex_str)
28
+ /\A\\A(?:#{Util::LITERAL_REGEX})+\z/o.match?(regex_str)
29
29
  end
30
30
 
31
31
  def literal_at_start_with_caret?(regex_str)
@@ -35,21 +35,21 @@ module RuboCop
35
35
  # (tricky: \s, \d, and so on are metacharacters, but other characters
36
36
  # escaped with a slash are just literals. LITERAL_REGEX takes all
37
37
  # that into account.)
38
- /\A\^(?:#{Util::LITERAL_REGEX})+\z/.match?(regex_str)
38
+ /\A\^(?:#{Util::LITERAL_REGEX})+\z/o.match?(regex_str)
39
39
  end
40
40
 
41
41
  def literal_at_end_with_backslash_z?(regex_str)
42
42
  # is this regexp 'literal' in the sense of only matching literal
43
43
  # chars, rather than using metachars like . and * and so on?
44
44
  # also, is it anchored at the end of the string?
45
- /\A(?:#{Util::LITERAL_REGEX})+\\z\z/.match?(regex_str)
45
+ /\A(?:#{Util::LITERAL_REGEX})+\\z\z/o.match?(regex_str)
46
46
  end
47
47
 
48
48
  def literal_at_end_with_dollar?(regex_str)
49
49
  # is this regexp 'literal' in the sense of only matching literal
50
50
  # chars, rather than using metachars like . and * and so on?
51
51
  # also, is it anchored at the end of the string?
52
- /\A(?:#{Util::LITERAL_REGEX})+\$\z/.match?(regex_str)
52
+ /\A(?:#{Util::LITERAL_REGEX})+\$\z/o.match?(regex_str)
53
53
  end
54
54
 
55
55
  def drop_start_metacharacter(regexp_string)
@@ -13,31 +13,35 @@ module RuboCop
13
13
  # # good
14
14
  # A <= B
15
15
  #
16
- class AncestorsInclude < Cop
16
+ class AncestorsInclude < Base
17
17
  include RangeHelp
18
+ extend AutoCorrector
18
19
 
19
20
  MSG = 'Use `<=` instead of `ancestors.include?`.'
21
+ RESTRICT_ON_SEND = %i[include?].freeze
20
22
 
21
23
  def_node_matcher :ancestors_include_candidate?, <<~PATTERN
22
24
  (send (send $_subclass :ancestors) :include? $_superclass)
23
25
  PATTERN
24
26
 
25
27
  def on_send(node)
26
- return unless ancestors_include_candidate?(node)
28
+ return unless (subclass, superclass = ancestors_include_candidate?(node))
29
+ return if subclass && !subclass.const_type?
27
30
 
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
+ add_offense(range(node)) do |corrector|
32
+ subclass_source = subclass ? subclass.source : 'self'
31
33
 
32
- add_offense(node, location: range)
34
+ corrector.replace(node, "#{subclass_source} <= #{superclass.source}")
35
+ end
33
36
  end
34
37
 
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
38
+ private
39
+
40
+ def range(node)
41
+ location_of_ancestors = node.children[0].loc.selector.begin_pos
42
+ end_location = node.loc.selector.end_pos
43
+
44
+ range_between(location_of_ancestors, end_location)
41
45
  end
42
46
  end
43
47
  end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # This cop identifies places where slicing arrays with semi-infinite ranges
7
+ # can be replaced by `Array#take` and `Array#drop`.
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
10
+ # This cop is also unsafe for string slices because strings do not have `#take` and `#drop` methods.
11
+ #
12
+ # @example
13
+ # # bad
14
+ # # array[..2]
15
+ # # array[...2]
16
+ # # array[2..]
17
+ # # array[2...]
18
+ # # array.slice(..2)
19
+ #
20
+ # # good
21
+ # array.take(3)
22
+ # array.take(2)
23
+ # array.drop(2)
24
+ # array.drop(2)
25
+ # array.take(3)
26
+ #
27
+ class ArraySemiInfiniteRangeSlice < Base
28
+ include RangeHelp
29
+ extend AutoCorrector
30
+ extend TargetRubyVersion
31
+
32
+ minimum_target_ruby_version 2.7
33
+
34
+ MSG = 'Use `%<prefer>s` instead of `%<current>s` with semi-infinite range.'
35
+
36
+ SLICE_METHODS = Set[:[], :slice].freeze
37
+ RESTRICT_ON_SEND = SLICE_METHODS
38
+
39
+ def_node_matcher :endless_range_slice?, <<~PATTERN
40
+ (send $_ $%SLICE_METHODS $#endless_range?)
41
+ PATTERN
42
+
43
+ def_node_matcher :endless_range?, <<~PATTERN
44
+ {
45
+ ({irange erange} nil? (int positive?))
46
+ ({irange erange} (int positive?) nil?)
47
+ }
48
+ PATTERN
49
+
50
+ def on_send(node)
51
+ endless_range_slice?(node) do |receiver, method_name, range_node|
52
+ prefer = range_node.begin ? :drop : :take
53
+ message = format(MSG, prefer: prefer, current: method_name)
54
+
55
+ add_offense(node, message: message) do |corrector|
56
+ corrector.replace(node, correction(receiver, range_node))
57
+ end
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def correction(receiver, range_node)
64
+ method_call = if range_node.begin
65
+ "drop(#{range_node.begin.value})"
66
+ elsif range_node.irange_type?
67
+ "take(#{range_node.end.value + 1})"
68
+ else
69
+ "take(#{range_node.end.value})"
70
+ end
71
+
72
+ "#{receiver.source}.#{method_call}"
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -8,34 +8,37 @@ module RuboCop
8
8
  # than from Numeric for BigDecimal.
9
9
  #
10
10
  # @example
11
- #
12
11
  # # bad
13
- # BigDecimal(1, 2)
14
- # BigDecimal(1.2, 3, exception: true)
12
+ # BigDecimal(1, 2)
13
+ # BigDecimal(1.2, 3, exception: true)
15
14
  #
16
15
  # # good
17
- # BigDecimal('1', 2)
18
- # BigDecimal('1.2', 3, exception: true)
16
+ # BigDecimal('1', 2)
17
+ # BigDecimal('1.2', 3, exception: true)
19
18
  #
20
- class BigDecimalWithNumericArgument < Cop
19
+ class BigDecimalWithNumericArgument < Base
20
+ extend AutoCorrector
21
+
21
22
  MSG = 'Convert numeric argument to string before passing to `BigDecimal`.'
23
+ RESTRICT_ON_SEND = %i[BigDecimal].freeze
22
24
 
23
25
  def_node_matcher :big_decimal_with_numeric_argument?, <<~PATTERN
24
26
  (send nil? :BigDecimal $numeric_type? ...)
25
27
  PATTERN
26
28
 
27
29
  def on_send(node)
28
- big_decimal_with_numeric_argument?(node) do |numeric|
29
- add_offense(node, location: numeric.source_range)
30
+ return unless (numeric = big_decimal_with_numeric_argument?(node))
31
+ return if numeric.float_type? && specifies_precision?(node)
32
+
33
+ add_offense(numeric.source_range) do |corrector|
34
+ corrector.wrap(numeric, "'", "'")
30
35
  end
31
36
  end
32
37
 
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
38
+ private
39
+
40
+ def specifies_precision?(node)
41
+ node.arguments.size > 1 && !node.arguments[1].hash_type?
39
42
  end
40
43
  end
41
44
  end
@@ -19,14 +19,16 @@ module RuboCop
19
19
  # # good
20
20
  # umethod.bind_call(obj, foo, bar)
21
21
  #
22
- class BindCall < Cop
22
+ class BindCall < Base
23
23
  include RangeHelp
24
+ extend AutoCorrector
24
25
  extend TargetRubyVersion
25
26
 
26
27
  minimum_target_ruby_version 2.7
27
28
 
28
29
  MSG = 'Use `bind_call(%<bind_arg>s%<comma>s%<call_args>s)` ' \
29
30
  'instead of `bind(%<bind_arg>s).call(%<call_args>s)`.'
31
+ RESTRICT_ON_SEND = %i[call].freeze
30
32
 
31
33
  def_node_matcher :bind_with_call_method?, <<~PATTERN
32
34
  (send
@@ -37,28 +39,17 @@ module RuboCop
37
39
  PATTERN
38
40
 
39
41
  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)
42
+ return unless (receiver, bind_arg, call_args_node = bind_with_call_method?(node))
53
43
 
54
44
  range = correction_range(receiver, node)
55
-
56
45
  call_args = build_call_args(call_args_node)
57
- call_args = ", #{call_args}" unless call_args.empty?
46
+ message = message(bind_arg.source, call_args)
47
+
48
+ add_offense(range, message: message) do |corrector|
49
+ call_args = ", #{call_args}" unless call_args.empty?
58
50
 
59
- replacement_method = "bind_call(#{bind_arg.source}#{call_args})"
51
+ replacement_method = "bind_call(#{bind_arg.source}#{call_args})"
60
52
 
61
- lambda do |corrector|
62
53
  corrector.replace(range, replacement_method)
63
54
  end
64
55
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # This cop identifies unnecessary use of a `block_given?` where explicit check
7
+ # of block argument would suffice.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # def method(&block)
12
+ # do_something if block_given?
13
+ # end
14
+ #
15
+ # # good
16
+ # def method(&block)
17
+ # do_something if block
18
+ # end
19
+ #
20
+ # # good - block is reassigned
21
+ # def method(&block)
22
+ # block ||= -> { do_something }
23
+ # warn "Using default ..." unless block_given?
24
+ # # ...
25
+ # end
26
+ #
27
+ class BlockGivenWithExplicitBlock < Base
28
+ extend AutoCorrector
29
+
30
+ RESTRICT_ON_SEND = %i[block_given?].freeze
31
+ MSG = 'Check block argument explicitly instead of using `block_given?`.'
32
+
33
+ def_node_matcher :reassigns_block_arg?, '`(lvasgn %1 ...)'
34
+
35
+ def on_send(node)
36
+ def_node = node.each_ancestor(:def, :defs).first
37
+ return unless def_node
38
+
39
+ block_arg = def_node.arguments.find(&:blockarg_type?)
40
+ return unless block_arg
41
+
42
+ block_arg_name = block_arg.loc.name.source.to_sym
43
+ return if reassigns_block_arg?(def_node, block_arg_name)
44
+
45
+ add_offense(node) do |corrector|
46
+ corrector.replace(node, block_arg_name)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end