Skip to content

fix Conformance: Missing type alias validation #2451#3174

Open
asukaminato0721 wants to merge 8 commits intofacebook:mainfrom
asukaminato0721:2451
Open

fix Conformance: Missing type alias validation #2451#3174
asukaminato0721 wants to merge 8 commits intofacebook:mainfrom
asukaminato0721:2451

Conversation

@asukaminato0721
Copy link
Copy Markdown
Contributor

@asukaminato0721 asukaminato0721 commented Apr 18, 2026

Summary

Fixes #2451

factored the syntax check into a reusable helper.

detect NameAssign bindings that were never real implicit aliases and surface the syntax error at the annotation use site.

preserves normal runtime-value typing while fixing the implicit-alias false negatives.

Test Plan

update test

@meta-cla meta-cla Bot added the cla signed label Apr 18, 2026
@asukaminato0721 asukaminato0721 marked this pull request as ready for review April 18, 2026 08:34
Copilot AI review requested due to automatic review settings April 18, 2026 08:34
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes missing validation for “implicit type aliases” whose RHS is not a valid annotation/type-expression syntax, ensuring these now error at the annotation use site (conformance #2451). The PR also refactors annotation-syntax validation into a reusable helper and updates conformance baselines.

Changes:

  • Refactor annotation syntax validation into annotation_syntax_problem and reuse it for diagnostics.
  • Detect invalid implicit type-alias bindings (via NameAssign) and surface InvalidAnnotation at the annotation usage site.
  • Update tests and third-party conformance expectation/result artifacts to reflect the new diagnostics.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
pyrefly/lib/alt/solve.rs Adds reusable annotation-syntax helper and new implicit type-alias syntax validation that reports invalid-annotation at use sites.
pyrefly/lib/test/type_alias.rs Updates the conformance test case to expect the new, more specific annotation-syntax errors.
conformance/third_party/results.json Updates aggregated third-party conformance pass/fail counts.
conformance/third_party/conformance.result Updates per-test expectation summary to reflect aliases_implicit.py now passing.
conformance/third_party/conformance.exp Updates expected diagnostic outputs (messages/kinds) for the affected conformance cases.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pyrefly/lib/alt/solve.rs Outdated
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions github-actions Bot added size/l and removed size/l labels Apr 18, 2026
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions github-actions Bot added size/l and removed size/l labels Apr 18, 2026
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@migeed-z migeed-z self-assigned this Apr 20, 2026
@meta-codesync
Copy link
Copy Markdown
Contributor

meta-codesync Bot commented Apr 20, 2026

@migeed-z has imported this pull request. If you are a Meta employee, you can view this in D101670073.

Comment thread pyrefly/lib/alt/solve.rs Outdated
Comment thread pyrefly/lib/test/type_alias.rs
Comment thread pyrefly/lib/test/type_alias.rs
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@migeed-z
Copy link
Copy Markdown
Contributor

Hmmm. I am not sure this approach is the right one. The reason is that something like this:

DynClass = type("DynClass", (), {})
def f(x: DynClass) -> None:  # E: Function call cannot be used in annotations
    pass

will fail and introduce false positives. The reason is that we are interpreting type(..) as a function call and rejecting it, but we should interpret the type of the value instead. So I think the approach needs to be modified to do that instead.

@asukaminato0721 asukaminato0721 force-pushed the 2451 branch 2 times, most recently from d36f325 to 58df9a1 Compare April 22, 2026 15:03
@github-actions github-actions Bot added size/l and removed size/l labels Apr 22, 2026
@asukaminato0721 asukaminato0721 marked this pull request as ready for review April 22, 2026 15:11
@github-actions github-actions Bot added size/l and removed size/l labels Apr 22, 2026
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@asukaminato0721 asukaminato0721 marked this pull request as draft April 22, 2026 17:43
@github-actions github-actions Bot added size/xl and removed size/l labels Apr 22, 2026
@github-actions
Copy link
Copy Markdown

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

pytest-autoprofile (https://gitlab.com/TTsangSC/pytest-autoprofile)
- ERROR src/pytest_autoprofile/_typing.py:81:10-14: Expected a type form, got instance of `TypeVar` [not-a-type]
+ ERROR src/pytest_autoprofile/_typing.py:81:10-14: Function call cannot be used in annotations [invalid-annotation]

mypy (https://github.com/python/mypy)
- ERROR mypyc/test-data/fixtures/typing-full.pyi:76:26-29: Expected a type form, got instance of `object` [not-a-type]
- ERROR mypyc/test-data/fixtures/typing-full.pyi:76:36-39: Expected a type form, got instance of `object` [not-a-type]
- ERROR mypyc/test-data/fixtures/typing-full.pyi:76:50-53: Expected a type form, got instance of `object` [not-a-type]
+ ERROR mypyc/test-data/fixtures/typing-full.pyi:76:26-29: Function call cannot be used in annotations [invalid-annotation]
+ ERROR mypyc/test-data/fixtures/typing-full.pyi:76:36-39: Function call cannot be used in annotations [invalid-annotation]
+ ERROR mypyc/test-data/fixtures/typing-full.pyi:76:50-53: Function call cannot be used in annotations [invalid-annotation]
- ERROR mypyc/test-data/fixtures/typing-full.pyi:92:27-30: Expected a type form, got instance of `object` [not-a-type]
- ERROR mypyc/test-data/fixtures/typing-full.pyi:92:37-40: Expected a type form, got instance of `object` [not-a-type]
- ERROR mypyc/test-data/fixtures/typing-full.pyi:92:51-54: Expected a type form, got instance of `object` [not-a-type]
+ ERROR mypyc/test-data/fixtures/typing-full.pyi:92:27-30: Function call cannot be used in annotations [invalid-annotation]
+ ERROR mypyc/test-data/fixtures/typing-full.pyi:92:37-40: Function call cannot be used in annotations [invalid-annotation]
+ ERROR mypyc/test-data/fixtures/typing-full.pyi:92:51-54: Function call cannot be used in annotations [invalid-annotation]
- ERROR mypyc/test-data/fixtures/typing-full.pyi:113:26-29: Expected a type form, got instance of `object` [not-a-type]
- ERROR mypyc/test-data/fixtures/typing-full.pyi:113:36-39: Expected a type form, got instance of `object` [not-a-type]
- ERROR mypyc/test-data/fixtures/typing-full.pyi:113:50-53: Expected a type form, got instance of `object` [not-a-type]
+ ERROR mypyc/test-data/fixtures/typing-full.pyi:113:26-29: Function call cannot be used in annotations [invalid-annotation]
+ ERROR mypyc/test-data/fixtures/typing-full.pyi:113:36-39: Function call cannot be used in annotations [invalid-annotation]
+ ERROR mypyc/test-data/fixtures/typing-full.pyi:113:50-53: Function call cannot be used in annotations [invalid-annotation]
- ERROR mypyc/test-data/fixtures/typing-full.pyi:131:30-33: Expected a type form, got instance of `object` [not-a-type]
+ ERROR mypyc/test-data/fixtures/typing-full.pyi:131:30-33: Function call cannot be used in annotations [invalid-annotation]
- ERROR mypyc/test-data/fixtures/typing-full.pyi:160:34-37: Expected a type form, got instance of `object` [not-a-type]
- ERROR mypyc/test-data/fixtures/typing-full.pyi:160:50-53: Expected a type form, got instance of `object` [not-a-type]
- ERROR mypyc/test-data/fixtures/typing-full.pyi:160:66-69: Expected a type form, got instance of `object` [not-a-type]
- ERROR mypyc/test-data/fixtures/typing-full.pyi:160:74-77: Expected a type form, got instance of `object` [not-a-type]
+ ERROR mypyc/test-data/fixtures/typing-full.pyi:160:34-37: Function call cannot be used in annotations [invalid-annotation]
+ ERROR mypyc/test-data/fixtures/typing-full.pyi:160:50-53: Function call cannot be used in annotations [invalid-annotation]
+ ERROR mypyc/test-data/fixtures/typing-full.pyi:160:66-69: Function call cannot be used in annotations [invalid-annotation]
+ ERROR mypyc/test-data/fixtures/typing-full.pyi:160:74-77: Function call cannot be used in annotations [invalid-annotation]
- ERROR mypyc/test-data/fixtures/typing-full.pyi:171:29-37: Expected a type form, got instance of `Literal[0]` [not-a-type]
- ERROR mypyc/test-data/fixtures/typing-full.pyi:173:22-30: Expected a type form, got instance of `Literal[0]` [not-a-type]
+ ERROR mypyc/test-data/fixtures/typing-full.pyi:171:29-37: Number literal cannot be used in annotations [invalid-annotation]
+ ERROR mypyc/test-data/fixtures/typing-full.pyi:173:22-30: Number literal cannot be used in annotations [invalid-annotation]
- ERROR mypyc/test-data/fixtures/typing-full.pyi:175:30-38: Expected a type form, got instance of `Literal[0]` [not-a-type]
+ ERROR mypyc/test-data/fixtures/typing-full.pyi:175:30-38: Number literal cannot be used in annotations [invalid-annotation]

paasta (https://github.com/yelp/paasta)
+ ERROR paasta_tools/bounce_lib.py:81:17-39: Function call cannot be used in annotations [invalid-annotation]
+ ERROR paasta_tools/bounce_lib.py:86:6-24: Function call cannot be used in annotations [invalid-annotation]
+ ERROR paasta_tools/bounce_lib.py:110:17-39: Function call cannot be used in annotations [invalid-annotation]
+ ERROR paasta_tools/bounce_lib.py:115:6-24: Function call cannot be used in annotations [invalid-annotation]
+ ERROR paasta_tools/bounce_lib.py:128:17-39: Function call cannot be used in annotations [invalid-annotation]
+ ERROR paasta_tools/bounce_lib.py:133:6-24: Function call cannot be used in annotations [invalid-annotation]
+ ERROR paasta_tools/bounce_lib.py:155:17-39: Function call cannot be used in annotations [invalid-annotation]
+ ERROR paasta_tools/bounce_lib.py:160:6-24: Function call cannot be used in annotations [invalid-annotation]
+ ERROR paasta_tools/bounce_lib.py:173:17-39: Function call cannot be used in annotations [invalid-annotation]
+ ERROR paasta_tools/bounce_lib.py:178:6-24: Function call cannot be used in annotations [invalid-annotation]
+ ERROR paasta_tools/drain_lib.py:263:16-23: Function call cannot be used in annotations [invalid-annotation]
+ ERROR paasta_tools/drain_lib.py:264:24-31: Function call cannot be used in annotations [invalid-annotation]
+ ERROR paasta_tools/drain_lib.py:265:22-29: Function call cannot be used in annotations [invalid-annotation]
+ ERROR paasta_tools/drain_lib.py:266:26-33: Function call cannot be used in annotations [invalid-annotation]
+ ERROR paasta_tools/drain_lib.py:304:45-52: Function call cannot be used in annotations [invalid-annotation]
+ ERROR paasta_tools/frameworks/native_service_config.py:96:14-20: Function call cannot be used in annotations [invalid-annotation]
+ ERROR paasta_tools/frameworks/native_service_config.py:97:15-22: Function call cannot be used in annotations [invalid-annotation]
+ ERROR paasta_tools/frameworks/native_service_config.py:102:16-29: Function call cannot be used in annotations [invalid-annotation]
+ ERROR paasta_tools/frameworks/native_service_config.py:103:14-25: Function call cannot be used in annotations [invalid-annotation]
+ ERROR paasta_tools/generate_deployments_for_service.py:132:18-29: Function call cannot be used in annotations [invalid-annotation]
+ ERROR paasta_tools/generate_deployments_for_service.py:227:77-88: Function call cannot be used in annotations [invalid-annotation]
+ ERROR paasta_tools/generate_deployments_for_service.py:228:6-21: Function call cannot be used in annotations [invalid-annotation]
+ ERROR paasta_tools/hacheck.py:39:40-49: Function call cannot be used in annotations [invalid-annotation]
+ ERROR paasta_tools/hacheck.py:67:19-28: Function call cannot be used in annotations [invalid-annotation]
+ ERROR paasta_tools/kubernetes_tools.py:2346:22-40: Function call cannot be used in annotations [invalid-annotation]
+ ERROR paasta_tools/kubernetes_tools.py:2454:17-30: Function call cannot be used in annotations [invalid-annotation]
+ ERROR paasta_tools/mesos/master.py:301:41-53: Function call cannot be used in annotations [invalid-annotation]

@github-actions
Copy link
Copy Markdown

Primer Diff Classification

❌ 1 regression(s) | ➖ 2 neutral | 3 project(s) total | +45, -18 errors

1 regression(s) across paasta. error kinds: invalid-annotation on mypy_extensions.TypedDict. caused by implicit_type_alias_syntax_problem().

Project Verdict Changes Error Kinds Root Cause
pytest-autoprofile ➖ Neutral +1, -1 invalid-annotation, not-a-type implicit_type_alias_syntax_problem()
mypy ➖ Neutral +17, -17 invalid-annotation, not-a-type implicit_type_alias_syntax_problem()
paasta ❌ Regression +27 invalid-annotation on mypy_extensions.TypedDict implicit_type_alias_syntax_problem()
Detailed analysis

❌ Regression (1)

paasta (+27)

invalid-annotation on mypy_extensions.TypedDict: All 27 errors flag uses of names defined via mypy_extensions.TypedDict(...) as invalid annotations. The PR's new implicit alias validation doesn't recognize mypy_extensions as a valid source for TypedDict, causing false positives. These are regression — the code is correct and mypy doesn't flag them.

Overall: The PR correctly implements detection of invalid implicit type aliases but has an incomplete allowlist. mypy_extensions.TypedDict is a legitimate TypedDict constructor that should be recognized alongside typing.TypedDict and typing_extensions.TypedDict. The 27 new errors are all false positives caused by this omission. Similar issues likely affect other files using mypy_extensions.TypedDict or mypy_extensions.Arg/DefaultArg patterns.

Attribution: The new implicit_type_alias_syntax_problem() function in pyrefly/lib/alt/solve.rs and the implicit_alias_call_uses_runtime_type() helper check whether a call expression targets a recognized special export. The call_targets_special_export_inner() method resolves imports but only recognizes TypedDict from specific modules (typing, typing_extensions). Since mypy_extensions.TypedDict is not in the allowlist, it's treated as a plain function call, triggering the invalid-annotation error.

➖ Neutral (2)

pytest-autoprofile (+1, -1)

Without access to the source code at line 81, we cannot definitively determine whether either error is a false positive. The removed error 'Expected a type form, got instance of TypeVar' indicated the checker saw a TypeVar instance where a type form was expected. The new error 'Function call cannot be used in annotations' suggests the checker now interprets the same expression as a function call within an annotation context. This could occur if a TypeVar is being constructed inline (e.g., TypeVar('T') used directly in an annotation) rather than being referenced by name after assignment. TypeVars are valid in annotations when referenced by their assigned name (e.g., T), but calling TypeVar(...) directly in an annotation position would indeed be a function call in an annotation. The change in error message reflects a change in how the checker categorizes this pattern. Without source code, we cannot confirm the false positive claim for either error. The new error message may actually be more accurate about the surface-level issue if the code contains an inline TypeVar construction.
Attribution: The new implicit_type_alias_syntax_problem() function in pyrefly/lib/alt/solve.rs traces name assignments back to their RHS and checks annotation syntax. The TypeVar exemption (checking expr_infer for Type::TypeVar) apparently doesn't fire for this project's specific pattern, causing the Expr::Call branch to incorrectly report 'Function call cannot be used in annotations'.

mypy (+17, -17)

This is a neutral change — 17 errors were removed and 17 equivalent errors were added at the exact same locations. The error kind changed from not-a-type to invalid-annotation and the message changed from 'Expected a type form, got instance of object' to 'Function call cannot be used in annotations'. Both correctly identify that Any = object() creates a non-type value used in annotations. The net effect on error count is zero, and the diagnostic quality is arguably slightly improved (more specific message).
Attribution: The change to implicit_type_alias_syntax_problem() in pyrefly/lib/alt/solve.rs now traces name assignments back to their definitions and checks annotation syntax at the use site. When it finds Any = object() and Any is used in annotations, it reports invalid-annotation instead of the previous not-a-type error. The new reports_implicit_alias_syntax_at_use_site() method on TypeFormContext determines which annotation contexts trigger this check.

Suggested fixes

Summary: The new implicit_alias_call_uses_runtime_type() function doesn't recognize mypy_extensions.TypedDict as a valid TypedDict constructor, causing 27 false-positive invalid-annotation errors in paasta.

1. In the defined_in() method of SpecialExport::TypedDict (likely in a special_export.rs or similar file where SpecialExport is defined), add mypy_extensions as a recognized module for TypedDict. The call_targets_special_export_inner() function in pyrefly/lib/alt/solve.rs calls export.defined_in(module) when resolving Expr::Attribute patterns like mypy_extensions.TypedDict. Currently SpecialExport::TypedDict.[defined_in()](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/alt/solve.rs) only returns true for typing and typing_extensions, so mypy_extensions.TypedDict(...) calls are not recognized as special exports, causing implicit_alias_call_uses_runtime_type() to return false and the annotation syntax check to fire. Add mypy_extensions to the list of modules recognized by defined_in() for SpecialExport::TypedDict. This eliminates all 27 false-positive invalid-annotation errors in paasta.

Files: pyrefly/lib/alt/solve.rs, pyrefly/lib/export.rs
Confidence: high
Affected projects: paasta
Fixes: invalid-annotation
The regression is clearly caused by the new implicit type alias syntax validation not recognizing mypy_extensions as a valid source for TypedDict. The call_targets_special_export_inner() method in solve.rs checks export.defined_in(module) for attribute-style access (e.g., mypy_extensions.TypedDict), and since mypy_extensions is not in the allowlist for TypedDict, the call is treated as a plain function call rather than a special TypedDict constructor. All 27 errors are on mypy_extensions.TypedDict usage. While pyright also reports these, mypy does not, and mypy_extensions.TypedDict is a widely-used legitimate TypedDict constructor that predates typing_extensions.TypedDict.


Was this helpful? React with 👍 or 👎

Classification by primer-classifier (3 LLM)

@asukaminato0721
Copy link
Copy Markdown
Contributor Author

so... should i also support mypy_extensions.TypedDict ?

@asukaminato0721 asukaminato0721 marked this pull request as ready for review April 22, 2026 18:38
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.

Conformance: Missing type alias validation

3 participants