Skip to content

[ty] Validate PEP 695 type alias scope and redeclaration rules#24341

Open
charliermarsh wants to merge 2 commits intomainfrom
charlie/type-alias-validation
Open

[ty] Validate PEP 695 type alias scope and redeclaration rules#24341
charliermarsh wants to merge 2 commits intomainfrom
charlie/type-alias-validation

Conversation

@charliermarsh
Copy link
Copy Markdown
Member

@charliermarsh charliermarsh commented Apr 1, 2026

Summary

We now detect PEP 695 redeclarations in the same scope, like:

type Alias = int
type Alias = str

And also declarations within function scopes:

def f():
    type Local = int

@charliermarsh charliermarsh added the ty Multi-file analysis & type inference label Apr 1, 2026
@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot bot commented Apr 1, 2026

Typing conformance results improved 🎉

The percentage of diagnostics emitted that were expected errors increased from 86.76% to 86.79%. The percentage of expected errors that received a diagnostic increased from 81.53% to 81.72%. The number of fully passing files held steady at 70/132.

Summary

How are test cases classified?

Each test case represents one expected error annotation or a group of annotations sharing a tag. Counts are per test case, not per diagnostic — multiple diagnostics on the same line count as one. Required annotations (E) are true positives when ty flags the expected location and false negatives when it does not. Optional annotations (E?) are true positives when flagged but true negatives (not false negatives) when not. Tagged annotations (E[tag]) require ty to flag exactly one of the tagged lines; tagged multi-annotations (E[tag+]) allow any number up to the tag count. Flagging unexpected locations counts as a false positive.

Metric Old New Diff Outcome
True Positives 865 867 +2 ⏫ (✅)
False Positives 132 132 +0
False Negatives 196 194 -2 ⏬ (✅)
Total Diagnostics 1050 1052 +2
Precision 86.76% 86.79% +0.03% ⏫ (✅)
Recall 81.53% 81.72% +0.19% ⏫ (✅)
Passing Files 70/132 70/132 +0

Test file breakdown

1 file altered
File True Positives False Positives False Negatives Status
aliases_type_statement.py 26 (+2) ✅ 1 1 (-2) ✅ 📈 Improving
Total (all files) 867 (+2) ✅ 132 194 (-2) ✅ 70/132

True positives added (2)

2 diagnostics
Test case Diff

aliases_type_statement.py:56

+error[invalid-type-alias] `type` statements are not allowed in function scopes

aliases_type_statement.py:52

+error[invalid-type-alias] Type alias `BadTypeAlias14` is already defined in this scope

@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot bot commented Apr 1, 2026

Memory usage report

Memory usage unchanged ✅

@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot bot commented Apr 1, 2026

ecosystem-analyzer results

Lint rule Added Removed Changed
invalid-type-alias 1 0 0
Total 1 0 0

Raw diff:

AutoSplit (https://github.com/Toufool/AutoSplit)
+ src/utils.py:34:10 error[invalid-type-alias] Type alias `STARTUPINFO` is already defined in this scope

Full report with detailed diff (timing results)

@charliermarsh charliermarsh force-pushed the charlie/type-alias-validation branch from ff207d0 to 9f29ce9 Compare April 1, 2026 01:22
@charliermarsh charliermarsh marked this pull request as ready for review April 1, 2026 01:33
@carljm carljm removed their request for review April 1, 2026 14:29
@charliermarsh charliermarsh marked this pull request as draft April 1, 2026 14:46
@charliermarsh charliermarsh force-pushed the charlie/type-alias-validation branch from 9f29ce9 to 70875ae Compare April 1, 2026 16:14
@charliermarsh charliermarsh force-pushed the charlie/type-alias-validation branch from 70875ae to bb6f05b Compare April 1, 2026 17:24
type BranchRedeclared = int
else:
# error: [invalid-type-alias] "Type alias `BranchRedeclared` is already defined in this scope"
type BranchRedeclared = str
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I was a bit surprised by this, but other type checkers do flag it.

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.

I don't like flagging this -- does the conformance suite require it?

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.

If we do flag this, we also need to ensure we don't flag it if one of the branches is statically unreachable.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I agree with @carljm, but yes -- unfortunately the conformance suite does require it currently: https://github.com/python/typing/blob/1df1565c69730d88ce6877009d268ba1d602af1e/conformance/tests/aliases_type_statement.py#L52. Not sure if that's actually following the spec or if the conformance suite is going beyond the spec there

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.

I can't find any such requirement in PEP 695 or in the spec. I think we should PR the conformance suite to not require this error.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Just to make sure I understand -- do you also dislike flagging this?

type Redeclared = int
# error: [invalid-type-alias] "Type alias `Redeclared` is already defined in this scope"
type Redeclared = str

Or are you referring to the conditional version?

Copy link
Copy Markdown
Member

@AlexWaygood AlexWaygood Apr 1, 2026

Choose a reason for hiding this comment

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

it's not consistent with the way we allow this, for example:

x: int = 42
x: str = "42"

other type checkers flag that, but we intentionally deviate from other type checkers there -- it's not specified that type checkers should reject that, and the intent of the user to fully shadow the prior variable with a new declaration is clear. So yeah, I dislike flagging Redeclared in your example above, for the same reasons that I've always disliked that other type checkers flag x in my example :-)

Copy link
Copy Markdown
Member Author

@charliermarsh charliermarsh Apr 1, 2026

Choose a reason for hiding this comment

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

Okay thanks. I kind of thought that snippet was fine to flag, but that the conditional should've been allowed -- at least, that was the intent of my initial comment :) But I defer to you two, your arguments make sense to me.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I also don't see the rule against defining type aliases in function bodies specified anywhere, and that also seems sorta unnecessary... it's not totally clear to me why users should be prohibited from doing something like this, which seems fine: https://play.ty.dev/f918c85d-8bf3-440c-a5bf-1188cd72d053

@charliermarsh charliermarsh marked this pull request as ready for review April 1, 2026 17:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants