rubocop-performance 1.7.0 → 1.9.1

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.
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