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

Limit differentForks to the current scope #342

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
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,7 +495,8 @@ def __init__(self, tree, filename='(none)', builtins=None,
if builtins:
self.builtIns = self.builtIns.union(builtins)
self.withDoctest = withDoctest
self.scopeStack = [ModuleScope()]
tree.depth = self.nodeDepth
self.scopeStack = [ModuleScope(tree)]
self.exceptHandlers = [()]
self.root = tree
self.handleChildren(tree)
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