about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer.rs50
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs17
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs244
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs21
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs24
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs12
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs29
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs18
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs65
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/tests.rs71
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/runnables.rs37
19 files changed, 403 insertions, 235 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
index 265d1f8541c..3d91a2558f0 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -54,6 +54,8 @@ use rustc_hash::{FxHashMap, FxHashSet};
 use stdx::{always, never};
 use triomphe::Arc;
 
+use crate::next_solver::DbInterner;
+use crate::next_solver::mapping::NextSolverToChalk;
 use crate::{
     AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, ImplTraitId, ImplTraitIdx,
     IncorrectGenericsLenKind, Interner, Lifetime, OpaqueTyId, ParamLoweringMode,
@@ -922,13 +924,15 @@ impl<'db> InferenceContext<'db> {
         });
         diagnostics.shrink_to_fit();
         for (_, subst) in method_resolutions.values_mut() {
-            *subst = table.resolve_completely(subst.clone());
+            *subst =
+                table.resolve_completely::<_, crate::next_solver::GenericArgs<'db>>(subst.clone());
             *has_errors =
                 *has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown());
         }
         method_resolutions.shrink_to_fit();
         for (_, subst) in assoc_resolutions.values_mut() {
-            *subst = table.resolve_completely(subst.clone());
+            *subst =
+                table.resolve_completely::<_, crate::next_solver::GenericArgs<'db>>(subst.clone());
             *has_errors =
                 *has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown());
         }
@@ -946,7 +950,12 @@ impl<'db> InferenceContext<'db> {
         result.tuple_field_access_types = tuple_field_accesses_rev
             .into_iter()
             .enumerate()
-            .map(|(idx, subst)| (TupleId(idx as u32), table.resolve_completely(subst)))
+            .map(|(idx, subst)| {
+                (
+                    TupleId(idx as u32),
+                    table.resolve_completely::<_, crate::next_solver::GenericArgs<'db>>(subst),
+                )
+            })
             .inspect(|(_, subst)| {
                 *has_errors =
                     *has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown());
@@ -1015,14 +1024,12 @@ impl<'db> InferenceContext<'db> {
         if let Some(self_param) = self.body.self_param
             && let Some(ty) = param_tys.next()
         {
-            let ty = self.insert_type_vars(ty);
-            let ty = self.normalize_associated_types_in(ty);
+            let ty = self.process_user_written_ty(ty);
             self.write_binding_ty(self_param, ty);
         }
         let mut tait_candidates = FxHashSet::default();
         for (ty, pat) in param_tys.zip(&*self.body.params) {
-            let ty = self.insert_type_vars(ty);
-            let ty = self.normalize_associated_types_in(ty);
+            let ty = self.process_user_written_ty(ty);
 
             self.infer_top_pat(*pat, &ty, None);
             if ty
@@ -1073,7 +1080,7 @@ impl<'db> InferenceContext<'db> {
             None => self.result.standard_types.unit.clone(),
         };
 
-        self.return_ty = self.normalize_associated_types_in(return_ty);
+        self.return_ty = self.process_user_written_ty(return_ty);
         self.return_coercion = Some(CoerceMany::new(self.return_ty.clone()));
 
         // Functions might be defining usage sites of TAITs.
@@ -1415,8 +1422,7 @@ impl<'db> InferenceContext<'db> {
     ) -> Ty {
         let ty = self
             .with_ty_lowering(store, type_source, lifetime_elision, |ctx| ctx.lower_ty(type_ref));
-        let ty = self.insert_type_vars(ty);
-        self.normalize_associated_types_in(ty)
+        self.process_user_written_ty(ty)
     }
 
     fn make_body_ty(&mut self, type_ref: TypeRefId) -> Ty {
@@ -1562,15 +1568,35 @@ impl<'db> InferenceContext<'db> {
         ty
     }
 
+    /// Whenever you lower a user-written type, you should call this.
+    fn process_user_written_ty<T, U>(&mut self, ty: T) -> T
+    where
+        T: HasInterner<Interner = Interner> + TypeFoldable<Interner> + ChalkToNextSolver<'db, U>,
+        U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable<DbInterner<'db>>,
+    {
+        self.table.process_user_written_ty(ty)
+    }
+
+    /// The difference of this method from `process_user_written_ty()` is that this method doesn't register a well-formed obligation,
+    /// while `process_user_written_ty()` should (but doesn't currently).
+    fn process_remote_user_written_ty<T, U>(&mut self, ty: T) -> T
+    where
+        T: HasInterner<Interner = Interner> + TypeFoldable<Interner> + ChalkToNextSolver<'db, U>,
+        U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable<DbInterner<'db>>,
+    {
+        self.table.process_remote_user_written_ty(ty)
+    }
+
     /// Recurses through the given type, normalizing associated types mentioned
     /// in it by replacing them by type variables and registering obligations to
     /// resolve later. This should be done once for every type we get from some
     /// type annotation (e.g. from a let type annotation, field type or function
     /// call). `make_ty` handles this already, but e.g. for field types we need
     /// to do it as well.
-    fn normalize_associated_types_in<T>(&mut self, ty: T) -> T
+    fn normalize_associated_types_in<T, U>(&mut self, ty: T) -> T
     where
-        T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
+        T: HasInterner<Interner = Interner> + TypeFoldable<Interner> + ChalkToNextSolver<'db, U>,
+        U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable<DbInterner<'db>>,
     {
         self.table.normalize_associated_types_in(ty)
     }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
index 5a69ad68b58..fd7e5a6a0e1 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
@@ -53,7 +53,7 @@ pub(super) struct ClosureSignature {
     pub(super) expected_sig: FnPointer,
 }
 
-impl InferenceContext<'_> {
+impl<'db> InferenceContext<'db> {
     pub(super) fn infer_closure(
         &mut self,
         body: &ExprId,
@@ -71,9 +71,13 @@ impl InferenceContext<'_> {
             None => (None, None),
         };
 
-        let ClosureSignature { expected_sig: bound_sig, ret_ty: body_ret_ty } =
+        let ClosureSignature { expected_sig: mut bound_sig, ret_ty: body_ret_ty } =
             self.sig_of_closure(body, ret_type, arg_types, closure_kind, expected_sig);
-        let bound_sig = self.normalize_associated_types_in(bound_sig);
+        bound_sig.substitution.0 = self
+            .normalize_associated_types_in::<_, crate::next_solver::GenericArgs<'db>>(
+                bound_sig.substitution.0,
+            );
+        let bound_sig = bound_sig;
         let sig_ty = TyKind::Function(bound_sig.clone()).intern(Interner);
 
         let (id, ty, resume_yield_tys) = match closure_kind {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
index bfeb5bae851..0a58ea11bb8 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
@@ -1419,7 +1419,7 @@ impl InferenceContext<'_> {
             None => self.err_ty(),
         };
 
-        let ret_ty = self.normalize_associated_types_in(ret_ty);
+        let ret_ty = self.process_remote_user_written_ty(ret_ty);
 
         if self.is_builtin_binop(&lhs_ty, &rhs_ty, op) {
             // use knowledge of built-in binary ops, which can sometimes help inference
@@ -1630,8 +1630,7 @@ impl InferenceContext<'_> {
         Some(match res {
             Some((field_id, ty)) => {
                 let adjustments = auto_deref_adjust_steps(&autoderef);
-                let ty = self.insert_type_vars(ty);
-                let ty = self.normalize_associated_types_in(ty);
+                let ty = self.process_remote_user_written_ty(ty);
 
                 (ty, field_id, adjustments, true)
             }
@@ -1641,8 +1640,7 @@ impl InferenceContext<'_> {
                 let ty = self.db.field_types(field_id.parent)[field_id.local_id]
                     .clone()
                     .substitute(Interner, &subst);
-                let ty = self.insert_type_vars(ty);
-                let ty = self.normalize_associated_types_in(ty);
+                let ty = self.process_remote_user_written_ty(ty);
 
                 (ty, Either::Left(field_id), adjustments, false)
             }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
index 16489e3068f..6781bc84d1c 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
@@ -88,7 +88,7 @@ impl InferenceContext<'_> {
                                     Some(substs) => f.substitute(Interner, substs),
                                     None => f.substitute(Interner, &Substitution::empty(Interner)),
                                 };
-                                self.normalize_associated_types_in(expected_ty)
+                                self.process_remote_user_written_ty(expected_ty)
                             }
                             None => self.err_ty(),
                         }
@@ -152,7 +152,7 @@ impl InferenceContext<'_> {
                                     Some(substs) => f.substitute(Interner, substs),
                                     None => f.substitute(Interner, &Substitution::empty(Interner)),
                                 };
-                                self.normalize_associated_types_in(expected_ty)
+                                self.process_remote_user_written_ty(expected_ty)
                             }
                             None => {
                                 self.push_diagnostic(InferenceDiagnostic::NoSuchField {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
index 1f62f9c4aa1..80f7324e58b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
@@ -23,7 +23,7 @@ use crate::{
 
 use super::{ExprOrPatId, InferenceContext, InferenceTyDiagnosticSource};
 
-impl InferenceContext<'_> {
+impl<'db> InferenceContext<'db> {
     pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty> {
         let (value_def, generic_def, substs) = match self.resolve_value_path(path, id)? {
             ValuePathResolution::GenericDef(value_def, generic_def, substs) => {
@@ -31,13 +31,13 @@ impl InferenceContext<'_> {
             }
             ValuePathResolution::NonGeneric(ty) => return Some(ty),
         };
-        let substs = self.insert_type_vars(substs);
-        let substs = self.normalize_associated_types_in(substs);
+        let substs =
+            self.process_remote_user_written_ty::<_, crate::next_solver::GenericArgs<'db>>(substs);
 
         self.add_required_obligations_for_value_path(generic_def, &substs);
 
         let ty = self.db.value_ty(value_def)?.substitute(Interner, &substs);
-        let ty = self.normalize_associated_types_in(ty);
+        let ty = self.process_remote_user_written_ty(ty);
         Some(ty)
     }
 
@@ -173,14 +173,12 @@ impl InferenceContext<'_> {
             let last = path.segments().last()?;
 
             let (ty, orig_ns) = path_ctx.ty_ctx().lower_ty_ext(type_ref);
-            let ty = self.table.insert_type_vars(ty);
-            let ty = self.table.normalize_associated_types_in(ty);
+            let ty = self.table.process_user_written_ty(ty);
 
             path_ctx.ignore_last_segment();
             let (ty, _) = path_ctx.lower_ty_relative_path(ty, orig_ns, true);
             drop_ctx(ctx, no_diagnostics);
-            let ty = self.table.insert_type_vars(ty);
-            let ty = self.table.normalize_associated_types_in(ty);
+            let ty = self.table.process_user_written_ty(ty);
             self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))?
         } else {
             let hygiene = self.body.expr_or_pat_path_hygiene(id);
@@ -223,8 +221,7 @@ impl InferenceContext<'_> {
                                 return None;
                             }
 
-                            let ty = self.insert_type_vars(ty);
-                            let ty = self.normalize_associated_types_in(ty);
+                            let ty = self.process_user_written_ty(ty);
 
                             self.resolve_ty_assoc_item(ty, last_segment.name, id)
                         }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
index ec4b7ee85dc..19b83d3c212 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
@@ -12,12 +12,14 @@ use hir_expand::name::Name;
 use intern::sym;
 use rustc_hash::{FxHashMap, FxHashSet};
 use rustc_next_trait_solver::solve::HasChanged;
+use rustc_type_ir::inherent::IntoKind;
 use rustc_type_ir::{
     AliasRelationDirection, FloatVid, IntVid, TyVid,
     inherent::{Span, Term as _},
     relate::{Relate, solver_relating::RelateExt},
     solve::{Certainty, NoSolution},
 };
+use rustc_type_ir::{TypeSuperFoldable, TypeVisitableExt};
 use smallvec::SmallVec;
 use triomphe::Arc;
 
@@ -31,11 +33,8 @@ use crate::{
     db::HirDatabase,
     fold_generic_args, fold_tys_and_consts,
     next_solver::{
-        self, Binder, DbInterner, ParamEnvAnd, Predicate, PredicateKind, SolverDefIds, Term,
-        infer::{
-            DbInternerInferExt, InferCtxt, canonical::canonicalizer::OriginalQueryValues,
-            snapshot::CombinedSnapshot,
-        },
+        self, Binder, DbInterner, Predicate, PredicateKind, SolverDefIds, Term,
+        infer::{DbInternerInferExt, InferCtxt, snapshot::CombinedSnapshot},
         mapping::{ChalkToNextSolver, InferenceVarExt, NextSolverToChalk},
     },
     to_chalk_trait_id,
@@ -305,116 +304,21 @@ impl<'a> InferenceTable<'a> {
     /// type annotation (e.g. from a let type annotation, field type or function
     /// call). `make_ty` handles this already, but e.g. for field types we need
     /// to do it as well.
-    #[tracing::instrument(skip(self), ret)]
-    pub(crate) fn normalize_associated_types_in<T>(&mut self, ty: T) -> T
+    pub(crate) fn normalize_associated_types_in<T, U>(&mut self, ty: T) -> T
     where
-        T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
+        T: ChalkToNextSolver<'a, U>,
+        U: NextSolverToChalk<'a, T> + rustc_type_ir::TypeFoldable<DbInterner<'a>>,
     {
-        fold_tys_and_consts(
-            ty,
-            |e, _| match e {
-                Either::Left(ty) => {
-                    let ty = self.resolve_ty_shallow(&ty);
-                    tracing::debug!(?ty);
-                    Either::Left(match ty.kind(Interner) {
-                        TyKind::Alias(AliasTy::Projection(proj_ty)) => {
-                            let ty = self.normalize_projection_ty(proj_ty.clone());
-                            self.resolve_ty_shallow(&ty)
-                        }
-                        TyKind::AssociatedType(id, subst) => {
-                            // return Either::Left(self.resolve_ty_shallow(&ty));
-                            if ty.data(Interner).flags.intersects(
-                                chalk_ir::TypeFlags::HAS_TY_INFER
-                                    | chalk_ir::TypeFlags::HAS_CT_INFER,
-                            ) {
-                                return Either::Left(ty);
-                            }
-                            let var = self.new_type_var();
-                            let proj_ty = chalk_ir::ProjectionTy {
-                                associated_ty_id: *id,
-                                substitution: subst.clone(),
-                            };
-                            let normalize = chalk_ir::Normalize {
-                                alias: AliasTy::Projection(proj_ty),
-                                ty: var.clone(),
-                            };
-                            let goal = chalk_ir::Goal::new(
-                                Interner,
-                                chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Normalize(
-                                    normalize,
-                                )),
-                            );
-                            let in_env = InEnvironment::new(&self.trait_env.env, goal);
-                            let goal = in_env.to_nextsolver(self.interner);
-                            let goal =
-                                ParamEnvAnd { param_env: goal.param_env, value: goal.predicate };
-
-                            let (canonical_goal, orig_values) = {
-                                let mut orig_values = OriginalQueryValues::default();
-                                let result =
-                                    self.infer_ctxt.canonicalize_query(goal, &mut orig_values);
-                                (result.canonical, orig_values)
-                            };
-                            let canonical_goal = rustc_type_ir::Canonical {
-                                max_universe: canonical_goal.max_universe,
-                                variables: canonical_goal.variables,
-                                value: crate::next_solver::Goal {
-                                    param_env: canonical_goal.value.param_env,
-                                    predicate: canonical_goal.value.value,
-                                },
-                            };
-                            let solution = next_trait_solve_canonical_in_ctxt(
-                                &self.infer_ctxt,
-                                canonical_goal,
-                            );
-                            if let NextTraitSolveResult::Certain(canonical_subst) = solution {
-                                let subst = self.instantiate_canonical(canonical_subst).subst;
-                                if subst.len(Interner) != orig_values.var_values.len() {
-                                    ty
-                                } else {
-                                    let target_ty = var.to_nextsolver(self.interner);
-                                    subst
-                                        .iter(Interner)
-                                        .zip(orig_values.var_values.iter())
-                                        .find_map(|(new, orig)| {
-                                            if orig.ty() == Some(target_ty) {
-                                                Some(new.assert_ty_ref(Interner).clone())
-                                            } else {
-                                                None
-                                            }
-                                        })
-                                        .unwrap_or(ty)
-                                }
-                            } else {
-                                ty
-                            }
-                        }
-                        _ => ty,
-                    })
-                }
-                Either::Right(c) => Either::Right(match &c.data(Interner).value {
-                    chalk_ir::ConstValue::Concrete(cc) => match &cc.interned {
-                        crate::ConstScalar::UnevaluatedConst(c_id, subst) => {
-                            // FIXME: Ideally here we should do everything that we do with type alias, i.e. adding a variable
-                            // and registering an obligation. But it needs chalk support, so we handle the most basic
-                            // case (a non associated const without generic parameters) manually.
-                            if subst.len(Interner) == 0 {
-                                if let Ok(eval) = self.db.const_eval(*c_id, subst.clone(), None) {
-                                    eval
-                                } else {
-                                    unknown_const(c.data(Interner).ty.clone())
-                                }
-                            } else {
-                                unknown_const(c.data(Interner).ty.clone())
-                            }
-                        }
-                        _ => c,
-                    },
-                    _ => c,
-                }),
-            },
-            DebruijnIndex::INNERMOST,
-        )
+        self.normalize_associated_types_in_ns(ty.to_nextsolver(self.interner))
+            .to_chalk(self.interner)
+    }
+
+    pub(crate) fn normalize_associated_types_in_ns<T>(&mut self, ty: T) -> T
+    where
+        T: rustc_type_ir::TypeFoldable<DbInterner<'a>>,
+    {
+        let ty = self.resolve_vars_with_obligations(ty);
+        ty.fold_with(&mut Normalizer { table: self })
     }
 
     /// Works almost same as [`Self::normalize_associated_types_in`], but this also resolves shallow
@@ -476,11 +380,27 @@ impl<'a> InferenceTable<'a> {
     }
 
     pub(crate) fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty {
-        let var = self.new_type_var();
-        let alias_eq = AliasEq { alias: AliasTy::Projection(proj_ty), ty: var.clone() };
-        let obligation: Goal = alias_eq.cast(Interner);
-        self.register_obligation(obligation.to_nextsolver(self.interner));
-        var
+        let ty = TyKind::Alias(chalk_ir::AliasTy::Projection(proj_ty))
+            .intern(Interner)
+            .to_nextsolver(self.interner);
+        self.normalize_alias_ty(ty).to_chalk(self.interner)
+    }
+
+    pub(crate) fn normalize_alias_ty(
+        &mut self,
+        alias: crate::next_solver::Ty<'a>,
+    ) -> crate::next_solver::Ty<'a> {
+        let infer_term = self.infer_ctxt.next_ty_var();
+        let obligation = crate::next_solver::Predicate::new(
+            self.interner,
+            crate::next_solver::Binder::dummy(crate::next_solver::PredicateKind::AliasRelate(
+                alias.into(),
+                infer_term.into(),
+                rustc_type_ir::AliasRelationDirection::Equate,
+            )),
+        );
+        self.register_obligation(obligation);
+        self.resolve_vars_with_obligations(infer_term)
     }
 
     fn new_var(&mut self, kind: TyVariableKind, diverging: bool) -> Ty {
@@ -591,9 +511,10 @@ impl<'a> InferenceTable<'a> {
         )
     }
 
-    pub(crate) fn resolve_completely<T>(&mut self, t: T) -> T
+    pub(crate) fn resolve_completely<T, U>(&mut self, t: T) -> T
     where
-        T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
+        T: HasInterner<Interner = Interner> + TypeFoldable<Interner> + ChalkToNextSolver<'a, U>,
+        U: NextSolverToChalk<'a, T> + rustc_type_ir::TypeFoldable<DbInterner<'a>>,
     {
         let t = self.resolve_with_fallback(t, &|_, _, d, _| d);
         let t = self.normalize_associated_types_in(t);
@@ -1045,6 +966,30 @@ impl<'a> InferenceTable<'a> {
         }
     }
 
+    /// Whenever you lower a user-written type, you should call this.
+    pub(crate) fn process_user_written_ty<T, U>(&mut self, ty: T) -> T
+    where
+        T: HasInterner<Interner = Interner> + TypeFoldable<Interner> + ChalkToNextSolver<'a, U>,
+        U: NextSolverToChalk<'a, T> + rustc_type_ir::TypeFoldable<DbInterner<'a>>,
+    {
+        self.process_remote_user_written_ty(ty)
+        // FIXME: Register a well-formed obligation.
+    }
+
+    /// The difference of this method from `process_user_written_ty()` is that this method doesn't register a well-formed obligation,
+    /// while `process_user_written_ty()` should (but doesn't currently).
+    pub(crate) fn process_remote_user_written_ty<T, U>(&mut self, ty: T) -> T
+    where
+        T: HasInterner<Interner = Interner> + TypeFoldable<Interner> + ChalkToNextSolver<'a, U>,
+        U: NextSolverToChalk<'a, T> + rustc_type_ir::TypeFoldable<DbInterner<'a>>,
+    {
+        let ty = self.insert_type_vars(ty);
+        // See https://github.com/rust-lang/rust/blob/cdb45c87e2cd43495379f7e867e3cc15dcee9f93/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs#L487-L495:
+        // Even though the new solver only lazily normalizes usually, here we eagerly normalize so that not everything needs
+        // to normalize before inspecting the `TyKind`.
+        self.normalize_associated_types_in(ty)
+    }
+
     /// Replaces ConstScalar::Unknown by a new type var, so we can maybe still infer it.
     pub(super) fn insert_const_vars_shallow(&mut self, c: Const) -> Const {
         let data = c.data(Interner);
@@ -1319,3 +1264,62 @@ mod resolve {
         }
     }
 }
+
+/// This expects its input to be resolved.
+struct Normalizer<'a, 'b> {
+    table: &'a mut InferenceTable<'b>,
+}
+
+impl<'db> Normalizer<'_, 'db> {
+    fn normalize_alias_term(
+        &mut self,
+        alias_term: crate::next_solver::Term<'db>,
+    ) -> crate::next_solver::Term<'db> {
+        let infer_term = self.table.infer_ctxt.next_term_var_of_kind(alias_term);
+        let obligation = crate::next_solver::Predicate::new(
+            self.table.interner,
+            crate::next_solver::Binder::dummy(crate::next_solver::PredicateKind::AliasRelate(
+                alias_term,
+                infer_term,
+                rustc_type_ir::AliasRelationDirection::Equate,
+            )),
+        );
+        self.table.register_obligation(obligation);
+        let term = self.table.resolve_vars_with_obligations(infer_term);
+        // Now normalize the result, because maybe it contains more aliases.
+        match term {
+            Term::Ty(term) => term.super_fold_with(self).into(),
+            Term::Const(term) => term.super_fold_with(self).into(),
+        }
+    }
+}
+
+impl<'db> rustc_type_ir::TypeFolder<DbInterner<'db>> for Normalizer<'_, 'db> {
+    fn cx(&self) -> DbInterner<'db> {
+        self.table.interner
+    }
+
+    fn fold_ty(&mut self, ty: crate::next_solver::Ty<'db>) -> crate::next_solver::Ty<'db> {
+        if !ty.has_aliases() {
+            return ty;
+        }
+
+        let crate::next_solver::TyKind::Alias(..) = ty.kind() else {
+            return ty.super_fold_with(self);
+        };
+        // FIXME: Handle escaping bound vars by replacing them with placeholders (relevant to when we handle HRTB only).
+        self.normalize_alias_term(ty.into()).expect_type()
+    }
+
+    fn fold_const(&mut self, ct: crate::next_solver::Const<'db>) -> crate::next_solver::Const<'db> {
+        if !ct.has_aliases() {
+            return ct;
+        }
+
+        let crate::next_solver::ConstKind::Unevaluated(..) = ct.kind() else {
+            return ct.super_fold_with(self);
+        };
+        // FIXME: Handle escaping bound vars by replacing them with placeholders (relevant to when we handle HRTB only).
+        self.normalize_alias_term(ct.into()).expect_const()
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
index 3f966694d50..ac85bf79507 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
@@ -22,10 +22,9 @@ use stdx::never;
 use triomphe::Arc;
 
 use crate::{
-    AdtId, AliasTy, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId,
-    GenericArgData, Goal, InEnvironment, Interner, Mutability, Scalar, Substitution,
-    TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, TyVariableKind,
-    VariableKind, WhereClause,
+    AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, GenericArgData,
+    Goal, InEnvironment, Interner, Mutability, Scalar, Substitution, TraitEnvironment, TraitRef,
+    TraitRefExt, Ty, TyBuilder, TyExt, TyKind, TyVariableKind, VariableKind, WhereClause,
     autoderef::{self, AutoderefKind},
     db::HirDatabase,
     from_chalk_trait_id, from_foreign_def_id,
@@ -106,8 +105,12 @@ impl TyFingerprint {
                 }
             }
             TyKind::AssociatedType(_, _)
+            // FIXME(next-solver): Putting `Alias` here is *probably* incorrect, AFAIK it should return `None`. But this breaks
+            // flyimport, which uses an incorrect but fast method resolution algorithm. Therefore we put it here,
+            // because this function is only called by flyimport, and anyway we should get rid of `TyFingerprint`
+            // and switch to `rustc_type_ir`'s `SimplifiedType`.
+            | TyKind::Alias(_)
             | TyKind::OpaqueType(_, _)
-            | TyKind::Alias(AliasTy::Opaque(_))
             | TyKind::FnDef(_, _)
             | TyKind::Closure(_, _)
             | TyKind::Coroutine(..)
@@ -115,8 +118,7 @@ impl TyFingerprint {
             TyKind::Function(fn_ptr) => {
                 TyFingerprint::Function(fn_ptr.substitution.0.len(Interner) as u32)
             }
-            TyKind::Alias(_)
-            | TyKind::Placeholder(_)
+            TyKind::Placeholder(_)
             | TyKind::BoundVar(_)
             | TyKind::InferenceVar(_, _)
             | TyKind::Error => return None,
@@ -905,7 +907,10 @@ fn find_matching_impl(
                 }
                 table.register_obligation(goal.to_nextsolver(table.interner));
             }
-            Some((impl_.impl_items(db), table.resolve_completely(impl_substs)))
+            Some((
+                impl_.impl_items(db),
+                table.resolve_completely::<_, crate::next_solver::GenericArgs<'_>>(impl_substs),
+            ))
         })
     })
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs
index ce56282a00b..eb2cc69167d 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs
@@ -1350,7 +1350,10 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>)
                 };
                 let associated_ty_id = to_assoc_type_id(assoc_ty_id);
                 let substitution = convert_args_for_result(interner, alias_ty.args.as_slice());
-                TyKind::AssociatedType(associated_ty_id, substitution)
+                TyKind::Alias(crate::AliasTy::Projection(crate::ProjectionTy {
+                    associated_ty_id,
+                    substitution,
+                }))
             }
             rustc_type_ir::AliasTyKind::Opaque => {
                 let opaque_ty_id = match alias_ty.def_id {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs
index 6490554b22b..5d088e40cde 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs
@@ -202,7 +202,7 @@ fn expr_macro_def_expanded_in_various_places() {
             100..119 'for _ ...!() {}': {unknown}
             100..119 'for _ ...!() {}': &'? mut {unknown}
             100..119 'for _ ...!() {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
-            100..119 'for _ ...!() {}': Option<<{unknown} as Iterator>::Item>
+            100..119 'for _ ...!() {}': Option<{unknown}>
             100..119 'for _ ...!() {}': ()
             100..119 'for _ ...!() {}': ()
             100..119 'for _ ...!() {}': ()
@@ -296,7 +296,7 @@ fn expr_macro_rules_expanded_in_various_places() {
             114..133 'for _ ...!() {}': {unknown}
             114..133 'for _ ...!() {}': &'? mut {unknown}
             114..133 'for _ ...!() {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
-            114..133 'for _ ...!() {}': Option<<{unknown} as Iterator>::Item>
+            114..133 'for _ ...!() {}': Option<{unknown}>
             114..133 'for _ ...!() {}': ()
             114..133 'for _ ...!() {}': ()
             114..133 'for _ ...!() {}': ()
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs
index baca7f2318e..6a9135622de 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs
@@ -362,12 +362,12 @@ fn diverging_expression_3_break() {
             140..141 'x': u32
             149..175 '{ for ...; }; }': u32
             151..172 'for a ...eak; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter
-            151..172 'for a ...eak; }': <{unknown} as IntoIterator>::IntoIter
+            151..172 'for a ...eak; }': {unknown}
             151..172 'for a ...eak; }': !
-            151..172 'for a ...eak; }': <{unknown} as IntoIterator>::IntoIter
-            151..172 'for a ...eak; }': &'? mut <{unknown} as IntoIterator>::IntoIter
+            151..172 'for a ...eak; }': {unknown}
+            151..172 'for a ...eak; }': &'? mut {unknown}
             151..172 'for a ...eak; }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
-            151..172 'for a ...eak; }': Option<<{unknown} as Iterator>::Item>
+            151..172 'for a ...eak; }': Option<{unknown}>
             151..172 'for a ...eak; }': ()
             151..172 'for a ...eak; }': ()
             151..172 'for a ...eak; }': ()
@@ -379,12 +379,12 @@ fn diverging_expression_3_break() {
             226..227 'x': u32
             235..253 '{ for ... {}; }': u32
             237..250 'for a in b {}': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter
-            237..250 'for a in b {}': <{unknown} as IntoIterator>::IntoIter
+            237..250 'for a in b {}': {unknown}
             237..250 'for a in b {}': !
-            237..250 'for a in b {}': <{unknown} as IntoIterator>::IntoIter
-            237..250 'for a in b {}': &'? mut <{unknown} as IntoIterator>::IntoIter
+            237..250 'for a in b {}': {unknown}
+            237..250 'for a in b {}': &'? mut {unknown}
             237..250 'for a in b {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
-            237..250 'for a in b {}': Option<<{unknown} as Iterator>::Item>
+            237..250 'for a in b {}': Option<{unknown}>
             237..250 'for a in b {}': ()
             237..250 'for a in b {}': ()
             237..250 'for a in b {}': ()
@@ -395,12 +395,12 @@ fn diverging_expression_3_break() {
             304..305 'x': u32
             313..340 '{ for ...; }; }': u32
             315..337 'for a ...urn; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter
-            315..337 'for a ...urn; }': <{unknown} as IntoIterator>::IntoIter
+            315..337 'for a ...urn; }': {unknown}
             315..337 'for a ...urn; }': !
-            315..337 'for a ...urn; }': <{unknown} as IntoIterator>::IntoIter
-            315..337 'for a ...urn; }': &'? mut <{unknown} as IntoIterator>::IntoIter
+            315..337 'for a ...urn; }': {unknown}
+            315..337 'for a ...urn; }': &'? mut {unknown}
             315..337 'for a ...urn; }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
-            315..337 'for a ...urn; }': Option<<{unknown} as Iterator>::Item>
+            315..337 'for a ...urn; }': Option<{unknown}>
             315..337 'for a ...urn; }': ()
             315..337 'for a ...urn; }': ()
             315..337 'for a ...urn; }': ()
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
index 60a2641e1a2..02cb0370691 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
@@ -48,12 +48,12 @@ fn infer_pattern() {
             83..84 '1': i32
             86..93 '"hello"': &'static str
             101..151 'for (e...     }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter
-            101..151 'for (e...     }': <{unknown} as IntoIterator>::IntoIter
+            101..151 'for (e...     }': {unknown}
             101..151 'for (e...     }': !
-            101..151 'for (e...     }': <{unknown} as IntoIterator>::IntoIter
-            101..151 'for (e...     }': &'? mut <{unknown} as IntoIterator>::IntoIter
+            101..151 'for (e...     }': {unknown}
+            101..151 'for (e...     }': &'? mut {unknown}
             101..151 'for (e...     }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
-            101..151 'for (e...     }': Option<<{unknown} as Iterator>::Item>
+            101..151 'for (e...     }': Option<({unknown}, {unknown})>
             101..151 'for (e...     }': ()
             101..151 'for (e...     }': ()
             101..151 'for (e...     }': ()
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
index eacc4603ea1..6a3f2286215 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
@@ -268,12 +268,12 @@ fn infer_std_crash_5() {
         expect![[r#"
             26..322 '{     ...   } }': ()
             32..320 'for co...     }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter
-            32..320 'for co...     }': <{unknown} as IntoIterator>::IntoIter
+            32..320 'for co...     }': {unknown}
             32..320 'for co...     }': !
-            32..320 'for co...     }': <{unknown} as IntoIterator>::IntoIter
-            32..320 'for co...     }': &'? mut <{unknown} as IntoIterator>::IntoIter
+            32..320 'for co...     }': {unknown}
+            32..320 'for co...     }': &'? mut {unknown}
             32..320 'for co...     }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
-            32..320 'for co...     }': Option<<{unknown} as Iterator>::Item>
+            32..320 'for co...     }': Option<{unknown}>
             32..320 'for co...     }': ()
             32..320 'for co...     }': ()
             32..320 'for co...     }': ()
@@ -628,7 +628,7 @@ fn issue_4053_diesel_where_clauses() {
             65..69 'self': Self
             267..271 'self': Self
             466..470 'self': SelectStatement<F, S, D, W, O, LOf, {unknown}, {unknown}>
-            488..522 '{     ...     }': <SelectStatement<F, S, D, W, O, LOf, {unknown}, {unknown}> as BoxedDsl<DB>>::Output
+            488..522 '{     ...     }': ()
             498..502 'self': SelectStatement<F, S, D, W, O, LOf, {unknown}, {unknown}>
             498..508 'self.order': O
             498..515 'self.o...into()': dyn QueryFragment<DB> + '?
@@ -1248,7 +1248,7 @@ fn test() {
             16..66 'for _ ...     }': {unknown}
             16..66 'for _ ...     }': &'? mut {unknown}
             16..66 'for _ ...     }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
-            16..66 'for _ ...     }': Option<<{unknown} as Iterator>::Item>
+            16..66 'for _ ...     }': Option<{unknown}>
             16..66 'for _ ...     }': ()
             16..66 'for _ ...     }': ()
             16..66 'for _ ...     }': ()
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs
index 97473bbabba..6d6c56696a3 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs
@@ -20,7 +20,7 @@ impl<'a> IntoIterator for &'a Grid {
     "#,
         expect![[r#"
             150..154 'self': &'a Grid
-            174..181 '{     }': impl Iterator<Item = &'? ()>
+            174..181 '{     }': impl Iterator<Item = &'a ()>
         "#]],
     );
 }
@@ -71,3 +71,30 @@ fn main() {
 }"#,
     );
 }
+
+#[test]
+fn projection_is_not_associated_type() {
+    check_no_mismatches(
+        r#"
+//- minicore: fn
+trait Iterator {
+    type Item;
+
+    fn partition<F>(self, f: F)
+    where
+        F: FnMut(&Self::Item) -> bool,
+    {
+    }
+}
+
+struct Iter;
+impl Iterator for Iter {
+    type Item = i32;
+}
+
+fn main() {
+    Iter.partition(|n| true);
+}
+    "#,
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
index e7357ed5aa7..60ad0f49c6a 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
@@ -2000,7 +2000,7 @@ fn test() {
             225..360 'match ...     }': ()
             231..239 'Pin::new': fn new<&'? mut |usize| yields i64 -> &'static str>(&'? mut |usize| yields i64 -> &'static str) -> Pin<&'? mut |usize| yields i64 -> &'static str>
             231..247 'Pin::n...mut g)': Pin<&'? mut |usize| yields i64 -> &'static str>
-            231..262 'Pin::n...usize)': CoroutineState<i64, &'? str>
+            231..262 'Pin::n...usize)': CoroutineState<i64, &'static str>
             240..246 '&mut g': &'? mut |usize| yields i64 -> &'static str
             245..246 'g': |usize| yields i64 -> &'static str
             255..261 '0usize': usize
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
index 7a946f7ec7c..22332fdc2b8 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
@@ -4190,8 +4190,6 @@ fn g<P: PointerFamily>(p: <P as PointerFamily>::Pointer<i32>) {
     );
 }
 
-// FIXME(next-solver): Was `&'a T` but now getting error lifetime.
-// This might be fixed once we migrate into next-solver fully without chalk-ir in lowering.
 #[test]
 fn gats_with_impl_trait() {
     // FIXME: the last function (`fn i()`) is not valid Rust as of this writing because you cannot
@@ -4215,21 +4213,21 @@ fn f<T>(v: impl Trait) {
 }
 fn g<'a, T: 'a>(v: impl Trait<Assoc<T> = &'a T>) {
     let a = v.get::<T>();
-      //^ &'? T
+      //^ &'a T
     let a = v.get::<()>();
       //^ <impl Trait<Assoc<T> = &'a T> as Trait>::Assoc<()>
 }
 fn h<'a>(v: impl Trait<Assoc<i32> = &'a i32> + Trait<Assoc<i64> = &'a i64>) {
     let a = v.get::<i32>();
-      //^ &'? i32
+      //^ &'a i32
     let a = v.get::<i64>();
-      //^ &'? i64
+      //^ &'a i64
 }
 fn i<'a>(v: impl Trait<Assoc<i32> = &'a i32, Assoc<i64> = &'a i64>) {
     let a = v.get::<i32>();
-      //^ &'? i32
+      //^ &'a i32
     let a = v.get::<i64>();
-      //^ &'? i64
+      //^ &'a i64
 }
     "#,
     );
@@ -4259,8 +4257,8 @@ fn f<'a>(v: &dyn Trait<Assoc<i32> = &'a i32>) {
             127..128 'v': &'? (dyn Trait<Assoc<i32> = &'a i32> + '?)
             164..195 '{     ...f(); }': ()
             170..171 'v': &'? (dyn Trait<Assoc<i32> = &'a i32> + '?)
-            170..184 'v.get::<i32>()': <dyn Trait<Assoc<i32> = &'a i32> + '? as Trait>::Assoc<i32>
-            170..192 'v.get:...eref()': {unknown}
+            170..184 'v.get::<i32>()': {unknown}
+            170..192 'v.get:...eref()': &'? {unknown}
         "#]],
     );
 }
@@ -4953,7 +4951,7 @@ where
 "#,
         expect![[r#"
             84..86 'de': D
-            135..138 '{ }': <D as Deserializer<'?>>::Error
+            135..138 '{ }': <D as Deserializer<'de>>::Error
         "#]],
     );
 }
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 52ab808d228..888392b0ff2 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -76,7 +76,7 @@ use hir_expand::{
 };
 use hir_ty::{
     AliasTy, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, GenericArg,
-    GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution,
+    GenericArgData, Interner, ParamKind, ProjectionTy, QuantifiedWhereClause, Scalar, Substitution,
     TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, TyLoweringDiagnostic,
     ValueTyDefId, WhereClause, all_super_traits, autoderef, check_orphan_rules,
     consteval::{ConstExt, try_const_usize, unknown_const_as_generic},
@@ -4973,6 +4973,7 @@ impl<'db> Type<'db> {
                 | TyKind::Tuple(_, substitution)
                 | TyKind::OpaqueType(_, substitution)
                 | TyKind::AssociatedType(_, substitution)
+                | TyKind::Alias(AliasTy::Projection(ProjectionTy { substitution, .. }))
                 | TyKind::FnDef(_, substitution) => {
                     substitution.iter(Interner).filter_map(|x| x.ty(Interner)).any(|ty| go(db, ty))
                 }
@@ -5819,7 +5820,11 @@ impl<'db> Type<'db> {
                     cb(type_.derived(ty.clone()));
                     walk_substs(db, type_, substs, cb);
                 }
-                TyKind::AssociatedType(_, substs) => {
+                TyKind::AssociatedType(_, substs)
+                | TyKind::Alias(AliasTy::Projection(hir_ty::ProjectionTy {
+                    substitution: substs,
+                    ..
+                })) => {
                     if ty.associated_type_parent_trait(db).is_some() {
                         cb(type_.derived(ty.clone()));
                     }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs
index 69015898967..358e0c43c2a 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs
@@ -1,5 +1,3 @@
-use std::iter;
-
 use either::Either;
 use hir::{Adt, FileRange, HasSource, HirDisplay, InFile, Struct, Union, db::ExpandDatabase};
 use ide_db::text_edit::TextEdit;
@@ -194,17 +192,20 @@ fn add_field_to_struct_fix(
                 Some(make::visibility_pub_crate())
             };
             // FIXME: Allow for choosing a visibility modifier see https://github.com/rust-lang/rust-analyzer/issues/11563
-            let indent = IndentLevel::from_node(struct_syntax.value) + 1;
+            let indent = IndentLevel::from_node(struct_syntax.value);
 
-            let field = make::record_field(visibility, field_name, suggested_type).indent(indent);
-            let record_field_list = make::record_field_list(iter::once(field));
+            let field =
+                make::record_field(visibility, field_name, suggested_type).indent(indent + 1);
             // A Unit Struct with no `;` is invalid syntax. We should not suggest this fix.
             let semi_colon =
                 algo::skip_trivia_token(struct_syntax.value.last_token()?, Direction::Prev)?;
             if semi_colon.kind() != SyntaxKind::SEMICOLON {
                 return None;
             }
-            src_change_builder.replace(semi_colon.text_range(), record_field_list.to_string());
+            src_change_builder.replace(
+                semi_colon.text_range(),
+                format!(" {{\n{}{field},\n{indent}}}", indent + 1),
+            );
 
             Some(Assist {
                 id: AssistId::quick_fix("convert-unit-struct-to-record-struct"),
@@ -230,7 +231,7 @@ fn record_field_layout(
     field_list: ast::RecordFieldList,
     struct_syntax: &SyntaxNode,
 ) -> Option<(TextSize, String)> {
-    let (offset, needs_comma, trailing_new_line, indent) = match field_list.fields().last() {
+    let (offset, needs_comma, indent) = match field_list.fields().last() {
         Some(record_field) => {
             let syntax = algo::skip_trivia_token(field_list.r_curly_token()?, Direction::Prev)?;
 
@@ -239,19 +240,22 @@ fn record_field_layout(
             (
                 last_field_syntax.text_range().end(),
                 syntax.kind() != SyntaxKind::COMMA,
-                false,
                 last_field_indent,
             )
         }
         // Empty Struct. Add a field right before the closing brace
         None => {
             let indent = IndentLevel::from_node(struct_syntax) + 1;
-            let offset = field_list.r_curly_token()?.text_range().start();
-            (offset, false, true, indent)
+            let offset = field_list.l_curly_token()?.text_range().end();
+            (offset, false, indent)
         }
     };
-    let comma = if needs_comma { ",\n" } else { "" };
-    let trailing_new_line = if trailing_new_line { "\n" } else { "" };
+    let trailing_new_line = if !field_list.syntax().text().contains_char('\n') {
+        format!("\n{}", field_list.indent_level())
+    } else {
+        String::new()
+    };
+    let comma = if needs_comma { ",\n" } else { "\n" };
     let record_field = make::record_field(visibility, name, suggested_type);
 
     Some((offset, format!("{comma}{indent}{record_field}{trailing_new_line}")))
@@ -377,18 +381,24 @@ fn foo() {
     fn unresolved_field_fix_on_unit() {
         check_fix(
             r#"
+            mod indent {
                 struct Foo;
 
                 fn foo() {
                     Foo.bar$0;
                 }
+            }
             "#,
             r#"
-                struct Foo{ bar: () }
+            mod indent {
+                struct Foo {
+                    bar: (),
+                }
 
                 fn foo() {
                     Foo.bar;
                 }
+            }
             "#,
         );
     }
@@ -396,6 +406,7 @@ fn foo() {
     fn unresolved_field_fix_on_empty() {
         check_fix(
             r#"
+            mod indent {
                 struct Foo{
                 }
 
@@ -403,8 +414,10 @@ fn foo() {
                     let foo = Foo{};
                     foo.bar$0;
                 }
+            }
             "#,
             r#"
+            mod indent {
                 struct Foo{
                     bar: ()
                 }
@@ -413,6 +426,32 @@ fn foo() {
                     let foo = Foo{};
                     foo.bar;
                 }
+            }
+            "#,
+        );
+
+        check_fix(
+            r#"
+            mod indent {
+                struct Foo {}
+
+                fn foo() {
+                    let foo = Foo{};
+                    foo.bar$0;
+                }
+            }
+            "#,
+            r#"
+            mod indent {
+                struct Foo {
+                    bar: ()
+                }
+
+                fn foo() {
+                    let foo = Foo{};
+                    foo.bar;
+                }
+            }
             "#,
         );
     }
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
index 6f1bbad6ba1..58d8a7edbe0 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -10570,6 +10570,77 @@ macro_rules! str {
 }
 
 #[test]
+fn test_runnables_with_snapshot_tests_indirect_dep() {
+    check_actions(
+        r#"
+//- /lib.rs crate:foo deps:utils
+use utils::expect_test::expect;
+
+#[test]
+fn test$0() {
+    let actual = "new25";
+    expect!["new25"].assert_eq(&actual);
+}
+
+//- /expect-test/lib.rs crate:expect_test
+struct Expect;
+
+impl Expect {
+    fn assert_eq(&self, actual: &str) {}
+}
+
+#[macro_export]
+macro_rules! expect {
+    ($e:expr) => Expect; // dummy
+}
+
+//- /utils/lib.rs crate:utils deps:expect_test
+pub use expect_test;
+        "#,
+        expect![[r#"
+            [
+                Reference(
+                    FilePositionWrapper {
+                        file_id: FileId(
+                            0,
+                        ),
+                        offset: 44,
+                    },
+                ),
+                Runnable(
+                    Runnable {
+                        use_name_in_title: false,
+                        nav: NavigationTarget {
+                            file_id: FileId(
+                                0,
+                            ),
+                            full_range: 33..121,
+                            focus_range: 44..48,
+                            name: "test",
+                            kind: Function,
+                        },
+                        kind: Test {
+                            test_id: Path(
+                                "test",
+                            ),
+                            attr: TestAttr {
+                                ignore: false,
+                            },
+                        },
+                        cfg: None,
+                        update_test: UpdateTest {
+                            expect_test: true,
+                            insta: false,
+                            snapbox: false,
+                        },
+                    },
+                ),
+            ]
+        "#]],
+    );
+}
+
+#[test]
 fn drop_glue() {
     check(
         r#"
diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
index 6ad9c7884db..ec13ba0fde3 100644
--- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
@@ -4,8 +4,8 @@ use arrayvec::ArrayVec;
 use ast::HasName;
 use cfg::{CfgAtom, CfgExpr};
 use hir::{
-    AsAssocItem, AttrsWithOwner, HasAttrs, HasCrate, HasSource, ModPath, Name, PathKind, Semantics,
-    Symbol, db::HirDatabase, sym,
+    AsAssocItem, AttrsWithOwner, HasAttrs, HasCrate, HasSource, Semantics, Symbol, db::HirDatabase,
+    sym,
 };
 use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn};
 use ide_db::{
@@ -352,8 +352,7 @@ pub(crate) fn runnable_fn(
     .call_site();
 
     let file_range = fn_source.syntax().original_file_range_with_macro_call_input(sema.db);
-    let update_test =
-        UpdateTest::find_snapshot_macro(sema, &fn_source.file_syntax(sema.db), file_range);
+    let update_test = UpdateTest::find_snapshot_macro(sema, file_range);
 
     let cfg = def.attrs(sema.db).cfg();
     Some(Runnable { use_name_in_title: false, nav, kind, cfg, update_test })
@@ -388,7 +387,7 @@ pub(crate) fn runnable_mod(
         file_id: module_source.file_id.original_file(sema.db),
         range: module_syntax.text_range(),
     };
-    let update_test = UpdateTest::find_snapshot_macro(sema, &module_syntax, file_range);
+    let update_test = UpdateTest::find_snapshot_macro(sema, file_range);
 
     Some(Runnable {
         use_name_in_title: false,
@@ -428,8 +427,7 @@ pub(crate) fn runnable_impl(
     let impl_source = sema.source(*def)?;
     let impl_syntax = impl_source.syntax();
     let file_range = impl_syntax.original_file_range_with_macro_call_input(sema.db);
-    let update_test =
-        UpdateTest::find_snapshot_macro(sema, &impl_syntax.file_syntax(sema.db), file_range);
+    let update_test = UpdateTest::find_snapshot_macro(sema, file_range);
 
     Some(Runnable {
         use_name_in_title: false,
@@ -475,7 +473,7 @@ fn runnable_mod_outline_definition(
         file_id: mod_source.file_id.original_file(sema.db),
         range: mod_syntax.text_range(),
     };
-    let update_test = UpdateTest::find_snapshot_macro(sema, &mod_syntax, file_range);
+    let update_test = UpdateTest::find_snapshot_macro(sema, file_range);
 
     Some(Runnable {
         use_name_in_title: false,
@@ -641,7 +639,7 @@ pub struct UpdateTest {
     pub snapbox: bool,
 }
 
-static SNAPSHOT_TEST_MACROS: OnceLock<FxHashMap<&str, Vec<ModPath>>> = OnceLock::new();
+static SNAPSHOT_TEST_MACROS: OnceLock<FxHashMap<&str, Vec<[Symbol; 2]>>> = OnceLock::new();
 
 impl UpdateTest {
     const EXPECT_CRATE: &str = "expect_test";
@@ -665,22 +663,17 @@ impl UpdateTest {
     const SNAPBOX_CRATE: &str = "snapbox";
     const SNAPBOX_MACROS: &[&str] = &["assert_data_eq", "file", "str"];
 
-    fn find_snapshot_macro(
-        sema: &Semantics<'_, RootDatabase>,
-        scope: &SyntaxNode,
-        file_range: hir::FileRange,
-    ) -> Self {
+    fn find_snapshot_macro(sema: &Semantics<'_, RootDatabase>, file_range: hir::FileRange) -> Self {
         fn init<'a>(
             krate_name: &'a str,
             paths: &[&str],
-            map: &mut FxHashMap<&'a str, Vec<ModPath>>,
+            map: &mut FxHashMap<&'a str, Vec<[Symbol; 2]>>,
         ) {
             let mut res = Vec::with_capacity(paths.len());
-            let krate = Name::new_symbol_root(Symbol::intern(krate_name));
+            let krate = Symbol::intern(krate_name);
             for path in paths {
-                let segments = [krate.clone(), Name::new_symbol_root(Symbol::intern(path))];
-                let mod_path = ModPath::from_segments(PathKind::Abs, segments);
-                res.push(mod_path);
+                let segments = [krate.clone(), Symbol::intern(path)];
+                res.push(segments);
             }
             map.insert(krate_name, res);
         }
@@ -694,11 +687,9 @@ impl UpdateTest {
         });
 
         let search_scope = SearchScope::file_range(file_range);
-        let find_macro = |paths: &[ModPath]| {
+        let find_macro = |paths: &[[Symbol; 2]]| {
             for path in paths {
-                let Some(items) = sema.resolve_mod_path(scope, path) else {
-                    continue;
-                };
+                let items = hir::resolve_absolute_path(sema.db, path.iter().cloned());
                 for item in items {
                     if let hir::ItemInNs::Macros(makro) = item
                         && Definition::Macro(makro)