diff --git a/mypy/nativeparse.py b/mypy/nativeparse.py index d5af1c8c6ec8..d7e660b470ff 100644 --- a/mypy/nativeparse.py +++ b/mypy/nativeparse.py @@ -1579,7 +1579,14 @@ def read_expression(state: State, data: ReadBuffer) -> Expression: elif tag == nodes.ASSIGNMENT_EXPR: target = read_expression(state, data) value = read_expression(state, data) - assert isinstance(target, NameExpr), f"Expected NameExpr for target, got {type(target)}" + if not isinstance(target, NameExpr): + # The only valid target of ":=" is a plain name, but ruff's error + # recovery can produce other targets for invalid code such as + # "(f() := 1)". Ruff already reports a syntax error in this case, so + # here we just recover with a placeholder name instead of crashing. + placeholder = NameExpr("") + placeholder.set_line(target) + target = placeholder expr = AssignmentExpr(target, value) read_loc(data, expr) expect_end_tag(data) diff --git a/test-data/unit/native-parser.test b/test-data/unit/native-parser.test index abf653fc213c..5d78ad68545a 100644 --- a/test-data/unit/native-parser.test +++ b/test-data/unit/native-parser.test @@ -2572,6 +2572,16 @@ MypyFile:1( Block:3( PassStmt:3()))) +[case testSyntaxError10] +(f() := 1) +[out] +1:2: error: Assignment expression target must be an identifier +MypyFile:1( + ExpressionStmt:1( + AssignmentExpr:1( + NameExpr() + IntExpr(1)))) + [case testArgConstructorTooManyArgs] from typing import Callable from mypy_extensions import Arg