Skip to content

Report irrefutable match patterns that make later cases unreachable - #21945

Open
istoolsfox wants to merge 1 commit into
python:masterfrom
istoolsfox:fix/irrefutable-match-patterns
Open

Report irrefutable match patterns that make later cases unreachable#21945
istoolsfox wants to merge 1 commit into
python:masterfrom
istoolsfox:fix/irrefutable-match-patterns

Conversation

@istoolsfox

Copy link
Copy Markdown

Fixes #21925.

CPython rejects at compile time a match statement whose non-final case is an unguarded capture or wildcard pattern, or that contains an or-pattern with an irrefutable alternative in a non-final position (SyntaxError: name capture 'y' makes remaining patterns unreachable / wildcard makes remaining patterns unreachable), since such patterns make the remaining cases unreachable. mypy currently checks such files clean — a file that cannot even be imported passes type checking.

What

Mirror CPython's irrefutability check (PEP 634) in the match statement checker:

  • an unguarded capture or wildcard in a non-final case is reported (a guard or being the final case makes it fine);
  • an irrefutable alternative in a non-final position of an or-pattern is reported, at any nesting depth (including inside sequence, mapping and class patterns);
  • captures inside composite patterns ([x], Cls(x), {k: x}) stay allowed, matching CPython — only the positions above are inspected.

Differences from CPython:

  • mypy reports every occurrence, while CPython stops at the first SyntaxError;
  • this is a regular error (not a syntax error), so the file still type checks after the report.

Implementation notes

  • The check runs before type-dependent narrowing, purely on the pattern AST, and is skipped on deferred passes to avoid duplicate reports.
  • case y as z: is handled naturally: the parser represents y as an inner capture pattern, which the recursion inspects with the outer allow flag — matching CPython's own AST (MatchAs(pattern=MatchAs(name='y'), name='z')).
  • Added sub_patterns() to mypy/patterns.py for enumerating composite sub-patterns.

Verification

  • 10 new unit tests in check-python310.test covering the full rule matrix (capture/wildcard non-final, guard, final case, or-pattern positions, nested or-patterns, composite captures allowed, x as y, multiple violations).
  • Full check suite: 8214 passed, 15 skipped, 7 xfailed — no regressions.
  • mypy --config-file mypy_self_check.ini clean; ruff check and ruff format clean on touched files (modulo two pre-existing format drifts on master).

CPython rejects at compile time a match statement whose non-final case
is an unguarded capture or wildcard, or that contains an or-pattern
with an irrefutable alternative in a non-final position ('name capture
'y' makes remaining patterns unreachable'), since such patterns make
the remaining cases unreachable. mypy checked such files clean.

Mirror that check in the match statement checker: report captures and
wildcards in non-final cases (unless guarded) and in non-final or-pattern
alternatives at any nesting depth. Captures inside composite patterns
(sequence, mapping, class) stay allowed, matching CPython. Unlike
CPython, which stops at the first occurrence, all of them are reported.

Fixes python#21925
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Irrefutable pattern before the last case in a match statement passes type checking but is a SyntaxError at runtime

1 participant