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

[mypyc] Emit errors for unsupported class defs (and don't crash) #15726

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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
2 changes: 2 additions & 0 deletions mypyc/irbuild/classdef.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ def transform_class_def(builder: IRBuilder, cdef: ClassDef) -> None:
elif isinstance(stmt, ExpressionStmt) and isinstance(stmt.expr, StrExpr):
# Docstring. Ignore
pass
elif isinstance(stmt, ClassDef):
builder.error("Nested classes are not supported", stmt.line)
else:
builder.error("Unsupported statement in class body", stmt.line)

Expand Down
19 changes: 17 additions & 2 deletions mypyc/irbuild/visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from __future__ import annotations

from contextlib import contextmanager
from typing import NoReturn

from mypy.nodes import (
Expand Down Expand Up @@ -159,11 +160,15 @@ class IRBuilderVisitor(IRVisitor):
# state and many helpers. The attribute is initialized outside
# this class since this class and IRBuilder form a reference loop.
builder: IRBuilder
# If set, raise an error when a class definition is visited.
_error_message_on_class_def: str = ""

def visit_mypy_file(self, mypyfile: MypyFile) -> None:
assert False, "use transform_mypy_file instead"

def visit_class_def(self, cdef: ClassDef) -> None:
if self._error_message_on_class_def:
self.bail(self._error_message_on_class_def, cdef.line)
transform_class_def(self.builder, cdef)

def visit_import(self, node: Import) -> None:
Expand All @@ -176,7 +181,8 @@ def visit_import_all(self, node: ImportAll) -> None:
transform_import_all(self.builder, node)

def visit_func_def(self, fdef: FuncDef) -> None:
transform_func_def(self.builder, fdef)
with self.error_on_class_def("Class definitions within a function are not supported"):
transform_func_def(self.builder, fdef)

def visit_overloaded_func_def(self, o: OverloadedFuncDef) -> None:
transform_overloaded_func_def(self.builder, o)
Expand All @@ -203,7 +209,8 @@ def visit_operator_assignment_stmt(self, stmt: OperatorAssignmentStmt) -> None:
transform_operator_assignment_stmt(self.builder, stmt)

def visit_if_stmt(self, stmt: IfStmt) -> None:
transform_if_stmt(self.builder, stmt)
with self.error_on_class_def("Conditional class definitions are not supported"):
transform_if_stmt(self.builder, stmt)

def visit_while_stmt(self, stmt: WhileStmt) -> None:
transform_while_stmt(self.builder, stmt)
Expand Down Expand Up @@ -399,3 +406,11 @@ def bail(self, msg: str, line: int) -> NoReturn:
"""
self.builder.error(msg, line)
raise UnsupportedException()

@contextmanager
def error_on_class_def(self, error: str) -> NoReturn:
self._error_message_on_class_def = error
try:
yield
finally:
self._error_message_on_class_def = ""
12 changes: 12 additions & 0 deletions mypyc/test-data/commandline.test
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,18 @@ class NonExt2:
def test(self, x: int) -> None:
pass

if True:
class ConditionalClass: # E: Conditional class definitions are not supported
pass

class Outer:
class Inner: # E: Nested classes are not supported
pass

def make_class() -> None:
class LocalClass: # E: Class definitions within a function are not supported
pass

iterator_warning = (i+1 for i in range(10)) # W: Treating generator comprehension as list

# But we don't want warnings for these cases:
Expand Down
Loading