diff --git a/changelog/fix_an_incorrect_autocorrect_for_style_hash_except.md b/changelog/fix_an_incorrect_autocorrect_for_style_hash_except.md new file mode 100644 index 00000000000..6961faadeb3 --- /dev/null +++ b/changelog/fix_an_incorrect_autocorrect_for_style_hash_except.md @@ -0,0 +1 @@ +* [#10754](https://github.com/rubocop/rubocop/issues/10754): Fix an incorrect autocorrect for `Style/HashExcept` when using a non-literal collection receiver for `include?`. ([@koic][]) diff --git a/lib/rubocop/cop/style/hash_except.rb b/lib/rubocop/cop/style/hash_except.rb index 6b5a489035e..4bad19d08e6 100644 --- a/lib/rubocop/cop/style/hash_except.rb +++ b/lib/rubocop/cop/style/hash_except.rb @@ -144,7 +144,7 @@ def except_key_source(key) return key.join(', ') end - key.source + key.literal? ? key.source : "*#{key.source}" end def decorate_source(value) diff --git a/spec/rubocop/cop/style/hash_except_spec.rb b/spec/rubocop/cop/style/hash_except_spec.rb index f22ad234569..265e87a2bcf 100644 --- a/spec/rubocop/cop/style/hash_except_spec.rb +++ b/spec/rubocop/cop/style/hash_except_spec.rb @@ -142,6 +142,30 @@ {foo: 1, bar: 2, baz: 3}.except("\#{foo}", 'bar') RUBY end + + it 'registers and corrects an offense when using `reject` and calling `include?` method with variable' do + expect_offense(<<~RUBY) + array = [:foo, :bar] + {foo: 1, bar: 2, baz: 3}.reject { |k, v| !array.include?(k) } + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `except(*array)` instead. + RUBY + + expect_correction(<<~RUBY) + array = [:foo, :bar] + {foo: 1, bar: 2, baz: 3}.except(*array) + RUBY + end + + it 'registers and corrects an offense when using `reject` and calling `include?` method with method call' do + expect_offense(<<~RUBY) + {foo: 1, bar: 2, baz: 3}.reject { |k, v| !array.include?(k) } + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `except(*array)` instead. + RUBY + + expect_correction(<<~RUBY) + {foo: 1, bar: 2, baz: 3}.except(*array) + RUBY + end end context 'using `exclude?`' do @@ -316,6 +340,30 @@ {foo: 1, bar: 2, baz: 3}.except("\#{foo}", 'bar') RUBY end + + it 'registers and corrects an offense when using `reject` and calling `key.in?` method with variable' do + expect_offense(<<~RUBY) + array = %i[foo bar] + {foo: 1, bar: 2, baz: 3}.reject { |k, v| k.in?(array) } + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `except(*array)` instead. + RUBY + + expect_correction(<<~RUBY) + array = %i[foo bar] + {foo: 1, bar: 2, baz: 3}.except(*array) + RUBY + end + + it 'registers and corrects an offense when using `reject` and calling `key.in?` method with method call' do + expect_offense(<<~RUBY) + {foo: 1, bar: 2, baz: 3}.reject { |k, v| k.in?(array) } + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `except(*array)` instead. + RUBY + + expect_correction(<<~RUBY) + {foo: 1, bar: 2, baz: 3}.except(*array) + RUBY + end end context 'using `include?`' do @@ -373,6 +421,30 @@ {foo: 1, bar: 2, baz: 3}.except("\#{foo}", 'bar') RUBY end + + it 'registers and corrects an offense when using `reject` and calling `include?` method with variable' do + expect_offense(<<~RUBY) + array = %i[foo bar] + {foo: 1, bar: 2, baz: 3}.reject { |k, v| !array.include?(k) } + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `except(*array)` instead. + RUBY + + expect_correction(<<~RUBY) + array = %i[foo bar] + {foo: 1, bar: 2, baz: 3}.except(*array) + RUBY + end + + it 'registers and corrects an offense when using `reject` and calling `include?` method with method call' do + expect_offense(<<~RUBY) + {foo: 1, bar: 2, baz: 3}.reject { |k, v| !array.include?(k) } + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `except(*array)` instead. + RUBY + + expect_correction(<<~RUBY) + {foo: 1, bar: 2, baz: 3}.except(*array) + RUBY + end end context 'using `exclude?`' do @@ -430,6 +502,30 @@ {foo: 1, bar: 2, baz: 3}.except("\#{foo}", 'bar') RUBY end + + it 'registers and corrects an offense when using `reject` and calling `!exclude?` method with variable' do + expect_offense(<<~RUBY) + array = %i[foo bar] + {foo: 1, bar: 2, baz: 3}.reject { |k, v| !array.exclude?(k) } + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `except(*array)` instead. + RUBY + + expect_correction(<<~RUBY) + array = %i[foo bar] + {foo: 1, bar: 2, baz: 3}.except(*array) + RUBY + end + + it 'registers and corrects an offense when using `reject` and calling `!exclude?` method with method call' do + expect_offense(<<~RUBY) + {foo: 1, bar: 2, baz: 3}.reject { |k, v| !array.exclude?(k) } + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `except(*array)` instead. + RUBY + + expect_correction(<<~RUBY) + {foo: 1, bar: 2, baz: 3}.except(*array) + RUBY + end end it 'does not register an offense when using `reject` and other than comparison by string and symbol using `==`' do