[ty] Improve the display of various special-form types (#21775)

This commit is contained in:
Alex Waygood
2025-12-03 21:19:59 +00:00
committed by GitHub
parent 8ebecb2a88
commit 14fce0d440
20 changed files with 220 additions and 178 deletions

View File

@@ -7331,7 +7331,7 @@ impl<'db> Type<'db> {
| SpecialFormType::Union
| SpecialFormType::Intersection => Err(InvalidTypeExpressionError {
invalid_expressions: smallvec::smallvec_inline![
InvalidTypeExpression::RequiresArguments(*self)
InvalidTypeExpression::RequiresArguments(*special_form)
],
fallback_type: Type::unknown(),
}),
@@ -7357,7 +7357,7 @@ impl<'db> Type<'db> {
| SpecialFormType::Unpack
| SpecialFormType::CallableTypeOf => Err(InvalidTypeExpressionError {
invalid_expressions: smallvec::smallvec_inline![
InvalidTypeExpression::RequiresOneArgument(*self)
InvalidTypeExpression::RequiresOneArgument(*special_form)
],
fallback_type: Type::unknown(),
}),
@@ -7365,7 +7365,7 @@ impl<'db> Type<'db> {
SpecialFormType::Annotated | SpecialFormType::Concatenate => {
Err(InvalidTypeExpressionError {
invalid_expressions: smallvec::smallvec_inline![
InvalidTypeExpression::RequiresTwoArguments(*self)
InvalidTypeExpression::RequiresTwoArguments(*special_form)
],
fallback_type: Type::unknown(),
})
@@ -9087,11 +9087,11 @@ impl<'db> InvalidTypeExpressionError<'db> {
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, get_size2::GetSize)]
enum InvalidTypeExpression<'db> {
/// Some types always require exactly one argument when used in a type expression
RequiresOneArgument(Type<'db>),
RequiresOneArgument(SpecialFormType),
/// Some types always require at least one argument when used in a type expression
RequiresArguments(Type<'db>),
RequiresArguments(SpecialFormType),
/// Some types always require at least two arguments when used in a type expression
RequiresTwoArguments(Type<'db>),
RequiresTwoArguments(SpecialFormType),
/// The `Protocol` class is invalid in type expressions
Protocol,
/// Same for `Generic`
@@ -9131,20 +9131,17 @@ impl<'db> InvalidTypeExpression<'db> {
impl std::fmt::Display for Display<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.error {
InvalidTypeExpression::RequiresOneArgument(ty) => write!(
InvalidTypeExpression::RequiresOneArgument(special_form) => write!(
f,
"`{ty}` requires exactly one argument when used in a type expression",
ty = ty.display(self.db)
"`{special_form}` requires exactly one argument when used in a type expression",
),
InvalidTypeExpression::RequiresArguments(ty) => write!(
InvalidTypeExpression::RequiresArguments(special_form) => write!(
f,
"`{ty}` requires at least one argument when used in a type expression",
ty = ty.display(self.db)
"`{special_form}` requires at least one argument when used in a type expression",
),
InvalidTypeExpression::RequiresTwoArguments(ty) => write!(
InvalidTypeExpression::RequiresTwoArguments(special_form) => write!(
f,
"`{ty}` requires at least two arguments when used in a type expression",
ty = ty.display(self.db)
"`{special_form}` requires at least two arguments when used in a type expression",
),
InvalidTypeExpression::Protocol => {
f.write_str("`typing.Protocol` is not allowed in type expressions")

View File

@@ -2998,11 +2998,10 @@ pub(crate) fn report_invalid_arguments_to_annotated(
let Some(builder) = context.report_lint(&INVALID_TYPE_FORM, subscript) else {
return;
};
builder.into_diagnostic(format_args!(
"Special form `{}` expected at least 2 arguments \
builder.into_diagnostic(
"Special form `typing.Annotated` expected at least 2 arguments \
(one type and at least one metadata element)",
SpecialFormType::Annotated
));
);
}
pub(crate) fn report_invalid_argument_number_to_special_form(
@@ -3103,8 +3102,7 @@ pub(crate) fn report_invalid_arguments_to_callable(
return;
};
builder.into_diagnostic(format_args!(
"Special form `{}` expected exactly two arguments (parameter types and return type)",
SpecialFormType::Callable
"Special form `typing.Callable` expected exactly two arguments (parameter types and return type)",
));
}

View File

@@ -696,7 +696,8 @@ impl<'db> FmtDetailed<'db> for DisplayRepresentation<'db> {
),
},
Type::SpecialForm(special_form) => {
write!(f.with_type(self.ty), "{special_form}")
f.set_invalid_syntax();
write!(f.with_type(self.ty), "<special form '{special_form}'>")
}
Type::KnownInstance(known_instance) => known_instance
.display_with(self.db, self.settings.clone())
@@ -2173,16 +2174,24 @@ impl<'db> FmtDetailed<'db> for DisplayKnownInstanceRepr<'db> {
let ty = Type::KnownInstance(self.known_instance);
match self.known_instance {
KnownInstanceType::SubscriptedProtocol(generic_context) => {
f.set_invalid_syntax();
f.write_str("<special form '")?;
f.with_type(ty).write_str("typing.Protocol")?;
f.write_str(&generic_context.display(self.db).to_string())
f.write_str(&generic_context.display(self.db).to_string())?;
f.write_str("'>")
}
KnownInstanceType::SubscriptedGeneric(generic_context) => {
f.set_invalid_syntax();
f.write_str("<special form '")?;
f.with_type(ty).write_str("typing.Generic")?;
f.write_str(&generic_context.display(self.db).to_string())
f.write_str(&generic_context.display(self.db).to_string())?;
f.write_str("'>")
}
KnownInstanceType::TypeAliasType(alias) => {
if let Some(specialization) = alias.specialization(self.db) {
f.write_str(alias.name(self.db))?;
f.set_invalid_syntax();
f.write_str("<type alias '")?;
f.with_type(ty).write_str(alias.name(self.db))?;
f.write_str(
&specialization
.display_short(
@@ -2191,7 +2200,8 @@ impl<'db> FmtDetailed<'db> for DisplayKnownInstanceRepr<'db> {
DisplaySettings::default(),
)
.to_string(),
)
)?;
f.write_str("'>")
} else {
f.with_type(ty).write_str("typing.TypeAliasType")
}
@@ -2201,9 +2211,9 @@ impl<'db> FmtDetailed<'db> for DisplayKnownInstanceRepr<'db> {
// 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")
f.with_type(ty).write_str("typing.ParamSpec")
} else {
f.write_str("typing.TypeVar")
f.with_type(ty).write_str("typing.TypeVar")
}
}
KnownInstanceType::Deprecated(_) => f.write_str("warnings.deprecated"),
@@ -2226,22 +2236,56 @@ impl<'db> FmtDetailed<'db> for DisplayKnownInstanceRepr<'db> {
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(_) => {
KnownInstanceType::UnionType(union) => {
f.set_invalid_syntax();
f.write_str("<typing.Literal special form>")
f.write_char('<')?;
f.with_type(ty).write_str("types.UnionType")?;
f.write_str(" special form")?;
if let Ok(ty) = union.union_type(self.db) {
write!(f, " '{}'", ty.display(self.db))?;
}
f.write_char('>')
}
KnownInstanceType::Annotated(_) => {
KnownInstanceType::Literal(inner) => {
f.set_invalid_syntax();
f.write_str("<typing.Annotated special form>")
write!(
f,
"<special form '{}'>",
inner.inner(self.db).display(self.db)
)
}
KnownInstanceType::TypeGenericAlias(_) | KnownInstanceType::Callable(_) => {
f.with_type(ty).write_str("GenericAlias")
KnownInstanceType::Annotated(inner) => {
f.set_invalid_syntax();
f.write_str("<special form '")?;
f.with_type(ty).write_str("typing.Annotated")?;
write!(
f,
"[{}, <metadata>]'>",
inner.inner(self.db).display(self.db)
)
}
KnownInstanceType::Callable(callable) => {
f.set_invalid_syntax();
f.write_char('<')?;
f.with_type(ty).write_str("typing.Callable")?;
write!(f, " special form '{}'>", callable.display(self.db))
}
KnownInstanceType::TypeGenericAlias(inner) => {
f.set_invalid_syntax();
f.write_str("<special form '")?;
write!(
f.with_type(ty),
"type[{}]",
inner.inner(self.db).display(self.db)
)?;
f.write_str("'>")
}
KnownInstanceType::LiteralStringAlias(_) => f.write_str("str"),
KnownInstanceType::NewType(declaration) => {
f.set_invalid_syntax();
write!(f, "<NewType pseudo-class '{}'>", declaration.name(self.db))
f.write_str("<NewType pseudo-class '")?;
f.with_type(ty).write_str(declaration.name(self.db))?;
f.write_str("'>")
}
}
}