[ty] Add more TypeDetails to the display code (#21541)

As far as I know this change is largely non-functional, largely because
of https://github.com/astral-sh/ty/issues/1601

It's possible some of these like `Type::KnownInstance` produce something
useful sometimes. `LiteralString` is a new introduction, although its
goto-type jumps to `str` which is a bit sad (considering that part of
the SpecialForm discourse for now).

Also wrt the generics testing followup: turns out the snapshot tests
were full of those already.
This commit is contained in:
Aria Desires 2025-11-20 12:08:59 -05:00 committed by GitHub
parent 0761ea42d9
commit 78ce17ce8f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 85 additions and 13 deletions

View File

@ -4143,6 +4143,43 @@ mod tests {
");
}
#[test]
fn test_literal_string() {
let mut test = inlay_hint_test(
r#"
from typing import LiteralString
def my_func(x: LiteralString):
y = x
my_func(x="hello")"#,
);
assert_snapshot!(test.inlay_hints(), @r#"
from typing import LiteralString
def my_func(x: LiteralString):
y[: LiteralString] = x
my_func(x="hello")
---------------------------------------------
info[inlay-hint-location]: Inlay Hint Target
--> stdlib/builtins.pyi:915:7
|
914 | @disjoint_base
915 | class str(Sequence[str]):
| ^^^
916 | """str(object='') -> str
917 | str(bytes_or_buffer[, encoding[, errors]]) -> str
|
info: Source
--> main2.py:4:9
|
2 | from typing import LiteralString
3 | def my_func(x: LiteralString):
4 | y[: LiteralString] = x
| ^^^^^^^^^^^^^
5 | my_func(x="hello")
|
"#);
}
#[test]
fn test_complex_parameter_combinations() {
let mut test = inlay_hint_test(

View File

@ -24,9 +24,9 @@ use crate::types::signatures::{CallableSignature, Parameter, Parameters, Signatu
use crate::types::tuple::TupleSpec;
use crate::types::visitor::TypeVisitor;
use crate::types::{
BoundTypeVarIdentity, CallableType, IntersectionType, KnownBoundMethodType, KnownClass,
MaterializationKind, Protocol, ProtocolInstanceType, StringLiteralType, SubclassOfInner, Type,
UnionType, WrapperDescriptorKind, visitor,
BoundTypeVarIdentity, CallableType, DynamicType, IntersectionType, KnownBoundMethodType,
KnownClass, MaterializationKind, Protocol, ProtocolInstanceType, SpecialFormType,
StringLiteralType, SubclassOfInner, Type, UnionType, WrapperDescriptorKind, visitor,
};
use ruff_db::parsed::parsed_module;
@ -470,7 +470,11 @@ impl<'db> FmtDetailed<'db> for DisplayType<'db> {
| Type::StringLiteral(_)
| Type::BytesLiteral(_)
| Type::EnumLiteral(_) => {
f.write_str("Literal[")?;
f.with_detail(TypeDetail::Type(Type::SpecialForm(
SpecialFormType::Literal,
)))
.write_str("Literal")?;
f.write_char('[')?;
representation.fmt_detailed(f)?;
f.write_str("]")
}
@ -608,7 +612,16 @@ 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, "{dynamic}"),
Type::Dynamic(dynamic) => {
if let DynamicType::Any = dynamic {
write!(
f.with_detail(TypeDetail::Type(Type::SpecialForm(SpecialFormType::Any))),
"{dynamic}"
)
} else {
write!(f, "{dynamic}")
}
}
Type::Never => f.with_detail(TypeDetail::Type(self.ty)).write_str("Never"),
Type::NominalInstance(instance) => {
let class = instance.class(self.db);
@ -638,7 +651,12 @@ impl<'db> FmtDetailed<'db> for DisplayRepresentation<'db> {
.fmt_detailed(f),
},
Protocol::Synthesized(synthetic) => {
f.write_str("<Protocol with members ")?;
f.write_char('<')?;
f.with_detail(TypeDetail::Type(Type::SpecialForm(
SpecialFormType::Protocol,
)))
.write_str("Protocol")?;
f.write_str(" with members ")?;
let interface = synthetic.interface();
let member_list = interface.members(self.db);
let num_members = member_list.len();
@ -687,8 +705,14 @@ impl<'db> FmtDetailed<'db> for DisplayRepresentation<'db> {
}
SubclassOfInner::Dynamic(dynamic) => write!(f, "type[{dynamic}]"),
},
Type::SpecialForm(special_form) => write!(f, "{special_form}"),
Type::KnownInstance(known_instance) => write!(f, "{}", known_instance.repr(self.db)),
Type::SpecialForm(special_form) => {
write!(f.with_detail(TypeDetail::Type(self.ty)), "{special_form}")
}
Type::KnownInstance(known_instance) => write!(
f.with_detail(TypeDetail::Type(self.ty)),
"{}",
known_instance.repr(self.db)
),
Type::FunctionLiteral(function) => function
.display_with(self.db, self.settings.clone())
.fmt_detailed(f),
@ -724,7 +748,10 @@ impl<'db> FmtDetailed<'db> for DisplayRepresentation<'db> {
signatures => {
// TODO: How to display overloads?
if !self.settings.multiline {
f.write_str("Overload[")?;
// TODO: This should ideally have a TypeDetail but we actually
// don't have a type for @overload (we just detect the decorator)
f.write_str("Overload")?;
f.write_char('[')?;
}
let separator = if self.settings.multiline { "\n" } else { ", " };
let mut join = f.join(separator);
@ -815,7 +842,9 @@ impl<'db> FmtDetailed<'db> for DisplayRepresentation<'db> {
Type::StringLiteral(string) => {
write!(f, "{}", string.display_with(self.db, self.settings.clone()))
}
Type::LiteralString => f.write_str("LiteralString"),
Type::LiteralString => f
.with_detail(TypeDetail::Type(self.ty))
.write_str("LiteralString"),
Type::BytesLiteral(bytes) => {
let escape = AsciiEscape::with_preferred_quote(bytes.value(self.db), Quote::Double);
@ -831,8 +860,12 @@ impl<'db> FmtDetailed<'db> for DisplayRepresentation<'db> {
Type::TypeVar(bound_typevar) => {
write!(f, "{}", bound_typevar.identity(self.db).display(self.db))
}
Type::AlwaysTruthy => f.write_str("AlwaysTruthy"),
Type::AlwaysFalsy => f.write_str("AlwaysFalsy"),
Type::AlwaysTruthy => f
.with_detail(TypeDetail::Type(self.ty))
.write_str("AlwaysTruthy"),
Type::AlwaysFalsy => f
.with_detail(TypeDetail::Type(self.ty))
.write_str("AlwaysFalsy"),
Type::BoundSuper(bound_super) => {
f.write_str("<super: ")?;
Type::from(bound_super.pivot_class(self.db))
@ -845,7 +878,9 @@ impl<'db> FmtDetailed<'db> for DisplayRepresentation<'db> {
f.write_str(">")
}
Type::TypeIs(type_is) => {
f.write_str("TypeIs[")?;
f.with_detail(TypeDetail::Type(Type::SpecialForm(SpecialFormType::TypeIs)))
.write_str("TypeIs")?;
f.write_char('[')?;
type_is
.return_type(self.db)
.display_with(self.db, self.settings.singleline())