diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bad2dd4efc..003b17940b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -279,6 +279,7 @@ * [#7805](https://github.com/rubocop-hq/rubocop/pull/7805): Change `AllowComments` option of `Lint/SuppressedException` to true by default. ([@koic][]) * [#7320](https://github.com/rubocop-hq/rubocop/issues/7320): `Naming/MethodName` now flags `attr_reader/attr_writer/attr_accessor/attr`. ([@denys281][]) * [#7813](https://github.com/rubocop-hq/rubocop/issues/7813): **(Breaking)** Remove `Lint/EndInMethod` cop. ([@tejasbubane][]) +* [#7737](https://github.com/rubocop-hq/rubocop/issues/7737): Add new `Style/RedundantArguments` cop. ([@tejasbubane][]) ## 0.80.1 (2020-02-29) diff --git a/config/default.yml b/config/default.yml index f3a87958b8d..ef09d08d773 100644 --- a/config/default.yml +++ b/config/default.yml @@ -3660,6 +3660,15 @@ Style/RandomWithOffset: Enabled: true VersionAdded: '0.52' +Style/RedundantArguments: + Description: 'Check for redundant arguments to methods.' + Enabled: pending + Safe: false + VersionAdded: '0.88' + RedundantArguments: + join: '' + split: ' ' + Style/RedundantAssignment: Description: 'Checks for redundant assignment before returning.' Enabled: 'pending' diff --git a/docs/modules/ROOT/pages/cops.adoc b/docs/modules/ROOT/pages/cops.adoc index 17a9797ca3f..9c0f25c18fe 100644 --- a/docs/modules/ROOT/pages/cops.adoc +++ b/docs/modules/ROOT/pages/cops.adoc @@ -437,6 +437,7 @@ In the following section you find all available cops: * xref:cops_style.adoc#styleproc[Style/Proc] * xref:cops_style.adoc#styleraiseargs[Style/RaiseArgs] * xref:cops_style.adoc#stylerandomwithoffset[Style/RandomWithOffset] +* xref:cops_style.adoc#styleredundantarguments[Style/RedundantArguments] * xref:cops_style.adoc#styleredundantassignment[Style/RedundantAssignment] * xref:cops_style.adoc#styleredundantbegin[Style/RedundantBegin] * xref:cops_style.adoc#styleredundantcapitalw[Style/RedundantCapitalW] diff --git a/docs/modules/ROOT/pages/cops_style.adoc b/docs/modules/ROOT/pages/cops_style.adoc index 564c88a591b..a2a8273e7f6 100644 --- a/docs/modules/ROOT/pages/cops_style.adoc +++ b/docs/modules/ROOT/pages/cops_style.adoc @@ -7097,6 +7097,61 @@ rand(1...7) * https://rubystyle.guide#random-numbers +== Style/RedundantArguments + +|=== +| Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged + +| Pending +| No +| No +| 0.88 +| - +|=== + +This cop checks for redundant arguments to methods. +Method names & arguments can be configured like: + +RedundantArguments: + join: '' + split: ' ' + foo: 2 + +Limitations: +1. This cop matches for method names only and hence cannot tell apart + methods with same name in different classes. +2. This cop is limited to methods with single parameter. + +=== Examples + +[source,ruby] +---- +# bad + +array.join('') +[1, 2, 3].join("") +string.split(" ") +"first\nsecond".split(" ") +A.foo(2) + +# good +array.join +[1, 2, 3].join +string.split +"first second".split +A.foo +---- + +=== Configurable attributes + +|=== +| Name | Default value | Configurable values + +| RedundantArguments +| `{"join"=>"", "split"=>" "}` +| +|=== + == Style/RedundantAssignment |=== diff --git a/lib/rubocop.rb b/lib/rubocop.rb index 9628ad53de5..37f45db0cc8 100644 --- a/lib/rubocop.rb +++ b/lib/rubocop.rb @@ -486,6 +486,7 @@ require_relative 'rubocop/cop/style/proc' require_relative 'rubocop/cop/style/raise_args' require_relative 'rubocop/cop/style/random_with_offset' +require_relative 'rubocop/cop/style/redundant_arguments' require_relative 'rubocop/cop/style/redundant_begin' require_relative 'rubocop/cop/style/redundant_capital_w' require_relative 'rubocop/cop/style/redundant_condition' diff --git a/lib/rubocop/cop/correctors/percent_literal_corrector.rb b/lib/rubocop/cop/correctors/percent_literal_corrector.rb index 1731125557e..3115ed70539 100644 --- a/lib/rubocop/cop/correctors/percent_literal_corrector.rb +++ b/lib/rubocop/cop/correctors/percent_literal_corrector.rb @@ -53,7 +53,7 @@ def new_contents(node, escape, delimiters) def autocorrect_multiline_words(node, escape, delimiters) contents = process_multiline_words(node, escape, delimiters) contents << end_content(node.source) - contents.join('') + contents.join end def autocorrect_words(node, escape, delimiters) diff --git a/lib/rubocop/cop/lint/literal_in_interpolation.rb b/lib/rubocop/cop/lint/literal_in_interpolation.rb index d704b222834..612a1ac9819 100644 --- a/lib/rubocop/cop/lint/literal_in_interpolation.rb +++ b/lib/rubocop/cop/lint/literal_in_interpolation.rb @@ -83,7 +83,7 @@ def autocorrected_value_for_symbol(node) def autocorrected_value_for_array(node) return node.source.gsub('"', '\"') unless node.percent_literal? - contents_range(node).source.split(' ').to_s.gsub('"', '\"') + contents_range(node).source.split.to_s.gsub('"', '\"') end # Does node print its own source when converted to a string? diff --git a/lib/rubocop/cop/style/redundant_arguments.rb b/lib/rubocop/cop/style/redundant_arguments.rb new file mode 100644 index 00000000000..85b8181600a --- /dev/null +++ b/lib/rubocop/cop/style/redundant_arguments.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +require 'parser/current' + +module RuboCop + module Cop + module Style + # This cop checks for redundant arguments to methods. + # Method names & arguments can be configured like: + # + # RedundantArguments: + # join: '' + # split: ' ' + # foo: 2 + # + # Limitations: + # 1. This cop matches for method names only and hence cannot tell apart + # methods with same name in different classes. + # 2. This cop is limited to methods with single parameter. + # + # @example + # # bad + # + # array.join('') + # [1, 2, 3].join("") + # string.split(" ") + # "first\nsecond".split(" ") + # A.foo(2) + # + # # good + # array.join + # [1, 2, 3].join + # string.split + # "first second".split + # A.foo + class RedundantArguments < Cop + MSG = 'Argument is redundant.' + + def on_send(node) + return unless redundant_argument?(node) + + add_offense(node) + end + + private + + def redundant_argument?(node) + redundant_argument = redundant_arg_for_method(node.method_name.to_s) + return false if redundant_argument.nil? + + node.arguments.first == redundant_argument + end + + def redundant_arg_for_method(method_name) + return nil unless cop_config['RedundantArguments'].key?(method_name) + + @mem ||= {} + @mem[method_name] ||= begin + arg = cop_config['RedundantArguments'].fetch(method_name) + buffer = Parser::Source::Buffer.new('(string)', 1) + buffer.source = arg.inspect + builder = RuboCop::AST::Builder.new + Parser::CurrentRuby.new(builder).parse(buffer) + end + end + end + end + end +end diff --git a/spec/rubocop/cop/style/redundant_arguments_spec.rb b/spec/rubocop/cop/style/redundant_arguments_spec.rb new file mode 100644 index 00000000000..a91aa455690 --- /dev/null +++ b/spec/rubocop/cop/style/redundant_arguments_spec.rb @@ -0,0 +1,120 @@ +# frozen_string_literal: true + +RSpec.describe RuboCop::Cop::Style::RedundantArguments, :config do + subject(:cop) { described_class.new(config) } + + context 'join' do + let(:cop_config) do + { 'RedundantArguments' => { 'join' => '' } } + end + + it 'registers an offense when using `#join` with empty string argument' do + expect_offense(<<~RUBY) + foo.join('') + ^^^^^^^^^^^^ Argument is redundant. + RUBY + end + + it 'registers an offense when using `#join` with double quoted string' do + expect_offense(<<~RUBY) + foo.join("") + ^^^^^^^^^^^^ Argument is redundant. + RUBY + end + + it 'registers an offense when using `#join` on literal arrays' do + expect_offense(<<~RUBY) + [1, 2, 3].join("") + ^^^^^^^^^^^^^^^^^^ Argument is redundant. + RUBY + end + + it 'does not register an offense when using `#join` with no arguments' do + expect_no_offenses(<<~RUBY) + foo.join + RUBY + end + + it 'does not register an offense when using `#join` with array literals' do + expect_no_offenses(<<~RUBY) + [1, 2, 3].join + RUBY + end + + it 'does not register an offense when using `#join` with separator' do + expect_no_offenses(<<~RUBY) + foo.join(',') + RUBY + end + end + + context 'split' do + let(:cop_config) do + { 'RedundantArguments' => { 'split' => ' ' } } + end + + it 'registers an offense when using `#split` with space' do + expect_offense(<<~RUBY) + foo.split(' ') + ^^^^^^^^^^^^^^ Argument is redundant. + RUBY + end + + it 'registers an offense when using `#split` on string literal' do + expect_offense(<<~RUBY) + "first second".split(' ') + ^^^^^^^^^^^^^^^^^^^^^^^^^ Argument is redundant. + RUBY + end + + it 'registers an offense when using `#join` with double quoted string' do + expect_offense(<<~RUBY) + foo.split(" ") + ^^^^^^^^^^^^^^ Argument is redundant. + RUBY + end + + it 'does not register an offense when using `#split` with no arguments' do + expect_no_offenses(<<~RUBY) + foo.split + RUBY + end + + it 'does not register an offense when using `#split` with string literal' do + expect_no_offenses(<<~RUBY) + "first second".split + RUBY + end + + it 'does not register an offense when using `#split` with separator' do + expect_no_offenses(<<~RUBY) + foo.split(',') + RUBY + end + + it 'does not register an offense when using `#split` with empty string' do + expect_no_offenses(<<~RUBY) + foo.split('') + RUBY + end + end + + context 'non-builtin method' do + let(:cop_config) do + { 'RedundantArguments' => { 'foo' => 2 } } + end + + it 'registers an offense with configured argument' do + expect_offense(<<~RUBY) + A.foo(2) + ^^^^^^^^ Argument is redundant. + RUBY + end + + it 'does not register an offense with other argument' do + expect_no_offenses(<<~RUBY) + A.foo(5) + RUBY + end + end +end