From f0bc51b012de2bf0163f3b3cb9774a8fdf38f73b Mon Sep 17 00:00:00 2001 From: fatkodima Date: Sun, 7 Jun 2020 02:38:28 +0300 Subject: [PATCH] Add new `Performance/BigDecimalWithNumericArgument` cop --- CHANGELOG.md | 1 + config/default.yml | 5 +++ docs/modules/ROOT/pages/cops.adoc | 1 + docs/modules/ROOT/pages/cops_performance.adoc | 30 +++++++++++++ .../big_decimal_with_numeric_argument.rb | 43 +++++++++++++++++++ lib/rubocop/cop/performance_cops.rb | 1 + .../big_decimal_with_numeric_argument_spec.rb | 33 ++++++++++++++ 7 files changed, 114 insertions(+) create mode 100644 lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb create mode 100644 spec/rubocop/cop/performance/big_decimal_with_numeric_argument_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 222a0c3b7c..2dbcba6977 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/BigDecimalWithNumericArgument` 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..4e35fb3414 100644 --- a/config/default.yml +++ b/config/default.yml @@ -6,6 +6,11 @@ Performance/AncestorsInclude: Enabled: true VersionAdded: '1.7' +Performance/BigDecimalWithNumericArgument: + 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..fa1f95024f 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#performancebigdecimalwithnumericargument[Performance/BigDecimalWithNumericArgument] * 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..7b6e5e926c 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/BigDecimalWithNumericArgument + +|=== +| 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_with_numeric_argument.rb b/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb new file mode 100644 index 0000000000..01f3bdd182 --- /dev/null +++ b/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.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 BigDecimalWithNumericArgument < Cop + MSG = 'Convert numeric argument to string before passing to `BigDecimal`.' + + def_node_matcher :big_decimal_with_numeric_argument?, <<~PATTERN + (send nil? :BigDecimal $numeric_type? ...) + PATTERN + + def on_send(node) + big_decimal_with_numeric_argument?(node) do |numeric| + add_offense(node, location: numeric.source_range) + end + end + + def autocorrect(node) + big_decimal_with_numeric_argument?(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..200fcac555 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_with_numeric_argument' require_relative 'performance/bind_call' require_relative 'performance/caller' require_relative 'performance/case_when_splat' diff --git a/spec/rubocop/cop/performance/big_decimal_with_numeric_argument_spec.rb b/spec/rubocop/cop/performance/big_decimal_with_numeric_argument_spec.rb new file mode 100644 index 0000000000..919bb01382 --- /dev/null +++ b/spec/rubocop/cop/performance/big_decimal_with_numeric_argument_spec.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +RSpec.describe RuboCop::Cop::Performance::BigDecimalWithNumericArgument 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