diff --git a/mypy/semanal.py b/mypy/semanal.py index 15566c9396c6..382d3e650995 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2658,8 +2658,33 @@ def visit_import_all(self, i: ImportAll) -> None: def visit_assignment_expr(self, s: AssignmentExpr) -> None: s.value.accept(self) + if self.is_func_scope(): + if not self.check_valid_comprehension(s): + return self.analyze_lvalue(s.target, escape_comprehensions=True, has_explicit_value=True) + def check_valid_comprehension(self, s: AssignmentExpr) -> bool: + """Check that assignment expression is not nested within comprehension at class scope. + + class C: + [(j := i) for i in [1, 2, 3]] + is a syntax error that is not enforced by Python parser, but at later steps. + """ + for i, is_comprehension in enumerate(reversed(self.is_comprehension_stack)): + if not is_comprehension and i < len(self.locals) - 1: + if self.locals[-1 - i] is None: + self.fail( + "Assignment expression within a comprehension" + " cannot be used in a class body", + s, + code=codes.SYNTAX, + serious=True, + blocker=True, + ) + return False + break + return True + def visit_assignment_stmt(self, s: AssignmentStmt) -> None: self.statement = s diff --git a/test-data/unit/check-python38.test b/test-data/unit/check-python38.test index c8fb1eb5aac8..7e5e0f3cf185 100644 --- a/test-data/unit/check-python38.test +++ b/test-data/unit/check-python38.test @@ -734,3 +734,8 @@ class C(Generic[T]): [out] main:10: note: Revealed type is "builtins.int" main:10: note: Revealed type is "builtins.str" + +[case testNoCrashOnAssignmentExprClass] +class C: + [(j := i) for i in [1, 2, 3]] # E: Assignment expression within a comprehension cannot be used in a class body +[builtins fixtures/list.pyi] diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index d1a2469efa56..ed7349aaa296 100644 --- a/test-data/unit/check-statements.test +++ b/test-data/unit/check-statements.test @@ -2194,3 +2194,7 @@ class B: pass def foo(x: int) -> Union[Generator[A, None, None], Generator[B, None, None]]: yield x # E: Incompatible types in "yield" (actual type "int", expected type "Union[A, B]") + +[case testNoCrashOnStarRightHandSide] +x = *(1, 2, 3) # E: Can use starred expression only as assignment target +[builtins fixtures/tuple.pyi]