diff --git a/crates/ty_ide/src/inlay_hints.rs b/crates/ty_ide/src/inlay_hints.rs index b905e6c066..a661ff1b9c 100644 --- a/crates/ty_ide/src/inlay_hints.rs +++ b/crates/ty_ide/src/inlay_hints.rs @@ -6294,6 +6294,22 @@ mod tests { "); } + #[test] + fn test_literal_type_alias_inlay_hint() { + let mut test = inlay_hint_test( + " + from typing import Literal + + a = Literal['a', 'b', 'c']", + ); + + assert_snapshot!(test.inlay_hints(), @r" + from typing import Literal + + a[: ] = Literal['a', 'b', 'c'] + "); + } + struct InlayHintLocationDiagnostic { source: FileRange, target: FileRange, diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index b90ac4b01a..f6c556f639 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -51,7 +51,6 @@ use crate::types::constraints::{ }; use crate::types::context::{LintDiagnosticGuard, LintDiagnosticGuardBuilder}; use crate::types::diagnostic::{INVALID_AWAIT, INVALID_TYPE_FORM, UNSUPPORTED_BOOL_CONVERSION}; -use crate::types::display::TupleSpecialization; pub use crate::types::display::{DisplaySettings, TypeDetail, TypeDisplayDetails}; use crate::types::enums::{enum_metadata, is_single_member_enum}; use crate::types::function::{ @@ -8233,97 +8232,7 @@ impl<'db> KnownInstanceType<'db> { /// Return the repr of the symbol at runtime fn repr(self, db: &'db dyn Db) -> impl std::fmt::Display + 'db { - struct KnownInstanceRepr<'db> { - known_instance: KnownInstanceType<'db>, - db: &'db dyn Db, - } - - impl std::fmt::Display for KnownInstanceRepr<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self.known_instance { - KnownInstanceType::SubscriptedProtocol(generic_context) => { - f.write_str("typing.Protocol")?; - generic_context.display(self.db).fmt(f) - } - KnownInstanceType::SubscriptedGeneric(generic_context) => { - f.write_str("typing.Generic")?; - generic_context.display(self.db).fmt(f) - } - KnownInstanceType::TypeAliasType(alias) => { - if let Some(specialization) = alias.specialization(self.db) { - f.write_str(alias.name(self.db))?; - specialization - .display_short( - self.db, - TupleSpecialization::No, - DisplaySettings::default(), - ) - .fmt(f) - } else { - f.write_str("typing.TypeAliasType") - } - } - // This is a legacy `TypeVar` _outside_ of any generic class or function, so we render - // it as an instance of `typing.TypeVar`. Inside of a generic class or function, we'll - // have a `Type::TypeVar(_)`, which is rendered as the typevar's name. - KnownInstanceType::TypeVar(typevar_instance) => { - if typevar_instance.kind(self.db).is_paramspec() { - f.write_str("typing.ParamSpec") - } else { - f.write_str("typing.TypeVar") - } - } - KnownInstanceType::Deprecated(_) => f.write_str("warnings.deprecated"), - KnownInstanceType::Field(field) => { - f.write_str("dataclasses.Field")?; - if let Some(default_ty) = field.default_type(self.db) { - write!(f, "[{}]", default_ty.display(self.db))?; - } - Ok(()) - } - KnownInstanceType::ConstraintSet(tracked_set) => { - let constraints = tracked_set.constraints(self.db); - write!( - f, - "ty_extensions.ConstraintSet[{}]", - constraints.display(self.db) - ) - } - KnownInstanceType::GenericContext(generic_context) => { - write!( - f, - "ty_extensions.GenericContext{}", - generic_context.display_full(self.db) - ) - } - KnownInstanceType::Specialization(specialization) => { - // Normalize for consistent output across CI platforms - write!( - f, - "ty_extensions.Specialization{}", - specialization.display_full(self.db) - ) - } - KnownInstanceType::UnionType(_) => f.write_str("types.UnionType"), - KnownInstanceType::Literal(_) => f.write_str(""), - KnownInstanceType::Annotated(_) => { - f.write_str("") - } - KnownInstanceType::TypeGenericAlias(_) | KnownInstanceType::Callable(_) => { - f.write_str("GenericAlias") - } - KnownInstanceType::LiteralStringAlias(_) => f.write_str("str"), - KnownInstanceType::NewType(declaration) => { - write!(f, "", declaration.name(self.db)) - } - } - } - } - - KnownInstanceRepr { - known_instance: self, - db, - } + self.display_with(db, DisplaySettings::default()) } } @@ -8368,6 +8277,10 @@ impl DynamicType<'_> { Self::Any } } + + pub(crate) fn is_todo(&self) -> bool { + matches!(self, Self::Todo(_) | Self::TodoUnpack) + } } impl std::fmt::Display for DynamicType<'_> { diff --git a/crates/ty_python_semantic/src/types/display.rs b/crates/ty_python_semantic/src/types/display.rs index 78bd91b795..10912385bf 100644 --- a/crates/ty_python_semantic/src/types/display.rs +++ b/crates/ty_python_semantic/src/types/display.rs @@ -22,8 +22,8 @@ use crate::types::tuple::TupleSpec; use crate::types::visitor::TypeVisitor; use crate::types::{ BoundTypeVarIdentity, CallableType, IntersectionType, KnownBoundMethodType, KnownClass, - MaterializationKind, Protocol, ProtocolInstanceType, SpecialFormType, StringLiteralType, - SubclassOfInner, Type, UnionType, WrapperDescriptorKind, visitor, + KnownInstanceType, MaterializationKind, Protocol, ProtocolInstanceType, SpecialFormType, + StringLiteralType, SubclassOfInner, Type, UnionType, WrapperDescriptorKind, visitor, }; /// Settings for displaying types and signatures @@ -582,7 +582,12 @@ impl Display for DisplayRepresentation<'_> { impl<'db> FmtDetailed<'db> for DisplayRepresentation<'db> { fn fmt_detailed(&self, f: &mut TypeWriter<'_, '_, 'db>) -> fmt::Result { match self.ty { - Type::Dynamic(dynamic) => write!(f.with_type(self.ty), "{dynamic}"), + Type::Dynamic(dynamic) => { + if dynamic.is_todo() { + f.set_invalid_syntax(); + } + write!(f.with_type(self.ty), "{dynamic}") + } Type::Never => f.with_type(self.ty).write_str("Never"), Type::NominalInstance(instance) => { let class = instance.class(self.db); @@ -687,9 +692,9 @@ impl<'db> FmtDetailed<'db> for DisplayRepresentation<'db> { Type::SpecialForm(special_form) => { write!(f.with_type(self.ty), "{special_form}") } - Type::KnownInstance(known_instance) => { - write!(f.with_type(self.ty), "{}", known_instance.repr(self.db)) - } + Type::KnownInstance(known_instance) => known_instance + .display_with(self.db, self.settings.clone()) + .fmt_detailed(f), Type::FunctionLiteral(function) => function .display_with(self.db, self.settings.clone()) .fmt_detailed(f), @@ -2133,6 +2138,111 @@ impl Display for DisplayStringLiteralType<'_> { } } +pub(crate) struct DisplayKnownInstanceRepr<'db> { + pub(crate) known_instance: KnownInstanceType<'db>, + pub(crate) db: &'db dyn Db, +} + +impl<'db> KnownInstanceType<'db> { + pub(crate) fn display_with( + self, + db: &'db dyn Db, + _settings: DisplaySettings<'db>, + ) -> DisplayKnownInstanceRepr<'db> { + DisplayKnownInstanceRepr { + known_instance: self, + db, + } + } +} + +impl Display for DisplayKnownInstanceRepr<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + self.fmt_detailed(&mut TypeWriter::Formatter(f)) + } +} + +impl<'db> FmtDetailed<'db> for DisplayKnownInstanceRepr<'db> { + fn fmt_detailed(&self, f: &mut TypeWriter<'_, '_, 'db>) -> fmt::Result { + let ty = Type::KnownInstance(self.known_instance); + match self.known_instance { + KnownInstanceType::SubscriptedProtocol(generic_context) => { + f.with_type(ty).write_str("typing.Protocol")?; + f.write_str(&generic_context.display(self.db).to_string()) + } + KnownInstanceType::SubscriptedGeneric(generic_context) => { + f.with_type(ty).write_str("typing.Generic")?; + f.write_str(&generic_context.display(self.db).to_string()) + } + KnownInstanceType::TypeAliasType(alias) => { + if let Some(specialization) = alias.specialization(self.db) { + f.write_str(alias.name(self.db))?; + f.write_str( + &specialization + .display_short( + self.db, + TupleSpecialization::No, + DisplaySettings::default(), + ) + .to_string(), + ) + } else { + f.with_type(ty).write_str("typing.TypeAliasType") + } + } + // This is a legacy `TypeVar` _outside_ of any generic class or function, so we render + // it as an instance of `typing.TypeVar`. Inside of a generic class or function, we'll + // have a `Type::TypeVar(_)`, which is rendered as the typevar's name. + KnownInstanceType::TypeVar(typevar_instance) => { + if typevar_instance.kind(self.db).is_paramspec() { + f.write_str("typing.ParamSpec") + } else { + f.write_str("typing.TypeVar") + } + } + KnownInstanceType::Deprecated(_) => f.write_str("warnings.deprecated"), + KnownInstanceType::Field(field) => { + f.with_type(ty).write_str("dataclasses.Field")?; + if let Some(default_ty) = field.default_type(self.db) { + write!(f, "[{}]", default_ty.display(self.db))?; + } + Ok(()) + } + KnownInstanceType::ConstraintSet(tracked_set) => { + let constraints = tracked_set.constraints(self.db); + f.with_type(ty).write_str("ty_extensions.ConstraintSet")?; + write!(f, "[{}]", constraints.display(self.db)) + } + KnownInstanceType::GenericContext(generic_context) => { + f.with_type(ty).write_str("ty_extensions.GenericContext")?; + write!(f, "{}", generic_context.display_full(self.db)) + } + KnownInstanceType::Specialization(specialization) => { + // Normalize for consistent output across CI platforms + f.with_type(ty).write_str("ty_extensions.Specialization")?; + write!(f, "{}", specialization.display_full(self.db)) + } + KnownInstanceType::UnionType(_) => f.with_type(ty).write_str("types.UnionType"), + KnownInstanceType::Literal(_) => { + f.set_invalid_syntax(); + f.write_str("") + } + KnownInstanceType::Annotated(_) => { + f.set_invalid_syntax(); + f.write_str("") + } + KnownInstanceType::TypeGenericAlias(_) | KnownInstanceType::Callable(_) => { + f.with_type(ty).write_str("GenericAlias") + } + KnownInstanceType::LiteralStringAlias(_) => f.write_str("str"), + KnownInstanceType::NewType(declaration) => { + f.set_invalid_syntax(); + write!(f, "", declaration.name(self.db)) + } + } + } +} + #[cfg(test)] mod tests { use insta::assert_snapshot;