Skip to content

Commit 123be91

Browse files
committed
Limit differentForks to the current scope
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.
1 parent 4b2d720 commit 123be91

File tree

1 file changed

+26
-13
lines changed

1 file changed

+26
-13
lines changed

pyflakes/checker.py

+26-13
Original file line numberDiff line numberDiff line change
@@ -394,9 +394,13 @@ def __init__(self, name, source, scope):
394394
class Scope(dict):
395395
importStarred = False # set to True when import * is found
396396

397+
def __init__(self, node):
398+
self.node = node
399+
397400
def __repr__(self):
398401
scope_cls = self.__class__.__name__
399-
return '<%s at 0x%x %s>' % (scope_cls, id(self), dict.__repr__(self))
402+
return '<%s at 0x%x for %r containing: %s>' % (
403+
scope_cls, id(self), self.node, dict.__repr__(self))
400404

401405

402406
class ClassScope(Scope):
@@ -413,8 +417,8 @@ class FunctionScope(Scope):
413417
alwaysUsed = {'__tracebackhide__', '__traceback_info__',
414418
'__traceback_supplement__'}
415419

416-
def __init__(self):
417-
super(FunctionScope, self).__init__()
420+
def __init__(self, node):
421+
super(FunctionScope, self).__init__(node)
418422
# Simplify: manage the special locals as globals
419423
self.globals = self.alwaysUsed.copy()
420424
self.returnValue = None # First non-empty return
@@ -491,8 +495,9 @@ def __init__(self, tree, filename='(none)', builtins=None,
491495
if builtins:
492496
self.builtIns = self.builtIns.union(builtins)
493497
self.withDoctest = withDoctest
494-
self.scopeStack = [ModuleScope()]
498+
self.scopeStack = [ModuleScope(tree)]
495499
self.exceptHandlers = [()]
500+
tree.depth = self.nodeDepth
496501
self.root = tree
497502
self.handleChildren(tree)
498503
self.runDeferred(self._deferredFunctions)
@@ -609,8 +614,8 @@ def checkDeadScopes(self):
609614
messg = messages.RedefinedWhileUnused
610615
self.report(messg, node, value.name, value.source)
611616

612-
def pushScope(self, scopeClass=FunctionScope):
613-
self.scopeStack.append(scopeClass())
617+
def pushScope(self, scopeClass=FunctionScope, node=None):
618+
self.scopeStack.append(scopeClass(node))
614619

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

637+
if stop.depth in (lnode.depth, rnode.depth):
638+
return None
639+
632640
if (lnode.depth > rnode.depth):
633641
return self.getCommonAncestor(lnode.parent, rnode, stop)
634642
if (lnode.depth < rnode.depth):
@@ -643,7 +651,8 @@ def descendantOf(self, node, ancestors, stop):
643651

644652
def differentForks(self, lnode, rnode):
645653
"""True, if lnode and rnode are located on different forks of IF/TRY"""
646-
ancestor = self.getCommonAncestor(lnode, rnode, self.root)
654+
ancestor = self.getCommonAncestor(lnode, rnode,
655+
self.scope.node or self.root)
647656
parts = getAlternatives(ancestor)
648657
if parts:
649658
for items in parts:
@@ -665,15 +674,19 @@ def addBinding(self, node, value):
665674
break
666675
existing = scope.get(value.name)
667676

668-
if existing and not self.differentForks(node, existing.source):
677+
if existing:
669678

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

675684
elif scope is self.scope:
676-
if (isinstance(parent_stmt, ast.comprehension) and
685+
if self.differentForks(node, existing.source):
686+
# ignore redefinitions in different forks of `if` & `try`
687+
pass
688+
689+
elif (isinstance(parent_stmt, ast.comprehension) and
677690
not isinstance(self.getParent(existing.source),
678691
(ast.For, ast.comprehension))):
679692
self.report(messages.RedefinedInListComp,
@@ -903,7 +916,7 @@ def handleDoctests(self, node):
903916
saved_stack = self.scopeStack
904917
self.scopeStack = [self.scopeStack[0]]
905918
node_offset = self.offset or (0, 0)
906-
self.pushScope(DoctestScope)
919+
self.pushScope(DoctestScope, node)
907920
underscore_in_builtins = '_' in self.builtIns
908921
if not underscore_in_builtins:
909922
self.builtIns.add('_')
@@ -1079,7 +1092,7 @@ def GLOBAL(self, node):
10791092
NONLOCAL = GLOBAL
10801093

10811094
def GENERATOREXP(self, node):
1082-
self.pushScope(GeneratorScope)
1095+
self.pushScope(GeneratorScope, node)
10831096
self.handleChildren(node)
10841097
self.popScope()
10851098

@@ -1219,7 +1232,7 @@ def addArgs(arglist):
12191232

12201233
def runFunction():
12211234

1222-
self.pushScope()
1235+
self.pushScope(FunctionScope, node)
12231236
for name in args:
12241237
self.addBinding(node, Argument(name, node))
12251238
if isinstance(node.body, list):
@@ -1265,7 +1278,7 @@ def CLASSDEF(self, node):
12651278
if not PY2:
12661279
for keywordNode in node.keywords:
12671280
self.handleNode(keywordNode, node)
1268-
self.pushScope(ClassScope)
1281+
self.pushScope(ClassScope, node)
12691282
# doctest does not process doctest within a doctest
12701283
# classes within classes are processed.
12711284
if (self.withDoctest and

0 commit comments

Comments
 (0)