diff --git a/crates/ty_python_semantic/resources/mdtest/diagnostics/same_names.md b/crates/ty_python_semantic/resources/mdtest/diagnostics/same_names.md index 1c4593fc6e..f55d7d106c 100644 --- a/crates/ty_python_semantic/resources/mdtest/diagnostics/same_names.md +++ b/crates/ty_python_semantic/resources/mdtest/diagnostics/same_names.md @@ -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`" 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[DataFrame]` is not assignable to `list[DataFrame]`" + # error: [invalid-assignment] "Object of type `list[b.DataFrame]` is not assignable to `list[a.DataFrame]`" dataframes: list[a.DataFrame] = dfs ``` @@ -228,3 +227,21 @@ from typing import TypedDict class Person(TypedDict): 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(),) +``` diff --git a/crates/ty_python_semantic/resources/mdtest/public_types.md b/crates/ty_python_semantic/resources/mdtest/public_types.md index b0bd3a8a74..4a6ef1c6fb 100644 --- a/crates/ty_python_semantic/resources/mdtest/public_types.md +++ b/crates/ty_python_semantic/resources/mdtest/public_types.md @@ -339,7 +339,7 @@ class A: ... def f(x: A): # 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() ``` diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 7d468058bd..c6d85c037d 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -7065,7 +7065,11 @@ impl<'db> KnownInstanceType<'db> { if let Some(specialization) = alias.specialization(self.db) { f.write_str(alias.name(self.db))?; specialization - .display_short(self.db, TupleSpecialization::No) + .display_short( + self.db, + TupleSpecialization::No, + DisplaySettings::default(), + ) .fmt(f) } else { f.write_str("typing.TypeAliasType") diff --git a/crates/ty_python_semantic/src/types/diagnostic.rs b/crates/ty_python_semantic/src/types/diagnostic.rs index 214eb30749..29d84041f3 100644 --- a/crates/ty_python_semantic/src/types/diagnostic.rs +++ b/crates/ty_python_semantic/src/types/diagnostic.rs @@ -1986,7 +1986,7 @@ pub(super) fn report_invalid_assignment<'db>( target_ty, format_args!( "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) ), ); @@ -2068,8 +2068,8 @@ pub(super) fn report_invalid_return_type( let mut diag = builder.into_diagnostic("Return type does not match returned value"); diag.set_primary_message(format_args!( "expected `{expected_ty}`, found `{actual_ty}`", - expected_ty = expected_ty.display_with(context.db(), settings), - actual_ty = actual_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.clone()), )); diag.annotate( Annotation::secondary(return_type_span).message(format_args!( diff --git a/crates/ty_python_semantic/src/types/display.rs b/crates/ty_python_semantic/src/types/display.rs index a49d563807..a2043c9b28 100644 --- a/crates/ty_python_semantic/src/types/display.rs +++ b/crates/ty_python_semantic/src/types/display.rs @@ -1,11 +1,15 @@ //! Display implementations for types. +use std::cell::RefCell; +use std::collections::hash_map::Entry; use std::fmt::{self, Display, Formatter, Write}; +use std::rc::Rc; use ruff_db::display::FormatterJoinExtension; use ruff_python_ast::str::{Quote, TripleQuotes}; use ruff_python_literal::escape::AsciiEscape; use ruff_text_size::{TextRange, TextSize}; +use rustc_hash::{FxHashMap, FxHashSet}; use crate::Db; 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::signatures::{CallableSignature, Parameter, Parameters, Signature}; use crate::types::tuple::TupleSpec; +use crate::types::visitor::TypeVisitor; use crate::types::{ BoundTypeVarInstance, CallableType, IntersectionType, KnownBoundMethodType, KnownClass, - MaterializationKind, Protocol, ProtocolInstanceType, StringLiteralType, SubclassOfInner, Type, - UnionType, WrapperDescriptorKind, + MaterializationKind, Protocol, StringLiteralType, SubclassOfInner, Type, UnionType, + WrapperDescriptorKind, visitor, }; use ruff_db::parsed::parsed_module; /// Settings for displaying types and signatures -#[derive(Debug, Copy, Clone, Default)] -pub struct DisplaySettings { +#[derive(Debug, Clone, Default)] +pub struct DisplaySettings<'db> { /// Whether rendering can be multiline pub multiline: bool, - /// Whether rendering will show qualified display (e.g., module.class) - pub qualified: bool, + /// Class names that should be displayed fully qualified + /// (e.g., `module.ClassName` instead of just `ClassName`) + pub qualified: Rc>, } -impl DisplaySettings { +impl<'db> DisplaySettings<'db> { #[must_use] - pub fn multiline(self) -> Self { + pub fn multiline(&self) -> Self { Self { multiline: true, - ..self + ..self.clone() } } #[must_use] - pub fn singleline(self) -> Self { + pub fn singleline(&self) -> Self { Self { multiline: false, - ..self + ..self.clone() } } #[must_use] - pub fn qualified(self) -> Self { - Self { - qualified: true, - ..self - } - } - - #[must_use] - pub fn from_possibly_ambiguous_type_pair<'db>( + pub fn from_possibly_ambiguous_type_pair( db: &'db dyn Db, type_1: Type<'db>, type_2: Type<'db>, ) -> 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 { - return result; - }; - - let Some(class_2) = type_to_class_literal(db, type_2) else { - return result; - }; - - if class_1 == class_2 { - return result; - } - - if class_1.name(db) == class_2.name(db) { - result.qualified() - } else { - result + Self { + qualified: Rc::new( + collector + .class_names + .borrow() + .iter() + .filter_map(|(name, ambiguity)| ambiguity.is_ambiguous().then_some(*name)) + .collect(), + ), + ..Self::default() } } } -// TODO: generalize this to a method that takes any two types, walks them recursively, and returns -// a set of types with ambiguous names whose display should be qualified. Then we can use this in -// any diagnostic that displays two types. -fn type_to_class_literal<'db>(db: &'db dyn Db, ty: Type<'db>) -> Option> { - match ty { - Type::ClassLiteral(class) => Some(class), - Type::NominalInstance(instance) => Some(instance.class_literal(db)), - Type::EnumLiteral(enum_literal) => Some(enum_literal.enum_class(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())) +#[derive(Debug, Default)] +struct AmbiguousClassCollector<'db> { + visited_types: RefCell>>, + class_names: RefCell>>, +} + +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; + } + } } - Type::SubclassOf(subclass_of) => { - type_to_class_literal(db, Type::from(subclass_of.subclass_of().into_class()?)) + } +} + +/// 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 { + Type::ClassLiteral(class) => self.record_class(db, class), + Type::EnumLiteral(literal) => self.record_class(db, literal.enum_class(db)), + Type::GenericAlias(alias) => self.record_class(db, alias.origin(db)), + _ => {} + } + + 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> { - pub fn display(&self, db: &'db dyn Db) -> DisplayType<'_> { + pub fn display(self, db: &'db dyn Db) -> DisplayType<'db> { DisplayType { ty: self, settings: DisplaySettings::default(), 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 { ty: self, db, settings, } } + fn representation( self, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, ) -> DisplayRepresentation<'db> { DisplayRepresentation { db, @@ -135,16 +171,15 @@ impl<'db> Type<'db> { } } -#[derive(Copy, Clone)] pub struct DisplayType<'db> { - ty: &'db Type<'db>, + ty: Type<'db>, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, } impl Display for DisplayType<'_> { 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 { Type::IntLiteral(_) | Type::BooleanLiteral(_) @@ -165,7 +200,7 @@ impl fmt::Debug for DisplayType<'_> { } 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 { db, class: self, @@ -177,7 +212,7 @@ impl<'db> ClassLiteral<'db> { struct ClassDisplay<'db> { db: &'db dyn Db, class: ClassLiteral<'db>, - settings: DisplaySettings, + settings: DisplaySettings<'db>, } impl ClassDisplay<'_> { @@ -224,7 +259,11 @@ impl ClassDisplay<'_> { impl Display for ClassDisplay<'_> { 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() { f.write_str(&parent)?; f.write_char('.')?; @@ -240,7 +279,7 @@ impl Display for ClassDisplay<'_> { struct DisplayRepresentation<'db> { ty: Type<'db>, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, } impl Display for DisplayRepresentation<'_> { @@ -258,20 +297,20 @@ impl Display for DisplayRepresentation<'_> { .specialization(self.db) .tuple(self.db) .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), (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 { 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)) => { - alias.display_with(self.db, self.settings).fmt(f) + alias.display_with(self.db, self.settings.clone()).fmt(f) } Protocol::Synthesized(synthetic) => { f.write_str(" { Type::ClassLiteral(class) => write!( f, "", - class.display_with(self.db, self.settings) + class.display_with(self.db, self.settings.clone()) ), Type::GenericAlias(generic) => write!( f, @@ -304,7 +343,11 @@ impl Display for DisplayRepresentation<'_> { ), Type::SubclassOf(subclass_of_ty) => match subclass_of_ty.subclass_of() { 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)) => { write!( @@ -317,8 +360,12 @@ impl Display for DisplayRepresentation<'_> { }, Type::SpecialForm(special_form) => special_form.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::Callable(callable) => callable.display_with(self.db, self.settings).fmt(f), + Type::FunctionLiteral(function) => { + 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) => { let function = bound_method.function(self.db); let self_ty = bound_method.self_instance(self.db); @@ -329,7 +376,7 @@ impl Display for DisplayRepresentation<'_> { let type_parameters = DisplayOptionalGenericContext { generic_context: signature.generic_context.as_ref(), db: self.db, - settings: self.settings, + settings: self.settings.clone(), }; write!( @@ -340,7 +387,7 @@ impl Display for DisplayRepresentation<'_> { type_parameters = type_parameters, signature = signature .bind_self(self.db, Some(typing_self_ty)) - .display_with(self.db, self.settings) + .display_with(self.db, self.settings.clone()) ) } signatures => { @@ -354,7 +401,7 @@ impl Display for DisplayRepresentation<'_> { join.entry( &signature .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 { @@ -404,13 +451,15 @@ impl Display for DisplayRepresentation<'_> { Type::DataclassTransformer(_) => { f.write_str("") } - Type::Union(union) => union.display_with(self.db, self.settings).fmt(f), - Type::Intersection(intersection) => { - intersection.display_with(self.db, self.settings).fmt(f) - } + Type::Union(union) => union.display_with(self.db, self.settings.clone()).fmt(f), + Type::Intersection(intersection) => intersection + .display_with(self.db, self.settings.clone()) + .fmt(f), Type::IntLiteral(n) => n.fmt(f), 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::BytesLiteral(bytes) => { 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 = enum_literal .enum_class(self.db) - .display_with(self.db, self.settings), + .display_with(self.db, self.settings.clone()), literal_name = enum_literal.name(self.db) ), Type::NonInferableTypeVar(bound_typevar) | Type::TypeVar(bound_typevar) => { @@ -458,7 +507,7 @@ impl Display for DisplayRepresentation<'_> { .defining_class() .class_literal(self.db) .0 - .display_with(self.db, self.settings) + .display_with(self.db, self.settings.clone()) .fmt(f), Type::TypeAlias(alias) => f.write_str(alias.name(self.db)), } @@ -493,7 +542,7 @@ impl<'db> TupleSpec<'db> { pub(crate) fn display_with( &'db self, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, ) -> DisplayTuple<'db> { DisplayTuple { tuple: self, @@ -506,7 +555,7 @@ impl<'db> TupleSpec<'db> { pub(crate) struct DisplayTuple<'db> { tuple: &'db TupleSpec<'db>, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, } impl Display for DisplayTuple<'_> { @@ -580,7 +629,7 @@ impl<'db> OverloadLiteral<'db> { pub(crate) fn display_with( self, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, ) -> DisplayOverloadLiteral<'db> { DisplayOverloadLiteral { literal: self, @@ -593,7 +642,7 @@ impl<'db> OverloadLiteral<'db> { pub(crate) struct DisplayOverloadLiteral<'db> { literal: OverloadLiteral<'db>, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, } impl Display for DisplayOverloadLiteral<'_> { @@ -602,7 +651,7 @@ impl Display for DisplayOverloadLiteral<'_> { let type_parameters = DisplayOptionalGenericContext { generic_context: signature.generic_context.as_ref(), db: self.db, - settings: self.settings, + settings: self.settings.clone(), }; write!( @@ -610,7 +659,7 @@ impl Display for DisplayOverloadLiteral<'_> { "def {name}{type_parameters}{signature}", name = self.literal.name(self.db), 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( self, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, ) -> DisplayFunctionType<'db> { DisplayFunctionType { ty: self, @@ -632,7 +681,7 @@ impl<'db> FunctionType<'db> { pub(crate) struct DisplayFunctionType<'db> { ty: FunctionType<'db>, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, } impl Display for DisplayFunctionType<'_> { @@ -644,7 +693,7 @@ impl Display for DisplayFunctionType<'_> { let type_parameters = DisplayOptionalGenericContext { generic_context: signature.generic_context.as_ref(), db: self.db, - settings: self.settings, + settings: self.settings.clone(), }; write!( @@ -652,7 +701,7 @@ impl Display for DisplayFunctionType<'_> { "def {name}{type_parameters}{signature}", name = self.ty.name(self.db), type_parameters = type_parameters, - signature = signature.display_with(self.db, self.settings) + signature = signature.display_with(self.db, self.settings.clone()) ) } signatures => { @@ -663,7 +712,7 @@ impl Display for DisplayFunctionType<'_> { let separator = if self.settings.multiline { "\n" } else { ", " }; let mut join = f.join(separator); 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 { f.write_str("]")?; @@ -678,7 +727,7 @@ impl<'db> GenericAlias<'db> { pub(crate) fn display_with( &'db self, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, ) -> DisplayGenericAlias<'db> { DisplayGenericAlias { origin: self.origin(db), @@ -693,13 +742,13 @@ pub(crate) struct DisplayGenericAlias<'db> { origin: ClassLiteral<'db>, specialization: Specialization<'db>, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, } impl Display for DisplayGenericAlias<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 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 { let prefix = match self.specialization.materialization_kind(self.db) { None => "", @@ -714,10 +763,11 @@ impl Display for DisplayGenericAlias<'_> { f, "{prefix}{origin}{specialization}{suffix}", 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( self.db, - TupleSpecialization::from_class(self.db, self.origin) + TupleSpecialization::from_class(self.db, self.origin), + self.settings.clone() ), suffix = suffix, ) @@ -732,7 +782,7 @@ impl<'db> GenericContext<'db> { pub fn display_with( &'db self, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, ) -> DisplayGenericContext<'db> { DisplayGenericContext { generic_context: self, @@ -745,7 +795,7 @@ impl<'db> GenericContext<'db> { struct DisplayOptionalGenericContext<'db> { generic_context: Option<&'db GenericContext<'db>>, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, } impl Display for DisplayOptionalGenericContext<'_> { @@ -754,7 +804,7 @@ impl Display for DisplayOptionalGenericContext<'_> { DisplayGenericContext { generic_context, db: self.db, - settings: self.settings, + settings: self.settings.clone(), } .fmt(f) } else { @@ -767,7 +817,7 @@ pub struct DisplayGenericContext<'db> { generic_context: &'db GenericContext<'db>, db: &'db dyn Db, #[expect(dead_code)] - settings: DisplaySettings, + settings: DisplaySettings<'db>, } impl Display for DisplayGenericContext<'_> { @@ -800,12 +850,13 @@ impl<'db> Specialization<'db> { &'db self, db: &'db dyn Db, tuple_specialization: TupleSpecialization, + settings: DisplaySettings<'db>, ) -> DisplaySpecialization<'db> { DisplaySpecialization { types: self.types(db), db, tuple_specialization, - settings: DisplaySettings::default(), + settings, } } } @@ -814,7 +865,7 @@ pub struct DisplaySpecialization<'db> { types: &'db [Type<'db>], db: &'db dyn Db, tuple_specialization: TupleSpecialization, - settings: DisplaySettings, + settings: DisplaySettings<'db>, } impl Display for DisplaySpecialization<'_> { @@ -824,7 +875,7 @@ impl Display for DisplaySpecialization<'_> { if idx > 0 { 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() { f.write_str(", ...")?; @@ -861,7 +912,7 @@ impl<'db> CallableType<'db> { pub(crate) fn display_with( &'db self, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, ) -> DisplayCallableType<'db> { DisplayCallableType { signatures: self.signatures(db), @@ -874,13 +925,15 @@ impl<'db> CallableType<'db> { pub(crate) struct DisplayCallableType<'db> { signatures: &'db CallableSignature<'db>, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, } impl Display for DisplayCallableType<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 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 => { // TODO: How to display overloads? if !self.settings.multiline { @@ -889,7 +942,7 @@ impl Display for DisplayCallableType<'_> { let separator = if self.settings.multiline { "\n" } else { ", " }; let mut join = f.join(separator); 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()?; if !self.settings.multiline { @@ -909,7 +962,7 @@ impl<'db> Signature<'db> { pub(crate) fn display_with( &'db self, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, ) -> DisplaySignature<'db> { DisplaySignature { parameters: self.parameters(), @@ -924,7 +977,7 @@ pub(crate) struct DisplaySignature<'db> { parameters: &'db Parameters<'db>, return_ty: Option>, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, } impl DisplaySignature<'_> { @@ -1128,7 +1181,7 @@ impl<'db> Parameter<'db> { fn display_with( &'db self, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, ) -> DisplayParameter<'db> { DisplayParameter { param: self, @@ -1141,7 +1194,7 @@ impl<'db> Parameter<'db> { struct DisplayParameter<'db> { param: &'db Parameter<'db>, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, } impl Display for DisplayParameter<'_> { @@ -1152,21 +1205,29 @@ impl Display for DisplayParameter<'_> { write!( 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. if let Some(default_ty) = self.param.default_type() { 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 { - 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() { // This case is specifically for the `Callable` signature where name and default value // cannot be provided. - ty.display_with(self.db, self.settings).fmt(f)?; + ty.display_with(self.db, self.settings.clone()).fmt(f)?; } Ok(()) } @@ -1176,7 +1237,7 @@ impl<'db> UnionType<'db> { fn display_with( &'db self, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, ) -> DisplayUnionType<'db> { DisplayUnionType { db, @@ -1189,7 +1250,7 @@ impl<'db> UnionType<'db> { struct DisplayUnionType<'db> { ty: &'db UnionType<'db>, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, } impl Display for DisplayUnionType<'_> { @@ -1249,7 +1310,7 @@ impl fmt::Debug for DisplayUnionType<'_> { struct DisplayLiteralGroup<'db> { literals: Vec>, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, } impl Display for DisplayLiteralGroup<'_> { @@ -1270,7 +1331,7 @@ impl<'db> IntersectionType<'db> { fn display_with( &'db self, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, ) -> DisplayIntersectionType<'db> { DisplayIntersectionType { db, @@ -1283,7 +1344,7 @@ impl<'db> IntersectionType<'db> { struct DisplayIntersectionType<'db> { ty: &'db IntersectionType<'db>, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, } impl Display for DisplayIntersectionType<'_> { @@ -1323,7 +1384,7 @@ struct DisplayMaybeNegatedType<'db> { ty: Type<'db>, db: &'db dyn Db, negated: bool, - settings: DisplaySettings, + settings: DisplaySettings<'db>, } impl Display for DisplayMaybeNegatedType<'_> { @@ -1334,7 +1395,7 @@ impl Display for DisplayMaybeNegatedType<'_> { DisplayMaybeParenthesizedType { ty: self.ty, db: self.db, - settings: self.settings, + settings: self.settings.clone(), } .fmt(f) } @@ -1343,13 +1404,18 @@ impl Display for DisplayMaybeNegatedType<'_> { struct DisplayMaybeParenthesizedType<'db> { ty: Type<'db>, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, } impl Display for DisplayMaybeParenthesizedType<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let write_parentheses = - |f: &mut Formatter<'_>| write!(f, "({})", self.ty.display_with(self.db, self.settings)); + let write_parentheses = |f: &mut Formatter<'_>| { + write!( + f, + "({})", + self.ty.display_with(self.db, self.settings.clone()) + ) + }; match self.ty { Type::Callable(_) | Type::KnownBoundMethod(_) @@ -1359,21 +1425,24 @@ impl Display for DisplayMaybeParenthesizedType<'_> { Type::Intersection(intersection) if !intersection.has_one_element(self.db) => { 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> { - fn display_with(&self, db: &'db dyn Db, settings: DisplaySettings) - -> DisplayTypeArray<'_, 'db>; + fn display_with( + &self, + db: &'db dyn Db, + settings: DisplaySettings<'db>, + ) -> DisplayTypeArray<'_, 'db>; } impl<'db> TypeArrayDisplay<'db> for Box<[Type<'db>]> { fn display_with( &self, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, ) -> DisplayTypeArray<'_, 'db> { DisplayTypeArray { types: self, @@ -1387,7 +1456,7 @@ impl<'db> TypeArrayDisplay<'db> for Vec> { fn display_with( &self, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, ) -> DisplayTypeArray<'_, 'db> { DisplayTypeArray { types: self, @@ -1401,7 +1470,7 @@ impl<'db> TypeArrayDisplay<'db> for [Type<'db>] { fn display_with( &self, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, ) -> DisplayTypeArray<'_, 'db> { DisplayTypeArray { types: self, @@ -1414,7 +1483,7 @@ impl<'db> TypeArrayDisplay<'db> for [Type<'db>] { pub(crate) struct DisplayTypeArray<'b, 'db> { types: &'b [Type<'db>], db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'db>, } impl Display for DisplayTypeArray<'_, '_> { @@ -1433,20 +1502,19 @@ impl<'db> StringLiteralType<'db> { fn display_with( &'db self, db: &'db dyn Db, - settings: DisplaySettings, + settings: DisplaySettings<'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> { string: &'db str, #[expect(dead_code)] - settings: DisplaySettings, + settings: DisplaySettings<'db>, } impl Display for DisplayStringLiteralType<'_> { diff --git a/crates/ty_python_semantic/src/types/visitor.rs b/crates/ty_python_semantic/src/types/visitor.rs index 35d9d5f1a1..9ca0f98e4c 100644 --- a/crates/ty_python_semantic/src/types/visitor.rs +++ b/crates/ty_python_semantic/src/types/visitor.rs @@ -107,7 +107,7 @@ pub(crate) trait TypeVisitor<'db> { /// Enumeration of types that may contain other types, such as unions, intersections, and generics. #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] -enum NonAtomicType<'db> { +pub(super) enum NonAtomicType<'db> { Union(UnionType<'db>), Intersection(IntersectionType<'db>), FunctionLiteral(FunctionType<'db>), @@ -128,7 +128,7 @@ enum NonAtomicType<'db> { TypeAlias(TypeAliasType<'db>), } -enum TypeKind<'db> { +pub(super) enum TypeKind<'db> { Atomic, NonAtomic(NonAtomicType<'db>), } @@ -200,7 +200,7 @@ impl<'db> From> 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, non_atomic_type: NonAtomicType<'db>, visitor: &V,