Repository navigation
fix invalid-inheritance on a call-expression base class even when the callable returns type[...] (mixin factory) #3931 - #3936
asukaminato0721 wants to merge 2 commits into
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
9f5eaac to
f67cf45
Compare
|
This pull request has been imported. If you are a Meta employee, you can view this in D118812281. (Because this pull request was imported automatically, there will not be any future comments.) |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
stroxler
left a comment
There was a problem hiding this comment.
This needs a few fixes I think before we can consider merging, based on AI review:
Call-base support unintentionally changes NewType validation
base_class_of also handles synthesized NewType bases, but those don’t receive the new call-validation expectation. Both solver paths discard call errors, so this now passes without diagnostics:
from typing import NewType
def factory(name: str) -> type[int]:
name.upper()
return int
N = NewType("N", factory(1))The baseline rejects it; the patched checker accepts it; Python raises AttributeError.
There’s a related hole in [class_metadata.rs:1825–1833](https://github.com/meta/pyrefly/blob/7f18c4ee15267a36f66e55f3c18ee1cbbead4780/pyrefly/lib/alt/class/class_metadata.rs#L1825-L1833): unlike the adjacent branch, Call doesn’t reject the value None before untyping. Consequently, NewType("N", returns_none()) is also newly accepted.
Suggested fix: keep this call support confined to ordinary class statements, preserving existing NewType validation. Add negative tests for both cases.
Call bases skip legacy TypeVar scoping checks
from typing import TypeVar
T = TypeVar("T")
S = TypeVar("S")
def ident(x: type[S]) -> type[S]: ...
class C(ident(list[T])): ...C isn’t made generic, yet the new branch retains the out-of-scope T: C()[0] reveals TypeVar[T]. The equivalent alias-base spelling correctly reports “Type variable T is not in scope.”
Suggested fix: run check_legacy_typevar_scoping on the inferred call-base type, with the same NewType exemption as the adjacent branch.
stroxler
left a comment
There was a problem hiding this comment.
I think this is mostly good. AI review flags a couple of concerns about NewType behavior though:
1. Calls returning None become valid NewType bases
from typing import NewType
def factory() -> None:
return None
N = NewType("N", factory()) # Incorrectly acceptedNewType synthesis also uses base_class_of, so this reaches the new Call branch. Unlike the neighboring BaseClassExpr branch, it lacks the is_new_type && Type::None guard. Untyping preserves None, and the parser mistakenly treats it as the valid class NoneType.
Fix: Preserve that guard for calls, or restrict the new call support to ordinary class inheritance.
2. Argument errors disappear inside NewType base calls
from typing import NewType
def factory(x: str) -> type[int]:
return int
N = NewType("N", factory(42)) # Incorrectly acceptedOrdinary class definitions now install a delayed TypeCheckBaseClassExpr check, but synthesized NewType definitions do not. Both new solver branches discard inference errors, leaving this argument mismatch with no reporting path.
Fix: Install the corresponding delayed check for synthesized call bases, or keep them outside this rollout.
|
Whoops, you got two copies of the review; too many PRs for me to get through I guess! The good news is, they agree: it seems like NewType behavior is the main thing to look over :) |
|
Diff from mypy_primer, showing the effect of this PR on open source code: ============================================================
SUMMARY
============================================================
Total: +35 new errors, -105 fixed errors
By preset: +8/-16 (default), +10/-16 (strict)
Projects with changes (5):
prefect: +0 -1
comtypes: +2 -2
steam.py: +2 -1
zope.interface: +28 -38
nox: +3 -63
============================================================
FULL DIFF DETAILS
------------------------------------------------------------
prefect (https://github.com/PrefectHQ/prefect)
- ERROR src/prefect/__init__.py:37:23-77: Invalid expression form for base class: `TypedDict("_FullRevisionId", {"full-revisionid": str})` [invalid-inheritance]
comtypes (https://github.com/enthought/comtypes)
- ERROR comtypes/test/test_compatibility_tlbparser_VT_I1_assignment.py:149:34-71: Invalid expression form for base class: `create_legacy_automatic_int8_output()` [invalid-inheritance]
+ ERROR comtypes/test/test_compatibility_tlbparser_VT_I1_assignment.py:149:11-33: Class `IByteCompatibilityTest` has unimplemented abstract members: `EchoUint8`, `EchoLpStr`, `ReadCharPointer`, `GetCharacterBytes` [implicit-abstract-class]
- ERROR comtypes/test/test_compatibility_tlbparser_VT_I1_assignment.py:154:18-55: The type of attribute `_IByteCompatibilityTest__com_EchoInt8` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
+ ERROR comtypes/test/test_compatibility_tlbparser_VT_I1_assignment.py:154:13-55: Object of class `IByteCompatibilityTest` has no attribute `_IByteCompatibilityTest__com_EchoInt8` [missing-attribute]
steam.py (https://github.com/Gobot1234/steam.py)
- ERROR steam/types/app.py:171:24-70: Invalid expression form for base class: `TypedDict("", {"class": list[dict[str, Any]]})` [invalid-inheritance]
+ ERROR steam/types/app.py:171:24-33: Expected a callable, got `type[TypedDict]` [not-callable]
+ ERROR steam/types/app.py:171:63-66: Explicit `Any` is not allowed [explicit-any]
zope.interface (https://github.com/zopefoundation/zope.interface)
- ERROR src/zope/interface/tests/test_adapter.py:977:23-45: Invalid expression form for base class: `self._getTargetClass()` [invalid-inheritance]
- ERROR src/zope/interface/tests/test_adapter.py:1364:23-45: Invalid expression form for base class: `self._getTargetClass()` [invalid-inheritance]
- ERROR src/zope/interface/tests/test_adapter.py:1588:23-45: Invalid expression form for base class: `self._getTargetClass()` [invalid-inheritance]
+ ERROR src/zope/interface/tests/test_adapter.py:1588:15-22: Field `changed` has inconsistent types inherited from multiple base classes [inconsistent-inheritance]
- ERROR src/zope/interface/tests/test_adapter.py:1602:23-45: Invalid expression form for base class: `self._getTargetClass()` [invalid-inheritance]
+ ERROR src/zope/interface/tests/test_adapter.py:1602:15-22: Field `changed` has inconsistent types inherited from multiple base classes [inconsistent-inheritance]
+ ERROR src/zope/interface/tests/test_adapter.py:1621:9-28: The type of this argument is partially unknown; it contains an implicit `Any` [partially-unknown-type]
- ERROR src/zope/interface/tests/test_adapter.py:1621:13-22: The type of attribute `_required` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
+ ERROR src/zope/interface/tests/test_adapter.py:1621:13-22: The type of attribute `_required` is partially unknown; it contains an implicit `Any` [partially-unknown-type]
+ ERROR src/zope/interface/tests/test_adapter.py:1622:9-28: The type of this argument is partially unknown; it contains an implicit `Any` [partially-unknown-type]
- ERROR src/zope/interface/tests/test_adapter.py:1622:13-22: The type of attribute `_required` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
+ ERROR src/zope/interface/tests/test_adapter.py:1622:13-22: The type of attribute `_required` is partially unknown; it contains an implicit `Any` [partially-unknown-type]
- ERROR src/zope/interface/tests/test_adapter.py:1624:30-43: The type of this argument is unknown [unknown-argument-type]
+ ERROR src/zope/interface/tests/test_adapter.py:1624:30-43: The type of this argument is partially unknown; it contains an implicit `Any` [partially-unknown-type]
- ERROR src/zope/interface/tests/test_adapter.py:1624:34-43: The type of attribute `_required` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
+ ERROR src/zope/interface/tests/test_adapter.py:1624:34-43: The type of attribute `_required` is partially unknown; it contains an implicit `Any` [partially-unknown-type]
- ERROR src/zope/interface/tests/test_declarations.py:1745:23-45: Invalid expression form for base class: `self._getTargetClass()` [invalid-inheritance]
- ERROR src/zope/interface/tests/test_interface.py:452:18-40: Invalid expression form for base class: `self._getTargetClass()` [invalid-inheritance]
+ ERROR src/zope/interface/tests/test_interface.py:452:18-40: Invalid base class: `Never` [invalid-inheritance]
+ ERROR src/zope/interface/tests/test_interface.py:452:18-40: Expected a type form, got instance of `Never` [not-a-type]
- ERROR src/zope/interface/tests/test_interface.py:1248:23-45: Invalid expression form for base class: `self._getTargetClass()` [invalid-inheritance]
- ERROR src/zope/interface/tests/test_interface.py:1307:32-54: Invalid expression form for base class: `self._getTargetClass()` [invalid-inheritance]
- ERROR src/zope/interface/tests/test_interface.py:1331:32-54: Invalid expression form for base class: `self._getTargetClass()` [invalid-inheritance]
- ERROR src/zope/interface/tests/test_registry.py:365:40-62: Invalid expression form for base class: `self._getTargetClass()` [invalid-inheritance]
+ ERROR src/zope/interface/tests/test_registry.py:368:17-32: Class member `CompThatChangesAfter1Reg.registerUtility` overrides parent class `Components` in an inconsistent manner [bad-override]
+ ERROR src/zope/interface/tests/test_registry.py:368:17-32: Class member `CompThatChangesAfter1Reg.registerUtility` overrides a member in a parent class but is missing an `@override` decorator [missing-override-decorator]
- ERROR src/zope/interface/tests/test_registry.py:370:25-40: The type of attribute `registerUtility` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
+ ERROR src/zope/interface/tests/test_registry.py:370:25-40: The type of attribute `registerUtility` is partially unknown; it contains an implicit `Any` [partially-unknown-type]
- ERROR src/zope/interface/tests/test_registry.py:372:26-48: Attribute `_utility_registrations` is implicitly defined by assignment in method `registerUtility`, which is not a constructor [implicitly-defined-attribute]
- ERROR src/zope/interface/tests/test_registry.py:372:51-374:22: This expression's type is partially unknown; it contains an implicit `Any`. Please provide an explicit type annotation. [partially-unknown-type]
+ ERROR src/zope/interface/tests/test_registry.py:370:41-46: The type of this argument is partially unknown; it contains an implicit `Any` [partially-unknown-type]
- ERROR src/zope/interface/tests/test_registry.py:379:30-62: The type of this argument is partially unknown; it contains an implicit `Any` [partially-unknown-type]
+ ERROR src/zope/interface/tests/test_registry.py:379:35-61: The type of this argument is partially unknown; it contains an implicit `Any` [partially-unknown-type]
- ERROR src/zope/interface/tests/test_registry.py:379:35-61: The type of this argument is unknown [unknown-argument-type]
- ERROR src/zope/interface/tests/test_registry.py:379:40-59: The type of attribute `registeredUtilities` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
+ ERROR src/zope/interface/tests/test_registry.py:379:40-59: The type of attribute `registeredUtilities` is partially unknown; it contains an implicit `Any` [partially-unknown-type]
- ERROR src/zope/interface/tests/test_registry.py:385:30-62: The type of this argument is partially unknown; it contains an implicit `Any` [partially-unknown-type]
+ ERROR src/zope/interface/tests/test_registry.py:385:35-61: The type of this argument is partially unknown; it contains an implicit `Any` [partially-unknown-type]
- ERROR src/zope/interface/tests/test_registry.py:385:35-61: The type of this argument is unknown [unknown-argument-type]
- ERROR src/zope/interface/tests/test_registry.py:385:40-59: The type of attribute `registeredUtilities` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
+ ERROR src/zope/interface/tests/test_registry.py:385:40-59: The type of attribute `registeredUtilities` is partially unknown; it contains an implicit `Any` [partially-unknown-type]
- ERROR src/zope/interface/tests/test_registry.py:390:40-62: Invalid expression form for base class: `self._getTargetClass()` [invalid-inheritance]
+ ERROR src/zope/interface/tests/test_registry.py:393:17-32: Class member `CompThatChangesAfter2Reg.registerUtility` overrides parent class `Components` in an inconsistent manner [bad-override]
+ ERROR src/zope/interface/tests/test_registry.py:393:17-32: Class member `CompThatChangesAfter2Reg.registerUtility` overrides a member in a parent class but is missing an `@override` decorator [missing-override-decorator]
- ERROR src/zope/interface/tests/test_registry.py:396:26-48: Attribute `_utility_registrations` is implicitly defined by assignment in method `registerUtility`, which is not a constructor [implicitly-defined-attribute]
- ERROR src/zope/interface/tests/test_registry.py:396:51-398:22: This expression's type is partially unknown; it contains an implicit `Any`. Please provide an explicit type annotation. [partially-unknown-type]
- ERROR src/zope/interface/tests/test_registry.py:400:25-40: The type of attribute `registerUtility` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
+ ERROR src/zope/interface/tests/test_registry.py:400:25-40: The type of attribute `registerUtility` is partially unknown; it contains an implicit `Any` [partially-unknown-type]
+ ERROR src/zope/interface/tests/test_registry.py:400:41-46: The type of this argument is partially unknown; it contains an implicit `Any` [partially-unknown-type]
- ERROR src/zope/interface/tests/test_registry.py:405:30-62: The type of this argument is partially unknown; it contains an implicit `Any` [partially-unknown-type]
+ ERROR src/zope/interface/tests/test_registry.py:405:35-61: The type of this argument is partially unknown; it contains an implicit `Any` [partially-unknown-type]
- ERROR src/zope/interface/tests/test_registry.py:405:35-61: The type of this argument is unknown [unknown-argument-type]
- ERROR src/zope/interface/tests/test_registry.py:405:40-59: The type of attribute `registeredUtilities` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
+ ERROR src/zope/interface/tests/test_registry.py:405:40-59: The type of attribute `registeredUtilities` is partially unknown; it contains an implicit `Any` [partially-unknown-type]
- ERROR src/zope/interface/tests/test_registry.py:411:30-62: The type of this argument is partially unknown; it contains an implicit `Any` [partially-unknown-type]
+ ERROR src/zope/interface/tests/test_registry.py:411:35-61: The type of this argument is partially unknown; it contains an implicit `Any` [partially-unknown-type]
- ERROR src/zope/interface/tests/test_registry.py:411:35-61: The type of this argument is unknown [unknown-argument-type]
- ERROR src/zope/interface/tests/test_registry.py:411:40-59: The type of attribute `registeredUtilities` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
+ ERROR src/zope/interface/tests/test_registry.py:411:40-59: The type of attribute `registeredUtilities` is partially unknown; it contains an implicit `Any` [partially-unknown-type]
- ERROR src/zope/interface/tests/test_registry.py:417:30-62: The type of this argument is partially unknown; it contains an implicit `Any` [partially-unknown-type]
+ ERROR src/zope/interface/tests/test_registry.py:417:35-61: The type of this argument is partially unknown; it contains an implicit `Any` [partially-unknown-type]
- ERROR src/zope/interface/tests/test_registry.py:417:35-61: The type of this argument is unknown [unknown-argument-type]
- ERROR src/zope/interface/tests/test_registry.py:417:40-59: The type of attribute `registeredUtilities` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
+ ERROR src/zope/interface/tests/test_registry.py:417:40-59: The type of attribute `registeredUtilities` is partially unknown; it contains an implicit `Any` [partially-unknown-type]
- ERROR src/zope/interface/tests/test_registry.py:2709:9-24: Class member `UnhashableComponentsTests._getTargetClass` overrides parent class `ComponentsTests` in an inconsistent manner [bad-override]
- ERROR src/zope/interface/tests/test_registry.py:2714:13-69: Invalid expression form for base class: `super(UnhashableComponentsTests, self)._getTargetClass()` [invalid-inheritance]
nox (https://github.com/wntrblm/nox)
- ERROR nox/_cli.py:317:24-28: The type of attribute `info` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/_cli.py:453:28-35: The type of attribute `warning` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/command.py:70:12-17: The type of attribute `error` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/command.py:169:16-20: The type of attribute `info` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/command.py:177:20-25: The type of attribute `error` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/command.py:185:20-27: The type of attribute `warning` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/command.py:206:20-25: The type of attribute `error` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/command.py:211:24-29: The type of attribute `error` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/command.py:220:16-21: The type of attribute `error` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
+ ERROR nox/logger.py:87:62-82: Unused `# type: ignore` comment [unused-type-ignore]
- ERROR nox/logger.py:89:17-29: The type of attribute `isEnabledFor` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/logger.py:90:18-22: The type of attribute `_log` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/logger.py:93:17-29: The type of attribute `isEnabledFor` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/logger.py:94:18-22: The type of attribute `_log` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/logger.py:97:17-29: The type of attribute `isEnabledFor` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/logger.py:98:18-22: The type of attribute `_log` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/sessions.py:113:16-23: The type of attribute `warning` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/sessions.py:123:20-27: The type of attribute `warning` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/sessions.py:125:20-25: The type of attribute `error` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/sessions.py:347:20-29: The type of attribute `exception` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/sessions.py:521:20-24: The type of attribute `info` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/sessions.py:797:20-27: The type of attribute `warning` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/sessions.py:806:24-28: The type of attribute `info` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/sessions.py:982:16-20: The type of attribute `info` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/sessions.py:986:16-23: The type of attribute `warning` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/sessions.py:990:16-21: The type of attribute `debug` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/sessions.py:1223:24-31: The type of attribute `warning` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/sessions.py:1255:20-25: The type of attribute `error` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/sessions.py:1259:20-29: The type of attribute `exception` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/sessions.py:1346:32-46: The type of `log_function` is unknown; it is inferred as an implicit `Any` [unknown-variable-type]
- ERROR nox/sessions.py:1346:39-46: The type of attribute `warning` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/sessions.py:1348:32-82: The type of `log_function` is unknown; it is inferred as an implicit `Any` [unknown-variable-type]
- ERROR nox/sessions.py:1348:39-44: The type of attribute `error` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/sessions.py:1348:78-82: The type of attribute `info` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/sessions.py:1349:9-30: Result of call expression is of type `Unknown | None` and is not used; assign to `_` if this is intentional [unused-call-result]
- ERROR nox/tasks.py:141:16-21: The type of attribute `error` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/tasks.py:144:16-21: The type of attribute `error` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/tasks.py:149:16-25: The type of attribute `exception` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/tasks.py:207:16-21: The type of attribute `error` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
+ ERROR nox/tasks.py:222:30-41: The type of this argument is an explicit `Any` [explicit-any-use]
- ERROR nox/tasks.py:221:24-29: The type of attribute `error` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/tasks.py:222:24-29: The type of attribute `error` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/tasks.py:234:20-25: The type of attribute `error` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/tasks.py:241:20-25: The type of attribute `error` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/tasks.py:249:20-25: The type of attribute `error` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/tasks.py:260:16-21: The type of attribute `error` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
+ ERROR nox/tasks.py:276:22-33: The type of this argument is an explicit `Any` [explicit-any-use]
- ERROR nox/tasks.py:275:16-21: The type of attribute `error` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/tasks.py:276:16-21: The type of attribute `error` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/tasks.py:350:16-21: The type of attribute `error` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/tasks.py:387:16-21: The type of attribute `error` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/tasks.py:392:16-21: The type of attribute `error` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/tasks.py:418:16-23: The type of attribute `warning` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/tasks.py:439:20-27: The type of attribute `warning` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/virtualenv.py:185:16-21: The type of attribute `debug` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/virtualenv.py:309:16-20: The type of attribute `info` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/virtualenv.py:325:12-16: The type of attribute `info` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/virtualenv.py:375:16-23: The type of attribute `warning` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/virtualenv.py:392:16-23: The type of attribute `warning` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/virtualenv.py:405:16-23: The type of attribute `warning` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/virtualenv.py:465:16-21: The type of attribute `debug` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/virtualenv.py:481:16-21: The type of attribute `debug` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/virtualenv.py:677:20-25: The type of attribute `debug` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/virtualenv.py:684:16-20: The type of attribute `info` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/virtualenv.py:1060:20-25: The type of attribute `debug` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access]
- ERROR nox/virtualenv.py:1096:16-20: The type of attribute `info` is unknown; it is inferred as an implicit `Any` [unknown-attribute-access] |
stroxler
left a comment
There was a problem hiding this comment.
Thanks for the changes!
I just realized this is another one I should have closed - it's attached to a stale imported diff, so I can't easily review and merge the PR as-is.
If you could close this, open a fresh PR, and assign to me then I think we can merge pretty quickly.
While you're at it, a fresh round of review caught one more issue: explicitly bound legacy TypeVars are incorrectly rejected
In [class_bases.rs:350–355](https://github.com/meta/pyrefly/blob/fc1eb4363f339f23000fc0fc712c56f45e9d8fa4/pyrefly/lib/alt/types/class_bases.rs#L350-L355), the new scoping check rejects this valid explicitly generic class:
from typing import Generic, TypeVar, assert_type
T = TypeVar("T")
S = TypeVar("S")
def ident(x: type[S]) -> type[S]: ...
class C(ident(list[T]), Generic[T]):
pass
assert_type(C[int]()[0], int)Confirmed on the updated implementation: it reports “Type variable T is not in scope,” then fails assert_type(Unknown, int). The equivalent direct base, class C(list[T], Generic[T]), passes.
Generic[T] legitimately binds T, but the call’s nested argument is bound without the class’s LegacyTParamCollector. The inferred base therefore retains a raw T, which the new check diagnoses and replaces with error Any.
Suggested fix: quantify references to class-bound legacy parameters before checking the inferred call-base type. Add this positive test alongside the existing genuinely-out-of-scope negative test.
This is a gap in the newly added support—not a regression of previously supported call syntax. I’d treat it as a small fix-forward rather than another broad author revision.
|
I'll leave this PR open so we don't lose track of it, because I think the code changes are almost ready to merge. Again - if you can close this PR, open a fresh one addressing the new feedback, and assign me then I think we're about ready to merge it |
Summary
Fixes #3931
Call-expression bases like class
C(factory("x"))are now represented as evaluable base-class expressions instead of being rejected syntactically.The solver now untypes the call result in base-class context, so
type[Mixin]becomes a valid Mixin base, and Any-returning factories are tolerated.Invalid functional namedtuple/NamedTuple bases keep their existing diagnostics.
Test Plan
add test