Skip to content

Commit

Permalink
Limit differentForks to the current scope
Browse files Browse the repository at this point in the history
Alternate if & try forks are always in the same scope.

Rearranging the code a little allows for differentForks
to only be called on redefinitions within the same scope,
and inside differentForks the use of getCommonAncestor
can be limited to finding ancestors only when in current
scope.
  • Loading branch information
jayvdb committed Jul 16, 2018
1 parent 4b2d720 commit 123be91
Showing 1 changed file with 26 additions and 13 deletions.
39 changes: 26 additions & 13 deletions pyflakes/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,9 +394,13 @@ def __init__(self, name, source, scope):
class Scope(dict):
importStarred = False # set to True when import * is found

def __init__(self, node):
self.node = node

def __repr__(self):
scope_cls = self.__class__.__name__
return '<%s at 0x%x %s>' % (scope_cls, id(self), dict.__repr__(self))
return '<%s at 0x%x for %r containing: %s>' % (
scope_cls, id(self), self.node, dict.__repr__(self))


class ClassScope(Scope):
Expand All @@ -413,8 +417,8 @@ class FunctionScope(Scope):
alwaysUsed = {'__tracebackhide__', '__traceback_info__',
'__traceback_supplement__'}

def __init__(self):
super(FunctionScope, self).__init__()
def __init__(self, node):
super(FunctionScope, self).__init__(node)
# Simplify: manage the special locals as globals
self.globals = self.alwaysUsed.copy()
self.returnValue = None # First non-empty return
Expand Down Expand Up @@ -491,8 +495,9 @@ def __init__(self, tree, filename='(none)', builtins=None,
if builtins:
self.builtIns = self.builtIns.union(builtins)
self.withDoctest = withDoctest
self.scopeStack = [ModuleScope()]
self.scopeStack = [ModuleScope(tree)]
self.exceptHandlers = [()]
tree.depth = self.nodeDepth
self.root = tree
self.handleChildren(tree)
self.runDeferred(self._deferredFunctions)
Expand Down Expand Up @@ -609,8 +614,8 @@ def checkDeadScopes(self):
messg = messages.RedefinedWhileUnused
self.report(messg, node, value.name, value.source)

def pushScope(self, scopeClass=FunctionScope):
self.scopeStack.append(scopeClass())
def pushScope(self, scopeClass=FunctionScope, node=None):
self.scopeStack.append(scopeClass(node))

def report(self, messageClass, *args, **kwargs):
self.messages.append(messageClass(self.filename, *args, **kwargs))
Expand All @@ -629,6 +634,9 @@ def getCommonAncestor(self, lnode, rnode, stop):
if lnode is rnode:
return lnode

if stop.depth in (lnode.depth, rnode.depth):
return None

if (lnode.depth > rnode.depth):
return self.getCommonAncestor(lnode.parent, rnode, stop)
if (lnode.depth < rnode.depth):
Expand All @@ -643,7 +651,8 @@ def descendantOf(self, node, ancestors, stop):

def differentForks(self, lnode, rnode):
"""True, if lnode and rnode are located on different forks of IF/TRY"""
ancestor = self.getCommonAncestor(lnode, rnode, self.root)
ancestor = self.getCommonAncestor(lnode, rnode,
self.scope.node or self.root)
parts = getAlternatives(ancestor)
if parts:
for items in parts:
Expand All @@ -665,15 +674,19 @@ def addBinding(self, node, value):
break
existing = scope.get(value.name)

if existing and not self.differentForks(node, existing.source):
if existing:

parent_stmt = self.getParent(value.source)
if isinstance(existing, Importation) and isinstance(parent_stmt, ast.For):
self.report(messages.ImportShadowedByLoopVar,
node, value.name, existing.source)

elif scope is self.scope:
if (isinstance(parent_stmt, ast.comprehension) and
if self.differentForks(node, existing.source):
# ignore redefinitions in different forks of `if` & `try`
pass

elif (isinstance(parent_stmt, ast.comprehension) and
not isinstance(self.getParent(existing.source),
(ast.For, ast.comprehension))):
self.report(messages.RedefinedInListComp,
Expand Down Expand Up @@ -903,7 +916,7 @@ def handleDoctests(self, node):
saved_stack = self.scopeStack
self.scopeStack = [self.scopeStack[0]]
node_offset = self.offset or (0, 0)
self.pushScope(DoctestScope)
self.pushScope(DoctestScope, node)
underscore_in_builtins = '_' in self.builtIns
if not underscore_in_builtins:
self.builtIns.add('_')
Expand Down Expand Up @@ -1079,7 +1092,7 @@ def GLOBAL(self, node):
NONLOCAL = GLOBAL

def GENERATOREXP(self, node):
self.pushScope(GeneratorScope)
self.pushScope(GeneratorScope, node)
self.handleChildren(node)
self.popScope()

Expand Down Expand Up @@ -1219,7 +1232,7 @@ def addArgs(arglist):

def runFunction():

self.pushScope()
self.pushScope(FunctionScope, node)
for name in args:
self.addBinding(node, Argument(name, node))
if isinstance(node.body, list):
Expand Down Expand Up @@ -1265,7 +1278,7 @@ def CLASSDEF(self, node):
if not PY2:
for keywordNode in node.keywords:
self.handleNode(keywordNode, node)
self.pushScope(ClassScope)
self.pushScope(ClassScope, node)
# doctest does not process doctest within a doctest
# classes within classes are processed.
if (self.withDoctest and
Expand Down

0 comments on commit 123be91

Please sign in to comment.