Skip to content

Commit 9c6545b

Browse files
committed
Add remaining-patterns-unreachable checker.
This will emit an error message when a name capture pattern is used in a match statement which would make the remaining patterns unreachable. This code is a SyntaxError at runtime. Closes #7128
1 parent a3e5bef commit 9c6545b

File tree

10 files changed

+125
-0
lines changed

10 files changed

+125
-0
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
red = 0
2+
green = 1
3+
blue = 2
4+
5+
6+
color = blue
7+
match color:
8+
case red: # [unreachable-match-patterns]
9+
print("I see red!")
10+
case green: # [unreachable-match-patterns]
11+
print("Grass is green")
12+
case blue:
13+
print("I'm feeling the blues :(")
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from enum import Enum
2+
class Color(Enum):
3+
RED = 0
4+
GREEN = 1
5+
BLUE = 2
6+
7+
8+
color = Color.BLUE
9+
match color:
10+
case Color.RED:
11+
print("I see red!")
12+
case Color.GREEN:
13+
print("Grass is green")
14+
case Color.BLUE:
15+
print("I'm feeling the blues :(")
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- `PEP 636 <https://peps.python.org/pep-0636/#matching-against-constants-and-enums/>`_

doc/user_guide/checkers/features.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1343,6 +1343,18 @@ Unnecessary Ellipsis checker Messages
13431343
preceding line or if there is a statement in the same scope.
13441344

13451345

1346+
Unreachable Match Patterns checker
1347+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1348+
1349+
Verbatim name of the checker is ``unreachable_match_patterns``.
1350+
1351+
Unreachable Match Patterns checker Messages
1352+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1353+
:unreachable-match-patterns (E5000): *The name capture `case %s` makes the remaining patterns unreachable. Use a dotted name(for example an enum) to fix this*
1354+
Emitted when a name capture pattern in a match statement is used and there
1355+
are case statements below it.
1356+
1357+
13461358
Unsupported Version checker
13471359
~~~~~~~~~~~~~~~~~~~~~~~~~~~
13481360

doc/user_guide/messages/messages_overview.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ All messages in the error category:
165165
error/unexpected-special-method-signature
166166
error/unhashable-member
167167
error/unpacking-non-sequence
168+
error/unreachable-match-patterns
168169
error/unrecognized-inline-option
169170
error/unrecognized-option
170171
error/unsubscriptable-object

doc/whatsnew/fragments/7128.new_check

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Add ``unreachable-match-patterns`` checker.
2+
This will emit an error message when a name capture pattern is used in a match statement which would make the remaining patterns unreachable.
3+
This code is a SyntaxError at runtime.
4+
5+
Closes #7128
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
2+
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
3+
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
4+
5+
"""Match statement checker for Python code."""
6+
7+
from __future__ import annotations
8+
9+
from typing import TYPE_CHECKING
10+
11+
from astroid import nodes
12+
13+
from pylint.checkers import BaseChecker
14+
from pylint.checkers.utils import only_required_for_messages
15+
from pylint.interfaces import HIGH
16+
17+
if TYPE_CHECKING:
18+
from pylint.lint import PyLinter
19+
20+
21+
class MatchStatementChecker(BaseChecker):
22+
name = "unreachable_match_patterns"
23+
msgs = {
24+
"E5000": (
25+
"The name capture `case %s` makes the remaining patterns unreachable. "
26+
"Use a dotted name(for example an enum) to fix this",
27+
"unreachable-match-patterns",
28+
"Emitted when a name capture pattern in a match statement is used "
29+
"and there are case statements below it.",
30+
)
31+
}
32+
33+
def open(self) -> None:
34+
py_version = self.linter.config.py_version
35+
self._py310_plus = py_version >= (3, 10)
36+
37+
@only_required_for_messages("unreachable-match-patterns")
38+
def visit_match(self, node: nodes.Match) -> None:
39+
"""Check if a name capture pattern prevents the other cases from being
40+
reached
41+
"""
42+
for idx, case in enumerate(node.cases):
43+
if (
44+
isinstance(case.pattern, nodes.MatchAs)
45+
and case.pattern.pattern is None
46+
and isinstance(case.pattern.name, nodes.AssignName)
47+
and idx < len(node.cases) - 1
48+
and self._py310_plus
49+
):
50+
self.add_message(
51+
"unreachable-match-patterns",
52+
node=case,
53+
args=case.pattern.name.name,
54+
confidence=HIGH,
55+
)
56+
57+
58+
def register(linter: PyLinter) -> None:
59+
linter.register_checker(MatchStatementChecker(linter))
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
"""Functional tests for the ``unreachable-match-patterns`` check"""
2+
3+
4+
a = 'a'
5+
b = 'b'
6+
s = 'a'
7+
8+
9+
match s:
10+
case a: # [unreachable-match-patterns]
11+
pass
12+
case b: # [unreachable-match-patterns]
13+
pass
14+
case s:
15+
pass
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[testoptions]
2+
min_pyver=3.10
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
unreachable-match-patterns:10:0:None:None::The name capture `case a` makes the remaining patterns unreachable. Use a dotted name(for example an enum) to fix this:HIGH
2+
unreachable-match-patterns:12:0:None:None::The name capture `case b` makes the remaining patterns unreachable. Use a dotted name(for example an enum) to fix this:HIGH

0 commit comments

Comments
 (0)