Skip to content

Fix false positive on attribute access for TypeVar bounded by type#2672

Closed
yangdanny97 wants to merge 1 commit intofacebook:mainfrom
yangdanny97:export-D95419868
Closed

Fix false positive on attribute access for TypeVar bounded by type#2672
yangdanny97 wants to merge 1 commit intofacebook:mainfrom
yangdanny97:export-D95419868

Conversation

@yangdanny97
Copy link
Contributor

Summary:
When a TypeVar has bound=type, pyrefly internally represents the bound as
type[Any], which resolves to AttributeBase1::TypeAny during attribute
lookup. The quantified TypeVar attribute resolution only handled
AttributeBase1::ClassInstance, causing any other variant (including TypeAny)
to fall back to object. This produced false positives like
"Object of class object has no attribute __name__" when accessing
attributes that exist on the type class.

The fix extracts the bound-to-ClassType mapping into a quantified_bound_class
helper that handles both ClassInstance (the common case) and TypeAny
(the bound=type case, mapping to builtins.type). This is applied to all
four sites where quantified bounds are resolved: both T and type[T] paths,
for both Bound and Constraints restrictions.

Fixes #2614

Differential Revision: D95419868

Summary:
When a TypeVar has `bound=type`, pyrefly internally represents the bound as
`type[Any]`, which resolves to `AttributeBase1::TypeAny` during attribute
lookup. The quantified TypeVar attribute resolution only handled
`AttributeBase1::ClassInstance`, causing any other variant (including `TypeAny`)
to fall back to `object`. This produced false positives like
"Object of class `object` has no attribute `__name__`" when accessing
attributes that exist on the `type` class.

The fix extracts the bound-to-ClassType mapping into a `quantified_bound_class`
helper that handles both `ClassInstance` (the common case) and `TypeAny`
(the `bound=type` case, mapping to `builtins.type`). This is applied to all
four sites where quantified bounds are resolved: both `T` and `type[T]` paths,
for both `Bound` and `Constraints` restrictions.

Fixes facebook#2614

Differential Revision: D95419868
@meta-cla meta-cla bot added the cla signed label Mar 5, 2026
@meta-codesync
Copy link

meta-codesync bot commented Mar 5, 2026

@yangdanny97 has exported this pull request. If you are a Meta employee, you can view the originating Diff in D95419868.

@github-actions
Copy link

github-actions bot commented Mar 5, 2026

Diff from mypy_primer, showing the effect of this PR on open source code:

kornia (https://github.com/kornia/kornia)
- ERROR kornia/core/check.py:309:37-49: Object of class `object` has no attribute `__name__` [missing-attribute]
- ERROR kornia/core/check.py:309:96-106: Object of class `object` has no attribute `__name__` [missing-attribute]

pydantic (https://github.com/pydantic/pydantic)
- ERROR pydantic/config.py:1290:9-35: Object of class `object` has no attribute `__pydantic_config__` [missing-attribute]
+ ERROR pydantic/config.py:1290:9-35: Object of class `type` has no attribute `__pydantic_config__` [missing-attribute]

strawberry (https://github.com/strawberry-graphql/strawberry)
- ERROR strawberry/types/object_type.py:133:34-46: Object of class `object` has no attribute `__name__` [missing-attribute]

prefect (https://github.com/PrefectHQ/prefect)
- ERROR src/prefect/utilities/dispatch.py:40:25-32: Object of class `object` has no attribute `mro` [missing-attribute]
- ERROR src/prefect/utilities/dispatch.py:129:9-48: Object of class `object` has no attribute `__pydantic_init_subclass_original__` [missing-attribute]
+ ERROR src/prefect/utilities/dispatch.py:129:9-48: Object of class `type` has no attribute `__pydantic_init_subclass_original__` [missing-attribute]
- ERROR src/prefect/utilities/dispatch.py:132:9-39: Object of class `object` has no attribute `__pydantic_init_subclass__` [missing-attribute]
+ ERROR src/prefect/utilities/dispatch.py:132:9-39: Object of class `type` has no attribute `__pydantic_init_subclass__` [missing-attribute]
- ERROR src/prefect/utilities/dispatch.py:134:9-39: Object of class `object` has no attribute `__init_subclass_original__` [missing-attribute]
+ ERROR src/prefect/utilities/dispatch.py:134:9-39: Object of class `type` has no attribute `__init_subclass_original__` [missing-attribute]
- ERROR src/prefect/utilities/dispatch.py:163:45-52: Object of class `object` has no attribute `mro` [missing-attribute]

@github-actions
Copy link

github-actions bot commented Mar 5, 2026

Primer Diff Classification

✅ 3 improvement(s) | ➖ 1 neutral | 4 project(s) total

3 improvement(s) across kornia, strawberry, prefect.

Project Verdict Changes Error Kinds Root Cause
kornia ✅ Improvement -2 missing-attribute quantified_bound_class()
pydantic ➖ Neutral +1, -1 missing-attribute
strawberry ✅ Improvement -1 missing-attribute quantified_bound_class()
prefect ✅ Improvement +3, -5 missing-attribute quantified_bound_class()
Detailed analysis

✅ Improvement (3)

kornia (-2)

The removed errors were false positives caused by a type inference bug in pyrefly. Looking at line 269, T = TypeVar('T', bound=type) creates a TypeVar bounded by the type class. In the KORNIA_CHECK_TYPE function on line 309, when typ has type T, pyrefly should understand that typ has all attributes of type, including __name__. However, pyrefly was incorrectly resolving TypeVars with bound=type to object instead of type, leading to false missing-attribute errors. The PR description confirms this was a known issue (#2614) where pyrefly's internal representation of bound=type as type[Any] wasn't being handled properly in attribute lookup, causing fallback to object. The fix correctly maps TypeAny variants to builtins.type during quantified bound resolution.
Attribution: The fix in quantified_bound_class() in pyrefly/lib/alt/attr.rs now handles AttributeBase1::TypeAny (which represents type[Any] from bound=type) by mapping it to builtins.type, allowing proper attribute resolution. Previously, only AttributeBase1::ClassInstance was handled, causing TypeVars with bound=type to fall back to object.

strawberry (-1)

This is an improvement. The error was a false positive caused by pyrefly's attribute resolution failing for TypeVars with bound=type. Looking at line 27, we see T = TypeVar('T', bound=builtins.type), and line 133 accesses cls.__name__ where cls: T. Since T is bounded by type, and all type objects have a __name__ attribute, this access is completely valid. The PR description explains that pyrefly was incorrectly representing bound=type as type[Any] which resolved to AttributeBase1::TypeAny, but the quantified TypeVar resolution only handled AttributeBase1::ClassInstance, causing it to fall back to object and produce the false positive. The fix properly maps the TypeAny case to builtins.type so attribute lookup works correctly.
Attribution: The fix in quantified_bound_class() method in pyrefly/lib/alt/attr.rs now handles AttributeBase1::TypeAny (which represents type[Any] from bound=type) by mapping it to builtins.type, allowing proper attribute resolution instead of falling back to object.

prefect (+3, -5)

The removed errors were false positives where pyrefly incorrectly resolved TypeVar('T', bound=type[Any]) to object instead of type, causing it to claim that type-specific attributes like mro() and __init_subclass__ were missing. The PR fixed this by properly handling the bound=type case in attribute resolution. The new errors appear to be a different issue with dynamic attribute tracking, but the core fix (removing false positives about missing type attributes) represents an improvement in type checker accuracy.
Attribution: The change to quantified_bound_class() in pyrefly/lib/alt/attr.rs fixed the TypeVar bound resolution by handling AttributeBase1::TypeAny (which represents type[Any] from bound=type) and mapping it to builtins.type instead of falling back to object. This resolved the false positive missing-attribute errors on the object class.

➖ Neutral (1)

pydantic (+1, -1)

Same errors at same locations with same error kinds — message wording changed, no behavioral impact.


Was this helpful? React with 👍 or 👎

Classification by primer-classifier (1 heuristic, 3 LLM)

Copy link
Contributor

@grievejia grievejia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review automatically exported from Phabricator review in Meta.

@meta-codesync
Copy link

meta-codesync bot commented Mar 5, 2026

This pull request has been merged in e311fea.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Missing attribute false positive from TypeVar bounded with type

3 participants