diff options
| author | Aaron Meurer <asmeurer@gmail.com> | 2015-11-13 07:31:40 -0500 |
|---|---|---|
| committer | Phil Frost <indigo@bitglue.com> | 2015-11-13 07:33:34 -0500 |
| commit | 7f751bec39459c9799f0d0553589e6193c1c00a4 (patch) | |
| tree | be4a5e60496cf2699275d44e7db580d115ca51c8 /pyflakes/checker.py | |
| parent | 93aa3c435505b8541b151c3e4b24c0ec4333f0bb (diff) | |
| download | pyflakes-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.py | 65 |
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: |
