Don't report abstract instantiation when __new__ returns other classes - #11786
Shubham Padkonde (Shubham-Padkonde) wants to merge 2 commits into
Conversation
A class can define `__new__` so that calling it always produces some other concrete class -- `pathlib.Path` returning `PosixPath` or `WindowsPath` is the canonical example. pyright already evaluates the call to that return type, but the abstract check ran first and reported `reportAbstractUsage` for the class's own abstract members, so the pattern could not be used with an abstract base. Skip the diagnostic when the class's `__new__` is declared to return only instances of other classes that are themselves concrete. A `__new__` returning the class itself or `Self`, one whose returned class is also abstract, and overloaded or synthesized `__new__` methods all keep the existing behaviour. Fixes microsoft#11255 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Compared candidate Type checker benchmark🟢 No performance regressions detected. Regression threshold:
Pyright stats
|
|
🔒 Automated review in progress — Bill Schnurr (@bschnurr) is auto-reviewing this PR. |
| @@ -29465,6 +29471,46 @@ export function createTypeEvaluator( | |||
|
|
|||
| // Returns a list of unimplemented abstract symbols (methods or variables) for | |||
| // the specified class. | |||
There was a problem hiding this comment.
Info · Optional note
The comment describing getAbstractSymbols now precedes instantiatesOnlyOtherConcreteClasses, where it incorrectly describes the following function. Move it immediately above getAbstractSymbols.
[verified]
There was a problem hiding this comment.
Moved back in 2235d59 — the getAbstractSymbols comment now sits directly above that function again, and the new function carries its own comment.
|
Result: Verification detailsVerification: Isolated verification observed failures whose relationship to this PR could not be determined: Type evaluator tests; this review is not fully verified. Isolated verification observed failures that were not classified as caused by this PR: Dependency and test discovery. Summary: The targeted abstract-class checker tests passed: all 12 `AbstractClass` cases, including the new `AbstractClass12`, succeeded. The broader type-evaluator run exceeded the 300-second limit without producing a result. Coverage is missing for explicitly preserved cases such as `Self`, `Never`, non-class, and overloaded `__new__` return types, so confidence is limited. Test runs: 1 passed, 2 failed
❌
|
Bill Schnurr (bschnurr)
left a comment
There was a problem hiding this comment.
Approved via Review Center.
The new function was inserted between the getAbstractSymbols comment and the function it describes, so move the comment back. Cover the return types the check deliberately declines to treat as instantiating another class: Self, Never, a non-class return and an overloaded __new__. Self and Never still report, which is the point. The Any and overloaded cases report nothing, but they report nothing on main either, so the sample records that rather than claiming otherwise. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Both points addressed in The comment — you were exactly right: the new function landed between the The missing coverage — added all four cases. Two behave as the check intends, and two turned out differently from what I first assumed, so I checked them against
So I've written those two into the sample as not generating an error, with a comment saying the behavior predates this change, rather than asserting errors that never fire. Expected count moved 2 → 4. The sample still pins the fix: reverting On the run that timed out in your sandbox — I ran it here without the 300s cap: Also Disclosure: this change was written by Claude Code (Claude Opus 5) working as my agent, at my direction. The test output quoted above comes from real runs in my local environment; I am accountable for this PR. |
|
Compared candidate Type checker benchmark🟢 No performance regressions detected. Regression threshold:
Pyright stats
|
| # This does not generate an error either. An overloaded `__new__` is left | ||
| # to the normal abstract check, which already tolerates it. That behavior | ||
| # also predates this change. | ||
| OverloadedNew(1) |
There was a problem hiding this comment.
Info · Optional note
📍 packages/pyright-internal/src/tests/samples/abstractClass12.py:103
This characterization confirms that overloaded __new__ remains tolerated, while the PR description says it remains reported. Update the PR description to match the verified behavior.
[verified]
Fixes #11255
Problem
A class can define
__new__so that calling it always produces some other concrete class.pathlib.Pathis the canonical example — instantiating it yields aPosixPathor aWindowsPath.pyright already understands the return type: given
def __new__(cls) -> "Foo | Bar",Base()evaluates toFoo | Bar. But the abstract check invalidateCallForInstantiableClassruns before the constructor is evaluated, so ifBasehas abstract members the call is still reported:That makes the pattern unusable with an abstract base, even though no abstract instance is ever created.
Fix
Before reporting
reportAbstractUsage, check whether the class's__new__is declared to return only instances of other classes that are themselves concrete. Existing behaviour is kept everywhere else:__new__returning the class itself orSelf→ still reported;__new__(including the defaultobject.__new__signature) → still reported, sincegetBoundNewMethodskips theobjectbase class and the helper bails out on anything that is not a plain function;Neverand non-class return types → still reported.Test
abstractClass12.pycovers all three cases: aBasewhose__new__returnsConcrete1 | Concrete2(no error, andreveal_typeconfirms the call still evaluates to that union), a class whose__new__returns an abstract class, and one whose__new__returns itself — the last two still error, so the sample expects exactly 2 errors.On
mainthe sample produces 3 errors:With the change:
prettier --checkis clean.Disclosure: this change was written by Claude Code (Claude Opus 5) working as my agent, at my direction. The test output quoted above comes from real runs in my local environment; I am accountable for what is submitted here and will follow up on review feedback.
🤖 Generated with Claude Code