facebook / facebook/pyrefly

False positive on `__init_subclass__` keyword validation in diamond inheritance

Open
#4,264 2 comments 0 reactions 1 assignee Claimed by @NathanTempest View on GitHub
stale typechecking
Dominant language
Rust
Stars
7k
Forks
516
PR merge metrics
No merged PRs in 30d

Description

### Describe the Bug

Pyrefly reports a spurious "Missing argument" error on a **valid** class header
when `__init_subclass__` is overridden in a diamond hierarchy. The inherited
`__init_subclass__` is resolved by a depth-first walk over base classes, which
diverges from Python's actual C3 MRO.

**Steps to reproduce** :

```python
class D:
def __init_subclass__(cls, kw: str) -> None: ...
class B(D): ...
class C(D):
def __init_subclass__(cls, **kwargs) -> None: ...
class A(B, C): ...
```

**What I see:** an error on `class A(B, C)` on a missing required argument `kw`.

**Expected:** no error. At runtime `MRO(A) == [A, B, C, D]`, so
`C.__init_subclass__` (which accepts `**kwargs`) is what runs, and `class A(B, C)`
is valid. Pyrefly's base walk visits `B → D` first, finds `D`'s stricter
signature, and stops before reaching `C`, producing the false positive.

The linear (non-diamond) case is handled correctly; only diamonds where an
intermediate base overrides `__init_subclass__` more permissively than a deeper
shared base are affected.

---

_Note for maintainers:_ `find_inherited_init_subclass` in
`pyrefly/lib/alt/class/class_metadata.rs` walks bases pre-order DFS and takes the
first `__init_subclass__` found. It deliberately avoids the class's own MRO
(still being computed during metadata, unsafe on cyclic hierarchies), so a fix
should linearize from the bases' already-computed MROs rather than a raw
pre-order walk. Introduced alongside the `__init_subclass__` keyword validation.

### Sandbox Link

_No response_

### (Only applicable for extension issues) IDE Information

_No response_

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.