diff --git a/conformance/third_party/conformance.exp b/conformance/third_party/conformance.exp index 35161953f..6e8419e19 100644 --- a/conformance/third_party/conformance.exp +++ b/conformance/third_party/conformance.exp @@ -4,8 +4,8 @@ { "code": -2, "column": 9, - "concise_description": "`TypeAlias[GoodTypeAlias2, type[int | None]]` is not subscriptable", - "description": "`TypeAlias[GoodTypeAlias2, type[int | None]]` is not subscriptable", + "concise_description": "`GoodTypeAlias2` is not subscriptable", + "description": "`GoodTypeAlias2` is not subscriptable", "line": 67, "name": "unsupported-operation", "severity": "error", @@ -15,8 +15,8 @@ { "code": -2, "column": 9, - "concise_description": "`TypeAlias[GoodTypeAlias3, type[list[int | None]]]` is not subscriptable", - "description": "`TypeAlias[GoodTypeAlias3, type[list[int | None]]]` is not subscriptable", + "concise_description": "`GoodTypeAlias3` is not subscriptable", + "description": "`GoodTypeAlias3` is not subscriptable", "line": 68, "name": "unsupported-operation", "severity": "error", @@ -224,8 +224,8 @@ { "code": -2, "column": 5, - "concise_description": "`TypeAlias[ListAlias, type[list[Unknown]]]` is not subscriptable", - "description": "`TypeAlias[ListAlias, type[list[Unknown]]]` is not subscriptable", + "concise_description": "`ListAlias` is not subscriptable", + "description": "`ListAlias` is not subscriptable", "line": 100, "name": "unsupported-operation", "severity": "error", @@ -235,8 +235,8 @@ { "code": -2, "column": 6, - "concise_description": "Expected a callable, got `TypeAlias[ListOrSetAlias, type[list[Unknown] | set[Unknown]]]`", - "description": "Expected a callable, got `TypeAlias[ListOrSetAlias, type[list[Unknown] | set[Unknown]]]`", + "concise_description": "Expected a callable, got `ListOrSetAlias`", + "description": "Expected a callable, got `ListOrSetAlias`", "line": 101, "name": "not-callable", "severity": "error", @@ -246,8 +246,8 @@ { "code": -2, "column": 5, - "concise_description": "`TypeAlias[ListOrSetAlias, type[list[Unknown] | set[Unknown]]]` is not subscriptable", - "description": "`TypeAlias[ListOrSetAlias, type[list[Unknown] | set[Unknown]]]` is not subscriptable", + "concise_description": "`ListOrSetAlias` is not subscriptable", + "description": "`ListOrSetAlias` is not subscriptable", "line": 102, "name": "unsupported-operation", "severity": "error", @@ -628,8 +628,8 @@ { "code": -2, "column": 1, - "concise_description": "Expected a callable, got `TypeAlias[GoodAlias1, type[int]]`", - "description": "Expected a callable, got `TypeAlias[GoodAlias1, type[int]]`", + "concise_description": "Expected a callable, got `GoodAlias1`", + "description": "Expected a callable, got `GoodAlias1`", "line": 19, "name": "not-callable", "severity": "error", @@ -9877,8 +9877,8 @@ { "code": -2, "column": 1, - "concise_description": "TODO: Expr::attr_infer_for_type attribute base undefined for type: TypeAlias[TA1, type[type[Unknown]]] (trying to access unknown)", - "description": "TODO: Expr::attr_infer_for_type attribute base undefined for type: TypeAlias[TA1, type[type[Unknown]]] (trying to access unknown)", + "concise_description": "TODO: Expr::attr_infer_for_type attribute base undefined for type: TA1 (trying to access unknown)", + "description": "TODO: Expr::attr_infer_for_type attribute base undefined for type: TA1 (trying to access unknown)", "line": 143, "name": "missing-attribute", "severity": "error", @@ -9888,8 +9888,8 @@ { "code": -2, "column": 1, - "concise_description": "TODO: Expr::attr_infer_for_type attribute base undefined for type: TypeAlias[TA2, type[type[Any]]] (trying to access unknown)", - "description": "TODO: Expr::attr_infer_for_type attribute base undefined for type: TypeAlias[TA2, type[type[Any]]] (trying to access unknown)", + "concise_description": "TODO: Expr::attr_infer_for_type attribute base undefined for type: TA2 (trying to access unknown)", + "description": "TODO: Expr::attr_infer_for_type attribute base undefined for type: TA2 (trying to access unknown)", "line": 144, "name": "missing-attribute", "severity": "error", @@ -9910,8 +9910,8 @@ { "code": -2, "column": 1, - "concise_description": "TODO: Expr::attr_infer_for_type attribute base undefined for type: TypeAlias[TA4, type[type[Any]]] (trying to access unknown)", - "description": "TODO: Expr::attr_infer_for_type attribute base undefined for type: TypeAlias[TA4, type[type[Any]]] (trying to access unknown)", + "concise_description": "TODO: Expr::attr_infer_for_type attribute base undefined for type: TA4 (trying to access unknown)", + "description": "TODO: Expr::attr_infer_for_type attribute base undefined for type: TA4 (trying to access unknown)", "line": 146, "name": "missing-attribute", "severity": "error", @@ -11547,4 +11547,4 @@ "stop_line": 40 } ] -} \ No newline at end of file +} diff --git a/crates/pyrefly_types/src/display.rs b/crates/pyrefly_types/src/display.rs index ce0ef4418..bfa7440be 100644 --- a/crates/pyrefly_types/src/display.rs +++ b/crates/pyrefly_types/src/display.rs @@ -35,7 +35,10 @@ use crate::types::NeverStyle; use crate::types::SuperObj; use crate::types::TArgs; use crate::types::TParam; +use crate::types::TParams; use crate::types::Type; +use crate::types::TypeAlias; +use crate::types::TypeAliasStyle; /// Information about the qnames we have seen. /// Set to None to indicate we have seen different values, or Some if they are all the same. @@ -87,6 +90,7 @@ pub struct TypeDisplayContext<'a> { /// Should we display for IDE Hover? This makes type names more readable but less precise. hover: bool, always_display_module_name: bool, + expand_type_aliases: bool, } impl<'a> TypeDisplayContext<'a> { @@ -149,6 +153,11 @@ impl<'a> TypeDisplayContext<'a> { self.hover = true; } + /// Configure whether to expand type aliases when rendering. + pub fn set_expand_type_aliases(&mut self, expand: bool) { + self.expand_type_aliases = expand; + } + pub fn display(&'a self, t: &'a Type) -> impl Display + 'a { Fmt(|f| self.fmt(t, f)) } @@ -489,7 +498,7 @@ impl<'a> TypeDisplayContext<'a> { body: Forallable::TypeAlias(ta), }) => { if is_toplevel { - ta.fmt_with_type(f, &|t| self.display_internal(t), Some(tparams)) + self.fmt_type_alias(ta, Some(tparams.as_ref()), f) } else { write!(f, "{}", *ta.name) } @@ -536,7 +545,7 @@ impl<'a> TypeDisplayContext<'a> { }, Type::TypeAlias(ta) => { if is_toplevel { - ta.fmt_with_type(f, &|t| self.display_internal(t), None) + self.fmt_type_alias(ta, None, f) } else { write!(f, "{}", *ta.name) } @@ -561,6 +570,35 @@ impl<'a> TypeDisplayContext<'a> { Type::None => write!(f, "None"), } } + + fn fmt_type_alias( + &self, + ta: &TypeAlias, + tparams: Option<&TParams>, + f: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + let should_expand = + self.expand_type_aliases || matches!(ta.style, TypeAliasStyle::LegacyImplicit); + if should_expand { + ta.fmt_with_type(f, &|t| self.display_internal(t), tparams) + } else { + self.fmt_type_alias_name(ta, tparams, f) + } + } + + fn fmt_type_alias_name( + &self, + ta: &TypeAlias, + tparams: Option<&TParams>, + f: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + if let Some(tparams) = tparams { + if !tparams.is_empty() { + return write!(f, "{}[{}]", *ta.name, commas_iter(|| tparams.iter()),); + } + } + write!(f, "{}", *ta.name) + } } impl Display for Type { diff --git a/pyrefly/lib/test/type_alias.rs b/pyrefly/lib/test/type_alias.rs index eecf50951..de23dd8fd 100644 --- a/pyrefly/lib/test/type_alias.rs +++ b/pyrefly/lib/test/type_alias.rs @@ -788,7 +788,7 @@ type Spam1[T1, T2] = T2 | type[T1] Spam2: TypeAlias = Spam1[T1, T2] x1: Spam1[int, str] = int -x2: Spam2[int, str] = int # E: `TypeAlias[Spam2, type[T2 | type[T1]]]` is not subscriptable +x2: Spam2[int, str] = int # E: `Spam2` is not subscriptable "#, );