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

Report unassigned expressions #55

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions pyflakes/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
PY2 = sys.version_info < (3, 0)
PY32 = sys.version_info < (3, 3) # Python 2.5 to 3.2
PY33 = sys.version_info < (3, 4) # Python 2.5 to 3.3
PY34 = sys.version_info < (3, 5) # Python 2.5 to 3.4
try:
sys.pypy_version_info
PYPY = True
Expand All @@ -33,6 +34,20 @@
from pyflakes import messages


ALLOWED_EXPR_NODE_TYPE = (
ast.Str, ast.Yield, ast.Call,
ast.Name, ast.Attribute,
ast.Ellipsis,
)

if not PY32:
ALLOWED_EXPR_NODE_TYPE = ALLOWED_EXPR_NODE_TYPE + (
ast.YieldFrom, )

if not PY34:
ALLOWED_EXPR_NODE_TYPE = ALLOWED_EXPR_NODE_TYPE + (
ast.Await, )

if PY2:
def getNodeType(node_class):
# workaround str.upper() which is locale-dependent
Expand Down Expand Up @@ -772,6 +787,15 @@ def ignore(self, node):
# additional node types
COMPREHENSION = KEYWORD = FORMATTEDVALUE = handleChildren

def EXPR(self, node):
value = node.value

if (not self._in_doctest() and
not isinstance(value, ALLOWED_EXPR_NODE_TYPE)):
self.report(messages.UnusedExpression, node)

self.handleNode(value, node)

def ASSERT(self, node):
if isinstance(node.test, ast.Tuple) and node.test.elts != []:
self.report(messages.AssertTuple, node)
Expand Down
11 changes: 11 additions & 0 deletions pyflakes/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,17 @@ def __init__(self, filename, loc, names):
self.message_args = (names,)


class UnusedExpression(Message):
"""
Indicates that an expression was not assigned.
"""
message = 'expression not used'

def __init__(self, filename, loc):
Message.__init__(self, filename, loc)
self.message_args = ()


class ReturnWithArgsInsideGenerator(Message):
"""
Indicates a return statement with arguments inside a generator.
Expand Down
79 changes: 43 additions & 36 deletions pyflakes/test/test_imports.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,38 +396,38 @@ def fun():
''')

def test_usedInOperators(self):
self.flakes('import fu; 3 + fu.bar')
self.flakes('import fu; 3 % fu.bar')
self.flakes('import fu; 3 - fu.bar')
self.flakes('import fu; 3 * fu.bar')
self.flakes('import fu; 3 ** fu.bar')
self.flakes('import fu; 3 / fu.bar')
self.flakes('import fu; 3 // fu.bar')
self.flakes('import fu; -fu.bar')
self.flakes('import fu; ~fu.bar')
self.flakes('import fu; 1 == fu.bar')
self.flakes('import fu; 1 | fu.bar')
self.flakes('import fu; 1 & fu.bar')
self.flakes('import fu; 1 ^ fu.bar')
self.flakes('import fu; 1 >> fu.bar')
self.flakes('import fu; 1 << fu.bar')
self.flakes('import fu; x = 3 + fu.bar')
self.flakes('import fu; x = 3 % fu.bar')
self.flakes('import fu; x = 3 - fu.bar')
self.flakes('import fu; x = 3 * fu.bar')
self.flakes('import fu; x = 3 ** fu.bar')
self.flakes('import fu; x = 3 / fu.bar')
self.flakes('import fu; x = 3 // fu.bar')
self.flakes('import fu; x = -fu.bar')
self.flakes('import fu; x = ~fu.bar')
self.flakes('import fu; x = 1 == fu.bar')
self.flakes('import fu; x = 1 | fu.bar')
self.flakes('import fu; x = 1 & fu.bar')
self.flakes('import fu; x = 1 ^ fu.bar')
self.flakes('import fu; x = 1 >> fu.bar')
self.flakes('import fu; x = 1 << fu.bar')

def test_usedInAssert(self):
self.flakes('import fu; assert fu.bar')

def test_usedInSubscript(self):
self.flakes('import fu; fu.bar[1]')
self.flakes('import fu; x = fu.bar[1]')

def test_usedInLogic(self):
self.flakes('import fu; fu and False')
self.flakes('import fu; fu or False')
self.flakes('import fu; not fu.bar')
self.flakes('import fu; x = fu and False')
self.flakes('import fu; x = fu or False')
self.flakes('import fu; x = not fu.bar')

def test_usedInList(self):
self.flakes('import fu; [fu]')
self.flakes('import fu; x = [fu]')

def test_usedInTuple(self):
self.flakes('import fu; (fu,)')
self.flakes('import fu; x = (fu,)')

def test_usedInTry(self):
self.flakes('''
Expand Down Expand Up @@ -465,8 +465,8 @@ def gen():
''')

def test_usedInDict(self):
self.flakes('import fu; {fu:None}')
self.flakes('import fu; {1:fu}')
self.flakes('import fu; x = {fu:None}')
self.flakes('import fu; x = {1:fu}')

def test_usedInParameterDefault(self):
self.flakes('''
Expand All @@ -486,13 +486,13 @@ def test_usedInAssignment(self):
self.flakes('import fu; n=0; n+=fu')

def test_usedInListComp(self):
self.flakes('import fu; [fu for _ in range(1)]')
self.flakes('import fu; [1 for _ in range(1) if fu]')
self.flakes('import fu; x = [fu for _ in range(1)]')
self.flakes('import fu; x = [1 for _ in range(1) if fu]')

@skipIf(version_info >= (3,),
'in Python 3 list comprehensions execute in a separate scope')
def test_redefinedByListComp(self):
self.flakes('import fu; [1 for fu in range(1)]',
self.flakes('import fu; x = [1 for fu in range(1)]',
m.RedefinedInListComp)

def test_usedInTryFinally(self):
Expand Down Expand Up @@ -553,7 +553,7 @@ def g(): foo.is_used()

@skipIf(version_info >= (3,), 'deprecated syntax')
def test_usedInBackquote(self):
self.flakes('import fu; `fu`')
self.flakes('import fu; x = `fu`')

def test_usedInExec(self):
if version_info < (3,):
Expand All @@ -563,15 +563,20 @@ def test_usedInExec(self):
self.flakes('import fu; %s' % exec_stmt)

def test_usedInLambda(self):
self.flakes('import fu; lambda: fu')
self.flakes('import fu; x = lambda: fu')

def test_shadowedByLambda(self):
self.flakes('import fu; lambda fu: fu',
self.flakes('import fu; x = lambda fu: fu',
m.UnusedImport, m.RedefinedWhileUnused)
self.flakes('import fu; lambda fu: fu\nfu()')

if self.withDoctest:
expect = []
else:
expect = [m.UnusedExpression]
self.flakes('import fu; lambda fu: fu\nfu()', *expect)

def test_usedInSliceObj(self):
self.flakes('import fu; "meow"[::fu]')
self.flakes('import fu; x = "meow"[::fu]')

def test_unusedInNestedScope(self):
self.flakes('''
Expand Down Expand Up @@ -677,12 +682,14 @@ def test_differentSubmoduleImport(self):
"""
self.flakes('''
import fu.bar, fu.baz
fu.bar, fu.baz
fu.bar
fu.baz
''')
self.flakes('''
import fu.bar
import fu.baz
fu.bar, fu.baz
fu.bar
fu.baz
''')

def test_assignRHSFirst(self):
Expand Down Expand Up @@ -903,15 +910,15 @@ def test_usedInGenExp(self):
"""
Using a global in a generator expression results in no warnings.
"""
self.flakes('import fu; (fu for _ in range(1))')
self.flakes('import fu; (1 for _ in range(1) if fu)')
self.flakes('import fu; x = (fu for _ in range(1))')
self.flakes('import fu; x = (1 for _ in range(1) if fu)')

def test_redefinedByGenExp(self):
"""
Re-using a global name as the loop variable for a generator
expression results in a redefinition warning.
"""
self.flakes('import fu; (1 for fu in range(1))',
self.flakes('import fu; x = (1 for fu in range(1))',
m.RedefinedWhileUnused, m.UnusedImport)

def test_usedAsDecorator(self):
Expand Down
Loading