From 5b40295cd15eab2a6ab0f361b9af7d2591938c6f Mon Sep 17 00:00:00 2001 From: David Peter Date: Wed, 1 Oct 2025 15:16:16 +0200 Subject: [PATCH 1/2] [ty] Enums: allow multiple aliases to point to the same member --- .../resources/mdtest/enums.md | 35 +++++++++++++++++++ crates/ty_python_semantic/src/types/enums.rs | 8 +++-- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/crates/ty_python_semantic/resources/mdtest/enums.md b/crates/ty_python_semantic/resources/mdtest/enums.md index 2c3c03375ca281..c526e111243a57 100644 --- a/crates/ty_python_semantic/resources/mdtest/enums.md +++ b/crates/ty_python_semantic/resources/mdtest/enums.md @@ -265,6 +265,41 @@ reveal_type(enum_members(Color)) reveal_type(Color.red) ``` +Multiple aliases to the same member are also supported. This is a regression test for +: + +```py +from ty_extensions import enum_members + +class ManyAliases(Enum): + real_member = "real_member" + alias1 = "real_member" + alias2 = "real_member" + alias3 = "real_member" + + other_member = "other_real_member" + +# revealed: tuple[Literal["real_member"], Literal["other_member"]] +reveal_type(enum_members(ManyAliases)) + +reveal_type(ManyAliases.real_member) # revealed: Literal[ManyAliases.real_member] +reveal_type(ManyAliases.alias1) # revealed: Literal[ManyAliases.real_member] +reveal_type(ManyAliases.alias2) # revealed: Literal[ManyAliases.real_member] +reveal_type(ManyAliases.alias3) # revealed: Literal[ManyAliases.real_member] + +reveal_type(ManyAliases.real_member.value) # revealed: Literal["real_member"] +reveal_type(ManyAliases.real_member.name) # revealed: Literal["real_member"] + +reveal_type(ManyAliases.alias1.value) # revealed: Literal["real_member"] +reveal_type(ManyAliases.alias1.name) # revealed: Literal["real_member"] + +reveal_type(ManyAliases.alias2.value) # revealed: Literal["real_member"] +reveal_type(ManyAliases.alias2.name) # revealed: Literal["real_member"] + +reveal_type(ManyAliases.alias3.value) # revealed: Literal["real_member"] +reveal_type(ManyAliases.alias3.name) # revealed: Literal["real_member"] +``` + ### Using `auto()` ```toml diff --git a/crates/ty_python_semantic/src/types/enums.rs b/crates/ty_python_semantic/src/types/enums.rs index 47000ce7ea1145..f702378629a7e6 100644 --- a/crates/ty_python_semantic/src/types/enums.rs +++ b/crates/ty_python_semantic/src/types/enums.rs @@ -196,10 +196,14 @@ pub(crate) fn enum_metadata<'db>( value_ty, Type::IntLiteral(_) | Type::StringLiteral(_) | Type::BytesLiteral(_) ) { - if let Some(previous) = enum_values.insert(value_ty, name.clone()) { - aliases.insert(name.clone(), previous); + if let Some(canonical) = enum_values.get(&value_ty) { + // This is a duplicate value, create an alias to the canonical (first) member + aliases.insert(name.clone(), canonical.clone()); return None; } + + // This is the first occurrence of this value, track it as the canonical member + enum_values.insert(value_ty, name.clone()); } let declarations = use_def_map.end_of_scope_symbol_declarations(symbol_id); From 435b01dc8bc6b296acc568fbed2dbd16dcf6e5ac Mon Sep 17 00:00:00 2001 From: David Peter Date: Wed, 1 Oct 2025 15:37:32 +0200 Subject: [PATCH 2/2] Apply alias check for Boolean literals --- crates/ty_python_semantic/src/types/enums.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/ty_python_semantic/src/types/enums.rs b/crates/ty_python_semantic/src/types/enums.rs index f702378629a7e6..6ddb8de18550e9 100644 --- a/crates/ty_python_semantic/src/types/enums.rs +++ b/crates/ty_python_semantic/src/types/enums.rs @@ -194,7 +194,10 @@ pub(crate) fn enum_metadata<'db>( // we don't know if it's a duplicate or not. if matches!( value_ty, - Type::IntLiteral(_) | Type::StringLiteral(_) | Type::BytesLiteral(_) + Type::BooleanLiteral(_) + | Type::IntLiteral(_) + | Type::StringLiteral(_) + | Type::BytesLiteral(_) ) { if let Some(canonical) = enum_values.get(&value_ty) { // This is a duplicate value, create an alias to the canonical (first) member