diff --git a/mypy/checker.py b/mypy/checker.py index 33ed5387554d..d4ba6c54fb35 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -6124,6 +6124,12 @@ def _get_recursive_sub_patterns_map( sub_patterns_map: dict[Expression, Type] = {} typ_ = get_proper_type(typ) if isinstance(expr, TupleExpr) and isinstance(typ_, TupleType): + if any(isinstance(item, StarExpr) for item in expr.items): + # For a starred item (e.g. `*bar`), there's no one-to-one correspondence + # between expr.items and typ_.items, we are returning early to avoid an + # assertion crash. See: https://github.com/python/mypy/issues/21468. + return sub_patterns_map + # When matching a tuple expression with a sequence pattern, narrow individual tuple items assert len(expr.items) == len(typ_.items) for item_expr, item_typ in zip(expr.items, typ_.items): diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index 01e490d0da50..33b952f48f49 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -509,6 +509,20 @@ match m, (n, o): pass # E: Statement is unreachable [builtins fixtures/tuple.pyi] +[case testMatchUnpackedTupleSubjectExpressionNoCrash] +# flags: --strict-equality --warn-unreachable +# See: https://github.com/python/mypy/issues/21468 + +foo: int = 1 +bar: tuple[int, int] = 1, 2 + +match foo, *bar: + case x, y, z: + reveal_type(x) # N: Revealed type is "builtins.int" + reveal_type(y) # N: Revealed type is "builtins.int" + reveal_type(z) # N: Revealed type is "builtins.int" +[builtins fixtures/tuple.pyi] + -- Mapping Pattern -- [case testMatchMappingPatternCaptures]