From b05a51c3a0b8f69d25811a29e636ce2eafa0a81d Mon Sep 17 00:00:00 2001 From: Zopolis4 Date: Mon, 29 Jul 2024 10:42:54 +1000 Subject: [PATCH] Add new `Lint/UselessNumericOperation` cop Makes progress on #11191. Certain numeric operations have no impact, being: Adding or subtracting 0, multiplying or dividing by 1 or raising to the power of 1. These are probably mistakes or leftover from debugging, so flag and optionally remove them. --- .../new_lint_useless_numeric_operation.md | 1 + config/default.yml | 5 + lib/rubocop.rb | 1 + .../cop/lint/useless_numeric_operation.rb | 77 ++++++++++++ .../lint/useless_numeric_operation_spec.rb | 113 ++++++++++++++++++ 5 files changed, 197 insertions(+) create mode 100644 changelog/new_lint_useless_numeric_operation.md create mode 100644 lib/rubocop/cop/lint/useless_numeric_operation.rb create mode 100644 spec/rubocop/cop/lint/useless_numeric_operation_spec.rb diff --git a/changelog/new_lint_useless_numeric_operation.md b/changelog/new_lint_useless_numeric_operation.md new file mode 100644 index 000000000000..fd221e44f97f --- /dev/null +++ b/changelog/new_lint_useless_numeric_operation.md @@ -0,0 +1 @@ +* [#13074](https://github.com/rubocop/rubocop/issues/13074): Add new `Lint/UselessNumericOperation` cop to check for inconsequential numeric operations. ([@zopolis4][]) diff --git a/config/default.yml b/config/default.yml index bc52d408ecf7..08cd7e0484bb 100644 --- a/config/default.yml +++ b/config/default.yml @@ -2559,6 +2559,11 @@ Lint/UselessMethodDefinition: VersionChanged: '1.61' Safe: false +Lint/UselessNumericOperation: + Description: 'Checks for useless numeric operations.' + Enabled: pending + VersionAdded: '<>' + Lint/UselessRescue: Description: 'Checks for useless `rescue`s.' Enabled: pending diff --git a/lib/rubocop.rb b/lib/rubocop.rb index aa9eb2b714f8..22303d400e5e 100644 --- a/lib/rubocop.rb +++ b/lib/rubocop.rb @@ -416,6 +416,7 @@ require_relative 'rubocop/cop/lint/useless_assignment' require_relative 'rubocop/cop/lint/useless_else_without_rescue' require_relative 'rubocop/cop/lint/useless_method_definition' +require_relative 'rubocop/cop/lint/useless_numeric_operation' require_relative 'rubocop/cop/lint/useless_rescue' require_relative 'rubocop/cop/lint/useless_ruby2_keywords' require_relative 'rubocop/cop/lint/useless_setter_call' diff --git a/lib/rubocop/cop/lint/useless_numeric_operation.rb b/lib/rubocop/cop/lint/useless_numeric_operation.rb new file mode 100644 index 000000000000..9a93ecb45aa7 --- /dev/null +++ b/lib/rubocop/cop/lint/useless_numeric_operation.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Lint + # Certain numeric operations have no impact, being: + # Adding or subtracting 0, multiplying or dividing by 1 or raising to the power of 1. + # These are probably leftover from debugging, or are mistakes. + # + # @example + # + # # bad + # x + 0 + # x - 0 + # x * 1 + # x / 1 + # x ** 1 + # + # # good + # x + # + # # bad + # x += 0 + # x -= 0 + # x *= 1 + # x /= 1 + # x **= 1 + # + # # good + # x = x + # + class UselessNumericOperation < Base + extend AutoCorrector + MSG = 'Do not apply inconsequential numeric operations to variables.' + RESTRICT_ON_SEND = %i[+ - * / **].freeze + + # @!method useless_operation?(node) + def_node_matcher :useless_operation?, '(send (send nil? $_) $_ (int $_))' + + # @!method useless_abbreviated_assignment?(node) + def_node_matcher :useless_abbreviated_assignment?, '(op-asgn (lvasgn $_) $_ (int $_))' + + def on_send(node) + return unless useless_operation?(node) + + variable, operation, number = useless_operation?(node) + return unless useless?(operation, number) + + add_offense(node) do |corrector| + corrector.replace(node, variable) + end + end + + def on_op_asgn(node) + return unless useless_abbreviated_assignment?(node) + + variable, operation, number = useless_abbreviated_assignment?(node) + return unless useless?(operation, number) + + add_offense(node) do |corrector| + corrector.replace(node, "#{variable} = #{variable}") + end + end + + private + + def useless?(operation, number) + if number.zero? + true if %i[+ -].include?(operation) + elsif number == 1 + true if %i[* / **].include?(operation) + end + end + end + end + end +end diff --git a/spec/rubocop/cop/lint/useless_numeric_operation_spec.rb b/spec/rubocop/cop/lint/useless_numeric_operation_spec.rb new file mode 100644 index 000000000000..2a255838485a --- /dev/null +++ b/spec/rubocop/cop/lint/useless_numeric_operation_spec.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true + +RSpec.describe RuboCop::Cop::Lint::UselessNumericOperation, :config do + it 'registers an offense when 0 is added to a variable' do + expect_offense(<<~RUBY) + x + 0 + ^^^^^ Do not apply inconsequential numeric operations to variables. + RUBY + + expect_correction(<<~RUBY) + x + RUBY + end + + it 'registers an offense when 0 is subtracted from a variable' do + expect_offense(<<~RUBY) + x - 0 + ^^^^^ Do not apply inconsequential numeric operations to variables. + RUBY + + expect_correction(<<~RUBY) + x + RUBY + end + + it 'registers an offense when a variable is multiplied by 1' do + expect_offense(<<~RUBY) + x * 1 + ^^^^^ Do not apply inconsequential numeric operations to variables. + RUBY + + expect_correction(<<~RUBY) + x + RUBY + end + + it 'registers an offense when a variable is divided by 1' do + expect_offense(<<~RUBY) + x / 1 + ^^^^^ Do not apply inconsequential numeric operations to variables. + RUBY + + expect_correction(<<~RUBY) + x + RUBY + end + + it 'registers an offense when a variable is raised to the power of 1' do + expect_offense(<<~RUBY) + x ** 1 + ^^^^^^ Do not apply inconsequential numeric operations to variables. + RUBY + + expect_correction(<<~RUBY) + x + RUBY + end + + it 'registers an offense when a variable is set to itself plus zero via abbreviated assignment' do + expect_offense(<<~RUBY) + x += 0 + ^^^^^^ Do not apply inconsequential numeric operations to variables. + RUBY + + expect_correction(<<~RUBY) + x = x + RUBY + end + + it 'registers an offense when a variable is set to itself minus zero via abbreviated assignment' do + expect_offense(<<~RUBY) + x -= 0 + ^^^^^^ Do not apply inconsequential numeric operations to variables. + RUBY + + expect_correction(<<~RUBY) + x = x + RUBY + end + + it 'registers an offense when a variable is set to itself times one via abbreviated assignment' do + expect_offense(<<~RUBY) + x *= 1 + ^^^^^^ Do not apply inconsequential numeric operations to variables. + RUBY + + expect_correction(<<~RUBY) + x = x + RUBY + end + + it 'registers an offense when a variable is set to itself divided by one via abbreviated assignment' do + expect_offense(<<~RUBY) + x /= 1 + ^^^^^^ Do not apply inconsequential numeric operations to variables. + RUBY + + expect_correction(<<~RUBY) + x = x + RUBY + end + + it 'registers an offense when a variable is set to itself raised to the power of one via abbreviated assignment' do + expect_offense(<<~RUBY) + x **= 1 + ^^^^^^^ Do not apply inconsequential numeric operations to variables. + RUBY + + expect_correction(<<~RUBY) + x = x + RUBY + end +end