diff --git a/CHANGELOG.md b/CHANGELOG.md index 222a0c3b7c..edc7b56e9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### New features +* [#129](https://github.com/rubocop-hq/rubocop-performance/pull/129): Add new `Performance/BigDecimalString` cop. ([@fatkodima][]) * [#81](https://github.com/rubocop-hq/rubocop-performance/issues/81): Add new `Performance/StringInclude` cop. ([@fatkodima][]) * [#123](https://github.com/rubocop-hq/rubocop-performance/pull/123): Add new `Performance/AncestorsInclude` cop. ([@fatkodima][]) * [#125](https://github.com/rubocop-hq/rubocop-performance/pull/125): Support `Range#member?` method for `Performance/RangeInclude` cop. ([@fatkodima][]) diff --git a/config/default.yml b/config/default.yml index 943a0f8340..78722a8985 100644 --- a/config/default.yml +++ b/config/default.yml @@ -6,6 +6,11 @@ Performance/AncestorsInclude: Enabled: true VersionAdded: '1.7' +Performance/BigDecimalString: + Description: 'Convert numeric argument to string before passing to BigDecimal.' + Enabled: true + VersionAdded: '1.7' + Performance/BindCall: Description: 'Use `bind_call(obj, args, ...)` instead of `bind(obj).call(args, ...)`.' Enabled: true diff --git a/docs/modules/ROOT/pages/cops.adoc b/docs/modules/ROOT/pages/cops.adoc index 75357f3efc..4f3032097c 100644 --- a/docs/modules/ROOT/pages/cops.adoc +++ b/docs/modules/ROOT/pages/cops.adoc @@ -3,6 +3,7 @@ = Department xref:cops_performance.adoc[Performance] * xref:cops_performance.adoc#performanceancestorsinclude[Performance/AncestorsInclude] +* xref:cops_performance.adoc#performancebigdecimalstring[Performance/BigDecimalString] * xref:cops_performance.adoc#performancebindcall[Performance/BindCall] * xref:cops_performance.adoc#performancecaller[Performance/Caller] * xref:cops_performance.adoc#performancecasewhensplat[Performance/CaseWhenSplat] diff --git a/docs/modules/ROOT/pages/cops_performance.adoc b/docs/modules/ROOT/pages/cops_performance.adoc index 32a90e0ac0..20fc2493b8 100644 --- a/docs/modules/ROOT/pages/cops_performance.adoc +++ b/docs/modules/ROOT/pages/cops_performance.adoc @@ -30,6 +30,36 @@ A <= B * https://github.com/JuanitoFatas/fast-ruby#ancestorsinclude-vs--code +== Performance/BigDecimalString + +|=== +| Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged + +| Enabled +| Yes +| Yes +| 1.7 +| - +|=== + +This cop identifies places where numeric argument to BigDecimal should be +converted to string. Initializing from String is faster +than from Numeric for BigDecimal. + +BigDecimal(1, 2) +BigDecimal(1.2, 3, exception: true) + + # good +BigDecimal('1', 2) +BigDecimal('1.2', 3, exception: true) + +=== Examples + +[source,ruby] +---- +# bad +---- + == Performance/BindCall NOTE: Required Ruby version: 2.7 diff --git a/lib/rubocop/cop/performance/big_decimal_string.rb b/lib/rubocop/cop/performance/big_decimal_string.rb new file mode 100644 index 0000000000..0f33aface9 --- /dev/null +++ b/lib/rubocop/cop/performance/big_decimal_string.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Performance + # This cop identifies places where numeric argument to BigDecimal should be + # converted to string. Initializing from String is faster + # than from Numeric for BigDecimal. + # + # @example + # + # # bad + # BigDecimal(1, 2) + # BigDecimal(1.2, 3, exception: true) + # + # # good + # BigDecimal('1', 2) + # BigDecimal('1.2', 3, exception: true) + # + class BigDecimalString < Cop + MSG = 'Convert numeric argument to string before passing to `BigDecimal`.' + + def_node_matcher :big_decimal_string_candidate?, <<~PATTERN + (send nil? :BigDecimal $numeric_type? ...) + PATTERN + + def on_send(node) + big_decimal_string_candidate?(node) do |numeric| + add_offense(node, location: numeric.source_range) + end + end + + def autocorrect(node) + big_decimal_string_candidate?(node) do |numeric| + lambda do |corrector| + corrector.wrap(numeric, "'", "'") + end + end + end + end + end + end +end diff --git a/lib/rubocop/cop/performance_cops.rb b/lib/rubocop/cop/performance_cops.rb index 1d4b44080f..0f4c33f939 100644 --- a/lib/rubocop/cop/performance_cops.rb +++ b/lib/rubocop/cop/performance_cops.rb @@ -3,6 +3,7 @@ require_relative 'mixin/regexp_metacharacter' require_relative 'performance/ancestors_include' +require_relative 'performance/big_decimal_string' require_relative 'performance/bind_call' require_relative 'performance/caller' require_relative 'performance/case_when_splat' diff --git a/spec/rubocop/cop/performance/big_decimal_string_spec.rb b/spec/rubocop/cop/performance/big_decimal_string_spec.rb new file mode 100644 index 0000000000..50f66184a8 --- /dev/null +++ b/spec/rubocop/cop/performance/big_decimal_string_spec.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +RSpec.describe RuboCop::Cop::Performance::BigDecimalString do + subject(:cop) { described_class.new } + + it 'registers an offense and corrects when using `BigDecimal` with integer' do + expect_offense(<<~RUBY) + BigDecimal(1) + ^ Convert numeric argument to string before passing to `BigDecimal`. + RUBY + + expect_correction(<<~RUBY) + BigDecimal('1') + RUBY + end + + it 'registers an offense and corrects when using `BigDecimal` with float' do + expect_offense(<<~RUBY) + BigDecimal(1.5, 2, exception: true) + ^^^ Convert numeric argument to string before passing to `BigDecimal`. + RUBY + + expect_correction(<<~RUBY) + BigDecimal('1.5', 2, exception: true) + RUBY + end + + it 'does not register an offense when using `BigDecimal` with string' do + expect_no_offenses(<<~RUBY) + BigDecimal('1') + RUBY + end +end