Skip to content

Commit

Permalink
B904: ensure the raise is in the same context with the except
Browse files Browse the repository at this point in the history
  • Loading branch information
isidentical committed Oct 5, 2021
1 parent 1fa4e6a commit ac1a65e
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 3 deletions.
40 changes: 37 additions & 3 deletions bugbear.py
Expand Up @@ -16,7 +16,19 @@
__version__ = "21.9.2"

LOG = logging.getLogger("flake8.bugbear")
CONTEXTFUL_NODES = (
ast.Module,
ast.ClassDef,
ast.AsyncFunctionDef,
ast.FunctionDef,
ast.Lambda,
ast.ListComp,
ast.SetComp,
ast.DictComp,
ast.GeneratorExp,
)

Context = namedtuple("Context", ["node", "stack"])

@attr.s(hash=False)
class BugBearChecker:
Expand Down Expand Up @@ -147,10 +159,11 @@ def _typesafe_issubclass(cls, class_or_tuple):
class BugBearVisitor(ast.NodeVisitor):
filename = attr.ib()
lines = attr.ib()
node_stack = attr.ib(default=attr.Factory(list))
contexts = attr.ib(default=attr.Factory(list))
node_window = attr.ib(default=attr.Factory(list))
errors = attr.ib(default=attr.Factory(list))
futures = attr.ib(default=attr.Factory(set))
context = attr.ib(default=attr.Factory(list))

NODE_WINDOW_SIZE = 4

Expand All @@ -161,13 +174,30 @@ def __getattr__(self, name):
print(name)
return self.__getattribute__(name)

@property
def node_stack(self):
if len(self.contexts) == 0:
return []

context, stack = self.contexts[-1]
return stack

def visit(self, node):
is_contextful = isinstance(node, CONTEXTFUL_NODES)

if is_contextful:
context = Context(node, [])
self.contexts.append(context)

self.node_stack.append(node)
self.node_window.append(node)
self.node_window = self.node_window[-self.NODE_WINDOW_SIZE :]
super().visit(node)
self.node_stack.pop()

if is_contextful:
self.contexts.pop()

def visit_ExceptHandler(self, node):
if node.type is None:
self.errors.append(
Expand Down Expand Up @@ -479,9 +509,13 @@ def check_for_b901(self, node):
break

def check_for_b902(self, node):
if not isinstance(self.node_stack[-2], ast.ClassDef):
if (
not len(self.contexts) >= 2
or not isinstance(self.contexts[-2].node, ast.ClassDef)
):
return

cls = self.contexts[-2].node
decorators = NameFinder()
decorators.visit(node.decorator_list)

Expand All @@ -490,7 +524,7 @@ def check_for_b902(self, node):
# `cls`?
return

bases = {b.id for b in self.node_stack[-2].bases if isinstance(b, ast.Name)}
bases = {b.id for b in cls.bases if isinstance(b, ast.Name)}
if "type" in bases:
if (
"classmethod" in decorators.names
Expand Down
7 changes: 7 additions & 0 deletions tests/b904.py
Expand Up @@ -19,3 +19,10 @@
raise base_err
finally:
raise Exception("Nothing to chain from, so no warning here")

try:
raise ValueError
except ValueError:
# should not emit, since we are not raising something
def proxy():
raise NameError

0 comments on commit ac1a65e

Please sign in to comment.