summaryrefslogtreecommitdiff
path: root/pyflakes/checker.py
diff options
context:
space:
mode:
authorAaron Meurer <asmeurer@gmail.com>2015-11-13 07:31:40 -0500
committerPhil Frost <indigo@bitglue.com>2015-11-13 07:33:34 -0500
commit7f751bec39459c9799f0d0553589e6193c1c00a4 (patch)
treebe4a5e60496cf2699275d44e7db580d115ca51c8 /pyflakes/checker.py
parent93aa3c435505b8541b151c3e4b24c0ec4333f0bb (diff)
downloadpyflakes-7f751bec39459c9799f0d0553589e6193c1c00a4.tar.gz
Check for non-ast SyntaxErrorsnonast
This includes return and yield outside of a function and break and continue outside of a loop. Fixes lp 1293654. The problem is that these SyntaxErrors are not encoded in the ast grammar, so they are not detected when just compiling to ast. You must compile down to bytecode to catch them. The advantage here is that we can still check for other kinds of errors in this case, because the ast is still valid.
Diffstat (limited to 'pyflakes/checker.py')
-rw-r--r--pyflakes/checker.py65
1 files changed, 61 insertions, 4 deletions
diff --git a/pyflakes/checker.py b/pyflakes/checker.py
index 7a51328..3b35ca6 100644
--- a/pyflakes/checker.py
+++ b/pyflakes/checker.py
@@ -658,11 +658,11 @@ class Checker(object):
ASYNCWITH = ASYNCWITHITEM = RAISE = TRYFINALLY = ASSERT = EXEC = \
EXPR = ASSIGN = handleChildren
- CONTINUE = BREAK = PASS = ignore
+ PASS = ignore
# "expr" type nodes
BOOLOP = BINOP = UNARYOP = IFEXP = DICT = SET = \
- COMPARE = CALL = REPR = ATTRIBUTE = SUBSCRIPT = LIST = TUPLE = \
+ COMPARE = CALL = REPR = ATTRIBUTE = SUBSCRIPT = \
STARRED = NAMECONSTANT = handleChildren
NUM = STR = BYTES = ELLIPSIS = ignore
@@ -748,8 +748,33 @@ class Checker(object):
# arguments, but these aren't dispatched through here
raise RuntimeError("Got impossible expression context: %r" % (node.ctx,))
+ def CONTINUE(self, node):
+ # Walk the tree up until we see a loop (OK), a function or class
+ # definition (not OK), for 'continue', a finally block (not OK), or
+ # the top module scope (not OK)
+ n = node
+ while hasattr(n, 'parent'):
+ n, n_child = n.parent, n
+ if isinstance(n, (ast.While, ast.For)):
+ # Doesn't apply unless it's in the loop itself
+ if n_child not in n.orelse:
+ return
+ if isinstance(n, (ast.FunctionDef, ast.ClassDef)):
+ break
+ # Handle Try/TryFinally difference in Python < and >= 3.3
+ if hasattr(n, 'finalbody') and isinstance(node, ast.Continue):
+ if n_child in n.finalbody:
+ self.report(messages.ContinueInFinally, node)
+ return
+ if isinstance(node, ast.Continue):
+ self.report(messages.ContinueOutsideLoop, node)
+ else: # ast.Break
+ self.report(messages.BreakOutsideLoop, node)
+
+ BREAK = CONTINUE
+
def RETURN(self, node):
- if isinstance(self.scope, ClassScope):
+ if isinstance(self.scope, (ClassScope, ModuleScope)):
self.report(messages.ReturnOutsideFunction, node)
return
@@ -762,6 +787,10 @@ class Checker(object):
self.handleNode(node.value, node)
def YIELD(self, node):
+ if isinstance(self.scope, (ClassScope, ModuleScope)):
+ self.report(messages.YieldOutsideFunction, node)
+ return
+
self.scope.isGenerator = True
self.handleNode(node.value, node)
@@ -886,6 +915,31 @@ class Checker(object):
self.handleNode(node.value, node)
self.handleNode(node.target, node)
+ def TUPLE(self, node):
+ if not PY2 and isinstance(node.ctx, ast.Store):
+ # Python 3 advanced tuple unpacking: a, *b, c = d.
+ # Only one starred expression is allowed, and no more than 1<<8
+ # assignments are allowed before a stared expression. There is
+ # also a limit of 1<<24 expressions after the starred expression,
+ # which is impossible to test due to memory restrictions, but we
+ # add it here anyway
+ has_starred = False
+ star_loc = -1
+ for i, n in enumerate(node.elts):
+ if isinstance(n, ast.Starred):
+ if has_starred:
+ self.report(messages.TwoStarredExpressions, node)
+ # The SyntaxError doesn't distinguish two from more
+ # than two.
+ break
+ has_starred = True
+ star_loc = i
+ if star_loc >= 1 << 8 or len(node.elts) - star_loc - 1 >= 1 << 24:
+ self.report(messages.TooManyExpressionsInStarredAssignment, node)
+ self.handleChildren(node)
+
+ LIST = TUPLE
+
def IMPORT(self, node):
for alias in node.names:
name = alias.asname or alias.name
@@ -914,12 +968,15 @@ class Checker(object):
def TRY(self, node):
handler_names = []
# List the exception handlers
- for handler in node.handlers:
+ for i, handler in enumerate(node.handlers):
if isinstance(handler.type, ast.Tuple):
for exc_type in handler.type.elts:
handler_names.append(getNodeName(exc_type))
elif handler.type:
handler_names.append(getNodeName(handler.type))
+
+ if handler.type is None and i < len(node.handlers) - 1:
+ self.report(messages.DefaultExceptNotLast, handler)
# Memorize the except handlers and process the body
self.exceptHandlers.append(handler_names)
for child in node.body: