Skip to content

Commit 0f401f7

Browse files
authored
Merge pull request #463 from koic/fix_false_positives_for_performance_big_decimal_with_numeric_argument
[Fix #454] Fix false positives for `Performance/BigDecimalWithNumericArgument`
2 parents b52d821 + 6f20945 commit 0f401f7

File tree

2 files changed

+153
-140
lines changed

2 files changed

+153
-140
lines changed

lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,45 +3,48 @@
33
module RuboCop
44
module Cop
55
module Performance
6-
# Identifies places where numeric argument to BigDecimal should be
7-
# converted to string. Initializing from String is faster
8-
# than from Numeric for BigDecimal.
6+
# Identifies places where string argument to `BigDecimal` should be
7+
# converted to numeric. Initializing from Integer is faster
8+
# than from String for BigDecimal.
99
#
1010
# @example
1111
# # bad
12-
# BigDecimal(1, 2)
13-
# 4.to_d(6)
14-
# BigDecimal(1.2, 3, exception: true)
15-
# 4.5.to_d(6, exception: true)
16-
#
17-
# # good
1812
# BigDecimal('1', 2)
1913
# BigDecimal('4', 6)
2014
# BigDecimal('1.2', 3, exception: true)
2115
# BigDecimal('4.5', 6, exception: true)
2216
#
17+
# # good
18+
# BigDecimal(1, 2)
19+
# 4.to_d(6)
20+
# BigDecimal(1.2, 3, exception: true)
21+
# 4.5.to_d(6, exception: true)
22+
#
2323
class BigDecimalWithNumericArgument < Base
2424
extend AutoCorrector
25+
extend TargetRubyVersion
26+
27+
minimum_target_ruby_version 3.1
2528

26-
MSG = 'Convert numeric literal to string and pass it to `BigDecimal`.'
29+
MSG = 'Convert string literal to numeric and pass it to `BigDecimal`.'
2730
RESTRICT_ON_SEND = %i[BigDecimal to_d].freeze
2831

2932
def_node_matcher :big_decimal_with_numeric_argument?, <<~PATTERN
30-
(send nil? :BigDecimal $numeric_type? ...)
33+
(send nil? :BigDecimal $str_type? ...)
3134
PATTERN
3235

3336
def_node_matcher :to_d?, <<~PATTERN
34-
(send [!nil? $numeric_type?] :to_d ...)
37+
(send [!nil? $str_type?] :to_d ...)
3538
PATTERN
3639

3740
def on_send(node)
38-
if (numeric = big_decimal_with_numeric_argument?(node))
39-
add_offense(numeric.source_range) do |corrector|
40-
corrector.wrap(numeric, "'", "'")
41+
if (string = big_decimal_with_numeric_argument?(node))
42+
add_offense(string.source_range) do |corrector|
43+
corrector.replace(string, string.value)
4144
end
42-
elsif (numeric_to_d = to_d?(node))
43-
add_offense(numeric_to_d.source_range) do |corrector|
44-
big_decimal_args = node.arguments.map(&:source).unshift("'#{numeric_to_d.source}'").join(', ')
45+
elsif (string_to_d = to_d?(node))
46+
add_offense(string_to_d.source_range) do |corrector|
47+
big_decimal_args = node.arguments.map(&:source).unshift(string_to_d.value).join(', ')
4548

4649
corrector.replace(node, "BigDecimal(#{big_decimal_args})")
4750
end
Lines changed: 132 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,129 +1,139 @@
11
# frozen_string_literal: true
22

33
RSpec.describe RuboCop::Cop::Performance::BigDecimalWithNumericArgument, :config do
4-
it 'registers an offense and corrects when using `BigDecimal` with integer' do
5-
expect_offense(<<~RUBY)
6-
BigDecimal(1)
7-
^ Convert numeric literal to string and pass it to `BigDecimal`.
8-
RUBY
9-
10-
expect_correction(<<~RUBY)
11-
BigDecimal('1')
12-
RUBY
4+
context 'when Ruby >= 3.1', :ruby31 do
5+
it 'registers an offense and corrects when using `BigDecimal` with string' do
6+
expect_offense(<<~RUBY)
7+
BigDecimal('1')
8+
^^^ Convert string literal to numeric and pass it to `BigDecimal`.
9+
RUBY
10+
11+
expect_correction(<<~RUBY)
12+
BigDecimal(1)
13+
RUBY
14+
end
15+
16+
it 'registers an offense and corrects when using `String#to_d`' do
17+
expect_offense(<<~RUBY)
18+
'1'.to_d
19+
^^^ Convert string literal to numeric and pass it to `BigDecimal`.
20+
RUBY
21+
22+
expect_correction(<<~RUBY)
23+
BigDecimal(1)
24+
RUBY
25+
end
26+
27+
it 'registers an offense and corrects when using `BigDecimal` with float string' do
28+
expect_offense(<<~RUBY)
29+
BigDecimal('1.5', exception: true)
30+
^^^^^ Convert string literal to numeric and pass it to `BigDecimal`.
31+
RUBY
32+
33+
expect_correction(<<~RUBY)
34+
BigDecimal(1.5, exception: true)
35+
RUBY
36+
end
37+
38+
it 'registers an offense and corrects when using float `String#to_d`' do
39+
expect_offense(<<~RUBY)
40+
'1.5'.to_d(exception: true)
41+
^^^^^ Convert string literal to numeric and pass it to `BigDecimal`.
42+
RUBY
43+
44+
expect_correction(<<~RUBY)
45+
BigDecimal(1.5, exception: true)
46+
RUBY
47+
end
48+
49+
it 'registers an offense when using `BigDecimal` with float string and precision' do
50+
expect_offense(<<~RUBY)
51+
BigDecimal('3.14', 1)
52+
^^^^^^ Convert string literal to numeric and pass it to `BigDecimal`.
53+
RUBY
54+
55+
expect_correction(<<~RUBY)
56+
BigDecimal(3.14, 1)
57+
RUBY
58+
end
59+
60+
it 'registers an offense when using float `String#to_d` with precision' do
61+
expect_offense(<<~RUBY)
62+
'3.14'.to_d(1)
63+
^^^^^^ Convert string literal to numeric and pass it to `BigDecimal`.
64+
RUBY
65+
66+
expect_correction(<<~RUBY)
67+
BigDecimal(3.14, 1)
68+
RUBY
69+
end
70+
71+
it 'registers an offense when using `BigDecimal` with float string and non-literal precision' do
72+
expect_offense(<<~RUBY)
73+
precision = 1
74+
BigDecimal('3.14', precision)
75+
^^^^^^ Convert string literal to numeric and pass it to `BigDecimal`.
76+
RUBY
77+
78+
expect_correction(<<~RUBY)
79+
precision = 1
80+
BigDecimal(3.14, precision)
81+
RUBY
82+
end
83+
84+
it 'registers an offense when using float `String#to_d` with non-literal precision' do
85+
expect_offense(<<~RUBY)
86+
precision = 1
87+
'3.14'.to_d(precision)
88+
^^^^^^ Convert string literal to numeric and pass it to `BigDecimal`.
89+
RUBY
90+
91+
expect_correction(<<~RUBY)
92+
precision = 1
93+
BigDecimal(3.14, precision)
94+
RUBY
95+
end
96+
97+
it 'registers an offense when using `BigDecimal` with float string, precision, and a keyword argument' do
98+
expect_offense(<<~RUBY)
99+
BigDecimal('3.14', 1, exception: true)
100+
^^^^^^ Convert string literal to numeric and pass it to `BigDecimal`.
101+
RUBY
102+
103+
expect_correction(<<~RUBY)
104+
BigDecimal(3.14, 1, exception: true)
105+
RUBY
106+
end
107+
108+
it 'registers an offense when using float `String#to_d` with precision and a keyword argument' do
109+
expect_offense(<<~RUBY)
110+
'3.14'.to_d(1, exception: true)
111+
^^^^^^ Convert string literal to numeric and pass it to `BigDecimal`.
112+
RUBY
113+
114+
expect_correction(<<~RUBY)
115+
BigDecimal(3.14, 1, exception: true)
116+
RUBY
117+
end
118+
119+
it 'does not register an offense when using `BigDecimal` with integer' do
120+
expect_no_offenses(<<~RUBY)
121+
BigDecimal(1)
122+
RUBY
123+
end
124+
125+
it 'does not register an offense when using `Integer#to_d`' do
126+
expect_no_offenses(<<~RUBY)
127+
1.to_d
128+
RUBY
129+
end
13130
end
14131

15-
it 'registers an offense and corrects when using `Integer#to_d`' do
16-
expect_offense(<<~RUBY)
17-
1.to_d
18-
^ Convert numeric literal to string and pass it to `BigDecimal`.
19-
RUBY
20-
21-
expect_correction(<<~RUBY)
22-
BigDecimal('1')
23-
RUBY
24-
end
25-
26-
it 'registers an offense and corrects when using `BigDecimal` with float' do
27-
expect_offense(<<~RUBY)
28-
BigDecimal(1.5, exception: true)
29-
^^^ Convert numeric literal to string and pass it to `BigDecimal`.
30-
RUBY
31-
32-
expect_correction(<<~RUBY)
33-
BigDecimal('1.5', exception: true)
34-
RUBY
35-
end
36-
37-
it 'registers an offense and corrects when using `Float#to_d`' do
38-
expect_offense(<<~RUBY)
39-
1.5.to_d(exception: true)
40-
^^^ Convert numeric literal to string and pass it to `BigDecimal`.
41-
RUBY
42-
43-
expect_correction(<<~RUBY)
44-
BigDecimal('1.5', exception: true)
45-
RUBY
46-
end
47-
48-
it 'registers an offense when using `BigDecimal` with float and precision' do
49-
expect_offense(<<~RUBY)
50-
BigDecimal(3.14, 1)
51-
^^^^ Convert numeric literal to string and pass it to `BigDecimal`.
52-
RUBY
53-
54-
expect_correction(<<~RUBY)
55-
BigDecimal('3.14', 1)
56-
RUBY
57-
end
58-
59-
it 'registers an offense when using `Float#to_d` with precision' do
60-
expect_offense(<<~RUBY)
61-
3.14.to_d(1)
62-
^^^^ Convert numeric literal to string and pass it to `BigDecimal`.
63-
RUBY
64-
65-
expect_correction(<<~RUBY)
66-
BigDecimal('3.14', 1)
67-
RUBY
68-
end
69-
70-
it 'registers an offense when using `BigDecimal` with float and non-literal precision' do
71-
expect_offense(<<~RUBY)
72-
precision = 1
73-
BigDecimal(3.14, precision)
74-
^^^^ Convert numeric literal to string and pass it to `BigDecimal`.
75-
RUBY
76-
77-
expect_correction(<<~RUBY)
78-
precision = 1
79-
BigDecimal('3.14', precision)
80-
RUBY
81-
end
82-
83-
it 'registers an offense when using `Float#to_d` with non-literal precision' do
84-
expect_offense(<<~RUBY)
85-
precision = 1
86-
3.14.to_d(precision)
87-
^^^^ Convert numeric literal to string and pass it to `BigDecimal`.
88-
RUBY
89-
90-
expect_correction(<<~RUBY)
91-
precision = 1
92-
BigDecimal('3.14', precision)
93-
RUBY
94-
end
95-
96-
it 'registers an offense when using `BigDecimal` with float, precision, and a keyword argument' do
97-
expect_offense(<<~RUBY)
98-
BigDecimal(3.14, 1, exception: true)
99-
^^^^ Convert numeric literal to string and pass it to `BigDecimal`.
100-
RUBY
101-
102-
expect_correction(<<~RUBY)
103-
BigDecimal('3.14', 1, exception: true)
104-
RUBY
105-
end
106-
107-
it 'registers an offense when using `Float#to_d` with precision and a keyword argument' do
108-
expect_offense(<<~RUBY)
109-
3.14.to_d(1, exception: true)
110-
^^^^ Convert numeric literal to string and pass it to `BigDecimal`.
111-
RUBY
112-
113-
expect_correction(<<~RUBY)
114-
BigDecimal('3.14', 1, exception: true)
115-
RUBY
116-
end
117-
118-
it 'does not register an offense when using `BigDecimal` with string' do
119-
expect_no_offenses(<<~RUBY)
120-
BigDecimal('1')
121-
RUBY
122-
end
123-
124-
it 'does not register an offense when using `String#to_d`' do
125-
expect_no_offenses(<<~RUBY)
126-
'1'.to_d
127-
RUBY
132+
context 'when Ruby <= 3.0', :ruby30, unsupported_on: :prism do
133+
it 'does not register an offense and corrects when using `BigDecimal` with string' do
134+
expect_no_offenses(<<~RUBY)
135+
BigDecimal('1')
136+
RUBY
137+
end
128138
end
129139
end

0 commit comments

Comments
 (0)