//! Module for inferring the variance of type and lifetime parameters. See the [rustc dev guide] //! chapter for more info. //! //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/variance.html //! //! The implementation here differs from rustc. Rustc does a crate wide fixpoint resolution //! as the algorithm for determining variance is a fixpoint computation with potential cycles that //! need to be resolved. rust-analyzer does not want a crate-wide analysis though as that would hurt //! incrementality too much and as such our query is based on a per item basis. //! //! This does unfortunately run into the issue that we can run into query cycles which salsa //! currently does not allow to be resolved via a fixpoint computation. This will likely be resolved //! by the next salsa version. If not, we will likely have to adapt and go with the rustc approach //! while installing firewall per item queries to prevent invalidation issues. use crate::db::HirDatabase; use crate::generics::{generics, Generics}; use crate::{ AliasTy, Const, ConstScalar, DynTyExt, GenericArg, GenericArgData, Interner, Lifetime, LifetimeData, Ty, TyKind, }; use base_db::ra_salsa::Cycle; use chalk_ir::Mutability; use hir_def::data::adt::StructFlags; use hir_def::{AdtId, GenericDefId, GenericParamId, VariantId}; use std::fmt; use std::ops::Not; use stdx::never; use triomphe::Arc; pub(crate) fn variances_of(db: &dyn HirDatabase, def: GenericDefId) -> Option> { tracing::debug!("variances_of(def={:?})", def); match def { GenericDefId::FunctionId(_) => (), GenericDefId::AdtId(adt) => { if let AdtId::StructId(id) = adt { let flags = &db.struct_data(id).flags; if flags.contains(StructFlags::IS_UNSAFE_CELL) { return Some(Arc::from_iter(vec![Variance::Invariant; 1])); } else if flags.contains(StructFlags::IS_PHANTOM_DATA) { return Some(Arc::from_iter(vec![Variance::Covariant; 1])); } } } _ => return None, } let generics = generics(db.upcast(), def); let count = generics.len(); if count == 0 { return None; } let variances = Context { generics, variances: vec![Variance::Bivariant; count], db }.solve(); variances.is_empty().not().then(|| Arc::from_iter(variances)) } pub(crate) fn variances_of_cycle( db: &dyn HirDatabase, _cycle: &Cycle, def: &GenericDefId, ) -> Option> { let generics = generics(db.upcast(), *def); let count = generics.len(); if count == 0 { return None; } Some(Arc::from(vec![Variance::Bivariant; count])) } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum Variance { Covariant, // T <: T iff A <: B -- e.g., function return type Invariant, // T <: T iff B == A -- e.g., type of mutable cell Contravariant, // T <: T iff B <: A -- e.g., function param type Bivariant, // T <: T -- e.g., unused type parameter } impl fmt::Display for Variance { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Variance::Covariant => write!(f, "covariant"), Variance::Invariant => write!(f, "invariant"), Variance::Contravariant => write!(f, "contravariant"), Variance::Bivariant => write!(f, "bivariant"), } } } impl Variance { /// `a.xform(b)` combines the variance of a context with the /// variance of a type with the following meaning. If we are in a /// context with variance `a`, and we encounter a type argument in /// a position with variance `b`, then `a.xform(b)` is the new /// variance with which the argument appears. /// /// Example 1: /// ```ignore (illustrative) /// *mut Vec /// ``` /// Here, the "ambient" variance starts as covariant. `*mut T` is /// invariant with respect to `T`, so the variance in which the /// `Vec` appears is `Covariant.xform(Invariant)`, which /// yields `Invariant`. Now, the type `Vec` is covariant with /// respect to its type argument `T`, and hence the variance of /// the `i32` here is `Invariant.xform(Covariant)`, which results /// (again) in `Invariant`. /// /// Example 2: /// ```ignore (illustrative) /// fn(*const Vec, *mut Vec` appears is /// `Contravariant.xform(Covariant)` or `Contravariant`. The same /// is true for its `i32` argument. In the `*mut T` case, the /// variance of `Vec` is `Contravariant.xform(Invariant)`, /// and hence the outermost type is `Invariant` with respect to /// `Vec` (and its `i32` argument). /// /// Source: Figure 1 of "Taming the Wildcards: /// Combining Definition- and Use-Site Variance" published in PLDI'11. fn xform(self, v: Variance) -> Variance { match (self, v) { // Figure 1, column 1. (Variance::Covariant, Variance::Covariant) => Variance::Covariant, (Variance::Covariant, Variance::Contravariant) => Variance::Contravariant, (Variance::Covariant, Variance::Invariant) => Variance::Invariant, (Variance::Covariant, Variance::Bivariant) => Variance::Bivariant, // Figure 1, column 2. (Variance::Contravariant, Variance::Covariant) => Variance::Contravariant, (Variance::Contravariant, Variance::Contravariant) => Variance::Covariant, (Variance::Contravariant, Variance::Invariant) => Variance::Invariant, (Variance::Contravariant, Variance::Bivariant) => Variance::Bivariant, // Figure 1, column 3. (Variance::Invariant, _) => Variance::Invariant, // Figure 1, column 4. (Variance::Bivariant, _) => Variance::Bivariant, } } fn glb(self, v: Variance) -> Variance { // Greatest lower bound of the variance lattice as // defined in The Paper: // // * // - + // o match (self, v) { (Variance::Invariant, _) | (_, Variance::Invariant) => Variance::Invariant, (Variance::Covariant, Variance::Contravariant) => Variance::Invariant, (Variance::Contravariant, Variance::Covariant) => Variance::Invariant, (Variance::Covariant, Variance::Covariant) => Variance::Covariant, (Variance::Contravariant, Variance::Contravariant) => Variance::Contravariant, (x, Variance::Bivariant) | (Variance::Bivariant, x) => x, } } pub fn invariant(self) -> Self { self.xform(Variance::Invariant) } pub fn covariant(self) -> Self { self.xform(Variance::Covariant) } pub fn contravariant(self) -> Self { self.xform(Variance::Contravariant) } } struct Context<'db> { db: &'db dyn HirDatabase, generics: Generics, variances: Vec, } impl Context<'_> { fn solve(mut self) -> Vec { tracing::debug!("solve(generics={:?})", self.generics); match self.generics.def() { GenericDefId::AdtId(adt) => { let db = self.db; let mut add_constraints_from_variant = |variant| { let subst = self.generics.placeholder_subst(db); for (_, field) in db.field_types(variant).iter() { self.add_constraints_from_ty( &field.clone().substitute(Interner, &subst), Variance::Covariant, ); } }; match adt { AdtId::StructId(s) => add_constraints_from_variant(VariantId::StructId(s)), AdtId::UnionId(u) => add_constraints_from_variant(VariantId::UnionId(u)), AdtId::EnumId(e) => { db.enum_data(e).variants.iter().for_each(|&(variant, _)| { add_constraints_from_variant(VariantId::EnumVariantId(variant)) }); } } } GenericDefId::FunctionId(f) => { let subst = self.generics.placeholder_subst(self.db); self.add_constraints_from_sig( self.db .callable_item_signature(f.into()) .substitute(Interner, &subst) .params_and_return .iter(), Variance::Covariant, ); } _ => {} } let mut variances = self.variances; // Const parameters are always invariant. // Make all const parameters invariant. for (idx, param) in self.generics.iter_id().enumerate() { if let GenericParamId::ConstParamId(_) = param { variances[idx] = Variance::Invariant; } } // Functions are permitted to have unused generic parameters: make those invariant. if let GenericDefId::FunctionId(_) = self.generics.def() { variances .iter_mut() .filter(|&&mut v| v == Variance::Bivariant) .for_each(|v| *v = Variance::Invariant); } variances } /// Adds constraints appropriate for an instance of `ty` appearing /// in a context with the generics defined in `generics` and /// ambient variance `variance` fn add_constraints_from_ty(&mut self, ty: &Ty, variance: Variance) { tracing::debug!("add_constraints_from_ty(ty={:?}, variance={:?})", ty, variance); match ty.kind(Interner) { TyKind::Scalar(_) | TyKind::Never | TyKind::Str | TyKind::Foreign(..) => { // leaf type -- noop } TyKind::FnDef(..) | TyKind::Coroutine(..) | TyKind::Closure(..) => { never!("Unexpected unnameable type in variance computation: {:?}", ty); } TyKind::Ref(mutbl, lifetime, ty) => { self.add_constraints_from_region(lifetime, variance); self.add_constraints_from_mt(ty, *mutbl, variance); } TyKind::Array(typ, len) => { self.add_constraints_from_const(len, variance); self.add_constraints_from_ty(typ, variance); } TyKind::Slice(typ) => { self.add_constraints_from_ty(typ, variance); } TyKind::Raw(mutbl, ty) => { self.add_constraints_from_mt(ty, *mutbl, variance); } TyKind::Tuple(_, subtys) => { for subty in subtys.type_parameters(Interner) { self.add_constraints_from_ty(&subty, variance); } } TyKind::Adt(def, args) => { self.add_constraints_from_args(def.0.into(), args.as_slice(Interner), variance); } TyKind::Alias(AliasTy::Opaque(opaque)) => { self.add_constraints_from_invariant_args( opaque.substitution.as_slice(Interner), variance, ); } TyKind::Alias(AliasTy::Projection(proj)) => { self.add_constraints_from_invariant_args( proj.substitution.as_slice(Interner), variance, ); } // FIXME: check this TyKind::AssociatedType(_, subst) => { self.add_constraints_from_invariant_args(subst.as_slice(Interner), variance); } // FIXME: check this TyKind::OpaqueType(_, subst) => { self.add_constraints_from_invariant_args(subst.as_slice(Interner), variance); } TyKind::Dyn(it) => { // The type `dyn Trait +'a` is covariant w/r/t `'a`: self.add_constraints_from_region(&it.lifetime, variance); if let Some(trait_ref) = it.principal() { // Trait are always invariant so we can take advantage of that. self.add_constraints_from_invariant_args( trait_ref .map(|it| it.map(|it| it.substitution.clone())) .substitute( Interner, &[GenericArg::new( Interner, chalk_ir::GenericArgData::Ty(TyKind::Error.intern(Interner)), )], ) .skip_binders() .as_slice(Interner), variance, ); } // FIXME // for projection in data.projection_bounds() { // match projection.skip_binder().term.unpack() { // TyKind::TermKind::Ty(ty) => { // self.add_constraints_from_ty( ty, self.invariant); // } // TyKind::TermKind::Const(c) => { // self.add_constraints_from_const( c, self.invariant) // } // } // } } // Chalk has no params, so use placeholders for now? TyKind::Placeholder(index) => { let idx = crate::from_placeholder_idx(self.db, *index); let index = self.generics.type_or_const_param_idx(idx).unwrap(); self.constrain(index, variance); } TyKind::Function(f) => { self.add_constraints_from_sig( f.substitution.0.iter(Interner).filter_map(move |p| p.ty(Interner)), variance, ); } TyKind::Error => { // we encounter this when walking the trait references for object // types, where we use Error as the Self type } TyKind::CoroutineWitness(..) | TyKind::BoundVar(..) | TyKind::InferenceVar(..) => { never!("unexpected type encountered in variance inference: {:?}", ty) } } } fn add_constraints_from_invariant_args(&mut self, args: &[GenericArg], variance: Variance) { let variance_i = variance.invariant(); for k in args { match k.data(Interner) { GenericArgData::Lifetime(lt) => self.add_constraints_from_region(lt, variance_i), GenericArgData::Ty(ty) => self.add_constraints_from_ty(ty, variance_i), GenericArgData::Const(val) => self.add_constraints_from_const(val, variance_i), } } } /// Adds constraints appropriate for a nominal type (enum, struct, /// object, etc) appearing in a context with ambient variance `variance` fn add_constraints_from_args( &mut self, def_id: GenericDefId, args: &[GenericArg], variance: Variance, ) { // We don't record `inferred_starts` entries for empty generics. if args.is_empty() { return; } let Some(variances) = self.db.variances_of(def_id) else { return; }; for (i, k) in args.iter().enumerate() { match k.data(Interner) { GenericArgData::Lifetime(lt) => { self.add_constraints_from_region(lt, variance.xform(variances[i])) } GenericArgData::Ty(ty) => { self.add_constraints_from_ty(ty, variance.xform(variances[i])) } GenericArgData::Const(val) => self.add_constraints_from_const(val, variance), } } } /// Adds constraints appropriate for a const expression `val` /// in a context with ambient variance `variance` fn add_constraints_from_const(&mut self, c: &Const, variance: Variance) { match &c.data(Interner).value { chalk_ir::ConstValue::Concrete(c) => { if let ConstScalar::UnevaluatedConst(_, subst) = &c.interned { self.add_constraints_from_invariant_args(subst.as_slice(Interner), variance); } } _ => {} } } /// Adds constraints appropriate for a function with signature /// `sig` appearing in a context with ambient variance `variance` fn add_constraints_from_sig<'a>( &mut self, mut sig_tys: impl DoubleEndedIterator, variance: Variance, ) { let contra = variance.contravariant(); let Some(output) = sig_tys.next_back() else { return never!("function signature has no return type"); }; self.add_constraints_from_ty(output, variance); for input in sig_tys { self.add_constraints_from_ty(input, contra); } } /// Adds constraints appropriate for a region appearing in a /// context with ambient variance `variance` fn add_constraints_from_region(&mut self, region: &Lifetime, variance: Variance) { tracing::debug!( "add_constraints_from_region(region={:?}, variance={:?})", region, variance ); match region.data(Interner) { LifetimeData::Placeholder(index) => { let idx = crate::lt_from_placeholder_idx(self.db, *index); let inferred = self.generics.lifetime_idx(idx).unwrap(); self.constrain(inferred, variance); } LifetimeData::Static => {} LifetimeData::BoundVar(..) => { // Either a higher-ranked region inside of a type or a // late-bound function parameter. // // We do not compute constraints for either of these. } LifetimeData::Error => {} LifetimeData::Phantom(..) | LifetimeData::InferenceVar(..) | LifetimeData::Erased => { // We don't expect to see anything but 'static or bound // regions when visiting member types or method types. never!( "unexpected region encountered in variance \ inference: {:?}", region ); } } } /// Adds constraints appropriate for a mutability-type pair /// appearing in a context with ambient variance `variance` fn add_constraints_from_mt(&mut self, ty: &Ty, mt: Mutability, variance: Variance) { self.add_constraints_from_ty( ty, match mt { Mutability::Mut => variance.invariant(), Mutability::Not => variance, }, ); } fn constrain(&mut self, index: usize, variance: Variance) { tracing::debug!( "constrain(index={:?}, variance={:?}, to={:?})", index, self.variances[index], variance ); self.variances[index] = self.variances[index].glb(variance); } } #[cfg(test)] mod tests { use expect_test::{expect, Expect}; use hir_def::{ generics::GenericParamDataRef, src::HasSource, AdtId, GenericDefId, ModuleDefId, }; use itertools::Itertools; use stdx::format_to; use syntax::{ast::HasName, AstNode}; use test_fixture::WithFixture; use hir_def::Lookup; use crate::{db::HirDatabase, test_db::TestDB, variance::generics}; #[test] fn phantom_data() { check( r#" //- minicore: phantom_data struct Covariant { t: core::marker::PhantomData } "#, expect![[r#" Covariant[A: covariant] "#]], ); } #[test] fn rustc_test_variance_types() { check( r#" //- minicore: cell use core::cell::UnsafeCell; struct InvariantMut<'a,A:'a,B:'a> { //~ ERROR ['a: +, A: o, B: o] t: &'a mut (A,B) } struct InvariantCell { //~ ERROR [A: o] t: UnsafeCell } struct InvariantIndirect { //~ ERROR [A: o] t: InvariantCell } struct Covariant { //~ ERROR [A: +] t: A, u: fn() -> A } struct Contravariant { //~ ERROR [A: -] t: fn(A) } enum Enum { //~ ERROR [A: +, B: -, C: o] Foo(Covariant), Bar(Contravariant),` Zed(Covariant,Contravariant) } "#, expect![[r#" InvariantMut['a: covariant, A: invariant, B: invariant] InvariantCell[A: invariant] InvariantIndirect[A: invariant] Covariant[A: covariant] Contravariant[A: contravariant] Enum[A: covariant, B: contravariant, C: invariant] "#]], ); } #[test] fn type_resolve_error_two_structs_deep() { check( r#" struct Hello<'a> { missing: Missing<'a>, } struct Other<'a> { hello: Hello<'a>, } "#, expect![[r#" Hello['a: bivariant] Other['a: bivariant] "#]], ); } #[test] fn rustc_test_variance_associated_consts() { // FIXME: Should be invariant check( r#" trait Trait { const Const: usize; } struct Foo { //~ ERROR [T: o] field: [u8; ::Const] } "#, expect![[r#" Foo[T: bivariant] "#]], ); } #[test] fn rustc_test_variance_associated_types() { check( r#" trait Trait<'a> { type Type; fn method(&'a self) { } } struct Foo<'a, T : Trait<'a>> { //~ ERROR ['a: +, T: +] field: (T, &'a ()) } struct Bar<'a, T : Trait<'a>> { //~ ERROR ['a: o, T: o] field: >::Type } "#, expect![[r#" method[Self: contravariant, 'a: contravariant] Foo['a: covariant, T: covariant] Bar['a: invariant, T: invariant] "#]], ); } #[test] fn rustc_test_variance_associated_types2() { // FIXME: RPITs have variance, but we can't treat them as their own thing right now check( r#" trait Foo { type Bar; } fn make() -> *const dyn Foo {} "#, expect![""], ); } #[test] fn rustc_test_variance_trait_bounds() { check( r#" trait Getter { fn get(&self) -> T; } trait Setter { fn get(&self, _: T); } struct TestStruct> { //~ ERROR [U: +, T: +] t: T, u: U } enum TestEnum> { //~ ERROR [U: *, T: +] //~^ ERROR: `U` is never used Foo(T) } struct TestContraStruct> { //~ ERROR [U: *, T: +] //~^ ERROR: `U` is never used t: T } struct TestBox+Setter> { //~ ERROR [U: *, T: +] //~^ ERROR: `U` is never used t: T } "#, expect![[r#" get[Self: contravariant, T: covariant] get[Self: contravariant, T: contravariant] TestStruct[U: covariant, T: covariant] TestEnum[U: bivariant, T: covariant] TestContraStruct[U: bivariant, T: covariant] TestBox[U: bivariant, T: covariant] "#]], ); } #[test] fn rustc_test_variance_trait_matching() { check( r#" trait Get { fn get(&self) -> T; } struct Cloner { t: T } impl Get for Cloner { fn get(&self) -> T {} } fn get<'a, G>(get: &G) -> i32 where G : Get<&'a i32> {} fn pick<'b, G>(get: &'b G, if_odd: &'b i32) -> i32 where G : Get<&'b i32> {} "#, expect![[r#" get[Self: contravariant, T: covariant] Cloner[T: covariant] get[T: invariant] get['a: invariant, G: contravariant] pick['b: contravariant, G: contravariant] "#]], ); } #[test] fn rustc_test_variance_trait_object_bound() { check( r#" enum Option { Some(T), None } trait T { fn foo(&self); } struct TOption<'a> { //~ ERROR ['a: +] v: Option<*const (dyn T + 'a)>, } "#, expect![[r#" Option[T: covariant] foo[Self: contravariant] TOption['a: covariant] "#]], ); } #[test] fn rustc_test_variance_types_bounds() { check( r#" //- minicore: send struct TestImm { //~ ERROR [A: +, B: +] x: A, y: B, } struct TestMut { //~ ERROR [A: +, B: o] x: A, y: &'static mut B, } struct TestIndirect { //~ ERROR [A: +, B: o] m: TestMut } struct TestIndirect2 { //~ ERROR [A: o, B: o] n: TestMut, m: TestMut } trait Getter { fn get(&self) -> A; } trait Setter { fn set(&mut self, a: A); } struct TestObject { //~ ERROR [A: o, R: o] n: *const (dyn Setter + Send), m: *const (dyn Getter + Send), } "#, expect![[r#" TestImm[A: covariant, B: covariant] TestMut[A: covariant, B: invariant] TestIndirect[A: covariant, B: invariant] TestIndirect2[A: invariant, B: invariant] get[Self: contravariant, A: covariant] set[Self: invariant, A: contravariant] TestObject[A: invariant, R: invariant] "#]], ); } #[test] fn rustc_test_variance_unused_region_param() { check( r#" struct SomeStruct<'a> { x: u32 } //~ ERROR parameter `'a` is never used enum SomeEnum<'a> { Nothing } //~ ERROR parameter `'a` is never used trait SomeTrait<'a> { fn foo(&self); } // OK on traits. "#, expect![[r#" SomeStruct['a: bivariant] SomeEnum['a: bivariant] foo[Self: contravariant, 'a: invariant] "#]], ); } #[test] fn rustc_test_variance_unused_type_param() { check( r#" //- minicore: sized struct SomeStruct { x: u32 } enum SomeEnum { Nothing } enum ListCell { Cons(*const ListCell), Nil } struct SelfTyAlias(*const Self); struct WithBounds {} struct WithWhereBounds where T: Sized {} struct WithOutlivesBounds {} struct DoubleNothing { s: SomeStruct, } "#, expect![[r#" SomeStruct[A: bivariant] SomeEnum[A: bivariant] ListCell[T: bivariant] SelfTyAlias[T: bivariant] WithBounds[T: bivariant] WithWhereBounds[T: bivariant] WithOutlivesBounds[T: bivariant] DoubleNothing[T: bivariant] "#]], ); } #[test] fn rustc_test_variance_use_contravariant_struct1() { check( r#" struct SomeStruct(fn(T)); fn foo<'min,'max>(v: SomeStruct<&'max ()>) -> SomeStruct<&'min ()> where 'max : 'min {} "#, expect![[r#" SomeStruct[T: contravariant] foo['min: contravariant, 'max: covariant] "#]], ); } #[test] fn rustc_test_variance_use_contravariant_struct2() { check( r#" struct SomeStruct(fn(T)); fn bar<'min,'max>(v: SomeStruct<&'min ()>) -> SomeStruct<&'max ()> where 'max : 'min {} "#, expect![[r#" SomeStruct[T: contravariant] bar['min: covariant, 'max: contravariant] "#]], ); } #[test] fn rustc_test_variance_use_covariant_struct1() { check( r#" struct SomeStruct(T); fn foo<'min,'max>(v: SomeStruct<&'min ()>) -> SomeStruct<&'max ()> where 'max : 'min {} "#, expect![[r#" SomeStruct[T: covariant] foo['min: contravariant, 'max: covariant] "#]], ); } #[test] fn rustc_test_variance_use_covariant_struct2() { check( r#" struct SomeStruct(T); fn foo<'min,'max>(v: SomeStruct<&'max ()>) -> SomeStruct<&'min ()> where 'max : 'min {} "#, expect![[r#" SomeStruct[T: covariant] foo['min: covariant, 'max: contravariant] "#]], ); } #[test] fn rustc_test_variance_use_invariant_struct1() { check( r#" struct SomeStruct(*mut T); fn foo<'min,'max>(v: SomeStruct<&'max ()>) -> SomeStruct<&'min ()> where 'max : 'min {} fn bar<'min,'max>(v: SomeStruct<&'min ()>) -> SomeStruct<&'max ()> where 'max : 'min {} "#, expect![[r#" SomeStruct[T: invariant] foo['min: invariant, 'max: invariant] bar['min: invariant, 'max: invariant] "#]], ); } #[test] fn invalid_arg_counts() { check( r#" struct S(T); struct S2(S<>); struct S3(S); "#, expect![[r#" S[T: covariant] S2[T: bivariant] S3[T: covariant] "#]], ); } #[test] fn prove_fixedpoint() { // FIXME: This is wrong, this should be `FixedPoint[T: covariant, U: covariant, V: covariant]` // This is a limitation of current salsa where a cycle may only set a fallback value to the // query result, but we need to solve a fixpoint here. The new salsa will have this // fortunately. check( r#" struct FixedPoint(&'static FixedPoint<(), T, U>, V); "#, expect![[r#" FixedPoint[T: bivariant, U: bivariant, V: bivariant] "#]], ); } #[track_caller] fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected: Expect) { // use tracing_subscriber::{layer::SubscriberExt, Layer}; // let my_layer = tracing_subscriber::fmt::layer(); // let _g = tracing::subscriber::set_default(tracing_subscriber::registry().with( // my_layer.with_filter(tracing_subscriber::filter::filter_fn(|metadata| { // metadata.target().starts_with("hir_ty::variance") // })), // )); let (db, file_id) = TestDB::with_single_file(ra_fixture); let mut defs: Vec = Vec::new(); let module = db.module_for_file_opt(file_id).unwrap(); let def_map = module.def_map(&db); crate::tests::visit_module(&db, &def_map, module.local_id, &mut |it| { defs.push(match it { ModuleDefId::FunctionId(it) => it.into(), ModuleDefId::AdtId(it) => it.into(), ModuleDefId::ConstId(it) => it.into(), ModuleDefId::TraitId(it) => it.into(), ModuleDefId::TraitAliasId(it) => it.into(), ModuleDefId::TypeAliasId(it) => it.into(), _ => return, }) }); let defs = defs .into_iter() .filter_map(|def| { Some(( def, match def { GenericDefId::FunctionId(it) => { let loc = it.lookup(&db); loc.source(&db).value.name().unwrap() } GenericDefId::AdtId(AdtId::EnumId(it)) => { let loc = it.lookup(&db); loc.source(&db).value.name().unwrap() } GenericDefId::AdtId(AdtId::StructId(it)) => { let loc = it.lookup(&db); loc.source(&db).value.name().unwrap() } GenericDefId::AdtId(AdtId::UnionId(it)) => { let loc = it.lookup(&db); loc.source(&db).value.name().unwrap() } GenericDefId::TraitId(it) => { let loc = it.lookup(&db); loc.source(&db).value.name().unwrap() } GenericDefId::TraitAliasId(it) => { let loc = it.lookup(&db); loc.source(&db).value.name().unwrap() } GenericDefId::TypeAliasId(it) => { let loc = it.lookup(&db); loc.source(&db).value.name().unwrap() } GenericDefId::ImplId(_) => return None, GenericDefId::ConstId(_) => return None, GenericDefId::StaticId(_) => return None, }, )) }) .sorted_by_key(|(_, n)| n.syntax().text_range().start()); let mut res = String::new(); for (def, name) in defs { let Some(variances) = db.variances_of(def) else { continue; }; format_to!( res, "{name}[{}]\n", generics(&db, def) .iter() .map(|(_, param)| match param { GenericParamDataRef::TypeParamData(type_param_data) => { type_param_data.name.as_ref().unwrap() } GenericParamDataRef::ConstParamData(const_param_data) => &const_param_data.name, GenericParamDataRef::LifetimeParamData(lifetime_param_data) => { &lifetime_param_data.name } }) .zip_eq(&*variances) .format_with(", ", |(name, var), f| f(&format_args!( "{}: {var}", name.as_str() ))) ); } expected.assert_eq(&res); } }