Skip to content

Commit b177923

Browse files
yangdanny97meta-codesync[bot]
authored andcommitted
populate display name when untyping a type alias (facebook#1641)
Summary: Pull Request resolved: facebook#1641 - populate display name when untyping a union - keep name when simplifying - display the expanded type when the top level type being printed is the alias (only use the display name for nested types) Reviewed By: rchen152 Differential Revision: D87558543
1 parent 58b1cd4 commit b177923

File tree

8 files changed

+85
-17
lines changed

8 files changed

+85
-17
lines changed

conformance/third_party/conformance.exp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
{
1616
"code": -2,
1717
"column": 9,
18-
"concise_description": "`TypeAlias[GoodTypeAlias3, type[list[int | None]]]` is not subscriptable",
19-
"description": "`TypeAlias[GoodTypeAlias3, type[list[int | None]]]` is not subscriptable",
18+
"concise_description": "`TypeAlias[GoodTypeAlias3, type[list[GoodTypeAlias2]]]` is not subscriptable",
19+
"description": "`TypeAlias[GoodTypeAlias3, type[list[GoodTypeAlias2]]]` is not subscriptable",
2020
"line": 68,
2121
"name": "unsupported-operation",
2222
"severity": "error",
@@ -270,8 +270,8 @@
270270
{
271271
"code": -2,
272272
"column": 9,
273-
"concise_description": "`type[list[int | None]]` is not subscriptable",
274-
"description": "`type[list[int | None]]` is not subscriptable",
273+
"concise_description": "`type[list[GoodTypeAlias2]]` is not subscriptable",
274+
"description": "`type[list[GoodTypeAlias2]]` is not subscriptable",
275275
"line": 77,
276276
"name": "unsupported-operation",
277277
"severity": "error",

crates/pyrefly_types/src/display.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -449,11 +449,8 @@ impl<'a> TypeDisplayContext<'a> {
449449
Type::Union(box Union {
450450
display_name: Some(name),
451451
..
452-
}) => output.write_str(name),
453-
Type::Union(box Union {
454-
members,
455-
display_name: None,
456-
}) => {
452+
}) if !is_toplevel => output.write_str(name),
453+
Type::Union(box Union { members, .. }) => {
457454
let mut literal_idx = None;
458455
let mut literals = Vec::new();
459456
let mut union_members: Vec<&Type> = Vec::new();

crates/pyrefly_types/src/types.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1472,10 +1472,15 @@ impl Type {
14721472
})
14731473
}
14741474

1475-
pub fn sort_unions(self) -> Self {
1475+
pub fn sort_unions_and_drop_names(self) -> Self {
14761476
self.transform(&mut |ty| {
1477-
if let Type::Union(box Union { members: ts, .. }) = ty {
1477+
if let Type::Union(box Union {
1478+
members: ts,
1479+
display_name,
1480+
}) = ty
1481+
{
14781482
ts.sort();
1483+
*display_name = None;
14791484
}
14801485
})
14811486
}

pyrefly/lib/alt/solve.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3800,7 +3800,13 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
38003800
Type::None => Some(Type::None), // Both a value and a type
38013801
Type::Ellipsis => Some(Type::Ellipsis), // A bit weird because of tuples, so just promote it
38023802
Type::Any(style) => Some(style.propagate()),
3803-
Type::TypeAlias(ta) => self.untype_opt(ta.as_type(), range, errors),
3803+
Type::TypeAlias(ta) => {
3804+
let mut aliased_type = self.untype_opt(ta.as_type(), range, errors)?;
3805+
if let Type::Union(box Union { display_name, .. }) = &mut aliased_type {
3806+
*display_name = Some(ta.name.to_string());
3807+
}
3808+
Some(aliased_type)
3809+
}
38043810
t @ Type::Unpack(
38053811
box Type::Tuple(_) | box Type::TypeVarTuple(_) | box Type::Quantified(_),
38063812
) => Some(t),

pyrefly/lib/alt/special_calls.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,9 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
7070
.distribute_type_over_union();
7171
// Make assert_type(Self@SomeClass, typing.Self) work.
7272
ty.subst_self_type_mut(&self_form);
73-
// Re-sort unions. Make sure to keep this as the final step before comparison.
74-
ty.sort_unions()
73+
// Re-sort unions & drop any display names.
74+
// Make sure to keep this as the final step before comparison.
75+
ty.sort_unions_and_drop_names()
7576
};
7677
let a = normalize_type(a, expr_a);
7778
let b = normalize_type(b, expr_b);

pyrefly/lib/solver/solver.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -457,8 +457,17 @@ impl Solver {
457457
/// Simplify a type as much as we can.
458458
fn simplify_mut(&self, t: &mut Type) {
459459
t.transform_mut(&mut |x| {
460-
if let Type::Union(box Union { members: xs, .. }) = x {
461-
*x = unions(mem::take(xs));
460+
if let Type::Union(box Union {
461+
members: xs,
462+
display_name: original_name,
463+
}) = x
464+
{
465+
let mut merged = unions(mem::take(xs));
466+
// Preserve union display names during simplification
467+
if let Type::Union(box Union { display_name, .. }) = &mut merged {
468+
*display_name = original_name.clone();
469+
}
470+
*x = merged;
462471
}
463472
if let Type::Intersect(y) = x {
464473
*x = intersect(mem::take(&mut y.0), y.1.clone());

pyrefly/lib/test/class_overrides.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@ class A:
577577
def f(self, x: TA1):
578578
pass
579579
class B(A):
580-
def f(self, x: TA2): # E: `B.f` has type `BoundMethod[B, (self: B, x: float | int) -> None]`, which is not assignable to `BoundMethod[B, (self: B, x: float | int | None) -> None]`, the type of `A.f`
580+
def f(self, x: TA2): # E: `B.f` has type `BoundMethod[B, (self: B, x: TA2) -> None]`, which is not assignable to `BoundMethod[B, (self: B, x: TA1) -> None]`, the type of `A.f`
581581
pass
582582
"#,
583583
);

pyrefly/lib/test/type_alias.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,3 +824,53 @@ class UseC(Generic[T]):
824824
current.make()
825825
"#,
826826
);
827+
828+
fn env_with_alias() -> TestEnv {
829+
TestEnv::one(
830+
"foo",
831+
r#"
832+
type TA = int | str
833+
834+
def f(x: TA) -> TA:
835+
return x
836+
"#,
837+
)
838+
}
839+
840+
testcase!(
841+
test_alias_union_name,
842+
env_with_alias(),
843+
r#"
844+
from foo import TA, f
845+
from typing import Callable
846+
847+
val1: int | str = 1
848+
val2: TA = 1
849+
850+
# Union names are only shown when nested in another type
851+
852+
f(object()) # E: Argument `object` is not assignable to parameter `x` with type `int | str` in function `foo.f`
853+
f(val1)
854+
f(val2)
855+
x1: TA = object() # E: `object` is not assignable to `int | str`
856+
x2: TA = val1
857+
x3: TA = val2
858+
c1: Callable[[int], int] = f # E: `(x: TA) -> TA` is not assignable to `(int) -> int`
859+
860+
# Union names are lost when flattened into another union
861+
862+
class C: pass
863+
def f2(x: TA | C) -> TA | C:
864+
return x
865+
866+
f2(object()) # E: Argument `object` is not assignable to parameter `x` with type `C | int | str` in function `f2`
867+
f2(val1)
868+
f2(val2)
869+
f2(C())
870+
x4: TA | C = object() # E: `object` is not assignable to `C | int | str`
871+
x5: TA | C = val1
872+
x6: TA | C = val2
873+
x7: TA | C = C()
874+
c2: Callable[[int], int] = f2 # E: `(x: C | int | str) -> C | int | str` is not assignable to `(int) -> int`
875+
"#,
876+
);

0 commit comments

Comments
 (0)