Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new Lint/SelfAssignment cop #8387

Merged
merged 2 commits into from Jul 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -5,6 +5,7 @@
### New features

* [#8322](https://github.com/rubocop-hq/rubocop/pull/8322): Support autocorrect for `Style/CaseEquality` cop. ([@fatkodima][])
* [#8291](https://github.com/rubocop-hq/rubocop/pull/8291): Add new `Lint/SelfAssignment` cop. ([@fatkodima][])
* [#8389](https://github.com/rubocop-hq/rubocop/pull/8389): Add new `Lint/DuplicateRescueException` cop. ([@fatkodima][])
* [#8376](https://github.com/rubocop-hq/rubocop/pull/8376): Add new `Lint/MissingSuper` cop. ([@fatkodima][])
* [#8339](https://github.com/rubocop-hq/rubocop/pull/8339): Add `Config#for_badge` as an efficient way to get a cop's config merged with its department's. ([@marcandre][])
Expand Down
5 changes: 5 additions & 0 deletions config/default.yml
Expand Up @@ -1770,6 +1770,11 @@ Lint/ScriptPermission:
VersionAdded: '0.49'
VersionChanged: '0.50'

Lint/SelfAssignment:
Description: 'Checks for self-assignments.'
Enabled: pending
VersionAdded: '0.89'

Lint/SendWithMixinArgument:
Description: 'Checks for `send` method when using mixin.'
Enabled: true
Expand Down
1 change: 1 addition & 0 deletions docs/modules/ROOT/pages/cops.adoc
Expand Up @@ -248,6 +248,7 @@ In the following section you find all available cops:
* xref:cops_lint.adoc#lintsafenavigationconsistency[Lint/SafeNavigationConsistency]
* xref:cops_lint.adoc#lintsafenavigationwithempty[Lint/SafeNavigationWithEmpty]
* xref:cops_lint.adoc#lintscriptpermission[Lint/ScriptPermission]
* xref:cops_lint.adoc#lintselfassignment[Lint/SelfAssignment]
* xref:cops_lint.adoc#lintsendwithmixinargument[Lint/SendWithMixinArgument]
* xref:cops_lint.adoc#lintshadowedargument[Lint/ShadowedArgument]
* xref:cops_lint.adoc#lintshadowedexception[Lint/ShadowedException]
Expand Down
29 changes: 29 additions & 0 deletions docs/modules/ROOT/pages/cops_lint.adoc
Expand Up @@ -3049,6 +3049,35 @@ puts 'hello, world'
puts 'hello, world'
----

== Lint/SelfAssignment

|===
| Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged

| Pending
| Yes
| No
| 0.89
| -
|===

This cop checks for self-assignments.

=== Examples

[source,ruby]
----
# bad
foo = foo
foo, bar = foo, bar
Foo = Foo

# good
foo = bar
foo, bar = bar, foo
Foo = Bar
----

== Lint/SendWithMixinArgument

|===
Expand Down
1 change: 1 addition & 0 deletions lib/rubocop.rb
Expand Up @@ -305,6 +305,7 @@
require_relative 'rubocop/cop/lint/safe_navigation_chain'
require_relative 'rubocop/cop/lint/safe_navigation_with_empty'
require_relative 'rubocop/cop/lint/script_permission'
require_relative 'rubocop/cop/lint/self_assignment'
require_relative 'rubocop/cop/lint/send_with_mixin_argument'
require_relative 'rubocop/cop/lint/shadowed_argument'
require_relative 'rubocop/cop/lint/shadowed_exception'
Expand Down
78 changes: 78 additions & 0 deletions lib/rubocop/cop/lint/self_assignment.rb
@@ -0,0 +1,78 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Lint
# This cop checks for self-assignments.
#
# @example
# # bad
# foo = foo
# foo, bar = foo, bar
# Foo = Foo
#
# # good
# foo = bar
# foo, bar = bar, foo
# Foo = Bar
#
class SelfAssignment < Base
MSG = 'Self-assignment detected.'

ASSIGNMENT_TYPE_TO_RHS_TYPE = {
lvasgn: :lvar,
ivasgn: :ivar,
cvasgn: :cvar,
gvasgn: :gvar
}.freeze

def on_lvasgn(node)
lhs, rhs = *node
return unless rhs

rhs_type = ASSIGNMENT_TYPE_TO_RHS_TYPE[node.type]

add_offense(node) if rhs.type == rhs_type && rhs.source == lhs.to_s
end
alias on_ivasgn on_lvasgn
alias on_cvasgn on_lvasgn
alias on_gvasgn on_lvasgn

def on_casgn(node)
lhs_scope, lhs_name, rhs = *node
return unless rhs.const_type?

rhs_scope, rhs_name = *rhs
add_offense(node) if lhs_scope == rhs_scope && lhs_name == rhs_name
end

def on_masgn(node)
add_offense(node) if multiple_self_assignment?(node)
end

def on_or_asgn(node)
lhs, rhs = *node
add_offense(node) if rhs_matches_lhs?(rhs, lhs)
end
alias on_and_asgn on_or_asgn

private

def multiple_self_assignment?(node)
lhs, rhs = *node
return false unless rhs.array_type?
return false unless lhs.children.size == rhs.children.size

lhs.children.zip(rhs.children).all? do |lhs_item, rhs_item|
rhs_matches_lhs?(rhs_item, lhs_item)
end
end

def rhs_matches_lhs?(rhs, lhs)
rhs.type == ASSIGNMENT_TYPE_TO_RHS_TYPE[lhs.type] &&
rhs.children.first == lhs.children.first
end
end
end
end
end
128 changes: 128 additions & 0 deletions spec/rubocop/cop/lint/self_assignment_spec.rb
@@ -0,0 +1,128 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::Lint::SelfAssignment do
subject(:cop) { described_class.new }

it 'registers an offense when using local var self-assignment' do
expect_offense(<<~RUBY)
foo = foo
^^^^^^^^^ Self-assignment detected.
RUBY
end

it 'does not register an offense when using local var assignment' do
expect_no_offenses(<<~RUBY)
foo = bar
RUBY
end

it 'registers an offense when using instance var self-assignment' do
expect_offense(<<~RUBY)
@foo = @foo
^^^^^^^^^^^ Self-assignment detected.
RUBY
end

it 'does not register an offense when using instance var assignment' do
expect_no_offenses(<<~RUBY)
@foo = @bar
RUBY
end

it 'registers an offense when using class var self-assignment' do
expect_offense(<<~RUBY)
@@foo = @@foo
^^^^^^^^^^^^^ Self-assignment detected.
RUBY
end

it 'does not register an offense when using class var assignment' do
expect_no_offenses(<<~RUBY)
@@foo = @@bar
RUBY
end

it 'registers an offense when using global var self-assignment' do
expect_offense(<<~RUBY)
$foo = $foo
^^^^^^^^^^^ Self-assignment detected.
RUBY
end

it 'does not register an offense when using global var assignment' do
expect_no_offenses(<<~RUBY)
$foo = $bar
RUBY
end

it 'registers an offense when using constant var self-assignment' do
expect_offense(<<~RUBY)
Foo = Foo
^^^^^^^^^ Self-assignment detected.
RUBY
end

it 'does not register an offense when using constant var assignment for constant from another scope' do
expect_no_offenses(<<~RUBY)
Foo = ::Foo
RUBY
end

it 'registers an offense when using multiple var self-assignment' do
expect_offense(<<~RUBY)
foo, bar = foo, bar
^^^^^^^^^^^^^^^^^^^ Self-assignment detected.
RUBY
end

it 'registers an offense when using multiple var self-assignment through array' do
expect_offense(<<~RUBY)
foo, bar = [foo, bar]
^^^^^^^^^^^^^^^^^^^^^ Self-assignment detected.
RUBY
end

it 'does not register an offense when using multiple var assignment' do
expect_no_offenses(<<~RUBY)
foo, bar = bar, foo
RUBY
end

it 'does not register an offense when using multiple var assignment through splat' do
expect_no_offenses(<<~RUBY)
foo, bar = *something
RUBY
end

it 'does not register an offense when using multiple var assignment through method call' do
expect_no_offenses(<<~RUBY)
foo, bar = something
RUBY
end

it 'registers an offense when using shorthand-or var self-assignment' do
expect_offense(<<~RUBY)
foo ||= foo
^^^^^^^^^^^ Self-assignment detected.
RUBY
end

it 'does not register an offense when using shorthand-or var assignment' do
expect_no_offenses(<<~RUBY)
foo ||= bar
RUBY
end

it 'registers an offense when using shorthand-and var self-assignment' do
expect_offense(<<~RUBY)
foo &&= foo
^^^^^^^^^^^ Self-assignment detected.
RUBY
end

it 'does not register an offense when using shorthand-and var assignment' do
expect_no_offenses(<<~RUBY)
foo &&= bar
RUBY
end
end