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 a new check for self assigning variable #3010

Merged
merged 2 commits into from Jul 17, 2019
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
2 changes: 1 addition & 1 deletion .isort.cfg
Expand Up @@ -3,5 +3,5 @@ multi_line_output=3
line_length=88
known_third_party=astroid, sphinx, isort, pytest, mccabe, six,
include_trailing_comma=True
skip_glob=*/functional/**,*/input/**,*/test/extension/**,*/test/regrtest_data/**,*/test/data/**
skip_glob=tests/functional/**,tests/input/**,*/test/extension/**,tests/regrtest_data/**,*tests/data/**
project=pylint
6 changes: 6 additions & 0 deletions ChangeLog
Expand Up @@ -7,6 +7,12 @@ What's New in Pylint 2.4.0?

Release date: TBA

* Added a new check, ``self-assigning-variable``

This check is emitted when we detect that a variable is assigned
to itself, which might indicate a potential bug in the code application.
Close #2930

* Added a new check, ``property-with-parameters``.

This check is emitted when we detect that a defined property also
Expand Down
16 changes: 16 additions & 0 deletions doc/whatsnew/2.4.rst
Expand Up @@ -20,6 +20,22 @@ New checkers

Close #2905

* Added a new check, ``self-assigning-variable``

This check is emitted when we detect that a variable is assigned
to itself, which might indicate a potential bug in the code application.

For example, the following would raise this warning::

def new_a(attr, attr2):
a_inst = Aclass()
a_inst.attr2 = attr2
# should be: a_inst.attr = attr, but have a typo
attr = attr
return a_inst

Close #2930

* Added a new check ``property-with-parameters`` which detects when a property
has more than a single argument.

Expand Down
37 changes: 37 additions & 0 deletions pylint/checkers/base.py
Expand Up @@ -981,6 +981,11 @@ class BasicChecker(_BasicChecker):
"Emitted when a conditional statement (If or ternary if) "
"seems to wrongly call a function due to missing parentheses",
),
"W0127": (
"Assigning the same variable %r to itself",
"self-assigning-variable",
"Emitted when we detect that a variable is assigned to itself",
),
"E0111": (
"The first reversed() argument is not a sequence",
"bad-reversed-sequence",
Expand Down Expand Up @@ -1497,6 +1502,38 @@ def visit_with(self, node):
# we assume it's a nested "with"
self.add_message("confusing-with-statement", node=node)

@utils.check_messages("self-assigning-variable")
def visit_assign(self, node):

# Detect assigning to the same variable.
rhs_names = []
targets = node.targets
if isinstance(targets[0], astroid.Tuple):
if len(targets) != 1:
# A complex assignment, so bail out early.
return
targets = targets[0].elts

if isinstance(node.value, astroid.Name):
if len(targets) != 1:
return
rhs_names = [node.value]
elif isinstance(node.value, astroid.Tuple):
rhs_count = len(node.value.elts)
if len(targets) != rhs_count or rhs_count == 1:
return
rhs_names = node.value.elts

for target, lhs_name in zip(targets, rhs_names):
if not isinstance(lhs_name, astroid.Name):
continue
if not isinstance(target, astroid.AssignName):
continue
if target.name == lhs_name.name:
self.add_message(
"self-assigning-variable", args=(target.name,), node=target
)


KNOWN_NAME_TYPES = {
"module",
Expand Down
20 changes: 20 additions & 0 deletions tests/functional/self_assigning_variable.py
@@ -0,0 +1,20 @@
# pylint: disable=missing-docstring,too-few-public-methods
# pylint: disable=unpacking-non-sequence,attribute-defined-outside-init,invalid-name

class Class:
pass

CLS = Class()
FIRST = 1
# Not enough values on the right hand side
FIRST, SECOND = FIRST
# Not enough values on the left hand side
FIRST = FIRST, SECOND
# Not equivalent to a self assignment
FIRST = (FIRST, )
# Not assigning to an attribute
CLS.FIRST = FIRST
# Not a name on the right hand side
FIRST = Class()
FIRST = FIRST # [self-assigning-variable]
FIRST, SECOND = FIRST, CLS.FIRST # [self-assigning-variable]
2 changes: 2 additions & 0 deletions tests/functional/self_assigning_variable.txt
@@ -0,0 +1,2 @@
self-assigning-variable:19::Assigning the same variable 'FIRST' to itself
self-assigning-variable:20::Assigning the same variable 'FIRST' to itself
2 changes: 1 addition & 1 deletion tests/functional/unused_import.py
Expand Up @@ -9,7 +9,7 @@
from collections import deque, OrderedDict, Counter
import re, html.parser # [unused-import]
DATA = Counter()

# pylint: disable=self-assigning-variable
from fake import SomeName, SomeOtherName # [unused-import]
class SomeClass(object):
SomeName = SomeName # https://bitbucket.org/logilab/pylint/issue/475
Expand Down