Skip to content

Commit c4bc420

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) Differential Revision: D87558543
1 parent f4b8833 commit c4bc420

File tree

8 files changed

+108
-34
lines changed

8 files changed

+108
-34
lines changed

conformance/third_party/conformance.exp

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4275,8 +4275,8 @@
42754275
{
42764276
"code": -2,
42774277
"column": 44,
4278-
"concise_description": "`Ham` is deprecated",
4279-
"description": "`Ham` is deprecated\n Use Spam instead",
4278+
"concise_description": "`Ham` is deprecated: Use Spam instead",
4279+
"description": "`Ham` is deprecated: Use Spam instead",
42804280
"line": 18,
42814281
"name": "deprecated",
42824282
"severity": "warn",
@@ -4286,8 +4286,8 @@
42864286
{
42874287
"code": -2,
42884288
"column": 1,
4289-
"concise_description": "`_directives_deprecated_library.norwegian_blue` is deprecated",
4290-
"description": "`_directives_deprecated_library.norwegian_blue` is deprecated\n It is pining for the fjords",
4289+
"concise_description": "`_directives_deprecated_library.norwegian_blue` is deprecated: It is pining for the fjords",
4290+
"description": "`_directives_deprecated_library.norwegian_blue` is deprecated: It is pining for the fjords",
42914291
"line": 24,
42924292
"name": "deprecated",
42934293
"severity": "warn",
@@ -4297,8 +4297,8 @@
42974297
{
42984298
"code": -2,
42994299
"column": 5,
4300-
"concise_description": "`_directives_deprecated_library.norwegian_blue` is deprecated",
4301-
"description": "`_directives_deprecated_library.norwegian_blue` is deprecated\n It is pining for the fjords",
4300+
"concise_description": "`_directives_deprecated_library.norwegian_blue` is deprecated: It is pining for the fjords",
4301+
"description": "`_directives_deprecated_library.norwegian_blue` is deprecated: It is pining for the fjords",
43024302
"line": 25,
43034303
"name": "deprecated",
43044304
"severity": "warn",
@@ -4308,8 +4308,8 @@
43084308
{
43094309
"code": -2,
43104310
"column": 12,
4311-
"concise_description": "Call to deprecated overload `_directives_deprecated_library.foo`",
4312-
"description": "Call to deprecated overload `_directives_deprecated_library.foo`\n Only str will be allowed",
4311+
"concise_description": "Call to deprecated overload `_directives_deprecated_library.foo`: Only str will be allowed",
4312+
"description": "Call to deprecated overload `_directives_deprecated_library.foo`: Only str will be allowed",
43134313
"line": 30,
43144314
"name": "deprecated",
43154315
"severity": "warn",
@@ -4320,7 +4320,7 @@
43204320
"code": -2,
43214321
"column": 5,
43224322
"concise_description": "`+` is not supported between `Spam` and `Literal[1]`",
4323-
"description": "`+` is not supported between `Spam` and `Literal[1]`\n `_directives_deprecated_library.Spam.__add__` is deprecated\n There is enough spam in the world",
4323+
"description": "`+` is not supported between `Spam` and `Literal[1]`\n `_directives_deprecated_library.Spam.__add__` is deprecated: There is enough spam in the world",
43244324
"line": 41,
43254325
"name": "deprecated",
43264326
"severity": "warn",
@@ -4331,7 +4331,7 @@
43314331
"code": -2,
43324332
"column": 1,
43334333
"concise_description": "`+=` is not supported between `Spam` and `Literal[1]`",
4334-
"description": "`+=` is not supported between `Spam` and `Literal[1]`\n `_directives_deprecated_library.Spam.__add__` is deprecated\n There is enough spam in the world",
4334+
"description": "`+=` is not supported between `Spam` and `Literal[1]`\n `_directives_deprecated_library.Spam.__add__` is deprecated: There is enough spam in the world",
43354335
"line": 42,
43364336
"name": "deprecated",
43374337
"severity": "warn",
@@ -4341,8 +4341,8 @@
43414341
{
43424342
"code": -2,
43434343
"column": 1,
4344-
"concise_description": "`_directives_deprecated_library.Spam.greasy` is deprecated",
4345-
"description": "`_directives_deprecated_library.Spam.greasy` is deprecated\n All spam will be equally greasy",
4344+
"concise_description": "`_directives_deprecated_library.Spam.greasy` is deprecated: All spam will be equally greasy",
4345+
"description": "`_directives_deprecated_library.Spam.greasy` is deprecated: All spam will be equally greasy",
43464346
"line": 44,
43474347
"name": "deprecated",
43484348
"severity": "warn",
@@ -4352,8 +4352,8 @@
43524352
{
43534353
"code": -2,
43544354
"column": 1,
4355-
"concise_description": "`_directives_deprecated_library.Spam.shape` is deprecated",
4356-
"description": "`_directives_deprecated_library.Spam.shape` is deprecated\n Shapes are becoming immutable",
4355+
"concise_description": "`_directives_deprecated_library.Spam.shape` is deprecated: Shapes are becoming immutable",
4356+
"description": "`_directives_deprecated_library.Spam.shape` is deprecated: Shapes are becoming immutable",
43574357
"line": 47,
43584358
"name": "deprecated",
43594359
"severity": "warn",
@@ -4363,8 +4363,8 @@
43634363
{
43644364
"code": -2,
43654365
"column": 1,
4366-
"concise_description": "`_directives_deprecated_library.Spam.shape` is deprecated",
4367-
"description": "`_directives_deprecated_library.Spam.shape` is deprecated\n Shapes are becoming immutable",
4366+
"concise_description": "`_directives_deprecated_library.Spam.shape` is deprecated: Shapes are becoming immutable",
4367+
"description": "`_directives_deprecated_library.Spam.shape` is deprecated: Shapes are becoming immutable",
43684368
"line": 48,
43694369
"name": "deprecated",
43704370
"severity": "warn",
@@ -4374,8 +4374,8 @@
43744374
{
43754375
"code": -2,
43764376
"column": 1,
4377-
"concise_description": "`Invocable.__call__` is deprecated",
4378-
"description": "`Invocable.__call__` is deprecated\n Deprecated",
4377+
"concise_description": "`Invocable.__call__` is deprecated: Deprecated",
4378+
"description": "`Invocable.__call__` is deprecated: Deprecated",
43794379
"line": 58,
43804380
"name": "deprecated",
43814381
"severity": "warn",
@@ -4385,8 +4385,8 @@
43854385
{
43864386
"code": -2,
43874387
"column": 1,
4388-
"concise_description": "`lorem` is deprecated",
4389-
"description": "`lorem` is deprecated\n Deprecated",
4388+
"concise_description": "`lorem` is deprecated: Deprecated",
4389+
"description": "`lorem` is deprecated: Deprecated",
43904390
"line": 69,
43914391
"name": "deprecated",
43924392
"severity": "warn",
@@ -4396,8 +4396,8 @@
43964396
{
43974397
"code": -2,
43984398
"column": 5,
4399-
"concise_description": "`SupportsFoo1.foo` is deprecated",
4400-
"description": "`SupportsFoo1.foo` is deprecated\n Deprecated",
4399+
"concise_description": "`SupportsFoo1.foo` is deprecated: Deprecated",
4400+
"description": "`SupportsFoo1.foo` is deprecated: Deprecated",
44014401
"line": 98,
44024402
"name": "deprecated",
44034403
"severity": "warn",

crates/pyrefly_types/src/display.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -415,8 +415,8 @@ impl<'a> TypeDisplayContext<'a> {
415415
Type::Union(box (types, _)) if types.is_empty() => {
416416
self.maybe_fmt_with_module("typing", "Never", output)
417417
}
418-
Type::Union(box (_, Some(name))) => output.write_str(name),
419-
Type::Union(box (types, None)) => {
418+
Type::Union(box (_, Some(name))) if !is_toplevel => output.write_str(name),
419+
Type::Union(box (types, _)) => {
420420
// All Literals will be collected into a single Literal at the index of the first Literal.
421421
let mut literal_idx = None;
422422
let mut literals = Vec::new();
@@ -1102,13 +1102,24 @@ pub mod tests {
11021102
Type::union(vec![nonlit1.clone(), lit1, nonlit2.clone(), lit2]).to_string(),
11031103
"None | Literal[True, 'test'] | LiteralString"
11041104
);
1105+
1106+
// Unions have an optional name field, which is shown only when
1107+
// the union is nested in another type
11051108
assert_eq!(
11061109
Type::Union(Box::new((
1107-
vec![nonlit1, nonlit2],
1110+
vec![nonlit1.clone(), nonlit2.clone()],
11081111
Some("MyUnion".to_owned())
11091112
)))
11101113
.to_string(),
1111-
"MyUnion"
1114+
"None | LiteralString"
1115+
);
1116+
assert_eq!(
1117+
Type::Type(Box::new(Type::Union(Box::new((
1118+
vec![nonlit1, nonlit2],
1119+
Some("MyUnion".to_owned())
1120+
)))))
1121+
.to_string(),
1122+
"type[MyUnion]"
11121123
);
11131124
}
11141125

crates/pyrefly_types/src/types.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1406,10 +1406,11 @@ impl Type {
14061406
})
14071407
}
14081408

1409-
pub fn sort_unions(self) -> Self {
1409+
pub fn sort_unions_and_drop_names(self) -> Self {
14101410
self.transform(&mut |ty| {
1411-
if let Type::Union(box (ts, _)) = ty {
1411+
if let Type::Union(box (ts, name)) = ty {
14121412
ts.sort();
1413+
*name = None;
14131414
}
14141415
})
14151416
}

pyrefly/lib/alt/solve.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3728,7 +3728,13 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
37283728
Type::None => Some(Type::None), // Both a value and a type
37293729
Type::Ellipsis => Some(Type::Ellipsis), // A bit weird because of tuples, so just promote it
37303730
Type::Any(style) => Some(style.propagate()),
3731-
Type::TypeAlias(ta) => self.untype_opt(ta.as_type(), range, errors),
3731+
Type::TypeAlias(ta) => {
3732+
let mut aliased_type = self.untype_opt(ta.as_type(), range, errors)?;
3733+
if let Type::Union(box (_, name)) = &mut aliased_type {
3734+
*name = Some(ta.name.to_string());
3735+
}
3736+
Some(aliased_type)
3737+
}
37323738
t @ Type::Unpack(
37333739
box Type::Tuple(_) | box Type::TypeVarTuple(_) | box Type::Quantified(_),
37343740
) => Some(t),

pyrefly/lib/alt/special_calls.rs

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

pyrefly/lib/solver/solver.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -456,8 +456,13 @@ impl Solver {
456456
/// Simplify a type as much as we can.
457457
fn simplify_mut(&self, t: &mut Type) {
458458
t.transform_mut(&mut |x| {
459-
if let Type::Union(box (xs, _)) = x {
460-
*x = unions(mem::take(xs));
459+
if let Type::Union(box (xs, original_name)) = x {
460+
let mut merged = unions(mem::take(xs));
461+
// Preserve union display names during simplification
462+
if let Type::Union(box (_, name)) = &mut merged {
463+
*name = original_name.clone();
464+
}
465+
*x = merged;
461466
}
462467
if let Type::Intersect(y) = x {
463468
*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
@@ -806,3 +806,53 @@ x1: Spam1[int, str] = int
806806
x2: Spam2[int, str] = int
807807
"#,
808808
);
809+
810+
fn env_with_alias() -> TestEnv {
811+
TestEnv::one(
812+
"foo",
813+
r#"
814+
type TA = int | str
815+
816+
def f(x: TA) -> TA:
817+
return x
818+
"#,
819+
)
820+
}
821+
822+
testcase!(
823+
test_alias_union_name,
824+
env_with_alias(),
825+
r#"
826+
from foo import TA, f
827+
from typing import Callable
828+
829+
val1: int | str = 1
830+
val2: TA = 1
831+
832+
# Union names are only shown when nested in another type
833+
834+
f(object()) # E: Argument `object` is not assignable to parameter `x` with type `int | str` in function `foo.f`
835+
f(val1)
836+
f(val2)
837+
x1: TA = object() # E: `object` is not assignable to `int | str`
838+
x2: TA = val1
839+
x3: TA = val2
840+
c1: Callable[[int], int] = f # E: `(x: TA) -> TA` is not assignable to `(int) -> int`
841+
842+
# Union names are lost when flattened into another union
843+
844+
class C: pass
845+
def f2(x: TA | C) -> TA | C:
846+
return x
847+
848+
f2(object()) # E: Argument `object` is not assignable to parameter `x` with type `C | int | str` in function `f2`
849+
f2(val1)
850+
f2(val2)
851+
f2(C())
852+
x4: TA | C = object() # E: `object` is not assignable to `C | int | str`
853+
x5: TA | C = val1
854+
x6: TA | C = val2
855+
x7: TA | C = C()
856+
c2: Callable[[int], int] = f2 # E: `(x: C | int | str) -> C | int | str` is not assignable to `(int) -> int`
857+
"#,
858+
);

0 commit comments

Comments
 (0)