[ty] Improve disambiguation of class names in diagnostics (#20603)

This commit is contained in:
Alex Waygood 2025-09-29 11:43:11 +01:00 committed by GitHub
parent 81f43a1fc8
commit 3f640dacd4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 243 additions and 154 deletions

View file

@ -43,8 +43,7 @@ import b
df: a.DataFrame = b.DataFrame() # error: [invalid-assignment] "Object of type `b.DataFrame` is not assignable to `a.DataFrame`" df: a.DataFrame = b.DataFrame() # error: [invalid-assignment] "Object of type `b.DataFrame` is not assignable to `a.DataFrame`"
def _(dfs: list[b.DataFrame]): def _(dfs: list[b.DataFrame]):
# TODO should be"Object of type `list[b.DataFrame]` is not assignable to `list[a.DataFrame]` # error: [invalid-assignment] "Object of type `list[b.DataFrame]` is not assignable to `list[a.DataFrame]`"
# error: [invalid-assignment] "Object of type `list[DataFrame]` is not assignable to `list[DataFrame]`"
dataframes: list[a.DataFrame] = dfs dataframes: list[a.DataFrame] = dfs
``` ```
@ -228,3 +227,21 @@ from typing import TypedDict
class Person(TypedDict): class Person(TypedDict):
name: bytes name: bytes
``` ```
## Tuple specializations
`module.py`:
```py
class Model: ...
```
```py
class Model: ...
def get_models_tuple() -> tuple[Model]:
from module import Model
# error: [invalid-return-type] "Return type does not match returned value: expected `tuple[mdtest_snippet.Model]`, found `tuple[module.Model]`"
return (Model(),)
```

View file

@ -339,7 +339,7 @@ class A: ...
def f(x: A): def f(x: A):
# TODO: no error # TODO: no error
# error: [invalid-assignment] "Object of type `A | A` is not assignable to `A`" # error: [invalid-assignment] "Object of type `mdtest_snippet.A | mdtest_snippet.A` is not assignable to `mdtest_snippet.A`"
x = A() x = A()
``` ```

View file

@ -7065,7 +7065,11 @@ impl<'db> KnownInstanceType<'db> {
if let Some(specialization) = alias.specialization(self.db) { if let Some(specialization) = alias.specialization(self.db) {
f.write_str(alias.name(self.db))?; f.write_str(alias.name(self.db))?;
specialization specialization
.display_short(self.db, TupleSpecialization::No) .display_short(
self.db,
TupleSpecialization::No,
DisplaySettings::default(),
)
.fmt(f) .fmt(f)
} else { } else {
f.write_str("typing.TypeAliasType") f.write_str("typing.TypeAliasType")

View file

@ -1986,7 +1986,7 @@ pub(super) fn report_invalid_assignment<'db>(
target_ty, target_ty,
format_args!( format_args!(
"Object of type `{}` is not assignable to `{}`", "Object of type `{}` is not assignable to `{}`",
source_ty.display_with(context.db(), settings), source_ty.display_with(context.db(), settings.clone()),
target_ty.display_with(context.db(), settings) target_ty.display_with(context.db(), settings)
), ),
); );
@ -2068,8 +2068,8 @@ pub(super) fn report_invalid_return_type(
let mut diag = builder.into_diagnostic("Return type does not match returned value"); let mut diag = builder.into_diagnostic("Return type does not match returned value");
diag.set_primary_message(format_args!( diag.set_primary_message(format_args!(
"expected `{expected_ty}`, found `{actual_ty}`", "expected `{expected_ty}`, found `{actual_ty}`",
expected_ty = expected_ty.display_with(context.db(), settings), expected_ty = expected_ty.display_with(context.db(), settings.clone()),
actual_ty = actual_ty.display_with(context.db(), settings), actual_ty = actual_ty.display_with(context.db(), settings.clone()),
)); ));
diag.annotate( diag.annotate(
Annotation::secondary(return_type_span).message(format_args!( Annotation::secondary(return_type_span).message(format_args!(

View file

@ -1,11 +1,15 @@
//! Display implementations for types. //! Display implementations for types.
use std::cell::RefCell;
use std::collections::hash_map::Entry;
use std::fmt::{self, Display, Formatter, Write}; use std::fmt::{self, Display, Formatter, Write};
use std::rc::Rc;
use ruff_db::display::FormatterJoinExtension; use ruff_db::display::FormatterJoinExtension;
use ruff_python_ast::str::{Quote, TripleQuotes}; use ruff_python_ast::str::{Quote, TripleQuotes};
use ruff_python_literal::escape::AsciiEscape; use ruff_python_literal::escape::AsciiEscape;
use ruff_text_size::{TextRange, TextSize}; use ruff_text_size::{TextRange, TextSize};
use rustc_hash::{FxHashMap, FxHashSet};
use crate::Db; use crate::Db;
use crate::module_resolver::file_to_module; use crate::module_resolver::file_to_module;
@ -15,117 +19,149 @@ use crate::types::function::{FunctionType, OverloadLiteral};
use crate::types::generics::{GenericContext, Specialization}; use crate::types::generics::{GenericContext, Specialization};
use crate::types::signatures::{CallableSignature, Parameter, Parameters, Signature}; use crate::types::signatures::{CallableSignature, Parameter, Parameters, Signature};
use crate::types::tuple::TupleSpec; use crate::types::tuple::TupleSpec;
use crate::types::visitor::TypeVisitor;
use crate::types::{ use crate::types::{
BoundTypeVarInstance, CallableType, IntersectionType, KnownBoundMethodType, KnownClass, BoundTypeVarInstance, CallableType, IntersectionType, KnownBoundMethodType, KnownClass,
MaterializationKind, Protocol, ProtocolInstanceType, StringLiteralType, SubclassOfInner, Type, MaterializationKind, Protocol, StringLiteralType, SubclassOfInner, Type, UnionType,
UnionType, WrapperDescriptorKind, WrapperDescriptorKind, visitor,
}; };
use ruff_db::parsed::parsed_module; use ruff_db::parsed::parsed_module;
/// Settings for displaying types and signatures /// Settings for displaying types and signatures
#[derive(Debug, Copy, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct DisplaySettings { pub struct DisplaySettings<'db> {
/// Whether rendering can be multiline /// Whether rendering can be multiline
pub multiline: bool, pub multiline: bool,
/// Whether rendering will show qualified display (e.g., module.class) /// Class names that should be displayed fully qualified
pub qualified: bool, /// (e.g., `module.ClassName` instead of just `ClassName`)
pub qualified: Rc<FxHashSet<&'db str>>,
} }
impl DisplaySettings { impl<'db> DisplaySettings<'db> {
#[must_use] #[must_use]
pub fn multiline(self) -> Self { pub fn multiline(&self) -> Self {
Self { Self {
multiline: true, multiline: true,
..self ..self.clone()
} }
} }
#[must_use] #[must_use]
pub fn singleline(self) -> Self { pub fn singleline(&self) -> Self {
Self { Self {
multiline: false, multiline: false,
..self ..self.clone()
} }
} }
#[must_use] #[must_use]
pub fn qualified(self) -> Self { pub fn from_possibly_ambiguous_type_pair(
Self {
qualified: true,
..self
}
}
#[must_use]
pub fn from_possibly_ambiguous_type_pair<'db>(
db: &'db dyn Db, db: &'db dyn Db,
type_1: Type<'db>, type_1: Type<'db>,
type_2: Type<'db>, type_2: Type<'db>,
) -> Self { ) -> Self {
let result = Self::default(); let collector = AmbiguousClassCollector::default();
collector.visit_type(db, type_1);
collector.visit_type(db, type_2);
let Some(class_1) = type_to_class_literal(db, type_1) else { Self {
return result; qualified: Rc::new(
}; collector
.class_names
let Some(class_2) = type_to_class_literal(db, type_2) else { .borrow()
return result; .iter()
}; .filter_map(|(name, ambiguity)| ambiguity.is_ambiguous().then_some(*name))
.collect(),
if class_1 == class_2 { ),
return result; ..Self::default()
}
if class_1.name(db) == class_2.name(db) {
result.qualified()
} else {
result
} }
} }
} }
// TODO: generalize this to a method that takes any two types, walks them recursively, and returns #[derive(Debug, Default)]
// a set of types with ambiguous names whose display should be qualified. Then we can use this in struct AmbiguousClassCollector<'db> {
// any diagnostic that displays two types. visited_types: RefCell<FxHashSet<Type<'db>>>,
fn type_to_class_literal<'db>(db: &'db dyn Db, ty: Type<'db>) -> Option<ClassLiteral<'db>> { class_names: RefCell<FxHashMap<&'db str, AmbiguityState<'db>>>,
}
impl<'db> AmbiguousClassCollector<'db> {
fn record_class(&self, db: &'db dyn Db, class: ClassLiteral<'db>) {
match self.class_names.borrow_mut().entry(class.name(db)) {
Entry::Vacant(entry) => {
entry.insert(AmbiguityState::Unambiguous(class));
}
Entry::Occupied(mut entry) => {
let value = entry.get_mut();
if let AmbiguityState::Unambiguous(existing) = value
&& *existing != class
{
*value = AmbiguityState::Ambiguous;
}
}
}
}
}
/// Whether or not a class can be unambiguously identified by its *unqualified* name
/// given the other types that are present in the same context.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum AmbiguityState<'db> {
/// The class can be displayed unambiguously using its unqualified name
Unambiguous(ClassLiteral<'db>),
/// The class must be displayed using its fully qualified name to avoid ambiguity.
Ambiguous,
}
impl AmbiguityState<'_> {
const fn is_ambiguous(self) -> bool {
matches!(self, AmbiguityState::Ambiguous)
}
}
impl<'db> super::visitor::TypeVisitor<'db> for AmbiguousClassCollector<'db> {
fn should_visit_lazy_type_attributes(&self) -> bool {
false
}
fn visit_type(&self, db: &'db dyn Db, ty: Type<'db>) {
match ty { match ty {
Type::ClassLiteral(class) => Some(class), Type::ClassLiteral(class) => self.record_class(db, class),
Type::NominalInstance(instance) => Some(instance.class_literal(db)), Type::EnumLiteral(literal) => self.record_class(db, literal.enum_class(db)),
Type::EnumLiteral(enum_literal) => Some(enum_literal.enum_class(db)), Type::GenericAlias(alias) => self.record_class(db, alias.origin(db)),
Type::GenericAlias(alias) => Some(alias.origin(db)), _ => {}
Type::ProtocolInstance(ProtocolInstanceType {
inner: Protocol::FromClass(class),
..
}) => type_to_class_literal(db, Type::from(class)),
Type::TypedDict(typed_dict) => {
type_to_class_literal(db, Type::from(typed_dict.defining_class()))
} }
Type::SubclassOf(subclass_of) => {
type_to_class_literal(db, Type::from(subclass_of.subclass_of().into_class()?)) if let visitor::TypeKind::NonAtomic(t) = visitor::TypeKind::from(ty) {
if !self.visited_types.borrow_mut().insert(ty) {
// If we have already seen this type, we can skip it.
return;
}
visitor::walk_non_atomic_type(db, t, self);
} }
_ => None,
} }
} }
impl<'db> Type<'db> { impl<'db> Type<'db> {
pub fn display(&self, db: &'db dyn Db) -> DisplayType<'_> { pub fn display(self, db: &'db dyn Db) -> DisplayType<'db> {
DisplayType { DisplayType {
ty: self, ty: self,
settings: DisplaySettings::default(), settings: DisplaySettings::default(),
db, db,
} }
} }
pub fn display_with(&self, db: &'db dyn Db, settings: DisplaySettings) -> DisplayType<'_> {
pub fn display_with(self, db: &'db dyn Db, settings: DisplaySettings<'db>) -> DisplayType<'db> {
DisplayType { DisplayType {
ty: self, ty: self,
db, db,
settings, settings,
} }
} }
fn representation( fn representation(
self, self,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
) -> DisplayRepresentation<'db> { ) -> DisplayRepresentation<'db> {
DisplayRepresentation { DisplayRepresentation {
db, db,
@ -135,16 +171,15 @@ impl<'db> Type<'db> {
} }
} }
#[derive(Copy, Clone)]
pub struct DisplayType<'db> { pub struct DisplayType<'db> {
ty: &'db Type<'db>, ty: Type<'db>,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
} }
impl Display for DisplayType<'_> { impl Display for DisplayType<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let representation = self.ty.representation(self.db, self.settings); let representation = self.ty.representation(self.db, self.settings.clone());
match self.ty { match self.ty {
Type::IntLiteral(_) Type::IntLiteral(_)
| Type::BooleanLiteral(_) | Type::BooleanLiteral(_)
@ -165,7 +200,7 @@ impl fmt::Debug for DisplayType<'_> {
} }
impl<'db> ClassLiteral<'db> { impl<'db> ClassLiteral<'db> {
fn display_with(self, db: &'db dyn Db, settings: DisplaySettings) -> ClassDisplay<'db> { fn display_with(self, db: &'db dyn Db, settings: DisplaySettings<'db>) -> ClassDisplay<'db> {
ClassDisplay { ClassDisplay {
db, db,
class: self, class: self,
@ -177,7 +212,7 @@ impl<'db> ClassLiteral<'db> {
struct ClassDisplay<'db> { struct ClassDisplay<'db> {
db: &'db dyn Db, db: &'db dyn Db,
class: ClassLiteral<'db>, class: ClassLiteral<'db>,
settings: DisplaySettings, settings: DisplaySettings<'db>,
} }
impl ClassDisplay<'_> { impl ClassDisplay<'_> {
@ -224,7 +259,11 @@ impl ClassDisplay<'_> {
impl Display for ClassDisplay<'_> { impl Display for ClassDisplay<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if self.settings.qualified { if self
.settings
.qualified
.contains(&**self.class.name(self.db))
{
for parent in self.class_parents() { for parent in self.class_parents() {
f.write_str(&parent)?; f.write_str(&parent)?;
f.write_char('.')?; f.write_char('.')?;
@ -240,7 +279,7 @@ impl Display for ClassDisplay<'_> {
struct DisplayRepresentation<'db> { struct DisplayRepresentation<'db> {
ty: Type<'db>, ty: Type<'db>,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
} }
impl Display for DisplayRepresentation<'_> { impl Display for DisplayRepresentation<'_> {
@ -258,20 +297,20 @@ impl Display for DisplayRepresentation<'_> {
.specialization(self.db) .specialization(self.db)
.tuple(self.db) .tuple(self.db)
.expect("Specialization::tuple() should always return `Some()` for `KnownClass::Tuple`") .expect("Specialization::tuple() should always return `Some()` for `KnownClass::Tuple`")
.display_with(self.db, self.settings) .display_with(self.db, self.settings.clone())
.fmt(f), .fmt(f),
(ClassType::NonGeneric(class), _) => { (ClassType::NonGeneric(class), _) => {
class.display_with(self.db, self.settings).fmt(f) class.display_with(self.db, self.settings.clone()).fmt(f)
}, },
(ClassType::Generic(alias), _) => alias.display_with(self.db, self.settings).fmt(f), (ClassType::Generic(alias), _) => alias.display_with(self.db, self.settings.clone()).fmt(f),
} }
} }
Type::ProtocolInstance(protocol) => match protocol.inner { Type::ProtocolInstance(protocol) => match protocol.inner {
Protocol::FromClass(ClassType::NonGeneric(class)) => { Protocol::FromClass(ClassType::NonGeneric(class)) => {
class.display_with(self.db, self.settings).fmt(f) class.display_with(self.db, self.settings.clone()).fmt(f)
} }
Protocol::FromClass(ClassType::Generic(alias)) => { Protocol::FromClass(ClassType::Generic(alias)) => {
alias.display_with(self.db, self.settings).fmt(f) alias.display_with(self.db, self.settings.clone()).fmt(f)
} }
Protocol::Synthesized(synthetic) => { Protocol::Synthesized(synthetic) => {
f.write_str("<Protocol with members ")?; f.write_str("<Protocol with members ")?;
@ -295,7 +334,7 @@ impl Display for DisplayRepresentation<'_> {
Type::ClassLiteral(class) => write!( Type::ClassLiteral(class) => write!(
f, f,
"<class '{}'>", "<class '{}'>",
class.display_with(self.db, self.settings) class.display_with(self.db, self.settings.clone())
), ),
Type::GenericAlias(generic) => write!( Type::GenericAlias(generic) => write!(
f, f,
@ -304,7 +343,11 @@ impl Display for DisplayRepresentation<'_> {
), ),
Type::SubclassOf(subclass_of_ty) => match subclass_of_ty.subclass_of() { Type::SubclassOf(subclass_of_ty) => match subclass_of_ty.subclass_of() {
SubclassOfInner::Class(ClassType::NonGeneric(class)) => { SubclassOfInner::Class(ClassType::NonGeneric(class)) => {
write!(f, "type[{}]", class.display_with(self.db, self.settings)) write!(
f,
"type[{}]",
class.display_with(self.db, self.settings.clone())
)
} }
SubclassOfInner::Class(ClassType::Generic(alias)) => { SubclassOfInner::Class(ClassType::Generic(alias)) => {
write!( write!(
@ -317,8 +360,12 @@ impl Display for DisplayRepresentation<'_> {
}, },
Type::SpecialForm(special_form) => special_form.fmt(f), Type::SpecialForm(special_form) => special_form.fmt(f),
Type::KnownInstance(known_instance) => known_instance.repr(self.db).fmt(f), Type::KnownInstance(known_instance) => known_instance.repr(self.db).fmt(f),
Type::FunctionLiteral(function) => function.display_with(self.db, self.settings).fmt(f), Type::FunctionLiteral(function) => {
Type::Callable(callable) => callable.display_with(self.db, self.settings).fmt(f), function.display_with(self.db, self.settings.clone()).fmt(f)
}
Type::Callable(callable) => {
callable.display_with(self.db, self.settings.clone()).fmt(f)
}
Type::BoundMethod(bound_method) => { Type::BoundMethod(bound_method) => {
let function = bound_method.function(self.db); let function = bound_method.function(self.db);
let self_ty = bound_method.self_instance(self.db); let self_ty = bound_method.self_instance(self.db);
@ -329,7 +376,7 @@ impl Display for DisplayRepresentation<'_> {
let type_parameters = DisplayOptionalGenericContext { let type_parameters = DisplayOptionalGenericContext {
generic_context: signature.generic_context.as_ref(), generic_context: signature.generic_context.as_ref(),
db: self.db, db: self.db,
settings: self.settings, settings: self.settings.clone(),
}; };
write!( write!(
@ -340,7 +387,7 @@ impl Display for DisplayRepresentation<'_> {
type_parameters = type_parameters, type_parameters = type_parameters,
signature = signature signature = signature
.bind_self(self.db, Some(typing_self_ty)) .bind_self(self.db, Some(typing_self_ty))
.display_with(self.db, self.settings) .display_with(self.db, self.settings.clone())
) )
} }
signatures => { signatures => {
@ -354,7 +401,7 @@ impl Display for DisplayRepresentation<'_> {
join.entry( join.entry(
&signature &signature
.bind_self(self.db, Some(typing_self_ty)) .bind_self(self.db, Some(typing_self_ty))
.display_with(self.db, self.settings), .display_with(self.db, self.settings.clone()),
); );
} }
if !self.settings.multiline { if !self.settings.multiline {
@ -404,13 +451,15 @@ impl Display for DisplayRepresentation<'_> {
Type::DataclassTransformer(_) => { Type::DataclassTransformer(_) => {
f.write_str("<decorator produced by typing.dataclass_transform>") f.write_str("<decorator produced by typing.dataclass_transform>")
} }
Type::Union(union) => union.display_with(self.db, self.settings).fmt(f), Type::Union(union) => union.display_with(self.db, self.settings.clone()).fmt(f),
Type::Intersection(intersection) => { Type::Intersection(intersection) => intersection
intersection.display_with(self.db, self.settings).fmt(f) .display_with(self.db, self.settings.clone())
} .fmt(f),
Type::IntLiteral(n) => n.fmt(f), Type::IntLiteral(n) => n.fmt(f),
Type::BooleanLiteral(boolean) => f.write_str(if boolean { "True" } else { "False" }), Type::BooleanLiteral(boolean) => f.write_str(if boolean { "True" } else { "False" }),
Type::StringLiteral(string) => string.display_with(self.db, self.settings).fmt(f), Type::StringLiteral(string) => {
string.display_with(self.db, self.settings.clone()).fmt(f)
}
Type::LiteralString => f.write_str("LiteralString"), Type::LiteralString => f.write_str("LiteralString"),
Type::BytesLiteral(bytes) => { Type::BytesLiteral(bytes) => {
let escape = AsciiEscape::with_preferred_quote(bytes.value(self.db), Quote::Double); let escape = AsciiEscape::with_preferred_quote(bytes.value(self.db), Quote::Double);
@ -422,7 +471,7 @@ impl Display for DisplayRepresentation<'_> {
"{enum_class}.{literal_name}", "{enum_class}.{literal_name}",
enum_class = enum_literal enum_class = enum_literal
.enum_class(self.db) .enum_class(self.db)
.display_with(self.db, self.settings), .display_with(self.db, self.settings.clone()),
literal_name = enum_literal.name(self.db) literal_name = enum_literal.name(self.db)
), ),
Type::NonInferableTypeVar(bound_typevar) | Type::TypeVar(bound_typevar) => { Type::NonInferableTypeVar(bound_typevar) | Type::TypeVar(bound_typevar) => {
@ -458,7 +507,7 @@ impl Display for DisplayRepresentation<'_> {
.defining_class() .defining_class()
.class_literal(self.db) .class_literal(self.db)
.0 .0
.display_with(self.db, self.settings) .display_with(self.db, self.settings.clone())
.fmt(f), .fmt(f),
Type::TypeAlias(alias) => f.write_str(alias.name(self.db)), Type::TypeAlias(alias) => f.write_str(alias.name(self.db)),
} }
@ -493,7 +542,7 @@ impl<'db> TupleSpec<'db> {
pub(crate) fn display_with( pub(crate) fn display_with(
&'db self, &'db self,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
) -> DisplayTuple<'db> { ) -> DisplayTuple<'db> {
DisplayTuple { DisplayTuple {
tuple: self, tuple: self,
@ -506,7 +555,7 @@ impl<'db> TupleSpec<'db> {
pub(crate) struct DisplayTuple<'db> { pub(crate) struct DisplayTuple<'db> {
tuple: &'db TupleSpec<'db>, tuple: &'db TupleSpec<'db>,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
} }
impl Display for DisplayTuple<'_> { impl Display for DisplayTuple<'_> {
@ -580,7 +629,7 @@ impl<'db> OverloadLiteral<'db> {
pub(crate) fn display_with( pub(crate) fn display_with(
self, self,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
) -> DisplayOverloadLiteral<'db> { ) -> DisplayOverloadLiteral<'db> {
DisplayOverloadLiteral { DisplayOverloadLiteral {
literal: self, literal: self,
@ -593,7 +642,7 @@ impl<'db> OverloadLiteral<'db> {
pub(crate) struct DisplayOverloadLiteral<'db> { pub(crate) struct DisplayOverloadLiteral<'db> {
literal: OverloadLiteral<'db>, literal: OverloadLiteral<'db>,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
} }
impl Display for DisplayOverloadLiteral<'_> { impl Display for DisplayOverloadLiteral<'_> {
@ -602,7 +651,7 @@ impl Display for DisplayOverloadLiteral<'_> {
let type_parameters = DisplayOptionalGenericContext { let type_parameters = DisplayOptionalGenericContext {
generic_context: signature.generic_context.as_ref(), generic_context: signature.generic_context.as_ref(),
db: self.db, db: self.db,
settings: self.settings, settings: self.settings.clone(),
}; };
write!( write!(
@ -610,7 +659,7 @@ impl Display for DisplayOverloadLiteral<'_> {
"def {name}{type_parameters}{signature}", "def {name}{type_parameters}{signature}",
name = self.literal.name(self.db), name = self.literal.name(self.db),
type_parameters = type_parameters, type_parameters = type_parameters,
signature = signature.display_with(self.db, self.settings) signature = signature.display_with(self.db, self.settings.clone())
) )
} }
} }
@ -619,7 +668,7 @@ impl<'db> FunctionType<'db> {
pub(crate) fn display_with( pub(crate) fn display_with(
self, self,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
) -> DisplayFunctionType<'db> { ) -> DisplayFunctionType<'db> {
DisplayFunctionType { DisplayFunctionType {
ty: self, ty: self,
@ -632,7 +681,7 @@ impl<'db> FunctionType<'db> {
pub(crate) struct DisplayFunctionType<'db> { pub(crate) struct DisplayFunctionType<'db> {
ty: FunctionType<'db>, ty: FunctionType<'db>,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
} }
impl Display for DisplayFunctionType<'_> { impl Display for DisplayFunctionType<'_> {
@ -644,7 +693,7 @@ impl Display for DisplayFunctionType<'_> {
let type_parameters = DisplayOptionalGenericContext { let type_parameters = DisplayOptionalGenericContext {
generic_context: signature.generic_context.as_ref(), generic_context: signature.generic_context.as_ref(),
db: self.db, db: self.db,
settings: self.settings, settings: self.settings.clone(),
}; };
write!( write!(
@ -652,7 +701,7 @@ impl Display for DisplayFunctionType<'_> {
"def {name}{type_parameters}{signature}", "def {name}{type_parameters}{signature}",
name = self.ty.name(self.db), name = self.ty.name(self.db),
type_parameters = type_parameters, type_parameters = type_parameters,
signature = signature.display_with(self.db, self.settings) signature = signature.display_with(self.db, self.settings.clone())
) )
} }
signatures => { signatures => {
@ -663,7 +712,7 @@ impl Display for DisplayFunctionType<'_> {
let separator = if self.settings.multiline { "\n" } else { ", " }; let separator = if self.settings.multiline { "\n" } else { ", " };
let mut join = f.join(separator); let mut join = f.join(separator);
for signature in signatures { for signature in signatures {
join.entry(&signature.display_with(self.db, self.settings)); join.entry(&signature.display_with(self.db, self.settings.clone()));
} }
if !self.settings.multiline { if !self.settings.multiline {
f.write_str("]")?; f.write_str("]")?;
@ -678,7 +727,7 @@ impl<'db> GenericAlias<'db> {
pub(crate) fn display_with( pub(crate) fn display_with(
&'db self, &'db self,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
) -> DisplayGenericAlias<'db> { ) -> DisplayGenericAlias<'db> {
DisplayGenericAlias { DisplayGenericAlias {
origin: self.origin(db), origin: self.origin(db),
@ -693,13 +742,13 @@ pub(crate) struct DisplayGenericAlias<'db> {
origin: ClassLiteral<'db>, origin: ClassLiteral<'db>,
specialization: Specialization<'db>, specialization: Specialization<'db>,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
} }
impl Display for DisplayGenericAlias<'_> { impl Display for DisplayGenericAlias<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if let Some(tuple) = self.specialization.tuple(self.db) { if let Some(tuple) = self.specialization.tuple(self.db) {
tuple.display_with(self.db, self.settings).fmt(f) tuple.display_with(self.db, self.settings.clone()).fmt(f)
} else { } else {
let prefix = match self.specialization.materialization_kind(self.db) { let prefix = match self.specialization.materialization_kind(self.db) {
None => "", None => "",
@ -714,10 +763,11 @@ impl Display for DisplayGenericAlias<'_> {
f, f,
"{prefix}{origin}{specialization}{suffix}", "{prefix}{origin}{specialization}{suffix}",
prefix = prefix, prefix = prefix,
origin = self.origin.display_with(self.db, self.settings), origin = self.origin.display_with(self.db, self.settings.clone()),
specialization = self.specialization.display_short( specialization = self.specialization.display_short(
self.db, self.db,
TupleSpecialization::from_class(self.db, self.origin) TupleSpecialization::from_class(self.db, self.origin),
self.settings.clone()
), ),
suffix = suffix, suffix = suffix,
) )
@ -732,7 +782,7 @@ impl<'db> GenericContext<'db> {
pub fn display_with( pub fn display_with(
&'db self, &'db self,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
) -> DisplayGenericContext<'db> { ) -> DisplayGenericContext<'db> {
DisplayGenericContext { DisplayGenericContext {
generic_context: self, generic_context: self,
@ -745,7 +795,7 @@ impl<'db> GenericContext<'db> {
struct DisplayOptionalGenericContext<'db> { struct DisplayOptionalGenericContext<'db> {
generic_context: Option<&'db GenericContext<'db>>, generic_context: Option<&'db GenericContext<'db>>,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
} }
impl Display for DisplayOptionalGenericContext<'_> { impl Display for DisplayOptionalGenericContext<'_> {
@ -754,7 +804,7 @@ impl Display for DisplayOptionalGenericContext<'_> {
DisplayGenericContext { DisplayGenericContext {
generic_context, generic_context,
db: self.db, db: self.db,
settings: self.settings, settings: self.settings.clone(),
} }
.fmt(f) .fmt(f)
} else { } else {
@ -767,7 +817,7 @@ pub struct DisplayGenericContext<'db> {
generic_context: &'db GenericContext<'db>, generic_context: &'db GenericContext<'db>,
db: &'db dyn Db, db: &'db dyn Db,
#[expect(dead_code)] #[expect(dead_code)]
settings: DisplaySettings, settings: DisplaySettings<'db>,
} }
impl Display for DisplayGenericContext<'_> { impl Display for DisplayGenericContext<'_> {
@ -800,12 +850,13 @@ impl<'db> Specialization<'db> {
&'db self, &'db self,
db: &'db dyn Db, db: &'db dyn Db,
tuple_specialization: TupleSpecialization, tuple_specialization: TupleSpecialization,
settings: DisplaySettings<'db>,
) -> DisplaySpecialization<'db> { ) -> DisplaySpecialization<'db> {
DisplaySpecialization { DisplaySpecialization {
types: self.types(db), types: self.types(db),
db, db,
tuple_specialization, tuple_specialization,
settings: DisplaySettings::default(), settings,
} }
} }
} }
@ -814,7 +865,7 @@ pub struct DisplaySpecialization<'db> {
types: &'db [Type<'db>], types: &'db [Type<'db>],
db: &'db dyn Db, db: &'db dyn Db,
tuple_specialization: TupleSpecialization, tuple_specialization: TupleSpecialization,
settings: DisplaySettings, settings: DisplaySettings<'db>,
} }
impl Display for DisplaySpecialization<'_> { impl Display for DisplaySpecialization<'_> {
@ -824,7 +875,7 @@ impl Display for DisplaySpecialization<'_> {
if idx > 0 { if idx > 0 {
f.write_str(", ")?; f.write_str(", ")?;
} }
ty.display_with(self.db, self.settings).fmt(f)?; ty.display_with(self.db, self.settings.clone()).fmt(f)?;
} }
if self.tuple_specialization.is_yes() { if self.tuple_specialization.is_yes() {
f.write_str(", ...")?; f.write_str(", ...")?;
@ -861,7 +912,7 @@ impl<'db> CallableType<'db> {
pub(crate) fn display_with( pub(crate) fn display_with(
&'db self, &'db self,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
) -> DisplayCallableType<'db> { ) -> DisplayCallableType<'db> {
DisplayCallableType { DisplayCallableType {
signatures: self.signatures(db), signatures: self.signatures(db),
@ -874,13 +925,15 @@ impl<'db> CallableType<'db> {
pub(crate) struct DisplayCallableType<'db> { pub(crate) struct DisplayCallableType<'db> {
signatures: &'db CallableSignature<'db>, signatures: &'db CallableSignature<'db>,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
} }
impl Display for DisplayCallableType<'_> { impl Display for DisplayCallableType<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self.signatures.overloads.as_slice() { match self.signatures.overloads.as_slice() {
[signature] => signature.display_with(self.db, self.settings).fmt(f), [signature] => signature
.display_with(self.db, self.settings.clone())
.fmt(f),
signatures => { signatures => {
// TODO: How to display overloads? // TODO: How to display overloads?
if !self.settings.multiline { if !self.settings.multiline {
@ -889,7 +942,7 @@ impl Display for DisplayCallableType<'_> {
let separator = if self.settings.multiline { "\n" } else { ", " }; let separator = if self.settings.multiline { "\n" } else { ", " };
let mut join = f.join(separator); let mut join = f.join(separator);
for signature in signatures { for signature in signatures {
join.entry(&signature.display_with(self.db, self.settings)); join.entry(&signature.display_with(self.db, self.settings.clone()));
} }
join.finish()?; join.finish()?;
if !self.settings.multiline { if !self.settings.multiline {
@ -909,7 +962,7 @@ impl<'db> Signature<'db> {
pub(crate) fn display_with( pub(crate) fn display_with(
&'db self, &'db self,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
) -> DisplaySignature<'db> { ) -> DisplaySignature<'db> {
DisplaySignature { DisplaySignature {
parameters: self.parameters(), parameters: self.parameters(),
@ -924,7 +977,7 @@ pub(crate) struct DisplaySignature<'db> {
parameters: &'db Parameters<'db>, parameters: &'db Parameters<'db>,
return_ty: Option<Type<'db>>, return_ty: Option<Type<'db>>,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
} }
impl DisplaySignature<'_> { impl DisplaySignature<'_> {
@ -1128,7 +1181,7 @@ impl<'db> Parameter<'db> {
fn display_with( fn display_with(
&'db self, &'db self,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
) -> DisplayParameter<'db> { ) -> DisplayParameter<'db> {
DisplayParameter { DisplayParameter {
param: self, param: self,
@ -1141,7 +1194,7 @@ impl<'db> Parameter<'db> {
struct DisplayParameter<'db> { struct DisplayParameter<'db> {
param: &'db Parameter<'db>, param: &'db Parameter<'db>,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
} }
impl Display for DisplayParameter<'_> { impl Display for DisplayParameter<'_> {
@ -1152,21 +1205,29 @@ impl Display for DisplayParameter<'_> {
write!( write!(
f, f,
": {}", ": {}",
annotated_type.display_with(self.db, self.settings) annotated_type.display_with(self.db, self.settings.clone())
)?; )?;
} }
// Default value can only be specified if `name` is given. // Default value can only be specified if `name` is given.
if let Some(default_ty) = self.param.default_type() { if let Some(default_ty) = self.param.default_type() {
if self.param.annotated_type().is_some() { if self.param.annotated_type().is_some() {
write!(f, " = {}", default_ty.display_with(self.db, self.settings))?; write!(
f,
" = {}",
default_ty.display_with(self.db, self.settings.clone())
)?;
} else { } else {
write!(f, "={}", default_ty.display_with(self.db, self.settings))?; write!(
f,
"={}",
default_ty.display_with(self.db, self.settings.clone())
)?;
} }
} }
} else if let Some(ty) = self.param.annotated_type() { } else if let Some(ty) = self.param.annotated_type() {
// This case is specifically for the `Callable` signature where name and default value // This case is specifically for the `Callable` signature where name and default value
// cannot be provided. // cannot be provided.
ty.display_with(self.db, self.settings).fmt(f)?; ty.display_with(self.db, self.settings.clone()).fmt(f)?;
} }
Ok(()) Ok(())
} }
@ -1176,7 +1237,7 @@ impl<'db> UnionType<'db> {
fn display_with( fn display_with(
&'db self, &'db self,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
) -> DisplayUnionType<'db> { ) -> DisplayUnionType<'db> {
DisplayUnionType { DisplayUnionType {
db, db,
@ -1189,7 +1250,7 @@ impl<'db> UnionType<'db> {
struct DisplayUnionType<'db> { struct DisplayUnionType<'db> {
ty: &'db UnionType<'db>, ty: &'db UnionType<'db>,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
} }
impl Display for DisplayUnionType<'_> { impl Display for DisplayUnionType<'_> {
@ -1249,7 +1310,7 @@ impl fmt::Debug for DisplayUnionType<'_> {
struct DisplayLiteralGroup<'db> { struct DisplayLiteralGroup<'db> {
literals: Vec<Type<'db>>, literals: Vec<Type<'db>>,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
} }
impl Display for DisplayLiteralGroup<'_> { impl Display for DisplayLiteralGroup<'_> {
@ -1270,7 +1331,7 @@ impl<'db> IntersectionType<'db> {
fn display_with( fn display_with(
&'db self, &'db self,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
) -> DisplayIntersectionType<'db> { ) -> DisplayIntersectionType<'db> {
DisplayIntersectionType { DisplayIntersectionType {
db, db,
@ -1283,7 +1344,7 @@ impl<'db> IntersectionType<'db> {
struct DisplayIntersectionType<'db> { struct DisplayIntersectionType<'db> {
ty: &'db IntersectionType<'db>, ty: &'db IntersectionType<'db>,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
} }
impl Display for DisplayIntersectionType<'_> { impl Display for DisplayIntersectionType<'_> {
@ -1323,7 +1384,7 @@ struct DisplayMaybeNegatedType<'db> {
ty: Type<'db>, ty: Type<'db>,
db: &'db dyn Db, db: &'db dyn Db,
negated: bool, negated: bool,
settings: DisplaySettings, settings: DisplaySettings<'db>,
} }
impl Display for DisplayMaybeNegatedType<'_> { impl Display for DisplayMaybeNegatedType<'_> {
@ -1334,7 +1395,7 @@ impl Display for DisplayMaybeNegatedType<'_> {
DisplayMaybeParenthesizedType { DisplayMaybeParenthesizedType {
ty: self.ty, ty: self.ty,
db: self.db, db: self.db,
settings: self.settings, settings: self.settings.clone(),
} }
.fmt(f) .fmt(f)
} }
@ -1343,13 +1404,18 @@ impl Display for DisplayMaybeNegatedType<'_> {
struct DisplayMaybeParenthesizedType<'db> { struct DisplayMaybeParenthesizedType<'db> {
ty: Type<'db>, ty: Type<'db>,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
} }
impl Display for DisplayMaybeParenthesizedType<'_> { impl Display for DisplayMaybeParenthesizedType<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let write_parentheses = let write_parentheses = |f: &mut Formatter<'_>| {
|f: &mut Formatter<'_>| write!(f, "({})", self.ty.display_with(self.db, self.settings)); write!(
f,
"({})",
self.ty.display_with(self.db, self.settings.clone())
)
};
match self.ty { match self.ty {
Type::Callable(_) Type::Callable(_)
| Type::KnownBoundMethod(_) | Type::KnownBoundMethod(_)
@ -1359,21 +1425,24 @@ impl Display for DisplayMaybeParenthesizedType<'_> {
Type::Intersection(intersection) if !intersection.has_one_element(self.db) => { Type::Intersection(intersection) if !intersection.has_one_element(self.db) => {
write_parentheses(f) write_parentheses(f)
} }
_ => self.ty.display_with(self.db, self.settings).fmt(f), _ => self.ty.display_with(self.db, self.settings.clone()).fmt(f),
} }
} }
} }
pub(crate) trait TypeArrayDisplay<'db> { pub(crate) trait TypeArrayDisplay<'db> {
fn display_with(&self, db: &'db dyn Db, settings: DisplaySettings) fn display_with(
-> DisplayTypeArray<'_, 'db>; &self,
db: &'db dyn Db,
settings: DisplaySettings<'db>,
) -> DisplayTypeArray<'_, 'db>;
} }
impl<'db> TypeArrayDisplay<'db> for Box<[Type<'db>]> { impl<'db> TypeArrayDisplay<'db> for Box<[Type<'db>]> {
fn display_with( fn display_with(
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
) -> DisplayTypeArray<'_, 'db> { ) -> DisplayTypeArray<'_, 'db> {
DisplayTypeArray { DisplayTypeArray {
types: self, types: self,
@ -1387,7 +1456,7 @@ impl<'db> TypeArrayDisplay<'db> for Vec<Type<'db>> {
fn display_with( fn display_with(
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
) -> DisplayTypeArray<'_, 'db> { ) -> DisplayTypeArray<'_, 'db> {
DisplayTypeArray { DisplayTypeArray {
types: self, types: self,
@ -1401,7 +1470,7 @@ impl<'db> TypeArrayDisplay<'db> for [Type<'db>] {
fn display_with( fn display_with(
&self, &self,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
) -> DisplayTypeArray<'_, 'db> { ) -> DisplayTypeArray<'_, 'db> {
DisplayTypeArray { DisplayTypeArray {
types: self, types: self,
@ -1414,7 +1483,7 @@ impl<'db> TypeArrayDisplay<'db> for [Type<'db>] {
pub(crate) struct DisplayTypeArray<'b, 'db> { pub(crate) struct DisplayTypeArray<'b, 'db> {
types: &'b [Type<'db>], types: &'b [Type<'db>],
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
} }
impl Display for DisplayTypeArray<'_, '_> { impl Display for DisplayTypeArray<'_, '_> {
@ -1433,20 +1502,19 @@ impl<'db> StringLiteralType<'db> {
fn display_with( fn display_with(
&'db self, &'db self,
db: &'db dyn Db, db: &'db dyn Db,
settings: DisplaySettings, settings: DisplaySettings<'db>,
) -> DisplayStringLiteralType<'db> { ) -> DisplayStringLiteralType<'db> {
display_quoted_string(self.value(db), settings) DisplayStringLiteralType {
string: self.value(db),
settings,
}
} }
}
fn display_quoted_string(string: &str, settings: DisplaySettings) -> DisplayStringLiteralType<'_> {
DisplayStringLiteralType { string, settings }
} }
struct DisplayStringLiteralType<'db> { struct DisplayStringLiteralType<'db> {
string: &'db str, string: &'db str,
#[expect(dead_code)] #[expect(dead_code)]
settings: DisplaySettings, settings: DisplaySettings<'db>,
} }
impl Display for DisplayStringLiteralType<'_> { impl Display for DisplayStringLiteralType<'_> {

View file

@ -107,7 +107,7 @@ pub(crate) trait TypeVisitor<'db> {
/// Enumeration of types that may contain other types, such as unions, intersections, and generics. /// Enumeration of types that may contain other types, such as unions, intersections, and generics.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
enum NonAtomicType<'db> { pub(super) enum NonAtomicType<'db> {
Union(UnionType<'db>), Union(UnionType<'db>),
Intersection(IntersectionType<'db>), Intersection(IntersectionType<'db>),
FunctionLiteral(FunctionType<'db>), FunctionLiteral(FunctionType<'db>),
@ -128,7 +128,7 @@ enum NonAtomicType<'db> {
TypeAlias(TypeAliasType<'db>), TypeAlias(TypeAliasType<'db>),
} }
enum TypeKind<'db> { pub(super) enum TypeKind<'db> {
Atomic, Atomic,
NonAtomic(NonAtomicType<'db>), NonAtomic(NonAtomicType<'db>),
} }
@ -200,7 +200,7 @@ impl<'db> From<Type<'db>> for TypeKind<'db> {
} }
} }
fn walk_non_atomic_type<'db, V: TypeVisitor<'db> + ?Sized>( pub(super) fn walk_non_atomic_type<'db, V: TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db, db: &'db dyn Db,
non_atomic_type: NonAtomicType<'db>, non_atomic_type: NonAtomicType<'db>,
visitor: &V, visitor: &V,