rubocop-performance 1.6.1 → 1.9.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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -0
  3. data/config/default.yml +95 -8
  4. data/lib/rubocop/cop/mixin/regexp_metacharacter.rb +4 -4
  5. data/lib/rubocop/cop/mixin/sort_block.rb +28 -0
  6. data/lib/rubocop/cop/performance/ancestors_include.rb +49 -0
  7. data/lib/rubocop/cop/performance/array_semi_infinite_range_slice.rb +74 -0
  8. data/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb +46 -0
  9. data/lib/rubocop/cop/performance/bind_call.rb +9 -18
  10. data/lib/rubocop/cop/performance/block_given_with_explicit_block.rb +52 -0
  11. data/lib/rubocop/cop/performance/caller.rb +14 -15
  12. data/lib/rubocop/cop/performance/case_when_splat.rb +18 -11
  13. data/lib/rubocop/cop/performance/casecmp.rb +13 -20
  14. data/lib/rubocop/cop/performance/chain_array_allocation.rb +4 -10
  15. data/lib/rubocop/cop/performance/collection_literal_in_loop.rb +140 -0
  16. data/lib/rubocop/cop/performance/compare_with_block.rb +10 -21
  17. data/lib/rubocop/cop/performance/constant_regexp.rb +68 -0
  18. data/lib/rubocop/cop/performance/count.rb +14 -16
  19. data/lib/rubocop/cop/performance/delete_prefix.rb +14 -22
  20. data/lib/rubocop/cop/performance/delete_suffix.rb +14 -22
  21. data/lib/rubocop/cop/performance/detect.rb +65 -32
  22. data/lib/rubocop/cop/performance/double_start_end_with.rb +16 -24
  23. data/lib/rubocop/cop/performance/end_with.rb +9 -13
  24. data/lib/rubocop/cop/performance/fixed_size.rb +2 -1
  25. data/lib/rubocop/cop/performance/flat_map.rb +21 -22
  26. data/lib/rubocop/cop/performance/inefficient_hash_search.rb +15 -14
  27. data/lib/rubocop/cop/performance/io_readlines.rb +112 -0
  28. data/lib/rubocop/cop/performance/method_object_as_block.rb +32 -0
  29. data/lib/rubocop/cop/performance/open_struct.rb +3 -2
  30. data/lib/rubocop/cop/performance/range_include.rb +15 -11
  31. data/lib/rubocop/cop/performance/redundant_block_call.rb +15 -10
  32. data/lib/rubocop/cop/performance/redundant_match.rb +12 -6
  33. data/lib/rubocop/cop/performance/redundant_merge.rb +19 -17
  34. data/lib/rubocop/cop/performance/redundant_sort_block.rb +43 -0
  35. data/lib/rubocop/cop/performance/redundant_string_chars.rb +129 -0
  36. data/lib/rubocop/cop/performance/regexp_match.rb +20 -20
  37. data/lib/rubocop/cop/performance/reverse_each.rb +10 -5
  38. data/lib/rubocop/cop/performance/reverse_first.rb +73 -0
  39. data/lib/rubocop/cop/performance/size.rb +42 -43
  40. data/lib/rubocop/cop/performance/sort_reverse.rb +45 -0
  41. data/lib/rubocop/cop/performance/squeeze.rb +67 -0
  42. data/lib/rubocop/cop/performance/start_with.rb +9 -13
  43. data/lib/rubocop/cop/performance/string_include.rb +56 -0
  44. data/lib/rubocop/cop/performance/string_replacement.rb +24 -27
  45. data/lib/rubocop/cop/performance/sum.rb +236 -0
  46. data/lib/rubocop/cop/performance/times_map.rb +12 -18
  47. data/lib/rubocop/cop/performance/unfreeze_string.rb +20 -2
  48. data/lib/rubocop/cop/performance/uri_default_parser.rb +7 -12
  49. data/lib/rubocop/cop/performance_cops.rb +16 -0
  50. data/lib/rubocop/performance/version.rb +6 -1
  51. metadata +35 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 111d420207542c8522d9f34b5b8bf24356572b5fbcf0c11dfcb1e7f738a4c76f
4
- data.tar.gz: 97632031f51e154320bc3d585da448740596436882b67277f1b5d26ddfe55fba
3
+ metadata.gz: 9b44c147120a9f64f16a0bf21782f1dbe0a51c64170c3a5c217e548eded4e106
4
+ data.tar.gz: 7b4e6e464ee3631071f3bd3f447acde2a59763d326a86658412f4ec9527850a6
5
5
  SHA512:
6
- metadata.gz: a36e6950e6fe7847303c87862df76767d32c4f68640c229ad4ceb6115d4ae2093be18a8296c2e49324c67e8eb06dd99db42f8823586f1b572e10a74693b831d1
7
- data.tar.gz: 782352b1a7c420ac3c5498aeff749577e6280fb35d6e36844ae875511e9b6b44becfd9a2789d1b4c672cb71a91b8ab1cc63c89ec41abda8d1f34f1dacd26a07c
6
+ metadata.gz: e450740a348bd17e276af8393f4e8225521afc4c9fac5816c9a3bbd9883d16a15f28a1044e24b89c9037593f4d2931cf3dc900385d42d7ad035dbb1dd461a306
7
+ data.tar.gz: feef1ae1d9735d3abadf17799ecfb4840ce359fa932902387633e67dc15e1eeb62573ae0a2832c050a8722e004f614c109483a9d8857143f28c866dbd65bc5bf
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).
@@ -1,15 +1,38 @@
1
1
  # This is the default configuration file.
2
2
 
3
+ Performance/AncestorsInclude:
4
+ Description: 'Use `A <= B` instead of `A.ancestors.include?(B)`.'
5
+ Reference: 'https://github.com/JuanitoFatas/fast-ruby#ancestorsinclude-vs--code'
6
+ Enabled: 'pending'
7
+ Safe: false
8
+ VersionAdded: '1.7'
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
+ Enabled: pending
13
+ VersionAdded: '1.9'
14
+
15
+ Performance/BigDecimalWithNumericArgument:
16
+ Description: 'Convert numeric argument to string before passing to BigDecimal.'
17
+ Enabled: 'pending'
18
+ VersionAdded: '1.7'
19
+
3
20
  Performance/BindCall:
4
21
  Description: 'Use `bind_call(obj, args, ...)` instead of `bind(obj).call(args, ...)`.'
5
22
  Enabled: true
6
23
  VersionAdded: '1.6'
7
24
 
25
+ Performance/BlockGivenWithExplicitBlock:
26
+ Description: 'Check block argument explicitly instead of using `block_given?`.'
27
+ Enabled: pending
28
+ VersionAdded: '1.9'
29
+
8
30
  Performance/Caller:
9
31
  Description: >-
10
32
  Use `caller(n..n)` instead of `caller`.
11
33
  Enabled: true
12
34
  VersionAdded: '0.49'
35
+ VersionChanged: '1.9'
13
36
 
14
37
  Performance/CaseWhenSplat:
15
38
  Description: >-
@@ -37,23 +60,33 @@ Performance/ChainArrayAllocation:
37
60
  Enabled: false
38
61
  VersionAdded: '0.59'
39
62
 
63
+ Performance/CollectionLiteralInLoop:
64
+ Description: 'Extract Array and Hash literals outside of loops into local variables or constants.'
65
+ Enabled: 'pending'
66
+ VersionAdded: '1.8'
67
+ # Min number of elements to consider an offense
68
+ MinSize: 1
69
+
40
70
  Performance/CompareWithBlock:
41
71
  Description: 'Use `sort_by(&:foo)` instead of `sort { |a, b| a.foo <=> b.foo }`.'
42
72
  Enabled: true
43
73
  VersionAdded: '0.46'
44
74
 
75
+ Performance/ConstantRegexp:
76
+ Description: 'Finds regular expressions with dynamic components that are all constants.'
77
+ Enabled: pending
78
+ VersionAdded: '1.9'
79
+
45
80
  Performance/Count:
46
81
  Description: >-
47
- Use `count` instead of `select...size`, `reject...size`,
48
- `select...count`, `reject...count`, `select...length`,
49
- and `reject...length`.
82
+ Use `count` instead of `{select,find_all,filter,reject}...{size,count,length}`.
50
83
  # This cop has known compatibility issues with `ActiveRecord` and other
51
84
  # frameworks. ActiveRecord's `count` ignores the block that is passed to it.
52
85
  # For more information, see the documentation in the cop itself.
53
86
  SafeAutoCorrect: false
54
87
  Enabled: true
55
88
  VersionAdded: '0.31'
56
- VersionChanged: '1.5'
89
+ VersionChanged: '1.8'
57
90
 
58
91
  Performance/DeletePrefix:
59
92
  Description: 'Use `delete_prefix` instead of `gsub`.'
@@ -69,8 +102,8 @@ Performance/DeleteSuffix:
69
102
 
70
103
  Performance/Detect:
71
104
  Description: >-
72
- Use `detect` instead of `select.first`, `find_all.first`,
73
- `select.last`, and `find_all.last`.
105
+ Use `detect` instead of `select.first`, `find_all.first`, `filter.first`,
106
+ `select.last`, `find_all.last`, and `filter.last`.
74
107
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerabledetect-vs-enumerableselectfirst-code'
75
108
  # This cop has known compatibility issues with `ActiveRecord` and other
76
109
  # frameworks. `ActiveRecord` does not implement a `detect` method and `find`
@@ -79,7 +112,7 @@ Performance/Detect:
79
112
  SafeAutoCorrect: false
80
113
  Enabled: true
81
114
  VersionAdded: '0.30'
82
- VersionChanged: '1.5'
115
+ VersionChanged: '1.8'
83
116
 
84
117
  Performance/DoubleStartEndWith:
85
118
  Description: >-
@@ -131,6 +164,18 @@ Performance/InefficientHashSearch:
131
164
  VersionAdded: '0.56'
132
165
  Safe: false
133
166
 
167
+ Performance/IoReadlines:
168
+ Description: 'Use `IO.each_line` (`IO#each_line`) instead of `IO.readlines` (`IO#readlines`).'
169
+ Reference: 'https://docs.gitlab.com/ee/development/performance.html#reading-from-files-and-other-data-sources'
170
+ Enabled: false
171
+ VersionAdded: '1.7'
172
+
173
+ Performance/MethodObjectAsBlock:
174
+ Description: 'Use block explicitly instead of block-passing a method object.'
175
+ Reference: 'https://github.com/JuanitoFatas/fast-ruby#normal-way-to-apply-method-vs-method-code'
176
+ Enabled: pending
177
+ VersionAdded: '1.9'
178
+
134
179
  Performance/OpenStruct:
135
180
  Description: 'Use `Struct` instead of `OpenStruct`.'
136
181
  Enabled: false
@@ -138,10 +183,11 @@ Performance/OpenStruct:
138
183
  Safe: false
139
184
 
140
185
  Performance/RangeInclude:
141
- Description: 'Use `Range#cover?` instead of `Range#include?`.'
186
+ Description: 'Use `Range#cover?` instead of `Range#include?` (or `Range#member?`).'
142
187
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#cover-vs-include-code'
143
188
  Enabled: true
144
189
  VersionAdded: '0.36'
190
+ VersionChanged: '1.7'
145
191
  Safe: false
146
192
 
147
193
  Performance/RedundantBlockCall:
@@ -165,6 +211,16 @@ Performance/RedundantMerge:
165
211
  # Max number of key-value pairs to consider an offense
166
212
  MaxKeyValuePairs: 2
167
213
 
214
+ Performance/RedundantSortBlock:
215
+ Description: 'Use `sort` instead of `sort { |a, b| a <=> b }`.'
216
+ Enabled: 'pending'
217
+ VersionAdded: '1.7'
218
+
219
+ Performance/RedundantStringChars:
220
+ Description: 'Checks for redundant `String#chars`.'
221
+ Enabled: 'pending'
222
+ VersionAdded: '1.7'
223
+
168
224
  Performance/RegexpMatch:
169
225
  Description: >-
170
226
  Use `match?` instead of `Regexp#match`, `String#match`, `Symbol#match`,
@@ -179,6 +235,11 @@ Performance/ReverseEach:
179
235
  Enabled: true
180
236
  VersionAdded: '0.30'
181
237
 
238
+ Performance/ReverseFirst:
239
+ Description: 'Use `last(n).reverse` instead of `reverse.first(n)`.'
240
+ Enabled: 'pending'
241
+ VersionAdded: '1.7'
242
+
182
243
  Performance/Size:
183
244
  Description: >-
184
245
  Use `size` instead of `count` for counting
@@ -187,6 +248,17 @@ Performance/Size:
187
248
  Enabled: true
188
249
  VersionAdded: '0.30'
189
250
 
251
+ Performance/SortReverse:
252
+ Description: 'Use `sort.reverse` instead of `sort { |a, b| b <=> a }`.'
253
+ Enabled: 'pending'
254
+ VersionAdded: '1.7'
255
+
256
+ Performance/Squeeze:
257
+ Description: "Use `squeeze('a')` instead of `gsub(/a+/, 'a')`."
258
+ Reference: 'https://github.com/JuanitoFatas/fast-ruby#remove-extra-spaces-or-other-contiguous-characters-code'
259
+ Enabled: 'pending'
260
+ VersionAdded: '1.7'
261
+
190
262
  Performance/StartWith:
191
263
  Description: 'Use `start_with?` instead of a regex match anchored to the beginning of a string.'
192
264
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringmatch-vs-stringstart_withstringend_with-code-start-code-end'
@@ -200,6 +272,13 @@ Performance/StartWith:
200
272
  VersionAdded: '0.36'
201
273
  VersionChanged: '1.6'
202
274
 
275
+ Performance/StringInclude:
276
+ Description: 'Use `String#include?` instead of a regex match with literal-only pattern.'
277
+ Enabled: 'pending'
278
+ AutoCorrect: false
279
+ SafeAutoCorrect: false
280
+ VersionAdded: '1.7'
281
+
203
282
  Performance/StringReplacement:
204
283
  Description: >-
205
284
  Use `tr` instead of `gsub` when you are replacing the same
@@ -209,6 +288,12 @@ Performance/StringReplacement:
209
288
  Enabled: true
210
289
  VersionAdded: '0.33'
211
290
 
291
+ Performance/Sum:
292
+ Description: 'Use `sum` instead of a custom array summation.'
293
+ Reference: 'https://blog.bigbinary.com/2016/11/02/ruby-2-4-introduces-enumerable-sum.html'
294
+ Enabled: 'pending'
295
+ VersionAdded: '1.8'
296
+
212
297
  Performance/TimesMap:
213
298
  Description: 'Checks for .times.map calls.'
214
299
  AutoCorrect: false
@@ -220,7 +305,9 @@ Performance/TimesMap:
220
305
  Performance/UnfreezeString:
221
306
  Description: 'Use unary plus to get an unfrozen string literal.'
222
307
  Enabled: true
308
+ SafeAutoCorrect: false
223
309
  VersionAdded: '0.50'
310
+ VersionChanged: '1.9'
224
311
 
225
312
  Performance/UriDefaultParser:
226
313
  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)
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Common functionality for cops checking `Enumerable#sort` blocks.
6
+ module SortBlock
7
+ extend NodePattern::Macros
8
+ include RangeHelp
9
+
10
+ def_node_matcher :sort_with_block?, <<~PATTERN
11
+ (block
12
+ $(send _ :sort)
13
+ (args (arg $_a) (arg $_b))
14
+ $send)
15
+ PATTERN
16
+
17
+ def_node_matcher :replaceable_body?, <<~PATTERN
18
+ (send (lvar %1) :<=> (lvar %2))
19
+ PATTERN
20
+
21
+ private
22
+
23
+ def sort_range(send, node)
24
+ range_between(send.loc.selector.begin_pos, node.loc.end.end_pos)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # This cop is used to identify usages of `ancestors.include?` and
7
+ # change them to use `<=` instead.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # A.ancestors.include?(B)
12
+ #
13
+ # # good
14
+ # A <= B
15
+ #
16
+ class AncestorsInclude < Base
17
+ include RangeHelp
18
+ extend AutoCorrector
19
+
20
+ MSG = 'Use `<=` instead of `ancestors.include?`.'
21
+ RESTRICT_ON_SEND = %i[include?].freeze
22
+
23
+ def_node_matcher :ancestors_include_candidate?, <<~PATTERN
24
+ (send (send $_subclass :ancestors) :include? $_superclass)
25
+ PATTERN
26
+
27
+ def on_send(node)
28
+ return unless (subclass, superclass = ancestors_include_candidate?(node))
29
+ return if subclass && !subclass.const_type?
30
+
31
+ add_offense(range(node)) do |corrector|
32
+ subclass_source = subclass ? subclass.source : 'self'
33
+
34
+ corrector.replace(node, "#{subclass_source} <= #{superclass.source}")
35
+ end
36
+ end
37
+
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)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,74 @@
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
+ #
9
+ # @example
10
+ # # bad
11
+ # # array[..2]
12
+ # # array[...2]
13
+ # # array[2..]
14
+ # # array[2...]
15
+ # # array.slice(..2)
16
+ #
17
+ # # good
18
+ # array.take(3)
19
+ # array.take(2)
20
+ # array.drop(2)
21
+ # array.drop(2)
22
+ # array.take(3)
23
+ #
24
+ class ArraySemiInfiniteRangeSlice < Base
25
+ include RangeHelp
26
+ extend AutoCorrector
27
+ extend TargetRubyVersion
28
+
29
+ minimum_target_ruby_version 2.7
30
+
31
+ MSG = 'Use `%<prefer>s` instead of `%<current>s` with semi-infinite range.'
32
+
33
+ SLICE_METHODS = Set[:[], :slice].freeze
34
+ RESTRICT_ON_SEND = SLICE_METHODS
35
+
36
+ def_node_matcher :endless_range_slice?, <<~PATTERN
37
+ (send $_ $%SLICE_METHODS $#endless_range?)
38
+ PATTERN
39
+
40
+ def_node_matcher :endless_range?, <<~PATTERN
41
+ {
42
+ ({irange erange} nil? (int positive?))
43
+ ({irange erange} (int positive?) nil?)
44
+ }
45
+ PATTERN
46
+
47
+ def on_send(node)
48
+ endless_range_slice?(node) do |receiver, method_name, range_node|
49
+ prefer = range_node.begin ? :drop : :take
50
+ message = format(MSG, prefer: prefer, current: method_name)
51
+
52
+ add_offense(node, message: message) do |corrector|
53
+ corrector.replace(node, correction(receiver, range_node))
54
+ end
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def correction(receiver, range_node)
61
+ method_call = if range_node.begin
62
+ "drop(#{range_node.begin.value})"
63
+ elsif range_node.irange_type?
64
+ "take(#{range_node.end.value + 1})"
65
+ else
66
+ "take(#{range_node.end.value})"
67
+ end
68
+
69
+ "#{receiver.source}.#{method_call}"
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # This cop identifies places where numeric argument to BigDecimal should be
7
+ # converted to string. Initializing from String is faster
8
+ # than from Numeric for BigDecimal.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # BigDecimal(1, 2)
13
+ # BigDecimal(1.2, 3, exception: true)
14
+ #
15
+ # # good
16
+ # BigDecimal('1', 2)
17
+ # BigDecimal('1.2', 3, exception: true)
18
+ #
19
+ class BigDecimalWithNumericArgument < Base
20
+ extend AutoCorrector
21
+
22
+ MSG = 'Convert numeric argument to string before passing to `BigDecimal`.'
23
+ RESTRICT_ON_SEND = %i[BigDecimal].freeze
24
+
25
+ def_node_matcher :big_decimal_with_numeric_argument?, <<~PATTERN
26
+ (send nil? :BigDecimal $numeric_type? ...)
27
+ PATTERN
28
+
29
+ def on_send(node)
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, "'", "'")
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def specifies_precision?(node)
41
+ node.arguments.size > 1 && !node.arguments[1].hash_type?
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end