Repository: python/mypy
Issue category: Bug Report

Issue:
Positional subpattern of a built-in type subclass with __match_args__ causes type confusion
Given

```python
class PrintableKey(bytes):
    __match_args__ = ('ch', )

    @property
    def ch(self) -> str:
        return self.decode('UTF-8')
```

now

```python
match event:
    case PrintableKey(ch):
        reveal_type(ch)  # mypy thinks it's PrintableKey
```

versus

```python
match event:
    case PrintableKey(ch=ch):
        reveal_type(ch)  # mypy correctly deduces it's str
```

_Originally posted by @mgedmin in https://github.com/python/mypy/issues/13804#issuecomment-2466177933_


Hints:

Patch:
diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py
index 6b4fa35f9c49..a7121712a6db 100644
--- a/mypy/checkpattern.py
+++ b/mypy/checkpattern.py
@@ -693,7 +693,9 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType:
 
     def should_self_match(self, typ: Type) -> bool:
         typ = get_proper_type(typ)
-        if isinstance(typ, Instance) and typ.type.is_named_tuple:
+        if isinstance(typ, Instance) and typ.type.get("__match_args__") is not None:
+            # Named tuples and other subtypes of builtins that define __match_args__
+            # should not self match.
             return False
         for other in self.self_match_types:
             if is_subtype(typ, other):


Test patch:
diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test
index 58b70d7b74d8..8187f27353a9 100644
--- a/test-data/unit/check-python310.test
+++ b/test-data/unit/check-python310.test
@@ -648,6 +648,25 @@ match m:
         reveal_type(m)  # N: Revealed type is "builtins.tuple[Any, ...]"
 [builtins fixtures/primitives.pyi]
 
+[case testMatchClassPatternCaptureSelfSubtype]
+class A(str):
+    pass
+
+class B(str):
+    __match_args__ = ("b",)
+    b: int
+
+def f1(x: A):
+    match x:
+        case A(a):
+            reveal_type(a)  # N: Revealed type is "__main__.A"
+
+def f2(x: B):
+    match x:
+        case B(b):
+            reveal_type(b)  # N: Revealed type is "builtins.int"
+[builtins fixtures/tuple.pyi]
+
 [case testMatchInvalidClassPattern]
 m: object