Skip to content

Avoid type-level DSL errors on invalid calls in return annotations - #5087

Closed
QEDady wants to merge 1 commit into
facebook:mainfrom
QEDady:fix-type-level-dsl-annotation-error
Closed

QEDady wants to merge 1 commit into
facebook:mainfrom
QEDady:fix-type-level-dsl-annotation-error

Conversation

@QEDady

@QEDady QEDady commented Oct 2, 2026

Copy link
Copy Markdown
Collaborator

Summary:

Python function calls are invalid type expressions in annotations (PEP 484), with native shape DSL calls as the exception in shape annotations. Previously, allows_type_level_dsl_call() returned true for all return annotations and any type arguments nested inside them. Consequently, any ordinary function call in a return annotation (e.g., def f() -> make_type(): ... or Union[bool, make_type()]) was passed to parse_type_level_dsl_call, producing a misleading error: Expected a type-level DSL function, got ....

This commit distinguishes shape DSL parameter contexts (ShapeTypeArgument) from ordinary generic type arguments, and checks whether the callee is an actual registered type-level DSL function before routing to the shape DSL parser. If not in a shape context and not a DSL function, it falls back to the standard "Function call cannot be used in annotations" error.

This shouldn't change type-checking behavior—only more fine-grained control for better error messages. I think that is a clearer error message, as users may be confused by the "DSL" error message, but feel free to not accept this if you disagree.

Example

from typing import Union

def make_type() -> type[int]: ...
def f() -> Union[bool, make_type()]: ...
  • Current behavior: Expected a type-level DSL function, got () -> type[int]
  • New behavior: Function call cannot be used in annotations

Test Plan

Added tests and run test.py

… annotations

Python function calls are invalid type expressions in annotations (PEP 484), with native shape DSL calls as the exception in shape annotations. Previously, `allows_type_level_dsl_call()` returned true for all return annotations and any type arguments nested inside them. Consequently, any ordinary function call in a return annotation (e.g., `def f() -> make_type(): ...` or `Union[bool, make_type()]`) was passed to `parse_type_level_dsl_call`, producing a misleading error: `Expected a type-level DSL function, got ...`.

This commit distinguishes shape DSL parameter contexts (`ShapeTypeArgument`) from ordinary generic type arguments, and checks whether the callee is an actual registered type-level DSL function before routing to the shape DSL parser. If not in a shape context and not a DSL function, it falls back to the standard "Function call cannot be used in annotations" error.

This shouldn't change type-checking behavior—only more fine-grained control for better error messages. I think that is a clearer error message, as users may be confused by the "DSL" thing, but feel free to not accept this if you disagree.

```python
from typing import Union

def make_type() -> type[int]: ...
def f() -> Union[bool, make_type()]: ...
```
- **Current behavior**: `Expected a type-level DSL function, got () -> type[int]`
- **New behavior**: `Function call cannot be used in annotations`

Added a test and run test.py
@meta-cla meta-cla Bot added the cla signed label Oct 2, 2026
@QEDady
QEDady requested a review from stroxler October 2, 2026 21:25
@github-actions github-actions Bot added google issues from google size/m labels Oct 2, 2026
@QEDady QEDady self-assigned this Oct 2, 2026
@meta-codesync

meta-codesync Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

This pull request has been imported. If you are a Meta employee, you can view this in D123101223. (Because this pull request was imported automatically, there will not be any future comments.)

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown

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

============================================================
SUMMARY
============================================================
Total: +3 new errors, -3 fixed errors
By preset: +3/-3 (default), +3/-3 (strict)

Projects with changes (1):
  spark: +3 -3
============================================================

FULL DIFF DETAILS
------------------------------------------------------------

spark (https://github.com/apache/spark)
- ERROR python/pyspark/pandas/tests/test_typedef.py:217:36-39: Expected a type-level DSL function, got `type[zip]` [invalid-annotation]
+ ERROR python/pyspark/pandas/tests/test_typedef.py:217:36-64: Function call cannot be used in annotations [invalid-annotation]
- ERROR python/pyspark/pandas/tests/test_typedef.py:227:36-39: Expected a type-level DSL function, got `type[zip]` [invalid-annotation]
+ ERROR python/pyspark/pandas/tests/test_typedef.py:227:36-64: Function call cannot be used in annotations [invalid-annotation]
- ERROR python/pyspark/pandas/tests/test_typedef.py:239:36-39: Expected a type-level DSL function, got `type[zip]` [invalid-annotation]
+ ERROR python/pyspark/pandas/tests/test_typedef.py:239:36-64: Function call cannot be used in annotations [invalid-annotation]

@rchen152 rchen152 left a comment

Copy link
Copy Markdown
Contributor

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.

@rchen152

rchen152 commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

Good catch, thank you!

@meta-codesync meta-codesync Bot closed this in cbc58eb Oct 6, 2026
@meta-codesync meta-codesync Bot added the Merged label Oct 6, 2026
@meta-codesync

meta-codesync Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

This pull request has been merged in cbc58eb.

meta-codesync Bot pushed a commit that referenced this pull request Oct 7, 2026
Summary:
Follow-up to #5087.

In Python, built-in singleton constants and runtime objects like `NotImplemented`, `Ellipsis`, `None`, and modules do not expose their types as builtins, so users frequently write `type(...)` in type annotations (or confuse `type(MyClass)` with `type[MyClass]`).

When a single-argument call to `builtins.type(...)` appears in an annotation where function calls are invalid, inspect the argument type and append a helpful suggestion to the `invalid-annotation` error message:
- `type(NotImplemented)` -> ``Did you mean `types.NotImplementedType`?``
- `type(Ellipsis)` / `type(...)` -> ``Did you mean `types.EllipsisType`?``
- `type(None)` -> ``Did you mean `None`?``
- `type(some_module)` -> ``Did you mean `types.ModuleType`?``
- `type(MyClass)` -> ``Did you mean `type[MyClass]`?``

Pull Request resolved: #5120

Test Plan: Added `test_type_call_in_annotations` in `pyrefly/lib/test/annotation.rs` and ran `cargo test`.

Reviewed By: stroxler

Differential Revision: D123716933

fbshipit-source-id: b09c93fce8e57e267f715465e768cebd14a0767a
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.

2 participants