diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs
index 43d87b96ead90..f1dddb3acacaa 100644
--- a/compiler/rustc_ast/src/token.rs
+++ b/compiler/rustc_ast/src/token.rs
@@ -486,6 +486,9 @@ impl Token {
     }
 
     /// Returns `true` if the token can appear at the start of an expression.
+    ///
+    /// **NB**: Take care when modifying this function, since it will change
+    /// the stable set of tokens that are allowed to match an expr nonterminal.
     pub fn can_begin_expr(&self) -> bool {
         match self.uninterpolate().kind {
             Ident(name, is_raw)              =>
@@ -504,10 +507,13 @@ impl Token {
             PathSep                            | // global path
             Lifetime(..)                      | // labeled loop
             Pound                             => true, // expression attributes
-            Interpolated(ref nt) => matches!(&**nt, NtLiteral(..) |
-                NtExpr(..)    |
-                NtBlock(..)   |
-                NtPath(..)),
+            Interpolated(ref nt) =>
+                matches!(&**nt,
+                    NtBlock(..)   |
+                    NtExpr(..)    |
+                    NtLiteral(..) |
+                    NtPath(..)
+                ),
             _ => false,
         }
     }
@@ -515,23 +521,32 @@ impl Token {
     /// Returns `true` if the token can appear at the start of a pattern.
     ///
     /// Shamelessly borrowed from `can_begin_expr`, only used for diagnostics right now.
-    pub fn can_begin_pattern(&self) -> bool {
-        match self.uninterpolate().kind {
-            Ident(name, is_raw)              =>
-                ident_can_begin_expr(name, self.span, is_raw), // value name or keyword
-            | OpenDelim(Delimiter::Bracket | Delimiter::Parenthesis)  // tuple or array
-            | Literal(..)                        // literal
-            | BinOp(Minus)                       // unary minus
-            | BinOp(And)                         // reference
-            | AndAnd                             // double reference
-            // DotDotDot is no longer supported
-            | DotDot | DotDotDot | DotDotEq      // ranges
-            | Lt | BinOp(Shl)                    // associated path
-            | PathSep                    => true, // global path
-            Interpolated(ref nt) => matches!(&**nt, NtLiteral(..) |
-                NtPat(..)     |
-                NtBlock(..)   |
-                NtPath(..)),
+    pub fn can_begin_pattern(&self, pat_kind: NtPatKind) -> bool {
+        match &self.uninterpolate().kind {
+            // box, ref, mut, and other identifiers (can stricten)
+            Ident(..) | NtIdent(..) |
+            OpenDelim(Delimiter::Parenthesis) |  // tuple pattern
+            OpenDelim(Delimiter::Bracket) |      // slice pattern
+            BinOp(And) |                  // reference
+            BinOp(Minus) |                // negative literal
+            AndAnd |                      // double reference
+            Literal(_) |                  // literal
+            DotDot |                      // range pattern (future compat)
+            DotDotDot |                   // range pattern (future compat)
+            PathSep |                     // path
+            Lt |                          // path (UFCS constant)
+            BinOp(Shl) => true,           // path (double UFCS)
+            // leading vert `|` or-pattern
+            BinOp(Or) => matches!(pat_kind, PatWithOr),
+            Interpolated(nt) =>
+                matches!(&**nt,
+                    | NtExpr(..)
+                    | NtLiteral(..)
+                    | NtMeta(..)
+                    | NtPat(..)
+                    | NtPath(..)
+                    | NtTy(..)
+                ),
             _ => false,
         }
     }
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index 3acf2c6314592..b8fbe0e99ef79 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -420,7 +420,7 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
         span: Span,
         def_id: LocalDefId,
         assoc_name: Ident,
-    ) -> ty::GenericPredicates<'tcx> {
+    ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> {
         self.tcx.at(span).type_param_predicates((self.item_def_id, def_id, assoc_name))
     }
 
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index bba8b0497be55..1bff91b1fac87 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -580,24 +580,24 @@ pub(super) fn explicit_predicates_of<'tcx>(
 /// Ensures that the super-predicates of the trait with a `DefId`
 /// of `trait_def_id` are lowered and stored. This also ensures that
 /// the transitive super-predicates are lowered.
-pub(super) fn explicit_super_predicates_of(
-    tcx: TyCtxt<'_>,
+pub(super) fn explicit_super_predicates_of<'tcx>(
+    tcx: TyCtxt<'tcx>,
     trait_def_id: LocalDefId,
-) -> ty::GenericPredicates<'_> {
+) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> {
     implied_predicates_with_filter(tcx, trait_def_id.to_def_id(), PredicateFilter::SelfOnly)
 }
 
-pub(super) fn explicit_supertraits_containing_assoc_item(
-    tcx: TyCtxt<'_>,
+pub(super) fn explicit_supertraits_containing_assoc_item<'tcx>(
+    tcx: TyCtxt<'tcx>,
     (trait_def_id, assoc_name): (DefId, Ident),
-) -> ty::GenericPredicates<'_> {
+) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> {
     implied_predicates_with_filter(tcx, trait_def_id, PredicateFilter::SelfThatDefines(assoc_name))
 }
 
-pub(super) fn explicit_implied_predicates_of(
-    tcx: TyCtxt<'_>,
+pub(super) fn explicit_implied_predicates_of<'tcx>(
+    tcx: TyCtxt<'tcx>,
     trait_def_id: LocalDefId,
-) -> ty::GenericPredicates<'_> {
+) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> {
     implied_predicates_with_filter(
         tcx,
         trait_def_id.to_def_id(),
@@ -612,11 +612,11 @@ pub(super) fn explicit_implied_predicates_of(
 /// Ensures that the super-predicates of the trait with a `DefId`
 /// of `trait_def_id` are lowered and stored. This also ensures that
 /// the transitive super-predicates are lowered.
-pub(super) fn implied_predicates_with_filter(
-    tcx: TyCtxt<'_>,
+pub(super) fn implied_predicates_with_filter<'tcx>(
+    tcx: TyCtxt<'tcx>,
     trait_def_id: DefId,
     filter: PredicateFilter,
-) -> ty::GenericPredicates<'_> {
+) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> {
     let Some(trait_def_id) = trait_def_id.as_local() else {
         // if `assoc_name` is None, then the query should've been redirected to an
         // external provider
@@ -679,20 +679,16 @@ pub(super) fn implied_predicates_with_filter(
         _ => {}
     }
 
-    ty::GenericPredicates {
-        parent: None,
-        predicates: implied_bounds,
-        effects_min_tys: ty::List::empty(),
-    }
+    ty::EarlyBinder::bind(implied_bounds)
 }
 
 /// Returns the predicates defined on `item_def_id` of the form
 /// `X: Foo` where `X` is the type parameter `def_id`.
 #[instrument(level = "trace", skip(tcx))]
-pub(super) fn type_param_predicates(
-    tcx: TyCtxt<'_>,
+pub(super) fn type_param_predicates<'tcx>(
+    tcx: TyCtxt<'tcx>,
     (item_def_id, def_id, assoc_name): (LocalDefId, LocalDefId, Ident),
-) -> ty::GenericPredicates<'_> {
+) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> {
     use rustc_hir::*;
     use rustc_middle::ty::Ty;
 
@@ -713,18 +709,20 @@ pub(super) fn type_param_predicates(
         tcx.generics_of(item_def_id).parent.map(|def_id| def_id.expect_local())
     };
 
-    let mut result = parent
-        .map(|parent| {
-            let icx = ItemCtxt::new(tcx, parent);
-            icx.probe_ty_param_bounds(DUMMY_SP, def_id, assoc_name)
-        })
-        .unwrap_or_default();
+    let result = if let Some(parent) = parent {
+        let icx = ItemCtxt::new(tcx, parent);
+        icx.probe_ty_param_bounds(DUMMY_SP, def_id, assoc_name)
+    } else {
+        ty::EarlyBinder::bind(&[] as &[_])
+    };
     let mut extend = None;
 
     let item_hir_id = tcx.local_def_id_to_hir_id(item_def_id);
 
     let hir_node = tcx.hir_node(item_hir_id);
-    let Some(hir_generics) = hir_node.generics() else { return result };
+    let Some(hir_generics) = hir_node.generics() else {
+        return result;
+    };
     if let Node::Item(item) = hir_node
         && let ItemKind::Trait(..) = item.kind
         // Implied `Self: Trait` and supertrait bounds.
@@ -748,9 +746,10 @@ pub(super) fn type_param_predicates(
             _ => false,
         }),
     );
-    result.predicates =
-        tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(extra_predicates));
-    result
+
+    ty::EarlyBinder::bind(
+        tcx.arena.alloc_from_iter(result.skip_binder().iter().copied().chain(extra_predicates)),
+    )
 }
 
 impl<'tcx> ItemCtxt<'tcx> {
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index e38492d9e6497..cb203e04f0c65 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -1761,7 +1761,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                 break Some((bound_vars.into_iter().collect(), assoc_item));
             }
             let predicates = tcx.explicit_supertraits_containing_assoc_item((def_id, assoc_name));
-            let obligations = predicates.predicates.iter().filter_map(|&(pred, _)| {
+            let obligations = predicates.iter_identity_copied().filter_map(|(pred, _)| {
                 let bound_predicate = pred.kind();
                 match bound_predicate.skip_binder() {
                     ty::ClauseKind::Trait(data) => {
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index 0cdd3e4a1c6c9..98e1297ed0694 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -136,7 +136,7 @@ pub trait HirTyLowerer<'tcx> {
         span: Span,
         def_id: LocalDefId,
         assoc_name: Ident,
-    ) -> ty::GenericPredicates<'tcx>;
+    ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]>;
 
     /// Lower an associated type to a projection.
     ///
@@ -831,13 +831,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         debug!(?ty_param_def_id, ?assoc_name, ?span);
         let tcx = self.tcx();
 
-        let predicates = &self.probe_ty_param_bounds(span, ty_param_def_id, assoc_name).predicates;
+        let predicates = &self.probe_ty_param_bounds(span, ty_param_def_id, assoc_name);
         debug!("predicates={:#?}", predicates);
 
         self.probe_single_bound_for_assoc_item(
             || {
                 let trait_refs = predicates
-                    .iter()
+                    .iter_identity_copied()
                     .filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref)));
                 traits::transitive_bounds_that_define_assoc_item(tcx, trait_refs, assoc_name)
             },
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index 8e69a075030be..a43d7aa31a522 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -263,27 +263,24 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> {
         _: Span,
         def_id: LocalDefId,
         _: Ident,
-    ) -> ty::GenericPredicates<'tcx> {
+    ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> {
         let tcx = self.tcx;
         let item_def_id = tcx.hir().ty_param_owner(def_id);
         let generics = tcx.generics_of(item_def_id);
         let index = generics.param_def_id_to_index[&def_id.to_def_id()];
         // HACK(eddyb) should get the original `Span`.
         let span = tcx.def_span(def_id);
-        ty::GenericPredicates {
-            parent: None,
-            predicates: tcx.arena.alloc_from_iter(
-                self.param_env.caller_bounds().iter().filter_map(|predicate| {
-                    match predicate.kind().skip_binder() {
-                        ty::ClauseKind::Trait(data) if data.self_ty().is_param(index) => {
-                            Some((predicate, span))
-                        }
-                        _ => None,
+
+        ty::EarlyBinder::bind(tcx.arena.alloc_from_iter(
+            self.param_env.caller_bounds().iter().filter_map(|predicate| {
+                match predicate.kind().skip_binder() {
+                    ty::ClauseKind::Trait(data) if data.self_ty().is_param(index) => {
+                        Some((predicate, span))
                     }
-                }),
-            ),
-            effects_min_tys: ty::List::empty(),
-        }
+                    _ => None,
+                }
+            }),
+        ))
     }
 
     fn lower_assoc_ty(
diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs
index 335c65da054c3..3e4f9b4816660 100644
--- a/compiler/rustc_infer/src/traits/util.rs
+++ b/compiler/rustc_infer/src/traits/util.rs
@@ -123,7 +123,7 @@ pub fn transitive_bounds_that_define_assoc_item<'tcx>(
 
             stack.extend(
                 tcx.explicit_supertraits_containing_assoc_item((trait_ref.def_id(), assoc_name))
-                    .instantiate_own_identity()
+                    .iter_identity_copied()
                     .map(|(clause, _)| clause.instantiate_supertrait(tcx, trait_ref))
                     .filter_map(|clause| clause.as_trait_clause())
                     // FIXME: Negative supertraits are elaborated here lol
diff --git a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs
index 978109aba5f92..78468020c4d56 100644
--- a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs
+++ b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs
@@ -45,8 +45,7 @@ impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable {
             let direct_super_traits_iter = cx
                 .tcx
                 .explicit_super_predicates_of(def_id)
-                .predicates
-                .into_iter()
+                .iter_identity_copied()
                 .filter_map(|(pred, _)| pred.as_trait_clause());
             if direct_super_traits_iter.count() > 1 {
                 cx.emit_span_lint(
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index 09aea2337185b..f3ae24a5895a8 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -1070,34 +1070,6 @@ impl<'a> CrateMetadataRef<'a> {
         )
     }
 
-    fn get_explicit_item_bounds<'tcx>(
-        self,
-        index: DefIndex,
-        tcx: TyCtxt<'tcx>,
-    ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> {
-        let lazy = self.root.tables.explicit_item_bounds.get(self, index);
-        let output = if lazy.is_default() {
-            &mut []
-        } else {
-            tcx.arena.alloc_from_iter(lazy.decode((self, tcx)))
-        };
-        ty::EarlyBinder::bind(&*output)
-    }
-
-    fn get_explicit_item_super_predicates<'tcx>(
-        self,
-        index: DefIndex,
-        tcx: TyCtxt<'tcx>,
-    ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> {
-        let lazy = self.root.tables.explicit_item_super_predicates.get(self, index);
-        let output = if lazy.is_default() {
-            &mut []
-        } else {
-            tcx.arena.alloc_from_iter(lazy.decode((self, tcx)))
-        };
-        ty::EarlyBinder::bind(&*output)
-    }
-
     fn get_variant(
         self,
         kind: DefKind,
@@ -1323,10 +1295,6 @@ impl<'a> CrateMetadataRef<'a> {
         self.root.tables.optimized_mir.get(self, id).is_some()
     }
 
-    fn cross_crate_inlinable(self, id: DefIndex) -> bool {
-        self.root.tables.cross_crate_inlinable.get(self, id)
-    }
-
     fn get_fn_has_self_parameter(self, id: DefIndex, sess: &'a Session) -> bool {
         self.root
             .tables
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index 27625f791082d..a82340e3d6130 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -32,13 +32,20 @@ trait ProcessQueryValue<'tcx, T> {
     fn process_decoded(self, _tcx: TyCtxt<'tcx>, _err: impl Fn() -> !) -> T;
 }
 
-impl<T> ProcessQueryValue<'_, Option<T>> for Option<T> {
+impl<T> ProcessQueryValue<'_, T> for T {
     #[inline(always)]
-    fn process_decoded(self, _tcx: TyCtxt<'_>, _err: impl Fn() -> !) -> Option<T> {
+    fn process_decoded(self, _tcx: TyCtxt<'_>, _err: impl Fn() -> !) -> T {
         self
     }
 }
 
+impl<'tcx, T> ProcessQueryValue<'tcx, ty::EarlyBinder<'tcx, T>> for T {
+    #[inline(always)]
+    fn process_decoded(self, _tcx: TyCtxt<'_>, _err: impl Fn() -> !) -> ty::EarlyBinder<'tcx, T> {
+        ty::EarlyBinder::bind(self)
+    }
+}
+
 impl<T> ProcessQueryValue<'_, T> for Option<T> {
     #[inline(always)]
     fn process_decoded(self, _tcx: TyCtxt<'_>, err: impl Fn() -> !) -> T {
@@ -69,6 +76,24 @@ impl<'a, 'tcx, T: Copy + Decodable<DecodeContext<'a, 'tcx>>> ProcessQueryValue<'
     }
 }
 
+impl<'a, 'tcx, T: Copy + Decodable<DecodeContext<'a, 'tcx>>>
+    ProcessQueryValue<'tcx, ty::EarlyBinder<'tcx, &'tcx [T]>>
+    for Option<DecodeIterator<'a, 'tcx, T>>
+{
+    #[inline(always)]
+    fn process_decoded(
+        self,
+        tcx: TyCtxt<'tcx>,
+        _err: impl Fn() -> !,
+    ) -> ty::EarlyBinder<'tcx, &'tcx [T]> {
+        ty::EarlyBinder::bind(if let Some(iter) = self {
+            tcx.arena.alloc_from_iter(iter)
+        } else {
+            &[]
+        })
+    }
+}
+
 impl<'a, 'tcx, T: Copy + Decodable<DecodeContext<'a, 'tcx>>>
     ProcessQueryValue<'tcx, Option<&'tcx [T]>> for Option<DecodeIterator<'a, 'tcx, T>>
 {
@@ -103,7 +128,12 @@ macro_rules! provide_one {
         provide_one! {
             $tcx, $def_id, $other, $cdata, $name => {
                 let lazy = $cdata.root.tables.$name.get($cdata, $def_id.index);
-                if lazy.is_default() { &[] } else { $tcx.arena.alloc_from_iter(lazy.decode(($cdata, $tcx))) }
+                let value = if lazy.is_default() {
+                    &[] as &[_]
+                } else {
+                    $tcx.arena.alloc_from_iter(lazy.decode(($cdata, $tcx)))
+                };
+                value.process_decoded($tcx, || panic!("{:?} does not have a {:?}", $def_id, stringify!($name)))
             }
         }
     };
@@ -212,15 +242,15 @@ impl IntoArgs for (CrateNum, SimplifiedType) {
 }
 
 provide! { tcx, def_id, other, cdata,
-    explicit_item_bounds => { cdata.get_explicit_item_bounds(def_id.index, tcx) }
-    explicit_item_super_predicates => { cdata.get_explicit_item_super_predicates(def_id.index, tcx) }
+    explicit_item_bounds => { table_defaulted_array }
+    explicit_item_super_predicates => { table_defaulted_array }
     explicit_predicates_of => { table }
     generics_of => { table }
     inferred_outlives_of => { table_defaulted_array }
     explicit_super_predicates_of => { table }
     explicit_implied_predicates_of => { table }
     type_of => { table }
-    type_alias_is_lazy => { cdata.root.tables.type_alias_is_lazy.get(cdata, def_id.index) }
+    type_alias_is_lazy => { table_direct }
     variances_of => { table }
     fn_sig => { table }
     codegen_fn_attrs => { table }
@@ -241,7 +271,7 @@ provide! { tcx, def_id, other, cdata,
     lookup_default_body_stability => { table }
     lookup_deprecation_entry => { table }
     params_in_repr => { table }
-    unused_generic_params => { cdata.root.tables.unused_generic_params.get(cdata, def_id.index) }
+    unused_generic_params => { table_direct }
     def_kind => { cdata.def_kind(def_id.index) }
     impl_parent => { table }
     defaultness => { table_direct }
@@ -287,9 +317,7 @@ provide! { tcx, def_id, other, cdata,
             .process_decoded(tcx, || panic!("{def_id:?} does not have trait_impl_trait_tys")))
     }
 
-    associated_type_for_effects => {
-        table
-    }
+    associated_type_for_effects => { table }
     associated_types_for_impl_traits_in_associated_fn => { table_defaulted_array }
 
     visibility => { cdata.get_visibility(def_id.index) }
@@ -310,7 +338,7 @@ provide! { tcx, def_id, other, cdata,
     item_attrs => { tcx.arena.alloc_from_iter(cdata.get_item_attrs(def_id.index, tcx.sess)) }
     is_mir_available => { cdata.is_item_mir_available(def_id.index) }
     is_ctfe_mir_available => { cdata.is_ctfe_mir_available(def_id.index) }
-    cross_crate_inlinable => { cdata.cross_crate_inlinable(def_id.index) }
+    cross_crate_inlinable => { table_direct }
 
     dylib_dependency_formats => { cdata.get_dylib_dependency_formats(tcx) }
     is_private_dep => { cdata.private_dep }
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index d9a844ad8374c..919623cff609c 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1446,8 +1446,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             }
             if let DefKind::Trait = def_kind {
                 record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id));
-                record!(self.tables.explicit_super_predicates_of[def_id] <- self.tcx.explicit_super_predicates_of(def_id));
-                record!(self.tables.explicit_implied_predicates_of[def_id] <- self.tcx.explicit_implied_predicates_of(def_id));
+                record_array!(self.tables.explicit_super_predicates_of[def_id] <-
+                    self.tcx.explicit_super_predicates_of(def_id).skip_binder());
+                record_array!(self.tables.explicit_implied_predicates_of[def_id] <-
+                    self.tcx.explicit_implied_predicates_of(def_id).skip_binder());
 
                 let module_children = self.tcx.module_children_local(local_id);
                 record_array!(self.tables.module_children_non_reexports[def_id] <-
@@ -1455,8 +1457,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             }
             if let DefKind::TraitAlias = def_kind {
                 record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id));
-                record!(self.tables.explicit_super_predicates_of[def_id] <- self.tcx.explicit_super_predicates_of(def_id));
-                record!(self.tables.explicit_implied_predicates_of[def_id] <- self.tcx.explicit_implied_predicates_of(def_id));
+                record_array!(self.tables.explicit_super_predicates_of[def_id] <-
+                    self.tcx.explicit_super_predicates_of(def_id).skip_binder());
+                record_array!(self.tables.explicit_implied_predicates_of[def_id] <-
+                    self.tcx.explicit_implied_predicates_of(def_id).skip_binder());
             }
             if let DefKind::Trait | DefKind::Impl { .. } = def_kind {
                 let associated_item_def_ids = self.tcx.associated_item_def_ids(def_id);
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index aec728d42624d..987ee3f07e924 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -419,10 +419,10 @@ define_tables! {
     lookup_deprecation_entry: Table<DefIndex, LazyValue<attr::Deprecation>>,
     explicit_predicates_of: Table<DefIndex, LazyValue<ty::GenericPredicates<'static>>>,
     generics_of: Table<DefIndex, LazyValue<ty::Generics>>,
-    explicit_super_predicates_of: Table<DefIndex, LazyValue<ty::GenericPredicates<'static>>>,
+    explicit_super_predicates_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>,
     // As an optimization, we only store this for trait aliases,
     // since it's identical to explicit_super_predicates_of for traits.
-    explicit_implied_predicates_of: Table<DefIndex, LazyValue<ty::GenericPredicates<'static>>>,
+    explicit_implied_predicates_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>,
     type_of: Table<DefIndex, LazyValue<ty::EarlyBinder<'static, Ty<'static>>>>,
     variances_of: Table<DefIndex, LazyArray<ty::Variance>>,
     fn_sig: Table<DefIndex, LazyValue<ty::EarlyBinder<'static, ty::PolyFnSig<'static>>>>,
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 6434bd0d7bfe5..b6443778c936b 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -651,7 +651,7 @@ rustc_queries! {
     /// is a subset of the full list of predicates. We store these in a separate map
     /// because we must evaluate them even during type conversion, often before the full
     /// predicates are available (note that super-predicates must not be cyclic).
-    query explicit_super_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> {
+    query explicit_super_predicates_of(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> {
         desc { |tcx| "computing the super predicates of `{}`", tcx.def_path_str(key) }
         cache_on_disk_if { key.is_local() }
         separate_provide_extern
@@ -662,7 +662,7 @@ rustc_queries! {
     /// of the trait. For regular traits, this includes all super-predicates and their
     /// associated type bounds. For trait aliases, currently, this includes all of the
     /// predicates of the trait alias.
-    query explicit_implied_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> {
+    query explicit_implied_predicates_of(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> {
         desc { |tcx| "computing the implied predicates of `{}`", tcx.def_path_str(key) }
         cache_on_disk_if { key.is_local() }
         separate_provide_extern
@@ -671,7 +671,9 @@ rustc_queries! {
     /// The Ident is the name of an associated type.The query returns only the subset
     /// of supertraits that define the given associated type. This is used to avoid
     /// cycles in resolving type-dependent associated item paths like `T::Item`.
-    query explicit_supertraits_containing_assoc_item(key: (DefId, rustc_span::symbol::Ident)) -> ty::GenericPredicates<'tcx> {
+    query explicit_supertraits_containing_assoc_item(
+        key: (DefId, rustc_span::symbol::Ident)
+    ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> {
         desc { |tcx| "computing the super traits of `{}` with associated type name `{}`",
             tcx.def_path_str(key.0),
             key.1
@@ -680,7 +682,9 @@ rustc_queries! {
 
     /// To avoid cycles within the predicates of a single item we compute
     /// per-type-parameter predicates for resolving `T::AssocTy`.
-    query type_param_predicates(key: (LocalDefId, LocalDefId, rustc_span::symbol::Ident)) -> ty::GenericPredicates<'tcx> {
+    query type_param_predicates(
+        key: (LocalDefId, LocalDefId, rustc_span::symbol::Ident)
+    ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> {
         desc { |tcx| "computing the bounds for type parameter `{}`", tcx.hir().ty_param_name(key.1) }
     }
 
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 70df205d8932b..d6dbad9dab845 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -349,16 +349,14 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self,
         def_id: DefId,
     ) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>> {
-        ty::EarlyBinder::bind(self.explicit_super_predicates_of(def_id).instantiate_identity(self))
+        self.explicit_super_predicates_of(def_id).map_bound(|preds| preds.into_iter().copied())
     }
 
     fn explicit_implied_predicates_of(
         self,
         def_id: DefId,
     ) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>> {
-        ty::EarlyBinder::bind(
-            self.explicit_implied_predicates_of(def_id).instantiate_identity(self),
-        )
+        self.explicit_implied_predicates_of(def_id).map_bound(|preds| preds.into_iter().copied())
     }
 
     fn has_target_features(self, def_id: DefId) -> bool {
diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
index f207216d6f423..03b1d426df4ad 100644
--- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
+++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
@@ -382,7 +382,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
         place: PlaceIndex,
         mut operand: OpTy<'tcx>,
         projection: &[PlaceElem<'tcx>],
-    ) -> Option<!> {
+    ) {
         for &(mut proj_elem) in projection {
             if let PlaceElem::Index(index) = proj_elem {
                 if let FlatSet::Elem(index) = state.get(index.into(), &self.map)
@@ -391,10 +391,14 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
                 {
                     proj_elem = PlaceElem::ConstantIndex { offset, min_length, from_end: false };
                 } else {
-                    return None;
+                    return;
                 }
             }
-            operand = self.ecx.project(&operand, proj_elem).ok()?;
+            operand = if let Ok(operand) = self.ecx.project(&operand, proj_elem) {
+                operand
+            } else {
+                return;
+            }
         }
 
         self.map.for_each_projection_value(
@@ -426,8 +430,6 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
                 }
             },
         );
-
-        None
     }
 
     fn binary_op(
diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs
index 96c52845a4a39..8997b1d57cd91 100644
--- a/compiler/rustc_mir_transform/src/jump_threading.rs
+++ b/compiler/rustc_mir_transform/src/jump_threading.rs
@@ -191,26 +191,26 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
 
     /// Recursion entry point to find threading opportunities.
     #[instrument(level = "trace", skip(self))]
-    fn start_from_switch(&mut self, bb: BasicBlock) -> Option<!> {
+    fn start_from_switch(&mut self, bb: BasicBlock) {
         let bbdata = &self.body[bb];
         if bbdata.is_cleanup || self.loop_headers.contains(bb) {
-            return None;
+            return;
         }
-        let (discr, targets) = bbdata.terminator().kind.as_switch()?;
-        let discr = discr.place()?;
+        let Some((discr, targets)) = bbdata.terminator().kind.as_switch() else { return };
+        let Some(discr) = discr.place() else { return };
         debug!(?discr, ?bb);
 
         let discr_ty = discr.ty(self.body, self.tcx).ty;
-        let discr_layout = self.ecx.layout_of(discr_ty).ok()?;
+        let Ok(discr_layout) = self.ecx.layout_of(discr_ty) else { return };
 
-        let discr = self.map.find(discr.as_ref())?;
+        let Some(discr) = self.map.find(discr.as_ref()) else { return };
         debug!(?discr);
 
         let cost = CostChecker::new(self.tcx, self.param_env, None, self.body);
         let mut state = State::new_reachable();
 
         let conds = if let Some((value, then, else_)) = targets.as_static_if() {
-            let value = ScalarInt::try_from_uint(value, discr_layout.size)?;
+            let Some(value) = ScalarInt::try_from_uint(value, discr_layout.size) else { return };
             self.arena.alloc_from_iter([
                 Condition { value, polarity: Polarity::Eq, target: then },
                 Condition { value, polarity: Polarity::Ne, target: else_ },
@@ -225,7 +225,6 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
         state.insert_value_idx(discr, conds, self.map);
 
         self.find_opportunity(bb, state, cost, 0);
-        None
     }
 
     /// Recursively walk statements backwards from this bb's terminator to find threading
@@ -364,18 +363,17 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
         lhs: PlaceIndex,
         rhs: ImmTy<'tcx>,
         state: &mut State<ConditionSet<'a>>,
-    ) -> Option<!> {
+    ) {
         let register_opportunity = |c: Condition| {
             debug!(?bb, ?c.target, "register");
             self.opportunities.push(ThreadingOpportunity { chain: vec![bb], target: c.target })
         };
 
-        let conditions = state.try_get_idx(lhs, self.map)?;
-        if let Immediate::Scalar(Scalar::Int(int)) = *rhs {
+        if let Some(conditions) = state.try_get_idx(lhs, self.map)
+            && let Immediate::Scalar(Scalar::Int(int)) = *rhs
+        {
             conditions.iter_matches(int).for_each(register_opportunity);
         }
-
-        None
     }
 
     /// If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`.
@@ -428,22 +426,23 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
         lhs: PlaceIndex,
         rhs: &Operand<'tcx>,
         state: &mut State<ConditionSet<'a>>,
-    ) -> Option<!> {
+    ) {
         match rhs {
             // If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`.
             Operand::Constant(constant) => {
-                let constant =
-                    self.ecx.eval_mir_constant(&constant.const_, constant.span, None).ok()?;
+                let Ok(constant) =
+                    self.ecx.eval_mir_constant(&constant.const_, constant.span, None)
+                else {
+                    return;
+                };
                 self.process_constant(bb, lhs, constant, state);
             }
             // Transfer the conditions on the copied rhs.
             Operand::Move(rhs) | Operand::Copy(rhs) => {
-                let rhs = self.map.find(rhs.as_ref())?;
+                let Some(rhs) = self.map.find(rhs.as_ref()) else { return };
                 state.insert_place_idx(rhs, lhs, self.map);
             }
         }
-
-        None
     }
 
     #[instrument(level = "trace", skip(self))]
@@ -453,16 +452,14 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
         lhs_place: &Place<'tcx>,
         rhs: &Rvalue<'tcx>,
         state: &mut State<ConditionSet<'a>>,
-    ) -> Option<!> {
-        let lhs = self.map.find(lhs_place.as_ref())?;
+    ) {
+        let Some(lhs) = self.map.find(lhs_place.as_ref()) else { return };
         match rhs {
-            Rvalue::Use(operand) => self.process_operand(bb, lhs, operand, state)?,
+            Rvalue::Use(operand) => self.process_operand(bb, lhs, operand, state),
             // Transfer the conditions on the copy rhs.
-            Rvalue::CopyForDeref(rhs) => {
-                self.process_operand(bb, lhs, &Operand::Copy(*rhs), state)?
-            }
+            Rvalue::CopyForDeref(rhs) => self.process_operand(bb, lhs, &Operand::Copy(*rhs), state),
             Rvalue::Discriminant(rhs) => {
-                let rhs = self.map.find_discr(rhs.as_ref())?;
+                let Some(rhs) = self.map.find_discr(rhs.as_ref()) else { return };
                 state.insert_place_idx(rhs, lhs, self.map);
             }
             // If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`.
@@ -470,7 +467,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
                 let agg_ty = lhs_place.ty(self.body, self.tcx).ty;
                 let lhs = match kind {
                     // Do not support unions.
-                    AggregateKind::Adt(.., Some(_)) => return None,
+                    AggregateKind::Adt(.., Some(_)) => return,
                     AggregateKind::Adt(_, variant_index, ..) if agg_ty.is_enum() => {
                         if let Some(discr_target) = self.map.apply(lhs, TrackElem::Discriminant)
                             && let Ok(discr_value) =
@@ -478,7 +475,11 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
                         {
                             self.process_immediate(bb, discr_target, discr_value, state);
                         }
-                        self.map.apply(lhs, TrackElem::Variant(*variant_index))?
+                        if let Some(idx) = self.map.apply(lhs, TrackElem::Variant(*variant_index)) {
+                            idx
+                        } else {
+                            return;
+                        }
                     }
                     _ => lhs,
                 };
@@ -490,8 +491,8 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
             }
             // Transfer the conditions on the copy rhs, after inversing polarity.
             Rvalue::UnaryOp(UnOp::Not, Operand::Move(place) | Operand::Copy(place)) => {
-                let conditions = state.try_get_idx(lhs, self.map)?;
-                let place = self.map.find(place.as_ref())?;
+                let Some(conditions) = state.try_get_idx(lhs, self.map) else { return };
+                let Some(place) = self.map.find(place.as_ref()) else { return };
                 let conds = conditions.map(self.arena, Condition::inv);
                 state.insert_value_idx(place, conds, self.map);
             }
@@ -502,21 +503,25 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
                 box (Operand::Move(place) | Operand::Copy(place), Operand::Constant(value))
                 | box (Operand::Constant(value), Operand::Move(place) | Operand::Copy(place)),
             ) => {
-                let conditions = state.try_get_idx(lhs, self.map)?;
-                let place = self.map.find(place.as_ref())?;
+                let Some(conditions) = state.try_get_idx(lhs, self.map) else { return };
+                let Some(place) = self.map.find(place.as_ref()) else { return };
                 let equals = match op {
                     BinOp::Eq => ScalarInt::TRUE,
                     BinOp::Ne => ScalarInt::FALSE,
-                    _ => return None,
+                    _ => return,
                 };
                 if value.const_.ty().is_floating_point() {
                     // Floating point equality does not follow bit-patterns.
                     // -0.0 and NaN both have special rules for equality,
                     // and therefore we cannot use integer comparisons for them.
                     // Avoid handling them, though this could be extended in the future.
-                    return None;
+                    return;
                 }
-                let value = value.const_.normalize(self.tcx, self.param_env).try_to_scalar_int()?;
+                let Some(value) =
+                    value.const_.normalize(self.tcx, self.param_env).try_to_scalar_int()
+                else {
+                    return;
+                };
                 let conds = conditions.map(self.arena, |c| Condition {
                     value,
                     polarity: if c.matches(equals) { Polarity::Eq } else { Polarity::Ne },
@@ -527,8 +532,6 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
 
             _ => {}
         }
-
-        None
     }
 
     #[instrument(level = "trace", skip(self))]
@@ -537,7 +540,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
         bb: BasicBlock,
         stmt: &Statement<'tcx>,
         state: &mut State<ConditionSet<'a>>,
-    ) -> Option<!> {
+    ) {
         let register_opportunity = |c: Condition| {
             debug!(?bb, ?c.target, "register");
             self.opportunities.push(ThreadingOpportunity { chain: vec![bb], target: c.target })
@@ -550,12 +553,12 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
             // If we expect `discriminant(place) ?= A`,
             // we have an opportunity if `variant_index ?= A`.
             StatementKind::SetDiscriminant { box place, variant_index } => {
-                let discr_target = self.map.find_discr(place.as_ref())?;
+                let Some(discr_target) = self.map.find_discr(place.as_ref()) else { return };
                 let enum_ty = place.ty(self.body, self.tcx).ty;
                 // `SetDiscriminant` may be a no-op if the assigned variant is the untagged variant
                 // of a niche encoding. If we cannot ensure that we write to the discriminant, do
                 // nothing.
-                let enum_layout = self.ecx.layout_of(enum_ty).ok()?;
+                let Ok(enum_layout) = self.ecx.layout_of(enum_ty) else { return };
                 let writes_discriminant = match enum_layout.variants {
                     Variants::Single { index } => {
                         assert_eq!(index, *variant_index);
@@ -568,24 +571,25 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
                     } => *variant_index != untagged_variant,
                 };
                 if writes_discriminant {
-                    let discr = self.ecx.discriminant_for_variant(enum_ty, *variant_index).ok()?;
-                    self.process_immediate(bb, discr_target, discr, state)?;
+                    let Ok(discr) = self.ecx.discriminant_for_variant(enum_ty, *variant_index)
+                    else {
+                        return;
+                    };
+                    self.process_immediate(bb, discr_target, discr, state);
                 }
             }
             // If we expect `lhs ?= true`, we have an opportunity if we assume `lhs == true`.
             StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(
                 Operand::Copy(place) | Operand::Move(place),
             )) => {
-                let conditions = state.try_get(place.as_ref(), self.map)?;
+                let Some(conditions) = state.try_get(place.as_ref(), self.map) else { return };
                 conditions.iter_matches(ScalarInt::TRUE).for_each(register_opportunity);
             }
             StatementKind::Assign(box (lhs_place, rhs)) => {
-                self.process_assign(bb, lhs_place, rhs, state)?;
+                self.process_assign(bb, lhs_place, rhs, state);
             }
             _ => {}
         }
-
-        None
     }
 
     #[instrument(level = "trace", skip(self, state, cost))]
@@ -638,17 +642,17 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
         targets: &SwitchTargets,
         target_bb: BasicBlock,
         state: &mut State<ConditionSet<'a>>,
-    ) -> Option<!> {
+    ) {
         debug_assert_ne!(target_bb, START_BLOCK);
         debug_assert_eq!(self.body.basic_blocks.predecessors()[target_bb].len(), 1);
 
-        let discr = discr.place()?;
+        let Some(discr) = discr.place() else { return };
         let discr_ty = discr.ty(self.body, self.tcx).ty;
-        let discr_layout = self.ecx.layout_of(discr_ty).ok()?;
-        let conditions = state.try_get(discr.as_ref(), self.map)?;
+        let Ok(discr_layout) = self.ecx.layout_of(discr_ty) else { return };
+        let Some(conditions) = state.try_get(discr.as_ref(), self.map) else { return };
 
         if let Some((value, _)) = targets.iter().find(|&(_, target)| target == target_bb) {
-            let value = ScalarInt::try_from_uint(value, discr_layout.size)?;
+            let Some(value) = ScalarInt::try_from_uint(value, discr_layout.size) else { return };
             debug_assert_eq!(targets.iter().filter(|&(_, target)| target == target_bb).count(), 1);
 
             // We are inside `target_bb`. Since we have a single predecessor, we know we passed
@@ -662,7 +666,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
         } else if let Some((value, _, else_bb)) = targets.as_static_if()
             && target_bb == else_bb
         {
-            let value = ScalarInt::try_from_uint(value, discr_layout.size)?;
+            let Some(value) = ScalarInt::try_from_uint(value, discr_layout.size) else { return };
 
             // We only know that `discr != value`. That's much weaker information than
             // the equality we had in the previous arm. All we can conclude is that
@@ -675,8 +679,6 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
                 }
             }
         }
-
-        None
     }
 }
 
diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs
index 7eed47cf2398e..3427b93c1f6fe 100644
--- a/compiler/rustc_mir_transform/src/known_panics_lint.rs
+++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs
@@ -469,12 +469,12 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
         msg: &AssertKind<Operand<'tcx>>,
         cond: &Operand<'tcx>,
         location: Location,
-    ) -> Option<!> {
-        let value = &self.eval_operand(cond)?;
+    ) {
+        let Some(value) = &self.eval_operand(cond) else { return };
         trace!("assertion on {:?} should be {:?}", value, expected);
 
         let expected = Scalar::from_bool(expected);
-        let value_const = self.use_ecx(|this| this.ecx.read_scalar(value))?;
+        let Some(value_const) = self.use_ecx(|this| this.ecx.read_scalar(value)) else { return };
 
         if expected != value_const {
             // Poison all places this operand references so that further code
@@ -516,14 +516,12 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
                     AssertKind::BoundsCheck { len, index }
                 }
                 // Remaining overflow errors are already covered by checks on the binary operators.
-                AssertKind::Overflow(..) | AssertKind::OverflowNeg(_) => return None,
+                AssertKind::Overflow(..) | AssertKind::OverflowNeg(_) => return,
                 // Need proper const propagator for these.
-                _ => return None,
+                _ => return,
             };
             self.report_assert_as_lint(location, AssertLintKind::UnconditionalPanic, msg);
         }
-
-        None
     }
 
     fn ensure_not_propagated(&self, local: Local) {
diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs
index 999f6f0eeb0ce..e66d0df012bcb 100644
--- a/compiler/rustc_parse/src/parser/nonterminal.rs
+++ b/compiler/rustc_parse/src/parser/nonterminal.rs
@@ -86,25 +86,7 @@ impl<'a> Parser<'a> {
                 token::Interpolated(nt) => may_be_ident(nt),
                 _ => false,
             },
-            NonterminalKind::Pat(pat_kind) => match &token.kind {
-                // box, ref, mut, and other identifiers (can stricten)
-                token::Ident(..) | token::NtIdent(..) |
-                token::OpenDelim(Delimiter::Parenthesis) |  // tuple pattern
-                token::OpenDelim(Delimiter::Bracket) |      // slice pattern
-                token::BinOp(token::And) |                  // reference
-                token::BinOp(token::Minus) |                // negative literal
-                token::AndAnd |                             // double reference
-                token::Literal(_) |                         // literal
-                token::DotDot |                             // range pattern (future compat)
-                token::DotDotDot |                          // range pattern (future compat)
-                token::PathSep |                             // path
-                token::Lt |                                 // path (UFCS constant)
-                token::BinOp(token::Shl) => true,           // path (double UFCS)
-                // leading vert `|` or-pattern
-                token::BinOp(token::Or) => matches!(pat_kind, PatWithOr),
-                token::Interpolated(nt) => may_be_ident(nt),
-                _ => false,
-            },
+            NonterminalKind::Pat(pat_kind) => token.can_begin_pattern(pat_kind),
             NonterminalKind::Lifetime => match &token.kind {
                 token::Lifetime(_) | token::NtLifetime(..) => true,
                 _ => false,
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index cc68ae237ba18..8233f9a79435f 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -444,7 +444,11 @@ impl<'a> Parser<'a> {
 
         let mut lo = self.token.span;
 
-        if self.token.is_keyword(kw::Let) && self.look_ahead(1, |tok| tok.can_begin_pattern()) {
+        if self.token.is_keyword(kw::Let)
+            && self.look_ahead(1, |tok| {
+                tok.can_begin_pattern(token::NtPatKind::PatParam { inferred: false })
+            })
+        {
             self.bump();
             self.dcx().emit_err(RemoveLet { span: lo });
             lo = self.token.span;
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index d8bf10e6021cc..8ee40ecd77e3f 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -378,7 +378,10 @@ impl<'a> Parser<'a> {
                     if self.may_recover()
                         && prev_token_before_parsing == token::PathSep
                         && (style == PathStyle::Expr && self.token.can_begin_expr()
-                            || style == PathStyle::Pat && self.token.can_begin_pattern())
+                            || style == PathStyle::Pat
+                                && self.token.can_begin_pattern(token::NtPatKind::PatParam {
+                                    inferred: false,
+                                }))
                     {
                         snapshot = Some(self.create_snapshot_for_diagnostic());
                     }
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index d5f227a84a4ca..3766501821957 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -1837,6 +1837,7 @@ supported_targets! {
 
     ("mips64-openwrt-linux-musl", mips64_openwrt_linux_musl),
 
+    ("aarch64-unknown-nto-qnx700", aarch64_unknown_nto_qnx700),
     ("aarch64-unknown-nto-qnx710", aarch64_unknown_nto_qnx710),
     ("x86_64-pc-nto-qnx710", x86_64_pc_nto_qnx710),
     ("i586-pc-nto-qnx700", i586_pc_nto_qnx700),
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx700.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx700.rs
new file mode 100644
index 0000000000000..904ae87579ec0
--- /dev/null
+++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx700.rs
@@ -0,0 +1,35 @@
+use crate::spec::{base, Cc, LinkerFlavor, Lld, Target, TargetOptions};
+
+pub fn target() -> Target {
+    Target {
+        llvm_target: "aarch64-unknown-unknown".into(),
+        metadata: crate::spec::TargetMetadata {
+            description: Some("ARM64 QNX Neutrino 7.0 RTOS".into()),
+            tier: Some(3),
+            host_tools: Some(false),
+            std: Some(true),
+        },
+        pointer_width: 64,
+        // from: https://llvm.org/docs/LangRef.html#data-layout
+        // e         = little endian
+        // m:e       = ELF mangling: Private symbols get a .L prefix
+        // i8:8:32   = 8-bit-integer, minimum_alignment=8, preferred_alignment=32
+        // i16:16:32 = 16-bit-integer, minimum_alignment=16, preferred_alignment=32
+        // i64:64    = 64-bit-integer, minimum_alignment=64, preferred_alignment=64
+        // i128:128  = 128-bit-integer, minimum_alignment=128, preferred_alignment=128
+        // n32:64    = 32 and 64 are native integer widths; Elements of this set are considered to support most general arithmetic operations efficiently.
+        // S128      = 128 bits are the natural alignment of the stack in bits.
+        data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        arch: "aarch64".into(),
+        options: TargetOptions {
+            features: "+v8a".into(),
+            max_atomic_width: Some(128),
+            pre_link_args: TargetOptions::link_args(
+                LinkerFlavor::Gnu(Cc::Yes, Lld::No),
+                &["-Vgcc_ntoaarch64le_cxx"],
+            ),
+            env: "nto70".into(),
+            ..base::nto_qnx::opts()
+        },
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs
index 8e1fc0d7fe687..eb01151baae10 100644
--- a/compiler/rustc_trait_selection/src/traits/object_safety.rs
+++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -185,12 +185,11 @@ fn predicates_reference_self(
 ) -> SmallVec<[Span; 1]> {
     let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_def_id));
     let predicates = if supertraits_only {
-        tcx.explicit_super_predicates_of(trait_def_id)
+        tcx.explicit_super_predicates_of(trait_def_id).skip_binder()
     } else {
-        tcx.predicates_of(trait_def_id)
+        tcx.predicates_of(trait_def_id).predicates
     };
     predicates
-        .predicates
         .iter()
         .map(|&(predicate, sp)| (predicate.instantiate_supertrait(tcx, trait_ref), sp))
         .filter_map(|(clause, sp)| {
@@ -266,9 +265,8 @@ fn super_predicates_have_non_lifetime_binders(
         return SmallVec::new();
     }
     tcx.explicit_super_predicates_of(trait_def_id)
-        .predicates
-        .iter()
-        .filter_map(|(pred, span)| pred.has_non_region_bound_vars().then_some(*span))
+        .iter_identity_copied()
+        .filter_map(|(pred, span)| pred.has_non_region_bound_vars().then_some(span))
         .collect()
 }
 
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index f19cd19c99a83..ceffc1d45c5ca 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -600,21 +600,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         // Check supertraits hold. This is so that their associated type bounds
         // will be checked in the code below.
-        for super_trait in tcx
+        for (supertrait, _) in tcx
             .explicit_super_predicates_of(trait_predicate.def_id())
-            .instantiate(tcx, trait_predicate.trait_ref.args)
-            .predicates
-            .into_iter()
+            .iter_instantiated_copied(tcx, trait_predicate.trait_ref.args)
         {
-            let normalized_super_trait = normalize_with_depth_to(
+            let normalized_supertrait = normalize_with_depth_to(
                 self,
                 obligation.param_env,
                 obligation.cause.clone(),
                 obligation.recursion_depth + 1,
-                super_trait,
+                supertrait,
                 &mut nested,
             );
-            nested.push(obligation.with(tcx, normalized_super_trait));
+            nested.push(obligation.with(tcx, normalized_supertrait));
         }
 
         let assoc_types: Vec<_> = tcx
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index 52f87699b164f..279f13896f48f 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -131,7 +131,7 @@ impl<'tcx> TraitAliasExpander<'tcx> {
         let predicates = tcx.explicit_super_predicates_of(trait_ref.def_id());
         debug!(?predicates);
 
-        let items = predicates.predicates.iter().rev().filter_map(|(pred, span)| {
+        let items = predicates.skip_binder().iter().rev().filter_map(|(pred, span)| {
             pred.instantiate_supertrait(tcx, trait_ref)
                 .as_trait_clause()
                 .map(|trait_ref| item.clone_and_push(trait_ref.map_bound(|t| t.trait_ref), *span))
diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs
index 1729d8d307a51..fcd7371e2aa7a 100644
--- a/compiler/rustc_trait_selection/src/traits/vtable.rs
+++ b/compiler/rustc_trait_selection/src/traits/vtable.rs
@@ -120,8 +120,7 @@ fn prepare_vtable_segments_inner<'tcx, T>(
 
             let mut direct_super_traits_iter = tcx
                 .explicit_super_predicates_of(inner_most_trait_ref.def_id())
-                .predicates
-                .into_iter()
+                .iter_identity_copied()
                 .filter_map(move |(pred, _)| {
                     pred.instantiate_supertrait(tcx, inner_most_trait_ref).as_trait_clause()
                 });
diff --git a/library/backtrace b/library/backtrace
index 72265bea21089..230570f2dac80 160000
--- a/library/backtrace
+++ b/library/backtrace
@@ -1 +1 @@
-Subproject commit 72265bea210891ae47bbe6d4f17b493ef0606619
+Subproject commit 230570f2dac80a601f5c0b30da00cc9480bd35eb
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index 5f0144922ca69..82cfd603a21e3 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -35,7 +35,7 @@ miniz_oxide = { version = "0.7.0", optional = true, default-features = false }
 addr2line = { version = "0.22.0", optional = true, default-features = false }
 
 [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies]
-libc = { version = "0.2.153", default-features = false, features = [
+libc = { version = "0.2.156", default-features = false, features = [
     'rustc-dep-of-std',
 ], public = true }
 
diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs
index 0b12e5777c840..cf226bd28d005 100644
--- a/library/std/src/io/buffered/bufreader.rs
+++ b/library/std/src/io/buffered/bufreader.rs
@@ -94,7 +94,9 @@ impl<R: Read> BufReader<R> {
     pub fn with_capacity(capacity: usize, inner: R) -> BufReader<R> {
         BufReader { inner, buf: Buffer::with_capacity(capacity) }
     }
+}
 
+impl<R: Read + ?Sized> BufReader<R> {
     /// Attempt to look ahead `n` bytes.
     ///
     /// `n` must be less than `capacity`.
diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs
index fc9d7e988835e..7fa147c9754b9 100644
--- a/library/std/src/sys/pal/unix/fs.rs
+++ b/library/std/src/sys/pal/unix/fs.rs
@@ -1717,7 +1717,7 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> {
     run_path_with_cstr(original, &|original| {
         run_path_with_cstr(link, &|link| {
             cfg_if::cfg_if! {
-                if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon", target_os = "vita"))] {
+                if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "nto"))] {
                     // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves
                     // it implementation-defined whether `link` follows symlinks, so rely on the
                     // `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior.
diff --git a/library/std/src/sys/pal/unix/process/process_unix.rs b/library/std/src/sys/pal/unix/process/process_unix.rs
index 5552e9ac97753..9d091f033e07f 100644
--- a/library/std/src/sys/pal/unix/process/process_unix.rs
+++ b/library/std/src/sys/pal/unix/process/process_unix.rs
@@ -19,7 +19,8 @@ use crate::sys::process::process_common::*;
 use crate::{fmt, mem, sys};
 
 cfg_if::cfg_if! {
-    if #[cfg(all(target_os = "nto", target_env = "nto71"))] {
+    // This workaround is only needed for QNX 7.0 and 7.1. The bug should have been fixed in 8.0
+    if #[cfg(any(target_env = "nto70", target_env = "nto71"))] {
         use crate::thread;
         use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t};
         use crate::time::Duration;
@@ -189,7 +190,8 @@ impl Command {
     #[cfg(not(any(
         target_os = "watchos",
         target_os = "tvos",
-        all(target_os = "nto", target_env = "nto71"),
+        target_env = "nto70",
+        target_env = "nto71"
     )))]
     unsafe fn do_fork(&mut self) -> Result<pid_t, io::Error> {
         cvt(libc::fork())
@@ -199,7 +201,8 @@ impl Command {
     // or closed a file descriptor while the fork() was occurring".
     // Documentation says "... or try calling fork() again". This is what we do here.
     // See also https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html
-    #[cfg(all(target_os = "nto", target_env = "nto71"))]
+    // This workaround is only needed for QNX 7.0 and 7.1. The bug should have been fixed in 8.0
+    #[cfg(any(target_env = "nto70", target_env = "nto71"))]
     unsafe fn do_fork(&mut self) -> Result<pid_t, io::Error> {
         use crate::sys::os::errno;
 
@@ -537,7 +540,7 @@ impl Command {
         // or closed a file descriptor while the posix_spawn() was occurring".
         // Documentation says "... or try calling posix_spawn() again". This is what we do here.
         // See also http://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/p/posix_spawn.html
-        #[cfg(all(target_os = "nto", target_env = "nto71"))]
+        #[cfg(target_os = "nto")]
         unsafe fn retrying_libc_posix_spawnp(
             pid: *mut pid_t,
             file: *const c_char,
diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasi/thread.rs
index 31c9cbd4699bd..4b83870fdea6c 100644
--- a/library/std/src/sys/pal/wasi/thread.rs
+++ b/library/std/src/sys/pal/wasi/thread.rs
@@ -136,36 +136,37 @@ impl Thread {
     }
 
     pub fn sleep(dur: Duration) {
-        let nanos = dur.as_nanos();
-        assert!(nanos <= u64::MAX as u128);
-
-        const USERDATA: wasi::Userdata = 0x0123_45678;
-
-        let clock = wasi::SubscriptionClock {
-            id: wasi::CLOCKID_MONOTONIC,
-            timeout: nanos as u64,
-            precision: 0,
-            flags: 0,
-        };
-
-        let in_ = wasi::Subscription {
-            userdata: USERDATA,
-            u: wasi::SubscriptionU { tag: 0, u: wasi::SubscriptionUU { clock } },
-        };
-        unsafe {
-            let mut event: wasi::Event = mem::zeroed();
-            let res = wasi::poll_oneoff(&in_, &mut event, 1);
-            match (res, event) {
-                (
-                    Ok(1),
-                    wasi::Event {
-                        userdata: USERDATA,
-                        error: wasi::ERRNO_SUCCESS,
-                        type_: wasi::EVENTTYPE_CLOCK,
-                        ..
-                    },
-                ) => {}
-                _ => panic!("thread::sleep(): unexpected result of poll_oneoff"),
+        let mut nanos = dur.as_nanos();
+        while nanos > 0 {
+            const USERDATA: wasi::Userdata = 0x0123_45678;
+
+            let clock = wasi::SubscriptionClock {
+                id: wasi::CLOCKID_MONOTONIC,
+                timeout: u64::try_from(nanos).unwrap_or(u64::MAX),
+                precision: 0,
+                flags: 0,
+            };
+            nanos -= u128::from(clock.timeout);
+
+            let in_ = wasi::Subscription {
+                userdata: USERDATA,
+                u: wasi::SubscriptionU { tag: 0, u: wasi::SubscriptionUU { clock } },
+            };
+            unsafe {
+                let mut event: wasi::Event = mem::zeroed();
+                let res = wasi::poll_oneoff(&in_, &mut event, 1);
+                match (res, event) {
+                    (
+                        Ok(1),
+                        wasi::Event {
+                            userdata: USERDATA,
+                            error: wasi::ERRNO_SUCCESS,
+                            type_: wasi::EVENTTYPE_CLOCK,
+                            ..
+                        },
+                    ) => {}
+                    _ => panic!("thread::sleep(): unexpected result of poll_oneoff"),
+                }
             }
         }
     }
diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs
index b3de71f29f394..250af912e072d 100644
--- a/library/unwind/src/lib.rs
+++ b/library/unwind/src/lib.rs
@@ -165,8 +165,15 @@ extern "C" {}
 extern "C" {}
 
 #[cfg(target_os = "nto")]
-#[link(name = "gcc_s")]
-extern "C" {}
+cfg_if::cfg_if! {
+    if #[cfg(target_env = "nto70")] {
+        #[link(name = "gcc")]
+        extern "C" {}
+    } else {
+        #[link(name = "gcc_s")]
+        extern "C" {}
+    }
+}
 
 #[cfg(target_os = "hurd")]
 #[link(name = "gcc_s")]
diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs
index c42d4c56c3816..9486f3d70fc8f 100644
--- a/src/bootstrap/src/core/sanity.rs
+++ b/src/bootstrap/src/core/sanity.rs
@@ -37,6 +37,7 @@ pub struct Finder {
 #[cfg(not(feature = "bootstrap-self-test"))]
 const STAGE0_MISSING_TARGETS: &[&str] = &[
     // just a dummy comment so the list doesn't get onelined
+    "aarch64-unknown-nto-qnx700",
 ];
 
 /// Minimum version threshold for libstdc++ required when using prebuilt LLVM
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index 0d79e5f4114e3..a2641b227531a 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -256,6 +256,7 @@ target | std | host | notes
 [`aarch64-kmc-solid_asp3`](platform-support/kmc-solid.md) | ✓ |  | ARM64 SOLID with TOPPERS/ASP3
 [`aarch64-nintendo-switch-freestanding`](platform-support/aarch64-nintendo-switch-freestanding.md) | * |  | ARM64 Nintendo Switch, Horizon
 [`aarch64-unknown-teeos`](platform-support/aarch64-unknown-teeos.md) | ? |  | ARM64 TEEOS |
+[`aarch64-unknown-nto-qnx700`](platform-support/nto-qnx.md) | ? |  | ARM64 QNX Neutrino 7.0 RTOS |
 [`aarch64-unknown-nto-qnx710`](platform-support/nto-qnx.md) | ✓ |  | ARM64 QNX Neutrino 7.1 RTOS |
 `aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD
 [`aarch64-unknown-hermit`](platform-support/hermit.md) | ✓ |  | ARM64 Hermit
diff --git a/src/doc/rustc/src/platform-support/nto-qnx.md b/src/doc/rustc/src/platform-support/nto-qnx.md
index 51a397a38d209..1c240d1255a3d 100644
--- a/src/doc/rustc/src/platform-support/nto-qnx.md
+++ b/src/doc/rustc/src/platform-support/nto-qnx.md
@@ -24,6 +24,7 @@ Currently, the following QNX Neutrino versions and compilation targets are suppo
 |----------------------|---------------------|:------------:|:----------------:|
 | 7.1 | AArch64 | ✓ | ✓ |
 | 7.1 | x86_64  | ✓ | ✓ |
+| 7.0 | AArch64 | ? | ✓ |
 | 7.0 | x86     |   | ✓ |
 
 Adding other architectures that are supported by QNX Neutrino is possible.
@@ -43,6 +44,23 @@ When linking `no_std` applications, they must link against `libc.so` (see exampl
 required because applications always link against the `crt` library and `crt` depends on `libc.so`.
 This is done automatically when using the standard library.
 
+### Disabling RELocation Read-Only (RELRO)
+
+While not recommended by default, some QNX kernel setups may require the `RELRO` to be disabled with `-C relro_level=off`, e.g. by adding it to the `.cargo/config.toml` file:
+
+```toml
+[target.aarch64-unknown-nto-qnx700]
+rustflags = ["-C", "relro_level=off"]
+```
+
+If your QNX kernel does not allow it, and `relro` is not disabled, running compiled binary would fail with `syntax error: ... unexpected` or similar.  This is due to kernel trying to interpret compiled binary with `/bin/sh`, and obviously failing.  To verify that this is really the case, run your binary with the `DL_DEBUG=all` env var, and look for this output. If you see it, you should disable `relro` as described above.
+
+```text
+Resolution scope for Executable->/bin/sh:
+        Executable->/bin/sh
+        libc.so.4->/usr/lib/ldqnx-64.so.2
+```
+
 ### Small example application
 
 Small `no_std` example is shown below. Applications using the standard library work as well.
diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs
index b59f939025e1a..b8db82e540c61 100644
--- a/src/librustdoc/clean/simplify.rs
+++ b/src/librustdoc/clean/simplify.rs
@@ -14,7 +14,6 @@
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::unord::UnordSet;
 use rustc_hir::def_id::DefId;
-use rustc_middle::ty;
 use thin_vec::ThinVec;
 
 use crate::clean;
@@ -113,18 +112,9 @@ fn trait_is_same_or_supertrait(cx: &DocContext<'_>, child: DefId, trait_: DefId)
         return true;
     }
     let predicates = cx.tcx.explicit_super_predicates_of(child);
-    debug_assert!(cx.tcx.generics_of(child).has_self);
-    let self_ty = cx.tcx.types.self_param;
     predicates
-        .predicates
-        .iter()
-        .filter_map(|(pred, _)| {
-            if let ty::ClauseKind::Trait(pred) = pred.kind().skip_binder() {
-                if pred.trait_ref.self_ty() == self_ty { Some(pred.def_id()) } else { None }
-            } else {
-                None
-            }
-        })
+        .iter_identity_copied()
+        .filter_map(|(pred, _)| Some(pred.as_trait_clause()?.def_id()))
         .any(|did| trait_is_same_or_supertrait(cx, did, trait_))
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
index 67b48878ca513..6794c6cabfeef 100644
--- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
@@ -246,7 +246,7 @@ fn collect_supertrait_bounds<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds
                 && let [.., path] = poly_trait.trait_ref.path.segments
                 && poly_trait.bound_generic_params.is_empty()
                 && let Some(trait_def_id) = path.res.opt_def_id()
-                && let predicates = cx.tcx.explicit_super_predicates_of(trait_def_id).predicates
+                && let predicates = cx.tcx.explicit_super_predicates_of(trait_def_id).skip_binder()
                 // If the trait has no supertrait, there is no need to collect anything from that bound
                 && !predicates.is_empty()
             {
diff --git a/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs b/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs
index b62ecef0069af..db8cc4595d4df 100644
--- a/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs
@@ -25,8 +25,7 @@ fn is_subtrait_of_any(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
                     || cx
                         .tcx
                         .explicit_super_predicates_of(tr.def_id)
-                        .predicates
-                        .iter()
+                        .iter_identity_copied()
                         .any(|(clause, _)| {
                             matches!(clause.kind().skip_binder(), ty::ClauseKind::Trait(super_tr)
                             if cx.tcx.is_diagnostic_item(sym::Any, super_tr.def_id()))
diff --git a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs
index a1d8ec3b32ec9..62e41088f3708 100644
--- a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs
@@ -91,7 +91,7 @@ fn path_to_sized_bound(cx: &LateContext<'_>, trait_bound: &PolyTraitRef<'_>) ->
             return true;
         }
 
-        for &(predicate, _) in cx.tcx.explicit_super_predicates_of(trait_def_id).predicates {
+        for (predicate, _) in cx.tcx.explicit_super_predicates_of(trait_def_id).iter_identity_copied() {
             if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
                 && trait_predicate.polarity == PredicatePolarity::Positive
                 && !path.contains(&trait_predicate.def_id())
diff --git a/src/tools/rust-analyzer/.git-blame-ignore-revs b/src/tools/rust-analyzer/.git-blame-ignore-revs
index d5951a9420916..2ccdc8c042563 100644
--- a/src/tools/rust-analyzer/.git-blame-ignore-revs
+++ b/src/tools/rust-analyzer/.git-blame-ignore-revs
@@ -7,9 +7,10 @@
 # prettier format
 f247090558c9ba3c551566eae5882b7ca865225f
 
-# subtree syncs
-932d85b52946d917deab2c23ead552f7f713b828
+# pre-josh subtree syncs
 3e358a6827d83e8d6473913a5e304734aadfed04
+932d85b52946d917deab2c23ead552f7f713b828
 9d2cb42a413e51deb50b36794a2e1605381878fc
-f532576ac53ddcc666bc8d59e0b6437065e2f599
+b2f6fd4f961fc7e4fbfdb80cae2e6065f8436f15
 c48062fe2ab9a2d913d1985a6b0aec4bf936bfc1
+f532576ac53ddcc666bc8d59e0b6437065e2f599
diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index 41dc440509934..4e9319f13aa83 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -495,7 +495,6 @@ dependencies = [
  "hir-ty",
  "intern",
  "itertools",
- "once_cell",
  "rustc-hash",
  "smallvec",
  "span",
@@ -528,7 +527,6 @@ dependencies = [
  "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "limit",
  "mbe",
- "once_cell",
  "ra-ap-rustc_abi",
  "ra-ap-rustc_parse_format",
  "rustc-hash",
@@ -595,7 +593,6 @@ dependencies = [
  "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "limit",
  "nohash-hasher",
- "once_cell",
  "oorandom",
  "project-model",
  "ra-ap-rustc_abi",
@@ -691,7 +688,6 @@ dependencies = [
  "hir",
  "ide-db",
  "itertools",
- "once_cell",
  "smallvec",
  "stdx",
  "syntax",
@@ -720,7 +716,6 @@ dependencies = [
  "line-index 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "memchr",
  "nohash-hasher",
- "once_cell",
  "parser",
  "profile",
  "rayon",
@@ -746,7 +741,6 @@ dependencies = [
  "hir",
  "ide-db",
  "itertools",
- "once_cell",
  "paths",
  "serde_json",
  "stdx",
@@ -1933,7 +1927,6 @@ dependencies = [
  "expect-test",
  "indexmap",
  "itertools",
- "once_cell",
  "parser",
  "ra-ap-rustc_lexer",
  "rayon",
diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml
index 56e80e11e774f..aa7bd2dc5fe8b 100644
--- a/src/tools/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/Cargo.toml
@@ -19,9 +19,10 @@ rowan.opt-level = 3
 rustc-hash.opt-level = 3
 smol_str.opt-level = 3
 text-size.opt-level = 3
+serde.opt-level = 3
+salsa.opt-level = 3
 # This speeds up `cargo xtask dist`.
 miniz_oxide.opt-level = 3
-salsa.opt-level = 3
 
 [profile.release]
 incremental = true
@@ -184,6 +185,8 @@ style = { level = "warn", priority = -1 }
 suspicious = { level = "warn", priority = -1 }
 
 ## allow following lints
+# subjective
+single_match = "allow"
 # () makes a fine error in most cases
 result_unit_err = "allow"
 # We don't expose public APIs that matter like this
diff --git a/src/tools/rust-analyzer/LICENSE-APACHE b/src/tools/rust-analyzer/LICENSE-APACHE
index 16fe87b06e802..1b5ec8b78e237 100644
--- a/src/tools/rust-analyzer/LICENSE-APACHE
+++ b/src/tools/rust-analyzer/LICENSE-APACHE
@@ -174,28 +174,3 @@ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
    of your accepting any such warranty or additional liability.
 
 END OF TERMS AND CONDITIONS
-
-APPENDIX: How to apply the Apache License to your work.
-
-   To apply the Apache License to your work, attach the following
-   boilerplate notice, with the fields enclosed by brackets "[]"
-   replaced with your own identifying information. (Don't include
-   the brackets!)  The text should be enclosed in the appropriate
-   comment syntax for the file format. We also recommend that a
-   file or class name and description of purpose be included on the
-   same "printed page" as the copyright notice for easier
-   identification within third-party archives.
-
-Copyright [yyyy] [name of copyright owner]
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-	http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
index 5b9d227e3152c..c8ba5da449e99 100644
--- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
@@ -23,7 +23,6 @@ fst = { version = "0.4.7", default-features = false }
 indexmap.workspace = true
 itertools.workspace = true
 la-arena.workspace = true
-once_cell = "1.17.0"
 rustc-hash.workspace = true
 tracing.workspace = true
 smallvec.workspace = true
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
index a988317e046ef..f5fe8f8770130 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
@@ -14,7 +14,7 @@ use hir_expand::{name::Name, ExpandError, InFile};
 use la_arena::{Arena, ArenaMap, Idx, RawIdx};
 use rustc_hash::FxHashMap;
 use smallvec::SmallVec;
-use span::MacroFileId;
+use span::{Edition, MacroFileId};
 use syntax::{ast, AstPtr, SyntaxNodePtr};
 use triomphe::Arc;
 
@@ -201,8 +201,13 @@ impl Body {
         self.block_scopes.iter().map(move |&block| (block, db.block_def_map(block)))
     }
 
-    pub fn pretty_print(&self, db: &dyn DefDatabase, owner: DefWithBodyId) -> String {
-        pretty::print_body_hir(db, self, owner)
+    pub fn pretty_print(
+        &self,
+        db: &dyn DefDatabase,
+        owner: DefWithBodyId,
+        edition: Edition,
+    ) -> String {
+        pretty::print_body_hir(db, self, owner, edition)
     }
 
     pub fn pretty_print_expr(
@@ -210,8 +215,9 @@ impl Body {
         db: &dyn DefDatabase,
         owner: DefWithBodyId,
         expr: ExprId,
+        edition: Edition,
     ) -> String {
-        pretty::print_expr_hir(db, self, owner, expr)
+        pretty::print_expr_hir(db, self, owner, expr, edition)
     }
 
     fn new(
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
index edaee60937d15..55740a68acd6e 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
@@ -3,6 +3,7 @@
 use std::fmt::{self, Write};
 
 use itertools::Itertools;
+use span::Edition;
 
 use crate::{
     hir::{
@@ -15,20 +16,26 @@ use crate::{
 
 use super::*;
 
-pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBodyId) -> String {
+pub(super) fn print_body_hir(
+    db: &dyn DefDatabase,
+    body: &Body,
+    owner: DefWithBodyId,
+    edition: Edition,
+) -> String {
     let header = match owner {
-        DefWithBodyId::FunctionId(it) => {
-            it.lookup(db).id.resolved(db, |it| format!("fn {}", it.name.display(db.upcast())))
-        }
+        DefWithBodyId::FunctionId(it) => it
+            .lookup(db)
+            .id
+            .resolved(db, |it| format!("fn {}", it.name.display(db.upcast(), edition))),
         DefWithBodyId::StaticId(it) => it
             .lookup(db)
             .id
-            .resolved(db, |it| format!("static {} = ", it.name.display(db.upcast()))),
+            .resolved(db, |it| format!("static {} = ", it.name.display(db.upcast(), edition))),
         DefWithBodyId::ConstId(it) => it.lookup(db).id.resolved(db, |it| {
             format!(
                 "const {} = ",
                 match &it.name {
-                    Some(name) => name.display(db.upcast()).to_string(),
+                    Some(name) => name.display(db.upcast(), edition).to_string(),
                     None => "_".to_owned(),
                 }
             )
@@ -39,13 +46,13 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo
             let enum_loc = loc.parent.lookup(db);
             format!(
                 "enum {}::{}",
-                enum_loc.id.item_tree(db)[enum_loc.id.value].name.display(db.upcast()),
-                loc.id.item_tree(db)[loc.id.value].name.display(db.upcast()),
+                enum_loc.id.item_tree(db)[enum_loc.id.value].name.display(db.upcast(), edition),
+                loc.id.item_tree(db)[loc.id.value].name.display(db.upcast(), edition),
             )
         }
     };
 
-    let mut p = Printer { db, body, buf: header, indent_level: 0, needs_indent: false };
+    let mut p = Printer { db, body, buf: header, indent_level: 0, needs_indent: false, edition };
     if let DefWithBodyId::FunctionId(it) = owner {
         p.buf.push('(');
         let function_data = &db.function_data(it);
@@ -86,8 +93,10 @@ pub(super) fn print_expr_hir(
     body: &Body,
     _owner: DefWithBodyId,
     expr: ExprId,
+    edition: Edition,
 ) -> String {
-    let mut p = Printer { db, body, buf: String::new(), indent_level: 0, needs_indent: false };
+    let mut p =
+        Printer { db, body, buf: String::new(), indent_level: 0, needs_indent: false, edition };
     p.print_expr(expr);
     p.buf
 }
@@ -113,6 +122,7 @@ struct Printer<'a> {
     buf: String,
     indent_level: usize,
     needs_indent: bool,
+    edition: Edition,
 }
 
 impl Write for Printer<'_> {
@@ -173,13 +183,14 @@ impl Printer<'_> {
             Expr::OffsetOf(offset_of) => {
                 w!(self, "builtin#offset_of(");
                 self.print_type_ref(&offset_of.container);
+                let edition = self.edition;
                 w!(
                     self,
                     ", {})",
                     offset_of
                         .fields
                         .iter()
-                        .format_with(".", |field, f| f(&field.display(self.db.upcast())))
+                        .format_with(".", |field, f| f(&field.display(self.db.upcast(), edition)))
                 );
             }
             Expr::Path(path) => self.print_path(path),
@@ -201,7 +212,7 @@ impl Printer<'_> {
             }
             Expr::Loop { body, label } => {
                 if let Some(lbl) = label {
-                    w!(self, "{}: ", self.body[*lbl].name.display(self.db.upcast()));
+                    w!(self, "{}: ", self.body[*lbl].name.display(self.db.upcast(), self.edition));
                 }
                 w!(self, "loop ");
                 self.print_expr(*body);
@@ -221,10 +232,11 @@ impl Printer<'_> {
             }
             Expr::MethodCall { receiver, method_name, args, generic_args } => {
                 self.print_expr(*receiver);
-                w!(self, ".{}", method_name.display(self.db.upcast()));
+                w!(self, ".{}", method_name.display(self.db.upcast(), self.edition));
                 if let Some(args) = generic_args {
                     w!(self, "::<");
-                    print_generic_args(self.db, args, self).unwrap();
+                    let edition = self.edition;
+                    print_generic_args(self.db, args, self, edition).unwrap();
                     w!(self, ">");
                 }
                 w!(self, "(");
@@ -259,13 +271,13 @@ impl Printer<'_> {
             Expr::Continue { label } => {
                 w!(self, "continue");
                 if let Some(lbl) = label {
-                    w!(self, " {}", self.body[*lbl].name.display(self.db.upcast()));
+                    w!(self, " {}", self.body[*lbl].name.display(self.db.upcast(), self.edition));
                 }
             }
             Expr::Break { expr, label } => {
                 w!(self, "break");
                 if let Some(lbl) = label {
-                    w!(self, " {}", self.body[*lbl].name.display(self.db.upcast()));
+                    w!(self, " {}", self.body[*lbl].name.display(self.db.upcast(), self.edition));
                 }
                 if let Some(expr) = expr {
                     self.whitespace();
@@ -307,9 +319,10 @@ impl Printer<'_> {
                 }
 
                 w!(self, "{{");
+                let edition = self.edition;
                 self.indented(|p| {
                     for field in &**fields {
-                        w!(p, "{}: ", field.name.display(self.db.upcast()));
+                        w!(p, "{}: ", field.name.display(self.db.upcast(), edition));
                         p.print_expr(field.expr);
                         wln!(p, ",");
                     }
@@ -326,7 +339,7 @@ impl Printer<'_> {
             }
             Expr::Field { expr, name } => {
                 self.print_expr(*expr);
-                w!(self, ".{}", name.display(self.db.upcast()));
+                w!(self, ".{}", name.display(self.db.upcast(), self.edition));
             }
             Expr::Await { expr } => {
                 self.print_expr(*expr);
@@ -464,8 +477,9 @@ impl Printer<'_> {
             }
             Expr::Literal(lit) => self.print_literal(lit),
             Expr::Block { id: _, statements, tail, label } => {
-                let label =
-                    label.map(|lbl| format!("{}: ", self.body[lbl].name.display(self.db.upcast())));
+                let label = label.map(|lbl| {
+                    format!("{}: ", self.body[lbl].name.display(self.db.upcast(), self.edition))
+                });
                 self.print_block(label.as_deref(), statements, tail);
             }
             Expr::Unsafe { id: _, statements, tail } => {
@@ -539,9 +553,10 @@ impl Printer<'_> {
                 }
 
                 w!(self, " {{");
+                let edition = self.edition;
                 self.indented(|p| {
                     for arg in args.iter() {
-                        w!(p, "{}: ", arg.name.display(self.db.upcast()));
+                        w!(p, "{}: ", arg.name.display(self.db.upcast(), edition));
                         p.print_pat(arg.pat);
                         wln!(p, ",");
                     }
@@ -686,11 +701,13 @@ impl Printer<'_> {
     }
 
     fn print_type_ref(&mut self, ty: &TypeRef) {
-        print_type_ref(self.db, ty, self).unwrap();
+        let edition = self.edition;
+        print_type_ref(self.db, ty, self, edition).unwrap();
     }
 
     fn print_path(&mut self, path: &Path) {
-        print_path(self.db, path, self).unwrap();
+        let edition = self.edition;
+        print_path(self.db, path, self, edition).unwrap();
     }
 
     fn print_binding(&mut self, id: BindingId) {
@@ -701,6 +718,6 @@ impl Printer<'_> {
             BindingAnnotation::Ref => "ref ",
             BindingAnnotation::RefMut => "ref mut ",
         };
-        w!(self, "{}{}", mode, name.display(self.db.upcast()));
+        w!(self, "{}{}", mode, name.display(self.db.upcast(), self.edition));
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs
index 0011d3a20c2a4..38fc01d8fca5f 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs
@@ -219,7 +219,7 @@ fn main() {
                 },
             );
         }"#]]
-    .assert_eq(&body.pretty_print(&db, def))
+    .assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
 }
 
 #[test]
@@ -285,7 +285,7 @@ impl SsrError {
                 ),
             );
         }"#]]
-    .assert_eq(&body.pretty_print(&db, def))
+    .assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
 }
 
 #[test]
@@ -333,5 +333,5 @@ fn f(a: i32, b: u32) -> String {
                 );
             };
         }"#]]
-    .assert_eq(&body.pretty_print(&db, def))
+    .assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
index 5a3a3e9189790..f5e03e5281e21 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
@@ -651,6 +651,7 @@ mod tests {
     use expect_test::{expect, Expect};
     use hir_expand::db::ExpandDatabase;
     use itertools::Itertools;
+    use span::Edition;
     use stdx::format_to;
     use syntax::ast::AstNode;
     use test_fixture::WithFixture;
@@ -717,8 +718,10 @@ mod tests {
                 "{:7}(imports {}): {}\n",
                 format!("{:?}", prefix),
                 if ignore_local_imports { '✖' } else { '✔' },
-                found_path
-                    .map_or_else(|| "<unresolvable>".to_owned(), |it| it.display(&db).to_string()),
+                found_path.map_or_else(
+                    || "<unresolvable>".to_owned(),
+                    |it| it.display(&db, Edition::CURRENT).to_string()
+                ),
             );
         }
         expect.assert_eq(&res);
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
index ebaaef66db6c8..6c34ee086aa9b 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
@@ -12,7 +12,6 @@ use hir_expand::{
 };
 use intern::Interned;
 use la_arena::{Arena, RawIdx};
-use once_cell::unsync::Lazy;
 use stdx::impl_from;
 use syntax::ast::{self, HasGenericParams, HasName, HasTypeBounds};
 use triomphe::Arc;
@@ -394,11 +393,16 @@ impl GenericParams {
 
                     // Don't create an `Expander` if not needed since this
                     // could cause a reparse after the `ItemTree` has been created due to the spanmap.
-                    let mut expander = Lazy::new(|| {
-                        (module.def_map(db), Expander::new(db, loc.id.file_id(), module))
-                    });
+                    let mut expander = None;
                     for param in func_data.params.iter() {
-                        generic_params.fill_implicit_impl_trait_args(db, &mut expander, param);
+                        generic_params.fill_implicit_impl_trait_args(
+                            db,
+                            &mut expander,
+                            &mut || {
+                                (module.def_map(db), Expander::new(db, loc.id.file_id(), module))
+                            },
+                            param,
+                        );
                     }
                     Interned::new(generic_params.finish())
                 }
@@ -597,7 +601,9 @@ impl GenericParamsCollector {
     fn fill_implicit_impl_trait_args(
         &mut self,
         db: &dyn DefDatabase,
-        exp: &mut Lazy<(Arc<DefMap>, Expander), impl FnOnce() -> (Arc<DefMap>, Expander)>,
+        // FIXME: Change this back to `LazyCell` if https://github.com/rust-lang/libs-team/issues/429 is accepted.
+        exp: &mut Option<(Arc<DefMap>, Expander)>,
+        exp_fill: &mut dyn FnMut() -> (Arc<DefMap>, Expander),
         type_ref: &TypeRef,
     ) {
         type_ref.walk(&mut |type_ref| {
@@ -617,7 +623,7 @@ impl GenericParamsCollector {
             }
             if let TypeRef::Macro(mc) = type_ref {
                 let macro_call = mc.to_node(db.upcast());
-                let (def_map, expander) = &mut **exp;
+                let (def_map, expander) = exp.get_or_insert_with(&mut *exp_fill);
 
                 let module = expander.module.local_id;
                 let resolver = |path: &_| {
@@ -637,8 +643,8 @@ impl GenericParamsCollector {
                 {
                     let ctx = expander.ctx(db);
                     let type_ref = TypeRef::from_ast(&ctx, expanded.tree());
-                    self.fill_implicit_impl_trait_args(db, &mut *exp, &type_ref);
-                    exp.1.exit(mark);
+                    self.fill_implicit_impl_trait_args(db, &mut *exp, exp_fill, &type_ref);
+                    exp.get_or_insert_with(&mut *exp_fill).1.exit(mark);
                 }
             }
         });
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs
index 390e7da677f64..e1c3bd25bcf0f 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs
@@ -250,7 +250,7 @@ pub(crate) fn parse(
                 }
             }
             ArgRef::Name(name, span) => {
-                let name = Name::new(name, tt::IdentIsRaw::No, call_ctx);
+                let name = Name::new(name, call_ctx);
                 if let Some((index, _)) = args.by_name(&name) {
                     record_usage(name, span);
                     // Name found in `args`, so we resolve it to its index.
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs
index 8f618b2d30343..b74cd90f6933a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs
@@ -10,6 +10,7 @@ use hir_expand::{
     AstId,
 };
 use intern::{sym, Interned, Symbol};
+use span::Edition;
 use syntax::ast::{self, HasGenericArgs, HasName, IsString};
 
 use crate::{
@@ -419,18 +420,22 @@ impl ConstRef {
         param.default_val().map(|default| Self::from_const_arg(lower_ctx, Some(default)))
     }
 
-    pub fn display<'a>(&'a self, db: &'a dyn ExpandDatabase) -> impl fmt::Display + 'a {
-        struct Display<'a>(&'a dyn ExpandDatabase, &'a ConstRef);
+    pub fn display<'a>(
+        &'a self,
+        db: &'a dyn ExpandDatabase,
+        edition: Edition,
+    ) -> impl fmt::Display + 'a {
+        struct Display<'a>(&'a dyn ExpandDatabase, &'a ConstRef, Edition);
         impl fmt::Display for Display<'_> {
             fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                 match self.1 {
                     ConstRef::Scalar(s) => s.fmt(f),
-                    ConstRef::Path(n) => n.display(self.0).fmt(f),
+                    ConstRef::Path(n) => n.display(self.0, self.2).fmt(f),
                     ConstRef::Complex(_) => f.write_str("{const}"),
                 }
             }
         }
-        Display(db, self)
+        Display(db, self, edition)
     }
 
     // We special case literals and single identifiers, to speed up things.
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
index 8cc022e4c60f2..9574e5d9cd37b 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
@@ -8,6 +8,7 @@ use hir_expand::name::Name;
 use itertools::Itertools;
 use rustc_hash::FxHashSet;
 use smallvec::SmallVec;
+use span::Edition;
 use stdx::{format_to, TupleExt};
 use syntax::ToSmolStr;
 use triomphe::Arc;
@@ -66,7 +67,12 @@ impl ImportMap {
         for (k, v) in self.item_to_info_map.iter() {
             format_to!(out, "{:?} ({:?}) -> ", k, v.1);
             for v in &v.0 {
-                format_to!(out, "{}:{:?}, ", v.name.display(db.upcast()), v.container);
+                format_to!(
+                    out,
+                    "{}:{:?}, ",
+                    v.name.display(db.upcast(), Edition::CURRENT),
+                    v.container
+                );
             }
             format_to!(out, "\n");
         }
@@ -83,7 +89,7 @@ impl ImportMap {
             // We've only collected items, whose name cannot be tuple field so unwrapping is fine.
             .flat_map(|(&item, (info, _))| {
                 info.iter().enumerate().map(move |(idx, info)| {
-                    (item, info.name.display(db.upcast()).to_smolstr(), idx as u32)
+                    (item, info.name.unescaped().display(db.upcast()).to_smolstr(), idx as u32)
                 })
             })
             .collect();
@@ -461,7 +467,7 @@ fn search_maps(
                     query.search_mode.check(
                         &query.query,
                         query.case_sensitive,
-                        &info.name.display(db.upcast()).to_smolstr(),
+                        &info.name.unescaped().display(db.upcast()).to_smolstr(),
                     )
                 });
             res.extend(iter.map(TupleExt::head));
@@ -577,7 +583,7 @@ mod tests {
         Some(format!(
             "{}::{}",
             render_path(db, &trait_info[0]),
-            assoc_item_name.display(db.upcast())
+            assoc_item_name.display(db.upcast(), Edition::CURRENT)
         ))
     }
 
@@ -616,7 +622,7 @@ mod tests {
             module = parent;
         }
 
-        segments.iter().rev().map(|it| it.display(db.upcast())).join("::")
+        segments.iter().rev().map(|it| it.display(db.upcast(), Edition::CURRENT)).join("::")
     }
 
     #[test]
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
index df6b1f55c1d96..a04f12cab7652 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
@@ -1,14 +1,16 @@
 //! Describes items defined or visible (ie, imported) in a certain scope.
 //! This is shared between modules and blocks.
 
+use std::sync::LazyLock;
+
 use base_db::CrateId;
 use hir_expand::{attrs::AttrId, db::ExpandDatabase, name::Name, AstId, MacroCallId};
 use indexmap::map::Entry;
 use itertools::Itertools;
 use la_arena::Idx;
-use once_cell::sync::Lazy;
 use rustc_hash::{FxHashMap, FxHashSet};
 use smallvec::{smallvec, SmallVec};
+use span::Edition;
 use stdx::format_to;
 use syntax::ast;
 
@@ -129,7 +131,7 @@ struct DeriveMacroInvocation {
     derive_call_ids: SmallVec<[Option<MacroCallId>; 1]>,
 }
 
-pub(crate) static BUILTIN_SCOPE: Lazy<FxIndexMap<Name, PerNs>> = Lazy::new(|| {
+pub(crate) static BUILTIN_SCOPE: LazyLock<FxIndexMap<Name, PerNs>> = LazyLock::new(|| {
     BuiltinType::all_builtin_types()
         .iter()
         .map(|(name, ty)| (name.clone(), PerNs::types((*ty).into(), Visibility::Public, None)))
@@ -706,7 +708,7 @@ impl ItemScope {
             format_to!(
                 buf,
                 "{}:",
-                name.map_or("_".to_owned(), |name| name.display(db).to_string())
+                name.map_or("_".to_owned(), |name| name.display(db, Edition::LATEST).to_string())
             );
 
             if let Some((.., i)) = def.types {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
index 28eebb286ed4b..f16230e1dc311 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
@@ -40,6 +40,7 @@ use std::{
     fmt::{self, Debug},
     hash::{Hash, Hasher},
     ops::{Index, Range},
+    sync::OnceLock,
 };
 
 use ast::{AstNode, StructKind};
@@ -48,10 +49,9 @@ use either::Either;
 use hir_expand::{attrs::RawAttrs, name::Name, ExpandTo, HirFileId, InFile};
 use intern::{Interned, Symbol};
 use la_arena::{Arena, Idx, RawIdx};
-use once_cell::sync::OnceCell;
 use rustc_hash::FxHashMap;
 use smallvec::SmallVec;
-use span::{AstIdNode, FileAstId, SyntaxContextId};
+use span::{AstIdNode, Edition, FileAstId, SyntaxContextId};
 use stdx::never;
 use syntax::{ast, match_ast, SyntaxKind};
 use triomphe::Arc;
@@ -101,7 +101,7 @@ pub struct ItemTree {
 impl ItemTree {
     pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
         let _p = tracing::info_span!("file_item_tree_query", ?file_id).entered();
-        static EMPTY: OnceCell<Arc<ItemTree>> = OnceCell::new();
+        static EMPTY: OnceLock<Arc<ItemTree>> = OnceLock::new();
 
         let syntax = db.parse_or_expand(file_id);
 
@@ -152,7 +152,7 @@ impl ItemTree {
 
     pub(crate) fn block_item_tree_query(db: &dyn DefDatabase, block: BlockId) -> Arc<ItemTree> {
         let _p = tracing::info_span!("block_item_tree_query", ?block).entered();
-        static EMPTY: OnceCell<Arc<ItemTree>> = OnceCell::new();
+        static EMPTY: OnceLock<Arc<ItemTree>> = OnceLock::new();
 
         let loc = block.lookup(db);
         let block = loc.ast_id.to_node(db.upcast());
@@ -199,8 +199,8 @@ impl ItemTree {
         Attrs::filter(db, krate, self.raw_attrs(of).clone())
     }
 
-    pub fn pretty_print(&self, db: &dyn DefDatabase) -> String {
-        pretty::print_item_tree(db, self)
+    pub fn pretty_print(&self, db: &dyn DefDatabase, edition: Edition) -> String {
+        pretty::print_item_tree(db, self, edition)
     }
 
     fn data(&self) -> &ItemTreeData {
@@ -626,9 +626,9 @@ impl Index<RawVisibilityId> for ItemTree {
     type Output = RawVisibility;
     fn index(&self, index: RawVisibilityId) -> &Self::Output {
         static VIS_PUB: RawVisibility = RawVisibility::Public;
-        static VIS_PRIV_IMPLICIT: OnceCell<RawVisibility> = OnceCell::new();
-        static VIS_PRIV_EXPLICIT: OnceCell<RawVisibility> = OnceCell::new();
-        static VIS_PUB_CRATE: OnceCell<RawVisibility> = OnceCell::new();
+        static VIS_PRIV_IMPLICIT: OnceLock<RawVisibility> = OnceLock::new();
+        static VIS_PRIV_EXPLICIT: OnceLock<RawVisibility> = OnceLock::new();
+        static VIS_PUB_CRATE: OnceLock<RawVisibility> = OnceLock::new();
 
         match index {
             RawVisibilityId::PRIV_IMPLICIT => VIS_PRIV_IMPLICIT.get_or_init(|| {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
index 740759e6e3948..b5a65abce8696 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
@@ -3,7 +3,7 @@
 use std::fmt::{self, Write};
 
 use la_arena::{Idx, RawIdx};
-use span::ErasedFileAstId;
+use span::{Edition, ErasedFileAstId};
 
 use crate::{
     generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget},
@@ -18,8 +18,9 @@ use crate::{
     visibility::RawVisibility,
 };
 
-pub(super) fn print_item_tree(db: &dyn DefDatabase, tree: &ItemTree) -> String {
-    let mut p = Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true };
+pub(super) fn print_item_tree(db: &dyn DefDatabase, tree: &ItemTree, edition: Edition) -> String {
+    let mut p =
+        Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true, edition };
 
     if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) {
         p.print_attrs(attrs, true, "\n");
@@ -56,6 +57,7 @@ struct Printer<'a> {
     buf: String,
     indent_level: usize,
     needs_indent: bool,
+    edition: Edition,
 }
 
 impl Printer<'_> {
@@ -97,7 +99,7 @@ impl Printer<'_> {
                 self,
                 "#{}[{}{}]{}",
                 inner,
-                attr.path.display(self.db.upcast()),
+                attr.path.display(self.db.upcast(), self.edition),
                 attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(),
                 separated_by,
             );
@@ -113,13 +115,14 @@ impl Printer<'_> {
     fn print_visibility(&mut self, vis: RawVisibilityId) {
         match &self.tree[vis] {
             RawVisibility::Module(path, _expl) => {
-                w!(self, "pub({}) ", path.display(self.db.upcast()))
+                w!(self, "pub({}) ", path.display(self.db.upcast(), self.edition))
             }
             RawVisibility::Public => w!(self, "pub "),
         };
     }
 
     fn print_fields(&mut self, parent: FieldParent, kind: FieldsShape, fields: &[Field]) {
+        let edition = self.edition;
         match kind {
             FieldsShape::Record => {
                 self.whitespace();
@@ -131,7 +134,7 @@ impl Printer<'_> {
                             "\n",
                         );
                         this.print_visibility(*visibility);
-                        w!(this, "{}: ", name.display(self.db.upcast()));
+                        w!(this, "{}: ", name.display(self.db.upcast(), edition));
                         this.print_type_ref(type_ref);
                         wln!(this, ",");
                     }
@@ -147,7 +150,7 @@ impl Printer<'_> {
                             "\n",
                         );
                         this.print_visibility(*visibility);
-                        w!(this, "{}: ", name.display(self.db.upcast()));
+                        w!(this, "{}: ", name.display(self.db.upcast(), edition));
                         this.print_type_ref(type_ref);
                         wln!(this, ",");
                     }
@@ -186,20 +189,20 @@ impl Printer<'_> {
     fn print_use_tree(&mut self, use_tree: &UseTree) {
         match &use_tree.kind {
             UseTreeKind::Single { path, alias } => {
-                w!(self, "{}", path.display(self.db.upcast()));
+                w!(self, "{}", path.display(self.db.upcast(), self.edition));
                 if let Some(alias) = alias {
-                    w!(self, " as {}", alias);
+                    w!(self, " as {}", alias.display(self.edition));
                 }
             }
             UseTreeKind::Glob { path } => {
                 if let Some(path) = path {
-                    w!(self, "{}::", path.display(self.db.upcast()));
+                    w!(self, "{}::", path.display(self.db.upcast(), self.edition));
                 }
                 w!(self, "*");
             }
             UseTreeKind::Prefixed { prefix, list } => {
                 if let Some(prefix) = prefix {
-                    w!(self, "{}::", prefix.display(self.db.upcast()));
+                    w!(self, "{}::", prefix.display(self.db.upcast(), self.edition));
                 }
                 w!(self, "{{");
                 for (i, tree) in list.iter().enumerate() {
@@ -229,9 +232,9 @@ impl Printer<'_> {
                 let ExternCrate { name, alias, visibility, ast_id } = &self.tree[it];
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
-                w!(self, "extern crate {}", name.display(self.db.upcast()));
+                w!(self, "extern crate {}", name.display(self.db.upcast(), self.edition));
                 if let Some(alias) = alias {
-                    w!(self, " as {}", alias);
+                    w!(self, " as {}", alias.display(self.edition));
                 }
                 wln!(self, ";");
             }
@@ -278,7 +281,7 @@ impl Printer<'_> {
                 if let Some(abi) = abi {
                     w!(self, "extern \"{}\" ", abi);
                 }
-                w!(self, "fn {}", name.display(self.db.upcast()));
+                w!(self, "fn {}", name.display(self.db.upcast(), self.edition));
                 self.print_generic_params(explicit_generic_params, it.into());
                 w!(self, "(");
                 if !params.is_empty() {
@@ -314,7 +317,7 @@ impl Printer<'_> {
                     &self.tree[it];
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
-                w!(self, "struct {}", name.display(self.db.upcast()));
+                w!(self, "struct {}", name.display(self.db.upcast(), self.edition));
                 self.print_generic_params(generic_params, it.into());
                 self.print_fields_and_where_clause(
                     FieldParent::Struct(it),
@@ -332,7 +335,7 @@ impl Printer<'_> {
                 let Union { name, visibility, fields, generic_params, ast_id } = &self.tree[it];
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
-                w!(self, "union {}", name.display(self.db.upcast()));
+                w!(self, "union {}", name.display(self.db.upcast(), self.edition));
                 self.print_generic_params(generic_params, it.into());
                 self.print_fields_and_where_clause(
                     FieldParent::Union(it),
@@ -346,15 +349,16 @@ impl Printer<'_> {
                 let Enum { name, visibility, variants, generic_params, ast_id } = &self.tree[it];
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
-                w!(self, "enum {}", name.display(self.db.upcast()));
+                w!(self, "enum {}", name.display(self.db.upcast(), self.edition));
                 self.print_generic_params(generic_params, it.into());
                 self.print_where_clause_and_opening_brace(generic_params);
+                let edition = self.edition;
                 self.indented(|this| {
                     for variant in FileItemTreeId::range_iter(variants.clone()) {
                         let Variant { name, fields, shape: kind, ast_id } = &this.tree[variant];
                         this.print_ast_id(ast_id.erase());
                         this.print_attrs_of(variant, "\n");
-                        w!(this, "{}", name.display(self.db.upcast()));
+                        w!(this, "{}", name.display(self.db.upcast(), edition));
                         this.print_fields(FieldParent::Variant(variant), *kind, fields);
                         wln!(this, ",");
                     }
@@ -367,7 +371,7 @@ impl Printer<'_> {
                 self.print_visibility(*visibility);
                 w!(self, "const ");
                 match name {
-                    Some(name) => w!(self, "{}", name.display(self.db.upcast())),
+                    Some(name) => w!(self, "{}", name.display(self.db.upcast(), self.edition)),
                     None => w!(self, "_"),
                 }
                 w!(self, ": ");
@@ -382,7 +386,7 @@ impl Printer<'_> {
                 if *mutable {
                     w!(self, "mut ");
                 }
-                w!(self, "{}: ", name.display(self.db.upcast()));
+                w!(self, "{}: ", name.display(self.db.upcast(), self.edition));
                 self.print_type_ref(type_ref);
                 w!(self, " = _;");
                 wln!(self);
@@ -398,7 +402,7 @@ impl Printer<'_> {
                 if *is_auto {
                     w!(self, "auto ");
                 }
-                w!(self, "trait {}", name.display(self.db.upcast()));
+                w!(self, "trait {}", name.display(self.db.upcast(), self.edition));
                 self.print_generic_params(generic_params, it.into());
                 self.print_where_clause_and_opening_brace(generic_params);
                 self.indented(|this| {
@@ -412,7 +416,7 @@ impl Printer<'_> {
                 let TraitAlias { name, visibility, generic_params, ast_id } = &self.tree[it];
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
-                w!(self, "trait {}", name.display(self.db.upcast()));
+                w!(self, "trait {}", name.display(self.db.upcast(), self.edition));
                 self.print_generic_params(generic_params, it.into());
                 w!(self, " = ");
                 self.print_where_clause(generic_params);
@@ -457,7 +461,7 @@ impl Printer<'_> {
                     &self.tree[it];
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
-                w!(self, "type {}", name.display(self.db.upcast()));
+                w!(self, "type {}", name.display(self.db.upcast(), self.edition));
                 self.print_generic_params(generic_params, it.into());
                 if !bounds.is_empty() {
                     w!(self, ": ");
@@ -475,7 +479,7 @@ impl Printer<'_> {
                 let Mod { name, visibility, kind, ast_id } = &self.tree[it];
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
-                w!(self, "mod {}", name.display(self.db.upcast()));
+                w!(self, "mod {}", name.display(self.db.upcast(), self.edition));
                 match kind {
                     ModKind::Inline { items } => {
                         w!(self, " {{");
@@ -500,18 +504,22 @@ impl Printer<'_> {
                     ctxt,
                     expand_to
                 );
-                wln!(self, "{}!(...);", path.display(self.db.upcast()));
+                wln!(self, "{}!(...);", path.display(self.db.upcast(), self.edition));
             }
             ModItem::MacroRules(it) => {
                 let MacroRules { name, ast_id } = &self.tree[it];
                 self.print_ast_id(ast_id.erase());
-                wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db.upcast()));
+                wln!(
+                    self,
+                    "macro_rules! {} {{ ... }}",
+                    name.display(self.db.upcast(), self.edition)
+                );
             }
             ModItem::Macro2(it) => {
                 let Macro2 { name, visibility, ast_id } = &self.tree[it];
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
-                wln!(self, "macro {} {{ ... }}", name.display(self.db.upcast()));
+                wln!(self, "macro {} {{ ... }}", name.display(self.db.upcast(), self.edition));
             }
         }
 
@@ -519,15 +527,18 @@ impl Printer<'_> {
     }
 
     fn print_type_ref(&mut self, type_ref: &TypeRef) {
-        print_type_ref(self.db, type_ref, self).unwrap();
+        let edition = self.edition;
+        print_type_ref(self.db, type_ref, self, edition).unwrap();
     }
 
     fn print_type_bounds(&mut self, bounds: &[Interned<TypeBound>]) {
-        print_type_bounds(self.db, bounds, self).unwrap();
+        let edition = self.edition;
+        print_type_bounds(self.db, bounds, self, edition).unwrap();
     }
 
     fn print_path(&mut self, path: &Path) {
-        print_path(self.db, path, self).unwrap();
+        let edition = self.edition;
+        print_path(self.db, path, self, edition).unwrap();
     }
 
     fn print_generic_params(&mut self, params: &GenericParams, parent: GenericModItem) {
@@ -543,7 +554,7 @@ impl Printer<'_> {
             }
             first = false;
             self.print_attrs_of(AttrOwner::LifetimeParamData(parent, idx), " ");
-            w!(self, "{}", lt.name.display(self.db.upcast()));
+            w!(self, "{}", lt.name.display(self.db.upcast(), self.edition));
         }
         for (idx, x) in params.iter_type_or_consts() {
             if !first {
@@ -553,11 +564,11 @@ impl Printer<'_> {
             self.print_attrs_of(AttrOwner::TypeOrConstParamData(parent, idx), " ");
             match x {
                 TypeOrConstParamData::TypeParamData(ty) => match &ty.name {
-                    Some(name) => w!(self, "{}", name.display(self.db.upcast())),
+                    Some(name) => w!(self, "{}", name.display(self.db.upcast(), self.edition)),
                     None => w!(self, "_anon_{}", idx.into_raw()),
                 },
                 TypeOrConstParamData::ConstParamData(konst) => {
-                    w!(self, "const {}: ", konst.name.display(self.db.upcast()));
+                    w!(self, "const {}: ", konst.name.display(self.db.upcast(), self.edition));
                     self.print_type_ref(&konst.ty);
                 }
             }
@@ -580,6 +591,7 @@ impl Printer<'_> {
         }
 
         w!(self, "\nwhere");
+        let edition = self.edition;
         self.indented(|this| {
             for (i, pred) in params.where_predicates().enumerate() {
                 if i != 0 {
@@ -592,8 +604,8 @@ impl Printer<'_> {
                         wln!(
                             this,
                             "{}: {},",
-                            target.name.display(self.db.upcast()),
-                            bound.name.display(self.db.upcast())
+                            target.name.display(self.db.upcast(), edition),
+                            bound.name.display(self.db.upcast(), edition)
                         );
                         continue;
                     }
@@ -603,7 +615,7 @@ impl Printer<'_> {
                             if i != 0 {
                                 w!(this, ", ");
                             }
-                            w!(this, "{}", lt.display(self.db.upcast()));
+                            w!(this, "{}", lt.display(self.db.upcast(), edition));
                         }
                         w!(this, "> ");
                         (target, bound)
@@ -613,7 +625,7 @@ impl Printer<'_> {
                 match target {
                     WherePredicateTypeTarget::TypeRef(ty) => this.print_type_ref(ty),
                     WherePredicateTypeTarget::TypeOrConstParam(id) => match params[*id].name() {
-                        Some(name) => w!(this, "{}", name.display(self.db.upcast())),
+                        Some(name) => w!(this, "{}", name.display(self.db.upcast(), edition)),
                         None => w!(this, "_anon_{}", id.into_raw()),
                     },
                 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
index c6930401a6f58..5c07369f4b5b6 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
@@ -1,4 +1,5 @@
 use expect_test::{expect, Expect};
+use span::Edition;
 use test_fixture::WithFixture;
 
 use crate::{db::DefDatabase, test_db::TestDB};
@@ -6,7 +7,7 @@ use crate::{db::DefDatabase, test_db::TestDB};
 fn check(ra_fixture: &str, expect: Expect) {
     let (db, file_id) = TestDB::with_single_file(ra_fixture);
     let item_tree = db.file_item_tree(file_id.into());
-    let pretty = item_tree.pretty_print(&db);
+    let pretty = item_tree.pretty_print(&db, Edition::CURRENT);
     expect.assert_eq(&pretty);
 }
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
index a09fd658aeb8c..166c965d14c67 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
@@ -74,6 +74,13 @@ impl LangItemTarget {
             _ => None,
         }
     }
+
+    pub fn as_type_alias(self) -> Option<TypeAliasId> {
+        match self {
+            LangItemTarget::TypeAlias(id) => Some(id),
+            _ => None,
+        }
+    }
 }
 
 #[derive(Default, Debug, Clone, PartialEq, Eq)]
@@ -117,11 +124,19 @@ impl LangItems {
                 match def {
                     ModuleDefId::TraitId(trait_) => {
                         lang_items.collect_lang_item(db, trait_, LangItemTarget::Trait);
-                        db.trait_data(trait_).items.iter().for_each(|&(_, assoc_id)| {
-                            if let AssocItemId::FunctionId(f) = assoc_id {
-                                lang_items.collect_lang_item(db, f, LangItemTarget::Function);
-                            }
-                        });
+                        db.trait_data(trait_).items.iter().for_each(
+                            |&(_, assoc_id)| match assoc_id {
+                                AssocItemId::FunctionId(f) => {
+                                    lang_items.collect_lang_item(db, f, LangItemTarget::Function);
+                                }
+                                AssocItemId::TypeAliasId(alias) => lang_items.collect_lang_item(
+                                    db,
+                                    alias,
+                                    LangItemTarget::TypeAlias,
+                                ),
+                                AssocItemId::ConstId(_) => {}
+                            },
+                        );
                     }
                     ModuleDefId::AdtId(AdtId::EnumId(e)) => {
                         lang_items.collect_lang_item(db, e, LangItemTarget::EnumId);
@@ -453,6 +468,7 @@ language_item_table! {
 
     Context,                 sym::Context,             context,                    Target::Struct,         GenericRequirement::None;
     FuturePoll,              sym::poll,                future_poll_fn,             Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+    FutureOutput,            sym::future_output,       future_output,              Target::TypeAlias,      GenericRequirement::None;
 
     Option,                  sym::Option,              option_type,                Target::Enum,           GenericRequirement::None;
     OptionSome,              sym::Some,                option_some_variant,        Target::Variant,        GenericRequirement::None;
@@ -467,6 +483,7 @@ language_item_table! {
     IntoFutureIntoFuture,    sym::into_future,         into_future_fn,             Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
     IntoIterIntoIter,        sym::into_iter,           into_iter_fn,               Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
     IteratorNext,            sym::next,                next_fn,                    Target::Method(MethodKind::Trait { body: false}), GenericRequirement::None;
+    Iterator,                sym::iterator,            iterator,                   Target::Trait,           GenericRequirement::None;
 
     PinNewUnchecked,         sym::new_unchecked,       new_unchecked_fn,           Target::Method(MethodKind::Inherent), GenericRequirement::None;
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
index 4ced30c81dced..0213bd904b68c 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
@@ -241,7 +241,7 @@ pub type StaticLoc = AssocItemLoc<Static>;
 impl_intern!(StaticId, StaticLoc, intern_static, lookup_intern_static);
 impl_loc!(StaticLoc, id: Static, container: ItemContainerId);
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
 pub struct TraitId(salsa::InternId);
 pub type TraitLoc = ItemLoc<Trait>;
 impl_intern!(TraitId, TraitLoc, intern_trait, lookup_intern_trait);
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
index b430e2cefb35d..7f761192517c4 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -25,7 +25,7 @@ use hir_expand::{
     InFile, MacroFileId, MacroFileIdExt,
 };
 use intern::Symbol;
-use span::Span;
+use span::{Edition, Span};
 use stdx::{format_to, format_to_acc};
 use syntax::{
     ast::{self, edit::IndentLevel},
@@ -257,21 +257,25 @@ fn pretty_print_macro_expansion(
             (T![;] | T!['{'] | T!['}'], _) => "\n",
             (_, T!['}']) => "\n",
             (IDENT | LIFETIME_IDENT, IDENT | LIFETIME_IDENT) => " ",
-            _ if prev_kind.is_keyword() && curr_kind.is_keyword() => " ",
-            (IDENT, _) if curr_kind.is_keyword() => " ",
-            (_, IDENT) if prev_kind.is_keyword() => " ",
+            _ if prev_kind.is_keyword(Edition::CURRENT)
+                && curr_kind.is_keyword(Edition::CURRENT) =>
+            {
+                " "
+            }
+            (IDENT, _) if curr_kind.is_keyword(Edition::CURRENT) => " ",
+            (_, IDENT) if prev_kind.is_keyword(Edition::CURRENT) => " ",
             (T![>], IDENT) => " ",
-            (T![>], _) if curr_kind.is_keyword() => " ",
+            (T![>], _) if curr_kind.is_keyword(Edition::CURRENT) => " ",
             (T![->], _) | (_, T![->]) => " ",
             (T![&&], _) | (_, T![&&]) => " ",
             (T![,], _) => " ",
             (T![:], IDENT | T!['(']) => " ",
-            (T![:], _) if curr_kind.is_keyword() => " ",
+            (T![:], _) if curr_kind.is_keyword(Edition::CURRENT) => " ",
             (T![fn], T!['(']) => "",
-            (T![']'], _) if curr_kind.is_keyword() => " ",
+            (T![']'], _) if curr_kind.is_keyword(Edition::CURRENT) => " ",
             (T![']'], T![#]) => "\n",
             (T![Self], T![::]) => "",
-            _ if prev_kind.is_keyword() => " ",
+            _ if prev_kind.is_keyword(Edition::CURRENT) => " ",
             _ => "",
         };
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
index 8825e46336326..11601c683e1ea 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
@@ -328,6 +328,10 @@ impl DefMap {
     /// The module id of a crate or block root.
     pub const ROOT: LocalModuleId = LocalModuleId::from_raw(la_arena::RawIdx::from_u32(0));
 
+    pub fn edition(&self) -> Edition {
+        self.data.edition
+    }
+
     pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, crate_id: CrateId) -> Arc<DefMap> {
         let crate_graph = db.crate_graph();
         let krate = &crate_graph[crate_id];
@@ -550,7 +554,7 @@ impl DefMap {
             for (name, child) in
                 map.modules[module].children.iter().sorted_by(|a, b| Ord::cmp(&a.0, &b.0))
             {
-                let path = format!("{path}::{}", name.display(db.upcast()));
+                let path = format!("{path}::{}", name.display(db.upcast(), Edition::LATEST));
                 buf.push('\n');
                 go(buf, db, map, &path, *child);
             }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
index debc5a44326ef..96db3db8f0d8a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
@@ -548,7 +548,7 @@ impl DefCollector<'_> {
             types => {
                 tracing::debug!(
                     "could not resolve prelude path `{}` to module (resolved to {:?})",
-                    path.display(self.db.upcast()),
+                    path.display(self.db.upcast(), Edition::LATEST),
                     types
                 );
             }
@@ -768,7 +768,7 @@ impl DefCollector<'_> {
     }
 
     fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport {
-        let _p = tracing::info_span!("resolve_import", import_path = %import.path.display(self.db.upcast()))
+        let _p = tracing::info_span!("resolve_import", import_path = %import.path.display(self.db.upcast(), Edition::LATEST))
             .entered();
         tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition);
         match import.source {
@@ -1606,7 +1606,11 @@ impl ModCollector<'_, '_> {
 
         // Prelude module is always considered to be `#[macro_use]`.
         if let Some((prelude_module, _use)) = self.def_collector.def_map.prelude {
-            if prelude_module.krate != krate && is_crate_root {
+            // Don't insert macros from the prelude into blocks, as they can be shadowed by other macros.
+            if prelude_module.krate != krate
+                && is_crate_root
+                && self.def_collector.def_map.block.is_none()
+            {
                 cov_mark::hit!(prelude_is_macro_use);
                 self.def_collector.import_macros_from_extern_crate(
                     prelude_module.krate,
@@ -2151,7 +2155,7 @@ impl ModCollector<'_, '_> {
             }
             tracing::debug!(
                 "non-builtin attribute {}",
-                attr.path.display(self.def_collector.db.upcast())
+                attr.path.display(self.def_collector.db.upcast(), Edition::LATEST)
             );
 
             let ast_id = AstIdWithPath::new(
@@ -2286,8 +2290,8 @@ impl ModCollector<'_, '_> {
                         stdx::always!(
                             name == mac.name,
                             "built-in macro {} has #[rustc_builtin_macro] which declares different name {}",
-                            mac.name.display(self.def_collector.db.upcast()),
-                            name.display(self.def_collector.db.upcast())
+                            mac.name.display(self.def_collector.db.upcast(), Edition::LATEST),
+                            name.display(self.def_collector.db.upcast(), Edition::LATEST),
                         );
                         helpers_opt = Some(helpers);
                     }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs
index 390c934f6dac1..a05c4dcf9bd70 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs
@@ -1,6 +1,7 @@
 use expect_test::expect;
 
 use itertools::Itertools;
+use span::Edition;
 
 use super::*;
 
@@ -1100,7 +1101,7 @@ pub fn derive_macro_2(_item: TokenStream) -> TokenStream {
     assert_eq!(def_map.data.exported_derives.len(), 1);
     match def_map.data.exported_derives.values().next() {
         Some(helpers) => match &**helpers {
-            [attr] => assert_eq!(attr.display(&db).to_string(), "helper_attr"),
+            [attr] => assert_eq!(attr.display(&db, Edition::CURRENT).to_string(), "helper_attr"),
             _ => unreachable!(),
         },
         _ => unreachable!(),
@@ -1456,7 +1457,7 @@ fn proc_attr(a: TokenStream, b: TokenStream) -> TokenStream { a }
     let actual = def_map
         .macro_use_prelude
         .keys()
-        .map(|name| name.display(&db).to_string())
+        .map(|name| name.display(&db, Edition::CURRENT).to_string())
         .sorted()
         .join("\n");
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs
index 1327d9aa62e12..071b55c83d8de 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs
@@ -144,14 +144,14 @@ pub struct Baz;
 
             crate::r#async
             Bar: t v
-            foo: t
             r#async: t
-
-            crate::r#async::foo
-            Foo: t v
+            foo: t
 
             crate::r#async::r#async
             Baz: t v
+
+            crate::r#async::foo
+            Foo: t v
         "#]],
     );
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path.rs b/src/tools/rust-analyzer/crates/hir-def/src/path.rs
index f90bc954a9b3d..077863c0c939c 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/path.rs
@@ -13,7 +13,8 @@ use crate::{
 };
 use hir_expand::name::Name;
 use intern::Interned;
-use syntax::{ast, ToSmolStr};
+use span::Edition;
+use syntax::ast;
 
 pub use hir_expand::mod_path::{path, ModPath, PathKind};
 
@@ -25,11 +26,21 @@ pub enum ImportAlias {
     Alias(Name),
 }
 
-impl Display for ImportAlias {
+impl ImportAlias {
+    pub fn display(&self, edition: Edition) -> impl Display + '_ {
+        ImportAliasDisplay { value: self, edition }
+    }
+}
+
+struct ImportAliasDisplay<'a> {
+    value: &'a ImportAlias,
+    edition: Edition,
+}
+impl Display for ImportAliasDisplay<'_> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
+        match self.value {
             ImportAlias::Underscore => f.write_str("_"),
-            ImportAlias::Alias(name) => f.write_str(&name.display_no_db().to_smolstr()),
+            ImportAlias::Alias(name) => Display::fmt(&name.display_no_db(self.edition), f),
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
index 3ee88b536fc4a..d5ef17a91fb2b 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
@@ -5,6 +5,7 @@ use std::fmt::{self, Write};
 use hir_expand::mod_path::PathKind;
 use intern::Interned;
 use itertools::Itertools;
+use span::Edition;
 
 use crate::{
     db::DefDatabase,
@@ -13,46 +14,51 @@ use crate::{
     type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef},
 };
 
-pub(crate) fn print_path(db: &dyn DefDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result {
+pub(crate) fn print_path(
+    db: &dyn DefDatabase,
+    path: &Path,
+    buf: &mut dyn Write,
+    edition: Edition,
+) -> fmt::Result {
     if let Path::LangItem(it, s) = path {
         write!(buf, "builtin#lang(")?;
         match *it {
             LangItemTarget::ImplDef(it) => write!(buf, "{it:?}")?,
             LangItemTarget::EnumId(it) => {
-                write!(buf, "{}", db.enum_data(it).name.display(db.upcast()))?
+                write!(buf, "{}", db.enum_data(it).name.display(db.upcast(), edition))?
             }
             LangItemTarget::Function(it) => {
-                write!(buf, "{}", db.function_data(it).name.display(db.upcast()))?
+                write!(buf, "{}", db.function_data(it).name.display(db.upcast(), edition))?
             }
             LangItemTarget::Static(it) => {
-                write!(buf, "{}", db.static_data(it).name.display(db.upcast()))?
+                write!(buf, "{}", db.static_data(it).name.display(db.upcast(), edition))?
             }
             LangItemTarget::Struct(it) => {
-                write!(buf, "{}", db.struct_data(it).name.display(db.upcast()))?
+                write!(buf, "{}", db.struct_data(it).name.display(db.upcast(), edition))?
             }
             LangItemTarget::Union(it) => {
-                write!(buf, "{}", db.union_data(it).name.display(db.upcast()))?
+                write!(buf, "{}", db.union_data(it).name.display(db.upcast(), edition))?
             }
             LangItemTarget::TypeAlias(it) => {
-                write!(buf, "{}", db.type_alias_data(it).name.display(db.upcast()))?
+                write!(buf, "{}", db.type_alias_data(it).name.display(db.upcast(), edition))?
             }
             LangItemTarget::Trait(it) => {
-                write!(buf, "{}", db.trait_data(it).name.display(db.upcast()))?
+                write!(buf, "{}", db.trait_data(it).name.display(db.upcast(), edition))?
             }
             LangItemTarget::EnumVariant(it) => {
-                write!(buf, "{}", db.enum_variant_data(it).name.display(db.upcast()))?
+                write!(buf, "{}", db.enum_variant_data(it).name.display(db.upcast(), edition))?
             }
         }
 
         if let Some(s) = s {
-            write!(buf, "::{}", s.display(db.upcast()))?;
+            write!(buf, "::{}", s.display(db.upcast(), edition))?;
         }
         return write!(buf, ")");
     }
     match path.type_anchor() {
         Some(anchor) => {
             write!(buf, "<")?;
-            print_type_ref(db, anchor, buf)?;
+            print_type_ref(db, anchor, buf, edition)?;
             write!(buf, ">::")?;
         }
         None => match path.kind() {
@@ -78,10 +84,10 @@ pub(crate) fn print_path(db: &dyn DefDatabase, path: &Path, buf: &mut dyn Write)
             write!(buf, "::")?;
         }
 
-        write!(buf, "{}", segment.name.display(db.upcast()))?;
+        write!(buf, "{}", segment.name.display(db.upcast(), edition))?;
         if let Some(generics) = segment.args_and_bindings {
             write!(buf, "::<")?;
-            print_generic_args(db, generics, buf)?;
+            print_generic_args(db, generics, buf, edition)?;
 
             write!(buf, ">")?;
         }
@@ -94,12 +100,13 @@ pub(crate) fn print_generic_args(
     db: &dyn DefDatabase,
     generics: &GenericArgs,
     buf: &mut dyn Write,
+    edition: Edition,
 ) -> fmt::Result {
     let mut first = true;
     let args = if generics.has_self_type {
         let (self_ty, args) = generics.args.split_first().unwrap();
         write!(buf, "Self=")?;
-        print_generic_arg(db, self_ty, buf)?;
+        print_generic_arg(db, self_ty, buf, edition)?;
         first = false;
         args
     } else {
@@ -110,21 +117,21 @@ pub(crate) fn print_generic_args(
             write!(buf, ", ")?;
         }
         first = false;
-        print_generic_arg(db, arg, buf)?;
+        print_generic_arg(db, arg, buf, edition)?;
     }
     for binding in generics.bindings.iter() {
         if !first {
             write!(buf, ", ")?;
         }
         first = false;
-        write!(buf, "{}", binding.name.display(db.upcast()))?;
+        write!(buf, "{}", binding.name.display(db.upcast(), edition))?;
         if !binding.bounds.is_empty() {
             write!(buf, ": ")?;
-            print_type_bounds(db, &binding.bounds, buf)?;
+            print_type_bounds(db, &binding.bounds, buf, edition)?;
         }
         if let Some(ty) = &binding.type_ref {
             write!(buf, " = ")?;
-            print_type_ref(db, ty, buf)?;
+            print_type_ref(db, ty, buf, edition)?;
         }
     }
     Ok(())
@@ -134,11 +141,12 @@ pub(crate) fn print_generic_arg(
     db: &dyn DefDatabase,
     arg: &GenericArg,
     buf: &mut dyn Write,
+    edition: Edition,
 ) -> fmt::Result {
     match arg {
-        GenericArg::Type(ty) => print_type_ref(db, ty, buf),
-        GenericArg::Const(c) => write!(buf, "{}", c.display(db.upcast())),
-        GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast())),
+        GenericArg::Type(ty) => print_type_ref(db, ty, buf, edition),
+        GenericArg::Const(c) => write!(buf, "{}", c.display(db.upcast(), edition)),
+        GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast(), edition)),
     }
 }
 
@@ -146,6 +154,7 @@ pub(crate) fn print_type_ref(
     db: &dyn DefDatabase,
     type_ref: &TypeRef,
     buf: &mut dyn Write,
+    edition: Edition,
 ) -> fmt::Result {
     // FIXME: deduplicate with `HirDisplay` impl
     match type_ref {
@@ -157,18 +166,18 @@ pub(crate) fn print_type_ref(
                 if i != 0 {
                     write!(buf, ", ")?;
                 }
-                print_type_ref(db, field, buf)?;
+                print_type_ref(db, field, buf, edition)?;
             }
             write!(buf, ")")?;
         }
-        TypeRef::Path(path) => print_path(db, path, buf)?,
+        TypeRef::Path(path) => print_path(db, path, buf, edition)?,
         TypeRef::RawPtr(pointee, mtbl) => {
             let mtbl = match mtbl {
                 Mutability::Shared => "*const",
                 Mutability::Mut => "*mut",
             };
             write!(buf, "{mtbl} ")?;
-            print_type_ref(db, pointee, buf)?;
+            print_type_ref(db, pointee, buf, edition)?;
         }
         TypeRef::Reference(pointee, lt, mtbl) => {
             let mtbl = match mtbl {
@@ -177,19 +186,19 @@ pub(crate) fn print_type_ref(
             };
             write!(buf, "&")?;
             if let Some(lt) = lt {
-                write!(buf, "{} ", lt.name.display(db.upcast()))?;
+                write!(buf, "{} ", lt.name.display(db.upcast(), edition))?;
             }
             write!(buf, "{mtbl}")?;
-            print_type_ref(db, pointee, buf)?;
+            print_type_ref(db, pointee, buf, edition)?;
         }
         TypeRef::Array(elem, len) => {
             write!(buf, "[")?;
-            print_type_ref(db, elem, buf)?;
-            write!(buf, "; {}]", len.display(db.upcast()))?;
+            print_type_ref(db, elem, buf, edition)?;
+            write!(buf, "; {}]", len.display(db.upcast(), edition))?;
         }
         TypeRef::Slice(elem) => {
             write!(buf, "[")?;
-            print_type_ref(db, elem, buf)?;
+            print_type_ref(db, elem, buf, edition)?;
             write!(buf, "]")?;
         }
         TypeRef::Fn(args_and_ret, varargs, is_unsafe, abi) => {
@@ -208,7 +217,7 @@ pub(crate) fn print_type_ref(
                 if i != 0 {
                     write!(buf, ", ")?;
                 }
-                print_type_ref(db, typeref, buf)?;
+                print_type_ref(db, typeref, buf, edition)?;
             }
             if *varargs {
                 if !args.is_empty() {
@@ -217,7 +226,7 @@ pub(crate) fn print_type_ref(
                 write!(buf, "...")?;
             }
             write!(buf, ") -> ")?;
-            print_type_ref(db, return_type, buf)?;
+            print_type_ref(db, return_type, buf, edition)?;
         }
         TypeRef::Macro(_ast_id) => {
             write!(buf, "<macro>")?;
@@ -225,11 +234,11 @@ pub(crate) fn print_type_ref(
         TypeRef::Error => write!(buf, "{{unknown}}")?,
         TypeRef::ImplTrait(bounds) => {
             write!(buf, "impl ")?;
-            print_type_bounds(db, bounds, buf)?;
+            print_type_bounds(db, bounds, buf, edition)?;
         }
         TypeRef::DynTrait(bounds) => {
             write!(buf, "dyn ")?;
-            print_type_bounds(db, bounds, buf)?;
+            print_type_bounds(db, bounds, buf, edition)?;
         }
     }
 
@@ -240,6 +249,7 @@ pub(crate) fn print_type_bounds(
     db: &dyn DefDatabase,
     bounds: &[Interned<TypeBound>],
     buf: &mut dyn Write,
+    edition: Edition,
 ) -> fmt::Result {
     for (i, bound) in bounds.iter().enumerate() {
         if i != 0 {
@@ -252,17 +262,17 @@ pub(crate) fn print_type_bounds(
                     TraitBoundModifier::None => (),
                     TraitBoundModifier::Maybe => write!(buf, "?")?,
                 }
-                print_path(db, path, buf)?;
+                print_path(db, path, buf, edition)?;
             }
             TypeBound::ForLifetime(lifetimes, path) => {
                 write!(
                     buf,
                     "for<{}> ",
-                    lifetimes.iter().map(|it| it.display(db.upcast())).format(", ")
+                    lifetimes.iter().map(|it| it.display(db.upcast(), edition)).format(", ")
                 )?;
-                print_path(db, path, buf)?;
+                print_path(db, path, buf, edition)?;
             }
-            TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast()))?,
+            TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast(), edition))?,
             TypeBound::Error => write!(buf, "{{unknown}}")?,
         }
     }
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs
index 3528b2dde73e7..1dadfe2ba99bb 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs
@@ -176,9 +176,10 @@ fn eager_macro_recur(
             Some(path) => match macro_resolver(&path) {
                 Some(def) => def,
                 None => {
+                    let edition = db.crate_graph()[krate].edition;
                     error = Some(ExpandError::other(
                         span_map.span_at(call.syntax().text_range().start()),
-                        format!("unresolved macro {}", path.display(db)),
+                        format!("unresolved macro {}", path.display(db, edition)),
                     ));
                     offset += call.syntax().text_range().len();
                     continue;
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs
index 20f484f672afb..d41f69812ee61 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs
@@ -461,3 +461,12 @@ impl<N: AstNode> InFile<N> {
         Some(InRealFile::new(file_id, value))
     }
 }
+
+impl<T> InFile<T> {
+    pub fn into_real_file(self) -> Result<InRealFile<T>, InFile<T>> {
+        match self.file_id.repr() {
+            HirFileIdRepr::FileId(file_id) => Ok(InRealFile { file_id, value: self.value }),
+            HirFileIdRepr::MacroFile(_) => Err(self),
+        }
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
index 2bea9026265a6..19c3c9c43f104 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
@@ -192,7 +192,7 @@ impl ExpandErrorKind {
                 ("overflow expanding the original macro".to_owned(), true)
             }
             ExpandErrorKind::Other(e) => ((**e).to_owned(), true),
-            ExpandErrorKind::ProcMacroPanic(e) => ((**e).to_owned(), true),
+            ExpandErrorKind::ProcMacroPanic(e) => (format!("proc-macro panicked: {e}"), true),
         }
     }
 }
@@ -279,6 +279,7 @@ pub enum MacroCallKind {
 }
 
 pub trait HirFileIdExt {
+    fn edition(self, db: &dyn ExpandDatabase) -> Edition;
     /// Returns the original file of this macro call hierarchy.
     fn original_file(self, db: &dyn ExpandDatabase) -> EditionedFileId;
 
@@ -293,6 +294,12 @@ pub trait HirFileIdExt {
 }
 
 impl HirFileIdExt for HirFileId {
+    fn edition(self, db: &dyn ExpandDatabase) -> Edition {
+        match self.repr() {
+            HirFileIdRepr::FileId(file_id) => file_id.edition(),
+            HirFileIdRepr::MacroFile(m) => m.macro_call_id.lookup(db).def.edition,
+        }
+    }
     fn original_file(self, db: &dyn ExpandDatabase) -> EditionedFileId {
         let mut file_id = self;
         loop {
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs
index 2c26fe414d91e..dcf2af3997970 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs
@@ -14,7 +14,7 @@ use crate::{
 use base_db::CrateId;
 use intern::sym;
 use smallvec::SmallVec;
-use span::SyntaxContextId;
+use span::{Edition, SyntaxContextId};
 use syntax::{ast, AstNode};
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -140,8 +140,12 @@ impl ModPath {
         UnescapedModPath(self)
     }
 
-    pub fn display<'a>(&'a self, db: &'a dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
-        Display { db, path: self }
+    pub fn display<'a>(
+        &'a self,
+        db: &'a dyn crate::db::ExpandDatabase,
+        edition: Edition,
+    ) -> impl fmt::Display + 'a {
+        Display { db, path: self, edition }
     }
 }
 
@@ -154,11 +158,12 @@ impl Extend<Name> for ModPath {
 struct Display<'a> {
     db: &'a dyn ExpandDatabase,
     path: &'a ModPath,
+    edition: Edition,
 }
 
 impl fmt::Display for Display<'_> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        display_fmt_path(self.db, self.path, f, true)
+        display_fmt_path(self.db, self.path, f, Escape::IfNeeded(self.edition))
     }
 }
 
@@ -169,7 +174,7 @@ struct UnescapedDisplay<'a> {
 
 impl fmt::Display for UnescapedDisplay<'_> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        display_fmt_path(self.db, self.path.0, f, false)
+        display_fmt_path(self.db, self.path.0, f, Escape::No)
     }
 }
 
@@ -178,11 +183,17 @@ impl From<Name> for ModPath {
         ModPath::from_segments(PathKind::Plain, iter::once(name))
     }
 }
+
+enum Escape {
+    No,
+    IfNeeded(Edition),
+}
+
 fn display_fmt_path(
     db: &dyn ExpandDatabase,
     path: &ModPath,
     f: &mut fmt::Formatter<'_>,
-    escaped: bool,
+    escaped: Escape,
 ) -> fmt::Result {
     let mut first_segment = true;
     let mut add_segment = |s| -> fmt::Result {
@@ -210,10 +221,9 @@ fn display_fmt_path(
             f.write_str("::")?;
         }
         first_segment = false;
-        if escaped {
-            segment.display(db).fmt(f)?;
-        } else {
-            segment.unescaped().display(db).fmt(f)?;
+        match escaped {
+            Escape::IfNeeded(edition) => segment.display(db, edition).fmt(f)?,
+            Escape::No => segment.unescaped().display(db).fmt(f)?,
         }
     }
     Ok(())
@@ -322,9 +332,11 @@ fn convert_path_tt(db: &dyn ExpandDatabase, tt: &[tt::TokenTree]) -> Option<ModP
         tt::Leaf::Ident(tt::Ident { sym: text, .. }) if *text == sym::self_ => PathKind::SELF,
         tt::Leaf::Ident(tt::Ident { sym: text, .. }) if *text == sym::super_ => {
             let mut deg = 1;
-            while let Some(tt::Leaf::Ident(tt::Ident { sym: text, span, is_raw })) = leaves.next() {
+            while let Some(tt::Leaf::Ident(tt::Ident { sym: text, span, is_raw: _ })) =
+                leaves.next()
+            {
                 if *text != sym::super_ {
-                    segments.push(Name::new_symbol_maybe_raw(text.clone(), *is_raw, span.ctx));
+                    segments.push(Name::new_symbol(text.clone(), span.ctx));
                     break;
                 }
                 deg += 1;
@@ -333,19 +345,13 @@ fn convert_path_tt(db: &dyn ExpandDatabase, tt: &[tt::TokenTree]) -> Option<ModP
         }
         tt::Leaf::Ident(tt::Ident { sym: text, .. }) if *text == sym::crate_ => PathKind::Crate,
         tt::Leaf::Ident(ident) => {
-            segments.push(Name::new_symbol_maybe_raw(
-                ident.sym.clone(),
-                ident.is_raw,
-                ident.span.ctx,
-            ));
+            segments.push(Name::new_symbol(ident.sym.clone(), ident.span.ctx));
             PathKind::Plain
         }
         _ => return None,
     };
     segments.extend(leaves.filter_map(|leaf| match leaf {
-        ::tt::Leaf::Ident(ident) => {
-            Some(Name::new_symbol_maybe_raw(ident.sym.clone(), ident.is_raw, ident.span.ctx))
-        }
+        ::tt::Leaf::Ident(ident) => Some(Name::new_symbol(ident.sym.clone(), ident.span.ctx)),
         _ => None,
     }));
     Some(ModPath { kind, segments })
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
index d012d272d743d..54313904a7ecd 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
@@ -3,22 +3,22 @@
 use std::fmt;
 
 use intern::{sym, Symbol};
-use span::SyntaxContextId;
-use syntax::{ast, utils::is_raw_identifier};
+use span::{Edition, SyntaxContextId};
+use syntax::ast;
+use syntax::utils::is_raw_identifier;
 
 /// `Name` is a wrapper around string, which is used in hir for both references
 /// and declarations. In theory, names should also carry hygiene info, but we are
 /// not there yet!
 ///
-/// Note that `Name` holds and prints escaped name i.e. prefixed with "r#" when it
-/// is a raw identifier. Use [`unescaped()`][Name::unescaped] when you need the
-/// name without "r#".
+/// Note that the rawness (`r#`) of names does not depend on whether they are written raw.
+/// This is because we want to show (in completions etc.) names as raw depending on the needs
+/// of the current crate, for example if it is edition 2021 complete `gen` even if the defining
+/// crate is in edition 2024 and wrote `r#gen`, and the opposite holds as well.
 #[derive(Clone, PartialEq, Eq, Hash)]
 pub struct Name {
     symbol: Symbol,
     ctx: (),
-    // FIXME: We should probably encode rawness as a property here instead, once we have hygiene
-    // in here we've got 4 bytes of padding to fill anyways
 }
 
 impl fmt::Debug for Name {
@@ -42,6 +42,7 @@ impl PartialOrd for Name {
     }
 }
 
+// No need to strip `r#`, all comparisons are done against well-known symbols.
 impl PartialEq<Symbol> for Name {
     fn eq(&self, sym: &Symbol) -> bool {
         self.symbol == *sym
@@ -55,16 +56,16 @@ impl PartialEq<Name> for Symbol {
 }
 
 /// Wrapper of `Name` to print the name without "r#" even when it is a raw identifier.
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct UnescapedName<'a>(&'a Name);
 
-impl UnescapedName<'_> {
-    pub fn display(&self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + '_ {
+impl<'a> UnescapedName<'a> {
+    pub fn display(self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
         _ = db;
         UnescapedDisplay { name: self }
     }
     #[doc(hidden)]
-    pub fn display_no_db(&self) -> impl fmt::Display + '_ {
+    pub fn display_no_db(self) -> impl fmt::Display + 'a {
         UnescapedDisplay { name: self }
     }
 }
@@ -77,16 +78,9 @@ impl Name {
         Name { symbol: Symbol::intern(text), ctx: () }
     }
 
-    pub fn new(text: &str, raw: tt::IdentIsRaw, ctx: SyntaxContextId) -> Name {
+    pub fn new(text: &str, ctx: SyntaxContextId) -> Name {
         _ = ctx;
-        Name {
-            symbol: if raw.yes() {
-                Symbol::intern(&format!("{}{text}", raw.as_str()))
-            } else {
-                Symbol::intern(text)
-            },
-            ctx: (),
-        }
+        Self::new_text(text)
     }
 
     pub fn new_tuple_field(idx: usize) -> Name {
@@ -97,23 +91,9 @@ impl Name {
         Name { symbol: Symbol::intern(lt.text().as_str()), ctx: () }
     }
 
-    /// Shortcut to create a name from a string literal.
-    fn new_ref(text: &str) -> Name {
-        Name { symbol: Symbol::intern(text), ctx: () }
-    }
-
     /// Resolve a name from the text of token.
     fn resolve(raw_text: &str) -> Name {
-        match raw_text.strip_prefix("r#") {
-            // When `raw_text` starts with "r#" but the name does not coincide with any
-            // keyword, we never need the prefix so we strip it.
-            Some(text) if !is_raw_identifier(text) => Name::new_ref(text),
-            // Keywords (in the current edition) *can* be used as a name in earlier editions of
-            // Rust, e.g. "try" in Rust 2015. Even in such cases, we keep track of them in their
-            // escaped form.
-            None if is_raw_identifier(raw_text) => Name::new_text(&format!("r#{}", raw_text)),
-            _ => Name::new_text(raw_text),
-        }
+        Name::new_text(raw_text.trim_start_matches("r#"))
     }
 
     /// A fake name for things missing in the source code.
@@ -159,19 +139,23 @@ impl Name {
         UnescapedName(self)
     }
 
-    pub fn is_escaped(&self) -> bool {
-        self.symbol.as_str().starts_with("r#")
+    pub fn is_escaped(&self, edition: Edition) -> bool {
+        is_raw_identifier(self.symbol.as_str(), edition)
     }
 
-    pub fn display<'a>(&'a self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
+    pub fn display<'a>(
+        &'a self,
+        db: &dyn crate::db::ExpandDatabase,
+        edition: Edition,
+    ) -> impl fmt::Display + 'a {
         _ = db;
-        Display { name: self }
+        self.display_no_db(edition)
     }
 
     // FIXME: Remove this
     #[doc(hidden)]
-    pub fn display_no_db(&self) -> impl fmt::Display + '_ {
-        Display { name: self }
+    pub fn display_no_db(&self, edition: Edition) -> impl fmt::Display + '_ {
+        Display { name: self, needs_escaping: is_raw_identifier(self.symbol.as_str(), edition) }
     }
 
     pub fn symbol(&self) -> &Symbol {
@@ -183,39 +167,39 @@ impl Name {
         Self { symbol, ctx: () }
     }
 
-    pub fn new_symbol_maybe_raw(sym: Symbol, raw: tt::IdentIsRaw, ctx: SyntaxContextId) -> Self {
-        if raw.no() {
-            Self { symbol: sym, ctx: () }
-        } else {
-            Name::new(sym.as_str(), raw, ctx)
-        }
-    }
-
     // FIXME: This needs to go once we have hygiene
     pub const fn new_symbol_root(sym: Symbol) -> Self {
         Self { symbol: sym, ctx: () }
     }
+
+    #[inline]
+    pub fn eq_ident(&self, ident: &str) -> bool {
+        self.as_str() == ident.trim_start_matches("r#")
+    }
 }
 
 struct Display<'a> {
     name: &'a Name,
+    needs_escaping: bool,
 }
 
 impl fmt::Display for Display<'_> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        if self.needs_escaping {
+            write!(f, "r#")?;
+        }
         fmt::Display::fmt(self.name.symbol.as_str(), f)
     }
 }
 
 struct UnescapedDisplay<'a> {
-    name: &'a UnescapedName<'a>,
+    name: UnescapedName<'a>,
 }
 
 impl fmt::Display for UnescapedDisplay<'_> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let symbol = &self.name.0.symbol.as_str();
-        let text = symbol.strip_prefix("r#").unwrap_or(symbol);
-        fmt::Display::fmt(&text, f)
+        let symbol = self.name.0.symbol.as_str();
+        fmt::Display::fmt(symbol, f)
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
index b079b5675bd9e..989f0955e1e7d 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
@@ -29,7 +29,6 @@ chalk-ir.workspace = true
 chalk-recursive.workspace = true
 chalk-derive.workspace = true
 la-arena.workspace = true
-once_cell = "1.17.0"
 triomphe.workspace = true
 nohash-hasher.workspace = true
 typed-arena = "2.0.1"
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs
index ecfc1ff99e9ea..7a3846df40eef 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs
@@ -3,6 +3,8 @@
 //! reference to a type with the field `bar`. This is an approximation of the
 //! logic in rustc (which lives in rustc_hir_analysis/check/autoderef.rs).
 
+use std::mem;
+
 use chalk_ir::cast::Cast;
 use hir_def::lang_item::LangItem;
 use hir_expand::name::Name;
@@ -37,7 +39,7 @@ pub fn autoderef(
 ) -> impl Iterator<Item = Ty> {
     let mut table = InferenceTable::new(db, env);
     let ty = table.instantiate_canonical(ty);
-    let mut autoderef = Autoderef::new(&mut table, ty, false);
+    let mut autoderef = Autoderef::new_no_tracking(&mut table, ty, false);
     let mut v = Vec::new();
     while let Some((ty, _steps)) = autoderef.next() {
         // `ty` may contain unresolved inference variables. Since there's no chance they would be
@@ -58,41 +60,76 @@ pub fn autoderef(
     v.into_iter()
 }
 
+trait TrackAutoderefSteps {
+    fn len(&self) -> usize;
+    fn push(&mut self, kind: AutoderefKind, ty: &Ty);
+}
+
+impl TrackAutoderefSteps for usize {
+    fn len(&self) -> usize {
+        *self
+    }
+    fn push(&mut self, _: AutoderefKind, _: &Ty) {
+        *self += 1;
+    }
+}
+impl TrackAutoderefSteps for Vec<(AutoderefKind, Ty)> {
+    fn len(&self) -> usize {
+        self.len()
+    }
+    fn push(&mut self, kind: AutoderefKind, ty: &Ty) {
+        self.push((kind, ty.clone()));
+    }
+}
+
 #[derive(Debug)]
-pub(crate) struct Autoderef<'a, 'db> {
-    pub(crate) table: &'a mut InferenceTable<'db>,
+pub(crate) struct Autoderef<'table, 'db, T = Vec<(AutoderefKind, Ty)>> {
+    pub(crate) table: &'table mut InferenceTable<'db>,
     ty: Ty,
     at_start: bool,
-    steps: Vec<(AutoderefKind, Ty)>,
+    steps: T,
     explicit: bool,
 }
 
-impl<'a, 'db> Autoderef<'a, 'db> {
-    pub(crate) fn new(table: &'a mut InferenceTable<'db>, ty: Ty, explicit: bool) -> Self {
+impl<'table, 'db> Autoderef<'table, 'db> {
+    pub(crate) fn new(table: &'table mut InferenceTable<'db>, ty: Ty, explicit: bool) -> Self {
         let ty = table.resolve_ty_shallow(&ty);
         Autoderef { table, ty, at_start: true, steps: Vec::new(), explicit }
     }
 
-    pub(crate) fn step_count(&self) -> usize {
-        self.steps.len()
-    }
-
     pub(crate) fn steps(&self) -> &[(AutoderefKind, Ty)] {
         &self.steps
     }
+}
+
+impl<'table, 'db> Autoderef<'table, 'db, usize> {
+    pub(crate) fn new_no_tracking(
+        table: &'table mut InferenceTable<'db>,
+        ty: Ty,
+        explicit: bool,
+    ) -> Self {
+        let ty = table.resolve_ty_shallow(&ty);
+        Autoderef { table, ty, at_start: true, steps: 0, explicit }
+    }
+}
+
+#[allow(private_bounds)]
+impl<'table, 'db, T: TrackAutoderefSteps> Autoderef<'table, 'db, T> {
+    pub(crate) fn step_count(&self) -> usize {
+        self.steps.len()
+    }
 
     pub(crate) fn final_ty(&self) -> Ty {
         self.ty.clone()
     }
 }
 
-impl Iterator for Autoderef<'_, '_> {
+impl<T: TrackAutoderefSteps> Iterator for Autoderef<'_, '_, T> {
     type Item = (Ty, usize);
 
     #[tracing::instrument(skip_all)]
     fn next(&mut self) -> Option<Self::Item> {
-        if self.at_start {
-            self.at_start = false;
+        if mem::take(&mut self.at_start) {
             return Some((self.ty.clone(), 0));
         }
 
@@ -102,7 +139,7 @@ impl Iterator for Autoderef<'_, '_> {
 
         let (kind, new_ty) = autoderef_step(self.table, self.ty.clone(), self.explicit)?;
 
-        self.steps.push((kind, self.ty.clone()));
+        self.steps.push(kind, &self.ty);
         self.ty = new_ty;
 
         Some((self.ty.clone(), self.step_count()))
@@ -129,12 +166,8 @@ pub(crate) fn builtin_deref<'ty>(
     match ty.kind(Interner) {
         TyKind::Ref(.., ty) => Some(ty),
         TyKind::Raw(.., ty) if explicit => Some(ty),
-        &TyKind::Adt(chalk_ir::AdtId(adt), ref substs) => {
-            if crate::lang_items::is_box(db, adt) {
-                substs.at(Interner, 0).ty(Interner)
-            } else {
-                None
-            }
+        &TyKind::Adt(chalk_ir::AdtId(adt), ref substs) if crate::lang_items::is_box(db, adt) => {
+            substs.at(Interner, 0).ty(Interner)
         }
         _ => None,
     }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
index a151ee01e6455..a3e4da5d1afd7 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
@@ -5,6 +5,7 @@ use std::{iter, ops::ControlFlow, sync::Arc};
 
 use hir_expand::name::Name;
 use intern::sym;
+use span::Edition;
 use tracing::debug;
 
 use chalk_ir::{cast::Caster, fold::shift::Shift, CanonicalVarKinds};
@@ -424,18 +425,19 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
 
     fn trait_name(&self, trait_id: chalk_ir::TraitId<Interner>) -> String {
         let id = from_chalk_trait_id(trait_id);
-        self.db.trait_data(id).name.display(self.db.upcast()).to_string()
+        self.db.trait_data(id).name.display(self.db.upcast(), self.edition()).to_string()
     }
     fn adt_name(&self, chalk_ir::AdtId(adt_id): AdtId) -> String {
+        let edition = self.edition();
         match adt_id {
             hir_def::AdtId::StructId(id) => {
-                self.db.struct_data(id).name.display(self.db.upcast()).to_string()
+                self.db.struct_data(id).name.display(self.db.upcast(), edition).to_string()
             }
             hir_def::AdtId::EnumId(id) => {
-                self.db.enum_data(id).name.display(self.db.upcast()).to_string()
+                self.db.enum_data(id).name.display(self.db.upcast(), edition).to_string()
             }
             hir_def::AdtId::UnionId(id) => {
-                self.db.union_data(id).name.display(self.db.upcast()).to_string()
+                self.db.union_data(id).name.display(self.db.upcast(), edition).to_string()
             }
         }
     }
@@ -445,7 +447,7 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
     }
     fn assoc_type_name(&self, assoc_ty_id: chalk_ir::AssocTypeId<Interner>) -> String {
         let id = self.db.associated_ty_data(assoc_ty_id).name;
-        self.db.type_alias_data(id).name.display(self.db.upcast()).to_string()
+        self.db.type_alias_data(id).name.display(self.db.upcast(), self.edition()).to_string()
     }
     fn opaque_type_name(&self, opaque_ty_id: chalk_ir::OpaqueTyId<Interner>) -> String {
         format!("Opaque_{}", opaque_ty_id.0)
@@ -519,6 +521,10 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
 }
 
 impl<'a> ChalkContext<'a> {
+    fn edition(&self) -> Edition {
+        self.db.crate_graph()[self.krate].edition
+    }
+
     fn for_trait_impls(
         &self,
         trait_id: hir_def::TraitId,
@@ -843,7 +849,7 @@ fn impl_def_datum(
         "impl {:?}: {}{} where {:?}",
         chalk_id,
         if negative { "!" } else { "" },
-        trait_ref.display(db),
+        trait_ref.display(db, db.crate_graph()[krate].edition),
         where_clauses
     );
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
index dc3817ce3f40f..86228250c2005 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
@@ -1,3 +1,4 @@
+use base_db::SourceDatabase;
 use chalk_ir::Substitution;
 use hir_def::db::DefDatabase;
 use rustc_apfloat::{
@@ -94,9 +95,10 @@ fn check_answer(ra_fixture: &str, check: impl FnOnce(&[u8], &MemoryMap)) {
 fn pretty_print_err(e: ConstEvalError, db: TestDB) -> String {
     let mut err = String::new();
     let span_formatter = |file, range| format!("{file:?} {range:?}");
+    let edition = db.crate_graph()[db.test_crate()].edition;
     match e {
-        ConstEvalError::MirLowerError(e) => e.pretty_print(&mut err, &db, span_formatter),
-        ConstEvalError::MirEvalError(e) => e.pretty_print(&mut err, &db, span_formatter),
+        ConstEvalError::MirLowerError(e) => e.pretty_print(&mut err, &db, span_formatter, edition),
+        ConstEvalError::MirEvalError(e) => e.pretty_print(&mut err, &db, span_formatter, edition),
     }
     .unwrap();
     err
@@ -110,7 +112,9 @@ fn eval_goal(db: &TestDB, file_id: EditionedFileId) -> Result<Const, ConstEvalEr
         .declarations()
         .find_map(|x| match x {
             hir_def::ModuleDefId::ConstId(x) => {
-                if db.const_data(x).name.as_ref()?.display(db).to_string() == "GOAL" {
+                if db.const_data(x).name.as_ref()?.display(db, file_id.edition()).to_string()
+                    == "GOAL"
+                {
                     Some(x)
                 } else {
                     None
@@ -243,6 +247,17 @@ fn casts() {
     check_number(r#"const GOAL: i32 = -12i8 as i32"#, -12);
 }
 
+#[test]
+fn floating_point_casts() {
+    check_number(r#"const GOAL: usize = 12i32 as f32 as usize"#, 12);
+    check_number(r#"const GOAL: i8 = -12i32 as f64 as i8"#, -12);
+    check_number(r#"const GOAL: i32 = (-1ui8 as f32 + 2u64 as f32) as i32"#, 1);
+    check_number(r#"const GOAL: i8 = (0./0.) as i8"#, 0);
+    check_number(r#"const GOAL: i8 = (1./0.) as i8"#, 127);
+    check_number(r#"const GOAL: i8 = (-1./0.) as i8"#, -128);
+    check_number(r#"const GOAL: i64 = 1e18f64 as f32 as i64"#, 999999984306749440);
+}
+
 #[test]
 fn raw_pointer_equality() {
     check_number(
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
index b093440060806..024fc32f86370 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
@@ -17,17 +17,18 @@ use std::fmt;
 
 use hir_def::{
     data::adt::VariantData, db::DefDatabase, hir::Pat, src::HasSource, AdtId, AttrDefId, ConstId,
-    EnumId, EnumVariantId, FunctionId, ItemContainerId, Lookup, ModuleDefId, ModuleId, StaticId,
-    StructId, TraitId, TypeAliasId,
+    EnumId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, ModuleDefId, ModuleId,
+    StaticId, StructId, TraitId, TypeAliasId,
 };
 use hir_expand::{
     name::{AsName, Name},
-    HirFileId, MacroFileIdExt,
+    HirFileId, HirFileIdExt, MacroFileIdExt,
 };
 use intern::sym;
 use stdx::{always, never};
 use syntax::{
     ast::{self, HasName},
+    utils::is_raw_identifier,
     AstNode, AstPtr, ToSmolStr,
 };
 
@@ -318,17 +319,21 @@ impl<'a> DeclValidator<'a> {
     /// This includes function parameters except for trait implementation associated functions.
     fn validate_func_body(&mut self, func: FunctionId) {
         let body = self.db.body(func.into());
+        let edition = self.edition(func);
         let mut pats_replacements = body
             .pats
             .iter()
             .filter_map(|(pat_id, pat)| match pat {
                 Pat::Bind { id, .. } => {
                     let bind_name = &body.bindings[*id].name;
+                    let mut suggested_text =
+                        to_lower_snake_case(&bind_name.unescaped().display_no_db().to_smolstr())?;
+                    if is_raw_identifier(&suggested_text, edition) {
+                        suggested_text.insert_str(0, "r#");
+                    }
                     let replacement = Replacement {
                         current_name: bind_name.clone(),
-                        suggested_text: to_lower_snake_case(
-                            &bind_name.display_no_db().to_smolstr(),
-                        )?,
+                        suggested_text,
                         expected_case: CaseType::LowerSnakeCase,
                     };
                     Some((pat_id, replacement))
@@ -377,6 +382,11 @@ impl<'a> DeclValidator<'a> {
         }
     }
 
+    fn edition(&self, id: impl HasModule) -> span::Edition {
+        let krate = id.krate(self.db.upcast());
+        self.db.crate_graph()[krate].edition
+    }
+
     fn validate_struct(&mut self, struct_id: StructId) {
         // Check the structure name.
         let non_camel_case_allowed =
@@ -405,16 +415,17 @@ impl<'a> DeclValidator<'a> {
         let VariantData::Record(fields) = data.variant_data.as_ref() else {
             return;
         };
+        let edition = self.edition(struct_id);
         let mut struct_fields_replacements = fields
             .iter()
             .filter_map(|(_, field)| {
-                to_lower_snake_case(&field.name.display_no_db().to_smolstr()).map(|new_name| {
-                    Replacement {
+                to_lower_snake_case(&field.name.display_no_db(edition).to_smolstr()).map(
+                    |new_name| Replacement {
                         current_name: field.name.clone(),
                         suggested_text: new_name,
                         expected_case: CaseType::LowerSnakeCase,
-                    }
-                })
+                    },
+                )
             })
             .peekable();
 
@@ -498,14 +509,17 @@ impl<'a> DeclValidator<'a> {
             self.validate_enum_variant_fields(*variant_id);
         }
 
+        let edition = self.edition(enum_id);
         let mut enum_variants_replacements = data
             .variants
             .iter()
             .filter_map(|(_, name)| {
-                to_camel_case(&name.display_no_db().to_smolstr()).map(|new_name| Replacement {
-                    current_name: name.clone(),
-                    suggested_text: new_name,
-                    expected_case: CaseType::UpperCamelCase,
+                to_camel_case(&name.display_no_db(edition).to_smolstr()).map(|new_name| {
+                    Replacement {
+                        current_name: name.clone(),
+                        suggested_text: new_name,
+                        expected_case: CaseType::UpperCamelCase,
+                    }
                 })
             })
             .peekable();
@@ -566,16 +580,17 @@ impl<'a> DeclValidator<'a> {
         let VariantData::Record(fields) = variant_data.variant_data.as_ref() else {
             return;
         };
+        let edition = self.edition(variant_id);
         let mut variant_field_replacements = fields
             .iter()
             .filter_map(|(_, field)| {
-                to_lower_snake_case(&field.name.display_no_db().to_smolstr()).map(|new_name| {
-                    Replacement {
+                to_lower_snake_case(&field.name.display_no_db(edition).to_smolstr()).map(
+                    |new_name| Replacement {
                         current_name: field.name.clone(),
                         suggested_text: new_name,
                         expected_case: CaseType::LowerSnakeCase,
-                    }
-                })
+                    },
+                )
             })
             .peekable();
 
@@ -704,18 +719,22 @@ impl<'a> DeclValidator<'a> {
     ) where
         N: AstNode + HasName + fmt::Debug,
         S: HasSource<Value = N>,
-        L: Lookup<Data = S, Database<'a> = dyn DefDatabase + 'a>,
+        L: Lookup<Data = S, Database<'a> = dyn DefDatabase + 'a> + HasModule + Copy,
     {
         let to_expected_case_type = match expected_case {
             CaseType::LowerSnakeCase => to_lower_snake_case,
             CaseType::UpperSnakeCase => to_upper_snake_case,
             CaseType::UpperCamelCase => to_camel_case,
         };
-        let Some(replacement) =
-            to_expected_case_type(&name.display(self.db.upcast()).to_smolstr()).map(|new_name| {
-                Replacement { current_name: name.clone(), suggested_text: new_name, expected_case }
-            })
-        else {
+        let edition = self.edition(item_id);
+        let Some(replacement) = to_expected_case_type(
+            &name.display(self.db.upcast(), edition).to_smolstr(),
+        )
+        .map(|new_name| Replacement {
+            current_name: name.clone(),
+            suggested_text: new_name,
+            expected_case,
+        }) else {
             return;
         };
 
@@ -748,12 +767,13 @@ impl<'a> DeclValidator<'a> {
             return;
         };
 
+        let edition = file_id.original_file(self.db.upcast()).edition();
         let diagnostic = IncorrectCase {
             file: file_id,
             ident_type,
             ident: AstPtr::new(&name_ast),
             expected_case: replacement.expected_case,
-            ident_text: replacement.current_name.display(self.db.upcast()).to_string(),
+            ident_text: replacement.current_name.display(self.db.upcast(), edition).to_string(),
             suggested_text: replacement.suggested_text,
         };
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
index 6e5a7cce9c976..f8b5c7d0ce2c8 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
@@ -4,6 +4,7 @@
 
 use std::fmt;
 
+use base_db::CrateId;
 use chalk_solve::rust_ir::AdtKind;
 use either::Either;
 use hir_def::{
@@ -15,6 +16,7 @@ use intern::sym;
 use itertools::Itertools;
 use rustc_hash::FxHashSet;
 use rustc_pattern_analysis::constructor::Constructor;
+use span::Edition;
 use syntax::{
     ast::{self, UnaryOp},
     AstNode,
@@ -258,7 +260,13 @@ impl ExprValidator {
         if !witnesses.is_empty() {
             self.diagnostics.push(BodyValidationDiagnostic::MissingMatchArms {
                 match_expr,
-                uncovered_patterns: missing_match_arms(&cx, scrut_ty, witnesses, m_arms.is_empty()),
+                uncovered_patterns: missing_match_arms(
+                    &cx,
+                    scrut_ty,
+                    witnesses,
+                    m_arms.is_empty(),
+                    self.owner.krate(db.upcast()),
+                ),
             });
         }
     }
@@ -345,7 +353,13 @@ impl ExprValidator {
             if !witnesses.is_empty() {
                 self.diagnostics.push(BodyValidationDiagnostic::NonExhaustiveLet {
                     pat,
-                    uncovered_patterns: missing_match_arms(&cx, ty, witnesses, false),
+                    uncovered_patterns: missing_match_arms(
+                        &cx,
+                        ty,
+                        witnesses,
+                        false,
+                        self.owner.krate(db.upcast()),
+                    ),
                 });
             }
         }
@@ -616,24 +630,26 @@ fn missing_match_arms<'p>(
     scrut_ty: &Ty,
     witnesses: Vec<WitnessPat<'p>>,
     arms_is_empty: bool,
+    krate: CrateId,
 ) -> String {
-    struct DisplayWitness<'a, 'p>(&'a WitnessPat<'p>, &'a MatchCheckCtx<'p>);
+    struct DisplayWitness<'a, 'p>(&'a WitnessPat<'p>, &'a MatchCheckCtx<'p>, Edition);
     impl fmt::Display for DisplayWitness<'_, '_> {
         fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-            let DisplayWitness(witness, cx) = *self;
+            let DisplayWitness(witness, cx, edition) = *self;
             let pat = cx.hoist_witness_pat(witness);
-            write!(f, "{}", pat.display(cx.db))
+            write!(f, "{}", pat.display(cx.db, edition))
         }
     }
 
+    let edition = cx.db.crate_graph()[krate].edition;
     let non_empty_enum = match scrut_ty.as_adt() {
         Some((AdtId::EnumId(e), _)) => !cx.db.enum_data(e).variants.is_empty(),
         _ => false,
     };
     if arms_is_empty && !non_empty_enum {
-        format!("type `{}` is non-empty", scrut_ty.display(cx.db))
+        format!("type `{}` is non-empty", scrut_ty.display(cx.db, edition))
     } else {
-        let pat_display = |witness| DisplayWitness(witness, cx);
+        let pat_display = |witness| DisplayWitness(witness, cx, edition);
         const LIMIT: usize = 3;
         match &*witnesses {
             [witness] => format!("`{}` not covered", pat_display(witness)),
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs
index a0ee7c0748b19..4bc07bc9ec8fe 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs
@@ -14,6 +14,7 @@ use hir_def::{
     body::Body, data::adt::VariantData, hir::PatId, AdtId, EnumVariantId, LocalFieldId, VariantId,
 };
 use hir_expand::name::Name;
+use span::Edition;
 use stdx::{always, never};
 
 use crate::{
@@ -151,7 +152,11 @@ impl<'a> PatCtxt<'a> {
                 match (bm, ty.kind(Interner)) {
                     (BindingMode::Ref(_), TyKind::Ref(.., rty)) => ty = rty,
                     (BindingMode::Ref(_), _) => {
-                        never!("`ref {}` has wrong type {:?}", name.display(self.db.upcast()), ty);
+                        never!(
+                            "`ref {}` has wrong type {:?}",
+                            name.display(self.db.upcast(), Edition::LATEST),
+                            ty
+                        );
                         self.errors.push(PatternError::UnexpectedType);
                         return Pat { ty: ty.clone(), kind: PatKind::Wild.into() };
                     }
@@ -297,7 +302,7 @@ impl HirDisplay for Pat {
             PatKind::Wild => write!(f, "_"),
             PatKind::Never => write!(f, "!"),
             PatKind::Binding { name, subpattern } => {
-                write!(f, "{}", name.display(f.db.upcast()))?;
+                write!(f, "{}", name.display(f.db.upcast(), f.edition()))?;
                 if let Some(subpattern) = subpattern {
                     write!(f, " @ ")?;
                     subpattern.hir_fmt(f)?;
@@ -317,14 +322,22 @@ impl HirDisplay for Pat {
                 if let Some(variant) = variant {
                     match variant {
                         VariantId::EnumVariantId(v) => {
-                            write!(f, "{}", f.db.enum_variant_data(v).name.display(f.db.upcast()))?;
-                        }
-                        VariantId::StructId(s) => {
-                            write!(f, "{}", f.db.struct_data(s).name.display(f.db.upcast()))?
-                        }
-                        VariantId::UnionId(u) => {
-                            write!(f, "{}", f.db.union_data(u).name.display(f.db.upcast()))?
+                            write!(
+                                f,
+                                "{}",
+                                f.db.enum_variant_data(v).name.display(f.db.upcast(), f.edition())
+                            )?;
                         }
+                        VariantId::StructId(s) => write!(
+                            f,
+                            "{}",
+                            f.db.struct_data(s).name.display(f.db.upcast(), f.edition())
+                        )?,
+                        VariantId::UnionId(u) => write!(
+                            f,
+                            "{}",
+                            f.db.union_data(u).name.display(f.db.upcast(), f.edition())
+                        )?,
                     };
 
                     let variant_data = variant.variant_data(f.db.upcast());
@@ -341,7 +354,9 @@ impl HirDisplay for Pat {
                                     write!(
                                         f,
                                         "{}: ",
-                                        rec_fields[p.field].name.display(f.db.upcast())
+                                        rec_fields[p.field]
+                                            .name
+                                            .display(f.db.upcast(), f.edition())
                                     )?;
                                     p.pattern.hir_fmt(f)
                                 })
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
index 7b3abf501d293..1066a28c3ff88 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
@@ -1,10 +1,10 @@
 //! Interface with `rustc_pattern_analysis`.
 
+use std::cell::LazyCell;
 use std::fmt;
 
 use hir_def::{DefWithBodyId, EnumId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId};
 use intern::sym;
-use once_cell::unsync::Lazy;
 use rustc_pattern_analysis::{
     constructor::{Constructor, ConstructorSet, VariantVisibility},
     usefulness::{compute_match_usefulness, PlaceValidity, UsefulnessReport},
@@ -384,8 +384,9 @@ impl<'db> PatCx for MatchCheckCtx<'db> {
                         let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap();
 
                         // Whether we must not match the fields of this variant exhaustively.
-                        let is_non_exhaustive = Lazy::new(|| self.is_foreign_non_exhaustive(adt));
-                        let visibilities = Lazy::new(|| self.db.field_visibilities(variant));
+                        let is_non_exhaustive =
+                            LazyCell::new(|| self.is_foreign_non_exhaustive(adt));
+                        let visibilities = LazyCell::new(|| self.db.field_visibilities(variant));
 
                         self.list_variant_fields(ty, variant)
                             .map(move |(fid, ty)| {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
index f406666ae5a76..70c03477c4c02 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
@@ -33,7 +33,8 @@ use rustc_apfloat::{
     Float,
 };
 use smallvec::SmallVec;
-use stdx::{never, IsNoneOr};
+use span::Edition;
+use stdx::never;
 use triomphe::Arc;
 
 use crate::{
@@ -131,7 +132,11 @@ pub trait HirDisplay {
 
     /// Returns a `Display`able type that is human-readable.
     /// Use this for showing types to the user (e.g. diagnostics)
-    fn display<'a>(&'a self, db: &'a dyn HirDatabase) -> HirDisplayWrapper<'a, Self>
+    fn display<'a>(
+        &'a self,
+        db: &'a dyn HirDatabase,
+        edition: Edition,
+    ) -> HirDisplayWrapper<'a, Self>
     where
         Self: Sized,
     {
@@ -142,7 +147,7 @@ pub trait HirDisplay {
             limited_size: None,
             omit_verbose_types: false,
             closure_style: ClosureStyle::ImplFn,
-            display_target: DisplayTarget::Diagnostics,
+            display_target: DisplayTarget::Diagnostics { edition },
             show_container_bounds: false,
         }
     }
@@ -153,6 +158,7 @@ pub trait HirDisplay {
         &'a self,
         db: &'a dyn HirDatabase,
         max_size: Option<usize>,
+        edition: Edition,
     ) -> HirDisplayWrapper<'a, Self>
     where
         Self: Sized,
@@ -164,7 +170,7 @@ pub trait HirDisplay {
             limited_size: None,
             omit_verbose_types: true,
             closure_style: ClosureStyle::ImplFn,
-            display_target: DisplayTarget::Diagnostics,
+            display_target: DisplayTarget::Diagnostics { edition },
             show_container_bounds: false,
         }
     }
@@ -175,6 +181,7 @@ pub trait HirDisplay {
         &'a self,
         db: &'a dyn HirDatabase,
         limited_size: Option<usize>,
+        edition: Edition,
     ) -> HirDisplayWrapper<'a, Self>
     where
         Self: Sized,
@@ -186,7 +193,7 @@ pub trait HirDisplay {
             limited_size,
             omit_verbose_types: true,
             closure_style: ClosureStyle::ImplFn,
-            display_target: DisplayTarget::Diagnostics,
+            display_target: DisplayTarget::Diagnostics { edition },
             show_container_bounds: false,
         }
     }
@@ -242,6 +249,7 @@ pub trait HirDisplay {
         &'a self,
         db: &'a dyn HirDatabase,
         show_container_bounds: bool,
+        edition: Edition,
     ) -> HirDisplayWrapper<'a, Self>
     where
         Self: Sized,
@@ -253,13 +261,23 @@ pub trait HirDisplay {
             limited_size: None,
             omit_verbose_types: false,
             closure_style: ClosureStyle::ImplFn,
-            display_target: DisplayTarget::Diagnostics,
+            display_target: DisplayTarget::Diagnostics { edition },
             show_container_bounds,
         }
     }
 }
 
 impl HirFormatter<'_> {
+    pub fn edition(&self) -> Edition {
+        match self.display_target {
+            DisplayTarget::Diagnostics { edition } => edition,
+            DisplayTarget::SourceCode { module_id, .. } => {
+                self.db.crate_graph()[module_id.krate()].edition
+            }
+            DisplayTarget::Test => Edition::CURRENT,
+        }
+    }
+
     pub fn write_joined<T: HirDisplay>(
         &mut self,
         iter: impl IntoIterator<Item = T>,
@@ -324,7 +342,7 @@ pub enum DisplayTarget {
     /// Display types for inlays, doc popups, autocompletion, etc...
     /// Showing `{unknown}` or not qualifying paths is fine here.
     /// There's no reason for this to fail.
-    Diagnostics,
+    Diagnostics { edition: Edition },
     /// Display types for inserting them in source files.
     /// The generated code should compile, so paths need to be qualified.
     SourceCode { module_id: ModuleId, allow_opaque: bool },
@@ -460,7 +478,7 @@ impl HirDisplay for ProjectionTy {
             ">::{}",
             f.db.type_alias_data(from_assoc_type_id(self.associated_ty_id))
                 .name
-                .display(f.db.upcast())
+                .display(f.db.upcast(), f.edition())
         )?;
         let proj_params_count =
             self.substitution.len(Interner) - trait_ref.substitution.len(Interner);
@@ -499,7 +517,7 @@ impl HirDisplay for Const {
                 let id = from_placeholder_idx(f.db, *idx);
                 let generics = generics(f.db.upcast(), id.parent);
                 let param_data = &generics[id.local_id];
-                write!(f, "{}", param_data.name().unwrap().display(f.db.upcast()))?;
+                write!(f, "{}", param_data.name().unwrap().display(f.db.upcast(), f.edition()))?;
                 Ok(())
             }
             ConstValue::Concrete(c) => match &c.interned {
@@ -633,7 +651,7 @@ fn render_const_scalar(
             TyKind::Adt(adt, _) if b.len() == 2 * size_of::<usize>() => match adt.0 {
                 hir_def::AdtId::StructId(s) => {
                     let data = f.db.struct_data(s);
-                    write!(f, "&{}", data.name.display(f.db.upcast()))?;
+                    write!(f, "&{}", data.name.display(f.db.upcast(), f.edition()))?;
                     Ok(())
                 }
                 _ => f.write_str("<unsized-enum-or-union>"),
@@ -691,7 +709,7 @@ fn render_const_scalar(
             match adt.0 {
                 hir_def::AdtId::StructId(s) => {
                     let data = f.db.struct_data(s);
-                    write!(f, "{}", data.name.display(f.db.upcast()))?;
+                    write!(f, "{}", data.name.display(f.db.upcast(), f.edition()))?;
                     let field_types = f.db.field_types(s.into());
                     render_variant_after_name(
                         &data.variant_data,
@@ -705,7 +723,7 @@ fn render_const_scalar(
                     )
                 }
                 hir_def::AdtId::UnionId(u) => {
-                    write!(f, "{}", f.db.union_data(u).name.display(f.db.upcast()))
+                    write!(f, "{}", f.db.union_data(u).name.display(f.db.upcast(), f.edition()))
                 }
                 hir_def::AdtId::EnumId(e) => {
                     let Ok(target_data_layout) = f.db.target_data_layout(trait_env.krate) else {
@@ -717,7 +735,7 @@ fn render_const_scalar(
                         return f.write_str("<failed-to-detect-variant>");
                     };
                     let data = f.db.enum_variant_data(var_id);
-                    write!(f, "{}", data.name.display(f.db.upcast()))?;
+                    write!(f, "{}", data.name.display(f.db.upcast(), f.edition()))?;
                     let field_types = f.db.field_types(var_id.into());
                     render_variant_after_name(
                         &data.variant_data,
@@ -802,11 +820,11 @@ fn render_variant_after_name(
             if matches!(data, VariantData::Record(_)) {
                 write!(f, " {{")?;
                 if let Some((id, data)) = it.next() {
-                    write!(f, " {}: ", data.name.display(f.db.upcast()))?;
+                    write!(f, " {}: ", data.name.display(f.db.upcast(), f.edition()))?;
                     render_field(f, id)?;
                 }
                 for (id, data) in it {
-                    write!(f, ", {}: ", data.name.display(f.db.upcast()))?;
+                    write!(f, ", {}: ", data.name.display(f.db.upcast(), f.edition()))?;
                     render_field(f, id)?;
                 }
                 write!(f, " }}")?;
@@ -1000,15 +1018,23 @@ impl HirDisplay for Ty {
                     CallableDefId::FunctionId(ff) => {
                         write!(f, "fn ")?;
                         f.start_location_link(def.into());
-                        write!(f, "{}", db.function_data(ff).name.display(f.db.upcast()))?
+                        write!(
+                            f,
+                            "{}",
+                            db.function_data(ff).name.display(f.db.upcast(), f.edition())
+                        )?
                     }
                     CallableDefId::StructId(s) => {
                         f.start_location_link(def.into());
-                        write!(f, "{}", db.struct_data(s).name.display(f.db.upcast()))?
+                        write!(f, "{}", db.struct_data(s).name.display(f.db.upcast(), f.edition()))?
                     }
                     CallableDefId::EnumVariantId(e) => {
                         f.start_location_link(def.into());
-                        write!(f, "{}", db.enum_variant_data(e).name.display(f.db.upcast()))?
+                        write!(
+                            f,
+                            "{}",
+                            db.enum_variant_data(e).name.display(f.db.upcast(), f.edition())
+                        )?
                     }
                 };
                 f.end_location_link();
@@ -1019,26 +1045,25 @@ impl HirDisplay for Ty {
                     let (parent_len, self_param, type_, const_, impl_, lifetime) =
                         generics.provenance_split();
                     let parameters = parameters.as_slice(Interner);
+                    debug_assert_eq!(
+                        parameters.len(),
+                        parent_len + self_param as usize + type_ + const_ + impl_ + lifetime
+                    );
                     // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self?
                     if parameters.len() - impl_ > 0 {
                         // `parameters` are in the order of fn's params (including impl traits), fn's lifetimes
+                        let parameters =
+                            generic_args_sans_defaults(f, Some(generic_def_id), parameters);
                         let without_impl = self_param as usize + type_ + const_ + lifetime;
                         // parent's params (those from enclosing impl or trait, if any).
                         let (fn_params, parent_params) = parameters.split_at(without_impl + impl_);
-                        debug_assert_eq!(parent_params.len(), parent_len);
-
-                        let parent_params =
-                            generic_args_sans_defaults(f, Some(generic_def_id), parent_params);
-                        let fn_params =
-                            &generic_args_sans_defaults(f, Some(generic_def_id), fn_params)
-                                [0..without_impl];
 
                         write!(f, "<")?;
                         hir_fmt_generic_arguments(f, parent_params, None)?;
                         if !parent_params.is_empty() && !fn_params.is_empty() {
                             write!(f, ", ")?;
                         }
-                        hir_fmt_generic_arguments(f, fn_params, None)?;
+                        hir_fmt_generic_arguments(f, &fn_params[0..without_impl], None)?;
                         write!(f, ">")?;
                     }
                 }
@@ -1054,13 +1079,13 @@ impl HirDisplay for Ty {
             TyKind::Adt(AdtId(def_id), parameters) => {
                 f.start_location_link((*def_id).into());
                 match f.display_target {
-                    DisplayTarget::Diagnostics | DisplayTarget::Test => {
+                    DisplayTarget::Diagnostics { .. } | DisplayTarget::Test => {
                         let name = match *def_id {
                             hir_def::AdtId::StructId(it) => db.struct_data(it).name.clone(),
                             hir_def::AdtId::UnionId(it) => db.union_data(it).name.clone(),
                             hir_def::AdtId::EnumId(it) => db.enum_data(it).name.clone(),
                         };
-                        write!(f, "{}", name.display(f.db.upcast()))?;
+                        write!(f, "{}", name.display(f.db.upcast(), f.edition()))?;
                     }
                     DisplayTarget::SourceCode { module_id, allow_opaque: _ } => {
                         if let Some(path) = find_path::find_path(
@@ -1076,7 +1101,7 @@ impl HirDisplay for Ty {
                                 prefer_absolute: false,
                             },
                         ) {
-                            write!(f, "{}", path.display(f.db.upcast()))?;
+                            write!(f, "{}", path.display(f.db.upcast(), f.edition()))?;
                         } else {
                             return Err(HirDisplayError::DisplaySourceCodeError(
                                 DisplaySourceCodeError::PathNotFound,
@@ -1102,12 +1127,12 @@ impl HirDisplay for Ty {
                 // Use placeholder associated types when the target is test (https://rust-lang.github.io/chalk/book/clauses/type_equality.html#placeholder-associated-types)
                 if f.display_target.is_test() {
                     f.start_location_link(trait_.into());
-                    write!(f, "{}", trait_data.name.display(f.db.upcast()))?;
+                    write!(f, "{}", trait_data.name.display(f.db.upcast(), f.edition()))?;
                     f.end_location_link();
                     write!(f, "::")?;
 
                     f.start_location_link(type_alias.into());
-                    write!(f, "{}", type_alias_data.name.display(f.db.upcast()))?;
+                    write!(f, "{}", type_alias_data.name.display(f.db.upcast(), f.edition()))?;
                     f.end_location_link();
                     // Note that the generic args for the associated type come before those for the
                     // trait (including the self type).
@@ -1125,7 +1150,7 @@ impl HirDisplay for Ty {
                 let alias = from_foreign_def_id(*type_alias);
                 let type_alias = db.type_alias_data(alias);
                 f.start_location_link(alias.into());
-                write!(f, "{}", type_alias.name.display(f.db.upcast()))?;
+                write!(f, "{}", type_alias.name.display(f.db.upcast(), f.edition()))?;
                 f.end_location_link();
             }
             TyKind::OpaqueType(opaque_ty_id, parameters) => {
@@ -1257,7 +1282,10 @@ impl HirDisplay for Ty {
                             write!(
                                 f,
                                 "{}",
-                                p.name.clone().unwrap_or_else(Name::missing).display(f.db.upcast())
+                                p.name
+                                    .clone()
+                                    .unwrap_or_else(Name::missing)
+                                    .display(f.db.upcast(), f.edition())
                             )?
                         }
                         TypeParamProvenance::ArgumentImplTrait => {
@@ -1290,7 +1318,7 @@ impl HirDisplay for Ty {
                         }
                     },
                     TypeOrConstParamData::ConstParamData(p) => {
-                        write!(f, "{}", p.name.display(f.db.upcast()))?;
+                        write!(f, "{}", p.name.display(f.db.upcast(), f.edition()))?;
                     }
                 }
             }
@@ -1410,17 +1438,7 @@ fn hir_fmt_generics(
 
     let parameters_to_write = generic_args_sans_defaults(f, generic_def, parameters);
 
-    // FIXME: Remote this
-    // most of our lifetimes will be errors as we lack elision and inference
-    // so don't render them for now
-    let only_err_lifetimes = !cfg!(test)
-        && parameters_to_write.iter().all(|arg| {
-            matches!(
-                arg.data(Interner),
-                chalk_ir::GenericArgData::Lifetime(it) if *it.data(Interner) == LifetimeData::Error
-            )
-        });
-    if !parameters_to_write.is_empty() && !only_err_lifetimes {
+    if !parameters_to_write.is_empty() {
         write!(f, "<")?;
         hir_fmt_generic_arguments(f, parameters_to_write, self_)?;
         write!(f, ">")?;
@@ -1461,12 +1479,14 @@ fn generic_args_sans_defaults<'ga>(
                     }
                     // otherwise, if the arg is equal to the param default, hide it (unless the
                     // default is an error which can happen for the trait Self type)
-                    #[allow(unstable_name_collisions)]
-                    IsNoneOr::is_none_or(default_parameters.get(i), |default_parameter| {
-                        // !is_err(default_parameter.skip_binders())
-                        //     &&
-                        arg != &default_parameter.clone().substitute(Interner, &parameters)
-                    })
+                    match default_parameters.get(i) {
+                        None => true,
+                        Some(default_parameter) => {
+                            // !is_err(default_parameter.skip_binders())
+                            // &&
+                            arg != &default_parameter.clone().substitute(Interner, &parameters)
+                        }
+                    }
                 };
                 let mut default_from = 0;
                 for (i, parameter) in parameters.iter().enumerate() {
@@ -1495,18 +1515,6 @@ fn hir_fmt_generic_arguments(
         None => (parameters, &[][..]),
     };
     for generic_arg in lifetimes.iter().chain(ty_or_const) {
-        // FIXME: Remove this
-        // most of our lifetimes will be errors as we lack elision and inference
-        // so don't render them for now
-        if !cfg!(test)
-            && matches!(
-                generic_arg.lifetime(Interner),
-                Some(l) if ***l.interned() == LifetimeData::Error
-            )
-        {
-            continue;
-        }
-
         if !mem::take(&mut first) {
             write!(f, ", ")?;
         }
@@ -1633,7 +1641,7 @@ fn write_bounds_like_dyn_trait(
                 // existential) here, which is the only thing that's
                 // possible in actual Rust, and hence don't print it
                 f.start_location_link(trait_.into());
-                write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast()))?;
+                write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast(), f.edition()))?;
                 f.end_location_link();
                 if is_fn_trait {
                     if let [self_, params @ ..] = trait_ref.substitution.as_slice(Interner) {
@@ -1707,7 +1715,7 @@ fn write_bounds_like_dyn_trait(
                     let assoc_ty_id = from_assoc_type_id(proj.associated_ty_id);
                     let type_alias = f.db.type_alias_data(assoc_ty_id);
                     f.start_location_link(assoc_ty_id.into());
-                    write!(f, "{}", type_alias.name.display(f.db.upcast()))?;
+                    write!(f, "{}", type_alias.name.display(f.db.upcast(), f.edition()))?;
                     f.end_location_link();
 
                     let proj_arg_count = generics(f.db.upcast(), assoc_ty_id.into()).len_self();
@@ -1771,7 +1779,7 @@ fn fmt_trait_ref(
     }
     let trait_ = tr.hir_trait_id();
     f.start_location_link(trait_.into());
-    write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast()))?;
+    write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast(), f.edition()))?;
     f.end_location_link();
     let substs = tr.substitution.as_slice(Interner);
     hir_fmt_generics(f, &substs[1..], None, substs[0].ty(Interner))
@@ -1797,7 +1805,11 @@ impl HirDisplay for WhereClause {
                 write!(f, ">::",)?;
                 let type_alias = from_assoc_type_id(projection_ty.associated_ty_id);
                 f.start_location_link(type_alias.into());
-                write!(f, "{}", f.db.type_alias_data(type_alias).name.display(f.db.upcast()),)?;
+                write!(
+                    f,
+                    "{}",
+                    f.db.type_alias_data(type_alias).name.display(f.db.upcast(), f.edition()),
+                )?;
                 f.end_location_link();
                 write!(f, " = ")?;
                 ty.hir_fmt(f)?;
@@ -1833,14 +1845,20 @@ impl HirDisplay for LifetimeData {
                 let id = lt_from_placeholder_idx(f.db, *idx);
                 let generics = generics(f.db.upcast(), id.parent);
                 let param_data = &generics[id.local_id];
-                write!(f, "{}", param_data.name.display(f.db.upcast()))?;
+                write!(f, "{}", param_data.name.display(f.db.upcast(), f.edition()))?;
                 Ok(())
             }
             _ if f.display_target.is_source_code() => write!(f, "'_"),
             LifetimeData::BoundVar(idx) => idx.hir_fmt(f),
             LifetimeData::InferenceVar(_) => write!(f, "_"),
             LifetimeData::Static => write!(f, "'static"),
-            LifetimeData::Error => write!(f, "'?"),
+            LifetimeData::Error => {
+                if cfg!(test) {
+                    write!(f, "'?")
+                } else {
+                    write!(f, "'_")
+                }
+            }
             LifetimeData::Erased => write!(f, "'<erased>"),
             LifetimeData::Phantom(void, _) => match *void {},
         }
@@ -1855,7 +1873,7 @@ impl HirDisplay for DomainGoal {
                 wc.hir_fmt(f)?;
                 write!(f, ")")?;
             }
-            _ => write!(f, "?")?,
+            _ => write!(f, "_")?,
         }
         Ok(())
     }
@@ -1914,7 +1932,7 @@ impl HirDisplay for TypeRef {
                 };
                 write!(f, "&")?;
                 if let Some(lifetime) = lifetime {
-                    write!(f, "{} ", lifetime.name.display(f.db.upcast()))?;
+                    write!(f, "{} ", lifetime.name.display(f.db.upcast(), f.edition()))?;
                 }
                 write!(f, "{mutability}")?;
                 inner.hir_fmt(f)?;
@@ -1922,7 +1940,7 @@ impl HirDisplay for TypeRef {
             TypeRef::Array(inner, len) => {
                 write!(f, "[")?;
                 inner.hir_fmt(f)?;
-                write!(f, "; {}]", len.display(f.db.upcast()))?;
+                write!(f, "; {}]", len.display(f.db.upcast(), f.edition()))?;
             }
             TypeRef::Slice(inner) => {
                 write!(f, "[")?;
@@ -1943,7 +1961,7 @@ impl HirDisplay for TypeRef {
                     for index in 0..function_parameters.len() {
                         let (param_name, param_type) = &function_parameters[index];
                         if let Some(name) = param_name {
-                            write!(f, "{}: ", name.display(f.db.upcast()))?;
+                            write!(f, "{}: ", name.display(f.db.upcast(), f.edition()))?;
                         }
 
                         param_type.hir_fmt(f)?;
@@ -2001,12 +2019,15 @@ impl HirDisplay for TypeBound {
                 }
                 path.hir_fmt(f)
             }
-            TypeBound::Lifetime(lifetime) => write!(f, "{}", lifetime.name.display(f.db.upcast())),
+            TypeBound::Lifetime(lifetime) => {
+                write!(f, "{}", lifetime.name.display(f.db.upcast(), f.edition()))
+            }
             TypeBound::ForLifetime(lifetimes, path) => {
+                let edition = f.edition();
                 write!(
                     f,
                     "for<{}> ",
-                    lifetimes.iter().map(|it| it.display(f.db.upcast())).format(", ")
+                    lifetimes.iter().map(|it| it.display(f.db.upcast(), edition)).format(", ")
                 )?;
                 path.hir_fmt(f)
             }
@@ -2072,7 +2093,7 @@ impl HirDisplay for Path {
             if !matches!(self.kind(), PathKind::Plain) || seg_idx > 0 {
                 write!(f, "::")?;
             }
-            write!(f, "{}", segment.name.display(f.db.upcast()))?;
+            write!(f, "{}", segment.name.display(f.db.upcast(), f.edition()))?;
             if let Some(generic_args) = segment.args_and_bindings {
                 // We should be in type context, so format as `Foo<Bar>` instead of `Foo::<Bar>`.
                 // Do we actually format expressions?
@@ -2117,7 +2138,7 @@ impl HirDisplay for Path {
                     } else {
                         write!(f, ", ")?;
                     }
-                    write!(f, "{}", binding.name.display(f.db.upcast()))?;
+                    write!(f, "{}", binding.name.display(f.db.upcast(), f.edition()))?;
                     match &binding.type_ref {
                         Some(ty) => {
                             write!(f, " = ")?;
@@ -2151,9 +2172,11 @@ impl HirDisplay for hir_def::path::GenericArg {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
         match self {
             hir_def::path::GenericArg::Type(ty) => ty.hir_fmt(f),
-            hir_def::path::GenericArg::Const(c) => write!(f, "{}", c.display(f.db.upcast())),
+            hir_def::path::GenericArg::Const(c) => {
+                write!(f, "{}", c.display(f.db.upcast(), f.edition()))
+            }
             hir_def::path::GenericArg::Lifetime(lifetime) => {
-                write!(f, "{}", lifetime.name.display(f.db.upcast()))
+                write!(f, "{}", lifetime.name.display(f.db.upcast(), f.edition()))
             }
         }
     }
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 45d423d03c021..062ea278151bb 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -22,7 +22,7 @@ mod pat;
 mod path;
 pub(crate) mod unify;
 
-use std::{convert::identity, iter, ops::Index};
+use std::{cell::OnceCell, convert::identity, iter, ops::Index};
 
 use chalk_ir::{
     cast::Cast,
@@ -49,17 +49,17 @@ use hir_expand::name::Name;
 use indexmap::IndexSet;
 use intern::sym;
 use la_arena::{ArenaMap, Entry};
-use once_cell::unsync::OnceCell;
 use rustc_hash::{FxHashMap, FxHashSet};
 use stdx::{always, never};
 use triomphe::Arc;
 
 use crate::{
     db::HirDatabase,
-    error_lifetime, fold_tys,
+    fold_tys,
     generics::Generics,
     infer::{coerce::CoerceMany, unify::InferenceTable},
     lower::ImplTraitLoweringMode,
+    mir::MirSpan,
     to_assoc_type_id,
     traits::FnTrait,
     utils::{InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder},
@@ -328,13 +328,13 @@ pub struct Adjustment {
 }
 
 impl Adjustment {
-    pub fn borrow(m: Mutability, ty: Ty) -> Self {
-        let ty = TyKind::Ref(m, error_lifetime(), ty).intern(Interner);
-        Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty }
+    pub fn borrow(m: Mutability, ty: Ty, lt: Lifetime) -> Self {
+        let ty = TyKind::Ref(m, lt.clone(), ty).intern(Interner);
+        Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(lt, m)), target: ty }
     }
 }
 
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
 pub enum Adjust {
     /// Go from ! to any type.
     NeverToAny,
@@ -354,18 +354,18 @@ pub enum Adjust {
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub struct OverloadedDeref(pub Option<Mutability>);
 
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
 pub enum AutoBorrow {
     /// Converts from T to &T.
-    Ref(Mutability),
+    Ref(Lifetime, Mutability),
     /// Converts from T to *T.
     RawPtr(Mutability),
 }
 
 impl AutoBorrow {
-    fn mutability(self) -> Mutability {
-        let (AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m)) = self;
-        m
+    fn mutability(&self) -> Mutability {
+        let (AutoBorrow::Ref(_, m) | AutoBorrow::RawPtr(m)) = self;
+        *m
     }
 }
 
@@ -554,6 +554,12 @@ pub(crate) struct InferenceContext<'a> {
 
     // fields related to closure capture
     current_captures: Vec<CapturedItemWithoutTy>,
+    /// A stack that has an entry for each projection in the current capture.
+    ///
+    /// For example, in `a.b.c`, we capture the spans of `a`, `a.b`, and `a.b.c`.
+    /// We do that because sometimes we truncate projections (when a closure captures
+    /// both `a.b` and `a.b.c`), and we want to provide accurate spans in this case.
+    current_capture_span_stack: Vec<MirSpan>,
     current_closure: Option<ClosureId>,
     /// Stores the list of closure ids that need to be analyzed before this closure. See the
     /// comment on `InferenceContext::sort_closures`
@@ -605,6 +611,11 @@ fn find_continuable(
     }
 }
 
+enum ImplTraitReplacingMode {
+    ReturnPosition(FxHashSet<Ty>),
+    TypeAlias,
+}
+
 impl<'a> InferenceContext<'a> {
     fn new(
         db: &'a dyn HirDatabase,
@@ -630,6 +641,7 @@ impl<'a> InferenceContext<'a> {
             breakables: Vec::new(),
             deferred_cast_checks: Vec::new(),
             current_captures: Vec::new(),
+            current_capture_span_stack: Vec::new(),
             current_closure: None,
             deferred_closures: FxHashMap::default(),
             closure_dependencies: FxHashMap::default(),
@@ -826,13 +838,19 @@ impl<'a> InferenceContext<'a> {
                 self.write_binding_ty(self_param, ty);
             }
         }
-        let mut params_and_ret_tys = Vec::new();
+        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);
 
             self.infer_top_pat(*pat, &ty);
-            params_and_ret_tys.push(ty);
+            if ty
+                .data(Interner)
+                .flags
+                .intersects(TypeFlags::HAS_TY_OPAQUE.union(TypeFlags::HAS_TY_INFER))
+            {
+                tait_candidates.insert(ty);
+            }
         }
         let return_ty = &*data.ret_type;
 
@@ -845,7 +863,12 @@ impl<'a> InferenceContext<'a> {
         let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) {
             // RPIT opaque types use substitution of their parent function.
             let fn_placeholders = TyBuilder::placeholder_subst(self.db, func);
-            let result = self.insert_inference_vars_for_impl_trait(return_ty, fn_placeholders);
+            let mut mode = ImplTraitReplacingMode::ReturnPosition(FxHashSet::default());
+            let result =
+                self.insert_inference_vars_for_impl_trait(return_ty, fn_placeholders, &mut mode);
+            if let ImplTraitReplacingMode::ReturnPosition(taits) = mode {
+                tait_candidates.extend(taits);
+            }
             let rpits = rpits.skip_binders();
             for (id, _) in rpits.impl_traits.iter() {
                 if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) {
@@ -864,11 +887,23 @@ impl<'a> InferenceContext<'a> {
         // Functions might be defining usage sites of TAITs.
         // To define an TAITs, that TAIT must appear in the function's signatures.
         // So, it suffices to check for params and return types.
-        params_and_ret_tys.push(self.return_ty.clone());
-        self.make_tait_coercion_table(params_and_ret_tys.iter());
+        if self
+            .return_ty
+            .data(Interner)
+            .flags
+            .intersects(TypeFlags::HAS_TY_OPAQUE.union(TypeFlags::HAS_TY_INFER))
+        {
+            tait_candidates.insert(self.return_ty.clone());
+        }
+        self.make_tait_coercion_table(tait_candidates.iter());
     }
 
-    fn insert_inference_vars_for_impl_trait<T>(&mut self, t: T, placeholders: Substitution) -> T
+    fn insert_inference_vars_for_impl_trait<T>(
+        &mut self,
+        t: T,
+        placeholders: Substitution,
+        mode: &mut ImplTraitReplacingMode,
+    ) -> T
     where
         T: crate::HasInterner<Interner = Interner> + crate::TypeFoldable<Interner>,
     {
@@ -881,10 +916,31 @@ impl<'a> InferenceContext<'a> {
                 };
                 let (impl_traits, idx) =
                     match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) {
+                        // We don't replace opaque types from other kind with inference vars
+                        // because `insert_inference_vars_for_impl_traits` for each kinds
+                        // and unreplaced opaque types of other kind are resolved while
+                        // inferencing because of `tait_coercion_table`.
+                        // Moreover, calling `insert_inference_vars_for_impl_traits` with same
+                        // `placeholders` for other kind may cause trouble because
+                        // the substs for the bounds of each impl traits do not match
                         ImplTraitId::ReturnTypeImplTrait(def, idx) => {
+                            if matches!(mode, ImplTraitReplacingMode::TypeAlias) {
+                                // RPITs don't have `tait_coercion_table`, so use inserted inference
+                                // vars for them.
+                                if let Some(ty) = self.result.type_of_rpit.get(idx) {
+                                    return ty.clone();
+                                }
+                                return ty;
+                            }
                             (self.db.return_type_impl_traits(def), idx)
                         }
                         ImplTraitId::TypeAliasImplTrait(def, idx) => {
+                            if let ImplTraitReplacingMode::ReturnPosition(taits) = mode {
+                                // Gather TAITs while replacing RPITs because TAITs inside RPITs
+                                // may not visited while replacing TAITs
+                                taits.insert(ty.clone());
+                                return ty;
+                            }
                             (self.db.type_alias_impl_traits(def), idx)
                         }
                         _ => unreachable!(),
@@ -893,16 +949,20 @@ impl<'a> InferenceContext<'a> {
                     return ty;
                 };
                 let bounds = (*impl_traits)
-                    .map_ref(|rpits| rpits.impl_traits[idx].bounds.map_ref(|it| it.iter()));
+                    .map_ref(|its| its.impl_traits[idx].bounds.map_ref(|it| it.iter()));
                 let var = self.table.new_type_var();
                 let var_subst = Substitution::from1(Interner, var.clone());
                 for bound in bounds {
-                    let predicate = bound.map(|it| it.cloned()).substitute(Interner, &placeholders);
+                    let predicate = bound.map(|it| it.cloned());
+                    let predicate = predicate.substitute(Interner, &placeholders);
                     let (var_predicate, binders) =
                         predicate.substitute(Interner, &var_subst).into_value_and_skipped_binders();
                     always!(binders.is_empty(Interner)); // quantified where clauses not yet handled
-                    let var_predicate = self
-                        .insert_inference_vars_for_impl_trait(var_predicate, placeholders.clone());
+                    let var_predicate = self.insert_inference_vars_for_impl_trait(
+                        var_predicate,
+                        placeholders.clone(),
+                        mode,
+                    );
                     self.push_obligation(var_predicate.cast(Interner));
                 }
                 self.result.type_of_rpit.insert(idx, var.clone());
@@ -1039,7 +1099,11 @@ impl<'a> InferenceContext<'a> {
                     self.db.lookup_intern_impl_trait_id(id.into())
                 {
                     let subst = TyBuilder::placeholder_subst(self.db, alias_id);
-                    let ty = self.insert_inference_vars_for_impl_trait(ty, subst);
+                    let ty = self.insert_inference_vars_for_impl_trait(
+                        ty,
+                        subst,
+                        &mut ImplTraitReplacingMode::TypeAlias,
+                    );
                     Some((id, ty))
                 } else {
                     None
@@ -1436,7 +1500,8 @@ impl<'a> InferenceContext<'a> {
         let remaining = unresolved.map(|it| path.segments()[it..].len()).filter(|it| it > &0);
         let ty = match ty.kind(Interner) {
             TyKind::Alias(AliasTy::Projection(proj_ty)) => {
-                self.db.normalize_projection(proj_ty.clone(), self.table.trait_env.clone())
+                let ty = self.table.normalize_projection_ty(proj_ty.clone());
+                self.table.resolve_ty_shallow(&ty)
             }
             _ => 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 034ed2d691b4f..36327d1d49c0a 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
@@ -18,8 +18,9 @@ use hir_def::{
 use hir_expand::name::Name;
 use intern::sym;
 use rustc_hash::FxHashMap;
-use smallvec::SmallVec;
-use stdx::never;
+use smallvec::{smallvec, SmallVec};
+use stdx::{format_to, never};
+use syntax::utils::is_raw_identifier;
 
 use crate::{
     db::{HirDatabase, InternedClosure},
@@ -236,7 +237,13 @@ pub enum CaptureKind {
 pub struct CapturedItem {
     pub(crate) place: HirPlace,
     pub(crate) kind: CaptureKind,
-    pub(crate) span: MirSpan,
+    /// The inner vec is the stacks; the outer vec is for each capture reference.
+    ///
+    /// Even though we always report only the last span (i.e. the most inclusive span),
+    /// we need to keep them all, since when a closure occurs inside a closure, we
+    /// copy all captures of the inner closure to the outer closure, and then we may
+    /// truncate them, and we want the correct span to be reported.
+    span_stacks: SmallVec<[SmallVec<[MirSpan; 3]>; 3]>,
     pub(crate) ty: Binders<Ty>,
 }
 
@@ -245,6 +252,11 @@ impl CapturedItem {
         self.place.local
     }
 
+    /// Returns whether this place has any field (aka. non-deref) projections.
+    pub fn has_field_projections(&self) -> bool {
+        self.place.projections.iter().any(|it| !matches!(it, ProjectionElem::Deref))
+    }
+
     pub fn ty(&self, subst: &Substitution) -> Ty {
         self.ty.clone().substitute(Interner, utils::ClosureSubst(subst).parent_subst())
     }
@@ -253,9 +265,106 @@ impl CapturedItem {
         self.kind
     }
 
+    pub fn spans(&self) -> SmallVec<[MirSpan; 3]> {
+        self.span_stacks.iter().map(|stack| *stack.last().expect("empty span stack")).collect()
+    }
+
+    /// Converts the place to a name that can be inserted into source code.
+    pub fn place_to_name(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String {
+        let body = db.body(owner);
+        let mut result = body[self.place.local].name.unescaped().display(db.upcast()).to_string();
+        for proj in &self.place.projections {
+            match proj {
+                ProjectionElem::Deref => {}
+                ProjectionElem::Field(Either::Left(f)) => {
+                    match &*f.parent.variant_data(db.upcast()) {
+                        VariantData::Record(fields) => {
+                            result.push('_');
+                            result.push_str(fields[f.local_id].name.as_str())
+                        }
+                        VariantData::Tuple(fields) => {
+                            let index = fields.iter().position(|it| it.0 == f.local_id);
+                            if let Some(index) = index {
+                                format_to!(result, "_{index}");
+                            }
+                        }
+                        VariantData::Unit => {}
+                    }
+                }
+                ProjectionElem::Field(Either::Right(f)) => format_to!(result, "_{}", f.index),
+                &ProjectionElem::ClosureField(field) => format_to!(result, "_{field}"),
+                ProjectionElem::Index(_)
+                | ProjectionElem::ConstantIndex { .. }
+                | ProjectionElem::Subslice { .. }
+                | ProjectionElem::OpaqueCast(_) => {
+                    never!("Not happen in closure capture");
+                    continue;
+                }
+            }
+        }
+        if is_raw_identifier(&result, db.crate_graph()[owner.module(db.upcast()).krate()].edition) {
+            result.insert_str(0, "r#");
+        }
+        result
+    }
+
+    pub fn display_place_source_code(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String {
+        let body = db.body(owner);
+        let krate = owner.krate(db.upcast());
+        let edition = db.crate_graph()[krate].edition;
+        let mut result = body[self.place.local].name.display(db.upcast(), edition).to_string();
+        for proj in &self.place.projections {
+            match proj {
+                // In source code autoderef kicks in.
+                ProjectionElem::Deref => {}
+                ProjectionElem::Field(Either::Left(f)) => {
+                    let variant_data = f.parent.variant_data(db.upcast());
+                    match &*variant_data {
+                        VariantData::Record(fields) => format_to!(
+                            result,
+                            ".{}",
+                            fields[f.local_id].name.display(db.upcast(), edition)
+                        ),
+                        VariantData::Tuple(fields) => format_to!(
+                            result,
+                            ".{}",
+                            fields.iter().position(|it| it.0 == f.local_id).unwrap_or_default()
+                        ),
+                        VariantData::Unit => {}
+                    }
+                }
+                ProjectionElem::Field(Either::Right(f)) => {
+                    let field = f.index;
+                    format_to!(result, ".{field}");
+                }
+                &ProjectionElem::ClosureField(field) => {
+                    format_to!(result, ".{field}");
+                }
+                ProjectionElem::Index(_)
+                | ProjectionElem::ConstantIndex { .. }
+                | ProjectionElem::Subslice { .. }
+                | ProjectionElem::OpaqueCast(_) => {
+                    never!("Not happen in closure capture");
+                    continue;
+                }
+            }
+        }
+        let final_derefs_count = self
+            .place
+            .projections
+            .iter()
+            .rev()
+            .take_while(|proj| matches!(proj, ProjectionElem::Deref))
+            .count();
+        result.insert_str(0, &"*".repeat(final_derefs_count));
+        result
+    }
+
     pub fn display_place(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String {
         let body = db.body(owner);
-        let mut result = body[self.place.local].name.display(db.upcast()).to_string();
+        let krate = owner.krate(db.upcast());
+        let edition = db.crate_graph()[krate].edition;
+        let mut result = body[self.place.local].name.display(db.upcast(), edition).to_string();
         let mut field_need_paren = false;
         for proj in &self.place.projections {
             match proj {
@@ -312,7 +421,8 @@ impl CapturedItem {
 pub(crate) struct CapturedItemWithoutTy {
     pub(crate) place: HirPlace,
     pub(crate) kind: CaptureKind,
-    pub(crate) span: MirSpan,
+    /// The inner vec is the stacks; the outer vec is for each capture reference.
+    pub(crate) span_stacks: SmallVec<[SmallVec<[MirSpan; 3]>; 3]>,
 }
 
 impl CapturedItemWithoutTy {
@@ -331,7 +441,7 @@ impl CapturedItemWithoutTy {
         return CapturedItem {
             place: self.place,
             kind: self.kind,
-            span: self.span,
+            span_stacks: self.span_stacks,
             ty: replace_placeholder_with_binder(ctx, ty),
         };
 
@@ -391,22 +501,26 @@ impl InferenceContext<'_> {
         let r = self.place_of_expr_without_adjust(tgt_expr)?;
         let default = vec![];
         let adjustments = self.result.expr_adjustments.get(&tgt_expr).unwrap_or(&default);
-        apply_adjusts_to_place(r, adjustments)
+        apply_adjusts_to_place(&mut self.current_capture_span_stack, r, adjustments)
     }
 
+    /// Changes `current_capture_span_stack` to contain the stack of spans for this expr.
     fn place_of_expr_without_adjust(&mut self, tgt_expr: ExprId) -> Option<HirPlace> {
+        self.current_capture_span_stack.clear();
         match &self.body[tgt_expr] {
             Expr::Path(p) => {
                 let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr);
                 if let Some(ResolveValueResult::ValueNs(ValueNs::LocalBinding(b), _)) =
                     resolver.resolve_path_in_value_ns(self.db.upcast(), p)
                 {
+                    self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr));
                     return Some(HirPlace { local: b, projections: vec![] });
                 }
             }
             Expr::Field { expr, name: _ } => {
                 let mut place = self.place_of_expr(*expr)?;
                 let field = self.result.field_resolution(tgt_expr)?;
+                self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr));
                 place.projections.push(ProjectionElem::Field(field));
                 return Some(place);
             }
@@ -416,6 +530,7 @@ impl InferenceContext<'_> {
                     TyKind::Ref(..) | TyKind::Raw(..)
                 ) {
                     let mut place = self.place_of_expr(*expr)?;
+                    self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr));
                     place.projections.push(ProjectionElem::Deref);
                     return Some(place);
                 }
@@ -425,29 +540,57 @@ impl InferenceContext<'_> {
         None
     }
 
-    fn push_capture(&mut self, capture: CapturedItemWithoutTy) {
-        self.current_captures.push(capture);
+    fn push_capture(&mut self, place: HirPlace, kind: CaptureKind) {
+        self.current_captures.push(CapturedItemWithoutTy {
+            place,
+            kind,
+            span_stacks: smallvec![self.current_capture_span_stack.iter().copied().collect()],
+        });
     }
 
-    fn ref_expr(&mut self, expr: ExprId) {
-        if let Some(place) = self.place_of_expr(expr) {
-            self.add_capture(place, CaptureKind::ByRef(BorrowKind::Shared), expr.into());
+    fn truncate_capture_spans(&self, capture: &mut CapturedItemWithoutTy, mut truncate_to: usize) {
+        // The first span is the identifier, and it must always remain.
+        truncate_to += 1;
+        for span_stack in &mut capture.span_stacks {
+            let mut remained = truncate_to;
+            let mut actual_truncate_to = 0;
+            for &span in &*span_stack {
+                actual_truncate_to += 1;
+                if !span.is_ref_span(self.body) {
+                    remained -= 1;
+                    if remained == 0 {
+                        break;
+                    }
+                }
+            }
+            if actual_truncate_to < span_stack.len()
+                && span_stack[actual_truncate_to].is_ref_span(self.body)
+            {
+                // Include the ref operator if there is one, we will fix it later (in `strip_captures_ref_span()`) if it's incorrect.
+                actual_truncate_to += 1;
+            }
+            span_stack.truncate(actual_truncate_to);
+        }
+    }
+
+    fn ref_expr(&mut self, expr: ExprId, place: Option<HirPlace>) {
+        if let Some(place) = place {
+            self.add_capture(place, CaptureKind::ByRef(BorrowKind::Shared));
         }
         self.walk_expr(expr);
     }
 
-    fn add_capture(&mut self, place: HirPlace, kind: CaptureKind, span: MirSpan) {
+    fn add_capture(&mut self, place: HirPlace, kind: CaptureKind) {
         if self.is_upvar(&place) {
-            self.push_capture(CapturedItemWithoutTy { place, kind, span });
+            self.push_capture(place, kind);
         }
     }
 
-    fn mutate_expr(&mut self, expr: ExprId) {
-        if let Some(place) = self.place_of_expr(expr) {
+    fn mutate_expr(&mut self, expr: ExprId, place: Option<HirPlace>) {
+        if let Some(place) = place {
             self.add_capture(
                 place,
                 CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::Default }),
-                expr.into(),
             );
         }
         self.walk_expr(expr);
@@ -455,12 +598,12 @@ impl InferenceContext<'_> {
 
     fn consume_expr(&mut self, expr: ExprId) {
         if let Some(place) = self.place_of_expr(expr) {
-            self.consume_place(place, expr.into());
+            self.consume_place(place);
         }
         self.walk_expr(expr);
     }
 
-    fn consume_place(&mut self, place: HirPlace, span: MirSpan) {
+    fn consume_place(&mut self, place: HirPlace) {
         if self.is_upvar(&place) {
             let ty = place.ty(self);
             let kind = if self.is_ty_copy(ty) {
@@ -468,13 +611,13 @@ impl InferenceContext<'_> {
             } else {
                 CaptureKind::ByValue
             };
-            self.push_capture(CapturedItemWithoutTy { place, kind, span });
+            self.push_capture(place, kind);
         }
     }
 
     fn walk_expr_with_adjust(&mut self, tgt_expr: ExprId, adjustment: &[Adjustment]) {
         if let Some((last, rest)) = adjustment.split_last() {
-            match last.kind {
+            match &last.kind {
                 Adjust::NeverToAny | Adjust::Deref(None) | Adjust::Pointer(_) => {
                     self.walk_expr_with_adjust(tgt_expr, rest)
                 }
@@ -499,8 +642,10 @@ impl InferenceContext<'_> {
             Mutability::Not => CaptureKind::ByRef(BorrowKind::Shared),
         };
         if let Some(place) = self.place_of_expr_without_adjust(tgt_expr) {
-            if let Some(place) = apply_adjusts_to_place(place, rest) {
-                self.add_capture(place, capture_kind, tgt_expr.into());
+            if let Some(place) =
+                apply_adjusts_to_place(&mut self.current_capture_span_stack, place, rest)
+            {
+                self.add_capture(place, capture_kind);
             }
         }
         self.walk_expr_with_adjust(tgt_expr, rest);
@@ -582,11 +727,7 @@ impl InferenceContext<'_> {
                             self.walk_pat(&mut capture_mode, arm.pat);
                         }
                         if let Some(c) = capture_mode {
-                            self.push_capture(CapturedItemWithoutTy {
-                                place: discr_place,
-                                kind: c,
-                                span: (*expr).into(),
-                            })
+                            self.push_capture(discr_place, c);
                         }
                     }
                 }
@@ -630,10 +771,11 @@ impl InferenceContext<'_> {
                         }
                         false
                     };
+                    let place = self.place_of_expr(*expr);
                     if mutability {
-                        self.mutate_expr(*expr);
+                        self.mutate_expr(*expr, place);
                     } else {
-                        self.ref_expr(*expr);
+                        self.ref_expr(*expr, place);
                     }
                 } else {
                     self.select_from_expr(*expr);
@@ -648,16 +790,22 @@ impl InferenceContext<'_> {
             | Expr::Cast { expr, type_ref: _ } => {
                 self.consume_expr(*expr);
             }
-            Expr::Ref { expr, rawness: _, mutability } => match mutability {
-                hir_def::type_ref::Mutability::Shared => self.ref_expr(*expr),
-                hir_def::type_ref::Mutability::Mut => self.mutate_expr(*expr),
-            },
+            Expr::Ref { expr, rawness: _, mutability } => {
+                // We need to do this before we push the span so the order will be correct.
+                let place = self.place_of_expr(*expr);
+                self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr));
+                match mutability {
+                    hir_def::type_ref::Mutability::Shared => self.ref_expr(*expr, place),
+                    hir_def::type_ref::Mutability::Mut => self.mutate_expr(*expr, place),
+                }
+            }
             Expr::BinaryOp { lhs, rhs, op } => {
                 let Some(op) = op else {
                     return;
                 };
                 if matches!(op, BinaryOp::Assignment { .. }) {
-                    self.mutate_expr(*lhs);
+                    let place = self.place_of_expr(*lhs);
+                    self.mutate_expr(*lhs, place);
                     self.consume_expr(*rhs);
                     return;
                 }
@@ -688,7 +836,11 @@ impl InferenceContext<'_> {
                     );
                 let mut cc = mem::take(&mut self.current_captures);
                 cc.extend(captures.iter().filter(|it| self.is_upvar(&it.place)).map(|it| {
-                    CapturedItemWithoutTy { place: it.place.clone(), kind: it.kind, span: it.span }
+                    CapturedItemWithoutTy {
+                        place: it.place.clone(),
+                        kind: it.kind,
+                        span_stacks: it.span_stacks.clone(),
+                    }
                 }));
                 self.current_captures = cc;
             }
@@ -810,10 +962,13 @@ impl InferenceContext<'_> {
     }
 
     fn restrict_precision_for_unsafe(&mut self) {
-        for capture in &mut self.current_captures {
+        // FIXME: Borrow checker problems without this.
+        let mut current_captures = std::mem::take(&mut self.current_captures);
+        for capture in &mut current_captures {
             let mut ty = self.table.resolve_completely(self.result[capture.place.local].clone());
             if ty.as_raw_ptr().is_some() || ty.is_union() {
                 capture.kind = CaptureKind::ByRef(BorrowKind::Shared);
+                self.truncate_capture_spans(capture, 0);
                 capture.place.projections.truncate(0);
                 continue;
             }
@@ -828,29 +983,35 @@ impl InferenceContext<'_> {
                 );
                 if ty.as_raw_ptr().is_some() || ty.is_union() {
                     capture.kind = CaptureKind::ByRef(BorrowKind::Shared);
+                    self.truncate_capture_spans(capture, i + 1);
                     capture.place.projections.truncate(i + 1);
                     break;
                 }
             }
         }
+        self.current_captures = current_captures;
     }
 
     fn adjust_for_move_closure(&mut self) {
-        for capture in &mut self.current_captures {
+        // FIXME: Borrow checker won't allow without this.
+        let mut current_captures = std::mem::take(&mut self.current_captures);
+        for capture in &mut current_captures {
             if let Some(first_deref) =
                 capture.place.projections.iter().position(|proj| *proj == ProjectionElem::Deref)
             {
+                self.truncate_capture_spans(capture, first_deref);
                 capture.place.projections.truncate(first_deref);
             }
             capture.kind = CaptureKind::ByValue;
         }
+        self.current_captures = current_captures;
     }
 
     fn minimize_captures(&mut self) {
-        self.current_captures.sort_by_key(|it| it.place.projections.len());
+        self.current_captures.sort_unstable_by_key(|it| it.place.projections.len());
         let mut hash_map = FxHashMap::<HirPlace, usize>::default();
         let result = mem::take(&mut self.current_captures);
-        for item in result {
+        for mut item in result {
             let mut lookup_place = HirPlace { local: item.place.local, projections: vec![] };
             let mut it = item.place.projections.iter();
             let prev_index = loop {
@@ -858,12 +1019,17 @@ impl InferenceContext<'_> {
                     break Some(*k);
                 }
                 match it.next() {
-                    Some(it) => lookup_place.projections.push(it.clone()),
+                    Some(it) => {
+                        lookup_place.projections.push(it.clone());
+                    }
                     None => break None,
                 }
             };
             match prev_index {
                 Some(p) => {
+                    let prev_projections_len = self.current_captures[p].place.projections.len();
+                    self.truncate_capture_spans(&mut item, prev_projections_len);
+                    self.current_captures[p].span_stacks.extend(item.span_stacks);
                     let len = self.current_captures[p].place.projections.len();
                     let kind_after_truncate =
                         item.place.capture_kind_of_truncated_place(item.kind, len);
@@ -878,113 +1044,128 @@ impl InferenceContext<'_> {
         }
     }
 
-    fn consume_with_pat(&mut self, mut place: HirPlace, pat: PatId) {
-        let cnt = self.result.pat_adjustments.get(&pat).map(|it| it.len()).unwrap_or_default();
-        place.projections = place
-            .projections
-            .iter()
-            .cloned()
-            .chain((0..cnt).map(|_| ProjectionElem::Deref))
-            .collect::<Vec<_>>();
-        match &self.body[pat] {
-            Pat::Missing | Pat::Wild => (),
-            Pat::Tuple { args, ellipsis } => {
-                let (al, ar) = args.split_at(ellipsis.map_or(args.len(), |it| it as usize));
-                let field_count = match self.result[pat].kind(Interner) {
-                    TyKind::Tuple(_, s) => s.len(Interner),
-                    _ => return,
-                };
-                let fields = 0..field_count;
-                let it = al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev()));
-                for (arg, i) in it {
-                    let mut p = place.clone();
-                    p.projections.push(ProjectionElem::Field(Either::Right(TupleFieldId {
-                        tuple: TupleId(!0), // dummy this, as its unused anyways
-                        index: i as u32,
-                    })));
-                    self.consume_with_pat(p, *arg);
-                }
-            }
-            Pat::Or(pats) => {
-                for pat in pats.iter() {
-                    self.consume_with_pat(place.clone(), *pat);
+    fn consume_with_pat(&mut self, mut place: HirPlace, tgt_pat: PatId) {
+        let adjustments_count =
+            self.result.pat_adjustments.get(&tgt_pat).map(|it| it.len()).unwrap_or_default();
+        place.projections.extend((0..adjustments_count).map(|_| ProjectionElem::Deref));
+        self.current_capture_span_stack
+            .extend((0..adjustments_count).map(|_| MirSpan::PatId(tgt_pat)));
+        'reset_span_stack: {
+            match &self.body[tgt_pat] {
+                Pat::Missing | Pat::Wild => (),
+                Pat::Tuple { args, ellipsis } => {
+                    let (al, ar) = args.split_at(ellipsis.map_or(args.len(), |it| it as usize));
+                    let field_count = match self.result[tgt_pat].kind(Interner) {
+                        TyKind::Tuple(_, s) => s.len(Interner),
+                        _ => break 'reset_span_stack,
+                    };
+                    let fields = 0..field_count;
+                    let it = al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev()));
+                    for (&arg, i) in it {
+                        let mut p = place.clone();
+                        self.current_capture_span_stack.push(MirSpan::PatId(arg));
+                        p.projections.push(ProjectionElem::Field(Either::Right(TupleFieldId {
+                            tuple: TupleId(!0), // dummy this, as its unused anyways
+                            index: i as u32,
+                        })));
+                        self.consume_with_pat(p, arg);
+                        self.current_capture_span_stack.pop();
+                    }
                 }
-            }
-            Pat::Record { args, .. } => {
-                let Some(variant) = self.result.variant_resolution_for_pat(pat) else {
-                    return;
-                };
-                match variant {
-                    VariantId::EnumVariantId(_) | VariantId::UnionId(_) => {
-                        self.consume_place(place, pat.into())
+                Pat::Or(pats) => {
+                    for pat in pats.iter() {
+                        self.consume_with_pat(place.clone(), *pat);
                     }
-                    VariantId::StructId(s) => {
-                        let vd = &*self.db.struct_data(s).variant_data;
-                        for field_pat in args.iter() {
-                            let arg = field_pat.pat;
-                            let Some(local_id) = vd.field(&field_pat.name) else {
-                                continue;
-                            };
-                            let mut p = place.clone();
-                            p.projections.push(ProjectionElem::Field(Either::Left(FieldId {
-                                parent: variant,
-                                local_id,
-                            })));
-                            self.consume_with_pat(p, arg);
+                }
+                Pat::Record { args, .. } => {
+                    let Some(variant) = self.result.variant_resolution_for_pat(tgt_pat) else {
+                        break 'reset_span_stack;
+                    };
+                    match variant {
+                        VariantId::EnumVariantId(_) | VariantId::UnionId(_) => {
+                            self.consume_place(place)
+                        }
+                        VariantId::StructId(s) => {
+                            let vd = &*self.db.struct_data(s).variant_data;
+                            for field_pat in args.iter() {
+                                let arg = field_pat.pat;
+                                let Some(local_id) = vd.field(&field_pat.name) else {
+                                    continue;
+                                };
+                                let mut p = place.clone();
+                                self.current_capture_span_stack.push(MirSpan::PatId(arg));
+                                p.projections.push(ProjectionElem::Field(Either::Left(FieldId {
+                                    parent: variant,
+                                    local_id,
+                                })));
+                                self.consume_with_pat(p, arg);
+                                self.current_capture_span_stack.pop();
+                            }
                         }
                     }
                 }
-            }
-            Pat::Range { .. }
-            | Pat::Slice { .. }
-            | Pat::ConstBlock(_)
-            | Pat::Path(_)
-            | Pat::Lit(_) => self.consume_place(place, pat.into()),
-            Pat::Bind { id: _, subpat: _ } => {
-                let mode = self.result.binding_modes[pat];
-                let capture_kind = match mode {
-                    BindingMode::Move => {
-                        self.consume_place(place, pat.into());
-                        return;
-                    }
-                    BindingMode::Ref(Mutability::Not) => BorrowKind::Shared,
-                    BindingMode::Ref(Mutability::Mut) => {
-                        BorrowKind::Mut { kind: MutBorrowKind::Default }
-                    }
-                };
-                self.add_capture(place, CaptureKind::ByRef(capture_kind), pat.into());
-            }
-            Pat::TupleStruct { path: _, args, ellipsis } => {
-                let Some(variant) = self.result.variant_resolution_for_pat(pat) else {
-                    return;
-                };
-                match variant {
-                    VariantId::EnumVariantId(_) | VariantId::UnionId(_) => {
-                        self.consume_place(place, pat.into())
-                    }
-                    VariantId::StructId(s) => {
-                        let vd = &*self.db.struct_data(s).variant_data;
-                        let (al, ar) = args.split_at(ellipsis.map_or(args.len(), |it| it as usize));
-                        let fields = vd.fields().iter();
-                        let it =
-                            al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev()));
-                        for (arg, (i, _)) in it {
-                            let mut p = place.clone();
-                            p.projections.push(ProjectionElem::Field(Either::Left(FieldId {
-                                parent: variant,
-                                local_id: i,
-                            })));
-                            self.consume_with_pat(p, *arg);
+                Pat::Range { .. }
+                | Pat::Slice { .. }
+                | Pat::ConstBlock(_)
+                | Pat::Path(_)
+                | Pat::Lit(_) => self.consume_place(place),
+                &Pat::Bind { id, subpat: _ } => {
+                    let mode = self.result.binding_modes[tgt_pat];
+                    let capture_kind = match mode {
+                        BindingMode::Move => {
+                            self.consume_place(place);
+                            break 'reset_span_stack;
+                        }
+                        BindingMode::Ref(Mutability::Not) => BorrowKind::Shared,
+                        BindingMode::Ref(Mutability::Mut) => {
+                            BorrowKind::Mut { kind: MutBorrowKind::Default }
+                        }
+                    };
+                    self.current_capture_span_stack.push(MirSpan::BindingId(id));
+                    self.add_capture(place, CaptureKind::ByRef(capture_kind));
+                    self.current_capture_span_stack.pop();
+                }
+                Pat::TupleStruct { path: _, args, ellipsis } => {
+                    let Some(variant) = self.result.variant_resolution_for_pat(tgt_pat) else {
+                        break 'reset_span_stack;
+                    };
+                    match variant {
+                        VariantId::EnumVariantId(_) | VariantId::UnionId(_) => {
+                            self.consume_place(place)
+                        }
+                        VariantId::StructId(s) => {
+                            let vd = &*self.db.struct_data(s).variant_data;
+                            let (al, ar) =
+                                args.split_at(ellipsis.map_or(args.len(), |it| it as usize));
+                            let fields = vd.fields().iter();
+                            let it = al
+                                .iter()
+                                .zip(fields.clone())
+                                .chain(ar.iter().rev().zip(fields.rev()));
+                            for (&arg, (i, _)) in it {
+                                let mut p = place.clone();
+                                self.current_capture_span_stack.push(MirSpan::PatId(arg));
+                                p.projections.push(ProjectionElem::Field(Either::Left(FieldId {
+                                    parent: variant,
+                                    local_id: i,
+                                })));
+                                self.consume_with_pat(p, arg);
+                                self.current_capture_span_stack.pop();
+                            }
                         }
                     }
                 }
+                Pat::Ref { pat, mutability: _ } => {
+                    self.current_capture_span_stack.push(MirSpan::PatId(tgt_pat));
+                    place.projections.push(ProjectionElem::Deref);
+                    self.consume_with_pat(place, *pat);
+                    self.current_capture_span_stack.pop();
+                }
+                Pat::Box { .. } => (), // not supported
             }
-            Pat::Ref { pat, mutability: _ } => {
-                place.projections.push(ProjectionElem::Deref);
-                self.consume_with_pat(place, *pat)
-            }
-            Pat::Box { .. } => (), // not supported
         }
+        self.current_capture_span_stack
+            .truncate(self.current_capture_span_stack.len() - adjustments_count);
     }
 
     fn consume_exprs(&mut self, exprs: impl Iterator<Item = ExprId>) {
@@ -1042,12 +1223,28 @@ impl InferenceContext<'_> {
             CaptureBy::Ref => (),
         }
         self.minimize_captures();
+        self.strip_captures_ref_span();
         let result = mem::take(&mut self.current_captures);
         let captures = result.into_iter().map(|it| it.with_ty(self)).collect::<Vec<_>>();
         self.result.closure_info.insert(closure, (captures, closure_kind));
         closure_kind
     }
 
+    fn strip_captures_ref_span(&mut self) {
+        // FIXME: Borrow checker won't allow without this.
+        let mut captures = std::mem::take(&mut self.current_captures);
+        for capture in &mut captures {
+            if matches!(capture.kind, CaptureKind::ByValue) {
+                for span_stack in &mut capture.span_stacks {
+                    if span_stack[span_stack.len() - 1].is_ref_span(self.body) {
+                        span_stack.truncate(span_stack.len() - 1);
+                    }
+                }
+            }
+        }
+        self.current_captures = captures;
+    }
+
     pub(crate) fn infer_closures(&mut self) {
         let deferred_closures = self.sort_closures();
         for (closure, exprs) in deferred_closures.into_iter().rev() {
@@ -1108,10 +1305,17 @@ impl InferenceContext<'_> {
     }
 }
 
-fn apply_adjusts_to_place(mut r: HirPlace, adjustments: &[Adjustment]) -> Option<HirPlace> {
+/// Call this only when the last span in the stack isn't a split.
+fn apply_adjusts_to_place(
+    current_capture_span_stack: &mut Vec<MirSpan>,
+    mut r: HirPlace,
+    adjustments: &[Adjustment],
+) -> Option<HirPlace> {
+    let span = *current_capture_span_stack.last().expect("empty capture span stack");
     for adj in adjustments {
         match &adj.kind {
             Adjust::Deref(None) => {
+                current_capture_span_stack.push(span);
                 r.projections.push(ProjectionElem::Deref);
             }
             _ => return None,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
index 6f85a4a4247c4..7e758c0b5173b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
@@ -18,14 +18,13 @@ use triomphe::Arc;
 use crate::{
     autoderef::{Autoderef, AutoderefKind},
     db::HirDatabase,
-    error_lifetime,
     infer::{
         Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast,
         TypeError, TypeMismatch,
     },
     utils::ClosureSubst,
-    Canonical, DomainGoal, FnAbi, FnPointer, FnSig, Guidance, InEnvironment, Interner, Solution,
-    Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
+    Canonical, DomainGoal, FnAbi, FnPointer, FnSig, Guidance, InEnvironment, Interner, Lifetime,
+    Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
 };
 
 use super::unify::InferenceTable;
@@ -301,7 +300,7 @@ impl InferenceTable<'_> {
         // Examine the supertype and consider auto-borrowing.
         match to_ty.kind(Interner) {
             TyKind::Raw(mt, _) => return self.coerce_ptr(from_ty, to_ty, *mt),
-            TyKind::Ref(mt, _, _) => return self.coerce_ref(from_ty, to_ty, *mt),
+            TyKind::Ref(mt, lt, _) => return self.coerce_ref(from_ty, to_ty, *mt, lt),
             _ => {}
         }
 
@@ -377,11 +376,17 @@ impl InferenceTable<'_> {
     /// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.
     /// To match `A` with `B`, autoderef will be performed,
     /// calling `deref`/`deref_mut` where necessary.
-    fn coerce_ref(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> CoerceResult {
-        let from_mt = match from_ty.kind(Interner) {
-            &TyKind::Ref(mt, _, _) => {
-                coerce_mutabilities(mt, to_mt)?;
-                mt
+    fn coerce_ref(
+        &mut self,
+        from_ty: Ty,
+        to_ty: &Ty,
+        to_mt: Mutability,
+        to_lt: &Lifetime,
+    ) -> CoerceResult {
+        let (_from_lt, from_mt) = match from_ty.kind(Interner) {
+            TyKind::Ref(mt, lt, _) => {
+                coerce_mutabilities(*mt, to_mt)?;
+                (lt.clone(), *mt) // clone is probably not good?
             }
             _ => return self.unify_and(&from_ty, to_ty, identity),
         };
@@ -427,8 +432,8 @@ impl InferenceTable<'_> {
             // compare those. Note that this means we use the target
             // mutability [1], since it may be that we are coercing
             // from `&mut T` to `&U`.
-            let lt = error_lifetime(); // FIXME: handle lifetimes correctly, see rustc
-            let derefd_from_ty = TyKind::Ref(to_mt, lt, referent_ty).intern(Interner);
+            let lt = to_lt; // FIXME: Involve rustc LUB and SUB flag checks
+            let derefd_from_ty = TyKind::Ref(to_mt, lt.clone(), referent_ty).intern(Interner);
             match autoderef.table.try_unify(&derefd_from_ty, to_ty) {
                 Ok(result) => {
                     found = Some(result.map(|()| derefd_from_ty));
@@ -472,8 +477,10 @@ impl InferenceTable<'_> {
         }
 
         let mut adjustments = auto_deref_adjust_steps(&autoderef);
-        adjustments
-            .push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(to_mt)), target: ty.clone() });
+        adjustments.push(Adjustment {
+            kind: Adjust::Borrow(AutoBorrow::Ref(to_lt.clone(), to_mt)),
+            target: ty.clone(),
+        });
 
         success(adjustments, ty, goals)
     }
@@ -621,11 +628,11 @@ impl InferenceTable<'_> {
             (TyKind::Ref(from_mt, _, from_inner), &TyKind::Ref(to_mt, _, _)) => {
                 coerce_mutabilities(*from_mt, to_mt)?;
 
-                let lt = error_lifetime();
+                let lt = self.new_lifetime_var();
                 Some((
                     Adjustment { kind: Adjust::Deref(None), target: from_inner.clone() },
                     Adjustment {
-                        kind: Adjust::Borrow(AutoBorrow::Ref(to_mt)),
+                        kind: Adjust::Borrow(AutoBorrow::Ref(lt.clone(), to_mt)),
                         target: TyKind::Ref(to_mt, lt, from_inner.clone()).intern(Interner),
                     },
                 ))
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 f5eb37f427876..89d92ea9af0b9 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
@@ -635,7 +635,10 @@ impl InferenceContext<'_> {
                 let inner_ty = self.infer_expr_inner(*expr, &expectation);
                 match rawness {
                     Rawness::RawPtr => TyKind::Raw(mutability, inner_ty),
-                    Rawness::Ref => TyKind::Ref(mutability, error_lifetime(), inner_ty),
+                    Rawness::Ref => {
+                        let lt = self.table.new_lifetime_var();
+                        TyKind::Ref(mutability, lt, inner_ty)
+                    }
                 }
                 .intern(Interner)
             }
@@ -786,18 +789,23 @@ impl InferenceContext<'_> {
                             adj.apply(&mut self.table, base_ty)
                         });
                     // mutability will be fixed up in `InferenceContext::infer_mut`;
-                    adj.push(Adjustment::borrow(Mutability::Not, self_ty.clone()));
+                    adj.push(Adjustment::borrow(
+                        Mutability::Not,
+                        self_ty.clone(),
+                        self.table.new_lifetime_var(),
+                    ));
                     self.write_expr_adj(*base, adj);
                     if let Some(func) = self
                         .db
                         .trait_data(index_trait)
                         .method_by_name(&Name::new_symbol_root(sym::index.clone()))
                     {
-                        let substs = TyBuilder::subst_for_def(self.db, index_trait, None)
-                            .push(self_ty.clone())
-                            .push(index_ty.clone())
-                            .build();
-                        self.write_method_resolution(tgt_expr, func, substs);
+                        let subst = TyBuilder::subst_for_def(self.db, index_trait, None);
+                        if subst.remaining() != 2 {
+                            return self.err_ty();
+                        }
+                        let subst = subst.push(self_ty.clone()).push(index_ty.clone()).build();
+                        self.write_method_resolution(tgt_expr, func, subst);
                     }
                     let assoc = self.resolve_ops_index_output();
                     let res = self.resolve_associated_type_with_params(
@@ -990,7 +998,7 @@ impl InferenceContext<'_> {
         match fn_x {
             FnTrait::FnOnce => (),
             FnTrait::FnMut => {
-                if let TyKind::Ref(Mutability::Mut, _, inner) = derefed_callee.kind(Interner) {
+                if let TyKind::Ref(Mutability::Mut, lt, inner) = derefed_callee.kind(Interner) {
                     if adjustments
                         .last()
                         .map(|it| matches!(it.kind, Adjust::Borrow(_)))
@@ -999,15 +1007,27 @@ impl InferenceContext<'_> {
                         // prefer reborrow to move
                         adjustments
                             .push(Adjustment { kind: Adjust::Deref(None), target: inner.clone() });
-                        adjustments.push(Adjustment::borrow(Mutability::Mut, inner.clone()))
+                        adjustments.push(Adjustment::borrow(
+                            Mutability::Mut,
+                            inner.clone(),
+                            lt.clone(),
+                        ))
                     }
                 } else {
-                    adjustments.push(Adjustment::borrow(Mutability::Mut, derefed_callee.clone()));
+                    adjustments.push(Adjustment::borrow(
+                        Mutability::Mut,
+                        derefed_callee.clone(),
+                        self.table.new_lifetime_var(),
+                    ));
                 }
             }
             FnTrait::Fn => {
                 if !matches!(derefed_callee.kind(Interner), TyKind::Ref(Mutability::Not, _, _)) {
-                    adjustments.push(Adjustment::borrow(Mutability::Not, derefed_callee.clone()));
+                    adjustments.push(Adjustment::borrow(
+                        Mutability::Not,
+                        derefed_callee.clone(),
+                        self.table.new_lifetime_var(),
+                    ));
                 }
             }
         }
@@ -1295,10 +1315,12 @@ impl InferenceContext<'_> {
 
         // HACK: We can use this substitution for the function because the function itself doesn't
         // have its own generic parameters.
-        let subst = TyBuilder::subst_for_def(self.db, trait_, None)
-            .push(lhs_ty.clone())
-            .push(rhs_ty.clone())
-            .build();
+        let subst = TyBuilder::subst_for_def(self.db, trait_, None);
+        if subst.remaining() != 2 {
+            return Ty::new(Interner, TyKind::Error);
+        }
+        let subst = subst.push(lhs_ty.clone()).push(rhs_ty.clone()).build();
+
         self.write_method_resolution(tgt_expr, func, subst.clone());
 
         let method_ty = self.db.value_ty(func.into()).unwrap().substitute(Interner, &subst);
@@ -1310,11 +1332,11 @@ impl InferenceContext<'_> {
             Some(sig) => {
                 let p_left = &sig.params()[0];
                 if matches!(op, BinaryOp::CmpOp(..) | BinaryOp::Assignment { .. }) {
-                    if let &TyKind::Ref(mtbl, _, _) = p_left.kind(Interner) {
+                    if let TyKind::Ref(mtbl, lt, _) = p_left.kind(Interner) {
                         self.write_expr_adj(
                             lhs,
                             vec![Adjustment {
-                                kind: Adjust::Borrow(AutoBorrow::Ref(mtbl)),
+                                kind: Adjust::Borrow(AutoBorrow::Ref(lt.clone(), *mtbl)),
                                 target: p_left.clone(),
                             }],
                         );
@@ -1322,11 +1344,11 @@ impl InferenceContext<'_> {
                 }
                 let p_right = &sig.params()[1];
                 if matches!(op, BinaryOp::CmpOp(..)) {
-                    if let &TyKind::Ref(mtbl, _, _) = p_right.kind(Interner) {
+                    if let TyKind::Ref(mtbl, lt, _) = p_right.kind(Interner) {
                         self.write_expr_adj(
                             rhs,
                             vec![Adjustment {
-                                kind: Adjust::Borrow(AutoBorrow::Ref(mtbl)),
+                                kind: Adjust::Borrow(AutoBorrow::Ref(lt.clone(), *mtbl)),
                                 target: p_right.clone(),
                             }],
                         );
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
index 66267e08db62b..7fed5f0203bad 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
@@ -28,7 +28,7 @@ impl InferenceContext<'_> {
                     Adjust::NeverToAny | Adjust::Deref(None) | Adjust::Pointer(_) => (),
                     Adjust::Deref(Some(d)) => *d = OverloadedDeref(Some(mutability)),
                     Adjust::Borrow(b) => match b {
-                        AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m) => mutability = *m,
+                        AutoBorrow::Ref(_, m) | AutoBorrow::RawPtr(m) => mutability = *m,
                     },
                 }
             }
@@ -125,7 +125,7 @@ impl InferenceContext<'_> {
                                     .get_mut(&base)
                                     .and_then(|it| it.last_mut());
                                 if let Some(Adjustment {
-                                    kind: Adjust::Borrow(AutoBorrow::Ref(mutability)),
+                                    kind: Adjust::Borrow(AutoBorrow::Ref(_, mutability)),
                                     target,
                                 }) = base_adjustments
                                 {
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 f3c6f13a08d07..50542b2acd459 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
@@ -12,7 +12,6 @@ use stdx::TupleExt;
 
 use crate::{
     consteval::{try_const_usize, usize_const},
-    error_lifetime,
     infer::{BindingMode, Expectation, InferenceContext, TypeMismatch},
     lower::lower_to_chalk_mutability,
     primitive::UintTy,
@@ -394,19 +393,20 @@ impl InferenceContext<'_> {
         expected: &Ty,
         default_bm: BindingMode,
     ) -> Ty {
-        let expectation = match expected.as_reference() {
-            Some((inner_ty, _lifetime, _exp_mut)) => inner_ty.clone(),
+        let (expectation_type, expectation_lt) = match expected.as_reference() {
+            Some((inner_ty, lifetime, _exp_mut)) => (inner_ty.clone(), lifetime.clone()),
             None => {
                 let inner_ty = self.table.new_type_var();
+                let inner_lt = self.table.new_lifetime_var();
                 let ref_ty =
-                    TyKind::Ref(mutability, error_lifetime(), inner_ty.clone()).intern(Interner);
+                    TyKind::Ref(mutability, inner_lt.clone(), inner_ty.clone()).intern(Interner);
                 // Unification failure will be reported by the caller.
                 self.unify(&ref_ty, expected);
-                inner_ty
+                (inner_ty, inner_lt)
             }
         };
-        let subty = self.infer_pat(inner_pat, &expectation, default_bm);
-        TyKind::Ref(mutability, error_lifetime(), subty).intern(Interner)
+        let subty = self.infer_pat(inner_pat, &expectation_type, default_bm);
+        TyKind::Ref(mutability, expectation_lt, subty).intern(Interner)
     }
 
     fn infer_bind_pat(
@@ -433,7 +433,8 @@ impl InferenceContext<'_> {
 
         let bound_ty = match mode {
             BindingMode::Ref(mutability) => {
-                TyKind::Ref(mutability, error_lifetime(), inner_ty.clone()).intern(Interner)
+                let inner_lt = self.table.new_lifetime_var();
+                TyKind::Ref(mutability, inner_lt, inner_ty.clone()).intern(Interner)
             }
             BindingMode::Move => inner_ty.clone(),
         };
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 3e3578b9f9b87..c0f5ddddcbe3e 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
@@ -17,12 +17,12 @@ use triomphe::Arc;
 
 use super::{InferOk, InferResult, InferenceContext, TypeError};
 use crate::{
-    consteval::unknown_const, db::HirDatabase, error_lifetime, fold_generic_args,
-    fold_tys_and_consts, to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical,
-    Const, ConstValue, DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData,
-    Guidance, InEnvironment, InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, ProjectionTy,
-    ProjectionTyExt, Scalar, Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
-    TyKind, VariableKind, WhereClause,
+    consteval::unknown_const, db::HirDatabase, fold_generic_args, fold_tys_and_consts,
+    to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue,
+    DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData, Guidance, InEnvironment,
+    InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar,
+    Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
+    WhereClause,
 };
 
 impl InferenceContext<'_> {
@@ -105,7 +105,7 @@ impl<T: HasInterner<Interner = Interner>> Canonicalized<T> {
                 VariableKind::Ty(TyVariableKind::Float) => ctx.new_float_var().cast(Interner),
                 // Chalk can sometimes return new lifetime variables. We just replace them by errors
                 // for now.
-                VariableKind::Lifetime => error_lifetime().cast(Interner),
+                VariableKind::Lifetime => ctx.new_lifetime_var().cast(Interner),
                 VariableKind::Const(ty) => ctx.new_const_var(ty.clone()).cast(Interner),
             }),
         );
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
index 8cb428a610a8c..f40d508f755a6 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
@@ -42,19 +42,20 @@ fn eval_goal(ra_fixture: &str, minicore: &str) -> Result<Arc<Layout>, LayoutErro
                 hir_def::ModuleDefId::AdtId(x) => {
                     let name = match x {
                         hir_def::AdtId::StructId(x) => {
-                            db.struct_data(x).name.display_no_db().to_smolstr()
+                            db.struct_data(x).name.display_no_db(file_id.edition()).to_smolstr()
                         }
                         hir_def::AdtId::UnionId(x) => {
-                            db.union_data(x).name.display_no_db().to_smolstr()
+                            db.union_data(x).name.display_no_db(file_id.edition()).to_smolstr()
                         }
                         hir_def::AdtId::EnumId(x) => {
-                            db.enum_data(x).name.display_no_db().to_smolstr()
+                            db.enum_data(x).name.display_no_db(file_id.edition()).to_smolstr()
                         }
                     };
                     (name == "Goal").then_some(Either::Left(x))
                 }
                 hir_def::ModuleDefId::TypeAliasId(x) => {
-                    let name = db.type_alias_data(x).name.display_no_db().to_smolstr();
+                    let name =
+                        db.type_alias_data(x).name.display_no_db(file_id.edition()).to_smolstr();
                     (name == "Goal").then_some(Either::Right(x))
                 }
                 _ => None,
@@ -94,7 +95,7 @@ fn eval_expr(ra_fixture: &str, minicore: &str) -> Result<Arc<Layout>, LayoutErro
         .declarations()
         .find_map(|x| match x {
             hir_def::ModuleDefId::FunctionId(x) => {
-                let name = db.function_data(x).name.display_no_db().to_smolstr();
+                let name = db.function_data(x).name.display_no_db(file_id.edition()).to_smolstr();
                 (name == "main").then_some(x)
             }
             _ => None,
@@ -104,7 +105,7 @@ fn eval_expr(ra_fixture: &str, minicore: &str) -> Result<Arc<Layout>, LayoutErro
     let b = hir_body
         .bindings
         .iter()
-        .find(|x| x.1.name.display_no_db().to_smolstr() == "goal")
+        .find(|x| x.1.name.display_no_db(file_id.edition()).to_smolstr() == "goal")
         .unwrap()
         .0;
     let infer = db.infer(function_id.into());
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
index 21c84511dc35e..2f4e764f4cecf 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
@@ -68,6 +68,7 @@ use intern::{sym, Symbol};
 use la_arena::{Arena, Idx};
 use mir::{MirEvalError, VTableMap};
 use rustc_hash::{FxHashMap, FxHashSet};
+use span::Edition;
 use syntax::ast::{make, ConstArg};
 use traits::FnTrait;
 use triomphe::Arc;
@@ -1027,7 +1028,11 @@ where
     collector.placeholders.into_iter().collect()
 }
 
-pub fn known_const_to_ast(konst: &Const, db: &dyn HirDatabase) -> Option<ConstArg> {
+pub fn known_const_to_ast(
+    konst: &Const,
+    db: &dyn HirDatabase,
+    edition: Edition,
+) -> Option<ConstArg> {
     if let ConstValue::Concrete(c) = &konst.interned().value {
         match c.interned {
             ConstScalar::UnevaluatedConst(GeneralConstId::InTypeConstId(cid), _) => {
@@ -1037,5 +1042,5 @@ pub fn known_const_to_ast(konst: &Const, db: &dyn HirDatabase) -> Option<ConstAr
             _ => (),
         }
     }
-    Some(make::expr_const_value(konst.display(db).to_string().as_str()))
+    Some(make::expr_const_value(konst.display(db, edition).to_string().as_str()))
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
index 67cdb99744ab7..370d9ba99cb0c 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
@@ -6,7 +6,7 @@
 //!
 //! This usually involves resolving names, collecting generic arguments etc.
 use std::{
-    cell::{Cell, RefCell, RefMut},
+    cell::{Cell, OnceCell, RefCell, RefMut},
     iter,
     ops::{self, Not as _},
 };
@@ -43,7 +43,6 @@ use hir_def::{
 use hir_expand::{name::Name, ExpandResult};
 use intern::Interned;
 use la_arena::{Arena, ArenaMap};
-use once_cell::unsync::OnceCell;
 use rustc_hash::FxHashSet;
 use rustc_pattern_analysis::Captures;
 use smallvec::SmallVec;
@@ -378,26 +377,25 @@ impl<'a> TyLoweringContext<'a> {
                         // Count the number of `impl Trait` things that appear within our bounds.
                         // Since t hose have been emitted as implicit type args already.
                         counter.set(idx + count_impl_traits(type_ref) as u16);
-                        let (
-                            _parent_params,
-                            self_param,
-                            type_params,
-                            const_params,
-                            _impl_trait_params,
-                            lifetime_params,
-                        ) = self
+                        let kind = self
                             .generics()
                             .expect("variable impl trait lowering must be in a generic def")
-                            .provenance_split();
-                        TyKind::BoundVar(BoundVar::new(
-                            self.in_binders,
-                            idx as usize
-                                + self_param as usize
-                                + type_params
-                                + const_params
-                                + lifetime_params,
-                        ))
-                        .intern(Interner)
+                            .iter()
+                            .enumerate()
+                            .filter_map(|(i, (id, data))| match (id, data) {
+                                (
+                                    GenericParamId::TypeParamId(_),
+                                    GenericParamDataRef::TypeParamData(data),
+                                ) if data.provenance == TypeParamProvenance::ArgumentImplTrait => {
+                                    Some(i)
+                                }
+                                _ => None,
+                            })
+                            .nth(idx as usize)
+                            .map_or(TyKind::Error, |id| {
+                                TyKind::BoundVar(BoundVar { debruijn: self.in_binders, index: id })
+                            });
+                        kind.intern(Interner)
                     }
                     ImplTraitLoweringState::Disallowed => {
                         // FIXME: report error
@@ -1553,6 +1551,10 @@ pub(crate) fn generic_predicates_for_param_query(
                 }
             };
             if invalid_target {
+                // If this is filtered out without lowering, `?Sized` is not gathered into `ctx.unsized_types`
+                if let TypeBound::Path(_, TraitBoundModifier::Maybe) = &**bound {
+                    ctx.lower_where_predicate(pred, &def, true).for_each(drop);
+                }
                 return false;
             }
 
@@ -1741,15 +1743,39 @@ fn implicitly_sized_clauses<'a, 'subst: 'a>(
     substitution: &'subst Substitution,
     resolver: &Resolver,
 ) -> Option<impl Iterator<Item = WhereClause> + Captures<'a> + Captures<'subst>> {
-    let is_trait_def = matches!(def, GenericDefId::TraitId(..));
-    let generic_args = &substitution.as_slice(Interner)[is_trait_def as usize..];
     let sized_trait = db
         .lang_item(resolver.krate(), LangItem::Sized)
-        .and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id));
+        .and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id))?;
 
-    sized_trait.map(move |sized_trait| {
-        generic_args
-            .iter()
+    let get_trait_self_idx = |container: ItemContainerId| {
+        if matches!(container, ItemContainerId::TraitId(_)) {
+            let generics = generics(db.upcast(), def);
+            Some(generics.len_self())
+        } else {
+            None
+        }
+    };
+    let trait_self_idx = match def {
+        GenericDefId::TraitId(_) => Some(0),
+        GenericDefId::FunctionId(it) => get_trait_self_idx(it.lookup(db.upcast()).container),
+        GenericDefId::ConstId(it) => get_trait_self_idx(it.lookup(db.upcast()).container),
+        GenericDefId::TypeAliasId(it) => get_trait_self_idx(it.lookup(db.upcast()).container),
+        _ => None,
+    };
+
+    Some(
+        substitution
+            .iter(Interner)
+            .enumerate()
+            .filter_map(
+                move |(idx, generic_arg)| {
+                    if Some(idx) == trait_self_idx {
+                        None
+                    } else {
+                        Some(generic_arg)
+                    }
+                },
+            )
             .filter_map(|generic_arg| generic_arg.ty(Interner))
             .filter(move |&self_ty| !explicitly_unsized_tys.contains(self_ty))
             .map(move |self_ty| {
@@ -1757,8 +1783,8 @@ fn implicitly_sized_clauses<'a, 'subst: 'a>(
                     trait_id: sized_trait,
                     substitution: Substitution::from1(Interner, self_ty.clone()),
                 })
-            })
-    })
+            }),
+    )
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -1978,13 +2004,13 @@ fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> {
         .with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
         .with_type_param_mode(ParamLoweringMode::Variable);
     let type_alias_data = db.type_alias_data(t);
-    if type_alias_data.is_extern {
-        Binders::empty(Interner, TyKind::Foreign(crate::to_foreign_def_id(t)).intern(Interner))
+    let inner = if type_alias_data.is_extern {
+        TyKind::Foreign(crate::to_foreign_def_id(t)).intern(Interner)
     } else {
         let type_ref = &type_alias_data.type_ref;
-        let inner = ctx.lower_ty(type_ref.as_deref().unwrap_or(&TypeRef::Error));
-        make_binders(db, &generics, inner)
-    }
+        ctx.lower_ty(type_ref.as_deref().unwrap_or(&TypeRef::Error))
+    };
+    make_binders(db, &generics, inner)
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
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 8ba8071d36ec4..5a72b97653dbf 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
@@ -35,7 +35,7 @@ use crate::{
 };
 
 /// This is used as a key for indexing impls.
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
 pub enum TyFingerprint {
     // These are lang item impls:
     Str,
@@ -542,7 +542,8 @@ impl ReceiverAdjustments {
             }
         }
         if let Some(m) = self.autoref {
-            let a = Adjustment::borrow(m, ty);
+            let lt = table.new_lifetime_var();
+            let a = Adjustment::borrow(m, ty, lt);
             ty = a.target.clone();
             adjust.push(a);
         }
@@ -1066,7 +1067,7 @@ fn iterate_method_candidates_by_receiver(
     // be found in any of the derefs of receiver_ty, so we have to go through
     // that, including raw derefs.
     table.run_in_snapshot(|table| {
-        let mut autoderef = autoderef::Autoderef::new(table, receiver_ty.clone(), true);
+        let mut autoderef = autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true);
         while let Some((self_ty, _)) = autoderef.next() {
             iterate_inherent_methods(
                 &self_ty,
@@ -1081,7 +1082,7 @@ fn iterate_method_candidates_by_receiver(
         ControlFlow::Continue(())
     })?;
     table.run_in_snapshot(|table| {
-        let mut autoderef = autoderef::Autoderef::new(table, receiver_ty.clone(), true);
+        let mut autoderef = autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true);
         while let Some((self_ty, _)) = autoderef.next() {
             if matches!(self_ty.kind(Interner), TyKind::InferenceVar(_, TyVariableKind::General)) {
                 // don't try to resolve methods on unknown types
@@ -1656,7 +1657,7 @@ fn autoderef_method_receiver(
     ty: Ty,
 ) -> Vec<(Canonical<Ty>, ReceiverAdjustments)> {
     let mut deref_chain: Vec<_> = Vec::new();
-    let mut autoderef = autoderef::Autoderef::new(table, ty, false);
+    let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ty, false);
     while let Some((ty, derefs)) = autoderef.next() {
         deref_chain.push((
             autoderef.table.canonicalize(ty),
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
index 06a4236e0acd2..22d4da0e755ae 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
@@ -16,7 +16,8 @@ use base_db::CrateId;
 use chalk_ir::Mutability;
 use either::Either;
 use hir_def::{
-    hir::{BindingId, Expr, ExprId, Ordering, PatId},
+    body::Body,
+    hir::{BindingAnnotation, BindingId, Expr, ExprId, Ordering, PatId},
     DefWithBodyId, FieldId, StaticId, TupleFieldId, UnionId, VariantId,
 };
 use la_arena::{Arena, ArenaMap, Idx, RawIdx};
@@ -158,7 +159,10 @@ impl<V, T> ProjectionElem<V, T> {
                     subst.at(Interner, 0).assert_ty_ref(Interner).clone()
                 }
                 _ => {
-                    never!("Overloaded deref on type {} is not a projection", base.display(db));
+                    never!(
+                        "Overloaded deref on type {} is not a projection",
+                        base.display(db, db.crate_graph()[krate].edition)
+                    );
                     TyKind::Error.intern(Interner)
                 }
             },
@@ -633,6 +637,7 @@ pub enum TerminatorKind {
     },
 }
 
+// Order of variants in this enum matter: they are used to compare borrow kinds.
 #[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
 pub enum BorrowKind {
     /// Data must be immutable and is aliasable.
@@ -663,15 +668,16 @@ pub enum BorrowKind {
     Mut { kind: MutBorrowKind },
 }
 
+// Order of variants in this enum matter: they are used to compare borrow kinds.
 #[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
 pub enum MutBorrowKind {
+    /// Data must be immutable but not aliasable. This kind of borrow cannot currently
+    /// be expressed by the user and is used only in implicit closure bindings.
+    ClosureCapture,
     Default,
     /// This borrow arose from method-call auto-ref
     /// (i.e., adjustment::Adjust::Borrow).
     TwoPhasedBorrow,
-    /// Data must be immutable but not aliasable. This kind of borrow cannot currently
-    /// be expressed by the user and is used only in implicit closure bindings.
-    ClosureCapture,
 }
 
 impl BorrowKind {
@@ -1169,6 +1175,20 @@ pub enum MirSpan {
     Unknown,
 }
 
+impl MirSpan {
+    pub fn is_ref_span(&self, body: &Body) -> bool {
+        match *self {
+            MirSpan::ExprId(expr) => matches!(body[expr], Expr::Ref { .. }),
+            // FIXME: Figure out if this is correct wrt. match ergonomics.
+            MirSpan::BindingId(binding) => matches!(
+                body.bindings[binding].mode,
+                BindingAnnotation::Ref | BindingAnnotation::RefMut
+            ),
+            MirSpan::PatId(_) | MirSpan::SelfParam | MirSpan::Unknown => false,
+        }
+    }
+}
+
 impl_from!(ExprId, PatId for MirSpan);
 
 impl From<&ExprId> for MirSpan {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
index f8083e898588e..1bb0c1886857a 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
@@ -23,7 +23,7 @@ use rustc_apfloat::{
     Float,
 };
 use rustc_hash::{FxHashMap, FxHashSet};
-use span::FileId;
+use span::{Edition, FileId};
 use stdx::never;
 use syntax::{SyntaxNodePtr, TextRange};
 use triomphe::Arc;
@@ -358,6 +358,7 @@ impl MirEvalError {
         f: &mut String,
         db: &dyn HirDatabase,
         span_formatter: impl Fn(FileId, TextRange) -> String,
+        edition: Edition,
     ) -> std::result::Result<(), std::fmt::Error> {
         writeln!(f, "Mir eval error:")?;
         let mut err = self;
@@ -370,7 +371,7 @@ impl MirEvalError {
                         writeln!(
                             f,
                             "In function {} ({:?})",
-                            function_name.name.display(db.upcast()),
+                            function_name.name.display(db.upcast(), edition),
                             func
                         )?;
                     }
@@ -415,7 +416,7 @@ impl MirEvalError {
                 write!(
                     f,
                     "Layout for type `{}` is not available due {err:?}",
-                    ty.display(db).with_closure_style(ClosureStyle::ClosureWithId)
+                    ty.display(db, edition).with_closure_style(ClosureStyle::ClosureWithId)
                 )?;
             }
             MirEvalError::MirLowerError(func, err) => {
@@ -423,16 +424,17 @@ impl MirEvalError {
                 writeln!(
                     f,
                     "MIR lowering for function `{}` ({:?}) failed due:",
-                    function_name.name.display(db.upcast()),
+                    function_name.name.display(db.upcast(), edition),
                     func
                 )?;
-                err.pretty_print(f, db, span_formatter)?;
+                err.pretty_print(f, db, span_formatter, edition)?;
             }
             MirEvalError::ConstEvalError(name, err) => {
                 MirLowerError::ConstEvalError((**name).into(), err.clone()).pretty_print(
                     f,
                     db,
                     span_formatter,
+                    edition,
                 )?;
             }
             MirEvalError::UndefinedBehavior(_)
@@ -1516,9 +1518,97 @@ impl Evaluator<'_> {
                         self.size_of_sized(target_ty, locals, "destination of int to int cast")?;
                     Owned(current[0..dest_size].to_vec())
                 }
-                CastKind::FloatToInt => not_supported!("float to int cast"),
-                CastKind::FloatToFloat => not_supported!("float to float cast"),
-                CastKind::IntToFloat => not_supported!("float to int cast"),
+                CastKind::FloatToInt => {
+                    let ty = self.operand_ty(operand, locals)?;
+                    let TyKind::Scalar(chalk_ir::Scalar::Float(ty)) = ty.kind(Interner) else {
+                        not_supported!("invalid float to int cast");
+                    };
+                    let value = self.eval_operand(operand, locals)?.get(self)?;
+                    let value = match ty {
+                        chalk_ir::FloatTy::F32 => {
+                            let value = value.try_into().unwrap();
+                            f32::from_le_bytes(value) as f64
+                        }
+                        chalk_ir::FloatTy::F64 => {
+                            let value = value.try_into().unwrap();
+                            f64::from_le_bytes(value)
+                        }
+                        chalk_ir::FloatTy::F16 | chalk_ir::FloatTy::F128 => {
+                            not_supported!("unstable floating point type f16 and f128");
+                        }
+                    };
+                    let is_signed = matches!(
+                        target_ty.kind(Interner),
+                        TyKind::Scalar(chalk_ir::Scalar::Int(_))
+                    );
+                    let dest_size =
+                        self.size_of_sized(target_ty, locals, "destination of float to int cast")?;
+                    let dest_bits = dest_size * 8;
+                    let (max, min) = if dest_bits == 128 {
+                        (i128::MAX, i128::MIN)
+                    } else if is_signed {
+                        let max = 1i128 << (dest_bits - 1);
+                        (max - 1, -max)
+                    } else {
+                        (1i128 << dest_bits, 0)
+                    };
+                    let value = (value as i128).min(max).max(min);
+                    let result = value.to_le_bytes();
+                    Owned(result[0..dest_size].to_vec())
+                }
+                CastKind::FloatToFloat => {
+                    let ty = self.operand_ty(operand, locals)?;
+                    let TyKind::Scalar(chalk_ir::Scalar::Float(ty)) = ty.kind(Interner) else {
+                        not_supported!("invalid float to int cast");
+                    };
+                    let value = self.eval_operand(operand, locals)?.get(self)?;
+                    let value = match ty {
+                        chalk_ir::FloatTy::F32 => {
+                            let value = value.try_into().unwrap();
+                            f32::from_le_bytes(value) as f64
+                        }
+                        chalk_ir::FloatTy::F64 => {
+                            let value = value.try_into().unwrap();
+                            f64::from_le_bytes(value)
+                        }
+                        chalk_ir::FloatTy::F16 | chalk_ir::FloatTy::F128 => {
+                            not_supported!("unstable floating point type f16 and f128");
+                        }
+                    };
+                    let TyKind::Scalar(chalk_ir::Scalar::Float(target_ty)) =
+                        target_ty.kind(Interner)
+                    else {
+                        not_supported!("invalid float to float cast");
+                    };
+                    match target_ty {
+                        chalk_ir::FloatTy::F32 => Owned((value as f32).to_le_bytes().to_vec()),
+                        chalk_ir::FloatTy::F64 => Owned((value as f64).to_le_bytes().to_vec()),
+                        chalk_ir::FloatTy::F16 | chalk_ir::FloatTy::F128 => {
+                            not_supported!("unstable floating point type f16 and f128");
+                        }
+                    }
+                }
+                CastKind::IntToFloat => {
+                    let current_ty = self.operand_ty(operand, locals)?;
+                    let is_signed = matches!(
+                        current_ty.kind(Interner),
+                        TyKind::Scalar(chalk_ir::Scalar::Int(_))
+                    );
+                    let value = pad16(self.eval_operand(operand, locals)?.get(self)?, is_signed);
+                    let value = i128::from_le_bytes(value);
+                    let TyKind::Scalar(chalk_ir::Scalar::Float(target_ty)) =
+                        target_ty.kind(Interner)
+                    else {
+                        not_supported!("invalid int to float cast");
+                    };
+                    match target_ty {
+                        chalk_ir::FloatTy::F32 => Owned((value as f32).to_le_bytes().to_vec()),
+                        chalk_ir::FloatTy::F64 => Owned((value as f64).to_le_bytes().to_vec()),
+                        chalk_ir::FloatTy::F16 | chalk_ir::FloatTy::F128 => {
+                            not_supported!("unstable floating point type f16 and f128");
+                        }
+                    }
+                }
                 CastKind::FnPtrToPtr => not_supported!("fn ptr to ptr cast"),
             },
         })
@@ -2675,10 +2765,11 @@ impl Evaluator<'_> {
                 let db = self.db.upcast();
                 let loc = variant.lookup(db);
                 let enum_loc = loc.parent.lookup(db);
+                let edition = self.db.crate_graph()[self.crate_id].edition;
                 let name = format!(
                     "{}::{}",
-                    enum_loc.id.item_tree(db)[enum_loc.id.value].name.display(db.upcast()),
-                    loc.id.item_tree(db)[loc.id.value].name.display(db.upcast()),
+                    enum_loc.id.item_tree(db)[enum_loc.id.value].name.display(db.upcast(), edition),
+                    loc.id.item_tree(db)[loc.id.value].name.display(db.upcast(), edition),
                 );
                 Err(MirEvalError::ConstEvalError(name, Box::new(e)))
             }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
index d76f53818716a..0cdad74a4f6c1 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
@@ -856,7 +856,11 @@ impl Evaluator<'_> {
                     Ok(ty_name) => ty_name,
                     // Fallback to human readable display in case of `Err`. Ideally we want to use `display_source_code` to
                     // render full paths.
-                    Err(_) => ty.display(self.db).to_string(),
+                    Err(_) => {
+                        let krate = locals.body.owner.krate(self.db.upcast());
+                        let edition = self.db.crate_graph()[krate].edition;
+                        ty.display(self.db, edition).to_string()
+                    }
                 };
                 let len = ty_name.len();
                 let addr = self.heap_allocate(len, 1)?;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
index b21a401fa764d..371a5278dc9a5 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
@@ -1,5 +1,5 @@
 use hir_def::db::DefDatabase;
-use span::EditionedFileId;
+use span::{Edition, EditionedFileId};
 use syntax::{TextRange, TextSize};
 use test_fixture::WithFixture;
 
@@ -15,7 +15,7 @@ fn eval_main(db: &TestDB, file_id: EditionedFileId) -> Result<(String, String),
         .declarations()
         .find_map(|x| match x {
             hir_def::ModuleDefId::FunctionId(x) => {
-                if db.function_data(x).name.display(db).to_string() == "main" {
+                if db.function_data(x).name.display(db, Edition::CURRENT).to_string() == "main" {
                     Some(x)
                 } else {
                     None
@@ -63,7 +63,7 @@ fn check_pass_and_stdio(ra_fixture: &str, expected_stdout: &str, expected_stderr
             let span_formatter = |file, range: TextRange| {
                 format!("{:?} {:?}..{:?}", file, line_index(range.start()), line_index(range.end()))
             };
-            e.pretty_print(&mut err, &db, span_formatter).unwrap();
+            e.pretty_print(&mut err, &db, span_formatter, Edition::CURRENT).unwrap();
             panic!("Error in interpreting: {err}");
         }
         Ok((stdout, stderr)) => {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
index 9aa2eeebc1757..9e235504519e2 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
@@ -21,7 +21,7 @@ use hir_expand::name::Name;
 use la_arena::ArenaMap;
 use rustc_apfloat::Float;
 use rustc_hash::FxHashMap;
-use span::FileId;
+use span::{Edition, FileId};
 use syntax::TextRange;
 use triomphe::Arc;
 
@@ -157,13 +157,18 @@ impl MirLowerError {
         f: &mut String,
         db: &dyn HirDatabase,
         span_formatter: impl Fn(FileId, TextRange) -> String,
+        edition: Edition,
     ) -> std::result::Result<(), std::fmt::Error> {
         match self {
             MirLowerError::ConstEvalError(name, e) => {
                 writeln!(f, "In evaluating constant {name}")?;
                 match &**e {
-                    ConstEvalError::MirLowerError(e) => e.pretty_print(f, db, span_formatter)?,
-                    ConstEvalError::MirEvalError(e) => e.pretty_print(f, db, span_formatter)?,
+                    ConstEvalError::MirLowerError(e) => {
+                        e.pretty_print(f, db, span_formatter, edition)?
+                    }
+                    ConstEvalError::MirEvalError(e) => {
+                        e.pretty_print(f, db, span_formatter, edition)?
+                    }
                 }
             }
             MirLowerError::MissingFunctionDefinition(owner, it) => {
@@ -171,15 +176,15 @@ impl MirLowerError {
                 writeln!(
                     f,
                     "Missing function definition for {}",
-                    body.pretty_print_expr(db.upcast(), *owner, *it)
+                    body.pretty_print_expr(db.upcast(), *owner, *it, edition)
                 )?;
             }
             MirLowerError::TypeMismatch(e) => match e {
                 Some(e) => writeln!(
                     f,
                     "Type mismatch: Expected {}, found {}",
-                    e.expected.display(db),
-                    e.actual.display(db),
+                    e.expected.display(db, edition),
+                    e.actual.display(db, edition),
                 )?,
                 None => writeln!(f, "Type mismatch: types mismatch with {{unknown}}",)?,
             },
@@ -189,11 +194,11 @@ impl MirLowerError {
                 writeln!(
                     f,
                     "Generic arg not provided for {}",
-                    param.name().unwrap_or(&Name::missing()).display(db.upcast())
+                    param.name().unwrap_or(&Name::missing()).display(db.upcast(), edition)
                 )?;
                 writeln!(f, "Provided args: [")?;
                 for g in subst.iter(Interner) {
-                    write!(f, "    {},", g.display(db))?;
+                    write!(f, "    {},", g.display(db, edition))?;
                 }
                 writeln!(f, "]")?;
             }
@@ -242,8 +247,8 @@ impl From<LayoutError> for MirLowerError {
 }
 
 impl MirLowerError {
-    fn unresolved_path(db: &dyn HirDatabase, p: &Path) -> Self {
-        Self::UnresolvedName(p.display(db).to_string())
+    fn unresolved_path(db: &dyn HirDatabase, p: &Path, edition: Edition) -> Self {
+        Self::UnresolvedName(p.display(db, edition).to_string())
     }
 }
 
@@ -337,7 +342,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
                     self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into());
                     Ok(Some(current))
                 }
-                Adjust::Borrow(AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m)) => {
+                Adjust::Borrow(AutoBorrow::Ref(_, m) | AutoBorrow::RawPtr(m)) => {
                     let Some((p, current)) =
                         self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)?
                     else {
@@ -436,7 +441,8 @@ impl<'ctx> MirLowerCtx<'ctx> {
                             VariantId::UnionId(_) => implementation_error!("Union variant as path"),
                         }
                     } else {
-                        let unresolved_name = || MirLowerError::unresolved_path(self.db, p);
+                        let unresolved_name =
+                            || MirLowerError::unresolved_path(self.db, p, self.edition());
                         let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
                         resolver
                             .resolve_path_in_value_ns_fully(self.db.upcast(), p)
@@ -662,7 +668,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
                 let (func_id, generic_args) =
                     self.infer.method_resolution(expr_id).ok_or_else(|| {
                         MirLowerError::UnresolvedMethod(
-                            method_name.display(self.db.upcast()).to_string(),
+                            method_name.display(self.db.upcast(), self.edition()).to_string(),
                         )
                     })?;
                 let func = Operand::from_fn(self.db, func_id, generic_args);
@@ -803,7 +809,9 @@ impl<'ctx> MirLowerCtx<'ctx> {
                 };
                 let variant_id =
                     self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| match path {
-                        Some(p) => MirLowerError::UnresolvedName(p.display(self.db).to_string()),
+                        Some(p) => MirLowerError::UnresolvedName(
+                            p.display(self.db, self.edition()).to_string(),
+                        ),
                         None => MirLowerError::RecordLiteralWithoutPath,
                     })?;
                 let subst = match self.expr_ty_without_adjust(expr_id).kind(Interner) {
@@ -1172,8 +1180,15 @@ impl<'ctx> MirLowerCtx<'ctx> {
                             let placeholder_subst = self.placeholder_subst();
                             let tmp_ty =
                                 capture.ty.clone().substitute(Interner, &placeholder_subst);
-                            let tmp: Place = self.temp(tmp_ty, current, capture.span)?.into();
-                            self.push_assignment(current, tmp, Rvalue::Ref(*bk, p), capture.span);
+                            // FIXME: Handle more than one span.
+                            let capture_spans = capture.spans();
+                            let tmp: Place = self.temp(tmp_ty, current, capture_spans[0])?.into();
+                            self.push_assignment(
+                                current,
+                                tmp,
+                                Rvalue::Ref(*bk, p),
+                                capture_spans[0],
+                            );
                             operands.push(Operand::Move(tmp));
                         }
                         CaptureKind::ByValue => operands.push(Operand::Move(p)),
@@ -1378,7 +1393,9 @@ impl<'ctx> MirLowerCtx<'ctx> {
                         "only `char` and numeric types are allowed in range patterns"
                     ),
                 };
-                let unresolved_name = || MirLowerError::unresolved_path(self.db, c.as_ref());
+                let edition = self.edition();
+                let unresolved_name =
+                    || MirLowerError::unresolved_path(self.db, c.as_ref(), edition);
                 let resolver = self.owner.resolver(self.db.upcast());
                 let pr = resolver
                     .resolve_path_in_value_ns(self.db.upcast(), c.as_ref())
@@ -1904,19 +1921,25 @@ impl<'ctx> MirLowerCtx<'ctx> {
         match r {
             Ok(r) => Ok(r),
             Err(e) => {
+                let edition = self.edition();
                 let db = self.db.upcast();
                 let loc = variant.lookup(db);
                 let enum_loc = loc.parent.lookup(db);
                 let name = format!(
                     "{}::{}",
-                    enum_loc.id.item_tree(db)[enum_loc.id.value].name.display(db.upcast()),
-                    loc.id.item_tree(db)[loc.id.value].name.display(db.upcast()),
+                    enum_loc.id.item_tree(db)[enum_loc.id.value].name.display(db.upcast(), edition),
+                    loc.id.item_tree(db)[loc.id.value].name.display(db.upcast(), edition),
                 );
                 Err(MirLowerError::ConstEvalError(name.into(), Box::new(e)))
             }
         }
     }
 
+    fn edition(&self) -> Edition {
+        let krate = self.owner.krate(self.db.upcast());
+        self.db.crate_graph()[krate].edition
+    }
+
     fn drop_until_scope(
         &mut self,
         scope_index: usize,
@@ -2121,18 +2144,24 @@ pub fn mir_body_for_closure_query(
 }
 
 pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result<Arc<MirBody>> {
+    let krate = def.krate(db.upcast());
+    let edition = db.crate_graph()[krate].edition;
     let detail = match def {
-        DefWithBodyId::FunctionId(it) => db.function_data(it).name.display(db.upcast()).to_string(),
-        DefWithBodyId::StaticId(it) => db.static_data(it).name.display(db.upcast()).to_string(),
+        DefWithBodyId::FunctionId(it) => {
+            db.function_data(it).name.display(db.upcast(), edition).to_string()
+        }
+        DefWithBodyId::StaticId(it) => {
+            db.static_data(it).name.display(db.upcast(), edition).to_string()
+        }
         DefWithBodyId::ConstId(it) => db
             .const_data(it)
             .name
             .clone()
             .unwrap_or_else(Name::missing)
-            .display(db.upcast())
+            .display(db.upcast(), edition)
             .to_string(),
         DefWithBodyId::VariantId(it) => {
-            db.enum_variant_data(it).name.display(db.upcast()).to_string()
+            db.enum_variant_data(it).name.display(db.upcast(), edition).to_string()
         }
         DefWithBodyId::InTypeConstId(it) => format!("in type const {it:?}"),
     };
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs
index 34e0f30afb7fe..b1c0d1f2b3901 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs
@@ -347,7 +347,8 @@ impl MirLowerCtx<'_> {
                         // A const don't bind anything. Only needs check.
                         return Ok((current, current_else));
                     }
-                    let unresolved_name = || MirLowerError::unresolved_path(self.db, p);
+                    let unresolved_name =
+                        || MirLowerError::unresolved_path(self.db, p, self.edition());
                     let resolver = self.owner.resolver(self.db.upcast());
                     let pr = resolver
                         .resolve_path_in_value_ns(self.db.upcast(), p)
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
index 0c641d7c6c2bc..df56071aa9af6 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
@@ -9,6 +9,7 @@ use either::Either;
 use hir_def::{body::Body, hir::BindingId};
 use hir_expand::{name::Name, Lookup};
 use la_arena::ArenaMap;
+use span::Edition;
 
 use crate::{
     db::HirDatabase,
@@ -44,18 +45,21 @@ impl MirBody {
         ctx.for_body(|this| match ctx.body.owner {
             hir_def::DefWithBodyId::FunctionId(id) => {
                 let data = db.function_data(id);
-                w!(this, "fn {}() ", data.name.display(db.upcast()));
+                w!(this, "fn {}() ", data.name.display(db.upcast(), Edition::LATEST));
             }
             hir_def::DefWithBodyId::StaticId(id) => {
                 let data = db.static_data(id);
-                w!(this, "static {}: _ = ", data.name.display(db.upcast()));
+                w!(this, "static {}: _ = ", data.name.display(db.upcast(), Edition::LATEST));
             }
             hir_def::DefWithBodyId::ConstId(id) => {
                 let data = db.const_data(id);
                 w!(
                     this,
                     "const {}: _ = ",
-                    data.name.as_ref().unwrap_or(&Name::missing()).display(db.upcast())
+                    data.name
+                        .as_ref()
+                        .unwrap_or(&Name::missing())
+                        .display(db.upcast(), Edition::LATEST)
                 );
             }
             hir_def::DefWithBodyId::VariantId(id) => {
@@ -64,8 +68,12 @@ impl MirBody {
                 w!(
                     this,
                     "enum {}::{} = ",
-                    enum_loc.id.item_tree(db.upcast())[enum_loc.id.value].name.display(db.upcast()),
-                    loc.id.item_tree(db.upcast())[loc.id.value].name.display(db.upcast()),
+                    enum_loc.id.item_tree(db.upcast())[enum_loc.id.value]
+                        .name
+                        .display(db.upcast(), Edition::LATEST),
+                    loc.id.item_tree(db.upcast())[loc.id.value]
+                        .name
+                        .display(db.upcast(), Edition::LATEST),
                 )
             }
             hir_def::DefWithBodyId::InTypeConstId(id) => {
@@ -122,7 +130,7 @@ impl HirDisplay for LocalName {
         match self {
             LocalName::Unknown(l) => write!(f, "_{}", u32::from(l.into_raw())),
             LocalName::Binding(n, l) => {
-                write!(f, "{}_{}", n.display(f.db.upcast()), u32::from(l.into_raw()))
+                write!(f, "{}_{}", n.display(f.db.upcast(), f.edition()), u32::from(l.into_raw()))
             }
         }
     }
@@ -200,7 +208,7 @@ impl<'a> MirPrettyCtx<'a> {
             wln!(
                 self,
                 "let {}: {};",
-                self.local_name(id).display(self.db),
+                self.local_name(id).display_test(self.db),
                 self.hir_display(&local.ty)
             );
         }
@@ -231,10 +239,18 @@ impl<'a> MirPrettyCtx<'a> {
                             wln!(this, ";");
                         }
                         StatementKind::StorageDead(p) => {
-                            wln!(this, "StorageDead({})", this.local_name(*p).display(self.db));
+                            wln!(
+                                this,
+                                "StorageDead({})",
+                                this.local_name(*p).display_test(self.db)
+                            );
                         }
                         StatementKind::StorageLive(p) => {
-                            wln!(this, "StorageLive({})", this.local_name(*p).display(self.db));
+                            wln!(
+                                this,
+                                "StorageLive({})",
+                                this.local_name(*p).display_test(self.db)
+                            );
                         }
                         StatementKind::Deinit(p) => {
                             w!(this, "Deinit(");
@@ -297,7 +313,7 @@ impl<'a> MirPrettyCtx<'a> {
         fn f(this: &mut MirPrettyCtx<'_>, local: LocalId, projections: &[PlaceElem]) {
             let Some((last, head)) = projections.split_last() else {
                 // no projection
-                w!(this, "{}", this.local_name(local).display(this.db));
+                w!(this, "{}", this.local_name(local).display_test(this.db));
                 return;
             };
             match last {
@@ -317,13 +333,13 @@ impl<'a> MirPrettyCtx<'a> {
                             w!(
                                 this,
                                 " as {}).{}",
-                                variant_name.display(this.db.upcast()),
-                                name.display(this.db.upcast())
+                                variant_name.display(this.db.upcast(), Edition::LATEST),
+                                name.display(this.db.upcast(), Edition::LATEST)
                             );
                         }
                         hir_def::VariantId::StructId(_) | hir_def::VariantId::UnionId(_) => {
                             f(this, local, head);
-                            w!(this, ".{}", name.display(this.db.upcast()));
+                            w!(this, ".{}", name.display(this.db.upcast(), Edition::LATEST));
                         }
                     }
                 }
@@ -337,7 +353,7 @@ impl<'a> MirPrettyCtx<'a> {
                 }
                 ProjectionElem::Index(l) => {
                     f(this, local, head);
-                    w!(this, "[{}]", this.local_name(*l).display(this.db));
+                    w!(this, "[{}]", this.local_name(*l).display_test(this.db));
                 }
                 it => {
                     f(this, local, head);
@@ -387,7 +403,7 @@ impl<'a> MirPrettyCtx<'a> {
             Rvalue::Repeat(op, len) => {
                 w!(self, "[");
                 self.operand(op);
-                w!(self, "; {}]", len.display(self.db));
+                w!(self, "; {}]", len.display_test(self.db));
             }
             Rvalue::Aggregate(AggregateKind::Adt(_, _), it) => {
                 w!(self, "Adt(");
@@ -458,6 +474,6 @@ impl<'a> MirPrettyCtx<'a> {
     }
 
     fn hir_display<T: HirDisplay>(&self, ty: &'a T) -> impl Display + 'a {
-        ty.display(self.db).with_closure_style(ClosureStyle::ClosureWithSubst)
+        ty.display_test(self.db).with_closure_style(ClosureStyle::ClosureWithSubst)
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
index 0fcd789f761c3..bcf9d5ceff0e1 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
@@ -1,3 +1,4 @@
+mod closure_captures;
 mod coercion;
 mod diagnostics;
 mod display_source_code;
@@ -12,6 +13,7 @@ mod traits;
 mod type_alias_impl_traits;
 
 use std::env;
+use std::sync::LazyLock;
 
 use base_db::SourceDatabaseFileInputExt as _;
 use expect_test::Expect;
@@ -25,7 +27,7 @@ use hir_def::{
     AssocItemId, DefWithBodyId, HasModule, LocalModuleId, Lookup, ModuleDefId,
 };
 use hir_expand::{db::ExpandDatabase, FileRange, InFile};
-use once_cell::race::OnceBool;
+use itertools::Itertools;
 use rustc_hash::FxHashMap;
 use stdx::format_to;
 use syntax::{
@@ -50,8 +52,8 @@ use crate::{
 // `env UPDATE_EXPECT=1 cargo test -p hir_ty` to update the snapshots.
 
 fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> {
-    static ENABLE: OnceBool = OnceBool::new();
-    if !ENABLE.get_or_init(|| env::var("CHALK_DEBUG").is_ok()) {
+    static ENABLE: LazyLock<bool> = LazyLock::new(|| env::var("CHALK_DEBUG").is_ok());
+    if !*ENABLE {
         return None;
     }
 
@@ -94,7 +96,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
     let mut had_annotations = false;
     let mut mismatches = FxHashMap::default();
     let mut types = FxHashMap::default();
-    let mut adjustments = FxHashMap::<_, Vec<_>>::default();
+    let mut adjustments = FxHashMap::default();
     for (file_id, annotations) in db.extract_annotations() {
         for (range, expected) in annotations {
             let file_range = FileRange { file_id, range };
@@ -107,13 +109,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
             } else if expected.starts_with("adjustments:") {
                 adjustments.insert(
                     file_range,
-                    expected
-                        .trim_start_matches("adjustments:")
-                        .trim()
-                        .split(',')
-                        .map(|it| it.trim().to_owned())
-                        .filter(|it| !it.is_empty())
-                        .collect(),
+                    expected.trim_start_matches("adjustments:").trim().to_owned(),
                 );
             } else {
                 panic!("unexpected annotation: {expected}");
@@ -200,7 +196,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
                     adjustments
                         .iter()
                         .map(|Adjustment { kind, .. }| format!("{kind:?}"))
-                        .collect::<Vec<_>>()
+                        .join(", ")
                 );
             }
         }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs
new file mode 100644
index 0000000000000..22cef3505bf04
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs
@@ -0,0 +1,433 @@
+use base_db::salsa::InternKey;
+use expect_test::{expect, Expect};
+use hir_def::db::DefDatabase;
+use hir_expand::files::InFileWrapper;
+use itertools::Itertools;
+use span::{HirFileId, TextRange};
+use syntax::{AstNode, AstPtr};
+use test_fixture::WithFixture;
+
+use crate::db::{HirDatabase, InternedClosureId};
+use crate::display::HirDisplay;
+use crate::mir::MirSpan;
+use crate::test_db::TestDB;
+
+use super::visit_module;
+
+fn check_closure_captures(ra_fixture: &str, expect: Expect) {
+    let (db, file_id) = TestDB::with_single_file(ra_fixture);
+    let module = db.module_for_file(file_id);
+    let def_map = module.def_map(&db);
+
+    let mut defs = Vec::new();
+    visit_module(&db, &def_map, module.local_id, &mut |it| defs.push(it));
+
+    let mut captures_info = Vec::new();
+    for def in defs {
+        let infer = db.infer(def);
+        let db = &db;
+        captures_info.extend(infer.closure_info.iter().flat_map(|(closure_id, (captures, _))| {
+            let closure = db.lookup_intern_closure(InternedClosureId::from_intern_id(closure_id.0));
+            let (_, source_map) = db.body_with_source_map(closure.0);
+            let closure_text_range = source_map
+                .expr_syntax(closure.1)
+                .expect("failed to map closure to SyntaxNode")
+                .value
+                .text_range();
+            captures.iter().map(move |capture| {
+                fn text_range<N: AstNode>(
+                    db: &TestDB,
+                    syntax: InFileWrapper<HirFileId, AstPtr<N>>,
+                ) -> TextRange {
+                    let root = syntax.file_syntax(db);
+                    syntax.value.to_node(&root).syntax().text_range()
+                }
+
+                // FIXME: Deduplicate this with hir::Local::sources().
+                let (body, source_map) = db.body_with_source_map(closure.0);
+                let local_text_range = match body.self_param.zip(source_map.self_param_syntax()) {
+                    Some((param, source)) if param == capture.local() => {
+                        format!("{:?}", text_range(db, source))
+                    }
+                    _ => source_map
+                        .patterns_for_binding(capture.local())
+                        .iter()
+                        .map(|&definition| {
+                            text_range(db, source_map.pat_syntax(definition).unwrap())
+                        })
+                        .map(|it| format!("{it:?}"))
+                        .join(", "),
+                };
+                let place = capture.display_place(closure.0, db);
+                let capture_ty = capture.ty.skip_binders().display_test(db).to_string();
+                let spans = capture
+                    .spans()
+                    .iter()
+                    .flat_map(|span| match *span {
+                        MirSpan::ExprId(expr) => {
+                            vec![text_range(db, source_map.expr_syntax(expr).unwrap())]
+                        }
+                        MirSpan::PatId(pat) => {
+                            vec![text_range(db, source_map.pat_syntax(pat).unwrap())]
+                        }
+                        MirSpan::BindingId(binding) => source_map
+                            .patterns_for_binding(binding)
+                            .iter()
+                            .map(|pat| text_range(db, source_map.pat_syntax(*pat).unwrap()))
+                            .collect(),
+                        MirSpan::SelfParam => {
+                            vec![text_range(db, source_map.self_param_syntax().unwrap())]
+                        }
+                        MirSpan::Unknown => Vec::new(),
+                    })
+                    .sorted_by_key(|it| it.start())
+                    .map(|it| format!("{it:?}"))
+                    .join(",");
+
+                (closure_text_range, local_text_range, spans, place, capture_ty, capture.kind())
+            })
+        }));
+    }
+    captures_info.sort_unstable_by_key(|(closure_text_range, local_text_range, ..)| {
+        (closure_text_range.start(), local_text_range.clone())
+    });
+
+    let rendered = captures_info
+        .iter()
+        .map(|(closure_text_range, local_text_range, spans, place, capture_ty, capture_kind)| {
+            format!(
+                "{closure_text_range:?};{local_text_range};{spans} {capture_kind:?} {place} {capture_ty}"
+            )
+        })
+        .join("\n");
+
+    expect.assert_eq(&rendered);
+}
+
+#[test]
+fn deref_in_let() {
+    check_closure_captures(
+        r#"
+//- minicore:copy
+fn main() {
+    let a = &mut true;
+    let closure = || { let b = *a; };
+}
+"#,
+        expect!["53..71;20..21;66..68 ByRef(Shared) *a &'? bool"],
+    );
+}
+
+#[test]
+fn deref_then_ref_pattern() {
+    check_closure_captures(
+        r#"
+//- minicore:copy
+fn main() {
+    let a = &mut true;
+    let closure = || { let &mut ref b = a; };
+}
+"#,
+        expect!["53..79;20..21;67..72 ByRef(Shared) *a &'? bool"],
+    );
+    check_closure_captures(
+        r#"
+//- minicore:copy
+fn main() {
+    let a = &mut true;
+    let closure = || { let &mut ref mut b = a; };
+}
+"#,
+        expect!["53..83;20..21;67..76 ByRef(Mut { kind: Default }) *a &'? mut bool"],
+    );
+}
+
+#[test]
+fn unique_borrow() {
+    check_closure_captures(
+        r#"
+//- minicore:copy
+fn main() {
+    let a = &mut true;
+    let closure = || { *a = false; };
+}
+"#,
+        expect!["53..71;20..21;58..60 ByRef(Mut { kind: Default }) *a &'? mut bool"],
+    );
+}
+
+#[test]
+fn deref_ref_mut() {
+    check_closure_captures(
+        r#"
+//- minicore:copy
+fn main() {
+    let a = &mut true;
+    let closure = || { let ref mut b = *a; };
+}
+"#,
+        expect!["53..79;20..21;62..71 ByRef(Mut { kind: Default }) *a &'? mut bool"],
+    );
+}
+
+#[test]
+fn let_else_not_consuming() {
+    check_closure_captures(
+        r#"
+//- minicore:copy
+fn main() {
+    let a = &mut true;
+    let closure = || { let _ = *a else { return; }; };
+}
+"#,
+        expect!["53..88;20..21;66..68 ByRef(Shared) *a &'? bool"],
+    );
+}
+
+#[test]
+fn consume() {
+    check_closure_captures(
+        r#"
+//- minicore:copy
+struct NonCopy;
+fn main() {
+    let a = NonCopy;
+    let closure = || { let b = a; };
+}
+"#,
+        expect!["67..84;36..37;80..81 ByValue a NonCopy"],
+    );
+}
+
+#[test]
+fn ref_to_upvar() {
+    check_closure_captures(
+        r#"
+//- minicore:copy
+struct NonCopy;
+fn main() {
+    let mut a = NonCopy;
+    let closure = || { let b = &a; };
+    let closure = || { let c = &mut a; };
+}
+"#,
+        expect![[r#"
+            71..89;36..41;84..86 ByRef(Shared) a &'? NonCopy
+            109..131;36..41;122..128 ByRef(Mut { kind: Default }) a &'? mut NonCopy"#]],
+    );
+}
+
+#[test]
+fn field() {
+    check_closure_captures(
+        r#"
+//- minicore:copy
+struct Foo { a: i32, b: i32 }
+fn main() {
+    let a = Foo { a: 0, b: 0 };
+    let closure = || { let b = a.a; };
+}
+"#,
+        expect!["92..111;50..51;105..108 ByRef(Shared) a.a &'? i32"],
+    );
+}
+
+#[test]
+fn fields_different_mode() {
+    check_closure_captures(
+        r#"
+//- minicore:copy
+struct NonCopy;
+struct Foo { a: i32, b: i32, c: NonCopy, d: bool }
+fn main() {
+    let mut a = Foo { a: 0, b: 0 };
+    let closure = || {
+        let b = &a.a;
+        let c = &mut a.b;
+        let d = a.c;
+    };
+}
+"#,
+        expect![[r#"
+            133..212;87..92;154..158 ByRef(Shared) a.a &'? i32
+            133..212;87..92;176..184 ByRef(Mut { kind: Default }) a.b &'? mut i32
+            133..212;87..92;202..205 ByValue a.c NonCopy"#]],
+    );
+}
+
+#[test]
+fn autoref() {
+    check_closure_captures(
+        r#"
+//- minicore:copy
+struct Foo;
+impl Foo {
+    fn imm(&self) {}
+    fn mut_(&mut self) {}
+}
+fn main() {
+    let mut a = Foo;
+    let closure = || a.imm();
+    let closure = || a.mut_();
+}
+"#,
+        expect![[r#"
+            123..133;92..97;126..127 ByRef(Shared) a &'? Foo
+            153..164;92..97;156..157 ByRef(Mut { kind: Default }) a &'? mut Foo"#]],
+    );
+}
+
+#[test]
+fn captures_priority() {
+    check_closure_captures(
+        r#"
+//- minicore:copy
+struct NonCopy;
+fn main() {
+    let mut a = &mut true;
+    // Max ByRef(Mut { kind: Default })
+    let closure = || {
+        *a = false;
+        let b = &mut a;
+    };
+    // Max ByValue
+    let mut a = NonCopy;
+    let closure = || {
+        let b = a;
+        let c = &mut a;
+        let d = &a;
+    };
+}
+"#,
+        expect![[r#"
+            113..167;36..41;127..128,154..160 ByRef(Mut { kind: Default }) a &'? mut &'? mut bool
+            231..304;196..201;252..253,276..277,296..297 ByValue a NonCopy"#]],
+    );
+}
+
+#[test]
+fn let_underscore() {
+    check_closure_captures(
+        r#"
+//- minicore:copy
+fn main() {
+    let mut a = true;
+    let closure = || { let _ = a; };
+}
+"#,
+        expect![""],
+    );
+}
+
+#[test]
+fn match_wildcard() {
+    check_closure_captures(
+        r#"
+//- minicore:copy
+struct NonCopy;
+fn main() {
+    let mut a = NonCopy;
+    let closure = || match a {
+        _ => {}
+    };
+    let closure = || match a {
+        ref b => {}
+    };
+    let closure = || match a {
+        ref mut b => {}
+    };
+}
+"#,
+        expect![[r#"
+            125..163;36..41;134..135 ByRef(Shared) a &'? NonCopy
+            183..225;36..41;192..193 ByRef(Mut { kind: Default }) a &'? mut NonCopy"#]],
+    );
+}
+
+#[test]
+fn multiple_bindings() {
+    check_closure_captures(
+        r#"
+//- minicore:copy
+fn main() {
+    let mut a = false;
+    let mut closure = || { let (b | b) = a; };
+}
+"#,
+        expect!["57..80;20..25;76..77,76..77 ByRef(Shared) a &'? bool"],
+    );
+}
+
+#[test]
+fn multiple_usages() {
+    check_closure_captures(
+        r#"
+//- minicore:copy
+fn main() {
+    let mut a = false;
+    let mut closure = || {
+        let b = &a;
+        let c = &a;
+        let d = &mut a;
+        a = true;
+    };
+}
+"#,
+        expect!["57..149;20..25;78..80,98..100,118..124,134..135 ByRef(Mut { kind: Default }) a &'? mut bool"],
+    );
+}
+
+#[test]
+fn ref_then_deref() {
+    check_closure_captures(
+        r#"
+//- minicore:copy
+fn main() {
+    let mut a = false;
+    let mut closure = || { let b = *&mut a; };
+}
+"#,
+        expect!["57..80;20..25;71..77 ByRef(Mut { kind: Default }) a &'? mut bool"],
+    );
+}
+
+#[test]
+fn ref_of_ref() {
+    check_closure_captures(
+        r#"
+//- minicore:copy
+fn main() {
+    let mut a = &false;
+    let closure = || { let b = &a; };
+    let closure = || { let b = &mut a; };
+    let a = &mut false;
+    let closure = || { let b = &a; };
+    let closure = || { let b = &mut a; };
+}
+"#,
+        expect![[r#"
+            54..72;20..25;67..69 ByRef(Shared) a &'? &'? bool
+            92..114;20..25;105..111 ByRef(Mut { kind: Default }) a &'? mut &'? bool
+            158..176;124..125;171..173 ByRef(Shared) a &'? &'? mut bool
+            196..218;124..125;209..215 ByRef(Mut { kind: Default }) a &'? mut &'? mut bool"#]],
+    );
+}
+
+#[test]
+fn multiple_capture_usages() {
+    check_closure_captures(
+        r#"
+//- minicore:copy
+struct A { a: i32, b: bool }
+fn main() {
+    let mut a = A { a: 123, b: false };
+    let closure = |$0| {
+        let b = a.b;
+        a = A { a: 456, b: true };
+    };
+    closure();
+}
+"#,
+        expect!["99..165;49..54;120..121,133..134 ByRef(Mut { kind: Default }) a &'? mut A"],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
index 526db2af6dccb..908bbc248c604 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
@@ -49,7 +49,7 @@ fn let_stmt_coerce() {
 //- minicore: coerce_unsized
 fn test() {
     let x: &[isize] = &[1];
-                   // ^^^^ adjustments: Deref(None), Borrow(Ref(Not)), Pointer(Unsize)
+                   // ^^^^ adjustments: Deref(None), Borrow(Ref('?3, Not)), Pointer(Unsize)
     let x: *const [isize] = &[1];
                          // ^^^^ adjustments: Deref(None), Borrow(RawPtr(Not)), Pointer(Unsize)
 }
@@ -96,7 +96,7 @@ fn foo<T>(x: &[T]) -> &[T] { x }
 fn test() {
     let x = if true {
         foo(&[1])
-         // ^^^^ adjustments: Deref(None), Borrow(Ref(Not)), Pointer(Unsize)
+         // ^^^^ adjustments: Deref(None), Borrow(Ref('?8, Not)), Pointer(Unsize)
     } else {
         &[1]
     };
@@ -148,7 +148,7 @@ fn foo<T>(x: &[T]) -> &[T] { x }
 fn test(i: i32) {
     let x = match i {
         2 => foo(&[2]),
-              // ^^^^ adjustments: Deref(None), Borrow(Ref(Not)), Pointer(Unsize)
+              // ^^^^ adjustments: Deref(None), Borrow(Ref('?10, Not)), Pointer(Unsize)
         1 => &[1],
         _ => &[3],
     };
@@ -267,7 +267,7 @@ fn takes_ref_str(x: &str) {}
 fn returns_string() -> String { loop {} }
 fn test() {
     takes_ref_str(&{ returns_string() });
-               // ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref(Not))
+               // ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref('{error}, Not))
 }
 "#,
     );
@@ -849,8 +849,8 @@ impl core::cmp::PartialEq for Struct {
 }
 fn test() {
     Struct == Struct;
- // ^^^^^^ adjustments: Borrow(Ref(Not))
-           // ^^^^^^ adjustments: Borrow(Ref(Not))
+ // ^^^^^^ adjustments: Borrow(Ref('{error}, Not))
+           // ^^^^^^ adjustments: Borrow(Ref('{error}, Not))
 }",
     );
 }
@@ -866,7 +866,7 @@ impl core::ops::AddAssign for Struct {
 }
 fn test() {
     Struct += Struct;
- // ^^^^^^ adjustments: Borrow(Ref(Mut))
+ // ^^^^^^ adjustments: Borrow(Ref('{error}, Mut))
            // ^^^^^^ adjustments:
 }",
     );
@@ -880,7 +880,7 @@ fn adjust_index() {
 fn test() {
     let x = [1, 2, 3];
     x[2] = 6;
- // ^ adjustments: Borrow(Ref(Mut))
+ // ^ adjustments: Borrow(Ref('?8, Mut))
 }
     ",
     );
@@ -905,11 +905,11 @@ impl core::ops::IndexMut for StructMut {
 }
 fn test() {
     Struct[0];
- // ^^^^^^ adjustments: Borrow(Ref(Not))
+ // ^^^^^^ adjustments: Borrow(Ref('?2, Not))
     StructMut[0];
- // ^^^^^^^^^ adjustments: Borrow(Ref(Not))
+ // ^^^^^^^^^ adjustments: Borrow(Ref('?5, Not))
     &mut StructMut[0];
-      // ^^^^^^^^^ adjustments: Borrow(Ref(Mut))
+      // ^^^^^^^^^ adjustments: Borrow(Ref('?8, Mut))
 }",
     );
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
index 14e2e7465315c..610fc9b6b4562 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
@@ -1186,11 +1186,11 @@ fn test() {
             89..109 '{     ...     }': bool
             99..103 'true': bool
             123..167 '{     ...o(); }': ()
-            133..134 's': &'? S
+            133..134 's': &'static S
             137..151 'unsafe { f() }': &'static S
             146..147 'f': fn f() -> &'static S
             146..149 'f()': &'static S
-            157..158 's': &'? S
+            157..158 's': &'static S
             157..164 's.foo()': bool
         "#]],
     );
@@ -1847,9 +1847,9 @@ impl Foo {
 }
 fn test() {
     Foo.foo();
-  //^^^ adjustments: Borrow(Ref(Not))
+  //^^^ adjustments: Borrow(Ref('?1, Not))
     (&Foo).foo();
-  // ^^^^ adjustments: Deref(None), Borrow(Ref(Not))
+  // ^^^^ adjustments: Deref(None), Borrow(Ref('?3, Not))
 }
 "#,
     );
@@ -1863,7 +1863,7 @@ fn receiver_adjustment_unsize_array() {
 fn test() {
     let a = [1, 2, 3];
     a.len();
-} //^ adjustments: Borrow(Ref(Not)), Pointer(Unsize)
+} //^ adjustments: Borrow(Ref('?7, Not)), Pointer(Unsize)
 "#,
     );
 }
@@ -2076,7 +2076,7 @@ impl Foo {
 }
 fn test() {
     Box::new(Foo).foo();
-  //^^^^^^^^^^^^^ adjustments: Deref(None), Borrow(Ref(Not))
+  //^^^^^^^^^^^^^ adjustments: Deref(None), Borrow(Ref('?3, Not))
 }
 "#,
     );
@@ -2094,7 +2094,7 @@ impl Foo {
 use core::mem::ManuallyDrop;
 fn test() {
     ManuallyDrop::new(Foo).foo();
-  //^^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref(Not))
+  //^^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref('?4, Not))
 }
 "#,
     );
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 281983861207b..17fbe4db31795 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
@@ -2141,3 +2141,90 @@ fn test() {
 }"#,
     );
 }
+
+#[test]
+fn issue_17866() {
+    check_infer(
+        r#"
+trait T {
+    type A;
+}
+
+type Foo = <S as T>::A;
+
+fn main() {
+    Foo {};
+}
+"#,
+        expect![[r#"
+            60..75 '{     Foo {}; }': ()
+            66..72 'Foo {}': {unknown}
+        "#]],
+    );
+}
+
+#[test]
+fn issue_17711() {
+    check_infer(
+        r#"
+//- minicore: deref
+use core::ops::Deref;
+
+struct Struct<'a, T>(&'a T);
+
+trait Trait {}
+
+impl<'a, T: Deref<Target = impl Trait>> Struct<'a, T> {
+    fn foo(&self) -> &Self { self }
+
+    fn bar(&self) {
+        let _ = self.foo();
+    }
+
+}
+"#,
+        expect![[r#"
+            137..141 'self': &'? Struct<'a, T>
+            152..160 '{ self }': &'? Struct<'a, T>
+            154..158 'self': &'? Struct<'a, T>
+            174..178 'self': &'? Struct<'a, T>
+            180..215 '{     ...     }': ()
+            194..195 '_': &'? Struct<'?, T>
+            198..202 'self': &'? Struct<'a, T>
+            198..208 'self.foo()': &'? Struct<'?, T>
+        "#]],
+    );
+}
+
+#[test]
+fn issue_17767() {
+    check_infer(
+        r#"
+extern "C" {
+    type Foo<T>;
+}
+
+fn f() -> Foo {}
+"#,
+        expect![[r#"
+            47..49 '{}': Foo
+        "#]],
+    );
+}
+
+#[test]
+fn issue_17921() {
+    check_infer(
+        r#"
+//- minicore: future
+trait Foo {}
+type Bar = impl Foo;
+
+async fn f<A, B, C>() -> Bar {}
+"#,
+        expect![[r#"
+            64..66 '{}': ()
+            64..66 '{}': impl Future<Output = ()>
+        "#]],
+    );
+}
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 d83a34298eab0..1c6fa62e30ceb 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
@@ -1201,8 +1201,8 @@ fn infer_array() {
             209..215 '[1, 2]': [i32; 2]
             210..211 '1': i32
             213..214 '2': i32
-            225..226 'i': [&'? str; 2]
-            229..239 '["a", "b"]': [&'? str; 2]
+            225..226 'i': [&'static str; 2]
+            229..239 '["a", "b"]': [&'static str; 2]
             230..233 '"a"': &'static str
             235..238 '"b"': &'static str
             250..251 'b': [[&'? str; 1]; 2]
@@ -3686,3 +3686,36 @@ fn main() {
 "#,
     );
 }
+
+#[test]
+fn infer_bad_lang_item() {
+    check_infer(
+        r#"
+#[lang="eq"]
+pub trait Eq {
+    fn eq(&self, ) -> bool;
+
+}
+
+#[lang="shr"]
+pub trait Shr<RHS,Result> {
+    fn shr(&self, rhs: &RHS) -> Result;
+}
+
+fn test() -> bool {
+    1 >> 1;
+    1 == 1;
+}
+"#,
+        expect![[r#"
+            39..43 'self': &'? Self
+            114..118 'self': &'? Self
+            120..123 'rhs': &'? RHS
+            163..190 '{     ...= 1; }': bool
+            169..170 '1': i32
+            169..175 '1 >> 1': {unknown}
+            181..182 '1': i32
+            181..187 '1 == 1': {unknown}
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs
index db5fa3205778d..6cb59491fac82 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs
@@ -2,6 +2,7 @@
 use std::fmt::{self, Display};
 
 use itertools::Itertools;
+use span::Edition;
 
 use crate::{
     chalk_db, db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, mapping::from_chalk,
@@ -24,7 +25,7 @@ impl DebugContext<'_> {
             AdtId::UnionId(it) => self.0.union_data(it).name.clone(),
             AdtId::EnumId(it) => self.0.enum_data(it).name.clone(),
         };
-        name.display(self.0.upcast()).fmt(f)?;
+        name.display(self.0.upcast(), Edition::LATEST).fmt(f)?;
         Ok(())
     }
 
@@ -35,7 +36,7 @@ impl DebugContext<'_> {
     ) -> Result<(), fmt::Error> {
         let trait_: hir_def::TraitId = from_chalk_trait_id(id);
         let trait_data = self.0.trait_data(trait_);
-        trait_data.name.display(self.0.upcast()).fmt(f)?;
+        trait_data.name.display(self.0.upcast(), Edition::LATEST).fmt(f)?;
         Ok(())
     }
 
@@ -54,8 +55,8 @@ impl DebugContext<'_> {
         write!(
             fmt,
             "{}::{}",
-            trait_data.name.display(self.0.upcast()),
-            type_alias_data.name.display(self.0.upcast())
+            trait_data.name.display(self.0.upcast(), Edition::LATEST),
+            type_alias_data.name.display(self.0.upcast(), Edition::LATEST)
         )?;
         Ok(())
     }
@@ -75,7 +76,7 @@ impl DebugContext<'_> {
         let trait_ref = projection_ty.trait_ref(self.0);
         let trait_params = trait_ref.substitution.as_slice(Interner);
         let self_ty = trait_ref.self_type_parameter(Interner);
-        write!(fmt, "<{self_ty:?} as {}", trait_name.display(self.0.upcast()))?;
+        write!(fmt, "<{self_ty:?} as {}", trait_name.display(self.0.upcast(), Edition::LATEST))?;
         if trait_params.len() > 1 {
             write!(
                 fmt,
@@ -83,7 +84,7 @@ impl DebugContext<'_> {
                 trait_params[1..].iter().format_with(", ", |x, f| f(&format_args!("{x:?}"))),
             )?;
         }
-        write!(fmt, ">::{}", type_alias_data.name.display(self.0.upcast()))?;
+        write!(fmt, ">::{}", type_alias_data.name.display(self.0.upcast(), Edition::LATEST))?;
 
         let proj_params_count = projection_ty.substitution.len(Interner) - trait_params.len();
         let proj_params = &projection_ty.substitution.as_slice(Interner)[..proj_params_count];
@@ -110,9 +111,11 @@ impl DebugContext<'_> {
             CallableDefId::EnumVariantId(e) => self.0.enum_variant_data(e).name.clone(),
         };
         match def {
-            CallableDefId::FunctionId(_) => write!(fmt, "{{fn {}}}", name.display(self.0.upcast())),
+            CallableDefId::FunctionId(_) => {
+                write!(fmt, "{{fn {}}}", name.display(self.0.upcast(), Edition::LATEST))
+            }
             CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => {
-                write!(fmt, "{{ctor {}}}", name.display(self.0.upcast()))
+                write!(fmt, "{{ctor {}}}", name.display(self.0.upcast(), Edition::LATEST))
             }
         }
     }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs
index c46382a0ea822..51ccd4ef293f9 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs
@@ -14,13 +14,14 @@ use hir_def::{
 };
 use hir_expand::name::Name;
 use intern::sym;
-use stdx::panic_context;
+use span::Edition;
+use stdx::{never, panic_context};
 use triomphe::Arc;
 
 use crate::{
     db::HirDatabase, infer::unify::InferenceTable, utils::UnevaluatedConstEvaluatorFolder, AliasEq,
     AliasTy, Canonical, DomainGoal, Goal, Guidance, InEnvironment, Interner, ProjectionTy,
-    ProjectionTyExt, Solution, TraitRefExt, Ty, TyKind, WhereClause,
+    ProjectionTyExt, Solution, TraitRefExt, Ty, TyKind, TypeFlags, WhereClause,
 };
 
 /// This controls how much 'time' we give the Chalk solver before giving up.
@@ -90,6 +91,16 @@ pub(crate) fn normalize_projection_query(
     projection: ProjectionTy,
     env: Arc<TraitEnvironment>,
 ) -> Ty {
+    if projection.substitution.iter(Interner).any(|arg| {
+        arg.ty(Interner)
+            .is_some_and(|ty| ty.data(Interner).flags.intersects(TypeFlags::HAS_TY_INFER))
+    }) {
+        never!(
+            "Invoking `normalize_projection_query` with a projection type containing inference var"
+        );
+        return TyKind::Error.intern(Interner);
+    }
+
     let mut table = InferenceTable::new(db, env);
     let ty = table.normalize_projection_ty(projection);
     table.resolve_completely(ty)
@@ -104,7 +115,7 @@ pub(crate) fn trait_solve_query(
 ) -> Option<Solution> {
     let detail = match &goal.value.goal.data(Interner) {
         GoalData::DomainGoal(DomainGoal::Holds(WhereClause::Implemented(it))) => {
-            db.trait_data(it.hir_trait_id()).name.display(db.upcast()).to_string()
+            db.trait_data(it.hir_trait_id()).name.display(db.upcast(), Edition::LATEST).to_string()
         }
         GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(_))) => "alias_eq".to_owned(),
         _ => "??".to_owned(),
diff --git a/src/tools/rust-analyzer/crates/hir/Cargo.toml b/src/tools/rust-analyzer/crates/hir/Cargo.toml
index 324fa1c6a8571..26666d6feb085 100644
--- a/src/tools/rust-analyzer/crates/hir/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir/Cargo.toml
@@ -20,7 +20,6 @@ itertools.workspace = true
 smallvec.workspace = true
 tracing.workspace = true
 triomphe.workspace = true
-once_cell = "1.17.1"
 
 # local deps
 base-db.workspace = true
diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs
index 02d92620e05b6..af60c233e5519 100644
--- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs
@@ -328,11 +328,9 @@ fn doc_modpath_from_str(link: &str) -> Option<ModPath> {
         };
         let parts = first_segment.into_iter().chain(parts).map(|segment| match segment.parse() {
             Ok(idx) => Name::new_tuple_field(idx),
-            Err(_) => Name::new(
-                segment.split_once('<').map_or(segment, |it| it.0),
-                tt::IdentIsRaw::No,
-                SyntaxContextId::ROOT,
-            ),
+            Err(_) => {
+                Name::new(segment.split_once('<').map_or(segment, |it| it.0), SyntaxContextId::ROOT)
+            }
         });
         Some(ModPath::from_segments(kind, parts))
     };
diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs
index 12dd8b5bf4f5d..923dca646673c 100644
--- a/src/tools/rust-analyzer/crates/hir/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/display.rs
@@ -84,7 +84,7 @@ impl HirDisplay for Function {
         if let Some(abi) = &data.abi {
             write!(f, "extern \"{}\" ", abi.as_str())?;
         }
-        write!(f, "fn {}", data.name.display(f.db.upcast()))?;
+        write!(f, "fn {}", data.name.display(f.db.upcast(), f.edition()))?;
 
         write_generic_params(GenericDefId::FunctionId(self.id), f)?;
 
@@ -107,7 +107,7 @@ impl HirDisplay for Function {
                 first = false;
             }
             match local {
-                Some(name) => write!(f, "{}: ", name.display(f.db.upcast()))?,
+                Some(name) => write!(f, "{}: ", name.display(f.db.upcast(), f.edition()))?,
                 None => f.write_str("_: ")?,
             }
             type_ref.hir_fmt(f)?;
@@ -177,7 +177,7 @@ fn write_impl_header(impl_: &Impl, f: &mut HirFormatter<'_>) -> Result<(), HirDi
 
     if let Some(trait_) = impl_.trait_(db) {
         let trait_data = db.trait_data(trait_.id);
-        write!(f, " {} for", trait_data.name.display(db.upcast()))?;
+        write!(f, " {} for", trait_data.name.display(db.upcast(), f.edition()))?;
     }
 
     f.write_char(' ')?;
@@ -196,7 +196,7 @@ impl HirDisplay for SelfParam {
             {
                 f.write_char('&')?;
                 if let Some(lifetime) = lifetime {
-                    write!(f, "{} ", lifetime.name.display(f.db.upcast()))?;
+                    write!(f, "{} ", lifetime.name.display(f.db.upcast(), f.edition()))?;
                 }
                 if let hir_def::type_ref::Mutability::Mut = mut_ {
                     f.write_str("mut ")?;
@@ -227,7 +227,7 @@ impl HirDisplay for Struct {
         // FIXME: Render repr if its set explicitly?
         write_visibility(module_id, self.visibility(f.db), f)?;
         f.write_str("struct ")?;
-        write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
+        write!(f, "{}", self.name(f.db).display(f.db.upcast(), f.edition()))?;
         let def_id = GenericDefId::AdtId(AdtId::StructId(self.id));
         write_generic_params(def_id, f)?;
 
@@ -266,7 +266,7 @@ impl HirDisplay for Enum {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
         write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
         f.write_str("enum ")?;
-        write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
+        write!(f, "{}", self.name(f.db).display(f.db.upcast(), f.edition()))?;
         let def_id = GenericDefId::AdtId(AdtId::EnumId(self.id));
         write_generic_params(def_id, f)?;
 
@@ -283,7 +283,7 @@ impl HirDisplay for Union {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
         write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
         f.write_str("union ")?;
-        write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
+        write!(f, "{}", self.name(f.db).display(f.db.upcast(), f.edition()))?;
         let def_id = GenericDefId::AdtId(AdtId::UnionId(self.id));
         write_generic_params(def_id, f)?;
 
@@ -343,7 +343,7 @@ fn write_variants(
     } else {
         f.write_str("{\n")?;
         for variant in &variants[..count] {
-            write!(f, "    {}", variant.name(f.db).display(f.db.upcast()))?;
+            write!(f, "    {}", variant.name(f.db).display(f.db.upcast(), f.edition()))?;
             match variant.kind(f.db) {
                 StructKind::Tuple => {
                     let fields_str =
@@ -372,21 +372,21 @@ fn write_variants(
 impl HirDisplay for Field {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
         write_visibility(self.parent.module(f.db).id, self.visibility(f.db), f)?;
-        write!(f, "{}: ", self.name(f.db).display(f.db.upcast()))?;
+        write!(f, "{}: ", self.name(f.db).display(f.db.upcast(), f.edition()))?;
         self.ty(f.db).hir_fmt(f)
     }
 }
 
 impl HirDisplay for TupleField {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
-        write!(f, "pub {}: ", self.name().display(f.db.upcast()))?;
+        write!(f, "pub {}: ", self.name().display(f.db.upcast(), f.edition()))?;
         self.ty(f.db).hir_fmt(f)
     }
 }
 
 impl HirDisplay for Variant {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
-        write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
+        write!(f, "{}", self.name(f.db).display(f.db.upcast(), f.edition()))?;
         let data = self.variant_data(f.db);
         match &*data {
             VariantData::Unit => {}
@@ -424,9 +424,9 @@ impl HirDisplay for ExternCrateDecl {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
         write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
         f.write_str("extern crate ")?;
-        write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
+        write!(f, "{}", self.name(f.db).display(f.db.upcast(), f.edition()))?;
         if let Some(alias) = self.alias(f.db) {
-            write!(f, " as {alias}",)?;
+            write!(f, " as {}", alias.display(f.edition()))?;
         }
         Ok(())
     }
@@ -478,7 +478,7 @@ impl HirDisplay for TypeParam {
         match param_data {
             TypeOrConstParamData::TypeParamData(p) => match p.provenance {
                 TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => {
-                    write!(f, "{}", p.name.clone().unwrap().display(f.db.upcast()))?
+                    write!(f, "{}", p.name.clone().unwrap().display(f.db.upcast(), f.edition()))?
                 }
                 TypeParamProvenance::ArgumentImplTrait => {
                     return write_bounds_like_dyn_trait_with_prefix(
@@ -491,7 +491,7 @@ impl HirDisplay for TypeParam {
                 }
             },
             TypeOrConstParamData::ConstParamData(p) => {
-                write!(f, "{}", p.name.display(f.db.upcast()))?;
+                write!(f, "{}", p.name.display(f.db.upcast(), f.edition()))?;
             }
         }
 
@@ -525,13 +525,13 @@ impl HirDisplay for TypeParam {
 
 impl HirDisplay for LifetimeParam {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
-        write!(f, "{}", self.name(f.db).display(f.db.upcast()))
+        write!(f, "{}", self.name(f.db).display(f.db.upcast(), f.edition()))
     }
 }
 
 impl HirDisplay for ConstParam {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
-        write!(f, "const {}: ", self.name(f.db).display(f.db.upcast()))?;
+        write!(f, "const {}: ", self.name(f.db).display(f.db.upcast(), f.edition()))?;
         self.ty(f.db).hir_fmt(f)
     }
 }
@@ -563,7 +563,7 @@ fn write_generic_params(
     };
     for (_, lifetime) in params.iter_lt() {
         delim(f)?;
-        write!(f, "{}", lifetime.name.display(f.db.upcast()))?;
+        write!(f, "{}", lifetime.name.display(f.db.upcast(), f.edition()))?;
     }
     for (_, ty) in params.iter_type_or_consts() {
         if let Some(name) = &ty.name() {
@@ -573,7 +573,7 @@ fn write_generic_params(
                         continue;
                     }
                     delim(f)?;
-                    write!(f, "{}", name.display(f.db.upcast()))?;
+                    write!(f, "{}", name.display(f.db.upcast(), f.edition()))?;
                     if let Some(default) = &ty.default {
                         f.write_str(" = ")?;
                         default.hir_fmt(f)?;
@@ -581,12 +581,12 @@ fn write_generic_params(
                 }
                 TypeOrConstParamData::ConstParamData(c) => {
                     delim(f)?;
-                    write!(f, "const {}: ", name.display(f.db.upcast()))?;
+                    write!(f, "const {}: ", name.display(f.db.upcast(), f.edition()))?;
                     c.ty.hir_fmt(f)?;
 
                     if let Some(default) = &c.default {
                         f.write_str(" = ")?;
-                        write!(f, "{}", default.display(f.db.upcast()))?;
+                        write!(f, "{}", default.display(f.db.upcast(), f.edition()))?;
                     }
                 }
             }
@@ -639,7 +639,7 @@ fn write_where_predicates(
     let write_target = |target: &WherePredicateTypeTarget, f: &mut HirFormatter<'_>| match target {
         WherePredicateTypeTarget::TypeRef(ty) => ty.hir_fmt(f),
         WherePredicateTypeTarget::TypeOrConstParam(id) => match params[*id].name() {
-            Some(name) => write!(f, "{}", name.display(f.db.upcast())),
+            Some(name) => write!(f, "{}", name.display(f.db.upcast(), f.edition())),
             None => f.write_str("{unnamed}"),
         },
     };
@@ -668,12 +668,13 @@ fn write_where_predicates(
                 bound.hir_fmt(f)?;
             }
             Lifetime { target, bound } => {
-                let target = target.name.display(f.db.upcast());
-                let bound = bound.name.display(f.db.upcast());
+                let target = target.name.display(f.db.upcast(), f.edition());
+                let bound = bound.name.display(f.db.upcast(), f.edition());
                 write!(f, "{target}: {bound}")?;
             }
             ForLifetime { lifetimes, target, bound } => {
-                let lifetimes = lifetimes.iter().map(|it| it.display(f.db.upcast())).join(", ");
+                let lifetimes =
+                    lifetimes.iter().map(|it| it.display(f.db.upcast(), f.edition())).join(", ");
                 write!(f, "for<{lifetimes}> ")?;
                 write_target(target, f)?;
                 f.write_str(": ")?;
@@ -685,7 +686,9 @@ fn write_where_predicates(
             f.write_str(" + ")?;
             match nxt {
                 TypeBound { bound, .. } | ForLifetime { bound, .. } => bound.hir_fmt(f)?,
-                Lifetime { bound, .. } => write!(f, "{}", bound.name.display(f.db.upcast()))?,
+                Lifetime { bound, .. } => {
+                    write!(f, "{}", bound.name.display(f.db.upcast(), f.edition()))?
+                }
             }
         }
         f.write_str(",")?;
@@ -707,7 +710,7 @@ impl HirDisplay for Const {
         let data = db.const_data(self.id);
         f.write_str("const ")?;
         match &data.name {
-            Some(name) => write!(f, "{}: ", name.display(f.db.upcast()))?,
+            Some(name) => write!(f, "{}: ", name.display(f.db.upcast(), f.edition()))?,
             None => f.write_str("_: ")?,
         }
         data.type_ref.hir_fmt(f)?;
@@ -723,7 +726,7 @@ impl HirDisplay for Static {
         if data.mutable {
             f.write_str("mut ")?;
         }
-        write!(f, "{}: ", data.name.display(f.db.upcast()))?;
+        write!(f, "{}: ", data.name.display(f.db.upcast(), f.edition()))?;
         data.type_ref.hir_fmt(f)?;
         Ok(())
     }
@@ -777,7 +780,7 @@ fn write_trait_header(trait_: &Trait, f: &mut HirFormatter<'_>) -> Result<(), Hi
     if data.is_auto {
         f.write_str("auto ")?;
     }
-    write!(f, "trait {}", data.name.display(f.db.upcast()))?;
+    write!(f, "trait {}", data.name.display(f.db.upcast(), f.edition()))?;
     write_generic_params(GenericDefId::TraitId(trait_.id), f)?;
     Ok(())
 }
@@ -786,7 +789,7 @@ impl HirDisplay for TraitAlias {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
         write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
         let data = f.db.trait_alias_data(self.id);
-        write!(f, "trait {}", data.name.display(f.db.upcast()))?;
+        write!(f, "trait {}", data.name.display(f.db.upcast(), f.edition()))?;
         let def_id = GenericDefId::TraitAliasId(self.id);
         write_generic_params(def_id, f)?;
         f.write_str(" = ")?;
@@ -802,7 +805,7 @@ impl HirDisplay for TypeAlias {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
         write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
         let data = f.db.type_alias_data(self.id);
-        write!(f, "type {}", data.name.display(f.db.upcast()))?;
+        write!(f, "type {}", data.name.display(f.db.upcast(), f.edition()))?;
         let def_id = GenericDefId::TypeAliasId(self.id);
         write_generic_params(def_id, f)?;
         if !data.bounds.is_empty() {
@@ -822,7 +825,7 @@ impl HirDisplay for Module {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
         // FIXME: Module doesn't have visibility saved in data.
         match self.name(f.db) {
-            Some(name) => write!(f, "mod {}", name.display(f.db.upcast())),
+            Some(name) => write!(f, "mod {}", name.display(f.db.upcast(), f.edition())),
             None if self.is_crate_root() => match self.krate(f.db).display_name(f.db) {
                 Some(name) => write!(f, "extern crate {name}"),
                 None => f.write_str("extern crate {unknown}"),
@@ -839,6 +842,6 @@ impl HirDisplay for Macro {
             hir_def::MacroId::MacroRulesId(_) => f.write_str("macro_rules!"),
             hir_def::MacroId::ProcMacroId(_) => f.write_str("proc_macro"),
         }?;
-        write!(f, " {}", self.name(f.db).display(f.db.upcast()))
+        write!(f, " {}", self.name(f.db).display(f.db.upcast(), f.edition()))
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 1a3becdf50e9b..58340c74fea9d 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -78,7 +78,8 @@ use hir_ty::{
 use itertools::Itertools;
 use nameres::diagnostics::DefDiagnosticKind;
 use rustc_hash::FxHashSet;
-use span::{Edition, EditionedFileId, FileId, MacroCallId};
+use smallvec::SmallVec;
+use span::{Edition, EditionedFileId, FileId, MacroCallId, SyntaxContextId};
 use stdx::{impl_from, never};
 use syntax::{
     ast::{self, HasAttrs as _, HasGenericParams, HasName},
@@ -93,8 +94,7 @@ pub use crate::{
     diagnostics::*,
     has_source::HasSource,
     semantics::{
-        DescendPreference, PathResolution, Semantics, SemanticsImpl, SemanticsScope, TypeInfo,
-        VisibleTraits,
+        PathResolution, Semantics, SemanticsImpl, SemanticsScope, TypeInfo, VisibleTraits,
     },
 };
 pub use hir_ty::method_resolution::TyFingerprint;
@@ -340,13 +340,13 @@ impl ModuleDef {
         }
     }
 
-    pub fn canonical_path(&self, db: &dyn HirDatabase) -> Option<String> {
+    pub fn canonical_path(&self, db: &dyn HirDatabase, edition: Edition) -> Option<String> {
         let mut segments = vec![self.name(db)?];
         for m in self.module(db)?.path_to_root(db) {
             segments.extend(m.name(db))
         }
         segments.reverse();
-        Some(segments.iter().map(|it| it.display(db.upcast())).join("::"))
+        Some(segments.iter().map(|it| it.display(db.upcast(), edition)).join("::"))
     }
 
     pub fn canonical_module_path(
@@ -556,13 +556,14 @@ impl Module {
         style_lints: bool,
     ) {
         let _p = tracing::info_span!("Module::diagnostics", name = ?self.name(db)).entered();
+        let edition = db.crate_graph()[self.id.krate()].edition;
         let def_map = self.id.def_map(db.upcast());
         for diag in def_map.diagnostics() {
             if diag.in_module != self.id.local_id {
                 // FIXME: This is accidentally quadratic.
                 continue;
             }
-            emit_def_diagnostic(db, acc, diag);
+            emit_def_diagnostic(db, acc, diag, edition);
         }
 
         if !self.id.is_block_module() {
@@ -582,7 +583,7 @@ impl Module {
                 }
                 ModuleDef::Trait(t) => {
                     for diag in db.trait_data_with_diagnostics(t.id).1.iter() {
-                        emit_def_diagnostic(db, acc, diag);
+                        emit_def_diagnostic(db, acc, diag, edition);
                     }
 
                     for item in t.items(db) {
@@ -599,19 +600,19 @@ impl Module {
                     match adt {
                         Adt::Struct(s) => {
                             for diag in db.struct_data_with_diagnostics(s.id).1.iter() {
-                                emit_def_diagnostic(db, acc, diag);
+                                emit_def_diagnostic(db, acc, diag, edition);
                             }
                         }
                         Adt::Union(u) => {
                             for diag in db.union_data_with_diagnostics(u.id).1.iter() {
-                                emit_def_diagnostic(db, acc, diag);
+                                emit_def_diagnostic(db, acc, diag, edition);
                             }
                         }
                         Adt::Enum(e) => {
                             for v in e.variants(db) {
                                 acc.extend(ModuleDef::Variant(v).diagnostics(db, style_lints));
                                 for diag in db.enum_variant_data_with_diagnostics(v.id).1.iter() {
-                                    emit_def_diagnostic(db, acc, diag);
+                                    emit_def_diagnostic(db, acc, diag, edition);
                                 }
                             }
                         }
@@ -645,7 +646,7 @@ impl Module {
             let ast_id_map = db.ast_id_map(file_id);
 
             for diag in db.impl_data_with_diagnostics(impl_def.id).1.iter() {
-                emit_def_diagnostic(db, acc, diag);
+                emit_def_diagnostic(db, acc, diag, edition);
             }
 
             if inherent_impls.invalid_impls().contains(&impl_def.id) {
@@ -869,23 +870,32 @@ fn emit_macro_def_diagnostics(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>
                 never!("declarative expander for non decl-macro: {:?}", e);
                 return;
             };
+            let krate = HasModule::krate(&m.id, db.upcast());
+            let edition = db.crate_graph()[krate].edition;
             emit_def_diagnostic_(
                 db,
                 acc,
                 &DefDiagnosticKind::MacroDefError { ast, message: e.to_string() },
+                edition,
             );
         }
     }
 }
 
-fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag: &DefDiagnostic) {
-    emit_def_diagnostic_(db, acc, &diag.kind)
+fn emit_def_diagnostic(
+    db: &dyn HirDatabase,
+    acc: &mut Vec<AnyDiagnostic>,
+    diag: &DefDiagnostic,
+    edition: Edition,
+) {
+    emit_def_diagnostic_(db, acc, &diag.kind, edition)
 }
 
 fn emit_def_diagnostic_(
     db: &dyn HirDatabase,
     acc: &mut Vec<AnyDiagnostic>,
     diag: &DefDiagnosticKind,
+    edition: Edition,
 ) {
     match diag {
         DefDiagnosticKind::UnresolvedModule { ast: declaration, candidates } => {
@@ -910,7 +920,7 @@ fn emit_def_diagnostic_(
                 MacroError {
                     node: InFile::new(ast.file_id, item.syntax_node_ptr()),
                     precise_location: None,
-                    message: format!("{}: {message}", path.display(db.upcast())),
+                    message: format!("{}: {message}", path.display(db.upcast(), edition)),
                     error,
                 }
                 .into(),
@@ -1764,7 +1774,7 @@ impl DefWithBody {
     /// A textual representation of the HIR of this def's body for debugging purposes.
     pub fn debug_hir(self, db: &dyn HirDatabase) -> String {
         let body = db.body(self.id());
-        body.pretty_print(db.upcast(), self.id())
+        body.pretty_print(db.upcast(), self.id(), Edition::CURRENT)
     }
 
     /// A textual representation of the MIR of this def's body for debugging purposes.
@@ -2259,6 +2269,8 @@ impl Function {
         db: &dyn HirDatabase,
         span_formatter: impl Fn(FileId, TextRange) -> String,
     ) -> String {
+        let krate = HasModule::krate(&self.id, db.upcast());
+        let edition = db.crate_graph()[krate].edition;
         let body = match db.monomorphized_mir_body(
             self.id.into(),
             Substitution::empty(Interner),
@@ -2267,7 +2279,7 @@ impl Function {
             Ok(body) => body,
             Err(e) => {
                 let mut r = String::new();
-                _ = e.pretty_print(&mut r, db, &span_formatter);
+                _ = e.pretty_print(&mut r, db, &span_formatter, edition);
                 return r;
             }
         };
@@ -2276,7 +2288,7 @@ impl Function {
             Ok(_) => "pass".to_owned(),
             Err(e) => {
                 let mut r = String::new();
-                _ = e.pretty_print(&mut r, db, &span_formatter);
+                _ = e.pretty_print(&mut r, db, &span_formatter, edition);
                 r
             }
         };
@@ -2510,7 +2522,11 @@ impl Const {
         Type::from_value_def(db, self.id)
     }
 
-    pub fn render_eval(self, db: &dyn HirDatabase) -> Result<String, ConstEvalError> {
+    pub fn render_eval(
+        self,
+        db: &dyn HirDatabase,
+        edition: Edition,
+    ) -> Result<String, ConstEvalError> {
         let c = db.const_eval(self.id.into(), Substitution::empty(Interner), None)?;
         let data = &c.data(Interner);
         if let TyKind::Scalar(s) = data.ty.kind(Interner) {
@@ -2532,7 +2548,7 @@ impl Const {
         if let Ok(s) = mir::render_const_using_debug_impl(db, self.id, &c) {
             Ok(s)
         } else {
-            Ok(format!("{}", c.display(db)))
+            Ok(format!("{}", c.display(db, edition)))
         }
     }
 }
@@ -3728,9 +3744,9 @@ impl ConstParam {
         Type::new(db, self.id.parent(), db.const_param_ty(self.id))
     }
 
-    pub fn default(self, db: &dyn HirDatabase) -> Option<ast::ConstArg> {
+    pub fn default(self, db: &dyn HirDatabase, edition: Edition) -> Option<ast::ConstArg> {
         let arg = generic_arg_from_param(db, self.id.into())?;
-        known_const_to_ast(arg.constant(Interner)?, db)
+        known_const_to_ast(arg.constant(Interner)?, db, edition)
     }
 }
 
@@ -4038,12 +4054,20 @@ impl Closure {
         TyKind::Closure(self.id, self.subst).intern(Interner)
     }
 
-    pub fn display_with_id(&self, db: &dyn HirDatabase) -> String {
-        self.clone().as_ty().display(db).with_closure_style(ClosureStyle::ClosureWithId).to_string()
+    pub fn display_with_id(&self, db: &dyn HirDatabase, edition: Edition) -> String {
+        self.clone()
+            .as_ty()
+            .display(db, edition)
+            .with_closure_style(ClosureStyle::ClosureWithId)
+            .to_string()
     }
 
-    pub fn display_with_impl(&self, db: &dyn HirDatabase) -> String {
-        self.clone().as_ty().display(db).with_closure_style(ClosureStyle::ImplFn).to_string()
+    pub fn display_with_impl(&self, db: &dyn HirDatabase, edition: Edition) -> String {
+        self.clone()
+            .as_ty()
+            .display(db, edition)
+            .with_closure_style(ClosureStyle::ImplFn)
+            .to_string()
     }
 
     pub fn captured_items(&self, db: &dyn HirDatabase) -> Vec<ClosureCapture> {
@@ -4090,6 +4114,15 @@ impl ClosureCapture {
         Local { parent: self.owner, binding_id: self.capture.local() }
     }
 
+    /// Returns whether this place has any field (aka. non-deref) projections.
+    pub fn has_field_projections(&self) -> bool {
+        self.capture.has_field_projections()
+    }
+
+    pub fn usages(&self) -> CaptureUsages {
+        CaptureUsages { parent: self.owner, spans: self.capture.spans() }
+    }
+
     pub fn kind(&self) -> CaptureKind {
         match self.capture.kind() {
             hir_ty::CaptureKind::ByRef(
@@ -4105,11 +4138,21 @@ impl ClosureCapture {
         }
     }
 
+    /// Converts the place to a name that can be inserted into source code.
+    pub fn place_to_name(&self, db: &dyn HirDatabase) -> String {
+        self.capture.place_to_name(self.owner, db)
+    }
+
+    pub fn display_place_source_code(&self, db: &dyn HirDatabase) -> String {
+        self.capture.display_place_source_code(self.owner, db)
+    }
+
     pub fn display_place(&self, db: &dyn HirDatabase) -> String {
         self.capture.display_place(self.owner, db)
     }
 }
 
+#[derive(Clone, Copy, PartialEq, Eq)]
 pub enum CaptureKind {
     SharedRef,
     UniqueSharedRef,
@@ -4117,6 +4160,74 @@ pub enum CaptureKind {
     Move,
 }
 
+#[derive(Debug, Clone)]
+pub struct CaptureUsages {
+    parent: DefWithBodyId,
+    spans: SmallVec<[mir::MirSpan; 3]>,
+}
+
+impl CaptureUsages {
+    pub fn sources(&self, db: &dyn HirDatabase) -> Vec<CaptureUsageSource> {
+        let (body, source_map) = db.body_with_source_map(self.parent);
+        let mut result = Vec::with_capacity(self.spans.len());
+        for &span in self.spans.iter() {
+            let is_ref = span.is_ref_span(&body);
+            match span {
+                mir::MirSpan::ExprId(expr) => {
+                    if let Ok(expr) = source_map.expr_syntax(expr) {
+                        result.push(CaptureUsageSource {
+                            is_ref,
+                            source: expr.map(AstPtr::wrap_left),
+                        })
+                    }
+                }
+                mir::MirSpan::PatId(pat) => {
+                    if let Ok(pat) = source_map.pat_syntax(pat) {
+                        result.push(CaptureUsageSource {
+                            is_ref,
+                            source: pat.map(AstPtr::wrap_right),
+                        });
+                    }
+                }
+                mir::MirSpan::BindingId(binding) => result.extend(
+                    source_map
+                        .patterns_for_binding(binding)
+                        .iter()
+                        .filter_map(|&pat| source_map.pat_syntax(pat).ok())
+                        .map(|pat| CaptureUsageSource {
+                            is_ref,
+                            source: pat.map(AstPtr::wrap_right),
+                        }),
+                ),
+                mir::MirSpan::SelfParam | mir::MirSpan::Unknown => {
+                    unreachable!("invalid capture usage span")
+                }
+            }
+        }
+        result
+    }
+}
+
+#[derive(Debug)]
+pub struct CaptureUsageSource {
+    is_ref: bool,
+    source: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
+}
+
+impl CaptureUsageSource {
+    pub fn source(&self) -> AstPtr<Either<ast::Expr, ast::Pat>> {
+        self.source.value
+    }
+
+    pub fn file_id(&self) -> HirFileId {
+        self.source.file_id
+    }
+
+    pub fn is_ref(&self) -> bool {
+        self.is_ref
+    }
+}
+
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
 pub struct Type {
     env: Arc<TraitEnvironment>,
@@ -4355,6 +4466,22 @@ impl Type {
         method_resolution::implements_trait(&canonical_ty, db, &self.env, trait_)
     }
 
+    /// This does **not** resolve `IntoFuture`, only `Future`.
+    pub fn future_output(self, db: &dyn HirDatabase) -> Option<Type> {
+        let future_output =
+            db.lang_item(self.env.krate, LangItem::FutureOutput)?.as_type_alias()?;
+        self.normalize_trait_assoc_type(db, &[], future_output.into())
+    }
+
+    /// This does **not** resolve `IntoIterator`, only `Iterator`.
+    pub fn iterator_item(self, db: &dyn HirDatabase) -> Option<Type> {
+        let iterator_trait = db.lang_item(self.env.krate, LangItem::Iterator)?.as_trait()?;
+        let iterator_item = db
+            .trait_data(iterator_trait)
+            .associated_type_by_name(&Name::new_symbol(sym::Item.clone(), SyntaxContextId::ROOT))?;
+        self.normalize_trait_assoc_type(db, &[], iterator_item.into())
+    }
+
     /// Checks that particular type `ty` implements `std::ops::FnOnce`.
     ///
     /// This function can be used to check if a particular type is callable, since FnOnce is a
@@ -4704,18 +4831,20 @@ impl Type {
     pub fn type_and_const_arguments<'a>(
         &'a self,
         db: &'a dyn HirDatabase,
+        edition: Edition,
     ) -> impl Iterator<Item = SmolStr> + 'a {
         self.ty
             .strip_references()
             .as_adt()
             .into_iter()
             .flat_map(|(_, substs)| substs.iter(Interner))
-            .filter_map(|arg| {
+            .filter_map(move |arg| {
                 // arg can be either a `Ty` or `constant`
                 if let Some(ty) = arg.ty(Interner) {
-                    Some(format_smolstr!("{}", ty.display(db)))
+                    Some(format_smolstr!("{}", ty.display(db, edition)))
                 } else {
-                    arg.constant(Interner).map(|const_| format_smolstr!("{}", const_.display(db)))
+                    arg.constant(Interner)
+                        .map(|const_| format_smolstr!("{}", const_.display(db, edition)))
                 }
             })
     }
@@ -4724,13 +4853,17 @@ impl Type {
     pub fn generic_parameters<'a>(
         &'a self,
         db: &'a dyn HirDatabase,
+        edition: Edition,
     ) -> impl Iterator<Item = SmolStr> + 'a {
         // iterate the lifetime
         self.as_adt()
-            .and_then(|a| a.lifetime(db).map(|lt| lt.name.display_no_db().to_smolstr()))
+            .and_then(|a| {
+                // Lifetimes do not need edition-specific handling as they cannot be escaped.
+                a.lifetime(db).map(|lt| lt.name.display_no_db(Edition::Edition2015).to_smolstr())
+            })
             .into_iter()
             // add the type and const parameters
-            .chain(self.type_and_const_arguments(db))
+            .chain(self.type_and_const_arguments(db, edition))
     }
 
     pub fn iterate_method_candidates_with_traits<T>(
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index a377163162c2c..763f53031e4c5 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -4,6 +4,7 @@ mod source_to_def;
 
 use std::{
     cell::RefCell,
+    convert::Infallible,
     fmt, iter, mem,
     ops::{self, ControlFlow, Not},
 };
@@ -22,9 +23,11 @@ use hir_expand::{
     builtin::{BuiltinFnLikeExpander, EagerExpander},
     db::ExpandDatabase,
     files::InRealFile,
+    inert_attr_macro::find_builtin_attr_idx,
     name::AsName,
     FileRange, InMacroFile, MacroCallId, MacroFileId, MacroFileIdExt,
 };
+use intern::Symbol;
 use itertools::Itertools;
 use rustc_hash::{FxHashMap, FxHashSet};
 use smallvec::{smallvec, SmallVec};
@@ -48,11 +51,7 @@ use crate::{
     Variant, VariantDef,
 };
 
-pub enum DescendPreference {
-    SameText,
-    SameKind,
-    None,
-}
+const CONTINUE_NO_BREAKS: ControlFlow<Infallible, ()> = ControlFlow::Continue(());
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
 pub enum PathResolution {
@@ -182,6 +181,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
 
     /// Find an AstNode by offset inside SyntaxNode, if it is inside *MacroCall*,
     /// descend it and find again
+    // FIXME: Rethink this API
     pub fn find_node_at_offset_with_descend<N: AstNode>(
         &self,
         node: &SyntaxNode,
@@ -190,8 +190,9 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         self.imp.descend_node_at_offset(node, offset).flatten().find_map(N::cast)
     }
 
-    /// Find an AstNode by offset inside SyntaxNode, if it is inside *MacroCall*,
+    /// Find an AstNode by offset inside SyntaxNode, if it is inside an attribute macro call,
     /// descend it and find again
+    // FIXME: Rethink this API
     pub fn find_nodes_at_offset_with_descend<'slf, N: AstNode + 'slf>(
         &'slf self,
         node: &SyntaxNode,
@@ -545,51 +546,53 @@ impl<'db> SemanticsImpl<'db> {
         )
     }
 
+    /// Retrieves all the formatting parts of the format_args! template string.
     pub fn as_format_args_parts(
         &self,
         string: &ast::String,
     ) -> Option<Vec<(TextRange, Option<PathResolution>)>> {
-        if let Some(quote) = string.open_quote_text_range() {
-            return self
-                .descend_into_macros(DescendPreference::SameText, string.syntax().clone())
-                .into_iter()
-                .find_map(|token| {
-                    let string = ast::String::cast(token)?;
-                    let literal =
-                        string.syntax().parent().filter(|it| it.kind() == SyntaxKind::LITERAL)?;
-                    let format_args = ast::FormatArgsExpr::cast(literal.parent()?)?;
-                    let source_analyzer = self.analyze_no_infer(format_args.syntax())?;
-                    let format_args = self.wrap_node_infile(format_args);
-                    let res = source_analyzer
-                        .as_format_args_parts(self.db, format_args.as_ref())?
-                        .map(|(range, res)| (range + quote.end(), res))
-                        .collect();
-                    Some(res)
-                });
-        }
-        None
+        let quote = string.open_quote_text_range()?;
+
+        let token = self.wrap_token_infile(string.syntax().clone()).into_real_file().ok()?;
+        self.descend_into_macros_breakable(token, |token| {
+            (|| {
+                let token = token.value;
+                let string = ast::String::cast(token)?;
+                let literal =
+                    string.syntax().parent().filter(|it| it.kind() == SyntaxKind::LITERAL)?;
+                let format_args = ast::FormatArgsExpr::cast(literal.parent()?)?;
+                let source_analyzer = self.analyze_no_infer(format_args.syntax())?;
+                let format_args = self.wrap_node_infile(format_args);
+                let res = source_analyzer
+                    .as_format_args_parts(self.db, format_args.as_ref())?
+                    .map(|(range, res)| (range + quote.end(), res))
+                    .collect();
+                Some(res)
+            })()
+            .map_or(ControlFlow::Continue(()), ControlFlow::Break)
+        })
     }
 
+    /// Retrieves the formatting part of the format_args! template string at the given offset.
     pub fn check_for_format_args_template(
         &self,
         original_token: SyntaxToken,
         offset: TextSize,
     ) -> Option<(TextRange, Option<PathResolution>)> {
-        if let Some(original_string) = ast::String::cast(original_token.clone()) {
-            if let Some(quote) = original_string.open_quote_text_range() {
-                return self
-                    .descend_into_macros(DescendPreference::SameText, original_token)
-                    .into_iter()
-                    .find_map(|token| {
-                        self.resolve_offset_in_format_args(
-                            ast::String::cast(token)?,
-                            offset.checked_sub(quote.end())?,
-                        )
-                    })
-                    .map(|(range, res)| (range + quote.end(), res));
-            }
-        }
-        None
+        let original_string = ast::String::cast(original_token.clone())?;
+        let original_token = self.wrap_token_infile(original_token).into_real_file().ok()?;
+        let quote = original_string.open_quote_text_range()?;
+        self.descend_into_macros_breakable(original_token, |token| {
+            (|| {
+                let token = token.value;
+                self.resolve_offset_in_format_args(
+                    ast::String::cast(token)?,
+                    offset.checked_sub(quote.end())?,
+                )
+                .map(|(range, res)| (range + quote.end(), res))
+            })()
+            .map_or(ControlFlow::Continue(()), ControlFlow::Break)
+        })
     }
 
     fn resolve_offset_in_format_args(
@@ -619,30 +622,37 @@ impl<'db> SemanticsImpl<'db> {
             Some(it) => it,
             None => return res,
         };
+        let file = self.find_file(node.syntax());
+        let Some(file_id) = file.file_id.file_id() else {
+            return res;
+        };
 
         if first == last {
             // node is just the token, so descend the token
-            self.descend_into_macros_impl(first, &mut |InFile { value, .. }| {
-                if let Some(node) = value
-                    .parent_ancestors()
-                    .take_while(|it| it.text_range() == value.text_range())
-                    .find_map(N::cast)
-                {
-                    res.push(node)
-                }
-                ControlFlow::Continue(())
-            });
+            self.descend_into_macros_impl(
+                InRealFile::new(file_id, first),
+                &mut |InFile { value, .. }| {
+                    if let Some(node) = value
+                        .parent_ancestors()
+                        .take_while(|it| it.text_range() == value.text_range())
+                        .find_map(N::cast)
+                    {
+                        res.push(node)
+                    }
+                    CONTINUE_NO_BREAKS
+                },
+            );
         } else {
             // Descend first and last token, then zip them to look for the node they belong to
             let mut scratch: SmallVec<[_; 1]> = smallvec![];
-            self.descend_into_macros_impl(first, &mut |token| {
+            self.descend_into_macros_impl(InRealFile::new(file_id, first), &mut |token| {
                 scratch.push(token);
-                ControlFlow::Continue(())
+                CONTINUE_NO_BREAKS
             });
 
             let mut scratch = scratch.into_iter();
             self.descend_into_macros_impl(
-                last,
+                InRealFile::new(file_id, last),
                 &mut |InFile { value: last, file_id: last_fid }| {
                     if let Some(InFile { value: first, file_id: first_fid }) = scratch.next() {
                         if first_fid == last_fid {
@@ -659,130 +669,151 @@ impl<'db> SemanticsImpl<'db> {
                             }
                         }
                     }
-                    ControlFlow::Continue(())
+                    CONTINUE_NO_BREAKS
                 },
             );
         }
         res
     }
 
-    /// Descend the token into its macro call if it is part of one, returning the tokens in the
-    /// expansion that it is associated with.
-    pub fn descend_into_macros(
+    fn is_inside_macro_call(token: &SyntaxToken) -> bool {
+        token.parent_ancestors().any(|ancestor| {
+            if ast::MacroCall::can_cast(ancestor.kind()) {
+                return true;
+            }
+            // Check if it is an item (only items can have macro attributes) that has a non-builtin attribute.
+            let Some(item) = ast::Item::cast(ancestor) else { return false };
+            item.attrs().any(|attr| {
+                let Some(meta) = attr.meta() else { return false };
+                let Some(path) = meta.path() else { return false };
+                let Some(attr_name) = path.as_single_name_ref() else { return true };
+                let attr_name = attr_name.text();
+                let attr_name = attr_name.as_str();
+                attr_name == "derive" || find_builtin_attr_idx(&Symbol::intern(attr_name)).is_none()
+            })
+        })
+    }
+
+    pub fn descend_into_macros_exact_if_in_macro(
         &self,
-        mode: DescendPreference,
         token: SyntaxToken,
     ) -> SmallVec<[SyntaxToken; 1]> {
-        enum Dp<'t> {
-            SameText(&'t str),
-            SameKind(SyntaxKind),
-            None,
+        if Self::is_inside_macro_call(&token) {
+            self.descend_into_macros_exact(token)
+        } else {
+            smallvec![token]
         }
-        let fetch_kind = |token: &SyntaxToken| match token.parent() {
-            Some(node) => match node.kind() {
-                kind @ (SyntaxKind::NAME | SyntaxKind::NAME_REF) => kind,
-                _ => token.kind(),
-            },
-            None => token.kind(),
-        };
-        let mode = match mode {
-            DescendPreference::SameText => Dp::SameText(token.text()),
-            DescendPreference::SameKind => Dp::SameKind(fetch_kind(&token)),
-            DescendPreference::None => Dp::None,
-        };
+    }
+
+    pub fn descend_into_macros_cb(
+        &self,
+        token: SyntaxToken,
+        mut cb: impl FnMut(InFile<SyntaxToken>),
+    ) {
+        if let Ok(token) = self.wrap_token_infile(token).into_real_file() {
+            self.descend_into_macros_impl(token, &mut |t| {
+                cb(t);
+                CONTINUE_NO_BREAKS
+            });
+        }
+    }
+
+    pub fn descend_into_macros(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> {
         let mut res = smallvec![];
-        self.descend_into_macros_impl(token.clone(), &mut |InFile { value, .. }| {
-            let is_a_match = match mode {
-                Dp::SameText(text) => value.text() == text,
-                Dp::SameKind(preferred_kind) => {
-                    let kind = fetch_kind(&value);
-                    kind == preferred_kind
-                        // special case for derive macros
-                        || (preferred_kind == SyntaxKind::IDENT && kind == SyntaxKind::NAME_REF)
-                }
-                Dp::None => true,
-            };
-            if is_a_match {
-                res.push(value);
-            }
-            ControlFlow::Continue(())
-        });
+        if let Ok(token) = self.wrap_token_infile(token.clone()).into_real_file() {
+            self.descend_into_macros_impl(token, &mut |t| {
+                res.push(t.value);
+                CONTINUE_NO_BREAKS
+            });
+        }
         if res.is_empty() {
             res.push(token);
         }
         res
     }
 
-    pub fn descend_into_macros_single(
+    pub fn descend_into_macros_breakable<T>(
         &self,
-        mode: DescendPreference,
-        token: SyntaxToken,
-    ) -> SyntaxToken {
-        enum Dp<'t> {
-            SameText(&'t str),
-            SameKind(SyntaxKind),
-            None,
-        }
-        let fetch_kind = |token: &SyntaxToken| match token.parent() {
-            Some(node) => match node.kind() {
-                kind @ (SyntaxKind::NAME | SyntaxKind::NAME_REF) => kind,
-                _ => token.kind(),
-            },
-            None => token.kind(),
-        };
-        let mode = match mode {
-            DescendPreference::SameText => Dp::SameText(token.text()),
-            DescendPreference::SameKind => Dp::SameKind(fetch_kind(&token)),
-            DescendPreference::None => Dp::None,
-        };
-        let mut res = token.clone();
-        self.descend_into_macros_impl(token.clone(), &mut |InFile { value, .. }| {
-            let is_a_match = match mode {
-                Dp::SameText(text) => value.text() == text,
-                Dp::SameKind(preferred_kind) => {
-                    let kind = fetch_kind(&value);
-                    kind == preferred_kind
-                        // special case for derive macros
-                        || (preferred_kind == SyntaxKind::IDENT && kind == SyntaxKind::NAME_REF)
-                }
-                Dp::None => true,
-            };
-            res = value;
-            if is_a_match {
-                ControlFlow::Break(())
-            } else {
-                ControlFlow::Continue(())
+        token: InRealFile<SyntaxToken>,
+        mut cb: impl FnMut(InFile<SyntaxToken>) -> ControlFlow<T>,
+    ) -> Option<T> {
+        self.descend_into_macros_impl(token.clone(), &mut cb)
+    }
+
+    /// Descends the token into expansions, returning the tokens that matches the input
+    /// token's [`SyntaxKind`] and text.
+    pub fn descend_into_macros_exact(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> {
+        let mut r = smallvec![];
+        let text = token.text();
+        let kind = token.kind();
+
+        self.descend_into_macros_cb(token.clone(), |InFile { value, file_id: _ }| {
+            let mapped_kind = value.kind();
+            let any_ident_match = || kind.is_any_identifier() && value.kind().is_any_identifier();
+            let matches = (kind == mapped_kind || any_ident_match()) && text == value.text();
+            if matches {
+                r.push(value);
             }
         });
-        res
+        if r.is_empty() {
+            r.push(token);
+        }
+        r
+    }
+
+    /// Descends the token into expansions, returning the first token that matches the input
+    /// token's [`SyntaxKind`] and text.
+    pub fn descend_into_macros_single_exact(&self, token: SyntaxToken) -> SyntaxToken {
+        let text = token.text();
+        let kind = token.kind();
+        if let Ok(token) = self.wrap_token_infile(token.clone()).into_real_file() {
+            self.descend_into_macros_breakable(token.clone(), |InFile { value, file_id: _ }| {
+                let mapped_kind = value.kind();
+                let any_ident_match =
+                    || kind.is_any_identifier() && value.kind().is_any_identifier();
+                let matches = (kind == mapped_kind || any_ident_match()) && text == value.text();
+                if matches {
+                    ControlFlow::Break(value)
+                } else {
+                    ControlFlow::Continue(())
+                }
+            })
+        } else {
+            None
+        }
+        .unwrap_or(token)
     }
 
-    fn descend_into_macros_impl(
+    fn descend_into_macros_impl<T>(
         &self,
-        token: SyntaxToken,
-        f: &mut dyn FnMut(InFile<SyntaxToken>) -> ControlFlow<()>,
-    ) {
+        InRealFile { value: token, file_id }: InRealFile<SyntaxToken>,
+        f: &mut dyn FnMut(InFile<SyntaxToken>) -> ControlFlow<T>,
+    ) -> Option<T> {
         let _p = tracing::info_span!("descend_into_macros_impl").entered();
-        let (sa, span, file_id) =
-            match token.parent().and_then(|parent| self.analyze_no_infer(&parent)) {
-                Some(sa) => match sa.file_id.file_id() {
-                    Some(file_id) => (
-                        sa,
-                        self.db.real_span_map(file_id).span_for_range(token.text_range()),
-                        file_id.into(),
-                    ),
-                    None => {
-                        stdx::never!();
-                        return;
-                    }
-                },
-                None => return,
-            };
+        let (sa, span, file_id) = token
+            .parent()
+            .and_then(|parent| {
+                self.analyze_impl(InRealFile::new(file_id, &parent).into(), None, false)
+            })
+            .and_then(|sa| {
+                let file_id = sa.file_id.file_id()?;
+                Some((
+                    sa,
+                    self.db.real_span_map(file_id).span_for_range(token.text_range()),
+                    HirFileId::from(file_id),
+                ))
+            })?;
 
         let mut m_cache = self.macro_call_cache.borrow_mut();
         let def_map = sa.resolver.def_map();
 
+        // A stack of tokens to process, along with the file they came from
+        // These are tracked to know which macro calls we still have to look into
+        // the tokens themselves aren't that interesting as the span that is being used to map
+        // things down never changes.
         let mut stack: Vec<(_, SmallVec<[_; 2]>)> = vec![(file_id, smallvec![token])];
+
+        // Process the expansion of a call, pushing all tokens with our span in the expansion back onto our stack
         let process_expansion_for_token = |stack: &mut Vec<_>, macro_file| {
             let InMacroFile { file_id, value: mapped_tokens } = self.with_ctx(|ctx| {
                 Some(
@@ -809,7 +840,13 @@ impl<'db> SemanticsImpl<'db> {
             res
         };
 
-        while let Some((file_id, mut tokens)) = stack.pop() {
+        // Filters out all tokens that contain the given range (usually the macro call), any such
+        // token is redundant as the corresponding macro call has already been processed
+        let filter_duplicates = |tokens: &mut SmallVec<_>, range: TextRange| {
+            tokens.retain(|t: &mut SyntaxToken| !range.contains_range(t.text_range()))
+        };
+
+        while let Some((expansion, ref mut tokens)) = stack.pop() {
             while let Some(token) = tokens.pop() {
                 let was_not_remapped = (|| {
                     // First expand into attribute invocations
@@ -817,7 +854,7 @@ impl<'db> SemanticsImpl<'db> {
                         token.parent_ancestors().filter_map(ast::Item::cast).find_map(|item| {
                             // Don't force populate the dyn cache for items that don't have an attribute anyways
                             item.attrs().next()?;
-                            Some((ctx.item_to_macro_call(InFile::new(file_id, &item))?, item))
+                            Some((ctx.item_to_macro_call(InFile::new(expansion, &item))?, item))
                         })
                     });
                     if let Some((call_id, item)) = containing_attribute_macro_call {
@@ -849,9 +886,7 @@ impl<'db> SemanticsImpl<'db> {
                             })
                             .unwrap_or_else(|| text_range.start());
                         let text_range = TextRange::new(start, text_range.end());
-                        // remove any other token in this macro input, all their mappings are the
-                        // same as this one
-                        tokens.retain(|t| !text_range.contains_range(t.text_range()));
+                        filter_duplicates(tokens, text_range);
                         return process_expansion_for_token(&mut stack, file_id);
                     }
 
@@ -862,6 +897,7 @@ impl<'db> SemanticsImpl<'db> {
                         .map_while(Either::<ast::TokenTree, ast::Meta>::cast)
                         .last()?;
                     match tt {
+                        // function-like macro call
                         Either::Left(tt) => {
                             if tt.left_delimiter_token().map_or(false, |it| it == token) {
                                 return None;
@@ -870,7 +906,7 @@ impl<'db> SemanticsImpl<'db> {
                                 return None;
                             }
                             let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?;
-                            let mcall = InFile::new(file_id, macro_call);
+                            let mcall = InFile::new(expansion, macro_call);
                             let file_id = match m_cache.get(&mcall) {
                                 Some(&it) => it,
                                 None => {
@@ -888,9 +924,7 @@ impl<'db> SemanticsImpl<'db> {
                                 }
                             };
                             let text_range = tt.syntax().text_range();
-                            // remove any other token in this macro input, all their mappings are the
-                            // same as this one
-                            tokens.retain(|t| !text_range.contains_range(t.text_range()));
+                            filter_duplicates(tokens, text_range);
 
                             process_expansion_for_token(&mut stack, file_id).or(file_id
                                 .eager_arg(self.db.upcast())
@@ -899,6 +933,7 @@ impl<'db> SemanticsImpl<'db> {
                                     process_expansion_for_token(&mut stack, arg.as_macro_file())
                                 }))
                         }
+                        // derive or derive helper
                         Either::Right(meta) => {
                             // attribute we failed expansion for earlier, this might be a derive invocation
                             // or derive helper attribute
@@ -910,8 +945,8 @@ impl<'db> SemanticsImpl<'db> {
                                         // so try downmapping the token into the pseudo derive expansion
                                         // see [hir_expand::builtin_attr_macro] for how the pseudo derive expansion works
                                         ctx.attr_to_derive_macro_call(
-                                            InFile::new(file_id, &adt),
-                                            InFile::new(file_id, attr.clone()),
+                                            InFile::new(expansion, &adt),
+                                            InFile::new(expansion, attr.clone()),
                                         )
                                         .map(|(_, call_id, _)| call_id)
                                     });
@@ -945,28 +980,29 @@ impl<'db> SemanticsImpl<'db> {
                                     )
                                 }
                             }?;
-                            if !self.with_ctx(|ctx| ctx.has_derives(InFile::new(file_id, &adt))) {
+                            if !self.with_ctx(|ctx| ctx.has_derives(InFile::new(expansion, &adt))) {
                                 return None;
                             }
                             let attr_name =
                                 attr.path().and_then(|it| it.as_single_name_ref())?.as_name();
-                            // Not an attribute, nor a derive, so it's either a builtin or a derive helper
+                            // Not an attribute, nor a derive, so it's either an intert attribute or a derive helper
                             // Try to resolve to a derive helper and downmap
-                            let id = self.db.ast_id_map(file_id).ast_id(&adt);
+                            let id = self.db.ast_id_map(expansion).ast_id(&adt);
                             let helpers =
-                                def_map.derive_helpers_in_scope(InFile::new(file_id, id))?;
+                                def_map.derive_helpers_in_scope(InFile::new(expansion, id))?;
 
                             if !helpers.is_empty() {
                                 let text_range = attr.syntax().text_range();
-                                // remove any other token in this macro input, all their mappings are the
-                                // same as this
-                                tokens.retain(|t| !text_range.contains_range(t.text_range()));
+                                filter_duplicates(tokens, text_range);
                             }
 
                             let mut res = None;
                             for (.., derive) in
                                 helpers.iter().filter(|(helper, ..)| *helper == attr_name)
                             {
+                                // as there may be multiple derives registering the same helper
+                                // name, we gotta make sure to call this for all of them!
+                                // FIXME: We need to call `f` for all of them as well though!
                                 res = res.or(process_expansion_for_token(
                                     &mut stack,
                                     derive.as_macro_file(),
@@ -978,11 +1014,14 @@ impl<'db> SemanticsImpl<'db> {
                 })()
                 .is_none();
 
-                if was_not_remapped && f(InFile::new(file_id, token)).is_break() {
-                    break;
+                if was_not_remapped {
+                    if let ControlFlow::Break(b) = f(InFile::new(expansion, token)) {
+                        return Some(b);
+                    }
                 }
             }
         }
+        None
     }
 
     // Note this return type is deliberate as [`find_nodes_at_offset_with_descend`] wants to stop
@@ -995,7 +1034,7 @@ impl<'db> SemanticsImpl<'db> {
         offset: TextSize,
     ) -> impl Iterator<Item = impl Iterator<Item = SyntaxNode> + '_> + '_ {
         node.token_at_offset(offset)
-            .map(move |token| self.descend_into_macros(DescendPreference::None, token))
+            .map(move |token| self.descend_into_macros_exact(token))
             .map(|descendants| {
                 descendants.into_iter().map(move |it| self.token_ancestors_with_macros(it))
             })
@@ -1179,7 +1218,8 @@ impl<'db> SemanticsImpl<'db> {
                         hir_ty::Adjust::Borrow(hir_ty::AutoBorrow::RawPtr(m)) => {
                             Adjust::Borrow(AutoBorrow::RawPtr(mutability(m)))
                         }
-                        hir_ty::Adjust::Borrow(hir_ty::AutoBorrow::Ref(m)) => {
+                        hir_ty::Adjust::Borrow(hir_ty::AutoBorrow::Ref(_, m)) => {
+                            // FIXME: Handle lifetimes here
                             Adjust::Borrow(AutoBorrow::Ref(mutability(m)))
                         }
                         hir_ty::Adjust::Pointer(pc) => Adjust::Pointer(pc),
@@ -1413,11 +1453,13 @@ impl<'db> SemanticsImpl<'db> {
 
     /// Returns none if the file of the node is not part of a crate.
     fn analyze(&self, node: &SyntaxNode) -> Option<SourceAnalyzer> {
+        let node = self.find_file(node);
         self.analyze_impl(node, None, true)
     }
 
     /// Returns none if the file of the node is not part of a crate.
     fn analyze_no_infer(&self, node: &SyntaxNode) -> Option<SourceAnalyzer> {
+        let node = self.find_file(node);
         self.analyze_impl(node, None, false)
     }
 
@@ -1426,17 +1468,17 @@ impl<'db> SemanticsImpl<'db> {
         node: &SyntaxNode,
         offset: TextSize,
     ) -> Option<SourceAnalyzer> {
+        let node = self.find_file(node);
         self.analyze_impl(node, Some(offset), false)
     }
 
     fn analyze_impl(
         &self,
-        node: &SyntaxNode,
+        node: InFile<&SyntaxNode>,
         offset: Option<TextSize>,
         infer_body: bool,
     ) -> Option<SourceAnalyzer> {
         let _p = tracing::info_span!("SemanticsImpl::analyze_impl").entered();
-        let node = self.find_file(node);
 
         let container = self.with_ctx(|ctx| ctx.find_container(node))?;
 
@@ -1481,6 +1523,11 @@ impl<'db> SemanticsImpl<'db> {
         InFile::new(file_id, node)
     }
 
+    fn wrap_token_infile(&self, token: SyntaxToken) -> InFile<SyntaxToken> {
+        let InFile { file_id, .. } = self.find_file(&token.parent().unwrap());
+        InFile::new(file_id, token)
+    }
+
     /// Wraps the node in a [`InFile`] with the file id it belongs to.
     fn find_file<'node>(&self, node: &'node SyntaxNode) -> InFile<&'node SyntaxNode> {
         let root_node = find_root(node);
diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
index b1f5df681f2c1..cabb7e3db3d92 100644
--- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
@@ -9,6 +9,7 @@ use hir_def::{
 };
 use hir_expand::HirFileId;
 use hir_ty::{db::HirDatabase, display::HirDisplay};
+use span::Edition;
 use syntax::{ast::HasName, AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, ToSmolStr};
 
 use crate::{Module, ModuleDef, Semantics};
@@ -54,6 +55,7 @@ pub struct SymbolCollector<'a> {
     symbols: Vec<FileSymbol>,
     work: Vec<SymbolCollectorWork>,
     current_container_name: Option<SmolStr>,
+    edition: Edition,
 }
 
 /// Given a [`ModuleId`] and a [`HirDatabase`], use the DefMap for the module's crate to collect
@@ -65,10 +67,13 @@ impl<'a> SymbolCollector<'a> {
             symbols: Default::default(),
             work: Default::default(),
             current_container_name: None,
+            edition: Edition::Edition2015,
         }
     }
 
     pub fn collect(&mut self, module: Module) {
+        self.edition = module.krate().edition(self.db);
+
         // The initial work is the root module we're collecting, additional work will
         // be populated as we traverse the module's definitions.
         self.work.push(SymbolCollectorWork { module_id: module.into(), parent: None });
@@ -209,7 +214,8 @@ impl<'a> SymbolCollector<'a> {
 
     fn collect_from_impl(&mut self, impl_id: ImplId) {
         let impl_data = self.db.impl_data(impl_id);
-        let impl_name = Some(SmolStr::new(impl_data.self_ty.display(self.db).to_string()));
+        let impl_name =
+            Some(SmolStr::new(impl_data.self_ty.display(self.db, self.edition).to_string()));
         self.with_container_name(impl_name, |s| {
             for &assoc_item_id in impl_data.items.iter() {
                 s.push_assoc_item(assoc_item_id)
@@ -239,16 +245,16 @@ impl<'a> SymbolCollector<'a> {
     fn def_with_body_id_name(&self, body_id: DefWithBodyId) -> Option<SmolStr> {
         match body_id {
             DefWithBodyId::FunctionId(id) => {
-                Some(self.db.function_data(id).name.display_no_db().to_smolstr())
+                Some(self.db.function_data(id).name.display_no_db(self.edition).to_smolstr())
             }
             DefWithBodyId::StaticId(id) => {
-                Some(self.db.static_data(id).name.display_no_db().to_smolstr())
+                Some(self.db.static_data(id).name.display_no_db(self.edition).to_smolstr())
             }
             DefWithBodyId::ConstId(id) => {
-                Some(self.db.const_data(id).name.as_ref()?.display_no_db().to_smolstr())
+                Some(self.db.const_data(id).name.as_ref()?.display_no_db(self.edition).to_smolstr())
             }
             DefWithBodyId::VariantId(id) => {
-                Some(self.db.enum_variant_data(id).name.display_no_db().to_smolstr())
+                Some(self.db.enum_variant_data(id).name.display_no_db(self.edition).to_smolstr())
             }
             DefWithBodyId::InTypeConstId(_) => Some("in type const".into()),
         }
diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs
index 0c8f6932c7170..6ad074e8e5c8f 100644
--- a/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs
@@ -7,6 +7,7 @@ use hir_ty::{
     display::{DisplaySourceCodeError, HirDisplay},
 };
 use itertools::Itertools;
+use span::Edition;
 
 use crate::{
     Adt, AsAssocItem, AssocItemContainer, Const, ConstParam, Field, Function, Local, ModuleDef,
@@ -29,9 +30,10 @@ fn mod_item_path_str(
     sema_scope: &SemanticsScope<'_>,
     def: &ModuleDef,
     cfg: ImportPathConfig,
+    edition: Edition,
 ) -> Result<String, DisplaySourceCodeError> {
     let path = mod_item_path(sema_scope, def, cfg);
-    path.map(|it| it.display(sema_scope.db.upcast()).to_string())
+    path.map(|it| it.display(sema_scope.db.upcast(), edition).to_string())
         .ok_or(DisplaySourceCodeError::PathNotFound)
 }
 
@@ -97,37 +99,38 @@ impl Expr {
         sema_scope: &SemanticsScope<'_>,
         many_formatter: &mut dyn FnMut(&Type) -> String,
         cfg: ImportPathConfig,
+        edition: Edition,
     ) -> Result<String, DisplaySourceCodeError> {
         let db = sema_scope.db;
-        let mod_item_path_str = |s, def| mod_item_path_str(s, def, cfg);
+        let mod_item_path_str = |s, def| mod_item_path_str(s, def, cfg, edition);
         match self {
             Expr::Const(it) => match it.as_assoc_item(db).map(|it| it.container(db)) {
                 Some(container) => {
-                    let container_name = container_name(container, sema_scope, cfg)?;
+                    let container_name = container_name(container, sema_scope, cfg, edition)?;
                     let const_name = it
                         .name(db)
-                        .map(|c| c.display(db.upcast()).to_string())
+                        .map(|c| c.display(db.upcast(), edition).to_string())
                         .unwrap_or(String::new());
                     Ok(format!("{container_name}::{const_name}"))
                 }
                 None => mod_item_path_str(sema_scope, &ModuleDef::Const(*it)),
             },
             Expr::Static(it) => mod_item_path_str(sema_scope, &ModuleDef::Static(*it)),
-            Expr::Local(it) => Ok(it.name(db).display(db.upcast()).to_string()),
-            Expr::ConstParam(it) => Ok(it.name(db).display(db.upcast()).to_string()),
+            Expr::Local(it) => Ok(it.name(db).display(db.upcast(), edition).to_string()),
+            Expr::ConstParam(it) => Ok(it.name(db).display(db.upcast(), edition).to_string()),
             Expr::FamousType { value, .. } => Ok(value.to_string()),
             Expr::Function { func, params, .. } => {
                 let args = params
                     .iter()
-                    .map(|f| f.gen_source_code(sema_scope, many_formatter, cfg))
+                    .map(|f| f.gen_source_code(sema_scope, many_formatter, cfg, edition))
                     .collect::<Result<Vec<String>, DisplaySourceCodeError>>()?
                     .into_iter()
                     .join(", ");
 
                 match func.as_assoc_item(db).map(|it| it.container(db)) {
                     Some(container) => {
-                        let container_name = container_name(container, sema_scope, cfg)?;
-                        let fn_name = func.name(db).display(db.upcast()).to_string();
+                        let container_name = container_name(container, sema_scope, cfg, edition)?;
+                        let fn_name = func.name(db).display(db.upcast(), edition).to_string();
                         Ok(format!("{container_name}::{fn_name}({args})"))
                     }
                     None => {
@@ -141,12 +144,13 @@ impl Expr {
                     return Ok(many_formatter(&target.ty(db)));
                 }
 
-                let func_name = func.name(db).display(db.upcast()).to_string();
+                let func_name = func.name(db).display(db.upcast(), edition).to_string();
                 let self_param = func.self_param(db).unwrap();
-                let target_str = target.gen_source_code(sema_scope, many_formatter, cfg)?;
+                let target_str =
+                    target.gen_source_code(sema_scope, many_formatter, cfg, edition)?;
                 let args = params
                     .iter()
-                    .map(|f| f.gen_source_code(sema_scope, many_formatter, cfg))
+                    .map(|f| f.gen_source_code(sema_scope, many_formatter, cfg, edition))
                     .collect::<Result<Vec<String>, DisplaySourceCodeError>>()?
                     .into_iter()
                     .join(", ");
@@ -176,7 +180,7 @@ impl Expr {
                     StructKind::Tuple => {
                         let args = params
                             .iter()
-                            .map(|f| f.gen_source_code(sema_scope, many_formatter, cfg))
+                            .map(|f| f.gen_source_code(sema_scope, many_formatter, cfg, edition))
                             .collect::<Result<Vec<String>, DisplaySourceCodeError>>()?
                             .into_iter()
                             .join(", ");
@@ -190,8 +194,8 @@ impl Expr {
                             .map(|(a, f)| {
                                 let tmp = format!(
                                     "{}: {}",
-                                    f.name(db).display(db.upcast()),
-                                    a.gen_source_code(sema_scope, many_formatter, cfg)?
+                                    f.name(db).display(db.upcast(), edition),
+                                    a.gen_source_code(sema_scope, many_formatter, cfg, edition)?
                                 );
                                 Ok(tmp)
                             })
@@ -211,7 +215,7 @@ impl Expr {
                     StructKind::Tuple => {
                         let args = params
                             .iter()
-                            .map(|a| a.gen_source_code(sema_scope, many_formatter, cfg))
+                            .map(|a| a.gen_source_code(sema_scope, many_formatter, cfg, edition))
                             .collect::<Result<Vec<String>, DisplaySourceCodeError>>()?
                             .into_iter()
                             .join(", ");
@@ -225,8 +229,8 @@ impl Expr {
                             .map(|(a, f)| {
                                 let tmp = format!(
                                     "{}: {}",
-                                    f.name(db).display(db.upcast()),
-                                    a.gen_source_code(sema_scope, many_formatter, cfg)?
+                                    f.name(db).display(db.upcast(), edition),
+                                    a.gen_source_code(sema_scope, many_formatter, cfg, edition)?
                                 );
                                 Ok(tmp)
                             })
@@ -244,7 +248,7 @@ impl Expr {
             Expr::Tuple { params, .. } => {
                 let args = params
                     .iter()
-                    .map(|a| a.gen_source_code(sema_scope, many_formatter, cfg))
+                    .map(|a| a.gen_source_code(sema_scope, many_formatter, cfg, edition))
                     .collect::<Result<Vec<String>, DisplaySourceCodeError>>()?
                     .into_iter()
                     .join(", ");
@@ -256,8 +260,8 @@ impl Expr {
                     return Ok(many_formatter(&expr.ty(db)));
                 }
 
-                let strukt = expr.gen_source_code(sema_scope, many_formatter, cfg)?;
-                let field = field.name(db).display(db.upcast()).to_string();
+                let strukt = expr.gen_source_code(sema_scope, many_formatter, cfg, edition)?;
+                let field = field.name(db).display(db.upcast(), edition).to_string();
                 Ok(format!("{strukt}.{field}"))
             }
             Expr::Reference(expr) => {
@@ -265,7 +269,7 @@ impl Expr {
                     return Ok(many_formatter(&expr.ty(db)));
                 }
 
-                let inner = expr.gen_source_code(sema_scope, many_formatter, cfg)?;
+                let inner = expr.gen_source_code(sema_scope, many_formatter, cfg, edition)?;
                 Ok(format!("&{inner}"))
             }
             Expr::Many(ty) => Ok(many_formatter(ty)),
@@ -353,17 +357,18 @@ fn container_name(
     container: AssocItemContainer,
     sema_scope: &SemanticsScope<'_>,
     cfg: ImportPathConfig,
+    edition: Edition,
 ) -> Result<String, DisplaySourceCodeError> {
     let container_name = match container {
         crate::AssocItemContainer::Trait(trait_) => {
-            mod_item_path_str(sema_scope, &ModuleDef::Trait(trait_), cfg)?
+            mod_item_path_str(sema_scope, &ModuleDef::Trait(trait_), cfg, edition)?
         }
         crate::AssocItemContainer::Impl(imp) => {
             let self_ty = imp.self_ty(sema_scope.db);
             // Should it be guaranteed that `mod_item_path` always exists?
             match self_ty.as_adt().and_then(|adt| mod_item_path(sema_scope, &adt.into(), cfg)) {
-                Some(path) => path.display(sema_scope.db.upcast()).to_string(),
-                None => self_ty.display(sema_scope.db).to_string(),
+                Some(path) => path.display(sema_scope.db.upcast(), edition).to_string(),
+                None => self_ty.display(sema_scope.db, edition).to_string(),
             }
         }
     };
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs
index 4cd15f1c755e1..7f8ea44fb12c4 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs
@@ -1,5 +1,8 @@
 use hir::HasSource;
-use syntax::ast::{self, make, AstNode};
+use syntax::{
+    ast::{self, make, AstNode},
+    Edition,
+};
 
 use crate::{
     assist_context::{AssistContext, Assists},
@@ -150,14 +153,22 @@ fn add_missing_impl_members_inner(
             &missing_items,
             trait_,
             &new_impl_def,
-            target_scope,
+            &target_scope,
         );
 
         if let Some(cap) = ctx.config.snippet_cap {
             let mut placeholder = None;
             if let DefaultMethods::No = mode {
                 if let ast::AssocItem::Fn(func) = &first_new_item {
-                    if try_gen_trait_body(ctx, func, trait_ref, &impl_def).is_none() {
+                    if try_gen_trait_body(
+                        ctx,
+                        func,
+                        trait_ref,
+                        &impl_def,
+                        target_scope.krate().edition(ctx.sema.db),
+                    )
+                    .is_none()
+                    {
                         if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast)
                         {
                             if m.syntax().text() == "todo!()" {
@@ -182,9 +193,11 @@ fn try_gen_trait_body(
     func: &ast::Fn,
     trait_ref: hir::TraitRef,
     impl_def: &ast::Impl,
+    edition: Edition,
 ) -> Option<()> {
-    let trait_path =
-        make::ext::ident_path(&trait_ref.trait_().name(ctx.db()).display(ctx.db()).to_string());
+    let trait_path = make::ext::ident_path(
+        &trait_ref.trait_().name(ctx.db()).display(ctx.db(), edition).to_string(),
+    );
     let hir_ty = ctx.sema.resolve_type(&impl_def.self_ty()?)?;
     let adt = hir_ty.as_adt()?.source(ctx.db())?;
     gen_trait_fn_body(func, &trait_path, &adt.value, Some(trait_ref))
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
index f4569ca848ff4..b6abb06a2afc1 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
@@ -445,7 +445,8 @@ fn build_pat(
 ) -> Option<ast::Pat> {
     match var {
         ExtendedVariant::Variant(var) => {
-            let path = mod_path_to_ast(&module.find_path(db, ModuleDef::from(var), cfg)?);
+            let edition = module.krate().edition(db);
+            let path = mod_path_to_ast(&module.find_path(db, ModuleDef::from(var), cfg)?, edition);
             // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
             Some(match var.source(db)?.value.kind() {
                 ast::StructKind::Tuple(field_list) => {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
index db53e49d84633..d86948818b163 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
@@ -8,7 +8,7 @@ use ide_db::{
         insert_use::{insert_use, insert_use_as_alias, ImportScope},
     },
 };
-use syntax::{ast, AstNode, NodeOrToken, SyntaxElement};
+use syntax::{ast, AstNode, Edition, NodeOrToken, SyntaxElement};
 
 use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
 
@@ -120,13 +120,14 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
     // prioritize more relevant imports
     proposed_imports
         .sort_by_key(|import| Reverse(relevance_score(ctx, import, current_module.as_ref())));
+    let edition = current_module.map(|it| it.krate().edition(ctx.db())).unwrap_or(Edition::CURRENT);
 
     let group_label = group_label(import_assets.import_candidate());
     for import in proposed_imports {
         let import_path = import.import_path;
 
         let (assist_id, import_name) =
-            (AssistId("auto_import", AssistKind::QuickFix), import_path.display(ctx.db()));
+            (AssistId("auto_import", AssistKind::QuickFix), import_path.display(ctx.db(), edition));
         acc.add_group(
             &group_label,
             assist_id,
@@ -138,7 +139,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
                     ImportScope::Module(it) => ImportScope::Module(builder.make_mut(it)),
                     ImportScope::Block(it) => ImportScope::Block(builder.make_mut(it)),
                 };
-                insert_use(&scope, mod_path_to_ast(&import_path), &ctx.config.insert_use);
+                insert_use(&scope, mod_path_to_ast(&import_path, edition), &ctx.config.insert_use);
             },
         );
 
@@ -165,7 +166,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
                         };
                         insert_use_as_alias(
                             &scope,
-                            mod_path_to_ast(&import_path),
+                            mod_path_to_ast(&import_path, edition),
                             &ctx.config.insert_use,
                         );
                     },
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bool_to_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bool_to_enum.rs
index 3a0754d60f857..cd5fe0f86269c 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bool_to_enum.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bool_to_enum.rs
@@ -339,6 +339,7 @@ fn augment_references_with_imports(
 
     let cfg = ctx.config.import_path_config();
 
+    let edition = target_module.krate().edition(ctx.db());
     references
         .into_iter()
         .filter_map(|FileReference { range, name, .. }| {
@@ -361,7 +362,10 @@ fn augment_references_with_imports(
                         cfg,
                     )
                     .map(|mod_path| {
-                        make::path_concat(mod_path_to_ast(&mod_path), make::path_from_text("Bool"))
+                        make::path_concat(
+                            mod_path_to_ast(&mod_path, edition),
+                            make::path_from_text("Bool"),
+                        )
                     });
 
                 import_scope.zip(path)
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs
index 77f9c66b354ee..a5c5b08d5b0c7 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs
@@ -159,7 +159,7 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_>
     };
     // Verify this is `bool::then` that is being called.
     let func = ctx.sema.resolve_method_call(&mcall)?;
-    if func.name(ctx.sema.db).display(ctx.db()).to_string() != "then" {
+    if !func.name(ctx.sema.db).eq_ident("then") {
         return None;
     }
     let assoc = func.as_assoc_item(ctx.sema.db)?;
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
new file mode 100644
index 0000000000000..79f303b37a442
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
@@ -0,0 +1,1271 @@
+use either::Either;
+use hir::{CaptureKind, ClosureCapture, FileRangeWrapper, HirDisplay};
+use ide_db::{
+    assists::{AssistId, AssistKind},
+    base_db::SourceDatabase,
+    defs::Definition,
+    search::FileReferenceNode,
+    source_change::SourceChangeBuilder,
+    FxHashSet,
+};
+use stdx::format_to;
+use syntax::{
+    algo::{skip_trivia_token, skip_whitespace_token},
+    ast::{
+        self,
+        edit::{AstNodeEdit, IndentLevel},
+        make, HasArgList, HasGenericParams, HasName,
+    },
+    hacks::parse_expr_from_str,
+    ted, AstNode, Direction, SyntaxKind, SyntaxNode, TextSize, ToSmolStr, T,
+};
+
+use crate::assist_context::{AssistContext, Assists};
+
+// Assist: convert_closure_to_fn
+//
+// This converts a closure to a freestanding function, changing all captures to parameters.
+//
+// ```
+// # //- minicore: copy
+// # struct String;
+// # impl String {
+// #     fn new() -> Self {}
+// #     fn push_str(&mut self, s: &str) {}
+// # }
+// fn main() {
+//     let mut s = String::new();
+//     let closure = |$0a| s.push_str(a);
+//     closure("abc");
+// }
+// ```
+// ->
+// ```
+// # struct String;
+// # impl String {
+// #     fn new() -> Self {}
+// #     fn push_str(&mut self, s: &str) {}
+// # }
+// fn main() {
+//     let mut s = String::new();
+//     fn closure(a: &str, s: &mut String) {
+//         s.push_str(a)
+//     }
+//     closure("abc", &mut s);
+// }
+// ```
+pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+    let closure = ctx.find_node_at_offset::<ast::ClosureExpr>()?;
+    if ctx.find_node_at_offset::<ast::Expr>() != Some(ast::Expr::ClosureExpr(closure.clone())) {
+        // Not inside the parameter list.
+        return None;
+    }
+    let closure_name = closure.syntax().parent().and_then(|parent| {
+        let closure_decl = ast::LetStmt::cast(parent)?;
+        match closure_decl.pat()? {
+            ast::Pat::IdentPat(pat) => Some((closure_decl, pat.clone(), pat.name()?)),
+            _ => None,
+        }
+    });
+    let module = ctx.sema.scope(closure.syntax())?.module();
+    let closure_ty = ctx.sema.type_of_expr(&closure.clone().into())?;
+    let callable = closure_ty.original.as_callable(ctx.db())?;
+    let closure_ty = closure_ty.original.as_closure()?;
+
+    let mut ret_ty = callable.return_type();
+    let mut closure_mentioned_generic_params = ret_ty.generic_params(ctx.db());
+
+    let mut params = callable
+        .params()
+        .into_iter()
+        .map(|param| {
+            let node = ctx.sema.source(param.clone())?.value.right()?;
+            let param_ty = param.ty();
+            closure_mentioned_generic_params.extend(param_ty.generic_params(ctx.db()));
+            match node.ty() {
+                Some(_) => Some(node),
+                None => {
+                    let ty = param_ty
+                        .display_source_code(ctx.db(), module.into(), true)
+                        .unwrap_or_else(|_| "_".to_owned());
+                    Some(make::param(node.pat()?, make::ty(&ty)))
+                }
+            }
+        })
+        .collect::<Option<Vec<_>>>()?;
+
+    let mut body = closure.body()?.clone_for_update();
+    let mut is_gen = false;
+    let mut is_async = closure.async_token().is_some();
+    if is_async {
+        ret_ty = ret_ty.future_output(ctx.db())?;
+    }
+    // We defer the wrapping of the body in the block, because `make::block()` will generate a new node,
+    // but we need to locate `AstPtr`s inside the body.
+    let mut wrap_body_in_block = true;
+    if let ast::Expr::BlockExpr(block) = &body {
+        if let Some(async_token) = block.async_token() {
+            if !is_async {
+                is_async = true;
+                ret_ty = ret_ty.future_output(ctx.db())?;
+                let token_idx = async_token.index();
+                let whitespace_tokens_after_count = async_token
+                    .siblings_with_tokens(Direction::Next)
+                    .skip(1)
+                    .take_while(|token| token.kind() == SyntaxKind::WHITESPACE)
+                    .count();
+                body.syntax().splice_children(
+                    token_idx..token_idx + whitespace_tokens_after_count + 1,
+                    Vec::new(),
+                );
+            }
+        }
+        if let Some(gen_token) = block.gen_token() {
+            is_gen = true;
+            ret_ty = ret_ty.iterator_item(ctx.db())?;
+            let token_idx = gen_token.index();
+            let whitespace_tokens_after_count = gen_token
+                .siblings_with_tokens(Direction::Next)
+                .skip(1)
+                .take_while(|token| token.kind() == SyntaxKind::WHITESPACE)
+                .count();
+            body.syntax().splice_children(
+                token_idx..token_idx + whitespace_tokens_after_count + 1,
+                Vec::new(),
+            );
+        }
+
+        if block.try_token().is_none()
+            && block.unsafe_token().is_none()
+            && block.label().is_none()
+            && block.const_token().is_none()
+            && block.async_token().is_none()
+        {
+            wrap_body_in_block = false;
+        }
+    };
+
+    acc.add(
+        AssistId("convert_closure_to_fn", AssistKind::RefactorRewrite),
+        "Convert closure to fn",
+        closure.param_list()?.syntax().text_range(),
+        |builder| {
+            let closure_name_or_default = closure_name
+                .as_ref()
+                .map(|(_, _, it)| it.clone())
+                .unwrap_or_else(|| make::name("fun_name"));
+            let captures = closure_ty.captured_items(ctx.db());
+            let capture_tys = closure_ty.capture_types(ctx.db());
+
+            let mut captures_as_args = Vec::with_capacity(captures.len());
+
+            let body_root = body.syntax().ancestors().last().unwrap();
+            // We need to defer this work because otherwise the text range of elements is being messed up, and
+            // replacements for the next captures won't work.
+            let mut capture_usages_replacement_map = Vec::with_capacity(captures.len());
+
+            for (capture, capture_ty) in std::iter::zip(&captures, &capture_tys) {
+                // FIXME: Allow configuring the replacement of `self`.
+                let capture_name =
+                    if capture.local().is_self(ctx.db()) && !capture.has_field_projections() {
+                        make::name("this")
+                    } else {
+                        make::name(&capture.place_to_name(ctx.db()))
+                    };
+
+                closure_mentioned_generic_params.extend(capture_ty.generic_params(ctx.db()));
+
+                let capture_ty = capture_ty
+                    .display_source_code(ctx.db(), module.into(), true)
+                    .unwrap_or_else(|_| "_".to_owned());
+                params.push(make::param(
+                    ast::Pat::IdentPat(make::ident_pat(false, false, capture_name.clone_subtree())),
+                    make::ty(&capture_ty),
+                ));
+
+                for capture_usage in capture.usages().sources(ctx.db()) {
+                    if capture_usage.file_id() != ctx.file_id() {
+                        // This is from a macro, don't change it.
+                        continue;
+                    }
+
+                    let capture_usage_source = capture_usage.source();
+                    let capture_usage_source = capture_usage_source.to_node(&body_root);
+                    let expr = match capture_usage_source {
+                        Either::Left(expr) => expr,
+                        Either::Right(pat) => {
+                            let Some(expr) = expr_of_pat(pat) else { continue };
+                            expr
+                        }
+                    };
+                    let replacement = wrap_capture_in_deref_if_needed(
+                        &expr,
+                        &capture_name,
+                        capture.kind(),
+                        capture_usage.is_ref(),
+                    )
+                    .clone_for_update();
+                    capture_usages_replacement_map.push((expr, replacement));
+                }
+
+                captures_as_args.push(capture_as_arg(ctx, capture));
+            }
+
+            let (closure_type_params, closure_where_clause) =
+                compute_closure_type_params(ctx, closure_mentioned_generic_params, &closure);
+
+            for (old, new) in capture_usages_replacement_map {
+                if old == body {
+                    body = new;
+                } else {
+                    ted::replace(old.syntax(), new.syntax());
+                }
+            }
+
+            let body = if wrap_body_in_block {
+                make::block_expr([], Some(body))
+            } else {
+                ast::BlockExpr::cast(body.syntax().clone()).unwrap()
+            };
+
+            let params = make::param_list(None, params);
+            let ret_ty = if ret_ty.is_unit() {
+                None
+            } else {
+                let ret_ty = ret_ty
+                    .display_source_code(ctx.db(), module.into(), true)
+                    .unwrap_or_else(|_| "_".to_owned());
+                Some(make::ret_type(make::ty(&ret_ty)))
+            };
+            let mut fn_ = make::fn_(
+                None,
+                closure_name_or_default.clone(),
+                closure_type_params,
+                closure_where_clause,
+                params,
+                body,
+                ret_ty,
+                is_async,
+                false,
+                false,
+                is_gen,
+            );
+            fn_ = fn_.dedent(IndentLevel::from_token(&fn_.syntax().last_token().unwrap()));
+
+            builder.edit_file(ctx.file_id());
+            match &closure_name {
+                Some((closure_decl, _, _)) => {
+                    fn_ = fn_.indent(closure_decl.indent_level());
+                    builder.replace(closure_decl.syntax().text_range(), fn_.to_string());
+                }
+                None => {
+                    let Some(top_stmt) =
+                        closure.syntax().ancestors().skip(1).find_map(|ancestor| {
+                            ast::Stmt::cast(ancestor.clone()).map(Either::Left).or_else(|| {
+                                ast::ClosureExpr::cast(ancestor.clone())
+                                    .map(Either::Left)
+                                    .or_else(|| ast::BlockExpr::cast(ancestor).map(Either::Right))
+                                    .map(Either::Right)
+                            })
+                        })
+                    else {
+                        return;
+                    };
+                    builder.replace(
+                        closure.syntax().text_range(),
+                        closure_name_or_default.to_string(),
+                    );
+                    match top_stmt {
+                        Either::Left(stmt) => {
+                            let indent = stmt.indent_level();
+                            fn_ = fn_.indent(indent);
+                            let range = stmt
+                                .syntax()
+                                .first_token()
+                                .and_then(|token| {
+                                    skip_whitespace_token(token.prev_token()?, Direction::Prev)
+                                })
+                                .map(|it| it.text_range().end())
+                                .unwrap_or_else(|| stmt.syntax().text_range().start());
+                            builder.insert(range, format!("\n{indent}{fn_}"));
+                        }
+                        Either::Right(Either::Left(closure_inside_closure)) => {
+                            let Some(closure_body) = closure_inside_closure.body() else { return };
+                            // FIXME: Maybe we can indent this properly, adding newlines and all, but this is hard.
+                            builder.insert(
+                                closure_body.syntax().text_range().start(),
+                                format!("{{ {fn_} "),
+                            );
+                            builder
+                                .insert(closure_body.syntax().text_range().end(), " }".to_owned());
+                        }
+                        Either::Right(Either::Right(block_expr)) => {
+                            let Some(tail_expr) = block_expr.tail_expr() else { return };
+                            let Some(insert_in) =
+                                tail_expr.syntax().first_token().and_then(|token| {
+                                    skip_whitespace_token(token.prev_token()?, Direction::Prev)
+                                })
+                            else {
+                                return;
+                            };
+                            let indent = tail_expr.indent_level();
+                            fn_ = fn_.indent(indent);
+                            builder
+                                .insert(insert_in.text_range().end(), format!("\n{indent}{fn_}"));
+                        }
+                    }
+                }
+            }
+
+            handle_calls(
+                builder,
+                ctx,
+                closure_name.as_ref().map(|(_, it, _)| it),
+                &captures_as_args,
+                &closure,
+            );
+
+            // FIXME: Place the cursor at `fun_name`, like rename does.
+        },
+    )?;
+    Some(())
+}
+
+fn compute_closure_type_params(
+    ctx: &AssistContext<'_>,
+    mentioned_generic_params: FxHashSet<hir::GenericParam>,
+    closure: &ast::ClosureExpr,
+) -> (Option<ast::GenericParamList>, Option<ast::WhereClause>) {
+    if mentioned_generic_params.is_empty() {
+        return (None, None);
+    }
+
+    let mut mentioned_names = mentioned_generic_params
+        .iter()
+        .filter_map(|param| match param {
+            hir::GenericParam::TypeParam(param) => {
+                Some(param.name(ctx.db()).unescaped().display(ctx.db()).to_smolstr())
+            }
+            hir::GenericParam::ConstParam(param) => {
+                Some(param.name(ctx.db()).unescaped().display(ctx.db()).to_smolstr())
+            }
+            hir::GenericParam::LifetimeParam(_) => None,
+        })
+        .collect::<FxHashSet<_>>();
+
+    let Some((container_params, container_where, container)) =
+        closure.syntax().ancestors().find_map(ast::AnyHasGenericParams::cast).and_then(
+            |container| {
+                Some((container.generic_param_list()?, container.where_clause(), container))
+            },
+        )
+    else {
+        return (None, None);
+    };
+    let containing_impl = if ast::AssocItem::can_cast(container.syntax().kind()) {
+        container
+            .syntax()
+            .ancestors()
+            .find_map(ast::Impl::cast)
+            .and_then(|impl_| Some((impl_.generic_param_list()?, impl_.where_clause())))
+    } else {
+        None
+    };
+
+    let all_params = container_params
+        .type_or_const_params()
+        .chain(containing_impl.iter().flat_map(|(param_list, _)| param_list.type_or_const_params()))
+        .filter_map(|param| Some(param.name()?.text().to_smolstr()))
+        .collect::<FxHashSet<_>>();
+
+    // A fixpoint algorithm to detect (very roughly) if we need to include a generic parameter
+    // by checking if it is mentioned by another parameter we need to include.
+    let mut reached_fixpoint = false;
+    let mut container_where_bounds_indices = Vec::new();
+    let mut impl_where_bounds_indices = Vec::new();
+    while !reached_fixpoint {
+        reached_fixpoint = true;
+
+        let mut insert_name = |syntax: &SyntaxNode| {
+            let has_name = syntax
+                .descendants()
+                .filter_map(ast::NameOrNameRef::cast)
+                .any(|name| mentioned_names.contains(&*name.text()));
+            let mut has_new_params = false;
+            if has_name {
+                syntax
+                    .descendants()
+                    .filter_map(ast::NameOrNameRef::cast)
+                    .filter(|name| all_params.contains(name.text().trim_start_matches("r#")))
+                    .for_each(|name| {
+                        if mentioned_names.insert(name.text().trim_start_matches("r#").to_smolstr())
+                        {
+                            // We do this here so we don't do it if there are only matches that are not in `all_params`.
+                            has_new_params = true;
+                            reached_fixpoint = false;
+                        }
+                    });
+            }
+            has_new_params
+        };
+
+        for param in container_params.type_or_const_params() {
+            insert_name(param.syntax());
+        }
+        for (pred_index, pred) in container_where.iter().flat_map(|it| it.predicates()).enumerate()
+        {
+            if insert_name(pred.syntax()) {
+                container_where_bounds_indices.push(pred_index);
+            }
+        }
+        if let Some((impl_params, impl_where)) = &containing_impl {
+            for param in impl_params.type_or_const_params() {
+                insert_name(param.syntax());
+            }
+            for (pred_index, pred) in impl_where.iter().flat_map(|it| it.predicates()).enumerate() {
+                if insert_name(pred.syntax()) {
+                    impl_where_bounds_indices.push(pred_index);
+                }
+            }
+        }
+    }
+
+    // Order matters here (for beauty). First the outer impl parameters, then the direct container's.
+    let include_params = containing_impl
+        .iter()
+        .flat_map(|(impl_params, _)| {
+            impl_params.type_or_const_params().filter(|param| {
+                param.name().is_some_and(|name| {
+                    mentioned_names.contains(name.text().trim_start_matches("r#"))
+                })
+            })
+        })
+        .chain(container_params.type_or_const_params().filter(|param| {
+            param
+                .name()
+                .is_some_and(|name| mentioned_names.contains(name.text().trim_start_matches("r#")))
+        }))
+        .map(ast::TypeOrConstParam::into);
+    let include_where_bounds = containing_impl
+        .as_ref()
+        .and_then(|(_, it)| it.as_ref())
+        .into_iter()
+        .flat_map(|where_| {
+            impl_where_bounds_indices.iter().filter_map(|&index| where_.predicates().nth(index))
+        })
+        .chain(container_where.iter().flat_map(|where_| {
+            container_where_bounds_indices
+                .iter()
+                .filter_map(|&index| where_.predicates().nth(index))
+        }))
+        .collect::<Vec<_>>();
+    let where_clause =
+        (!include_where_bounds.is_empty()).then(|| make::where_clause(include_where_bounds));
+
+    // FIXME: Consider generic parameters that do not appear in params/return type/captures but
+    // written explicitly inside the closure.
+    (Some(make::generic_param_list(include_params)), where_clause)
+}
+
+fn wrap_capture_in_deref_if_needed(
+    expr: &ast::Expr,
+    capture_name: &ast::Name,
+    capture_kind: CaptureKind,
+    is_ref: bool,
+) -> ast::Expr {
+    fn peel_parens(mut expr: ast::Expr) -> ast::Expr {
+        loop {
+            if ast::ParenExpr::can_cast(expr.syntax().kind()) {
+                let Some(parent) = expr.syntax().parent().and_then(ast::Expr::cast) else { break };
+                expr = parent;
+            } else {
+                break;
+            }
+        }
+        expr
+    }
+
+    let capture_name = make::expr_path(make::path_from_text(&capture_name.text()));
+    if capture_kind == CaptureKind::Move || is_ref {
+        return capture_name;
+    }
+    let expr_parent = expr.syntax().parent().and_then(ast::Expr::cast);
+    let expr_parent_peeled_parens = expr_parent.map(peel_parens);
+    let does_autoderef = match expr_parent_peeled_parens {
+        Some(
+            ast::Expr::AwaitExpr(_)
+            | ast::Expr::CallExpr(_)
+            | ast::Expr::FieldExpr(_)
+            | ast::Expr::FormatArgsExpr(_)
+            | ast::Expr::MethodCallExpr(_),
+        ) => true,
+        Some(ast::Expr::IndexExpr(parent_expr)) if parent_expr.base().as_ref() == Some(expr) => {
+            true
+        }
+        _ => false,
+    };
+    if does_autoderef {
+        return capture_name;
+    }
+    make::expr_prefix(T![*], capture_name)
+}
+
+fn capture_as_arg(ctx: &AssistContext<'_>, capture: &ClosureCapture) -> ast::Expr {
+    let place =
+        parse_expr_from_str(&capture.display_place_source_code(ctx.db()), ctx.file_id().edition())
+            .expect("`display_place_source_code()` produced an invalid expr");
+    let needs_mut = match capture.kind() {
+        CaptureKind::SharedRef => false,
+        CaptureKind::MutableRef | CaptureKind::UniqueSharedRef => true,
+        CaptureKind::Move => return place,
+    };
+    if let ast::Expr::PrefixExpr(expr) = &place {
+        if expr.op_kind() == Some(ast::UnaryOp::Deref) {
+            return expr.expr().expect("`display_place_source_code()` produced an invalid expr");
+        }
+    }
+    make::expr_ref(place, needs_mut)
+}
+
+fn handle_calls(
+    builder: &mut SourceChangeBuilder,
+    ctx: &AssistContext<'_>,
+    closure_name: Option<&ast::IdentPat>,
+    captures_as_args: &[ast::Expr],
+    closure: &ast::ClosureExpr,
+) {
+    if captures_as_args.is_empty() {
+        return;
+    }
+
+    match closure_name {
+        Some(closure_name) => {
+            let Some(closure_def) = ctx.sema.to_def(closure_name) else { return };
+            let closure_usages = Definition::from(closure_def).usages(&ctx.sema).all();
+            for (_, usages) in closure_usages {
+                for usage in usages {
+                    let name = match usage.name {
+                        FileReferenceNode::Name(name) => name.syntax().clone(),
+                        FileReferenceNode::NameRef(name_ref) => name_ref.syntax().clone(),
+                        FileReferenceNode::FormatStringEntry(..) => continue,
+                        FileReferenceNode::Lifetime(_) => {
+                            unreachable!("impossible usage")
+                        }
+                    };
+                    let Some(expr) = name.parent().and_then(|it| {
+                        ast::Expr::cast(
+                            ast::PathSegment::cast(it)?.parent_path().syntax().parent()?,
+                        )
+                    }) else {
+                        continue;
+                    };
+                    handle_call(builder, ctx, expr, captures_as_args);
+                }
+            }
+        }
+        None => {
+            handle_call(builder, ctx, ast::Expr::ClosureExpr(closure.clone()), captures_as_args);
+        }
+    }
+}
+
+fn handle_call(
+    builder: &mut SourceChangeBuilder,
+    ctx: &AssistContext<'_>,
+    closure_ref: ast::Expr,
+    captures_as_args: &[ast::Expr],
+) -> Option<()> {
+    let call =
+        ast::CallExpr::cast(peel_blocks_and_refs_and_parens(closure_ref).syntax().parent()?)?;
+    let args = call.arg_list()?;
+    // The really last token is `)`; we need one before that.
+    let has_trailing_comma = args.syntax().last_token()?.prev_token().is_some_and(|token| {
+        skip_trivia_token(token, Direction::Prev).is_some_and(|token| token.kind() == T![,])
+    });
+    let has_existing_args = args.args().next().is_some();
+
+    let FileRangeWrapper { file_id, range } = ctx.sema.original_range_opt(args.syntax())?;
+    let first_arg_indent = args.args().next().map(|it| it.indent_level());
+    let arg_list_indent = args.indent_level();
+    let insert_newlines =
+        first_arg_indent.is_some_and(|first_arg_indent| first_arg_indent != arg_list_indent);
+    let indent =
+        if insert_newlines { first_arg_indent.unwrap().to_string() } else { String::new() };
+    // FIXME: This text manipulation seems risky.
+    let text = ctx.db().file_text(file_id.file_id());
+    let mut text = text[..u32::from(range.end()).try_into().unwrap()].trim_end();
+    if !text.ends_with(')') {
+        return None;
+    }
+    text = text[..text.len() - 1].trim_end();
+    let offset = TextSize::new(text.len().try_into().unwrap());
+
+    let mut to_insert = String::new();
+    if has_existing_args && !has_trailing_comma {
+        to_insert.push(',');
+    }
+    if insert_newlines {
+        to_insert.push('\n');
+    }
+    let (last_arg, rest_args) =
+        captures_as_args.split_last().expect("already checked has captures");
+    if !insert_newlines && has_existing_args {
+        to_insert.push(' ');
+    }
+    if let Some((first_arg, rest_args)) = rest_args.split_first() {
+        format_to!(to_insert, "{indent}{first_arg},",);
+        if insert_newlines {
+            to_insert.push('\n');
+        }
+        for new_arg in rest_args {
+            if !insert_newlines {
+                to_insert.push(' ');
+            }
+            format_to!(to_insert, "{indent}{new_arg},",);
+            if insert_newlines {
+                to_insert.push('\n');
+            }
+        }
+        if !insert_newlines {
+            to_insert.push(' ');
+        }
+    }
+    format_to!(to_insert, "{indent}{last_arg}");
+    if has_trailing_comma {
+        to_insert.push(',');
+    }
+
+    builder.edit_file(file_id);
+    builder.insert(offset, to_insert);
+
+    Some(())
+}
+
+fn peel_blocks_and_refs_and_parens(mut expr: ast::Expr) -> ast::Expr {
+    loop {
+        let Some(parent) = expr.syntax().parent() else { break };
+        if matches!(parent.kind(), SyntaxKind::PAREN_EXPR | SyntaxKind::REF_EXPR) {
+            expr = ast::Expr::cast(parent).unwrap();
+            continue;
+        }
+        if let Some(stmt_list) = ast::StmtList::cast(parent) {
+            if let Some(block) = stmt_list.syntax().parent().and_then(ast::BlockExpr::cast) {
+                expr = ast::Expr::BlockExpr(block);
+                continue;
+            }
+        }
+        break;
+    }
+    expr
+}
+
+// FIXME:
+// Somehow handle the case of `let Struct { field, .. } = capture`.
+// Replacing `capture` with `capture_field` won't work.
+fn expr_of_pat(pat: ast::Pat) -> Option<ast::Expr> {
+    'find_expr: {
+        for ancestor in pat.syntax().ancestors() {
+            if let Some(let_stmt) = ast::LetStmt::cast(ancestor.clone()) {
+                break 'find_expr let_stmt.initializer();
+            }
+            if ast::MatchArm::can_cast(ancestor.kind()) {
+                if let Some(match_) =
+                    ancestor.parent().and_then(|it| it.parent()).and_then(ast::MatchExpr::cast)
+                {
+                    break 'find_expr match_.expr();
+                }
+            }
+            if ast::ExprStmt::can_cast(ancestor.kind()) {
+                break;
+            }
+        }
+        None
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::{check_assist, check_assist_not_applicable};
+
+    use super::*;
+
+    #[test]
+    fn handles_unique_captures() {
+        check_assist(
+            convert_closure_to_fn,
+            r#"
+//- minicore:copy
+fn main() {
+    let s = &mut true;
+    let closure = |$0| { *s = false; };
+    closure();
+}
+"#,
+            r#"
+fn main() {
+    let s = &mut true;
+    fn closure(s: &mut bool) { *s = false; }
+    closure(s);
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn multiple_capture_usages() {
+        check_assist(
+            convert_closure_to_fn,
+            r#"
+//- minicore:copy
+struct A { a: i32, b: bool }
+fn main() {
+    let mut a = A { a: 123, b: false };
+    let closure = |$0| {
+        let b = a.b;
+        a = A { a: 456, b: true };
+    };
+    closure();
+}
+"#,
+            r#"
+struct A { a: i32, b: bool }
+fn main() {
+    let mut a = A { a: 123, b: false };
+    fn closure(a: &mut A) {
+        let b = a.b;
+        *a = A { a: 456, b: true };
+    }
+    closure(&mut a);
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn changes_names_of_place() {
+        check_assist(
+            convert_closure_to_fn,
+            r#"
+//- minicore:copy
+struct A { b: &'static B, c: i32 }
+struct B(bool, i32);
+struct C;
+impl C {
+    fn foo(&self) {
+        let a = A { b: &B(false, 0), c: 123 };
+        let closure = |$0| {
+            let b = a.b.1;
+            let c = &*self;
+        };
+        closure();
+    }
+}
+"#,
+            r#"
+struct A { b: &'static B, c: i32 }
+struct B(bool, i32);
+struct C;
+impl C {
+    fn foo(&self) {
+        let a = A { b: &B(false, 0), c: 123 };
+        fn closure(this: &C, a_b_1: &i32) {
+            let b = *a_b_1;
+            let c = this;
+        }
+        closure(self, &a.b.1);
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn self_with_fields_does_not_change_to_this() {
+        check_assist(
+            convert_closure_to_fn,
+            r#"
+//- minicore:copy
+struct A { b: &'static B, c: i32 }
+struct B(bool, i32);
+impl A {
+    fn foo(&self) {
+        let closure = |$0| {
+            let b = self.b.1;
+        };
+        closure();
+    }
+}
+"#,
+            r#"
+struct A { b: &'static B, c: i32 }
+struct B(bool, i32);
+impl A {
+    fn foo(&self) {
+        fn closure(self_b_1: &i32) {
+            let b = *self_b_1;
+        }
+        closure(&self.b.1);
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn replaces_async_closure_with_async_fn() {
+        check_assist(
+            convert_closure_to_fn,
+            r#"
+//- minicore: copy, future
+fn foo(&self) {
+    let closure = async |$0| 1;
+    closure();
+}
+"#,
+            r#"
+fn foo(&self) {
+    async fn closure() -> i32 {
+        1
+    }
+    closure();
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn replaces_async_block_with_async_fn() {
+        check_assist(
+            convert_closure_to_fn,
+            r#"
+//- minicore: copy, future
+fn foo() {
+    let closure = |$0| async { 1 };
+    closure();
+}
+"#,
+            r#"
+fn foo() {
+    async fn closure() -> i32 { 1 }
+    closure();
+}
+"#,
+        );
+    }
+
+    #[test]
+    #[ignore = "FIXME: we do not do type inference for gen blocks yet"]
+    fn replaces_gen_block_with_gen_fn() {
+        check_assist(
+            convert_closure_to_fn,
+            r#"
+//- minicore: copy, iterator
+//- /lib.rs edition:2024
+fn foo() {
+    let closure = |$0| gen {
+        yield 1;
+    };
+    closure();
+}
+"#,
+            r#"
+fn foo() {
+    gen fn closure() -> i32 {
+        yield 1;
+    }
+    closure();
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn leaves_block_in_place() {
+        check_assist(
+            convert_closure_to_fn,
+            r#"
+//- minicore: copy
+fn foo() {
+    let closure = |$0| {};
+    closure();
+}
+"#,
+            r#"
+fn foo() {
+    fn closure() {}
+    closure();
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn wraps_in_block_if_needed() {
+        check_assist(
+            convert_closure_to_fn,
+            r#"
+//- minicore: copy
+fn foo() {
+    let a = 1;
+    let closure = |$0| a;
+    closure();
+}
+"#,
+            r#"
+fn foo() {
+    let a = 1;
+    fn closure(a: &i32) -> i32 {
+        *a
+    }
+    closure(&a);
+}
+"#,
+        );
+        check_assist(
+            convert_closure_to_fn,
+            r#"
+//- minicore: copy
+fn foo() {
+    let closure = |$0| 'label: {};
+    closure();
+}
+"#,
+            r#"
+fn foo() {
+    fn closure() {
+        'label: {}
+    }
+    closure();
+}
+"#,
+        );
+        check_assist(
+            convert_closure_to_fn,
+            r#"
+//- minicore: copy
+fn foo() {
+    let closure = |$0| {
+        const { () }
+    };
+    closure();
+}
+"#,
+            r#"
+fn foo() {
+    fn closure() {
+        const { () }
+    }
+    closure();
+}
+"#,
+        );
+        check_assist(
+            convert_closure_to_fn,
+            r#"
+//- minicore: copy
+fn foo() {
+    let closure = |$0| unsafe { };
+    closure();
+}
+"#,
+            r#"
+fn foo() {
+    fn closure() {
+        unsafe { }
+    }
+    closure();
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn closure_in_closure() {
+        check_assist(
+            convert_closure_to_fn,
+            r#"
+//- minicore: copy
+fn foo() {
+    let a = 1;
+    || |$0| { let b = &a; };
+}
+"#,
+            r#"
+fn foo() {
+    let a = 1;
+    || { fn fun_name(a: &i32) { let b = a; } fun_name };
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn closure_in_block() {
+        check_assist(
+            convert_closure_to_fn,
+            r#"
+//- minicore: copy
+fn foo() {
+    {
+        let a = 1;
+        |$0| { let b = &a; }
+    };
+}
+"#,
+            r#"
+fn foo() {
+    {
+        let a = 1;
+        fn fun_name(a: &i32) { let b = a; }
+        fun_name
+    };
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn finds_pat_for_expr() {
+        check_assist(
+            convert_closure_to_fn,
+            r#"
+//- minicore: copy
+struct A { b: B }
+struct B(bool, i32);
+fn foo() {
+    let mut a = A { b: B(true, 0) };
+    let closure = |$0| {
+        let A { b: B(_, ref mut c) } = a;
+    };
+    closure();
+}
+"#,
+            r#"
+struct A { b: B }
+struct B(bool, i32);
+fn foo() {
+    let mut a = A { b: B(true, 0) };
+    fn closure(a_b_1: &mut i32) {
+        let A { b: B(_, ref mut c) } = a_b_1;
+    }
+    closure(&mut a.b.1);
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn with_existing_params() {
+        check_assist(
+            convert_closure_to_fn,
+            r#"
+//- minicore: copy
+fn foo() {
+    let (mut a, b) = (0.1, "abc");
+    let closure = |$0p1: i32, p2: &mut bool| {
+        a = 1.2;
+        let c = b;
+    };
+    closure(0, &mut false);
+}
+"#,
+            r#"
+fn foo() {
+    let (mut a, b) = (0.1, "abc");
+    fn closure(p1: i32, p2: &mut bool, a: &mut f64, b: &&str) {
+        *a = 1.2;
+        let c = *b;
+    }
+    closure(0, &mut false, &mut a, &b);
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn with_existing_params_newlines() {
+        check_assist(
+            convert_closure_to_fn,
+            r#"
+//- minicore: copy
+fn foo() {
+    let (mut a, b) = (0.1, "abc");
+    let closure = |$0p1: i32, p2| {
+        let _: &mut bool = p2;
+        a = 1.2;
+        let c = b;
+    };
+    closure(
+        0,
+        &mut false
+    );
+}
+"#,
+            r#"
+fn foo() {
+    let (mut a, b) = (0.1, "abc");
+    fn closure(p1: i32, p2: &mut bool, a: &mut f64, b: &&str) {
+        let _: &mut bool = p2;
+        *a = 1.2;
+        let c = *b;
+    }
+    closure(
+        0,
+        &mut false,
+        &mut a,
+        &b
+    );
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn with_existing_params_trailing_comma() {
+        check_assist(
+            convert_closure_to_fn,
+            r#"
+//- minicore: copy
+fn foo() {
+    let (mut a, b) = (0.1, "abc");
+    let closure = |$0p1: i32, p2| {
+        let _: &mut bool = p2;
+        a = 1.2;
+        let c = b;
+    };
+    closure(
+        0,
+        &mut false,
+    );
+}
+"#,
+            r#"
+fn foo() {
+    let (mut a, b) = (0.1, "abc");
+    fn closure(p1: i32, p2: &mut bool, a: &mut f64, b: &&str) {
+        let _: &mut bool = p2;
+        *a = 1.2;
+        let c = *b;
+    }
+    closure(
+        0,
+        &mut false,
+        &mut a,
+        &b,
+    );
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn closure_using_generic_params() {
+        check_assist(
+            convert_closure_to_fn,
+            r#"
+//- minicore: copy
+struct Foo<A, B, const C: usize>(A, B);
+impl<A, B: From<A>, const C: usize> Foo<A, B, C> {
+    fn foo<D, E, F, G>(a: A, b: D)
+    where
+        E: From<D>,
+    {
+        let closure = |$0c: F| {
+            let a = B::from(a);
+            let b = E::from(b);
+        };
+    }
+}
+"#,
+            r#"
+struct Foo<A, B, const C: usize>(A, B);
+impl<A, B: From<A>, const C: usize> Foo<A, B, C> {
+    fn foo<D, E, F, G>(a: A, b: D)
+    where
+        E: From<D>,
+    {
+        fn closure<A, B: From<A>, D, E, F>(c: F, a: A, b: D) where E: From<D> {
+            let a = B::from(a);
+            let b = E::from(b);
+        }
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn closure_in_stmt() {
+        check_assist(
+            convert_closure_to_fn,
+            r#"
+//- minicore: copy
+fn bar(_: impl FnOnce() -> i32) {}
+fn foo() {
+    let a = 123;
+    bar(|$0| a);
+}
+"#,
+            r#"
+fn bar(_: impl FnOnce() -> i32) {}
+fn foo() {
+    let a = 123;
+    fn fun_name(a: &i32) -> i32 {
+        *a
+    }
+    bar(fun_name);
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn unique_and_imm() {
+        check_assist(
+            convert_closure_to_fn,
+            r#"
+//- minicore:copy
+fn main() {
+    let a = &mut true;
+    let closure = |$0| {
+        let b = &a;
+        *a = false;
+    };
+    closure();
+}
+"#,
+            r#"
+fn main() {
+    let a = &mut true;
+    fn closure(a: &mut &mut bool) {
+        let b = a;
+        **a = false;
+    }
+    closure(&mut a);
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn only_applicable_in_param_list() {
+        check_assist_not_applicable(
+            convert_closure_to_fn,
+            r#"
+//- minicore:copy
+fn main() {
+    let closure = || { $0 };
+}
+"#,
+        );
+        check_assist_not_applicable(
+            convert_closure_to_fn,
+            r#"
+//- minicore:copy
+fn main() {
+    let $0closure = || { };
+}
+"#,
+        );
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs
index 5aa94590e679b..8c59ef4314f06 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs
@@ -51,7 +51,10 @@ pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_>) -
             Some(hir::PathResolution::Def(module_def)) => module_def,
             _ => return None,
         };
-        mod_path_to_ast(&module.find_path(ctx.db(), src_type_def, cfg)?)
+        mod_path_to_ast(
+            &module.find_path(ctx.db(), src_type_def, cfg)?,
+            module.krate().edition(ctx.db()),
+        )
     };
 
     let dest_type = match &ast_trait {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
index e86ff0dbebc62..3c9a91741047e 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
@@ -114,12 +114,16 @@ pub(crate) fn convert_for_loop_with_for_each(
         |builder| {
             let mut buf = String::new();
 
-            if let Some((expr_behind_ref, method)) =
+            if let Some((expr_behind_ref, method, krate)) =
                 is_ref_and_impls_iter_method(&ctx.sema, &iterable)
             {
                 // We have either "for x in &col" and col implements a method called iter
                 //             or "for x in &mut col" and col implements a method called iter_mut
-                format_to!(buf, "{expr_behind_ref}.{}()", method.display(ctx.db()));
+                format_to!(
+                    buf,
+                    "{expr_behind_ref}.{}()",
+                    method.display(ctx.db(), krate.edition(ctx.db()))
+                );
             } else if let ast::Expr::RangeExpr(..) = iterable {
                 // range expressions need to be parenthesized for the syntax to be correct
                 format_to!(buf, "({iterable})");
@@ -144,7 +148,7 @@ pub(crate) fn convert_for_loop_with_for_each(
 fn is_ref_and_impls_iter_method(
     sema: &hir::Semantics<'_, ide_db::RootDatabase>,
     iterable: &ast::Expr,
-) -> Option<(ast::Expr, hir::Name)> {
+) -> Option<(ast::Expr, hir::Name, hir::Crate)> {
     let ref_expr = match iterable {
         ast::Expr::RefExpr(r) => r,
         _ => return None,
@@ -172,7 +176,7 @@ fn is_ref_and_impls_iter_method(
         return None;
     }
 
-    Some((expr_behind_ref, wanted_method))
+    Some((expr_behind_ref, wanted_method, krate))
 }
 
 /// Whether iterable implements core::Iterator
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
index 370559792763f..8d4ff84084bd3 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
@@ -2,8 +2,8 @@ use either::Either;
 use ide_db::{defs::Definition, search::FileReference};
 use itertools::Itertools;
 use syntax::{
-    ast::{self, AstNode, HasGenericParams, HasVisibility},
-    match_ast, SyntaxKind,
+    ast::{self, AstNode, HasAttrs, HasGenericParams, HasVisibility},
+    match_ast, ted, SyntaxKind,
 };
 
 use crate::{assist_context::SourceChangeBuilder, AssistContext, AssistId, AssistKind, Assists};
@@ -87,9 +87,14 @@ fn edit_struct_def(
 ) {
     // Note that we don't need to consider macro files in this function because this is
     // currently not triggered for struct definitions inside macro calls.
-    let tuple_fields = record_fields
-        .fields()
-        .filter_map(|f| Some(ast::make::tuple_field(f.visibility(), f.ty()?)));
+    let tuple_fields = record_fields.fields().filter_map(|f| {
+        let field = ast::make::tuple_field(f.visibility(), f.ty()?).clone_for_update();
+        ted::insert_all(
+            ted::Position::first_child_of(field.syntax()),
+            f.attrs().map(|attr| attr.syntax().clone_subtree().clone_for_update().into()).collect(),
+        );
+        Some(field)
+    });
     let tuple_fields = ast::make::tuple_field_list(tuple_fields);
     let record_fields_text_range = record_fields.syntax().text_range();
 
@@ -975,6 +980,22 @@ impl HasAssoc for Struct {
         let Self::Assoc { value } = a;
     }
 }
+"#,
+        );
+    }
+
+    #[test]
+    fn fields_with_attrs() {
+        check_assist(
+            convert_named_struct_to_tuple_struct,
+            r#"
+pub struct $0Foo {
+    #[my_custom_attr]
+    value: u32,
+}
+"#,
+            r#"
+pub struct Foo(#[my_custom_attr] u32);
 "#,
         );
     }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs
index 0f0b4442d8ac2..91af9b05bbb85 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs
@@ -211,7 +211,7 @@ fn augment_references_with_imports(
                     )
                     .map(|mod_path| {
                         make::path_concat(
-                            mod_path_to_ast(&mod_path),
+                            mod_path_to_ast(&mod_path, target_module.krate().edition(ctx.db())),
                             make::path_from_text(struct_name),
                         )
                     });
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
index c72bd411d644e..f01b4ea0fd44f 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
@@ -1,8 +1,8 @@
 use either::Either;
 use ide_db::defs::{Definition, NameRefClass};
 use syntax::{
-    ast::{self, AstNode, HasGenericParams, HasVisibility},
-    match_ast, SyntaxKind, SyntaxNode,
+    ast::{self, AstNode, HasAttrs, HasGenericParams, HasVisibility},
+    match_ast, ted, SyntaxKind, SyntaxNode,
 };
 
 use crate::{assist_context::SourceChangeBuilder, AssistContext, AssistId, AssistKind, Assists};
@@ -83,10 +83,14 @@ fn edit_struct_def(
     tuple_fields: ast::TupleFieldList,
     names: Vec<ast::Name>,
 ) {
-    let record_fields = tuple_fields
-        .fields()
-        .zip(names)
-        .filter_map(|(f, name)| Some(ast::make::record_field(f.visibility(), name, f.ty()?)));
+    let record_fields = tuple_fields.fields().zip(names).filter_map(|(f, name)| {
+        let field = ast::make::record_field(f.visibility(), name, f.ty()?).clone_for_update();
+        ted::insert_all(
+            ted::Position::first_child_of(field.syntax()),
+            f.attrs().map(|attr| attr.syntax().clone_subtree().clone_for_update().into()).collect(),
+        );
+        Some(field)
+    });
     let record_fields = ast::make::record_field_list(record_fields);
     let tuple_fields_text_range = tuple_fields.syntax().text_range();
 
@@ -904,6 +908,19 @@ where
     T: Foo,
 { pub field1: T }
 
+"#,
+        );
+    }
+
+    #[test]
+    fn fields_with_attrs() {
+        check_assist(
+            convert_tuple_struct_to_named_struct,
+            r#"
+pub struct $0Foo(#[my_custom_attr] u32);
+"#,
+            r#"
+pub struct Foo { #[my_custom_attr] field1: u32 }
 "#,
         );
     }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs
index c34b684112aaf..434daa279cac6 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs
@@ -1,5 +1,6 @@
-use std::iter::once;
+use std::iter;
 
+use either::Either;
 use ide_db::syntax_helpers::node_ext::is_pattern_cond;
 use syntax::{
     ast::{
@@ -52,18 +53,30 @@ pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>)
         |edit| {
             let while_indent_level = IndentLevel::from_node(while_expr.syntax());
 
-            let break_block =
-                make::block_expr(once(make::expr_stmt(make::expr_break(None, None)).into()), None)
-                    .indent(while_indent_level);
+            let break_block = make::block_expr(
+                iter::once(make::expr_stmt(make::expr_break(None, None)).into()),
+                None,
+            )
+            .indent(while_indent_level);
             let block_expr = if is_pattern_cond(while_cond.clone()) {
                 let if_expr = make::expr_if(while_cond, while_body, Some(break_block.into()));
-                let stmts = once(make::expr_stmt(if_expr).into());
+                let stmts = iter::once(make::expr_stmt(if_expr).into());
                 make::block_expr(stmts, None)
             } else {
                 let if_cond = invert_boolean_expression(while_cond);
-                let if_expr = make::expr_if(if_cond, break_block, None);
-                let stmts = once(make::expr_stmt(if_expr).into()).chain(while_body.statements());
-                make::block_expr(stmts, while_body.tail_expr())
+                let if_expr = make::expr_if(if_cond, break_block, None).syntax().clone().into();
+                let elements = while_body.stmt_list().map_or_else(
+                    || Either::Left(iter::empty()),
+                    |stmts| {
+                        Either::Right(stmts.syntax().children_with_tokens().filter(|node_or_tok| {
+                            // Filter out the trailing expr
+                            !node_or_tok
+                                .as_node()
+                                .is_some_and(|node| ast::Expr::can_cast(node.kind()))
+                        }))
+                    },
+                );
+                make::hacky_block_expr(iter::once(if_expr).chain(elements), while_body.tail_expr())
             };
 
             let replacement = make::expr_loop(block_expr.indent(while_indent_level));
@@ -182,6 +195,74 @@ fn main() {
         bar();
     }
 }
+"#,
+        );
+    }
+
+    #[test]
+    fn preserve_comments() {
+        check_assist(
+            convert_while_to_loop,
+            r#"
+fn main() {
+    let mut i = 0;
+
+    $0while i < 5 {
+        // comment 1
+        dbg!(i);
+        // comment 2
+        i += 1;
+        // comment 3
+    }
+}
+"#,
+            r#"
+fn main() {
+    let mut i = 0;
+
+    loop {
+        if i >= 5 {
+            break;
+        }
+        // comment 1
+        dbg!(i);
+        // comment 2
+        i += 1;
+        // comment 3
+    }
+}
+"#,
+        );
+
+        check_assist(
+            convert_while_to_loop,
+            r#"
+fn main() {
+    let v = vec![1, 2, 3];
+    let iter = v.iter();
+
+    $0while let Some(i) = iter.next() {
+        // comment 1
+        dbg!(i);
+        // comment 2
+    }
+}
+"#,
+            r#"
+fn main() {
+    let v = vec![1, 2, 3];
+    let iter = v.iter();
+
+    loop {
+        if let Some(i) = iter.next() {
+            // comment 1
+            dbg!(i);
+            // comment 2
+        } else {
+            break;
+        }
+    }
+}
 "#,
         );
     }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs
index 095b8f958d09a..b229b750e88ba 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs
@@ -7,7 +7,7 @@ use ide_db::{
     FxHashMap, FxHashSet,
 };
 use itertools::Itertools;
-use syntax::{ast, ted, AstNode, SmolStr, SyntaxNode, ToSmolStr};
+use syntax::{ast, ted, AstNode, Edition, SmolStr, SyntaxNode, ToSmolStr};
 use text_edit::TextRange;
 
 use crate::{
@@ -81,6 +81,7 @@ struct StructEditData {
     has_private_members: bool,
     is_nested: bool,
     is_ref: bool,
+    edition: Edition,
 }
 
 fn collect_data(ident_pat: ast::IdentPat, ctx: &AssistContext<'_>) -> Option<StructEditData> {
@@ -145,6 +146,7 @@ fn collect_data(ident_pat: ast::IdentPat, ctx: &AssistContext<'_>) -> Option<Str
         names_in_scope,
         is_nested,
         is_ref,
+        edition: module.krate().edition(ctx.db()),
     })
 }
 
@@ -180,7 +182,7 @@ fn build_assignment_edit(
 ) -> AssignmentEdit {
     let ident_pat = builder.make_mut(data.ident_pat.clone());
 
-    let struct_path = mod_path_to_ast(&data.struct_def_path);
+    let struct_path = mod_path_to_ast(&data.struct_def_path, data.edition);
     let is_ref = ident_pat.ref_token().is_some();
     let is_mut = ident_pat.mut_token().is_some();
 
@@ -247,7 +249,7 @@ fn generate_field_names(ctx: &AssistContext<'_>, data: &StructEditData) -> Vec<(
             .visible_fields
             .iter()
             .map(|field| {
-                let field_name = field.name(ctx.db()).display_no_db().to_smolstr();
+                let field_name = field.name(ctx.db()).display_no_db(data.edition).to_smolstr();
                 let new_name = new_field_name(field_name.clone(), &data.names_in_scope);
                 (field_name, new_name)
             })
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs
index 9beb616d99bc3..3d6d37ad93d26 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs
@@ -66,7 +66,9 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext<'_>) ->
 
             let names_to_import = find_names_to_import(ctx, refs_in_target, imported_defs);
             let expanded = make::use_tree_list(names_to_import.iter().map(|n| {
-                let path = make::ext::ident_path(&n.display(ctx.db()).to_string());
+                let path = make::ext::ident_path(
+                    &n.display(ctx.db(), current_module.krate().edition(ctx.db())).to_string(),
+                );
                 make::use_tree(path, None, None, false)
             }))
             .clone_for_update();
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
index 9180d8dfcbb21..e4d347ef16bd6 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
@@ -1,11 +1,7 @@
 use crate::{utils, AssistContext, Assists};
-use hir::DescendPreference;
 use ide_db::{
     assists::{AssistId, AssistKind},
-    syntax_helpers::{
-        format_string::is_format_string,
-        format_string_exprs::{parse_format_exprs, Arg},
-    },
+    syntax_helpers::format_string_exprs::{parse_format_exprs, Arg},
 };
 use itertools::Itertools;
 use syntax::{
@@ -40,13 +36,7 @@ pub(crate) fn extract_expressions_from_format_string(
     let tt = fmt_string.syntax().parent().and_then(ast::TokenTree::cast)?;
     let tt_delimiter = tt.left_delimiter_token()?.kind();
 
-    let expanded_t = ast::String::cast(
-        ctx.sema
-            .descend_into_macros_single(DescendPreference::SameKind, fmt_string.syntax().clone()),
-    )?;
-    if !is_format_string(&expanded_t) {
-        return None;
-    }
+    let _ = ctx.sema.as_format_args_parts(&fmt_string)?;
 
     let (new_fmt, extracted_args) = parse_format_exprs(fmt_string.text()).ok()?;
     if extracted_args.is_empty() {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
index 0a2cb6d5ef8ad..ad0be896450af 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
@@ -3,8 +3,8 @@ use std::{iter, ops::RangeInclusive};
 use ast::make;
 use either::Either;
 use hir::{
-    DescendPreference, HasSource, HirDisplay, InFile, Local, LocalSource, ModuleDef,
-    PathResolution, Semantics, TypeInfo, TypeParam,
+    HasSource, HirDisplay, InFile, Local, LocalSource, ModuleDef, PathResolution, Semantics,
+    TypeInfo, TypeParam,
 };
 use ide_db::{
     defs::{Definition, NameRefClass},
@@ -23,7 +23,7 @@ use syntax::{
         self, edit::IndentLevel, edit_in_place::Indent, AstNode, AstToken, HasGenericParams,
         HasName,
     },
-    match_ast, ted, SyntaxElement,
+    match_ast, ted, Edition, SyntaxElement,
     SyntaxKind::{self, COMMENT},
     SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T,
 };
@@ -84,7 +84,6 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
     };
 
     let body = extraction_target(&node, range)?;
-    let (container_info, contains_tail_expr) = body.analyze_container(&ctx.sema)?;
 
     let (locals_used, self_param) = body.analyze(&ctx.sema);
 
@@ -92,6 +91,9 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
     let insert_after = node_to_insert_after(&body, anchor)?;
     let semantics_scope = ctx.sema.scope(&insert_after)?;
     let module = semantics_scope.module();
+    let edition = semantics_scope.krate().edition(ctx.db());
+
+    let (container_info, contains_tail_expr) = body.analyze_container(&ctx.sema, edition)?;
 
     let ret_ty = body.return_ty(ctx)?;
     let control_flow = body.external_control_flow(ctx, &container_info)?;
@@ -217,7 +219,11 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
                     );
 
                     if let Some(mod_path) = mod_path {
-                        insert_use(&scope, mod_path_to_ast(&mod_path), &ctx.config.insert_use);
+                        insert_use(
+                            &scope,
+                            mod_path_to_ast(&mod_path, edition),
+                            &ctx.config.insert_use,
+                        );
                     }
                 }
             }
@@ -238,7 +244,13 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
 fn make_function_name(semantics_scope: &hir::SemanticsScope<'_>) -> ast::NameRef {
     let mut names_in_scope = vec![];
     semantics_scope.process_all_names(&mut |name, _| {
-        names_in_scope.push(name.display(semantics_scope.db.upcast()).to_string())
+        names_in_scope.push(
+            name.display(
+                semantics_scope.db.upcast(),
+                semantics_scope.krate().edition(semantics_scope.db),
+            )
+            .to_string(),
+        )
     });
 
     let default_name = "fun_name";
@@ -366,6 +378,7 @@ struct ContainerInfo {
     ret_type: Option<hir::Type>,
     generic_param_lists: Vec<ast::GenericParamList>,
     where_clauses: Vec<ast::WhereClause>,
+    edition: Edition,
 }
 
 /// Control flow that is exported from extracted function
@@ -489,8 +502,8 @@ impl Param {
         }
     }
 
-    fn to_arg(&self, ctx: &AssistContext<'_>) -> ast::Expr {
-        let var = path_expr_from_local(ctx, self.var);
+    fn to_arg(&self, ctx: &AssistContext<'_>, edition: Edition) -> ast::Expr {
+        let var = path_expr_from_local(ctx, self.var, edition);
         match self.kind() {
             ParamKind::Value | ParamKind::MutValue => var,
             ParamKind::SharedRef => make::expr_ref(var, false),
@@ -498,8 +511,13 @@ impl Param {
         }
     }
 
-    fn to_param(&self, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Param {
-        let var = self.var.name(ctx.db()).display(ctx.db()).to_string();
+    fn to_param(
+        &self,
+        ctx: &AssistContext<'_>,
+        module: hir::Module,
+        edition: Edition,
+    ) -> ast::Param {
+        let var = self.var.name(ctx.db()).display(ctx.db(), edition).to_string();
         let var_name = make::name(&var);
         let pat = match self.kind() {
             ParamKind::MutValue => make::ident_pat(false, true, var_name),
@@ -520,7 +538,7 @@ impl Param {
 }
 
 impl TryKind {
-    fn of_ty(ty: hir::Type, ctx: &AssistContext<'_>) -> Option<TryKind> {
+    fn of_ty(ty: hir::Type, ctx: &AssistContext<'_>, edition: Edition) -> Option<TryKind> {
         if ty.is_unknown() {
             // We favour Result for `expr?`
             return Some(TryKind::Result { ty });
@@ -529,7 +547,7 @@ impl TryKind {
         let name = adt.name(ctx.db());
         // FIXME: use lang items to determine if it is std type or user defined
         //        E.g. if user happens to define type named `Option`, we would have false positive
-        let name = &name.display(ctx.db()).to_string();
+        let name = &name.display(ctx.db(), edition).to_string();
         match name.as_str() {
             "Option" => Some(TryKind::Option),
             "Result" => Some(TryKind::Result { ty }),
@@ -816,7 +834,7 @@ impl FunctionBody {
                         .descendants_with_tokens()
                         .filter_map(SyntaxElement::into_token)
                         .filter(|it| matches!(it.kind(), SyntaxKind::IDENT | T![self]))
-                        .flat_map(|t| sema.descend_into_macros(DescendPreference::None, t))
+                        .flat_map(|t| sema.descend_into_macros_exact(t))
                         .for_each(|t| add_name_if_local(t.parent().and_then(ast::NameRef::cast)));
                 }
             }
@@ -828,6 +846,7 @@ impl FunctionBody {
     fn analyze_container(
         &self,
         sema: &Semantics<'_, RootDatabase>,
+        edition: Edition,
     ) -> Option<(ContainerInfo, bool)> {
         let mut ancestors = self.parent()?.ancestors();
         let infer_expr_opt = |expr| sema.type_of_expr(&expr?).map(TypeInfo::adjusted);
@@ -927,6 +946,7 @@ impl FunctionBody {
                 ret_type: ty,
                 generic_param_lists,
                 where_clauses,
+                edition,
             },
             contains_tail_expr,
         ))
@@ -1015,7 +1035,7 @@ impl FunctionBody {
         let kind = match (try_expr, ret_expr, break_expr, continue_expr) {
             (Some(_), _, None, None) => {
                 let ret_ty = container_info.ret_type.clone()?;
-                let kind = TryKind::of_ty(ret_ty, ctx)?;
+                let kind = TryKind::of_ty(ret_ty, ctx, container_info.edition)?;
 
                 Some(FlowKind::Try { kind })
             }
@@ -1397,7 +1417,7 @@ fn fixup_call_site(builder: &mut SourceChangeBuilder, body: &FunctionBody) {
 fn make_call(ctx: &AssistContext<'_>, fun: &Function, indent: IndentLevel) -> SyntaxNode {
     let ret_ty = fun.return_type(ctx);
 
-    let args = make::arg_list(fun.params.iter().map(|param| param.to_arg(ctx)));
+    let args = make::arg_list(fun.params.iter().map(|param| param.to_arg(ctx, fun.mods.edition)));
     let name = fun.name.clone();
     let mut call_expr = if fun.self_param.is_some() {
         let self_arg = make::expr_path(make::ext::ident_path("self"));
@@ -1420,13 +1440,13 @@ fn make_call(ctx: &AssistContext<'_>, fun: &Function, indent: IndentLevel) -> Sy
         [] => None,
         [var] => {
             let name = var.local.name(ctx.db());
-            let name = make::name(&name.display(ctx.db()).to_string());
+            let name = make::name(&name.display(ctx.db(), fun.mods.edition).to_string());
             Some(ast::Pat::IdentPat(make::ident_pat(false, var.mut_usage_outside_body, name)))
         }
         vars => {
             let binding_pats = vars.iter().map(|var| {
                 let name = var.local.name(ctx.db());
-                let name = make::name(&name.display(ctx.db()).to_string());
+                let name = make::name(&name.display(ctx.db(), fun.mods.edition).to_string());
                 make::ident_pat(false, var.mut_usage_outside_body, name).into()
             });
             Some(ast::Pat::TuplePat(make::tuple_pat(binding_pats)))
@@ -1569,8 +1589,8 @@ impl FlowHandler {
     }
 }
 
-fn path_expr_from_local(ctx: &AssistContext<'_>, var: Local) -> ast::Expr {
-    let name = var.name(ctx.db()).display(ctx.db()).to_string();
+fn path_expr_from_local(ctx: &AssistContext<'_>, var: Local, edition: Edition) -> ast::Expr {
+    let name = var.name(ctx.db()).display(ctx.db(), edition).to_string();
     make::expr_path(make::ext::ident_path(&name))
 }
 
@@ -1581,7 +1601,7 @@ fn format_function(
     old_indent: IndentLevel,
 ) -> ast::Fn {
     let fun_name = make::name(&fun.name.text());
-    let params = fun.make_param_list(ctx, module);
+    let params = fun.make_param_list(ctx, module, fun.mods.edition);
     let ret_ty = fun.make_ret_ty(ctx, module);
     let body = make_body(ctx, old_indent, fun);
     let (generic_params, where_clause) = make_generic_params_and_where_clause(ctx, fun);
@@ -1597,6 +1617,7 @@ fn format_function(
         fun.control_flow.is_async,
         fun.mods.is_const,
         fun.control_flow.is_unsafe,
+        false,
     )
 }
 
@@ -1707,9 +1728,14 @@ impl Function {
         type_params_in_descendant_paths.chain(type_params_in_params).collect()
     }
 
-    fn make_param_list(&self, ctx: &AssistContext<'_>, module: hir::Module) -> ast::ParamList {
+    fn make_param_list(
+        &self,
+        ctx: &AssistContext<'_>,
+        module: hir::Module,
+        edition: Edition,
+    ) -> ast::ParamList {
         let self_param = self.self_param.clone();
-        let params = self.params.iter().map(|param| param.to_param(ctx, module));
+        let params = self.params.iter().map(|param| param.to_param(ctx, module, edition));
         make::param_list(self_param, params)
     }
 
@@ -1842,10 +1868,12 @@ fn make_body(ctx: &AssistContext<'_>, old_indent: IndentLevel, fun: &Function) -
                 None => match fun.outliving_locals.as_slice() {
                     [] => {}
                     [var] => {
-                        tail_expr = Some(path_expr_from_local(ctx, var.local));
+                        tail_expr = Some(path_expr_from_local(ctx, var.local, fun.mods.edition));
                     }
                     vars => {
-                        let exprs = vars.iter().map(|var| path_expr_from_local(ctx, var.local));
+                        let exprs = vars
+                            .iter()
+                            .map(|var| path_expr_from_local(ctx, var.local, fun.mods.edition));
                         let expr = make::expr_tuple(exprs);
                         tail_expr = Some(expr);
                     }
@@ -5622,7 +5650,7 @@ fn func<T: Debug>(i: Struct<'_, T>) {
     fun_name(i);
 }
 
-fn $0fun_name(i: Struct<T>) {
+fn $0fun_name(i: Struct<'_, T>) {
     foo(i);
 }
 "#,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
index a62fdeb617394..615b5d3f98b55 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -1,7 +1,7 @@
 use std::iter;
 
 use either::Either;
-use hir::{Module, ModuleDef, Name, Variant};
+use hir::{HasCrate, Module, ModuleDef, Name, Variant};
 use ide_db::{
     defs::Definition,
     helpers::mod_path_to_ast,
@@ -16,7 +16,7 @@ use syntax::{
         self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, HasAttrs, HasGenericParams,
         HasName, HasVisibility,
     },
-    match_ast, ted, SyntaxElement,
+    match_ast, ted, Edition, SyntaxElement,
     SyntaxKind::*,
     SyntaxNode, T,
 };
@@ -58,6 +58,7 @@ pub(crate) fn extract_struct_from_enum_variant(
         "Extract struct from enum variant",
         target,
         |builder| {
+            let edition = enum_hir.krate(ctx.db()).edition(ctx.db());
             let variant_hir_name = variant_hir.name(ctx.db());
             let enum_module_def = ModuleDef::from(enum_hir);
             let usages = Definition::Variant(variant_hir).usages(&ctx.sema).all();
@@ -82,7 +83,7 @@ pub(crate) fn extract_struct_from_enum_variant(
                     references,
                 );
                 processed.into_iter().for_each(|(path, node, import)| {
-                    apply_references(ctx.config.insert_use, path, node, import)
+                    apply_references(ctx.config.insert_use, path, node, import, edition)
                 });
             }
             builder.edit_file(ctx.file_id());
@@ -98,7 +99,7 @@ pub(crate) fn extract_struct_from_enum_variant(
                     references,
                 );
                 processed.into_iter().for_each(|(path, node, import)| {
-                    apply_references(ctx.config.insert_use, path, node, import)
+                    apply_references(ctx.config.insert_use, path, node, import, edition)
                 });
             }
 
@@ -169,7 +170,7 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Va
             ),
             _ => false,
         })
-        .any(|(name, _)| name.display(db).to_string() == variant_name.to_string())
+        .any(|(name, _)| name.eq_ident(variant_name.text().as_str()))
 }
 
 fn extract_generic_params(
@@ -359,9 +360,10 @@ fn apply_references(
     segment: ast::PathSegment,
     node: SyntaxNode,
     import: Option<(ImportScope, hir::ModPath)>,
+    edition: Edition,
 ) {
     if let Some((scope, path)) = import {
-        insert_use(&scope, mod_path_to_ast(&path), &insert_use_cfg);
+        insert_use(&scope, mod_path_to_ast(&path, edition), &insert_use_cfg);
     }
     // deep clone to prevent cycle
     let path = make::path_from_segments(iter::once(segment.clone_subtree()), false);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
index 0ef71a386614f..5ae75bb1ff8e0 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
@@ -20,7 +20,7 @@ use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
 // ->
 // ```
 // fn main() {
-//     let $0var_name = (1 + 2);
+//     let $0var_name = 1 + 2;
 //     var_name * 4;
 // }
 // ```
@@ -58,9 +58,30 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
     }
 
     let parent = to_extract.syntax().parent().and_then(ast::Expr::cast);
-    let needs_adjust = parent
-        .as_ref()
-        .map_or(false, |it| matches!(it, ast::Expr::FieldExpr(_) | ast::Expr::MethodCallExpr(_)));
+    // Any expression that autoderefs may need adjustment.
+    let mut needs_adjust = parent.as_ref().map_or(false, |it| match it {
+        ast::Expr::FieldExpr(_)
+        | ast::Expr::MethodCallExpr(_)
+        | ast::Expr::CallExpr(_)
+        | ast::Expr::AwaitExpr(_) => true,
+        ast::Expr::IndexExpr(index) if index.base().as_ref() == Some(&to_extract) => true,
+        _ => false,
+    });
+    let mut to_extract_no_ref = peel_parens(to_extract.clone());
+    let needs_ref = needs_adjust
+        && match &to_extract_no_ref {
+            ast::Expr::FieldExpr(_)
+            | ast::Expr::IndexExpr(_)
+            | ast::Expr::MacroExpr(_)
+            | ast::Expr::ParenExpr(_)
+            | ast::Expr::PathExpr(_) => true,
+            ast::Expr::PrefixExpr(prefix) if prefix.op_kind() == Some(ast::UnaryOp::Deref) => {
+                to_extract_no_ref = prefix.expr()?;
+                needs_adjust = false;
+                false
+            }
+            _ => false,
+        };
 
     let anchor = Anchor::from(&to_extract)?;
     let target = to_extract.syntax().text_range();
@@ -87,22 +108,28 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
                 Some(ast::Expr::RefExpr(expr)) if expr.mut_token().is_some() => {
                     make::ident_pat(false, true, make::name(&var_name))
                 }
+                _ if needs_adjust
+                    && !needs_ref
+                    && ty.as_ref().is_some_and(|ty| ty.is_mutable_reference()) =>
+                {
+                    make::ident_pat(false, true, make::name(&var_name))
+                }
                 _ => make::ident_pat(false, false, make::name(&var_name)),
             };
 
-            let to_extract = match ty.as_ref().filter(|_| needs_adjust) {
+            let to_extract_no_ref = match ty.as_ref().filter(|_| needs_ref) {
                 Some(receiver_type) if receiver_type.is_mutable_reference() => {
-                    make::expr_ref(to_extract, true)
+                    make::expr_ref(to_extract_no_ref, true)
                 }
                 Some(receiver_type) if receiver_type.is_reference() => {
-                    make::expr_ref(to_extract, false)
+                    make::expr_ref(to_extract_no_ref, false)
                 }
-                _ => to_extract,
+                _ => to_extract_no_ref,
             };
 
             let expr_replace = edit.make_syntax_mut(expr_replace);
             let let_stmt =
-                make::let_stmt(ident_pat.into(), None, Some(to_extract)).clone_for_update();
+                make::let_stmt(ident_pat.into(), None, Some(to_extract_no_ref)).clone_for_update();
             let name_expr = make::expr_path(make::ext::ident_path(&var_name)).clone_for_update();
 
             match anchor {
@@ -202,6 +229,14 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
     )
 }
 
+fn peel_parens(mut expr: ast::Expr) -> ast::Expr {
+    while let ast::Expr::ParenExpr(parens) = &expr {
+        let Some(expr_inside) = parens.expr() else { break };
+        expr = expr_inside;
+    }
+    expr
+}
+
 /// Check whether the node is a valid expression which can be extracted to a variable.
 /// In general that's true for any expression, but in some cases that would produce invalid code.
 fn valid_target_expr(node: SyntaxNode) -> Option<ast::Expr> {
@@ -1220,6 +1255,45 @@ fn foo(s: &S) {
         );
     }
 
+    #[test]
+    fn test_extract_var_index_deref() {
+        check_assist(
+            extract_variable,
+            r#"
+//- minicore: index
+struct X;
+
+impl std::ops::Index<usize> for X {
+    type Output = i32;
+    fn index(&self) -> &Self::Output { 0 }
+}
+
+struct S {
+    sub: X
+}
+
+fn foo(s: &S) {
+    $0s.sub$0[0];
+}"#,
+            r#"
+struct X;
+
+impl std::ops::Index<usize> for X {
+    type Output = i32;
+    fn index(&self) -> &Self::Output { 0 }
+}
+
+struct S {
+    sub: X
+}
+
+fn foo(s: &S) {
+    let $0sub = &s.sub;
+    sub[0];
+}"#,
+        );
+    }
+
     #[test]
     fn test_extract_var_reference_parameter_deep_nesting() {
         check_assist(
@@ -1461,4 +1535,60 @@ fn foo() {
 }"#,
         );
     }
+
+    #[test]
+    fn generates_no_ref_on_calls() {
+        check_assist(
+            extract_variable,
+            r#"
+struct S;
+impl S {
+    fn do_work(&mut self) {}
+}
+fn bar() -> S { S }
+fn foo() {
+    $0bar()$0.do_work();
+}"#,
+            r#"
+struct S;
+impl S {
+    fn do_work(&mut self) {}
+}
+fn bar() -> S { S }
+fn foo() {
+    let mut $0bar = bar();
+    bar.do_work();
+}"#,
+        );
+    }
+
+    #[test]
+    fn generates_no_ref_for_deref() {
+        check_assist(
+            extract_variable,
+            r#"
+struct S;
+impl S {
+    fn do_work(&mut self) {}
+}
+fn bar() -> S { S }
+fn foo() {
+    let v = &mut &mut bar();
+    $0(**v)$0.do_work();
+}
+"#,
+            r#"
+struct S;
+impl S {
+    fn do_work(&mut self) {}
+}
+fn bar() -> S { S }
+fn foo() {
+    let v = &mut &mut bar();
+    let $0s = *v;
+    s.do_work();
+}
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fill_record_pattern_fields.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fill_record_pattern_fields.rs
index 758f50d3f47bf..ee321864805e7 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fill_record_pattern_fields.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fill_record_pattern_fields.rs
@@ -45,8 +45,9 @@ pub(crate) fn fill_record_pattern_fields(acc: &mut Assists, ctx: &AssistContext<
     let new_field_list =
         make::record_pat_field_list(old_field_list.fields(), None).clone_for_update();
     for (f, _) in missing_fields.iter() {
+        let edition = ctx.sema.scope(record_pat.syntax())?.krate().edition(ctx.db());
         let field = make::record_pat_field_shorthand(make::name_ref(
-            &f.name(ctx.sema.db).display_no_db().to_smolstr(),
+            &f.name(ctx.sema.db).display_no_db(edition).to_smolstr(),
         ));
         new_field_list.add_field(field.clone_for_update());
     }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs
index 9950f9c14749a..7a92d8911bf85 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs
@@ -4,7 +4,7 @@ use hir::{
 use ide_db::FileId;
 use syntax::{
     ast::{self, edit_in_place::HasVisibilityEdit, make, HasVisibility as _},
-    AstNode, TextRange, ToSmolStr,
+    AstNode, TextRange,
 };
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -48,7 +48,7 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>)
     let (_, def) = module
         .scope(ctx.db(), None)
         .into_iter()
-        .find(|(name, _)| name.display_no_db().to_smolstr() == name_ref.text().as_str())?;
+        .find(|(name, _)| name.eq_ident(name_ref.text().as_str()))?;
     let ScopeDef::ModuleDef(def) = def else {
         return None;
     };
@@ -71,7 +71,10 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>)
     let assist_label = match target_name {
         None => format!("Change visibility to {missing_visibility}"),
         Some(name) => {
-            format!("Change visibility of {} to {missing_visibility}", name.display(ctx.db()))
+            format!(
+                "Change visibility of {} to {missing_visibility}",
+                name.display(ctx.db(), current_module.krate().edition(ctx.db()))
+            )
         }
     };
 
@@ -92,6 +95,7 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext<'_>
     let (record_field_def, _, _) = ctx.sema.resolve_record_field(&record_field)?;
 
     let current_module = ctx.sema.scope(record_field.syntax())?.module();
+    let current_edition = current_module.krate().edition(ctx.db());
     let visibility = record_field_def.visibility(ctx.db());
     if visibility.is_visible_from(ctx.db(), current_module.into()) {
         return None;
@@ -123,8 +127,8 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext<'_>
     let target_name = record_field_def.name(ctx.db());
     let assist_label = format!(
         "Change visibility of {}.{} to {missing_visibility}",
-        parent_name.display(ctx.db()),
-        target_name.display(ctx.db())
+        parent_name.display(ctx.db(), current_edition),
+        target_name.display(ctx.db(), current_edition)
     );
 
     acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |edit| {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs
index 2150003bc1437..081e36b4ff61c 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs
@@ -51,6 +51,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
     let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
     let strukt_name = strukt.name()?;
     let current_module = ctx.sema.scope(strukt.syntax())?.module();
+    let current_edition = current_module.krate().edition(ctx.db());
 
     let (field_name, field_ty, target) = match ctx.find_node_at_offset::<ast::RecordField>() {
         Some(field) => {
@@ -89,7 +90,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
     methods.sort_by(|(a, _), (b, _)| a.cmp(b));
     for (name, method) in methods {
         let adt = ast::Adt::Struct(strukt.clone());
-        let name = name.display(ctx.db()).to_string();
+        let name = name.display(ctx.db(), current_edition).to_string();
         // if `find_struct_impl` returns None, that means that a function named `name` already exists.
         let Some(impl_def) = find_struct_impl(ctx, &adt, std::slice::from_ref(&name)) else {
             continue;
@@ -121,6 +122,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
                 let is_async = method_source.async_token().is_some();
                 let is_const = method_source.const_token().is_some();
                 let is_unsafe = method_source.unsafe_token().is_some();
+                let is_gen = method_source.gen_token().is_some();
 
                 let fn_name = make::name(&name);
 
@@ -153,6 +155,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
                     is_async,
                     is_const,
                     is_unsafe,
+                    is_gen,
                 )
                 .clone_for_update();
 
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs
index 5a3457e5b7a4a..bf4ce5c907e80 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs
@@ -22,7 +22,7 @@ use syntax::{
         WherePred,
     },
     ted::{self, Position},
-    AstNode, NodeOrToken, SmolStr, SyntaxKind, ToSmolStr,
+    AstNode, Edition, NodeOrToken, SmolStr, SyntaxKind, ToSmolStr,
 };
 
 // Assist: generate_delegate_trait
@@ -109,6 +109,7 @@ struct Field {
     ty: ast::Type,
     range: syntax::TextRange,
     impls: Vec<Delegee>,
+    edition: Edition,
 }
 
 impl Field {
@@ -119,6 +120,7 @@ impl Field {
         let db = ctx.sema.db;
 
         let module = ctx.sema.file_to_module_def(ctx.file_id())?;
+        let edition = module.krate().edition(ctx.db());
 
         let (name, range, ty) = match f {
             Either::Left(f) => {
@@ -147,7 +149,7 @@ impl Field {
             }
         }
 
-        Some(Field { name, ty, range, impls })
+        Some(Field { name, ty, range, impls, edition })
     }
 }
 
@@ -163,18 +165,18 @@ enum Delegee {
 }
 
 impl Delegee {
-    fn signature(&self, db: &dyn HirDatabase) -> String {
+    fn signature(&self, db: &dyn HirDatabase, edition: Edition) -> String {
         let mut s = String::new();
 
         let (Delegee::Bound(it) | Delegee::Impls(it, _)) = self;
 
         for m in it.module(db).path_to_root(db).iter().rev() {
             if let Some(name) = m.name(db) {
-                s.push_str(&format!("{}::", name.display_no_db().to_smolstr()));
+                s.push_str(&format!("{}::", name.display_no_db(edition).to_smolstr()));
             }
         }
 
-        s.push_str(&it.name(db).display_no_db().to_smolstr());
+        s.push_str(&it.name(db).display_no_db(edition).to_smolstr());
         s
     }
 }
@@ -212,9 +214,11 @@ impl Struct {
             // if self.hir_ty.impls_trait(db, trait_, &[]) {
             //     continue;
             // }
-            let signature = delegee.signature(db);
+            let signature = delegee.signature(db, field.edition);
 
-            let Some(delegate) = generate_impl(ctx, self, &field.ty, &field.name, delegee) else {
+            let Some(delegate) =
+                generate_impl(ctx, self, &field.ty, &field.name, delegee, field.edition)
+            else {
                 continue;
             };
 
@@ -240,6 +244,7 @@ fn generate_impl(
     field_ty: &ast::Type,
     field_name: &str,
     delegee: &Delegee,
+    edition: Edition,
 ) -> Option<ast::Impl> {
     let delegate: ast::Impl;
     let db = ctx.db();
@@ -259,7 +264,7 @@ fn generate_impl(
                 strukt_params.clone(),
                 strukt_params.map(|params| params.to_generic_args()),
                 delegee.is_auto(db),
-                make::ty(&delegee.name(db).display_no_db().to_smolstr()),
+                make::ty(&delegee.name(db).display_no_db(edition).to_smolstr()),
                 strukt_ty,
                 bound_def.where_clause(),
                 ast_strukt.where_clause(),
@@ -350,7 +355,7 @@ fn generate_impl(
             let type_gen_args = strukt_params.clone().map(|params| params.to_generic_args());
 
             let path_type =
-                make::ty(&trait_.name(db).display_no_db().to_smolstr()).clone_for_update();
+                make::ty(&trait_.name(db).display_no_db(edition).to_smolstr()).clone_for_update();
             transform_impl(ctx, ast_strukt, &old_impl, &transform_args, path_type.syntax())?;
 
             // 3) Generate delegate trait impl
@@ -735,6 +740,7 @@ fn func_assoc_item(
         item.async_token().is_some(),
         item.const_token().is_some(),
         item.unsafe_token().is_some(),
+        item.gen_token().is_some(),
     )
     .clone_for_update();
 
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs
index 2ac7057fe7946..e558bb6da89bc 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs
@@ -4,7 +4,7 @@ use hir::{ModPath, ModuleDef};
 use ide_db::{famous_defs::FamousDefs, RootDatabase};
 use syntax::{
     ast::{self, HasName},
-    AstNode, SyntaxNode,
+    AstNode, Edition, SyntaxNode,
 };
 
 use crate::{
@@ -77,6 +77,7 @@ fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
                 field_name.syntax(),
                 deref_type_to_generate,
                 trait_path,
+                module.krate().edition(ctx.db()),
             )
         },
     )
@@ -117,6 +118,7 @@ fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()
                 field_list_index,
                 deref_type_to_generate,
                 trait_path,
+                module.krate().edition(ctx.db()),
             )
         },
     )
@@ -130,6 +132,7 @@ fn generate_edit(
     field_name: impl Display,
     deref_type: DerefType,
     trait_path: ModPath,
+    edition: Edition,
 ) {
     let start_offset = strukt.syntax().text_range().end();
     let impl_code = match deref_type {
@@ -147,8 +150,11 @@ fn generate_edit(
         ),
     };
     let strukt_adt = ast::Adt::Struct(strukt);
-    let deref_impl =
-        generate_trait_impl_text(&strukt_adt, &trait_path.display(db).to_string(), &impl_code);
+    let deref_impl = generate_trait_impl_text(
+        &strukt_adt,
+        &trait_path.display(db, edition).to_string(),
+        &impl_code,
+    );
     edit.insert(start_offset, deref_impl);
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs
index 51dd4884547b5..c5c70c9f8eb6f 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs
@@ -5,7 +5,7 @@ use stdx::{format_to, to_lower_snake_case};
 use syntax::{
     algo::skip_whitespace_token,
     ast::{self, edit::IndentLevel, HasDocComments, HasGenericArgs, HasName},
-    match_ast, AstNode, AstToken,
+    match_ast, AstNode, AstToken, Edition,
 };
 
 use crate::assist_context::{AssistContext, Assists};
@@ -139,7 +139,8 @@ fn make_example_for_fn(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option<St
 
     let mut example = String::new();
 
-    let use_path = build_path(ast_func, ctx)?;
+    let edition = ctx.sema.scope(ast_func.syntax())?.krate().edition(ctx.db());
+    let use_path = build_path(ast_func, ctx, edition)?;
     let is_unsafe = ast_func.unsafe_token().is_some();
     let param_list = ast_func.param_list()?;
     let ref_mut_params = ref_mut_params(&param_list);
@@ -472,13 +473,13 @@ fn string_vec_from(string_array: &[&str]) -> Vec<String> {
 }
 
 /// Helper function to build the path of the module in the which is the node
-fn build_path(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option<String> {
+fn build_path(ast_func: &ast::Fn, ctx: &AssistContext<'_>, edition: Edition) -> Option<String> {
     let crate_name = crate_name(ast_func, ctx)?;
     let leaf = self_partial_type(ast_func)
         .or_else(|| ast_func.name().map(|n| n.to_string()))
         .unwrap_or_else(|| "*".into());
     let module_def: ModuleDef = ctx.sema.to_def(ast_func)?.module(ctx.db()).into();
-    match module_def.canonical_path(ctx.db()) {
+    match module_def.canonical_path(ctx.db(), edition) {
         Some(path) => Some(format!("{crate_name}::{path}::{leaf}")),
         None => Some(format!("{crate_name}::{leaf}")),
     }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
index b2980d5c63010..76a647807cb49 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
@@ -17,7 +17,7 @@ use syntax::{
         self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, BlockExpr, CallExpr,
         HasArgList, HasGenericParams, HasModuleItem, HasTypeBounds,
     },
-    ted, SyntaxKind, SyntaxNode, TextRange, T,
+    ted, Edition, SyntaxKind, SyntaxNode, TextRange, T,
 };
 
 use crate::{
@@ -175,6 +175,7 @@ fn add_func_to_accumulator(
         edit.edit_file(file);
 
         let target = function_builder.target.clone();
+        let edition = function_builder.target_edition;
         let func = function_builder.render(ctx.config.snippet_cap, edit);
 
         if let Some(adt) =
@@ -183,7 +184,7 @@ fn add_func_to_accumulator(
         {
             let name = make::ty_path(make::ext::ident_path(&format!(
                 "{}",
-                adt.name(ctx.db()).display(ctx.db())
+                adt.name(ctx.db()).display(ctx.db(), edition)
             )));
 
             // FIXME: adt may have generic params.
@@ -222,6 +223,7 @@ struct FunctionBuilder {
     should_focus_return_type: bool,
     visibility: Visibility,
     is_async: bool,
+    target_edition: Edition,
 }
 
 impl FunctionBuilder {
@@ -237,6 +239,7 @@ impl FunctionBuilder {
     ) -> Option<Self> {
         let target_module =
             target_module.or_else(|| ctx.sema.scope(target.syntax()).map(|it| it.module()))?;
+        let target_edition = target_module.krate().edition(ctx.db());
 
         let current_module = ctx.sema.scope(call.syntax())?.module();
         let visibility = calculate_necessary_visibility(current_module, target_module, ctx);
@@ -258,7 +261,9 @@ impl FunctionBuilder {
 
         // If generated function has the name "new" and is an associated function, we generate fn body
         // as a constructor and assume a "Self" return type.
-        if let Some(body) = make_fn_body_as_new_function(ctx, &fn_name.text(), adt_info) {
+        if let Some(body) =
+            make_fn_body_as_new_function(ctx, &fn_name.text(), adt_info, target_edition)
+        {
             ret_type = Some(make::ret_type(make::ty_path(make::ext::ident_path("Self"))));
             should_focus_return_type = false;
             fn_body = body;
@@ -288,6 +293,7 @@ impl FunctionBuilder {
             should_focus_return_type,
             visibility,
             is_async,
+            target_edition,
         })
     }
 
@@ -299,6 +305,8 @@ impl FunctionBuilder {
         target_module: Module,
         target: GeneratedFunctionTarget,
     ) -> Option<Self> {
+        let target_edition = target_module.krate().edition(ctx.db());
+
         let current_module = ctx.sema.scope(call.syntax())?.module();
         let visibility = calculate_necessary_visibility(current_module, target_module, ctx);
 
@@ -336,6 +344,7 @@ impl FunctionBuilder {
             should_focus_return_type,
             visibility,
             is_async,
+            target_edition,
         })
     }
 
@@ -356,6 +365,7 @@ impl FunctionBuilder {
             self.is_async,
             false, // FIXME : const and unsafe are not handled yet.
             false,
+            false,
         )
         .clone_for_update();
 
@@ -425,6 +435,7 @@ fn make_fn_body_as_new_function(
     ctx: &AssistContext<'_>,
     fn_name: &str,
     adt_info: &Option<AdtInfo>,
+    edition: Edition,
 ) -> Option<ast::BlockExpr> {
     if fn_name != "new" {
         return None;
@@ -441,7 +452,10 @@ fn make_fn_body_as_new_function(
                     .iter()
                     .map(|field| {
                         make::record_expr_field(
-                            make::name_ref(&format!("{}", field.name(ctx.db()).display(ctx.db()))),
+                            make::name_ref(&format!(
+                                "{}",
+                                field.name(ctx.db()).display(ctx.db(), edition)
+                            )),
                             Some(placeholder_expr.clone()),
                         )
                     })
@@ -1102,8 +1116,9 @@ fn fn_arg_type(
 
         if ty.is_reference() || ty.is_mutable_reference() {
             let famous_defs = &FamousDefs(&ctx.sema, ctx.sema.scope(fn_arg.syntax())?.krate());
+            let target_edition = target_module.krate().edition(ctx.db());
             convert_reference_type(ty.strip_references(), ctx.db(), famous_defs)
-                .map(|conversion| conversion.convert_type(ctx.db()).to_string())
+                .map(|conversion| conversion.convert_type(ctx.db(), target_edition).to_string())
                 .or_else(|| ty.display_source_code(ctx.db(), target_module.into(), true).ok())
         } else {
             ty.display_source_code(ctx.db(), target_module.into(), true).ok()
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
index 60214aaaf69ea..c879a4a3d959e 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
@@ -233,7 +233,7 @@ fn generate_getter_from_info(
                 .map(|conversion| {
                     cov_mark::hit!(convert_reference_type);
                     (
-                        conversion.convert_type(ctx.db()),
+                        conversion.convert_type(ctx.db(), krate.edition(ctx.db())),
                         conversion.getter(record_field_info.field_name.to_string()),
                     )
                 })
@@ -261,7 +261,19 @@ fn generate_getter_from_info(
     let ret_type = Some(make::ret_type(ty));
     let body = make::block_expr([], Some(body));
 
-    make::fn_(strukt.visibility(), fn_name, None, None, params, body, ret_type, false, false, false)
+    make::fn_(
+        strukt.visibility(),
+        fn_name,
+        None,
+        None,
+        params,
+        body,
+        ret_type,
+        false,
+        false,
+        false,
+        false,
+    )
 }
 
 fn generate_setter_from_info(info: &AssistInfo, record_field_info: &RecordFieldInfo) -> ast::Fn {
@@ -285,7 +297,19 @@ fn generate_setter_from_info(info: &AssistInfo, record_field_info: &RecordFieldI
     let body = make::block_expr([assign_stmt.into()], None);
 
     // Make the setter fn
-    make::fn_(strukt.visibility(), fn_name, None, None, params, body, None, false, false, false)
+    make::fn_(
+        strukt.visibility(),
+        fn_name,
+        None,
+        None,
+        params,
+        body,
+        None,
+        false,
+        false,
+        false,
+        false,
+    )
 }
 
 fn extract_and_parse(
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs
index b985b5e66c4df..70d14d6b95d85 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs
@@ -64,10 +64,13 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
                     ctx.config.import_path_config(),
                 )?;
 
+                let edition = current_module.krate().edition(ctx.db());
+
                 let expr = use_trivial_constructor(
                     ctx.sema.db,
-                    ide_db::helpers::mod_path_to_ast(&type_path),
+                    ide_db::helpers::mod_path_to_ast(&type_path, edition),
                     &ty,
+                    edition,
                 )?;
 
                 Some(make::record_expr_field(make::name_ref(&name.text()), Some(expr)))
@@ -112,6 +115,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
             false,
             false,
             false,
+            false,
         )
         .clone_for_update();
         fn_.indent(1.into());
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs
index 111ea50fdc941..f1c2acdd3ed91 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs
@@ -51,10 +51,13 @@ pub(crate) fn inline_const_as_literal(acc: &mut Assists, ctx: &AssistContext<'_>
             | ast::Expr::MatchExpr(_)
             | ast::Expr::MacroExpr(_)
             | ast::Expr::BinExpr(_)
-            | ast::Expr::CallExpr(_) => match konst.render_eval(ctx.sema.db) {
-                Ok(result) => result,
-                Err(_) => return None,
-            },
+            | ast::Expr::CallExpr(_) => {
+                let edition = ctx.sema.scope(variable.syntax())?.krate().edition(ctx.db());
+                match konst.render_eval(ctx.sema.db, edition) {
+                    Ok(result) => result,
+                    Err(_) => return None,
+                }
+            }
             _ => return None,
         };
 
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs
index 3057745a97b7f..743ea9476150d 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs
@@ -104,9 +104,13 @@ pub(crate) fn move_const_to_impl(acc: &mut Assists, ctx: &AssistContext<'_>) ->
             };
             builder.delete(range_to_delete);
 
-            let const_ref = format!("Self::{}", name.display(ctx.db()));
-            for range in usages.file_ranges().map(|it| it.range) {
-                builder.replace(range, const_ref.clone());
+            let usages = usages.iter().flat_map(|(file_id, usages)| {
+                let edition = file_id.edition();
+                usages.iter().map(move |usage| (edition, usage.range))
+            });
+            for (edition, range) in usages {
+                let const_ref = format!("Self::{}", name.display(ctx.db(), edition));
+                builder.replace(range, const_ref);
             }
 
             // Heuristically inserting the extracted const after the consecutive existing consts
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs
index 14381085a782f..8a7a06b380f51 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs
@@ -39,7 +39,7 @@ pub(crate) fn move_from_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
     }
 
     let target = source_file.syntax().text_range();
-    let module_name = module.name(ctx.db())?.display(ctx.db()).to_string();
+    let module_name = module.name(ctx.db())?.unescaped().display(ctx.db()).to_string();
     let path = format!("../{module_name}.rs");
     let dst = AnchoredPathBuf { anchor: ctx.file_id().into(), path };
     acc.add(
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs
index e679a68f4466d..9692b70592912 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs
@@ -61,7 +61,7 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_>) ->
                                 .string_value_unescape()
                                 .is_none() =>
                     {
-                        format_to!(buf, "{}/", name.display(db))
+                        format_to!(buf, "{}/", name.unescaped().display(db))
                     }
                     _ => (),
                 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs
index c89d54ff039df..2925e2334b44d 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs
@@ -39,7 +39,7 @@ pub(crate) fn move_to_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
     }
 
     let target = source_file.syntax().text_range();
-    let module_name = module.name(ctx.db())?.display(ctx.db()).to_string();
+    let module_name = module.name(ctx.db())?.unescaped().display(ctx.db()).to_string();
     let path = format!("./{module_name}/mod.rs");
     let dst = AnchoredPathBuf { anchor: ctx.file_id().into(), path };
     acc.add(
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs
index b1e98045fcfaa..14518c4d2cc4c 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs
@@ -42,6 +42,7 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) ->
     let resolved_call = ctx.sema.resolve_method_call(&call)?;
 
     let current_module = ctx.sema.scope(call.syntax())?.module();
+    let current_edition = current_module.krate().edition(ctx.db());
     let target_module_def = ModuleDef::from(resolved_call);
     let item_in_ns = ItemInNs::from(target_module_def);
     let receiver_path = current_module.find_path(
@@ -61,6 +62,7 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) ->
                 |replace_with: String| builder.replace(range, replace_with),
                 &receiver_path,
                 item_in_ns,
+                current_edition,
             )
         },
     );
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs
index d8e7da15d5bf8..ac88861fe4fdc 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs
@@ -8,6 +8,7 @@ use ide_db::{
     imports::import_assets::{ImportCandidate, LocatedImport},
 };
 use syntax::ast::HasGenericArgs;
+use syntax::Edition;
 use syntax::{
     ast,
     ast::{make, HasArgList},
@@ -93,6 +94,8 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
             NodeOrToken::Token(t) => t.parent()?,
         })
         .map(|scope| scope.module());
+    let current_edition =
+        current_module.map(|it| it.krate().edition(ctx.db())).unwrap_or(Edition::CURRENT);
     // prioritize more relevant imports
     proposed_imports.sort_by_key(|import| {
         Reverse(super::auto_import::relevance_score(ctx, import, current_module.as_ref()))
@@ -103,13 +106,14 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
         acc.add_group(
             &group_label,
             AssistId("qualify_path", AssistKind::QuickFix),
-            label(ctx.db(), candidate, &import),
+            label(ctx.db(), candidate, &import, current_edition),
             range,
             |builder| {
                 qualify_candidate.qualify(
                     |replace_with: String| builder.replace(range, replace_with),
                     &import.import_path,
                     import.item_to_import,
+                    current_edition,
                 )
             },
         );
@@ -130,8 +134,9 @@ impl QualifyCandidate<'_> {
         mut replacer: impl FnMut(String),
         import: &hir::ModPath,
         item: hir::ItemInNs,
+        edition: Edition,
     ) {
-        let import = mod_path_to_ast(import);
+        let import = mod_path_to_ast(import, edition);
         match self {
             QualifyCandidate::QualifierStart(segment, generics) => {
                 let generics = generics.as_ref().map_or_else(String::new, ToString::to_string);
@@ -203,7 +208,7 @@ fn find_trait_method(
     if let Some(hir::AssocItem::Function(method)) =
         trait_.items(db).into_iter().find(|item: &hir::AssocItem| {
             item.name(db)
-                .map(|name| name.display(db).to_string() == trait_method_name.to_string())
+                .map(|name| name.eq_ident(trait_method_name.text().as_str()))
                 .unwrap_or(false)
         })
     {
@@ -233,14 +238,19 @@ fn group_label(candidate: &ImportCandidate) -> GroupLabel {
     GroupLabel(format!("Qualify {name}"))
 }
 
-fn label(db: &RootDatabase, candidate: &ImportCandidate, import: &LocatedImport) -> String {
+fn label(
+    db: &RootDatabase,
+    candidate: &ImportCandidate,
+    import: &LocatedImport,
+    edition: Edition,
+) -> String {
     let import_path = &import.import_path;
 
     match candidate {
         ImportCandidate::Path(candidate) if candidate.qualifier.is_none() => {
-            format!("Qualify as `{}`", import_path.display(db))
+            format!("Qualify as `{}`", import_path.display(db, edition))
         }
-        _ => format!("Qualify with `{}`", import_path.display(db)),
+        _ => format!("Qualify with `{}`", import_path.display(db, edition)),
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
index cffa3f55c9092..0e9c463e02459 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
@@ -1,7 +1,7 @@
 use itertools::Itertools;
 use syntax::{
     ast::{self, make, AstNode, AstToken},
-    match_ast, ted, NodeOrToken, SyntaxElement, TextRange, TextSize, T,
+    match_ast, ted, Edition, NodeOrToken, SyntaxElement, TextRange, TextSize, T,
 };
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -77,7 +77,7 @@ fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Opt
     let input_expressions = input_expressions
         .into_iter()
         .filter_map(|(is_sep, group)| (!is_sep).then_some(group))
-        .map(|mut tokens| syntax::hacks::parse_expr_from_str(&tokens.join("")))
+        .map(|mut tokens| syntax::hacks::parse_expr_from_str(&tokens.join(""), Edition::CURRENT))
         .collect::<Option<Vec<ast::Expr>>>()?;
 
     let parent = macro_expr.syntax().parent()?;
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs
index 0256256697740..df7a5112f126b 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs
@@ -1,7 +1,7 @@
 use either::Either;
 use ide_db::FxHashMap;
 use itertools::Itertools;
-use syntax::{ast, ted, AstNode};
+use syntax::{ast, ted, AstNode, SmolStr, ToSmolStr};
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
 
@@ -25,8 +25,9 @@ pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
         path.syntax().parent().and_then(<Either<ast::RecordExpr, ast::RecordPat>>::cast)?;
 
     let ranks = compute_fields_ranks(&path, ctx)?;
-    let get_rank_of_field =
-        |of: Option<_>| *ranks.get(&of.unwrap_or_default()).unwrap_or(&usize::MAX);
+    let get_rank_of_field = |of: Option<SmolStr>| {
+        *ranks.get(of.unwrap_or_default().trim_start_matches("r#")).unwrap_or(&usize::MAX)
+    };
 
     let field_list = match &record {
         Either::Left(it) => Either::Left(it.record_expr_field_list()?),
@@ -36,7 +37,7 @@ pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
         Either::Left(it) => Either::Left((
             it.fields()
                 .sorted_unstable_by_key(|field| {
-                    get_rank_of_field(field.field_name().map(|it| it.to_string()))
+                    get_rank_of_field(field.field_name().map(|it| it.to_smolstr()))
                 })
                 .collect::<Vec<_>>(),
             it,
@@ -44,7 +45,7 @@ pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
         Either::Right(it) => Either::Right((
             it.fields()
                 .sorted_unstable_by_key(|field| {
-                    get_rank_of_field(field.field_name().map(|it| it.to_string()))
+                    get_rank_of_field(field.field_name().map(|it| it.to_smolstr()))
                 })
                 .collect::<Vec<_>>(),
             it,
@@ -97,7 +98,7 @@ fn compute_fields_ranks(
         .fields(ctx.db())
         .into_iter()
         .enumerate()
-        .map(|(idx, field)| (field.name(ctx.db()).display(ctx.db()).to_string(), idx))
+        .map(|(idx, field)| (field.name(ctx.db()).unescaped().display(ctx.db()).to_string(), idx))
         .collect();
 
     Some(res)
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs
index cf135f83e7b6a..ada89ce7c40ac 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs
@@ -77,7 +77,8 @@ pub(crate) fn reorder_impl_items(acc: &mut Assists, ctx: &AssistContext<'_>) ->
                 ast::AssocItem::MacroCall(_) => None,
             };
 
-            name.and_then(|n| ranks.get(&n.to_string()).copied()).unwrap_or(usize::MAX)
+            name.and_then(|n| ranks.get(n.text().as_str().trim_start_matches("r#")).copied())
+                .unwrap_or(usize::MAX)
         })
         .collect();
 
@@ -114,7 +115,7 @@ fn compute_item_ranks(
             .iter()
             .flat_map(|i| i.name(ctx.db()))
             .enumerate()
-            .map(|(idx, name)| (name.display(ctx.db()).to_string(), idx))
+            .map(|(idx, name)| (name.unescaped().display(ctx.db()).to_string(), idx))
             .collect(),
     )
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
index 5ff4af19fbf18..248f18789ce33 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -70,6 +70,7 @@ pub(crate) fn replace_derive_with_manual_impl(
 
     let current_module = ctx.sema.scope(adt.syntax())?.module();
     let current_crate = current_module.krate();
+    let current_edition = current_crate.edition(ctx.db());
 
     let found_traits = items_locator::items_with_name(
         &ctx.sema,
@@ -85,7 +86,7 @@ pub(crate) fn replace_derive_with_manual_impl(
         current_module
             .find_path(ctx.sema.db, hir::ModuleDef::Trait(trait_), ctx.config.import_path_config())
             .as_ref()
-            .map(mod_path_to_ast)
+            .map(|path| mod_path_to_ast(path, current_edition))
             .zip(Some(trait_))
     });
 
@@ -214,7 +215,7 @@ fn impl_def_from_trait(
     let impl_def = generate_trait_impl(adt, make::ty_path(trait_path.clone()));
 
     let first_assoc_item =
-        add_trait_assoc_items_to_impl(sema, &trait_items, trait_, &impl_def, target_scope);
+        add_trait_assoc_items_to_impl(sema, &trait_items, trait_, &impl_def, &target_scope);
 
     // Generate a default `impl` function body for the derived trait.
     if let ast::AssocItem::Fn(ref func) = first_assoc_item {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs
index d0aa835e79afd..65330b34c4ad2 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs
@@ -5,7 +5,7 @@ use ide_db::{
 };
 use syntax::{
     ast::{self, make, HasGenericArgs},
-    match_ast, ted, AstNode, SyntaxNode,
+    match_ast, ted, AstNode, Edition, SyntaxNode,
 };
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -29,32 +29,32 @@ pub(crate) fn replace_qualified_name_with_use(
     acc: &mut Assists,
     ctx: &AssistContext<'_>,
 ) -> Option<()> {
-    let path: ast::Path = ctx.find_node_at_offset()?;
+    let original_path: ast::Path = ctx.find_node_at_offset()?;
     // We don't want to mess with use statements
-    if path.syntax().ancestors().find_map(ast::UseTree::cast).is_some() {
+    if original_path.syntax().ancestors().find_map(ast::UseTree::cast).is_some() {
         cov_mark::hit!(not_applicable_in_use);
         return None;
     }
 
-    if path.qualifier().is_none() {
+    if original_path.qualifier().is_none() {
         cov_mark::hit!(dont_import_trivial_paths);
         return None;
     }
 
     // only offer replacement for non assoc items
-    match ctx.sema.resolve_path(&path)? {
+    match ctx.sema.resolve_path(&original_path)? {
         hir::PathResolution::Def(def) if def.as_assoc_item(ctx.sema.db).is_none() => (),
         _ => return None,
     }
     // then search for an import for the first path segment of what we want to replace
     // that way it is less likely that we import the item from a different location due re-exports
-    let module = match ctx.sema.resolve_path(&path.first_qualifier_or_self())? {
+    let module = match ctx.sema.resolve_path(&original_path.first_qualifier_or_self())? {
         hir::PathResolution::Def(module @ hir::ModuleDef::Module(_)) => module,
         _ => return None,
     };
 
     let starts_with_name_ref = !matches!(
-        path.first_segment().and_then(|it| it.kind()),
+        original_path.first_segment().and_then(|it| it.kind()),
         Some(
             ast::PathSegmentKind::CrateKw
                 | ast::PathSegmentKind::SuperKw
@@ -63,7 +63,7 @@ pub(crate) fn replace_qualified_name_with_use(
     );
     let path_to_qualifier = starts_with_name_ref
         .then(|| {
-            ctx.sema.scope(path.syntax())?.module().find_use_path(
+            ctx.sema.scope(original_path.syntax())?.module().find_use_path(
                 ctx.sema.db,
                 module,
                 ctx.config.insert_use.prefix_kind,
@@ -72,8 +72,8 @@ pub(crate) fn replace_qualified_name_with_use(
         })
         .flatten();
 
-    let scope = ImportScope::find_insert_use_container(path.syntax(), &ctx.sema)?;
-    let target = path.syntax().text_range();
+    let scope = ImportScope::find_insert_use_container(original_path.syntax(), &ctx.sema)?;
+    let target = original_path.syntax().text_range();
     acc.add(
         AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite),
         "Replace qualified path with use",
@@ -86,13 +86,19 @@ pub(crate) fn replace_qualified_name_with_use(
                 ImportScope::Module(it) => ImportScope::Module(builder.make_mut(it)),
                 ImportScope::Block(it) => ImportScope::Block(builder.make_mut(it)),
             };
-            shorten_paths(scope.as_syntax_node(), &path);
-            let path = drop_generic_args(&path);
+            shorten_paths(scope.as_syntax_node(), &original_path);
+            let path = drop_generic_args(&original_path);
+            let edition = ctx
+                .sema
+                .scope(original_path.syntax())
+                .map(|semantics_scope| semantics_scope.krate().edition(ctx.db()))
+                .unwrap_or(Edition::CURRENT);
             // stick the found import in front of the to be replaced path
-            let path = match path_to_qualifier.and_then(|it| mod_path_to_ast(&it).qualifier()) {
-                Some(qualifier) => make::path_concat(qualifier, path),
-                None => path,
-            };
+            let path =
+                match path_to_qualifier.and_then(|it| mod_path_to_ast(&it, edition).qualifier()) {
+                    Some(qualifier) => make::path_concat(qualifier, path),
+                    None => path,
+                };
             insert_use(&scope, path, &ctx.config.insert_use);
         },
     )
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs
index 4913cfdea9440..66671c934c4b7 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs
@@ -48,15 +48,17 @@ pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
 
     let mut formatter = |_: &hir::Type| String::from("todo!()");
 
+    let edition = scope.krate().edition(ctx.db());
     let paths = paths
         .into_iter()
         .filter_map(|path| {
-            path.gen_source_code(&scope, &mut formatter, ctx.config.import_path_config()).ok()
+            path.gen_source_code(&scope, &mut formatter, ctx.config.import_path_config(), edition)
+                .ok()
         })
         .unique();
 
     let macro_name = macro_call.name(ctx.sema.db);
-    let macro_name = macro_name.display(ctx.sema.db);
+    let macro_name = macro_name.display(ctx.sema.db, edition);
 
     for code in paths {
         acc.add_group(
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_async_sugar.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_async_sugar.rs
index 98975a324dc3b..9ab36bf7757d6 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_async_sugar.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_async_sugar.rs
@@ -141,7 +141,8 @@ pub(crate) fn desugar_async_into_impl_future(
         ModuleDef::Trait(future_trait),
         ctx.config.import_path_config(),
     )?;
-    let trait_path = trait_path.display(ctx.db());
+    let edition = scope.krate().edition(ctx.db());
+    let trait_path = trait_path.display(ctx.db(), edition);
 
     acc.add(
         AssistId("desugar_async_into_impl_future", AssistKind::RefactorRewrite),
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs
new file mode 100644
index 0000000000000..eedb2ea3b9ada
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs
@@ -0,0 +1,256 @@
+use ide_db::assists::{AssistId, AssistKind};
+use syntax::{
+    ast::{self, make},
+    ted, AstNode, T,
+};
+
+use crate::{AssistContext, Assists};
+
+// Assist: toggle_macro_delimiter
+//
+// Change macro delimiters in the order of `( -> { -> [ -> (`.
+//
+// ```
+// macro_rules! sth {
+//     () => {};
+// }
+//
+// sth!$0( );
+// ```
+// ->
+// ```
+// macro_rules! sth {
+//     () => {};
+// }
+//
+// sth!{ }
+// ```
+pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+    #[derive(Debug)]
+    enum MacroDelims {
+        LPar,
+        RPar,
+        LBra,
+        RBra,
+        LCur,
+        RCur,
+    }
+
+    let makro = ctx.find_node_at_offset::<ast::MacroCall>()?.clone_for_update();
+    let makro_text_range = makro.syntax().text_range();
+
+    let cursor_offset = ctx.offset();
+    let semicolon = makro.semicolon_token();
+    let token_tree = makro.token_tree()?;
+
+    let ltoken = token_tree.left_delimiter_token()?;
+    let rtoken = token_tree.right_delimiter_token()?;
+
+    if !ltoken.text_range().contains(cursor_offset) && !rtoken.text_range().contains(cursor_offset)
+    {
+        return None;
+    }
+
+    let token = match ltoken.kind() {
+        T!['{'] => MacroDelims::LCur,
+        T!['('] => MacroDelims::LPar,
+        T!['['] => MacroDelims::LBra,
+        T!['}'] => MacroDelims::RBra,
+        T![')'] => MacroDelims::RPar,
+        T!['}'] => MacroDelims::RCur,
+        _ => return None,
+    };
+
+    acc.add(
+        AssistId("toggle_macro_delimiter", AssistKind::Refactor),
+        match token {
+            MacroDelims::LPar => "Replace delimiters with braces",
+            MacroDelims::RPar => "Replace delimiters with braces",
+            MacroDelims::LBra => "Replace delimiters with parentheses",
+            MacroDelims::RBra => "Replace delimiters with parentheses",
+            MacroDelims::LCur => "Replace delimiters with brackets",
+            MacroDelims::RCur => "Replace delimiters with brackets",
+        },
+        token_tree.syntax().text_range(),
+        |builder| {
+            match token {
+                MacroDelims::LPar | MacroDelims::RPar => {
+                    ted::replace(ltoken, make::token(T!['{']));
+                    ted::replace(rtoken, make::token(T!['}']));
+                    if let Some(sc) = semicolon {
+                        ted::remove(sc);
+                    }
+                }
+                MacroDelims::LBra | MacroDelims::RBra => {
+                    ted::replace(ltoken, make::token(T!['(']));
+                    ted::replace(rtoken, make::token(T![')']));
+                }
+                MacroDelims::LCur | MacroDelims::RCur => {
+                    ted::replace(ltoken, make::token(T!['[']));
+                    ted::replace(rtoken, make::token(T![']']));
+                }
+            }
+            builder.replace(makro_text_range, makro.syntax().text());
+        },
+    )
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::{check_assist, check_assist_not_applicable};
+
+    use super::*;
+
+    #[test]
+    fn test_par() {
+        check_assist(
+            toggle_macro_delimiter,
+            r#"
+macro_rules! sth {
+    () => {};
+}
+
+sth!$0( );
+            "#,
+            r#"
+macro_rules! sth {
+    () => {};
+}
+
+sth!{ }
+            "#,
+        )
+    }
+
+    #[test]
+    fn test_braces() {
+        check_assist(
+            toggle_macro_delimiter,
+            r#"
+macro_rules! sth {
+    () => {};
+}
+
+sth!$0{ };
+            "#,
+            r#"
+macro_rules! sth {
+    () => {};
+}
+
+sth![ ];
+            "#,
+        )
+    }
+
+    #[test]
+    fn test_brackets() {
+        check_assist(
+            toggle_macro_delimiter,
+            r#"
+macro_rules! sth {
+    () => {};
+}
+
+sth!$0[ ];
+            "#,
+            r#"
+macro_rules! sth {
+    () => {};
+}
+
+sth!( );
+            "#,
+        )
+    }
+
+    #[test]
+    fn test_indent() {
+        check_assist(
+            toggle_macro_delimiter,
+            r#"
+mod abc {
+    macro_rules! sth {
+        () => {};
+    }
+
+    sth!$0{ };
+}
+            "#,
+            r#"
+mod abc {
+    macro_rules! sth {
+        () => {};
+    }
+
+    sth![ ];
+}
+            "#,
+        )
+    }
+
+    #[test]
+    fn test_unrelated_par() {
+        check_assist_not_applicable(
+            toggle_macro_delimiter,
+            r#"
+macro_rules! prt {
+    ($e:expr) => {{
+        println!("{}", stringify!{$e});
+    }};
+}
+
+prt!(($03 + 5));
+
+            "#,
+        )
+    }
+
+    #[test]
+    fn test_longer_macros() {
+        check_assist(
+            toggle_macro_delimiter,
+            r#"
+macro_rules! prt {
+    ($e:expr) => {{
+        println!("{}", stringify!{$e});
+    }};
+}
+
+prt!$0((3 + 5));
+"#,
+            r#"
+macro_rules! prt {
+    ($e:expr) => {{
+        println!("{}", stringify!{$e});
+    }};
+}
+
+prt!{(3 + 5)}
+"#,
+        )
+    }
+
+    // FIXME @alibektas : Inner macro_call is not seen as such. So this doesn't work.
+    #[test]
+    fn test_nested_macros() {
+        check_assist_not_applicable(
+            toggle_macro_delimiter,
+            r#"
+macro_rules! prt {
+    ($e:expr) => {{
+        println!("{}", stringify!{$e});
+    }};
+}
+
+macro_rules! abc {
+    ($e:expr) => {{
+        println!("{}", stringify!{$e});
+    }};
+}
+
+prt!{abc!($03 + 5)};
+"#,
+        )
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs
index de801279a0e1f..f3e7f5f416769 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs
@@ -128,10 +128,12 @@ fn update_expr_string_without_newline(expr_string: String) -> String {
 }
 
 fn update_expr_string_with_pat(expr_str: String, whitespace_pat: &[char]) -> String {
-    // Remove leading whitespace, index [1..] to remove the leading '{',
+    // Remove leading whitespace, index to remove the leading '{',
     // then continue to remove leading whitespace.
-    let expr_str =
-        expr_str.trim_start_matches(whitespace_pat)[1..].trim_start_matches(whitespace_pat);
+    // We cannot assume the `{` is the first character because there are block modifiers
+    // (`unsafe`, `async` etc.).
+    let after_open_brace_index = expr_str.find('{').map_or(0, |it| it + 1);
+    let expr_str = expr_str[after_open_brace_index..].trim_start_matches(whitespace_pat);
 
     // Remove trailing whitespace, index [..expr_str.len() - 1] to remove the trailing '}',
     // then continue to remove trailing whitespace.
@@ -824,6 +826,56 @@ fn main() {
     let a = 1;
     let x = foo;
 }
+"#,
+        );
+    }
+
+    #[test]
+    fn unwrap_block_with_modifiers() {
+        // https://github.com/rust-lang/rust-analyzer/issues/17964
+        check_assist(
+            unwrap_block,
+            r#"
+fn main() {
+    unsafe $0{
+        bar;
+    }
+}
+"#,
+            r#"
+fn main() {
+    bar;
+}
+"#,
+        );
+        check_assist(
+            unwrap_block,
+            r#"
+fn main() {
+    async move $0{
+        bar;
+    }
+}
+"#,
+            r#"
+fn main() {
+    bar;
+}
+"#,
+        );
+        check_assist(
+            unwrap_block,
+            r#"
+fn main() {
+    try $0{
+        bar;
+    }
+}
+"#,
+            r#"
+fn main() {
+    bar;
+}
 "#,
         );
     }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
index a9399ba6b7f47..c88cb3d5eaf05 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
@@ -116,6 +116,7 @@ mod handlers {
     mod bool_to_enum;
     mod change_visibility;
     mod convert_bool_then;
+    mod convert_closure_to_fn;
     mod convert_comment_block;
     mod convert_comment_from_or_to_doc;
     mod convert_from_to_tryfrom;
@@ -213,6 +214,7 @@ mod handlers {
     mod term_search;
     mod toggle_async_sugar;
     mod toggle_ignore;
+    mod toggle_macro_delimiter;
     mod unmerge_match_arm;
     mod unmerge_use;
     mod unnecessary_async;
@@ -245,6 +247,7 @@ mod handlers {
             toggle_async_sugar::sugar_impl_future_into_async,
             convert_comment_block::convert_comment_block,
             convert_comment_from_or_to_doc::convert_comment_from_or_to_doc,
+            convert_closure_to_fn::convert_closure_to_fn,
             convert_from_to_tryfrom::convert_from_to_tryfrom,
             convert_integer_literal::convert_integer_literal,
             convert_into_to_from::convert_into_to_from,
@@ -343,6 +346,7 @@ mod handlers {
             split_import::split_import,
             term_search::term_search,
             toggle_ignore::toggle_ignore,
+            toggle_macro_delimiter::toggle_macro_delimiter,
             unmerge_match_arm::unmerge_match_arm,
             unmerge_use::unmerge_use,
             unnecessary_async::unnecessary_async,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
index a2287b2977ddd..dce7bbf342f74 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
@@ -382,6 +382,40 @@ fn main() {
     )
 }
 
+#[test]
+fn doctest_convert_closure_to_fn() {
+    check_doc_test(
+        "convert_closure_to_fn",
+        r#####"
+//- minicore: copy
+struct String;
+impl String {
+    fn new() -> Self {}
+    fn push_str(&mut self, s: &str) {}
+}
+fn main() {
+    let mut s = String::new();
+    let closure = |$0a| s.push_str(a);
+    closure("abc");
+}
+"#####,
+        r#####"
+struct String;
+impl String {
+    fn new() -> Self {}
+    fn push_str(&mut self, s: &str) {}
+}
+fn main() {
+    let mut s = String::new();
+    fn closure(a: &str, s: &mut String) {
+        s.push_str(a)
+    }
+    closure("abc", &mut s);
+}
+"#####,
+    )
+}
+
 #[test]
 fn doctest_convert_for_loop_with_for_each() {
     check_doc_test(
@@ -994,7 +1028,7 @@ fn main() {
 "#####,
         r#####"
 fn main() {
-    let $0var_name = (1 + 2);
+    let $0var_name = 1 + 2;
     var_name * 4;
 }
 "#####,
@@ -3091,6 +3125,27 @@ fn arithmetics {
     )
 }
 
+#[test]
+fn doctest_toggle_macro_delimiter() {
+    check_doc_test(
+        "toggle_macro_delimiter",
+        r#####"
+macro_rules! sth {
+    () => {};
+}
+
+sth!$0( );
+"#####,
+        r#####"
+macro_rules! sth {
+    () => {};
+}
+
+sth!{ }
+"#####,
+    )
+}
+
 #[test]
 fn doctest_unmerge_match_arm() {
     check_doc_test(
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
index c67693ea2bb89..b8a6f3b6dbeb2 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
@@ -14,7 +14,7 @@ use syntax::{
         edit_in_place::{AttrsOwnerEdit, Indent, Removable},
         make, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace,
     },
-    ted, AstNode, AstToken, Direction, NodeOrToken, SourceFile,
+    ted, AstNode, AstToken, Direction, Edition, NodeOrToken, SourceFile,
     SyntaxKind::*,
     SyntaxNode, SyntaxToken, TextRange, TextSize, T,
 };
@@ -174,7 +174,7 @@ pub fn add_trait_assoc_items_to_impl(
     original_items: &[InFile<ast::AssocItem>],
     trait_: hir::Trait,
     impl_: &ast::Impl,
-    target_scope: hir::SemanticsScope<'_>,
+    target_scope: &hir::SemanticsScope<'_>,
 ) -> ast::AssocItem {
     let new_indent_level = IndentLevel::from_node(impl_.syntax()) + 1;
     let items = original_items.iter().map(|InFile { file_id, value: original_item }| {
@@ -195,7 +195,7 @@ pub fn add_trait_assoc_items_to_impl(
             // FIXME: Paths in nested macros are not handled well. See
             // `add_missing_impl_members::paths_in_nested_macro_should_get_transformed` test.
             let transform =
-                PathTransform::trait_impl(&target_scope, &source_scope, trait_, impl_.clone());
+                PathTransform::trait_impl(target_scope, &source_scope, trait_, impl_.clone());
             transform.apply(cloned_item.syntax());
         }
         cloned_item.remove_attrs_and_docs();
@@ -684,31 +684,31 @@ enum ReferenceConversionType {
 }
 
 impl ReferenceConversion {
-    pub(crate) fn convert_type(&self, db: &dyn HirDatabase) -> ast::Type {
+    pub(crate) fn convert_type(&self, db: &dyn HirDatabase, edition: Edition) -> ast::Type {
         let ty = match self.conversion {
-            ReferenceConversionType::Copy => self.ty.display(db).to_string(),
+            ReferenceConversionType::Copy => self.ty.display(db, edition).to_string(),
             ReferenceConversionType::AsRefStr => "&str".to_owned(),
             ReferenceConversionType::AsRefSlice => {
                 let type_argument_name =
-                    self.ty.type_arguments().next().unwrap().display(db).to_string();
+                    self.ty.type_arguments().next().unwrap().display(db, edition).to_string();
                 format!("&[{type_argument_name}]")
             }
             ReferenceConversionType::Dereferenced => {
                 let type_argument_name =
-                    self.ty.type_arguments().next().unwrap().display(db).to_string();
+                    self.ty.type_arguments().next().unwrap().display(db, edition).to_string();
                 format!("&{type_argument_name}")
             }
             ReferenceConversionType::Option => {
                 let type_argument_name =
-                    self.ty.type_arguments().next().unwrap().display(db).to_string();
+                    self.ty.type_arguments().next().unwrap().display(db, edition).to_string();
                 format!("Option<&{type_argument_name}>")
             }
             ReferenceConversionType::Result => {
                 let mut type_arguments = self.ty.type_arguments();
                 let first_type_argument_name =
-                    type_arguments.next().unwrap().display(db).to_string();
+                    type_arguments.next().unwrap().display(db, edition).to_string();
                 let second_type_argument_name =
-                    type_arguments.next().unwrap().display(db).to_string();
+                    type_arguments.next().unwrap().display(db, edition).to_string();
                 format!("Result<&{first_type_argument_name}, &{second_type_argument_name}>")
             }
         };
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs
index fc43d243b36ef..3130ef0695577 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs
@@ -6,7 +6,7 @@ use itertools::Itertools;
 use stdx::to_lower_snake_case;
 use syntax::{
     ast::{self, HasName},
-    match_ast, AstNode, SmolStr,
+    match_ast, AstNode, Edition, SmolStr,
 };
 
 /// Trait names, that will be ignored when in `impl Trait` and `dyn Trait`
@@ -271,24 +271,25 @@ fn var_name_from_pat(pat: &ast::Pat) -> Option<ast::Name> {
 fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> {
     let ty = sema.type_of_expr(expr)?.adjusted();
     let ty = ty.remove_ref().unwrap_or(ty);
+    let edition = sema.scope(expr.syntax())?.krate().edition(sema.db);
 
-    name_of_type(&ty, sema.db)
+    name_of_type(&ty, sema.db, edition)
 }
 
-fn name_of_type(ty: &hir::Type, db: &RootDatabase) -> Option<String> {
+fn name_of_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option<String> {
     let name = if let Some(adt) = ty.as_adt() {
-        let name = adt.name(db).display(db).to_string();
+        let name = adt.name(db).display(db, edition).to_string();
 
         if WRAPPER_TYPES.contains(&name.as_str()) {
             let inner_ty = ty.type_arguments().next()?;
-            return name_of_type(&inner_ty, db);
+            return name_of_type(&inner_ty, db, edition);
         }
 
         name
     } else if let Some(trait_) = ty.as_dyn_trait() {
-        trait_name(&trait_, db)?
+        trait_name(&trait_, db, edition)?
     } else if let Some(traits) = ty.as_impl_traits(db) {
-        let mut iter = traits.filter_map(|t| trait_name(&t, db));
+        let mut iter = traits.filter_map(|t| trait_name(&t, db, edition));
         let name = iter.next()?;
         if iter.next().is_some() {
             return None;
@@ -300,8 +301,8 @@ fn name_of_type(ty: &hir::Type, db: &RootDatabase) -> Option<String> {
     normalize(&name)
 }
 
-fn trait_name(trait_: &hir::Trait, db: &RootDatabase) -> Option<String> {
-    let name = trait_.name(db).display(db).to_string();
+fn trait_name(trait_: &hir::Trait, db: &RootDatabase, edition: Edition) -> Option<String> {
+    let name = trait_.name(db).display(db, edition).to_string();
     if USELESS_TRAITS.contains(&name.as_str()) {
         return None;
     }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml b/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml
index 035b2fc0db0c4..614465b4d06bc 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml
@@ -17,7 +17,6 @@ cov-mark = "2.0.0-pre.1"
 itertools.workspace = true
 tracing.workspace = true
 
-once_cell = "1.17.0"
 smallvec.workspace = true
 
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
index 58e9b724df2a0..b537150608bb0 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
@@ -85,6 +85,7 @@ impl Completions {
             CompletionItemKind::Keyword,
             ctx.source_range(),
             SmolStr::new_static(keyword),
+            ctx.edition,
         );
         item.add_to(self, ctx.db);
     }
@@ -124,7 +125,8 @@ impl Completions {
         kw: &str,
         snippet: &str,
     ) {
-        let mut item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), kw);
+        let mut item =
+            CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), kw, ctx.edition);
 
         match ctx.config.snippet_cap {
             Some(cap) => {
@@ -149,7 +151,8 @@ impl Completions {
         kw: &str,
         snippet: &str,
     ) {
-        let mut item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), kw);
+        let mut item =
+            CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), kw, ctx.edition);
 
         match ctx.config.snippet_cap {
             Some(cap) => item.insert_snippet(cap, snippet),
@@ -544,7 +547,8 @@ impl Completions {
         CompletionItem::new(
             SymbolKind::LifetimeParam,
             ctx.source_range(),
-            name.display_no_db().to_smolstr(),
+            name.display_no_db(ctx.edition).to_smolstr(),
+            ctx.edition,
         )
         .add_to(self, ctx.db)
     }
@@ -553,7 +557,8 @@ impl Completions {
         CompletionItem::new(
             SymbolKind::Label,
             ctx.source_range(),
-            name.display_no_db().to_smolstr(),
+            name.display_no_db(ctx.edition).to_smolstr(),
+            ctx.edition,
         )
         .add_to(self, ctx.db)
     }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs
index a7a6cdebd361e..9821fb4a2fa95 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs
@@ -2,6 +2,8 @@
 //!
 //! This module uses a bit of static metadata to provide completions for builtin-in attributes and lints.
 
+use std::sync::LazyLock;
+
 use ide_db::{
     generated::lints::{
         Lint, CLIPPY_LINTS, CLIPPY_LINT_GROUPS, DEFAULT_LINTS, FEATURES, RUSTDOC_LINTS,
@@ -10,10 +12,9 @@ use ide_db::{
     FxHashMap, SymbolKind,
 };
 use itertools::Itertools;
-use once_cell::sync::Lazy;
 use syntax::{
     ast::{self, AttrKind},
-    AstNode, SyntaxKind, T,
+    AstNode, Edition, SyntaxKind, T,
 };
 
 use crate::{
@@ -48,11 +49,15 @@ pub(crate) fn complete_known_attribute_input(
 
     match path.text().as_str() {
         "repr" => repr::complete_repr(acc, ctx, tt),
-        "feature" => {
-            lint::complete_lint(acc, ctx, colon_prefix, &parse_tt_as_comma_sep_paths(tt)?, FEATURES)
-        }
+        "feature" => lint::complete_lint(
+            acc,
+            ctx,
+            colon_prefix,
+            &parse_tt_as_comma_sep_paths(tt, ctx.edition)?,
+            FEATURES,
+        ),
         "allow" | "warn" | "deny" | "forbid" => {
-            let existing_lints = parse_tt_as_comma_sep_paths(tt)?;
+            let existing_lints = parse_tt_as_comma_sep_paths(tt, ctx.edition)?;
 
             let lints: Vec<Lint> = CLIPPY_LINT_GROUPS
                 .iter()
@@ -66,9 +71,12 @@ pub(crate) fn complete_known_attribute_input(
             lint::complete_lint(acc, ctx, colon_prefix, &existing_lints, &lints);
         }
         "cfg" => cfg::complete_cfg(acc, ctx),
-        "macro_use" => {
-            macro_use::complete_macro_use(acc, ctx, extern_crate, &parse_tt_as_comma_sep_paths(tt)?)
-        }
+        "macro_use" => macro_use::complete_macro_use(
+            acc,
+            ctx,
+            extern_crate,
+            &parse_tt_as_comma_sep_paths(tt, ctx.edition)?,
+        ),
         _ => (),
     }
     Some(())
@@ -130,8 +138,12 @@ pub(crate) fn complete_attribute_path(
     });
 
     let add_completion = |attr_completion: &AttrCompletion| {
-        let mut item =
-            CompletionItem::new(SymbolKind::Attribute, ctx.source_range(), attr_completion.label);
+        let mut item = CompletionItem::new(
+            SymbolKind::Attribute,
+            ctx.source_range(),
+            attr_completion.label,
+            ctx.edition,
+        );
 
         if let Some(lookup) = attr_completion.lookup {
             item.lookup_by(lookup);
@@ -215,7 +227,7 @@ macro_rules! attrs {
 }
 
 #[rustfmt::skip]
-static KIND_TO_ATTRIBUTES: Lazy<FxHashMap<SyntaxKind, &[&str]>> = Lazy::new(|| {
+static KIND_TO_ATTRIBUTES: LazyLock<FxHashMap<SyntaxKind, &[&str]>> = LazyLock::new(|| {
     use SyntaxKind::*;
     [
         (
@@ -361,7 +373,9 @@ fn parse_comma_sep_expr(input: ast::TokenTree) -> Option<Vec<ast::Expr>> {
         input_expressions
             .into_iter()
             .filter_map(|(is_sep, group)| (!is_sep).then_some(group))
-            .filter_map(|mut tokens| syntax::hacks::parse_expr_from_str(&tokens.join("")))
+            .filter_map(|mut tokens| {
+                syntax::hacks::parse_expr_from_str(&tokens.join(""), Edition::CURRENT)
+            })
             .collect::<Vec<ast::Expr>>(),
     )
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs
index 6e7d50ede066e..cda0da13b26eb 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs
@@ -8,7 +8,8 @@ use crate::{completions::Completions, context::CompletionContext, CompletionItem
 
 pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) {
     let add_completion = |item: &str| {
-        let mut completion = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), item);
+        let mut completion =
+            CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), item, ctx.edition);
         completion.insert_text(format!(r#""{item}""#));
         acc.add(completion.build(ctx.db));
     };
@@ -41,7 +42,12 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) {
             name => ctx.krate.potential_cfg(ctx.db).get_cfg_values(name).cloned().for_each(|s| {
                 let s = s.as_str();
                 let insert_text = format!(r#""{s}""#);
-                let mut item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s);
+                let mut item = CompletionItem::new(
+                    SymbolKind::BuiltinAttr,
+                    ctx.source_range(),
+                    s,
+                    ctx.edition,
+                );
                 item.insert_text(insert_text);
 
                 acc.add(item.build(ctx.db));
@@ -49,7 +55,8 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) {
         },
         None => ctx.krate.potential_cfg(ctx.db).get_cfg_keys().cloned().unique().for_each(|s| {
             let s = s.as_str();
-            let item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s);
+            let item =
+                CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s, ctx.edition);
             acc.add(item.build(ctx.db));
         }),
     }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs
index 0127a42824882..1f8927401b2f8 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs
@@ -62,7 +62,7 @@ pub(crate) fn complete_derive_path(
                     _ => return acc.add_macro(ctx, path_ctx, mac, name),
                 };
 
-                let name_ = name.display_no_db().to_smolstr();
+                let name_ = name.display_no_db(ctx.edition).to_smolstr();
                 let find = DEFAULT_DERIVE_DEPENDENCIES
                     .iter()
                     .find(|derive_completion| derive_completion.label == name_);
@@ -72,10 +72,9 @@ pub(crate) fn complete_derive_path(
                         let mut components = vec![derive_completion.label];
                         components.extend(derive_completion.dependencies.iter().filter(
                             |&&dependency| {
-                                !existing_derives
-                                    .iter()
-                                    .map(|it| it.name(ctx.db))
-                                    .any(|it| it.display_no_db().to_smolstr() == dependency)
+                                !existing_derives.iter().map(|it| it.name(ctx.db)).any(|it| {
+                                    it.display_no_db(ctx.edition).to_smolstr() == dependency
+                                })
                             },
                         ));
                         let lookup = components.join(", ");
@@ -85,6 +84,7 @@ pub(crate) fn complete_derive_path(
                             SymbolKind::Derive,
                             ctx.source_range(),
                             SmolStr::from_iter(label),
+                            ctx.edition,
                         );
                         if let Some(docs) = mac.docs(ctx.db) {
                             item.documentation(docs);
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs
index f9dec53806466..d5f9cd5fc76cd 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs
@@ -54,7 +54,8 @@ pub(super) fn complete_lint(
             Some(qual) if !is_qualified => format!("{qual}::{name}"),
             _ => name.to_owned(),
         };
-        let mut item = CompletionItem::new(SymbolKind::Attribute, ctx.source_range(), label);
+        let mut item =
+            CompletionItem::new(SymbolKind::Attribute, ctx.source_range(), label, ctx.edition);
         item.documentation(Documentation::new(description.to_owned()));
         item.add_to(acc, ctx.db)
     }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/macro_use.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/macro_use.rs
index 7e3a62405a78c..deb12282c025b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/macro_use.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/macro_use.rs
@@ -28,7 +28,8 @@ pub(super) fn complete_macro_use(
                 continue;
             }
 
-            let item = CompletionItem::new(SymbolKind::Macro, ctx.source_range(), mac_name);
+            let item =
+                CompletionItem::new(SymbolKind::Macro, ctx.source_range(), mac_name, ctx.edition);
             item.add_to(acc, ctx.db);
         }
     }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs
index 14f464b7753d7..12652b448925b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs
@@ -30,7 +30,12 @@ pub(super) fn complete_repr(
                 continue;
             }
 
-            let mut item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), label);
+            let mut item = CompletionItem::new(
+                SymbolKind::BuiltinAttr,
+                ctx.source_range(),
+                label,
+                ctx.edition,
+            );
             if let Some(lookup) = lookup {
                 item.lookup_by(lookup);
             }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
index a07daf4c4e49c..d55bc3ea5d03a 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
@@ -29,6 +29,7 @@ pub(crate) fn complete_dot(
             CompletionItemKind::Keyword,
             ctx.source_range(),
             SmolStr::new_static("await"),
+            ctx.edition,
         );
         item.detail("expr.await");
         item.add_to(acc, ctx.db);
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs
index 23d93d3b7467e..c9013d1d17d43 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs
@@ -56,7 +56,7 @@ pub(crate) fn complete_cargo_env_vars(
     let range = original.text_range_between_quotes()?;
 
     CARGO_DEFINED_VARS.iter().for_each(|&(var, detail)| {
-        let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, var);
+        let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, var, ctx.edition);
         item.detail(detail);
         item.add_to(acc, ctx.db);
     });
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs
index b5d5604c7519f..1a06e0a3a0e73 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs
@@ -52,8 +52,13 @@ pub(crate) fn complete_extern_abi(
     let abi_str = expanded;
     let source_range = abi_str.text_range_between_quotes()?;
     for &abi in SUPPORTED_CALLING_CONVENTIONS {
-        CompletionItem::new(CompletionItemKind::Keyword, source_range, SmolStr::new_static(abi))
-            .add_to(acc, ctx.db);
+        CompletionItem::new(
+            CompletionItemKind::Keyword,
+            source_range,
+            SmolStr::new_static(abi),
+            ctx.edition,
+        )
+        .add_to(acc, ctx.db);
     }
     Some(())
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs
index 2427f4e49f292..7cb710c2d963c 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs
@@ -19,7 +19,8 @@ pub(crate) fn complete_extern_crate(acc: &mut Completions, ctx: &CompletionConte
         let mut item = CompletionItem::new(
             CompletionItemKind::SymbolKind(SymbolKind::Module),
             ctx.source_range(),
-            name.display_no_db().to_smolstr(),
+            name.display_no_db(ctx.edition).to_smolstr(),
+            ctx.edition,
         );
         item.set_documentation(module.docs(ctx.db));
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
index fdce7c547a490..2a6b310d3a217 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
@@ -411,7 +411,7 @@ fn compute_fuzzy_completion_order_key(
     cov_mark::hit!(certain_fuzzy_order_test);
     let import_name = match proposed_mod_path.segments().last() {
         // FIXME: nasty alloc, this is a hot path!
-        Some(name) => name.display_no_db().to_smolstr().to_ascii_lowercase(),
+        Some(name) => name.unescaped().display_no_db().to_smolstr().to_ascii_lowercase(),
         None => return usize::MAX,
     };
     match import_name.match_indices(user_input_lowercased).next() {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs
index a59246229bb2d..ee3b817ee8cfb 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs
@@ -32,7 +32,7 @@ pub(crate) fn complete_fn_param(
     let comma_wrapper = comma_wrapper(ctx);
     let mut add_new_item_to_acc = |label: &str| {
         let mk_item = |label: &str, range: TextRange| {
-            CompletionItem::new(CompletionItemKind::Binding, range, label)
+            CompletionItem::new(CompletionItemKind::Binding, range, label, ctx.edition)
         };
         let item = match &comma_wrapper {
             Some((fmt, range)) => mk_item(&fmt(label), *range),
@@ -50,7 +50,7 @@ pub(crate) fn complete_fn_param(
         ParamKind::Closure(closure) => {
             let stmt_list = closure.syntax().ancestors().find_map(ast::StmtList::cast)?;
             params_from_stmt_list_scope(ctx, stmt_list, |name, ty| {
-                add_new_item_to_acc(&format!("{}: {ty}", name.display(ctx.db)));
+                add_new_item_to_acc(&format!("{}: {ty}", name.display(ctx.db, ctx.edition)));
             });
         }
     }
@@ -101,8 +101,8 @@ fn fill_fn_params(
     if let Some(stmt_list) = function.syntax().parent().and_then(ast::StmtList::cast) {
         params_from_stmt_list_scope(ctx, stmt_list, |name, ty| {
             file_params
-                .entry(format!("{}: {ty}", name.display(ctx.db)))
-                .or_insert(name.display(ctx.db).to_string());
+                .entry(format!("{}: {ty}", name.display(ctx.db, ctx.edition)))
+                .or_insert(name.display(ctx.db, ctx.edition).to_string());
         });
     }
     remove_duplicated(&mut file_params, param_list.params());
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs
index 23affc3659208..a87c60c694ac7 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs
@@ -35,7 +35,8 @@ pub(crate) fn format_string(
         CompletionItem::new(
             CompletionItemKind::Binding,
             source_range,
-            name.display_no_db().to_smolstr(),
+            name.display_no_db(ctx.edition).to_smolstr(),
+            ctx.edition,
         )
         .add_to(acc, ctx.db);
     });
@@ -50,7 +51,8 @@ pub(crate) fn format_string(
             CompletionItem::new(
                 CompletionItemKind::SymbolKind(symbol_kind),
                 source_range,
-                name.display_no_db().to_smolstr(),
+                name.display_no_db(ctx.edition).to_smolstr(),
+                ctx.edition,
             )
             .add_to(acc, ctx.db);
         }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
index 2fd7805e60d5e..fc6e1ebf05fc6 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -184,7 +184,7 @@ fn add_function_impl(
     let label = format_smolstr!(
         "{}fn {}({})",
         if is_async { "async " } else { "" },
-        fn_name.display(ctx.db),
+        fn_name.display(ctx.db, ctx.edition),
         if func.assoc_fn_params(ctx.db).is_empty() { "" } else { ".." }
     );
 
@@ -194,11 +194,11 @@ fn add_function_impl(
         SymbolKind::Function
     });
 
-    let mut item = CompletionItem::new(completion_kind, replacement_range, label);
+    let mut item = CompletionItem::new(completion_kind, replacement_range, label, ctx.edition);
     item.lookup_by(format!(
         "{}fn {}",
         if is_async { "async " } else { "" },
-        fn_name.display(ctx.db)
+        fn_name.display(ctx.db, ctx.edition)
     ))
     .set_documentation(func.docs(ctx.db))
     .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() });
@@ -262,7 +262,8 @@ fn add_type_alias_impl(
 
     let label = format_smolstr!("type {alias_name} =");
 
-    let mut item = CompletionItem::new(SymbolKind::TypeAlias, replacement_range, label);
+    let mut item =
+        CompletionItem::new(SymbolKind::TypeAlias, replacement_range, label, ctx.edition);
     item.lookup_by(format!("type {alias_name}"))
         .set_documentation(type_alias.docs(ctx.db))
         .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() });
@@ -320,7 +321,7 @@ fn add_const_impl(
     const_: hir::Const,
     impl_def: hir::Impl,
 ) {
-    let const_name = const_.name(ctx.db).map(|n| n.display_no_db().to_smolstr());
+    let const_name = const_.name(ctx.db).map(|n| n.display_no_db(ctx.edition).to_smolstr());
 
     if let Some(const_name) = const_name {
         if let Some(source) = ctx.sema.source(const_) {
@@ -334,7 +335,8 @@ fn add_const_impl(
                 let label = make_const_compl_syntax(&transformed_const, source.file_id.is_macro());
                 let replacement = format!("{label} ");
 
-                let mut item = CompletionItem::new(SymbolKind::Const, replacement_range, label);
+                let mut item =
+                    CompletionItem::new(SymbolKind::Const, replacement_range, label, ctx.edition);
                 item.lookup_by(format_smolstr!("const {const_name}"))
                     .set_documentation(const_.docs(ctx.db))
                     .set_relevance(CompletionRelevance {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs
index 03fe93c563ff8..9efc52428eff7 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs
@@ -41,7 +41,7 @@ pub(crate) fn complete_lifetime(
         if matches!(
             res,
             ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_))
-                 if param_lifetime != Some(&*name.display_no_db().to_smolstr())
+                 if param_lifetime != Some(&*name.display_no_db(ctx.edition).to_smolstr())
         ) {
             acc.add_lifetime(ctx, name);
         }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs
index d9a10893bf52b..05e2892fdc85c 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs
@@ -53,7 +53,7 @@ pub(crate) fn complete_mod(
 
     let existing_mod_declarations = current_module
         .children(ctx.db)
-        .filter_map(|module| Some(module.name(ctx.db)?.display(ctx.db).to_string()))
+        .filter_map(|module| Some(module.name(ctx.db)?.display(ctx.db, ctx.edition).to_string()))
         .filter(|module| module != ctx.original_token.text())
         .collect::<FxHashSet<_>>();
 
@@ -99,7 +99,8 @@ pub(crate) fn complete_mod(
             if mod_under_caret.semicolon_token().is_none() {
                 label.push(';');
             }
-            let item = CompletionItem::new(SymbolKind::Module, ctx.source_range(), &label);
+            let item =
+                CompletionItem::new(SymbolKind::Module, ctx.source_range(), &label, ctx.edition);
             item.add_to(acc, ctx.db)
         });
 
@@ -140,7 +141,9 @@ fn directory_to_look_for_submodules(
     module_chain_to_containing_module_file(module, db)
         .into_iter()
         .filter_map(|module| module.name(db))
-        .try_fold(base_directory, |path, name| path.join(&name.display_no_db().to_smolstr()))
+        .try_fold(base_directory, |path, name| {
+            path.join(&name.unescaped().display_no_db().to_smolstr())
+        })
 }
 
 fn module_chain_to_containing_module_file(
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
index 977e0d80a4d46..a632f148936dc 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
@@ -72,7 +72,10 @@ pub(crate) fn complete_postfix(
                     let mut item = postfix_snippet(
                         "drop",
                         "fn drop(&mut self)",
-                        &format!("{path}($0{receiver_text})", path = path.display(ctx.db)),
+                        &format!(
+                            "{path}($0{receiver_text})",
+                            path = path.display(ctx.db, ctx.edition)
+                        ),
                     );
                     item.set_documentation(drop_fn.docs(ctx.db));
                     item.add_to(acc, ctx.db);
@@ -335,8 +338,12 @@ fn build_postfix_snippet_builder<'ctx>(
     ) -> impl Fn(&str, &str, &str) -> Builder + 'ctx {
         move |label, detail, snippet| {
             let edit = TextEdit::replace(delete_range, snippet.to_owned());
-            let mut item =
-                CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), label);
+            let mut item = CompletionItem::new(
+                CompletionItemKind::Snippet,
+                ctx.source_range(),
+                label,
+                ctx.edition,
+            );
             item.detail(detail).snippet_edit(cap, edit);
             let postfix_match = if ctx.original_token.text() == label {
                 cov_mark::hit!(postfix_exact_match_is_high_priority);
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
index 1dcf41f8dd235..117a5e3d9359b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
@@ -73,6 +73,7 @@ pub(crate) fn complete_record_expr_fields(
                     CompletionItemKind::Snippet,
                     ctx.source_range(),
                     SmolStr::new_static(".."),
+                    ctx.edition,
                 );
                 item.insert_text(".");
                 item.add_to(acc, ctx.db);
@@ -101,6 +102,7 @@ pub(crate) fn add_default_update(
             SymbolKind::Field,
             ctx.source_range(),
             SmolStr::new_static(completion_text),
+            ctx.edition,
         );
         let completion_text =
             completion_text.strip_prefix(ctx.token.text()).unwrap_or(completion_text);
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs
index e831113350ac5..357709e0c1fde 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs
@@ -118,7 +118,8 @@ macro_rules! $1 {
 }
 
 fn snippet(ctx: &CompletionContext<'_>, cap: SnippetCap, label: &str, snippet: &str) -> Builder {
-    let mut item = CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), label);
+    let mut item =
+        CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), label, ctx.edition);
     item.insert_snippet(cap, snippet);
     item
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs
index 8e5b55360dc11..45704549e6090 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs
@@ -107,7 +107,11 @@ pub(crate) fn complete_use_path(
                             let item = CompletionItem::new(
                                 CompletionItemKind::SymbolKind(SymbolKind::Enum),
                                 ctx.source_range(),
-                                format_smolstr!("{}::", e.name(ctx.db).display(ctx.db)),
+                                format_smolstr!(
+                                    "{}::",
+                                    e.name(ctx.db).display(ctx.db, ctx.edition)
+                                ),
+                                ctx.edition,
                             );
                             acc.add(item.build(ctx.db));
                         }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
index 952d9217c71d4..bcd9df941947f 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
@@ -15,7 +15,7 @@ use ide_db::{
 };
 use syntax::{
     ast::{self, AttrKind, NameOrNameRef},
-    AstNode, SmolStr,
+    AstNode, Edition, SmolStr,
     SyntaxKind::{self, *},
     SyntaxToken, TextRange, TextSize, T,
 };
@@ -437,6 +437,7 @@ pub(crate) struct CompletionContext<'a> {
     pub(crate) module: hir::Module,
     /// Whether nightly toolchain is used. Cached since this is looked up a lot.
     is_nightly: bool,
+    pub(crate) edition: Edition,
 
     /// The expected name of what we are completing.
     /// This is usually the parameter name of the function argument we are completing.
@@ -467,8 +468,9 @@ impl CompletionContext<'_> {
                 cov_mark::hit!(completes_if_lifetime_without_idents);
                 TextRange::at(self.original_token.text_range().start(), TextSize::from(1))
             }
-            IDENT | LIFETIME_IDENT | UNDERSCORE | INT_NUMBER => self.original_token.text_range(),
-            _ if kind.is_keyword() => self.original_token.text_range(),
+            LIFETIME_IDENT | UNDERSCORE | INT_NUMBER => self.original_token.text_range(),
+            // We want to consider all keywords in all editions.
+            _ if kind.is_any_identifier() => self.original_token.text_range(),
             _ => TextRange::empty(self.position.offset),
         }
     }
@@ -716,6 +718,7 @@ impl<'a> CompletionContext<'a> {
 
         let krate = scope.krate();
         let module = scope.module();
+        let edition = krate.edition(db);
 
         let toolchain = db.toolchain_channel(krate.into());
         // `toolchain == None` means we're in some detached files. Since we have no information on
@@ -742,6 +745,7 @@ impl<'a> CompletionContext<'a> {
             krate,
             module,
             is_nightly,
+            edition,
             expected_name,
             expected_type,
             qualifier_ctx,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
index 1e972b9b4ce06..ed359394f1c49 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
@@ -1385,7 +1385,7 @@ fn pattern_context_for(
                                         }).map(|enum_| enum_.variants(sema.db))
                                     })
                                 }).map(|variants| variants.iter().filter_map(|variant| {
-                                        let variant_name = variant.name(sema.db).display(sema.db).to_string();
+                                        let variant_name = variant.name(sema.db).unescaped().display(sema.db).to_string();
 
                                         let variant_already_present = match_arm_list.arms().any(|arm| {
                                             arm.pat().and_then(|pat| {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
index 3657a7d969b99..a30a115da1f1b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
@@ -10,7 +10,7 @@ use ide_db::{
 use itertools::Itertools;
 use smallvec::SmallVec;
 use stdx::{impl_from, never};
-use syntax::{format_smolstr, SmolStr, TextRange, TextSize};
+use syntax::{format_smolstr, Edition, SmolStr, TextRange, TextSize};
 use text_edit::TextEdit;
 
 use crate::{
@@ -400,6 +400,7 @@ impl CompletionItem {
         kind: impl Into<CompletionItemKind>,
         source_range: TextRange,
         label: impl Into<SmolStr>,
+        edition: Edition,
     ) -> Builder {
         let label = label.into();
         Builder {
@@ -419,6 +420,7 @@ impl CompletionItem {
             ref_match: None,
             imports_to_add: Default::default(),
             doc_aliases: vec![],
+            edition,
         }
     }
 
@@ -464,6 +466,7 @@ pub(crate) struct Builder {
     trigger_call_info: bool,
     relevance: CompletionRelevance,
     ref_match: Option<(Mutability, TextSize)>,
+    edition: Edition,
 }
 
 impl Builder {
@@ -517,7 +520,7 @@ impl Builder {
             label_detail.replace(format_smolstr!(
                 "{} (use {})",
                 label_detail.as_deref().unwrap_or_default(),
-                import_edit.import_path.display(db)
+                import_edit.import_path.display(db, self.edition)
             ));
         } else if let Some(trait_name) = self.trait_name {
             label_detail.replace(format_smolstr!(
@@ -536,8 +539,8 @@ impl Builder {
             .into_iter()
             .filter_map(|import| {
                 Some((
-                    import.import_path.display(db).to_string(),
-                    import.import_path.segments().last()?.display(db).to_string(),
+                    import.import_path.display(db, self.edition).to_string(),
+                    import.import_path.segments().last()?.display(db, self.edition).to_string(),
                 ))
             })
             .collect();
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
index 90c1728074d4d..58d1fad09504f 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
@@ -245,6 +245,7 @@ pub fn resolve_completion_edits(
 
     let current_module = sema.scope(position_for_import)?.module();
     let current_crate = current_module.krate();
+    let current_edition = current_crate.edition(db);
     let new_ast = scope.clone_for_update();
     let mut import_insert = TextEdit::builder();
 
@@ -261,9 +262,13 @@ pub fn resolve_completion_edits(
             .filter_map(|candidate| {
                 current_module.find_use_path(db, candidate, config.insert_use.prefix_kind, cfg)
             })
-            .find(|mod_path| mod_path.display(db).to_string() == full_import_path);
+            .find(|mod_path| mod_path.display(db, current_edition).to_string() == full_import_path);
         if let Some(import_path) = import {
-            insert_use::insert_use(&new_ast, mod_path_to_ast(&import_path), &config.insert_use);
+            insert_use::insert_use(
+                &new_ast,
+                mod_path_to_ast(&import_path, current_edition),
+                &config.insert_use,
+            );
         }
     });
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
index 02d667c52056a..ff5ec3a29f3e1 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
@@ -17,7 +17,7 @@ use ide_db::{
     imports::import_assets::LocatedImport,
     RootDatabase, SnippetCap, SymbolKind,
 };
-use syntax::{ast, format_smolstr, AstNode, SmolStr, SyntaxKind, TextRange, ToSmolStr};
+use syntax::{ast, format_smolstr, AstNode, Edition, SmolStr, SyntaxKind, TextRange, ToSmolStr};
 use text_edit::TextEdit;
 
 use crate::{
@@ -133,19 +133,22 @@ pub(crate) fn render_field(
     let db = ctx.db();
     let is_deprecated = ctx.is_deprecated(field);
     let name = field.name(db);
-    let (name, escaped_name) =
-        (name.unescaped().display(db).to_smolstr(), name.display_no_db().to_smolstr());
+    let (name, escaped_name) = (
+        name.unescaped().display(db).to_smolstr(),
+        name.display_no_db(ctx.completion.edition).to_smolstr(),
+    );
     let mut item = CompletionItem::new(
         SymbolKind::Field,
         ctx.source_range(),
-        field_with_receiver(db, receiver.as_ref(), &name),
+        field_with_receiver(db, receiver.as_ref(), &name, ctx.completion.edition),
+        ctx.completion.edition,
     );
     item.set_relevance(CompletionRelevance {
         type_match: compute_type_match(ctx.completion, ty),
         exact_name_match: compute_exact_name_match(ctx.completion, name.as_str()),
         ..CompletionRelevance::default()
     });
-    item.detail(ty.display(db).to_string())
+    item.detail(ty.display(db, ctx.completion.edition).to_string())
         .set_documentation(field.docs(db))
         .set_deprecated(is_deprecated)
         .lookup_by(name);
@@ -159,7 +162,8 @@ pub(crate) fn render_field(
 
         builder.replace(
             ctx.source_range(),
-            field_with_receiver(db, receiver.as_ref(), &escaped_name).into(),
+            field_with_receiver(db, receiver.as_ref(), &escaped_name, ctx.completion.edition)
+                .into(),
         );
 
         let expected_fn_type =
@@ -183,7 +187,12 @@ pub(crate) fn render_field(
 
         item.text_edit(builder.finish());
     } else {
-        item.insert_text(field_with_receiver(db, receiver.as_ref(), &escaped_name));
+        item.insert_text(field_with_receiver(
+            db,
+            receiver.as_ref(),
+            &escaped_name,
+            ctx.completion.edition,
+        ));
     }
     if let Some(receiver) = &dot_access.receiver {
         if let Some(original) = ctx.completion.sema.original_ast_node(receiver.clone()) {
@@ -200,10 +209,11 @@ fn field_with_receiver(
     db: &RootDatabase,
     receiver: Option<&hir::Name>,
     field_name: &str,
+    edition: Edition,
 ) -> SmolStr {
     receiver.map_or_else(
         || field_name.into(),
-        |receiver| format_smolstr!("{}.{field_name}", receiver.display(db)),
+        |receiver| format_smolstr!("{}.{field_name}", receiver.display(db, edition)),
     )
 }
 
@@ -216,9 +226,16 @@ pub(crate) fn render_tuple_field(
     let mut item = CompletionItem::new(
         SymbolKind::Field,
         ctx.source_range(),
-        field_with_receiver(ctx.db(), receiver.as_ref(), &field.to_string()),
+        field_with_receiver(
+            ctx.db(),
+            receiver.as_ref(),
+            &field.to_string(),
+            ctx.completion.edition,
+        ),
+        ctx.completion.edition,
     );
-    item.detail(ty.display(ctx.db()).to_string()).lookup_by(field.to_string());
+    item.detail(ty.display(ctx.db(), ctx.completion.edition).to_string())
+        .lookup_by(field.to_string());
     item.build(ctx.db())
 }
 
@@ -226,8 +243,12 @@ pub(crate) fn render_type_inference(
     ty_string: String,
     ctx: &CompletionContext<'_>,
 ) -> CompletionItem {
-    let mut builder =
-        CompletionItem::new(CompletionItemKind::InferredType, ctx.source_range(), ty_string);
+    let mut builder = CompletionItem::new(
+        CompletionItemKind::InferredType,
+        ctx.source_range(),
+        ty_string,
+        ctx.edition,
+    );
     builder.set_relevance(CompletionRelevance { is_definite: true, ..Default::default() });
     builder.build(ctx.db)
 }
@@ -296,7 +317,7 @@ pub(crate) fn render_expr(
 
     let cfg = ctx.config.import_path_config();
 
-    let label = expr.gen_source_code(&ctx.scope, &mut label_formatter, cfg).ok()?;
+    let label = expr.gen_source_code(&ctx.scope, &mut label_formatter, cfg, ctx.edition).ok()?;
 
     let source_range = match ctx.original_token.parent() {
         Some(node) => match node.ancestors().find_map(ast::Path::cast) {
@@ -306,10 +327,13 @@ pub(crate) fn render_expr(
         None => ctx.source_range(),
     };
 
-    let mut item = CompletionItem::new(CompletionItemKind::Expression, source_range, label);
+    let mut item =
+        CompletionItem::new(CompletionItemKind::Expression, source_range, label, ctx.edition);
 
-    let snippet =
-        format!("{}$0", expr.gen_source_code(&ctx.scope, &mut snippet_formatter, cfg).ok()?);
+    let snippet = format!(
+        "{}$0",
+        expr.gen_source_code(&ctx.scope, &mut snippet_formatter, cfg, ctx.edition).ok()?
+    );
     let edit = TextEdit::replace(source_range, snippet);
     item.snippet_edit(ctx.config.snippet_cap?, edit);
     item.documentation(Documentation::new(String::from("Autogenerated expression by term search")));
@@ -396,10 +420,10 @@ fn render_resolution_path(
     let config = completion.config;
     let requires_import = import_to_add.is_some();
 
-    let name = local_name.display_no_db().to_smolstr();
+    let name = local_name.display_no_db(ctx.completion.edition).to_smolstr();
     let mut item = render_resolution_simple_(ctx, &local_name, import_to_add, resolution);
-    if local_name.is_escaped() {
-        item.insert_text(local_name.display_no_db().to_smolstr());
+    if local_name.is_escaped(completion.edition) {
+        item.insert_text(local_name.display_no_db(completion.edition).to_smolstr());
     }
     // Add `<>` for generic types
     let type_path_no_ty_args = matches!(
@@ -421,14 +445,17 @@ fn render_resolution_path(
                 item.lookup_by(name.clone())
                     .label(SmolStr::from_iter([&name, "<…>"]))
                     .trigger_call_info()
-                    .insert_snippet(cap, format!("{}<$0>", local_name.display(db)));
+                    .insert_snippet(
+                        cap,
+                        format!("{}<$0>", local_name.display(db, completion.edition)),
+                    );
             }
         }
     }
 
     let mut set_item_relevance = |ty: Type| {
         if !ty.is_unknown() {
-            item.detail(ty.display(db).to_string());
+            item.detail(ty.display(db, completion.edition).to_string());
         }
 
         item.set_relevance(CompletionRelevance {
@@ -485,6 +512,7 @@ fn render_resolution_simple_(
         kind,
         ctx.source_range(),
         local_name.unescaped().display(db).to_smolstr(),
+        ctx.completion.edition,
     );
     item.set_relevance(ctx.completion_relevance())
         .set_documentation(scope_def_docs(db, resolution))
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs
index 3bfec0de6bca4..415d87c6239b4 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs
@@ -14,11 +14,14 @@ pub(crate) fn render_const(ctx: RenderContext<'_>, const_: hir::Const) -> Option
 fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option<CompletionItem> {
     let db = ctx.db();
     let name = const_.name(db)?;
-    let (name, escaped_name) =
-        (name.unescaped().display(db).to_smolstr(), name.display(db).to_smolstr());
-    let detail = const_.display(db).to_string();
+    let (name, escaped_name) = (
+        name.unescaped().display(db).to_smolstr(),
+        name.display(db, ctx.completion.edition).to_smolstr(),
+    );
+    let detail = const_.display(db, ctx.completion.edition).to_string();
 
-    let mut item = CompletionItem::new(SymbolKind::Const, ctx.source_range(), name);
+    let mut item =
+        CompletionItem::new(SymbolKind::Const, ctx.source_range(), name, ctx.completion.edition);
     item.set_documentation(ctx.docs(const_))
         .set_deprecated(ctx.is_deprecated(const_) || ctx.is_deprecated_assoc_item(const_))
         .detail(detail)
@@ -26,7 +29,7 @@ fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option<CompletionItem>
 
     if let Some(actm) = const_.as_assoc_item(db) {
         if let Some(trt) = actm.container_or_implemented_trait(db) {
-            item.trait_name(trt.name(db).display_no_db().to_smolstr());
+            item.trait_name(trt.name(db).display_no_db(ctx.completion.edition).to_smolstr());
         }
     }
     item.insert_text(escaped_name);
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs
index 05b2d0ae386b4..74092b53f523f 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs
@@ -4,7 +4,7 @@ use hir::{db::HirDatabase, AsAssocItem, HirDisplay};
 use ide_db::{SnippetCap, SymbolKind};
 use itertools::Itertools;
 use stdx::{format_to, to_lower_snake_case};
-use syntax::{format_smolstr, AstNode, SmolStr, ToSmolStr};
+use syntax::{format_smolstr, AstNode, Edition, SmolStr, ToSmolStr};
 
 use crate::{
     context::{CompletionContext, DotAccess, DotAccessKind, PathCompletionCtx, PathKind},
@@ -62,9 +62,16 @@ fn render(
                 receiver.unescaped().display(ctx.db()),
                 name.unescaped().display(ctx.db())
             ),
-            format_smolstr!("{}.{}", receiver.display(ctx.db()), name.display(ctx.db())),
+            format_smolstr!(
+                "{}.{}",
+                receiver.display(ctx.db(), completion.edition),
+                name.display(ctx.db(), completion.edition)
+            ),
+        ),
+        _ => (
+            name.unescaped().display(db).to_smolstr(),
+            name.display(db, completion.edition).to_smolstr(),
         ),
-        _ => (name.unescaped().display(db).to_smolstr(), name.display(db).to_smolstr()),
     };
     let has_self_param = func.self_param(db).is_some();
     let mut item = CompletionItem::new(
@@ -75,6 +82,7 @@ fn render(
         }),
         ctx.source_range(),
         call.clone(),
+        completion.edition,
     );
 
     let ret_type = func.ret_type(db);
@@ -141,9 +149,9 @@ fn render(
     }
 
     let detail = if ctx.completion.config.full_function_signatures {
-        detail_full(db, func)
+        detail_full(db, func, ctx.completion.edition)
     } else {
-        detail(db, func)
+        detail(db, func, ctx.completion.edition)
     };
     item.set_documentation(ctx.docs(func))
         .set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func))
@@ -161,7 +169,9 @@ fn render(
         None => {
             if let Some(actm) = assoc_item {
                 if let Some(trt) = actm.container_or_implemented_trait(db) {
-                    item.trait_name(trt.name(db).display_no_db().to_smolstr());
+                    item.trait_name(
+                        trt.name(db).display_no_db(ctx.completion.edition).to_smolstr(),
+                    );
                 }
             }
         }
@@ -219,7 +229,7 @@ pub(super) fn add_call_parens<'b>(
                 params.iter().enumerate().format_with(", ", |(index, param), f| {
                     match param.name(ctx.db) {
                         Some(n) => {
-                            let smol_str = n.display_no_db().to_smolstr();
+                            let smol_str = n.display_no_db(ctx.edition).to_smolstr();
                             let text = smol_str.as_str().trim_start_matches('_');
                             let ref_ = ref_of_param(ctx, text, param.ty());
                             f(&format_args!("${{{}:{ref_}{text}}}", index + offset))
@@ -238,7 +248,7 @@ pub(super) fn add_call_parens<'b>(
                     format!(
                         "{}(${{1:{}}}{}{})$0",
                         escaped_name,
-                        self_param.display(ctx.db),
+                        self_param.display(ctx.db, ctx.edition),
                         if params.is_empty() { "" } else { ", " },
                         function_params_snippet
                     )
@@ -276,7 +286,7 @@ fn ref_of_param(ctx: &CompletionContext<'_>, arg: &str, ty: &hir::Type) -> &'sta
     ""
 }
 
-fn detail(db: &dyn HirDatabase, func: hir::Function) -> String {
+fn detail(db: &dyn HirDatabase, func: hir::Function, edition: Edition) -> String {
     let mut ret_ty = func.ret_type(db);
     let mut detail = String::new();
 
@@ -293,15 +303,15 @@ fn detail(db: &dyn HirDatabase, func: hir::Function) -> String {
         format_to!(detail, "unsafe ");
     }
 
-    format_to!(detail, "fn({})", params_display(db, func));
+    format_to!(detail, "fn({})", params_display(db, func, edition));
     if !ret_ty.is_unit() {
-        format_to!(detail, " -> {}", ret_ty.display(db));
+        format_to!(detail, " -> {}", ret_ty.display(db, edition));
     }
     detail
 }
 
-fn detail_full(db: &dyn HirDatabase, func: hir::Function) -> String {
-    let signature = format!("{}", func.display(db));
+fn detail_full(db: &dyn HirDatabase, func: hir::Function, edition: Edition) -> String {
+    let signature = format!("{}", func.display(db, edition));
     let mut detail = String::with_capacity(signature.len());
 
     for segment in signature.split_whitespace() {
@@ -315,16 +325,16 @@ fn detail_full(db: &dyn HirDatabase, func: hir::Function) -> String {
     detail
 }
 
-fn params_display(db: &dyn HirDatabase, func: hir::Function) -> String {
+fn params_display(db: &dyn HirDatabase, func: hir::Function, edition: Edition) -> String {
     if let Some(self_param) = func.self_param(db) {
         let assoc_fn_params = func.assoc_fn_params(db);
         let params = assoc_fn_params
             .iter()
             .skip(1) // skip the self param because we are manually handling that
-            .map(|p| p.ty().display(db));
+            .map(|p| p.ty().display(db, edition));
         format!(
             "{}{}",
-            self_param.display(db),
+            self_param.display(db, edition),
             params.format_with("", |display, f| {
                 f(&", ")?;
                 f(&display)
@@ -332,7 +342,7 @@ fn params_display(db: &dyn HirDatabase, func: hir::Function) -> String {
         )
     } else {
         let assoc_fn_params = func.assoc_fn_params(db);
-        assoc_fn_params.iter().map(|p| p.ty().display(db)).join(", ")
+        assoc_fn_params.iter().map(|p| p.ty().display(db, edition)).join(", ")
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
index 27435307d5086..c71356e5300fc 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
@@ -76,16 +76,16 @@ fn render(
     };
     let (qualified_name, escaped_qualified_name) = (
         qualified_name.unescaped().display(ctx.db()).to_string(),
-        qualified_name.display(ctx.db()).to_string(),
+        qualified_name.display(ctx.db(), completion.edition).to_string(),
     );
     let snippet_cap = ctx.snippet_cap();
 
     let mut rendered = match kind {
         StructKind::Tuple if should_add_parens => {
-            render_tuple_lit(db, snippet_cap, &fields, &escaped_qualified_name)
+            render_tuple_lit(db, snippet_cap, &fields, &escaped_qualified_name, completion.edition)
         }
         StructKind::Record if should_add_parens => {
-            render_record_lit(db, snippet_cap, &fields, &escaped_qualified_name)
+            render_record_lit(db, snippet_cap, &fields, &escaped_qualified_name, completion.edition)
         }
         _ => RenderedLiteral {
             literal: escaped_qualified_name.clone(),
@@ -103,7 +103,10 @@ fn render(
     }
     let label = format_literal_label(&qualified_name, kind, snippet_cap);
     let lookup = if qualified {
-        format_literal_lookup(&short_qualified_name.display(ctx.db()).to_string(), kind)
+        format_literal_lookup(
+            &short_qualified_name.display(ctx.db(), completion.edition).to_string(),
+            kind,
+        )
     } else {
         format_literal_lookup(&qualified_name, kind)
     };
@@ -112,6 +115,7 @@ fn render(
         CompletionItemKind::SymbolKind(thing.symbol_kind()),
         ctx.source_range(),
         label,
+        completion.edition,
     );
 
     item.lookup_by(lookup);
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
index de715bcbfaf9d..6490171fbb48b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
@@ -46,8 +46,10 @@ fn render(
         ctx.source_range()
     };
 
-    let (name, escaped_name) =
-        (name.unescaped().display(ctx.db()).to_smolstr(), name.display(ctx.db()).to_smolstr());
+    let (name, escaped_name) = (
+        name.unescaped().display(ctx.db()).to_smolstr(),
+        name.display(ctx.db(), completion.edition).to_smolstr(),
+    );
     let docs = ctx.docs(macro_);
     let docs_str = docs.as_ref().map(Documentation::as_str).unwrap_or_default();
     let is_fn_like = macro_.is_fn_like(completion.db);
@@ -59,9 +61,10 @@ fn render(
         SymbolKind::from(macro_.kind(completion.db)),
         source_range,
         label(&ctx, needs_bang, bra, ket, &name),
+        completion.edition,
     );
     item.set_deprecated(ctx.is_deprecated(macro_))
-        .detail(macro_.display(completion.db).to_string())
+        .detail(macro_.display(completion.db, completion.edition).to_string())
         .set_documentation(docs)
         .set_relevance(ctx.completion_relevance());
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs
index 598b8762b6885..5675dfb5c6ff2 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs
@@ -3,7 +3,7 @@
 use hir::{db::HirDatabase, Name, StructKind};
 use ide_db::{documentation::HasDocs, SnippetCap};
 use itertools::Itertools;
-use syntax::{SmolStr, ToSmolStr};
+use syntax::{Edition, SmolStr, ToSmolStr};
 
 use crate::{
     context::{ParamContext, ParamKind, PathCompletionCtx, PatternContext},
@@ -31,8 +31,10 @@ pub(crate) fn render_struct_pat(
     }
 
     let name = local_name.unwrap_or_else(|| strukt.name(ctx.db()));
-    let (name, escaped_name) =
-        (name.unescaped().display(ctx.db()).to_smolstr(), name.display(ctx.db()).to_smolstr());
+    let (name, escaped_name) = (
+        name.unescaped().display(ctx.db()).to_smolstr(),
+        name.display(ctx.db(), ctx.completion.edition).to_smolstr(),
+    );
     let kind = strukt.kind(ctx.db());
     let label = format_literal_label(name.as_str(), kind, ctx.snippet_cap());
     let lookup = format_literal_lookup(name.as_str(), kind);
@@ -60,13 +62,13 @@ pub(crate) fn render_variant_pat(
     let (name, escaped_name) = match path {
         Some(path) => (
             path.unescaped().display(ctx.db()).to_string().into(),
-            path.display(ctx.db()).to_string().into(),
+            path.display(ctx.db(), ctx.completion.edition).to_string().into(),
         ),
         None => {
             let name = local_name.unwrap_or_else(|| variant.name(ctx.db()));
             let it = (
                 name.unescaped().display(ctx.db()).to_smolstr(),
-                name.display(ctx.db()).to_smolstr(),
+                name.display(ctx.db(), ctx.completion.edition).to_smolstr(),
             );
             it
         }
@@ -119,7 +121,12 @@ fn build_completion(
         relevance.type_match = super::compute_type_match(ctx.completion, &adt_ty);
     }
 
-    let mut item = CompletionItem::new(CompletionItemKind::Binding, ctx.source_range(), label);
+    let mut item = CompletionItem::new(
+        CompletionItemKind::Binding,
+        ctx.source_range(),
+        label,
+        ctx.completion.edition,
+    );
     item.set_documentation(ctx.docs(def))
         .set_deprecated(ctx.is_deprecated(def))
         .detail(&pat)
@@ -142,9 +149,14 @@ fn render_pat(
 ) -> Option<String> {
     let mut pat = match kind {
         StructKind::Tuple => render_tuple_as_pat(ctx.snippet_cap(), fields, name, fields_omitted),
-        StructKind::Record => {
-            render_record_as_pat(ctx.db(), ctx.snippet_cap(), fields, name, fields_omitted)
-        }
+        StructKind::Record => render_record_as_pat(
+            ctx.db(),
+            ctx.snippet_cap(),
+            fields,
+            name,
+            fields_omitted,
+            ctx.completion.edition,
+        ),
         StructKind::Unit => name.to_owned(),
     };
 
@@ -173,6 +185,7 @@ fn render_record_as_pat(
     fields: &[hir::Field],
     name: &str,
     fields_omitted: bool,
+    edition: Edition,
 ) -> String {
     let fields = fields.iter();
     match snippet_cap {
@@ -180,7 +193,7 @@ fn render_record_as_pat(
             format!(
                 "{name} {{ {}{} }}",
                 fields.enumerate().format_with(", ", |(idx, field), f| {
-                    f(&format_args!("{}${}", field.name(db).display(db.upcast()), idx + 1))
+                    f(&format_args!("{}${}", field.name(db).display(db.upcast(), edition), idx + 1))
                 }),
                 if fields_omitted { ", .." } else { "" },
                 name = name
@@ -189,7 +202,7 @@ fn render_record_as_pat(
         None => {
             format!(
                 "{name} {{ {}{} }}",
-                fields.map(|field| field.name(db).display_no_db().to_smolstr()).format(", "),
+                fields.map(|field| field.name(db).display_no_db(edition).to_smolstr()).format(", "),
                 if fields_omitted { ", .." } else { "" },
                 name = name
             )
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs
index b81caf24220bf..09eb19201c5b0 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs
@@ -33,14 +33,22 @@ fn render(
     let (name, escaped_name) = if with_eq {
         (
             SmolStr::from_iter([&name.unescaped().display(db).to_smolstr(), " = "]),
-            SmolStr::from_iter([&name.display_no_db().to_smolstr(), " = "]),
+            SmolStr::from_iter([&name.display_no_db(ctx.completion.edition).to_smolstr(), " = "]),
         )
     } else {
-        (name.unescaped().display(db).to_smolstr(), name.display_no_db().to_smolstr())
+        (
+            name.unescaped().display(db).to_smolstr(),
+            name.display_no_db(ctx.completion.edition).to_smolstr(),
+        )
     };
-    let detail = type_alias.display(db).to_string();
+    let detail = type_alias.display(db, ctx.completion.edition).to_string();
 
-    let mut item = CompletionItem::new(SymbolKind::TypeAlias, ctx.source_range(), name);
+    let mut item = CompletionItem::new(
+        SymbolKind::TypeAlias,
+        ctx.source_range(),
+        name,
+        ctx.completion.edition,
+    );
     item.set_documentation(ctx.docs(type_alias))
         .set_deprecated(ctx.is_deprecated(type_alias) || ctx.is_deprecated_assoc_item(type_alias))
         .detail(detail)
@@ -48,7 +56,7 @@ fn render(
 
     if let Some(actm) = type_alias.as_assoc_item(db) {
         if let Some(trt) = actm.container_or_implemented_trait(db) {
-            item.trait_name(trt.name(db).display_no_db().to_smolstr());
+            item.trait_name(trt.name(db).display_no_db(ctx.completion.edition).to_smolstr());
         }
     }
     item.insert_text(escaped_name);
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs
index ca7593c122ee2..e053e299d903a 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs
@@ -22,21 +22,29 @@ pub(crate) fn render_union_literal(
     let name = local_name.unwrap_or_else(|| un.name(ctx.db()));
 
     let (qualified_name, escaped_qualified_name) = match path {
-        Some(p) => (p.unescaped().display(ctx.db()).to_string(), p.display(ctx.db()).to_string()),
-        None => {
-            (name.unescaped().display(ctx.db()).to_string(), name.display(ctx.db()).to_string())
-        }
+        Some(p) => (
+            p.unescaped().display(ctx.db()).to_string(),
+            p.display(ctx.db(), ctx.completion.edition).to_string(),
+        ),
+        None => (
+            name.unescaped().display(ctx.db()).to_string(),
+            name.display(ctx.db(), ctx.completion.edition).to_string(),
+        ),
     };
     let label = format_literal_label(
-        &name.display_no_db().to_smolstr(),
+        &name.display_no_db(ctx.completion.edition).to_smolstr(),
         StructKind::Record,
         ctx.snippet_cap(),
     );
-    let lookup = format_literal_lookup(&name.display_no_db().to_smolstr(), StructKind::Record);
+    let lookup = format_literal_lookup(
+        &name.display_no_db(ctx.completion.edition).to_smolstr(),
+        StructKind::Record,
+    );
     let mut item = CompletionItem::new(
         CompletionItemKind::SymbolKind(SymbolKind::Union),
         ctx.source_range(),
         label,
+        ctx.completion.edition,
     );
 
     item.lookup_by(lookup);
@@ -54,7 +62,10 @@ pub(crate) fn render_union_literal(
             escaped_qualified_name,
             fields
                 .iter()
-                .map(|field| field.name(ctx.db()).display_no_db().to_smolstr())
+                .map(|field| field
+                    .name(ctx.db())
+                    .display_no_db(ctx.completion.edition)
+                    .to_smolstr())
                 .format(",")
         )
     } else {
@@ -62,7 +73,10 @@ pub(crate) fn render_union_literal(
             "{} {{ {} }}",
             escaped_qualified_name,
             fields.iter().format_with(", ", |field, f| {
-                f(&format_args!("{}: ()", field.name(ctx.db()).display(ctx.db())))
+                f(&format_args!(
+                    "{}: ()",
+                    field.name(ctx.db()).display(ctx.db(), ctx.completion.edition)
+                ))
             })
         )
     };
@@ -73,8 +87,8 @@ pub(crate) fn render_union_literal(
         fields.iter().format_with(", ", |field, f| {
             f(&format_args!(
                 "{}: {}",
-                field.name(ctx.db()).display(ctx.db()),
-                field.ty(ctx.db()).display(ctx.db())
+                field.name(ctx.db()).display(ctx.db(), ctx.completion.edition),
+                field.ty(ctx.db()).display(ctx.db(), ctx.completion.edition)
             ))
         }),
         if fields_omitted { ", .." } else { "" }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs
index bc2df9e39f3d5..d8516ea107869 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs
@@ -4,7 +4,7 @@ use crate::context::CompletionContext;
 use hir::{db::HirDatabase, sym, HasAttrs, HasCrate, HasVisibility, HirDisplay, StructKind};
 use ide_db::SnippetCap;
 use itertools::Itertools;
-use syntax::SmolStr;
+use syntax::{Edition, SmolStr};
 
 /// A rendered struct, union, or enum variant, split into fields for actual
 /// auto-completion (`literal`, using `field: ()`) and display in the
@@ -21,20 +21,29 @@ pub(crate) fn render_record_lit(
     snippet_cap: Option<SnippetCap>,
     fields: &[hir::Field],
     path: &str,
+    edition: Edition,
 ) -> RenderedLiteral {
     if snippet_cap.is_none() {
         return RenderedLiteral { literal: path.to_owned(), detail: path.to_owned() };
     }
     let completions = fields.iter().enumerate().format_with(", ", |(idx, field), f| {
         if snippet_cap.is_some() {
-            f(&format_args!("{}: ${{{}:()}}", field.name(db).display(db.upcast()), idx + 1))
+            f(&format_args!(
+                "{}: ${{{}:()}}",
+                field.name(db).display(db.upcast(), edition),
+                idx + 1
+            ))
         } else {
-            f(&format_args!("{}: ()", field.name(db).display(db.upcast())))
+            f(&format_args!("{}: ()", field.name(db).display(db.upcast(), edition)))
         }
     });
 
     let types = fields.iter().format_with(", ", |field, f| {
-        f(&format_args!("{}: {}", field.name(db).display(db.upcast()), field.ty(db).display(db)))
+        f(&format_args!(
+            "{}: {}",
+            field.name(db).display(db.upcast(), edition),
+            field.ty(db).display(db, edition)
+        ))
     });
 
     RenderedLiteral {
@@ -50,6 +59,7 @@ pub(crate) fn render_tuple_lit(
     snippet_cap: Option<SnippetCap>,
     fields: &[hir::Field],
     path: &str,
+    edition: Edition,
 ) -> RenderedLiteral {
     if snippet_cap.is_none() {
         return RenderedLiteral { literal: path.to_owned(), detail: path.to_owned() };
@@ -62,7 +72,7 @@ pub(crate) fn render_tuple_lit(
         }
     });
 
-    let types = fields.iter().format_with(", ", |field, f| f(&field.ty(db).display(db)));
+    let types = fields.iter().format_with(", ", |field, f| f(&field.ty(db).display(db, edition)));
 
     RenderedLiteral {
         literal: format!("{path}({completions})"),
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
index 415f2afeebbea..04ba7e1f41b62 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
@@ -17,6 +17,7 @@ mod item_list;
 mod pattern;
 mod predicate;
 mod proc_macros;
+mod raw_identifiers;
 mod record;
 mod special;
 mod type_pos;
@@ -105,22 +106,35 @@ pub(crate) fn completion_list_with_trigger_character(
     completion_list_with_config(TEST_CONFIG, ra_fixture, true, trigger_character)
 }
 
-fn completion_list_with_config(
+fn completion_list_with_config_raw(
     config: CompletionConfig,
     ra_fixture: &str,
     include_keywords: bool,
     trigger_character: Option<char>,
-) -> String {
+) -> Vec<CompletionItem> {
     // filter out all but one built-in type completion for smaller test outputs
     let items = get_all_items(config, ra_fixture, trigger_character);
-    let items = items
+    items
         .into_iter()
         .filter(|it| it.kind != CompletionItemKind::BuiltinType || it.label == "u32")
         .filter(|it| include_keywords || it.kind != CompletionItemKind::Keyword)
         .filter(|it| include_keywords || it.kind != CompletionItemKind::Snippet)
         .sorted_by_key(|it| (it.kind, it.label.clone(), it.detail.as_ref().map(ToOwned::to_owned)))
-        .collect();
-    render_completion_list(items)
+        .collect()
+}
+
+fn completion_list_with_config(
+    config: CompletionConfig,
+    ra_fixture: &str,
+    include_keywords: bool,
+    trigger_character: Option<char>,
+) -> String {
+    render_completion_list(completion_list_with_config_raw(
+        config,
+        ra_fixture,
+        include_keywords,
+        trigger_character,
+    ))
 }
 
 /// Creates analysis from a multi-file fixture, returns positions marked with $0.
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs
index 158dbaf1b1dd1..8350fdcc4c07c 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs
@@ -1633,3 +1633,39 @@ fn main() {
         "#]],
     );
 }
+
+#[test]
+fn trait_impl_on_slice_method_on_deref_slice_type() {
+    check(
+        r#"
+//- minicore: deref, sized
+struct SliceDeref;
+impl core::ops::Deref for SliceDeref {
+    type Target = [()];
+
+    fn deref(&self) -> &Self::Target {
+        &[]
+    }
+}
+fn main() {
+    SliceDeref.choose$0();
+}
+mod module {
+    pub(super) trait SliceRandom {
+        type Item;
+
+        fn choose(&self);
+    }
+
+    impl<T> SliceRandom for [T] {
+        type Item = T;
+
+        fn choose(&self) {}
+    }
+}
+"#,
+        expect![[r#"
+            me choose (use module::SliceRandom) fn(&self)
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs
index 62eb642b3bc33..46a3e97d3e92d 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs
@@ -19,7 +19,7 @@ struct Foo<'lt, T, const C: usize> where $0 {}
             en Enum      Enum
             ma makro!(…) macro_rules! makro
             md module
-            st Foo<…>    Foo<{unknown}, _>
+            st Foo<…>    Foo<'_, {unknown}, _>
             st Record    Record
             st Tuple     Tuple
             st Unit      Unit
@@ -92,7 +92,7 @@ struct Foo<'lt, T, const C: usize> where for<'a> $0 {}
             en Enum      Enum
             ma makro!(…) macro_rules! makro
             md module
-            st Foo<…>    Foo<{unknown}, _>
+            st Foo<…>    Foo<'_, {unknown}, _>
             st Record    Record
             st Tuple     Tuple
             st Unit      Unit
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/raw_identifiers.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/raw_identifiers.rs
new file mode 100644
index 0000000000000..bc630189edc91
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/raw_identifiers.rs
@@ -0,0 +1,84 @@
+use base_db::SourceDatabase;
+use expect_test::{expect, Expect};
+use itertools::Itertools;
+
+use crate::tests::{completion_list_with_config_raw, position, TEST_CONFIG};
+
+fn check(ra_fixture: &str, expect: Expect) {
+    let completions = completion_list_with_config_raw(TEST_CONFIG, ra_fixture, true, None);
+    let (db, position) = position(ra_fixture);
+    let mut actual = db.file_text(position.file_id).to_string();
+    completions
+        .into_iter()
+        .exactly_one()
+        .expect("more than one completion")
+        .text_edit
+        .apply(&mut actual);
+    expect.assert_eq(&actual);
+}
+
+#[test]
+fn keyword_since_edition_completes_without_raw_on_old_edition() {
+    check(
+        r#"
+//- /a.rs crate:a edition:2015
+pub fn dyn() {}
+
+//- /b.rs crate:b edition:2015 deps:a new_source_root:local
+fn foo() {
+    a::dyn$0
+"#,
+        expect![[r#"
+            fn foo() {
+                a::dyn()$0
+        "#]],
+    );
+
+    check(
+        r#"
+//- /a.rs crate:a edition:2018
+pub fn r#dyn() {}
+
+//- /b.rs crate:b edition:2015 deps:a new_source_root:local
+fn foo() {
+    a::dyn$0
+"#,
+        expect![[r#"
+            fn foo() {
+                a::dyn()$0
+        "#]],
+    );
+}
+
+#[test]
+fn keyword_since_edition_completes_with_raw_on_new_edition() {
+    check(
+        r#"
+//- /a.rs crate:a edition:2015
+pub fn dyn() {}
+
+//- /b.rs crate:b edition:2018 deps:a new_source_root:local
+fn foo() {
+    a::dyn$0
+"#,
+        expect![[r#"
+            fn foo() {
+                a::r#dyn()$0
+        "#]],
+    );
+
+    check(
+        r#"
+//- /a.rs crate:a edition:2018
+pub fn r#dyn() {}
+
+//- /b.rs crate:b edition:2018 deps:a new_source_root:local
+fn foo() {
+    a::dyn$0
+"#,
+        expect![[r#"
+            fn foo() {
+                a::r#dyn()$0
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
index ff38c16108791..db4ac9381cedb 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
@@ -20,8 +20,8 @@ struct Foo<'lt, T, const C: usize> {
             en Enum      Enum
             ma makro!(…) macro_rules! makro
             md module
-            sp Self      Foo<{unknown}, _>
-            st Foo<…>    Foo<{unknown}, _>
+            sp Self      Foo<'_, {unknown}, _>
+            st Foo<…>    Foo<'_, {unknown}, _>
             st Record    Record
             st Tuple     Tuple
             st Unit      Unit
@@ -45,8 +45,8 @@ struct Foo<'lt, T, const C: usize>(f$0);
             en Enum       Enum
             ma makro!(…)  macro_rules! makro
             md module
-            sp Self       Foo<{unknown}, _>
-            st Foo<…>     Foo<{unknown}, _>
+            sp Self       Foo<'_, {unknown}, _>
+            st Foo<…>     Foo<'_, {unknown}, _>
             st Record     Record
             st Tuple      Tuple
             st Unit       Unit
diff --git a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml
index 8f3cae2fa1703..c078188d6d3cd 100644
--- a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml
@@ -19,7 +19,6 @@ tracing.workspace = true
 rayon.workspace = true
 fst = { version = "0.4.7", default-features = false }
 rustc-hash.workspace = true
-once_cell = "1.17.0"
 either.workspace = true
 itertools.workspace = true
 arrayvec.workspace = true
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
index 991bef344a310..5d4b1999088a0 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
@@ -14,10 +14,11 @@ use hir::{
     ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, ToolModule, Trait,
     TraitAlias, TupleField, TypeAlias, Variant, VariantDef, Visibility,
 };
+use span::Edition;
 use stdx::{format_to, impl_from};
 use syntax::{
     ast::{self, AstNode},
-    match_ast, SyntaxKind, SyntaxNode, SyntaxToken, ToSmolStr,
+    match_ast, SyntaxKind, SyntaxNode, SyntaxToken,
 };
 
 use crate::documentation::{Documentation, HasDocs};
@@ -157,6 +158,7 @@ impl Definition {
         &self,
         db: &RootDatabase,
         famous_defs: Option<&FamousDefs<'_, '_>>,
+        edition: Edition,
     ) -> Option<Documentation> {
         let docs = match self {
             Definition::Macro(it) => it.docs(db),
@@ -173,8 +175,8 @@ impl Definition {
             Definition::BuiltinType(it) => {
                 famous_defs.and_then(|fd| {
                     // std exposes prim_{} modules with docstrings on the root to document the builtins
-                    let primitive_mod = format!("prim_{}", it.name().display(fd.0.db));
-                    let doc_owner = find_std_module(fd, &primitive_mod)?;
+                    let primitive_mod = format!("prim_{}", it.name().display(fd.0.db, edition));
+                    let doc_owner = find_std_module(fd, &primitive_mod, edition)?;
                     doc_owner.docs(fd.0.db)
                 })
             }
@@ -192,13 +194,18 @@ impl Definition {
                 let AttributeTemplate { word, list, name_value_str } = it.template(db)?;
                 let mut docs = "Valid forms are:".to_owned();
                 if word {
-                    format_to!(docs, "\n - #\\[{}]", name.display(db));
+                    format_to!(docs, "\n - #\\[{}]", name.display(db, edition));
                 }
                 if let Some(list) = list {
-                    format_to!(docs, "\n - #\\[{}({})]", name.display(db), list);
+                    format_to!(docs, "\n - #\\[{}({})]", name.display(db, edition), list);
                 }
                 if let Some(name_value_str) = name_value_str {
-                    format_to!(docs, "\n - #\\[{} = {}]", name.display(db), name_value_str);
+                    format_to!(
+                        docs,
+                        "\n - #\\[{} = {}]",
+                        name.display(db, edition),
+                        name_value_str
+                    );
                 }
                 Some(Documentation::new(docs.replace('*', "\\*")))
             }
@@ -218,57 +225,63 @@ impl Definition {
         })
     }
 
-    pub fn label(&self, db: &RootDatabase) -> String {
+    pub fn label(&self, db: &RootDatabase, edition: Edition) -> String {
         match *self {
-            Definition::Macro(it) => it.display(db).to_string(),
-            Definition::Field(it) => it.display(db).to_string(),
-            Definition::TupleField(it) => it.display(db).to_string(),
-            Definition::Module(it) => it.display(db).to_string(),
-            Definition::Function(it) => it.display(db).to_string(),
-            Definition::Adt(it) => it.display(db).to_string(),
-            Definition::Variant(it) => it.display(db).to_string(),
-            Definition::Const(it) => it.display(db).to_string(),
-            Definition::Static(it) => it.display(db).to_string(),
-            Definition::Trait(it) => it.display(db).to_string(),
-            Definition::TraitAlias(it) => it.display(db).to_string(),
-            Definition::TypeAlias(it) => it.display(db).to_string(),
-            Definition::BuiltinType(it) => it.name().display(db).to_string(),
-            Definition::BuiltinLifetime(it) => it.name().display(db).to_string(),
+            Definition::Macro(it) => it.display(db, edition).to_string(),
+            Definition::Field(it) => it.display(db, edition).to_string(),
+            Definition::TupleField(it) => it.display(db, edition).to_string(),
+            Definition::Module(it) => it.display(db, edition).to_string(),
+            Definition::Function(it) => it.display(db, edition).to_string(),
+            Definition::Adt(it) => it.display(db, edition).to_string(),
+            Definition::Variant(it) => it.display(db, edition).to_string(),
+            Definition::Const(it) => it.display(db, edition).to_string(),
+            Definition::Static(it) => it.display(db, edition).to_string(),
+            Definition::Trait(it) => it.display(db, edition).to_string(),
+            Definition::TraitAlias(it) => it.display(db, edition).to_string(),
+            Definition::TypeAlias(it) => it.display(db, edition).to_string(),
+            Definition::BuiltinType(it) => it.name().display(db, edition).to_string(),
+            Definition::BuiltinLifetime(it) => it.name().display(db, edition).to_string(),
             Definition::Local(it) => {
                 let ty = it.ty(db);
-                let ty_display = ty.display_truncated(db, None);
+                let ty_display = ty.display_truncated(db, None, edition);
                 let is_mut = if it.is_mut(db) { "mut " } else { "" };
                 if it.is_self(db) {
                     format!("{is_mut}self: {ty_display}")
                 } else {
                     let name = it.name(db);
                     let let_kw = if it.is_param(db) { "" } else { "let " };
-                    format!("{let_kw}{is_mut}{}: {ty_display}", name.display(db))
+                    format!("{let_kw}{is_mut}{}: {ty_display}", name.display(db, edition))
                 }
             }
             Definition::SelfType(impl_def) => {
                 let self_ty = &impl_def.self_ty(db);
                 match self_ty.as_adt() {
-                    Some(it) => it.display(db).to_string(),
-                    None => self_ty.display(db).to_string(),
+                    Some(it) => it.display(db, edition).to_string(),
+                    None => self_ty.display(db, edition).to_string(),
                 }
             }
-            Definition::GenericParam(it) => it.display(db).to_string(),
-            Definition::Label(it) => it.name(db).display(db).to_string(),
-            Definition::ExternCrateDecl(it) => it.display(db).to_string(),
-            Definition::BuiltinAttr(it) => format!("#[{}]", it.name(db).display(db)),
-            Definition::ToolModule(it) => it.name(db).display(db).to_string(),
-            Definition::DeriveHelper(it) => format!("derive_helper {}", it.name(db).display(db)),
+            Definition::GenericParam(it) => it.display(db, edition).to_string(),
+            Definition::Label(it) => it.name(db).display(db, edition).to_string(),
+            Definition::ExternCrateDecl(it) => it.display(db, edition).to_string(),
+            Definition::BuiltinAttr(it) => format!("#[{}]", it.name(db).display(db, edition)),
+            Definition::ToolModule(it) => it.name(db).display(db, edition).to_string(),
+            Definition::DeriveHelper(it) => {
+                format!("derive_helper {}", it.name(db).display(db, edition))
+            }
         }
     }
 }
 
-fn find_std_module(famous_defs: &FamousDefs<'_, '_>, name: &str) -> Option<hir::Module> {
+fn find_std_module(
+    famous_defs: &FamousDefs<'_, '_>,
+    name: &str,
+    edition: Edition,
+) -> Option<hir::Module> {
     let db = famous_defs.0.db;
     let std_crate = famous_defs.std()?;
     let std_root_module = std_crate.root_module();
     std_root_module.children(db).find(|module| {
-        module.name(db).map_or(false, |module| module.display(db).to_string() == name)
+        module.name(db).map_or(false, |module| module.display(db, edition).to_string() == name)
     })
 }
 
@@ -670,7 +683,7 @@ impl NameRefClass {
                                 hir::AssocItem::TypeAlias(it) => Some(it),
                                 _ => None,
                             })
-                            .find(|alias| alias.name(sema.db).display_no_db().to_smolstr() == name_ref.text().as_str())
+                            .find(|alias| alias.name(sema.db).eq_ident(name_ref.text().as_str()))
                         {
                             return Some(NameRefClass::Definition(Definition::TypeAlias(ty)));
                         }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs
index 1a16a567f36ba..ba6e50abf658b 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs
@@ -2,7 +2,6 @@
 
 use base_db::{CrateOrigin, LangCrateOrigin, SourceDatabase};
 use hir::{Crate, Enum, Function, Macro, Module, ScopeDef, Semantics, Trait};
-use syntax::ToSmolStr;
 
 use crate::RootDatabase;
 
@@ -199,18 +198,14 @@ impl FamousDefs<'_, '_> {
         for segment in path {
             module = module.children(db).find_map(|child| {
                 let name = child.name(db)?;
-                if name.display_no_db().to_smolstr() == segment {
+                if name.eq_ident(segment) {
                     Some(child)
                 } else {
                     None
                 }
             })?;
         }
-        let def = module
-            .scope(db, None)
-            .into_iter()
-            .find(|(name, _def)| name.display_no_db().to_smolstr() == trait_)?
-            .1;
+        let def = module.scope(db, None).into_iter().find(|(name, _def)| name.eq_ident(trait_))?.1;
         Some(def)
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs
index e6638dde5d403..84fa58d743bbc 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs
@@ -3,8 +3,8 @@
 use std::collections::VecDeque;
 
 use base_db::SourceRootDatabase;
-use hir::{Crate, DescendPreference, ItemInNs, ModuleDef, Name, Semantics};
-use span::FileId;
+use hir::{Crate, ItemInNs, ModuleDef, Name, Semantics};
+use span::{Edition, FileId};
 use syntax::{
     ast::{self, make},
     AstToken, SyntaxKind, SyntaxToken, ToSmolStr, TokenAtOffset,
@@ -35,7 +35,7 @@ pub fn pick_token<T: AstToken>(mut tokens: TokenAtOffset<SyntaxToken>) -> Option
 }
 
 /// Converts the mod path struct into its ast representation.
-pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path {
+pub fn mod_path_to_ast(path: &hir::ModPath, edition: Edition) -> ast::Path {
     let _p = tracing::info_span!("mod_path_to_ast").entered();
 
     let mut segments = Vec::new();
@@ -50,11 +50,9 @@ pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path {
         hir::PathKind::Abs => is_abs = true,
     }
 
-    segments.extend(
-        path.segments().iter().map(|segment| {
-            make::path_segment(make::name_ref(&segment.display_no_db().to_smolstr()))
-        }),
-    );
+    segments.extend(path.segments().iter().map(|segment| {
+        make::path_segment(make::name_ref(&segment.display_no_db(edition).to_smolstr()))
+    }));
     make::path_from_segments(segments, is_abs)
 }
 
@@ -114,11 +112,12 @@ pub fn is_editable_crate(krate: Crate, db: &RootDatabase) -> bool {
     !db.source_root(source_root_id).is_library
 }
 
+// FIXME: This is a weird function
 pub fn get_definition(
     sema: &Semantics<'_, RootDatabase>,
     token: SyntaxToken,
 ) -> Option<Definition> {
-    for token in sema.descend_into_macros(DescendPreference::None, token) {
+    for token in sema.descend_into_macros_exact(token) {
         let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops);
         if let Some(&[x]) = def.as_deref() {
             return Some(x);
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
index 1c4c15f2557d7..82a182806a4cb 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
@@ -9,7 +9,7 @@ use itertools::{EitherOrBoth, Itertools};
 use rustc_hash::{FxHashMap, FxHashSet};
 use syntax::{
     ast::{self, make, HasName},
-    AstNode, SmolStr, SyntaxNode, ToSmolStr,
+    AstNode, SmolStr, SyntaxNode,
 };
 
 use crate::{
@@ -459,7 +459,7 @@ fn find_import_for_segment(
     unresolved_first_segment: &str,
 ) -> Option<ItemInNs> {
     let segment_is_name = item_name(db, original_item)
-        .map(|name| name.display_no_db().to_smolstr() == unresolved_first_segment)
+        .map(|name| name.eq_ident(unresolved_first_segment))
         .unwrap_or(false);
 
     Some(if segment_is_name {
@@ -483,7 +483,7 @@ fn module_with_segment_name(
     };
     while let Some(module) = current_module {
         if let Some(module_name) = module.name(db) {
-            if module_name.display_no_db().to_smolstr() == segment_name {
+            if module_name.eq_ident(segment_name) {
                 return Some(module);
             }
         }
@@ -531,40 +531,61 @@ fn trait_applicable_items(
     })
     .collect();
 
-    trait_candidates.retain(|&candidate_trait_id| {
-        // we care about the following cases:
-        // 1. Trait's definition crate
-        // 2. Definition crates for all trait's generic arguments
-        //     a. This is recursive for fundamental types: `Into<Box<A>> for ()`` is OK, but
-        //        `Into<Vec<A>> for ()`` is *not*.
-        // 3. Receiver type definition crate
-        //    a. This is recursive for fundamental types
-        let defining_crate_for_trait = Trait::from(candidate_trait_id).krate(db);
-        let Some(receiver) = trait_candidate.receiver_ty.fingerprint_for_trait_impl() else {
-            return false;
-        };
-
-        // in order to handle implied bounds through an associated type, keep any
-        // method receiver that matches `TyFingerprint::Unnameable`. this receiver
-        // won't be in `TraitImpls` anyways, as `TraitImpls` only contains actual
-        // implementations.
-        if matches!(receiver, TyFingerprint::Unnameable) {
-            return true;
+    let autoderef_method_receiver = {
+        let mut deref_chain = trait_candidate.receiver_ty.autoderef(db).collect::<Vec<_>>();
+        // As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!)
+        if let Some((ty, _len)) = deref_chain.last().and_then(|ty| ty.as_array(db)) {
+            let slice = Type::new_slice(ty);
+            deref_chain.push(slice);
         }
+        deref_chain
+            .into_iter()
+            .filter_map(|ty| Some((ty.krate(db).into(), ty.fingerprint_for_trait_impl()?)))
+            .sorted()
+            .unique()
+            .collect::<Vec<_>>()
+    };
 
-        let definitions_exist_in_trait_crate = db
-            .trait_impls_in_crate(defining_crate_for_trait.into())
-            .has_impls_for_trait_and_self_ty(candidate_trait_id, receiver);
+    // can be empty if the entire deref chain is has no valid trait impl fingerprints
+    if autoderef_method_receiver.is_empty() {
+        return Default::default();
+    }
 
-        // this is a closure for laziness: if `definitions_exist_in_trait_crate` is true,
-        // we can avoid a second db lookup.
-        let definitions_exist_in_receiver_crate = || {
-            db.trait_impls_in_crate(trait_candidate.receiver_ty.krate(db).into())
-                .has_impls_for_trait_and_self_ty(candidate_trait_id, receiver)
-        };
+    // in order to handle implied bounds through an associated type, keep all traits if any
+    // type in the deref chain matches `TyFingerprint::Unnameable`. This fingerprint
+    // won't be in `TraitImpls` anyways, as `TraitImpls` only contains actual implementations.
+    if !autoderef_method_receiver
+        .iter()
+        .any(|(_, fingerprint)| matches!(fingerprint, TyFingerprint::Unnameable))
+    {
+        trait_candidates.retain(|&candidate_trait_id| {
+            // we care about the following cases:
+            // 1. Trait's definition crate
+            // 2. Definition crates for all trait's generic arguments
+            //     a. This is recursive for fundamental types: `Into<Box<A>> for ()`` is OK, but
+            //        `Into<Vec<A>> for ()`` is *not*.
+            // 3. Receiver type definition crate
+            //    a. This is recursive for fundamental types
+            let defining_crate_for_trait = Trait::from(candidate_trait_id).krate(db);
+
+            let trait_impls_in_crate = db.trait_impls_in_crate(defining_crate_for_trait.into());
+            let definitions_exist_in_trait_crate =
+                autoderef_method_receiver.iter().any(|&(_, fingerprint)| {
+                    trait_impls_in_crate
+                        .has_impls_for_trait_and_self_ty(candidate_trait_id, fingerprint)
+                });
+            // this is a closure for laziness: if `definitions_exist_in_trait_crate` is true,
+            // we can avoid a second db lookup.
+            let definitions_exist_in_receiver_crate = || {
+                autoderef_method_receiver.iter().any(|&(krate, fingerprint)| {
+                    db.trait_impls_in_crate(krate)
+                        .has_impls_for_trait_and_self_ty(candidate_trait_id, fingerprint)
+                })
+            };
 
-        definitions_exist_in_trait_crate || definitions_exist_in_receiver_crate()
-    });
+            definitions_exist_in_trait_crate || definitions_exist_in_receiver_crate()
+        });
+    }
 
     let mut located_imports = FxIndexSet::default();
     let mut trait_import_paths = FxHashMap::default();
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs
index 0afa9163e31d2..49b3ca290f07d 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs
@@ -5,6 +5,7 @@ use either::Either;
 use hir::{AsAssocItem, HirDisplay, ImportPathConfig, ModuleDef, SemanticsScope};
 use itertools::Itertools;
 use rustc_hash::FxHashMap;
+use span::Edition;
 use syntax::{
     ast::{self, make, AstNode, HasGenericArgs},
     ted, NodeOrToken, SyntaxNode,
@@ -146,6 +147,7 @@ impl<'a> PathTransform<'a> {
         let mut type_substs: FxHashMap<hir::TypeParam, ast::Type> = Default::default();
         let mut const_substs: FxHashMap<hir::ConstParam, SyntaxNode> = Default::default();
         let mut defaulted_params: Vec<DefaultedParam> = Default::default();
+        let target_edition = target_module.krate().edition(self.source_scope.db);
         self.generic_def
             .into_iter()
             .flat_map(|it| it.type_or_const_params(db))
@@ -190,7 +192,7 @@ impl<'a> PathTransform<'a> {
                     }
                 }
                 (Either::Left(k), None) => {
-                    if let Some(default) = k.default(db) {
+                    if let Some(default) = k.default(db, target_edition) {
                         if let Some(default) = default.expr() {
                             const_substs.insert(k, default.syntax().clone_for_update());
                             defaulted_params.push(Either::Right(k));
@@ -204,7 +206,9 @@ impl<'a> PathTransform<'a> {
             .into_iter()
             .flat_map(|it| it.lifetime_params(db))
             .zip(self.substs.lifetimes.clone())
-            .filter_map(|(k, v)| Some((k.name(db).display(db.upcast()).to_string(), v.lifetime()?)))
+            .filter_map(|(k, v)| {
+                Some((k.name(db).display(db.upcast(), target_edition).to_string(), v.lifetime()?))
+            })
             .collect();
         let ctx = Ctx {
             type_substs,
@@ -213,6 +217,7 @@ impl<'a> PathTransform<'a> {
             target_module,
             source_scope: self.source_scope,
             same_self_type: self.target_scope.has_same_self_type(self.source_scope),
+            target_edition,
         };
         ctx.transform_default_values(defaulted_params);
         ctx
@@ -226,6 +231,7 @@ struct Ctx<'a> {
     target_module: hir::Module,
     source_scope: &'a SemanticsScope<'a>,
     same_self_type: bool,
+    target_edition: Edition,
 }
 
 fn preorder_rev(item: &SyntaxNode) -> impl Iterator<Item = SyntaxNode> {
@@ -318,7 +324,7 @@ impl Ctx<'_> {
                                 hir::ModuleDef::Trait(trait_ref),
                                 cfg,
                             )?;
-                            match make::ty_path(mod_path_to_ast(&found_path)) {
+                            match make::ty_path(mod_path_to_ast(&found_path, self.target_edition)) {
                                 ast::Type::PathType(path_ty) => Some(path_ty),
                                 _ => None,
                             }
@@ -374,7 +380,7 @@ impl Ctx<'_> {
                 };
                 let found_path =
                     self.target_module.find_path(self.source_scope.db.upcast(), def, cfg)?;
-                let res = mod_path_to_ast(&found_path).clone_for_update();
+                let res = mod_path_to_ast(&found_path, self.target_edition).clone_for_update();
                 if let Some(args) = path.segment().and_then(|it| it.generic_arg_list()) {
                     if let Some(segment) = res.segment() {
                         let old = segment.get_or_create_generic_arg_list();
@@ -417,7 +423,9 @@ impl Ctx<'_> {
                             cfg,
                         )?;
 
-                        if let Some(qual) = mod_path_to_ast(&found_path).qualifier() {
+                        if let Some(qual) =
+                            mod_path_to_ast(&found_path, self.target_edition).qualifier()
+                        {
                             let res = make::path_concat(qual, path_ty.path()?).clone_for_update();
                             ted::replace(path.syntax(), res.syntax());
                             return Some(());
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
index 232f242828777..262eefeec00e4 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
@@ -29,6 +29,7 @@ use span::{Edition, EditionedFileId, FileId, SyntaxContextId};
 use stdx::{never, TupleExt};
 use syntax::{
     ast::{self, HasName},
+    utils::is_raw_identifier,
     AstNode, SyntaxKind, TextRange, T,
 };
 use text_edit::{TextEdit, TextEditBuilder};
@@ -72,6 +73,9 @@ impl Definition {
         sema: &Semantics<'_, RootDatabase>,
         new_name: &str,
     ) -> Result<SourceChange> {
+        // We append `r#` if needed.
+        let new_name = new_name.trim_start_matches("r#");
+
         // self.krate() returns None if
         // self is a built-in attr, built-in type or tool module.
         // it is not allowed for these defs to be renamed.
@@ -227,8 +231,7 @@ fn rename_mod(
     module: hir::Module,
     new_name: &str,
 ) -> Result<SourceChange> {
-    if IdentifierKind::classify(module.krate().edition(sema.db), new_name)? != IdentifierKind::Ident
-    {
+    if IdentifierKind::classify(new_name)? != IdentifierKind::Ident {
         bail!("Invalid name `{0}`: cannot rename module to {0}", new_name);
     }
 
@@ -240,7 +243,6 @@ fn rename_mod(
 
     let InFile { file_id, value: def_source } = module.definition_source(sema.db);
     if let ModuleSource::SourceFile(..) = def_source {
-        let new_name = new_name.trim_start_matches("r#");
         let anchor = file_id.original_file(sema.db).file_id();
 
         let is_mod_rs = module.is_mod_rs(sema.db);
@@ -289,9 +291,14 @@ fn rename_mod(
                     .original_file_range_opt(sema.db)
                     .map(TupleExt::head)
                 {
+                    let new_name = if is_raw_identifier(new_name, file_id.edition()) {
+                        format!("r#{new_name}")
+                    } else {
+                        new_name.to_owned()
+                    };
                     source_change.insert_source_edit(
                         file_id.file_id(),
-                        TextEdit::replace(file_range.range, new_name.to_owned()),
+                        TextEdit::replace(file_range.range, new_name),
                     )
                 };
             }
@@ -302,7 +309,10 @@ fn rename_mod(
     let def = Definition::Module(module);
     let usages = def.usages(sema).all();
     let ref_edits = usages.iter().map(|(file_id, references)| {
-        (EditionedFileId::file_id(file_id), source_edit_from_references(references, def, new_name))
+        (
+            EditionedFileId::file_id(file_id),
+            source_edit_from_references(references, def, new_name, file_id.edition()),
+        )
     });
     source_change.extend(ref_edits);
 
@@ -314,12 +324,7 @@ fn rename_reference(
     def: Definition,
     new_name: &str,
 ) -> Result<SourceChange> {
-    let ident_kind = IdentifierKind::classify(
-        def.krate(sema.db)
-            .ok_or_else(|| RenameError("definition has no krate?".into()))?
-            .edition(sema.db),
-        new_name,
-    )?;
+    let ident_kind = IdentifierKind::classify(new_name)?;
 
     if matches!(
         def,
@@ -351,7 +356,10 @@ fn rename_reference(
     }
     let mut source_change = SourceChange::default();
     source_change.extend(usages.iter().map(|(file_id, references)| {
-        (EditionedFileId::file_id(file_id), source_edit_from_references(references, def, new_name))
+        (
+            EditionedFileId::file_id(file_id),
+            source_edit_from_references(references, def, new_name, file_id.edition()),
+        )
     }));
 
     let mut insert_def_edit = |def| {
@@ -367,7 +375,13 @@ pub fn source_edit_from_references(
     references: &[FileReference],
     def: Definition,
     new_name: &str,
+    edition: Edition,
 ) -> TextEdit {
+    let new_name = if is_raw_identifier(new_name, edition) {
+        format!("r#{new_name}")
+    } else {
+        new_name.to_owned()
+    };
     let mut edit = TextEdit::builder();
     // macros can cause multiple refs to occur for the same text range, so keep track of what we have edited so far
     let mut edited_ranges = Vec::new();
@@ -383,10 +397,10 @@ pub fn source_edit_from_references(
             // to make special rewrites like shorthand syntax and such, so just rename the node in
             // the macro input
             FileReferenceNode::NameRef(name_ref) if name_range == range => {
-                source_edit_from_name_ref(&mut edit, name_ref, new_name, def)
+                source_edit_from_name_ref(&mut edit, name_ref, &new_name, def)
             }
             FileReferenceNode::Name(name) if name_range == range => {
-                source_edit_from_name(&mut edit, name, new_name)
+                source_edit_from_name(&mut edit, name, &new_name)
             }
             _ => false,
         };
@@ -394,7 +408,7 @@ pub fn source_edit_from_references(
             let (range, new_name) = match name {
                 FileReferenceNode::Lifetime(_) => (
                     TextRange::new(range.start() + syntax::TextSize::from(1), range.end()),
-                    new_name.strip_prefix('\'').unwrap_or(new_name).to_owned(),
+                    new_name.strip_prefix('\'').unwrap_or(&new_name).to_owned(),
                 ),
                 _ => (range, new_name.to_owned()),
             };
@@ -522,6 +536,13 @@ fn source_edit_from_def(
     def: Definition,
     new_name: &str,
 ) -> Result<(FileId, TextEdit)> {
+    let new_name_edition_aware = |new_name: &str, file_id: EditionedFileId| {
+        if is_raw_identifier(new_name, file_id.edition()) {
+            format!("r#{new_name}")
+        } else {
+            new_name.to_owned()
+        }
+    };
     let mut edit = TextEdit::builder();
     if let Definition::Local(local) = def {
         let mut file_id = None;
@@ -536,7 +557,7 @@ fn source_edit_from_def(
                 {
                     Some(FileRange { file_id: file_id2, range }) => {
                         file_id = Some(file_id2);
-                        edit.replace(range, new_name.to_owned());
+                        edit.replace(range, new_name_edition_aware(new_name, file_id2));
                         continue;
                     }
                     None => {
@@ -550,7 +571,9 @@ fn source_edit_from_def(
                 // special cases required for renaming fields/locals in Record patterns
                 if let Some(pat_field) = pat.syntax().parent().and_then(ast::RecordPatField::cast) {
                     if let Some(name_ref) = pat_field.name_ref() {
-                        if new_name == name_ref.text() && pat.at_token().is_none() {
+                        if new_name == name_ref.text().as_str().trim_start_matches("r#")
+                            && pat.at_token().is_none()
+                        {
                             // Foo { field: ref mut local } -> Foo { ref mut field }
                             //       ^^^^^^ delete this
                             //                      ^^^^^ replace this with `field`
@@ -566,7 +589,10 @@ fn source_edit_from_def(
                             // Foo { field: ref mut local @ local 2} -> Foo { field: ref mut new_name @ local2 }
                             // Foo { field: ref mut local } -> Foo { field: ref mut new_name }
                             //                      ^^^^^ replace this with `new_name`
-                            edit.replace(name_range, new_name.to_owned());
+                            edit.replace(
+                                name_range,
+                                new_name_edition_aware(new_name, source.file_id),
+                            );
                         }
                     } else {
                         // Foo { ref mut field } -> Foo { field: ref mut new_name }
@@ -576,10 +602,10 @@ fn source_edit_from_def(
                             pat.syntax().text_range().start(),
                             format!("{}: ", pat_field.field_name().unwrap()),
                         );
-                        edit.replace(name_range, new_name.to_owned());
+                        edit.replace(name_range, new_name_edition_aware(new_name, source.file_id));
                     }
                 } else {
-                    edit.replace(name_range, new_name.to_owned());
+                    edit.replace(name_range, new_name_edition_aware(new_name, source.file_id));
                 }
             }
         }
@@ -599,7 +625,7 @@ fn source_edit_from_def(
         }
         _ => (range, new_name.to_owned()),
     };
-    edit.replace(range, new_name);
+    edit.replace(range, new_name_edition_aware(&new_name, file_id));
     Ok((file_id.file_id(), edit.finish()))
 }
 
@@ -611,8 +637,9 @@ pub enum IdentifierKind {
 }
 
 impl IdentifierKind {
-    pub fn classify(edition: Edition, new_name: &str) -> Result<IdentifierKind> {
-        match parser::LexedStr::single_token(edition, new_name) {
+    pub fn classify(new_name: &str) -> Result<IdentifierKind> {
+        let new_name = new_name.trim_start_matches("r#");
+        match parser::LexedStr::single_token(Edition::LATEST, new_name) {
             Some(res) => match res {
                 (SyntaxKind::IDENT, _) => {
                     if let Some(inner) = new_name.strip_prefix("r#") {
@@ -626,6 +653,7 @@ impl IdentifierKind {
                 (SyntaxKind::LIFETIME_IDENT, _) if new_name != "'static" && new_name != "'_" => {
                     Ok(IdentifierKind::Lifetime)
                 }
+                _ if is_raw_identifier(new_name, Edition::LATEST) => Ok(IdentifierKind::Ident),
                 (_, Some(syntax_error)) => bail!("Invalid name `{}`: {}", new_name, syntax_error),
                 (_, None) => bail!("Invalid name `{}`: not an identifier", new_name),
             },
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
index 9e01a6d44083c..12ce5a403fe87 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
@@ -5,18 +5,23 @@
 //! name resolution.
 
 use std::mem;
+use std::{cell::LazyCell, cmp::Reverse};
 
 use base_db::{salsa::Database, SourceDatabase, SourceRootDatabase};
 use hir::{
-    sym, AsAssocItem, DefWithBody, DescendPreference, FileRange, HasAttrs, HasSource, HirFileIdExt,
-    InFile, InRealFile, ModuleSource, PathResolution, Semantics, Visibility,
+    sym, Adt, AsAssocItem, DefWithBody, FileRange, FileRangeWrapper, HasAttrs, HasContainer,
+    HasSource, HirFileIdExt, InFile, InFileWrapper, InRealFile, ItemContainer, ModuleSource,
+    PathResolution, Semantics, Visibility,
 };
 use memchr::memmem::Finder;
-use once_cell::unsync::Lazy;
 use parser::SyntaxKind;
-use rustc_hash::FxHashMap;
+use rustc_hash::{FxHashMap, FxHashSet};
 use span::EditionedFileId;
-use syntax::{ast, match_ast, AstNode, AstToken, SyntaxElement, TextRange, TextSize, ToSmolStr};
+use syntax::{
+    ast::{self, HasName},
+    match_ast, AstNode, AstToken, SmolStr, SyntaxElement, SyntaxNode, TextRange, TextSize,
+    ToSmolStr,
+};
 use triomphe::Arc;
 
 use crate::{
@@ -442,6 +447,411 @@ impl<'a> FindUsages<'a> {
         res
     }
 
+    fn scope_files<'b>(
+        db: &'b RootDatabase,
+        scope: &'b SearchScope,
+    ) -> impl Iterator<Item = (Arc<str>, EditionedFileId, TextRange)> + 'b {
+        scope.entries.iter().map(|(&file_id, &search_range)| {
+            let text = db.file_text(file_id.file_id());
+            let search_range =
+                search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&*text)));
+
+            (text, file_id, search_range)
+        })
+    }
+
+    fn match_indices<'b>(
+        text: &'b str,
+        finder: &'b Finder<'b>,
+        search_range: TextRange,
+    ) -> impl Iterator<Item = TextSize> + 'b {
+        finder.find_iter(text.as_bytes()).filter_map(move |idx| {
+            let offset: TextSize = idx.try_into().unwrap();
+            if !search_range.contains_inclusive(offset) {
+                return None;
+            }
+            // If this is not a word boundary, that means this is only part of an identifier,
+            // so it can't be what we're looking for.
+            // This speeds up short identifiers significantly.
+            if text[..idx]
+                .chars()
+                .next_back()
+                .is_some_and(|ch| matches!(ch, 'A'..='Z' | 'a'..='z' | '_'))
+                || text[idx + finder.needle().len()..]
+                    .chars()
+                    .next()
+                    .is_some_and(|ch| matches!(ch, 'A'..='Z' | 'a'..='z' | '_' | '0'..='9'))
+            {
+                return None;
+            }
+            Some(offset)
+        })
+    }
+
+    fn find_nodes<'b>(
+        sema: &'b Semantics<'_, RootDatabase>,
+        name: &str,
+        node: &syntax::SyntaxNode,
+        offset: TextSize,
+    ) -> impl Iterator<Item = SyntaxNode> + 'b {
+        node.token_at_offset(offset)
+            .find(|it| {
+                // `name` is stripped of raw ident prefix. See the comment on name retrieval below.
+                it.text().trim_start_matches("r#") == name
+            })
+            .into_iter()
+            .flat_map(move |token| {
+                sema.descend_into_macros_exact_if_in_macro(token)
+                    .into_iter()
+                    .filter_map(|it| it.parent())
+            })
+    }
+
+    /// Performs a special fast search for associated functions. This is mainly intended
+    /// to speed up `new()` which can take a long time.
+    ///
+    /// The trick is instead of searching for `func_name` search for `TypeThatContainsContainerName::func_name`.
+    /// We cannot search exactly that (not even in tokens), because `ContainerName` may be aliased.
+    /// Instead, we perform a textual search for `ContainerName`. Then, we look for all cases where
+    /// `ContainerName` may be aliased (that includes `use ContainerName as Xyz` and
+    /// `type Xyz = ContainerName`). We collect a list of all possible aliases of `ContainerName`.
+    /// The list can have false positives (because there may be multiple types named `ContainerName`),
+    /// but it cannot have false negatives. Then, we look for `TypeThatContainsContainerNameOrAnyAlias::func_name`.
+    /// Those that will be found are of high chance to be actual hits (of course, we will need to verify
+    /// that).
+    ///
+    /// Returns true if completed the search.
+    // FIXME: Extend this to other cases, such as associated types/consts/enum variants (note those can be `use`d).
+    fn short_associated_function_fast_search(
+        &self,
+        sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
+        search_scope: &SearchScope,
+        name: &str,
+    ) -> bool {
+        if self.scope.is_some() {
+            return false;
+        }
+
+        let _p = tracing::info_span!("short_associated_function_fast_search").entered();
+
+        let container = (|| {
+            let Definition::Function(function) = self.def else {
+                return None;
+            };
+            if function.has_self_param(self.sema.db) {
+                return None;
+            }
+            match function.container(self.sema.db) {
+                // Only freestanding `impl`s qualify; methods from trait
+                // can be called from within subtraits and bounds.
+                ItemContainer::Impl(impl_) => {
+                    let has_trait = impl_.trait_(self.sema.db).is_some();
+                    if has_trait {
+                        return None;
+                    }
+                    let adt = impl_.self_ty(self.sema.db).as_adt()?;
+                    Some(adt)
+                }
+                _ => None,
+            }
+        })();
+        let Some(container) = container else {
+            return false;
+        };
+
+        fn has_any_name(node: &SyntaxNode, mut predicate: impl FnMut(&str) -> bool) -> bool {
+            node.descendants().any(|node| {
+                match_ast! {
+                    match node {
+                        ast::Name(it) => predicate(it.text().trim_start_matches("r#")),
+                        ast::NameRef(it) => predicate(it.text().trim_start_matches("r#")),
+                        _ => false
+                    }
+                }
+            })
+        }
+
+        // This is a fixpoint algorithm with O(number of aliases), but most types have no or few aliases,
+        // so this should stay fast.
+        //
+        /// Returns `(aliases, ranges_where_Self_can_refer_to_our_type)`.
+        fn collect_possible_aliases(
+            sema: &Semantics<'_, RootDatabase>,
+            container: Adt,
+        ) -> Option<(FxHashSet<SmolStr>, Vec<FileRangeWrapper<EditionedFileId>>)> {
+            fn insert_type_alias(
+                db: &RootDatabase,
+                to_process: &mut Vec<(SmolStr, SearchScope)>,
+                alias_name: &str,
+                def: Definition,
+            ) {
+                let alias = alias_name.trim_start_matches("r#").to_smolstr();
+                tracing::debug!("found alias: {alias}");
+                to_process.push((alias, def.search_scope(db)));
+            }
+
+            let _p = tracing::info_span!("collect_possible_aliases").entered();
+
+            let db = sema.db;
+            let container_name = container.name(db).unescaped().display(db).to_smolstr();
+            let search_scope = Definition::from(container).search_scope(db);
+            let mut seen = FxHashSet::default();
+            let mut completed = FxHashSet::default();
+            let mut to_process = vec![(container_name, search_scope)];
+            let mut is_possibly_self = Vec::new();
+            let mut total_files_searched = 0;
+
+            while let Some((current_to_process, current_to_process_search_scope)) = to_process.pop()
+            {
+                let is_alias = |alias: &ast::TypeAlias| {
+                    let def = sema.to_def(alias)?;
+                    let ty = def.ty(db);
+                    let is_alias = ty.as_adt()? == container;
+                    is_alias.then_some(def)
+                };
+
+                let finder = Finder::new(current_to_process.as_bytes());
+                for (file_text, file_id, search_range) in
+                    FindUsages::scope_files(db, &current_to_process_search_scope)
+                {
+                    let tree = LazyCell::new(move || sema.parse(file_id).syntax().clone());
+
+                    for offset in FindUsages::match_indices(&file_text, &finder, search_range) {
+                        let usages =
+                            FindUsages::find_nodes(sema, &current_to_process, &tree, offset)
+                                .filter(|it| {
+                                    matches!(it.kind(), SyntaxKind::NAME | SyntaxKind::NAME_REF)
+                                });
+                        for usage in usages {
+                            if let Some(alias) = usage.parent().and_then(|it| {
+                                let path = ast::PathSegment::cast(it)?.parent_path();
+                                let use_tree = ast::UseTree::cast(path.syntax().parent()?)?;
+                                use_tree.rename()?.name()
+                            }) {
+                                if seen.insert(InFileWrapper::new(
+                                    file_id,
+                                    alias.syntax().text_range(),
+                                )) {
+                                    tracing::debug!("found alias: {alias}");
+                                    cov_mark::hit!(container_use_rename);
+                                    // FIXME: `use`s have no easy way to determine their search scope, but they are rare.
+                                    to_process.push((
+                                        alias.text().to_smolstr(),
+                                        current_to_process_search_scope.clone(),
+                                    ));
+                                }
+                            } else if let Some(alias) =
+                                usage.ancestors().find_map(ast::TypeAlias::cast)
+                            {
+                                if let Some(name) = alias.name() {
+                                    if seen.insert(InFileWrapper::new(
+                                        file_id,
+                                        name.syntax().text_range(),
+                                    )) {
+                                        if let Some(def) = is_alias(&alias) {
+                                            cov_mark::hit!(container_type_alias);
+                                            insert_type_alias(
+                                                sema.db,
+                                                &mut to_process,
+                                                name.text().as_str(),
+                                                def.into(),
+                                            );
+                                        } else {
+                                            cov_mark::hit!(same_name_different_def_type_alias);
+                                        }
+                                    }
+                                }
+                            }
+
+                            // We need to account for `Self`. It can only refer to our type inside an impl.
+                            let impl_ = 'impl_: {
+                                for ancestor in usage.ancestors() {
+                                    if let Some(parent) = ancestor.parent() {
+                                        if let Some(parent) = ast::Impl::cast(parent) {
+                                            // Only if the GENERIC_PARAM_LIST is directly under impl, otherwise it may be in the self ty.
+                                            if matches!(
+                                                ancestor.kind(),
+                                                SyntaxKind::ASSOC_ITEM_LIST
+                                                    | SyntaxKind::WHERE_CLAUSE
+                                                    | SyntaxKind::GENERIC_PARAM_LIST
+                                            ) {
+                                                break;
+                                            }
+                                            if parent
+                                                .trait_()
+                                                .is_some_and(|trait_| *trait_.syntax() == ancestor)
+                                            {
+                                                break;
+                                            }
+
+                                            // Otherwise, found an impl where its self ty may be our type.
+                                            break 'impl_ Some(parent);
+                                        }
+                                    }
+                                }
+                                None
+                            };
+                            (|| {
+                                let impl_ = impl_?;
+                                is_possibly_self.push(sema.original_range(impl_.syntax()));
+                                let assoc_items = impl_.assoc_item_list()?;
+                                let type_aliases = assoc_items
+                                    .syntax()
+                                    .descendants()
+                                    .filter_map(ast::TypeAlias::cast);
+                                for type_alias in type_aliases {
+                                    let Some(ty) = type_alias.ty() else { continue };
+                                    let Some(name) = type_alias.name() else { continue };
+                                    let contains_self = ty
+                                        .syntax()
+                                        .descendants_with_tokens()
+                                        .any(|node| node.kind() == SyntaxKind::SELF_TYPE_KW);
+                                    if !contains_self {
+                                        continue;
+                                    }
+                                    if seen.insert(InFileWrapper::new(
+                                        file_id,
+                                        name.syntax().text_range(),
+                                    )) {
+                                        if let Some(def) = is_alias(&type_alias) {
+                                            cov_mark::hit!(self_type_alias);
+                                            insert_type_alias(
+                                                sema.db,
+                                                &mut to_process,
+                                                name.text().as_str(),
+                                                def.into(),
+                                            );
+                                        } else {
+                                            cov_mark::hit!(same_name_different_def_type_alias);
+                                        }
+                                    }
+                                }
+                                Some(())
+                            })();
+                        }
+                    }
+                }
+
+                completed.insert(current_to_process);
+
+                total_files_searched += current_to_process_search_scope.entries.len();
+                // FIXME: Maybe this needs to be relative to the project size, or at least to the initial search scope?
+                if total_files_searched > 20_000 && completed.len() > 100 {
+                    // This case is extremely unlikely (even searching for `Vec::new()` on rust-analyzer does not enter
+                    // here - it searches less than 10,000 files, and it does so in five seconds), but if we get here,
+                    // we at a risk of entering an almost-infinite loop of growing the aliases list. So just stop and
+                    // let normal search handle this case.
+                    tracing::info!(aliases_count = %completed.len(), "too much aliases; leaving fast path");
+                    return None;
+                }
+            }
+
+            // Impls can contain each other, so we need to deduplicate their ranges.
+            is_possibly_self.sort_unstable_by_key(|position| {
+                (position.file_id, position.range.start(), Reverse(position.range.end()))
+            });
+            is_possibly_self.dedup_by(|pos2, pos1| {
+                pos1.file_id == pos2.file_id
+                    && pos1.range.start() <= pos2.range.start()
+                    && pos1.range.end() >= pos2.range.end()
+            });
+
+            tracing::info!(aliases_count = %completed.len(), "aliases search completed");
+
+            Some((completed, is_possibly_self))
+        }
+
+        fn search(
+            this: &FindUsages<'_>,
+            finder: &Finder<'_>,
+            name: &str,
+            files: impl Iterator<Item = (Arc<str>, EditionedFileId, TextRange)>,
+            mut container_predicate: impl FnMut(
+                &SyntaxNode,
+                InFileWrapper<EditionedFileId, TextRange>,
+            ) -> bool,
+            sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
+        ) {
+            for (file_text, file_id, search_range) in files {
+                let tree = LazyCell::new(move || this.sema.parse(file_id).syntax().clone());
+
+                for offset in FindUsages::match_indices(&file_text, finder, search_range) {
+                    let usages = FindUsages::find_nodes(this.sema, name, &tree, offset)
+                        .filter_map(ast::NameRef::cast);
+                    for usage in usages {
+                        let found_usage = usage
+                            .syntax()
+                            .parent()
+                            .and_then(ast::PathSegment::cast)
+                            .map(|path_segment| {
+                                container_predicate(
+                                    path_segment.parent_path().syntax(),
+                                    InFileWrapper::new(file_id, usage.syntax().text_range()),
+                                )
+                            })
+                            .unwrap_or(false);
+                        if found_usage {
+                            this.found_name_ref(&usage, sink);
+                        }
+                    }
+                }
+            }
+        }
+
+        let Some((container_possible_aliases, is_possibly_self)) =
+            collect_possible_aliases(self.sema, container)
+        else {
+            return false;
+        };
+
+        cov_mark::hit!(short_associated_function_fast_search);
+
+        // FIXME: If Rust ever gains the ability to `use Struct::method` we'll also need to account for free
+        // functions.
+        let finder = Finder::new(name.as_bytes());
+        // The search for `Self` may return duplicate results with `ContainerName`, so deduplicate them.
+        let mut self_positions = FxHashSet::default();
+        tracing::info_span!("Self_search").in_scope(|| {
+            search(
+                self,
+                &finder,
+                name,
+                is_possibly_self.into_iter().map(|position| {
+                    (
+                        self.sema.db.file_text(position.file_id.file_id()),
+                        position.file_id,
+                        position.range,
+                    )
+                }),
+                |path, name_position| {
+                    let has_self = path
+                        .descendants_with_tokens()
+                        .any(|node| node.kind() == SyntaxKind::SELF_TYPE_KW);
+                    if has_self {
+                        self_positions.insert(name_position);
+                    }
+                    has_self
+                },
+                sink,
+            )
+        });
+        tracing::info_span!("aliases_search").in_scope(|| {
+            search(
+                self,
+                &finder,
+                name,
+                FindUsages::scope_files(self.sema.db, search_scope),
+                |path, name_position| {
+                    has_any_name(path, |name| container_possible_aliases.contains(name))
+                        && !self_positions.contains(&name_position)
+                },
+                sink,
+            )
+        });
+
+        true
+    }
+
     pub fn search(&self, sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool) {
         let _p = tracing::info_span!("FindUsages:search").entered();
         let sema = self.sema;
@@ -488,65 +898,23 @@ impl<'a> FindUsages<'a> {
             Some(s) => s.as_str(),
             None => return,
         };
-        let finder = &Finder::new(name);
-        let include_self_kw_refs =
-            self.include_self_kw_refs.as_ref().map(|ty| (ty, Finder::new("Self")));
 
-        // for<'a> |text: &'a str, name: &'a str, search_range: TextRange| -> impl Iterator<Item = TextSize> + 'a { ... }
-        fn match_indices<'a>(
-            text: &'a str,
-            finder: &'a Finder<'a>,
-            search_range: TextRange,
-        ) -> impl Iterator<Item = TextSize> + 'a {
-            finder.find_iter(text.as_bytes()).filter_map(move |idx| {
-                let offset: TextSize = idx.try_into().unwrap();
-                if !search_range.contains_inclusive(offset) {
-                    return None;
-                }
-                Some(offset)
-            })
+        // FIXME: This should probably depend on the number of the results (specifically, the number of false results).
+        if name.len() <= 7 && self.short_associated_function_fast_search(sink, &search_scope, name)
+        {
+            return;
         }
 
-        // for<'a> |scope: &'a SearchScope| -> impl Iterator<Item = (Arc<String>, EditionedFileId, TextRange)> + 'a { ... }
-        fn scope_files<'a>(
-            sema: &'a Semantics<'_, RootDatabase>,
-            scope: &'a SearchScope,
-        ) -> impl Iterator<Item = (Arc<str>, EditionedFileId, TextRange)> + 'a {
-            scope.entries.iter().map(|(&file_id, &search_range)| {
-                let text = sema.db.file_text(file_id.file_id());
-                let search_range =
-                    search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&*text)));
-
-                (text, file_id, search_range)
-            })
-        }
-
-        let find_nodes = move |name: &str, node: &syntax::SyntaxNode, offset: TextSize| {
-            node.token_at_offset(offset)
-                .find(|it| {
-                    // `name` is stripped of raw ident prefix. See the comment on name retrieval above.
-                    it.text().trim_start_matches("r#") == name
-                })
-                .into_iter()
-                .flat_map(move |token| {
-                    // FIXME: There should be optimization potential here
-                    // Currently we try to descend everything we find which
-                    // means we call `Semantics::descend_into_macros` on
-                    // every textual hit. That function is notoriously
-                    // expensive even for things that do not get down mapped
-                    // into macros.
-                    sema.descend_into_macros(DescendPreference::None, token)
-                        .into_iter()
-                        .filter_map(|it| it.parent())
-                })
-        };
+        let finder = &Finder::new(name);
+        let include_self_kw_refs =
+            self.include_self_kw_refs.as_ref().map(|ty| (ty, Finder::new("Self")));
 
-        for (text, file_id, search_range) in scope_files(sema, &search_scope) {
+        for (text, file_id, search_range) in Self::scope_files(sema.db, &search_scope) {
             self.sema.db.unwind_if_cancelled();
-            let tree = Lazy::new(move || sema.parse(file_id).syntax().clone());
+            let tree = LazyCell::new(move || sema.parse(file_id).syntax().clone());
 
             // Search for occurrences of the items name
-            for offset in match_indices(&text, finder, search_range) {
+            for offset in Self::match_indices(&text, finder, search_range) {
                 tree.token_at_offset(offset).for_each(|token| {
                     let Some(str_token) = ast::String::cast(token.clone()) else { return };
                     if let Some((range, nameres)) =
@@ -556,7 +924,9 @@ impl<'a> FindUsages<'a> {
                     }
                 });
 
-                for name in find_nodes(name, &tree, offset).filter_map(ast::NameLike::cast) {
+                for name in
+                    Self::find_nodes(sema, name, &tree, offset).filter_map(ast::NameLike::cast)
+                {
                     if match name {
                         ast::NameLike::NameRef(name_ref) => self.found_name_ref(&name_ref, sink),
                         ast::NameLike::Name(name) => self.found_name(&name, sink),
@@ -568,8 +938,9 @@ impl<'a> FindUsages<'a> {
             }
             // Search for occurrences of the `Self` referring to our type
             if let Some((self_ty, finder)) = &include_self_kw_refs {
-                for offset in match_indices(&text, finder, search_range) {
-                    for name_ref in find_nodes("Self", &tree, offset).filter_map(ast::NameRef::cast)
+                for offset in Self::match_indices(&text, finder, search_range) {
+                    for name_ref in
+                        Self::find_nodes(sema, "Self", &tree, offset).filter_map(ast::NameRef::cast)
                     {
                         if self.found_self_ty_name_ref(self_ty, &name_ref, sink) {
                             return;
@@ -587,13 +958,13 @@ impl<'a> FindUsages<'a> {
             let is_crate_root = module.is_crate_root().then(|| Finder::new("crate"));
             let finder = &Finder::new("super");
 
-            for (text, file_id, search_range) in scope_files(sema, &scope) {
+            for (text, file_id, search_range) in Self::scope_files(sema.db, &scope) {
                 self.sema.db.unwind_if_cancelled();
-                let tree = Lazy::new(move || sema.parse(file_id).syntax().clone());
+                let tree = LazyCell::new(move || sema.parse(file_id).syntax().clone());
 
-                for offset in match_indices(&text, finder, search_range) {
-                    for name_ref in
-                        find_nodes("super", &tree, offset).filter_map(ast::NameRef::cast)
+                for offset in Self::match_indices(&text, finder, search_range) {
+                    for name_ref in Self::find_nodes(sema, "super", &tree, offset)
+                        .filter_map(ast::NameRef::cast)
                     {
                         if self.found_name_ref(&name_ref, sink) {
                             return;
@@ -601,9 +972,9 @@ impl<'a> FindUsages<'a> {
                     }
                 }
                 if let Some(finder) = &is_crate_root {
-                    for offset in match_indices(&text, finder, search_range) {
-                        for name_ref in
-                            find_nodes("crate", &tree, offset).filter_map(ast::NameRef::cast)
+                    for offset in Self::match_indices(&text, finder, search_range) {
+                        for name_ref in Self::find_nodes(sema, "crate", &tree, offset)
+                            .filter_map(ast::NameRef::cast)
                         {
                             if self.found_name_ref(&name_ref, sink) {
                                 return;
@@ -641,11 +1012,12 @@ impl<'a> FindUsages<'a> {
                 let search_range =
                     search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&*text)));
 
-                let tree = Lazy::new(|| sema.parse(file_id).syntax().clone());
+                let tree = LazyCell::new(|| sema.parse(file_id).syntax().clone());
                 let finder = &Finder::new("self");
 
-                for offset in match_indices(&text, finder, search_range) {
-                    for name_ref in find_nodes("self", &tree, offset).filter_map(ast::NameRef::cast)
+                for offset in Self::match_indices(&text, finder, search_range) {
+                    for name_ref in
+                        Self::find_nodes(sema, "self", &tree, offset).filter_map(ast::NameRef::cast)
                     {
                         if self.found_self_module_name_ref(&name_ref, sink) {
                             return;
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs
index 8ab5a6ede3bd5..c104aa571894d 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs
@@ -31,6 +31,7 @@ pub fn with_placeholders(args: Vec<Arg>) -> Vec<String> {
         .collect()
 }
 
+// FIXME Remove this, we have this information in the HIR now
 /// Parser for a format-like string. It is more allowing in terms of string contents,
 /// as we expect variable placeholders to be filled with expressions.
 ///
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs
index 97b6d4a572a21..dd4a665e8eb4f 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs
@@ -131,5 +131,6 @@ pub fn insert_ws_into(syn: SyntaxNode) -> SyntaxNode {
 }
 
 fn is_text(k: SyntaxKind) -> bool {
-    k.is_keyword() || k.is_literal() || k == IDENT || k == UNDERSCORE
+    // Consider all keywords in all editions.
+    k.is_any_identifier() || k.is_literal() || k == UNDERSCORE
 }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs
index 37238cc61d3b7..91e0b4495f5f1 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs
@@ -1,6 +1,7 @@
 //! Various helper functions to work with SyntaxNodes.
 use itertools::Itertools;
 use parser::T;
+use span::Edition;
 use syntax::{
     ast::{self, HasLoopBody, MacroCall, PathSegmentKind, VisibilityKind},
     AstNode, AstToken, Preorder, RustLanguage, WalkEvent,
@@ -456,12 +457,15 @@ impl Iterator for TreeWithDepthIterator {
 }
 
 /// Parses the input token tree as comma separated plain paths.
-pub fn parse_tt_as_comma_sep_paths(input: ast::TokenTree) -> Option<Vec<ast::Path>> {
+pub fn parse_tt_as_comma_sep_paths(
+    input: ast::TokenTree,
+    edition: Edition,
+) -> Option<Vec<ast::Path>> {
     let r_paren = input.r_paren_token();
     let tokens =
         input.syntax().children_with_tokens().skip(1).map_while(|it| match it.into_token() {
             // seeing a keyword means the attribute is unclosed so stop parsing here
-            Some(tok) if tok.kind().is_keyword() => None,
+            Some(tok) if tok.kind().is_keyword(edition) => None,
             // don't include the right token tree parenthesis if it exists
             tok @ Some(_) if tok == r_paren => None,
             // only nodes that we can find are other TokenTrees, those are unexpected in this parse though
@@ -473,10 +477,12 @@ pub fn parse_tt_as_comma_sep_paths(input: ast::TokenTree) -> Option<Vec<ast::Pat
         .into_iter()
         .filter_map(|(is_sep, group)| (!is_sep).then_some(group))
         .filter_map(|mut tokens| {
-            syntax::hacks::parse_expr_from_str(&tokens.join("")).and_then(|expr| match expr {
-                ast::Expr::PathExpr(it) => it.path(),
-                _ => None,
-            })
+            syntax::hacks::parse_expr_from_str(&tokens.join(""), Edition::CURRENT).and_then(
+                |expr| match expr {
+                    ast::Expr::PathExpr(it) => it.path(),
+                    _ => None,
+                },
+            )
         })
         .collect();
     Some(paths)
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/traits.rs b/src/tools/rust-analyzer/crates/ide-db/src/traits.rs
index 48a585bf333bc..82aca50d03998 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/traits.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/traits.rs
@@ -34,19 +34,20 @@ pub fn get_missing_assoc_items(
     // may share the same name as a function or constant.
     let mut impl_fns_consts = FxHashSet::default();
     let mut impl_type = FxHashSet::default();
+    let edition = imp.module(sema.db).krate().edition(sema.db);
 
     for item in imp.items(sema.db) {
         match item {
             hir::AssocItem::Function(it) => {
-                impl_fns_consts.insert(it.name(sema.db).display(sema.db).to_string());
+                impl_fns_consts.insert(it.name(sema.db).display(sema.db, edition).to_string());
             }
             hir::AssocItem::Const(it) => {
                 if let Some(name) = it.name(sema.db) {
-                    impl_fns_consts.insert(name.display(sema.db).to_string());
+                    impl_fns_consts.insert(name.display(sema.db, edition).to_string());
                 }
             }
             hir::AssocItem::TypeAlias(it) => {
-                impl_type.insert(it.name(sema.db).display(sema.db).to_string());
+                impl_type.insert(it.name(sema.db).display(sema.db, edition).to_string());
             }
         }
     }
@@ -56,15 +57,14 @@ pub fn get_missing_assoc_items(
             .items(sema.db)
             .into_iter()
             .filter(|i| match i {
-                hir::AssocItem::Function(f) => {
-                    !impl_fns_consts.contains(&f.name(sema.db).display(sema.db).to_string())
-                }
+                hir::AssocItem::Function(f) => !impl_fns_consts
+                    .contains(&f.name(sema.db).display(sema.db, edition).to_string()),
                 hir::AssocItem::TypeAlias(t) => {
-                    !impl_type.contains(&t.name(sema.db).display(sema.db).to_string())
+                    !impl_type.contains(&t.name(sema.db).display(sema.db, edition).to_string())
                 }
                 hir::AssocItem::Const(c) => c
                     .name(sema.db)
-                    .map(|n| !impl_fns_consts.contains(&n.display(sema.db).to_string()))
+                    .map(|n| !impl_fns_consts.contains(&n.display(sema.db, edition).to_string()))
                     .unwrap_or_default(),
             })
             .collect()
@@ -116,6 +116,7 @@ mod tests {
     use expect_test::{expect, Expect};
     use hir::FilePosition;
     use hir::Semantics;
+    use span::Edition;
     use syntax::ast::{self, AstNode};
     use test_fixture::ChangeFixture;
 
@@ -140,7 +141,7 @@ mod tests {
             sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
         let trait_ = crate::traits::resolve_target_trait(&sema, &impl_block);
         let actual = match trait_ {
-            Some(trait_) => trait_.name(&db).display(&db).to_string(),
+            Some(trait_) => trait_.name(&db).display(&db, Edition::CURRENT).to_string(),
             None => String::new(),
         };
         expect.assert_eq(&actual);
@@ -155,7 +156,7 @@ mod tests {
         let items = crate::traits::get_missing_assoc_items(&sema, &impl_block);
         let actual = items
             .into_iter()
-            .map(|item| item.name(&db).unwrap().display(&db).to_string())
+            .map(|item| item.name(&db).unwrap().display(&db, Edition::CURRENT).to_string())
             .collect::<Vec<_>>()
             .join("\n");
         expect.assert_eq(&actual);
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs b/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs
index 5b566c5067d85..515bc418cb467 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs
@@ -5,10 +5,7 @@
 use std::iter;
 
 use hir::Semantics;
-use syntax::{
-    ast::{self, make, Pat},
-    ToSmolStr,
-};
+use syntax::ast::{self, make, Pat};
 
 use crate::RootDatabase;
 
@@ -29,7 +26,7 @@ impl TryEnum {
             _ => return None,
         };
         TryEnum::ALL.iter().find_map(|&var| {
-            if enum_.name(sema.db).display_no_db().to_smolstr() == var.type_name() {
+            if enum_.name(sema.db).eq_ident(var.type_name()) {
                 return Some(var);
             }
             None
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs b/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs
index 965f432407b0e..c3f0bf3706904 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs
@@ -1,6 +1,7 @@
 //! Functionality for generating trivial constructors
 
 use hir::StructKind;
+use span::Edition;
 use syntax::{
     ast::{make, Expr, Path},
     ToSmolStr,
@@ -11,6 +12,7 @@ pub fn use_trivial_constructor(
     db: &crate::RootDatabase,
     path: Path,
     ty: &hir::Type,
+    edition: Edition,
 ) -> Option<Expr> {
     match ty.as_adt() {
         Some(hir::Adt::Enum(x)) => {
@@ -19,7 +21,7 @@ pub fn use_trivial_constructor(
                     let path = make::path_qualified(
                         path,
                         make::path_segment(make::name_ref(
-                            &variant.name(db).display_no_db().to_smolstr(),
+                            &variant.name(db).display_no_db(edition).to_smolstr(),
                         )),
                     );
 
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml
index 9c3a279a945dd..bf54f4ab3224f 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml
@@ -18,7 +18,6 @@ either.workspace = true
 itertools.workspace = true
 serde_json.workspace = true
 tracing.workspace = true
-once_cell = "1.17.0"
 
 # local deps
 stdx.workspace = true
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs
index 05fb1c29b3137..02299197b1258 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs
@@ -12,7 +12,7 @@ pub(crate) fn expected_function(
     Diagnostic::new_with_syntax_node_ptr(
         ctx,
         DiagnosticCode::RustcHardError("E0618"),
-        format!("expected function, found {}", d.found.display(ctx.sema.db)),
+        format!("expected function, found {}", d.found.display(ctx.sema.db, ctx.edition)),
         d.call.map(|it| it.into()),
     )
     .experimental()
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs
index 117088ca09c8a..ccb33fed10044 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs
@@ -12,7 +12,7 @@ use itertools::Itertools;
 use stdx::{format_to, never};
 use syntax::{
     ast::{self, make},
-    SyntaxKind, SyntaxNode,
+    Edition, SyntaxKind, SyntaxNode,
 };
 use text_edit::TextEdit;
 
@@ -104,6 +104,7 @@ pub(crate) fn json_in_items(
     file_id: EditionedFileId,
     node: &SyntaxNode,
     config: &DiagnosticsConfig,
+    edition: Edition,
 ) {
     (|| {
         if node.kind() == SyntaxKind::ERROR
@@ -156,7 +157,11 @@ pub(crate) fn json_in_items(
                                     config.insert_use.prefix_kind,
                                     cfg,
                                 ) {
-                                    insert_use(&scope, mod_path_to_ast(&it), &config.insert_use);
+                                    insert_use(
+                                        &scope,
+                                        mod_path_to_ast(&it, edition),
+                                        &config.insert_use,
+                                    );
                                 }
                             }
                         }
@@ -168,7 +173,11 @@ pub(crate) fn json_in_items(
                                     config.insert_use.prefix_kind,
                                     cfg,
                                 ) {
-                                    insert_use(&scope, mod_path_to_ast(&it), &config.insert_use);
+                                    insert_use(
+                                        &scope,
+                                        mod_path_to_ast(&it, edition),
+                                        &config.insert_use,
+                                    );
                                 }
                             }
                         }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
index ea7908525ae12..86c237f7b5ec3 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
@@ -11,7 +11,7 @@ use stdx::format_to;
 use syntax::{
     algo,
     ast::{self, make},
-    AstNode, SyntaxNode, SyntaxNodePtr, ToSmolStr,
+    AstNode, Edition, SyntaxNode, SyntaxNodePtr, ToSmolStr,
 };
 use text_edit::TextEdit;
 
@@ -31,7 +31,7 @@ use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext};
 pub(crate) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Diagnostic {
     let mut message = String::from("missing structure fields:\n");
     for field in &d.missed_fields {
-        format_to!(message, "- {}\n", field.display(ctx.sema.db));
+        format_to!(message, "- {}\n", field.display(ctx.sema.db, ctx.edition));
     }
 
     let ptr = InFile::new(
@@ -134,8 +134,9 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
 
                         use_trivial_constructor(
                             ctx.sema.db,
-                            ide_db::helpers::mod_path_to_ast(&type_path),
+                            ide_db::helpers::mod_path_to_ast(&type_path, ctx.edition),
                             ty,
+                            ctx.edition,
                         )
                     })();
 
@@ -146,7 +147,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
                     }
                 };
                 let field = make::record_expr_field(
-                    make::name_ref(&f.name(ctx.sema.db).display_no_db().to_smolstr()),
+                    make::name_ref(&f.name(ctx.sema.db).display_no_db(ctx.edition).to_smolstr()),
                     field_expr,
                 );
                 new_field_list.add_field(field.clone_for_update());
@@ -160,7 +161,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
             let new_field_list = old_field_list.clone_for_update();
             for (f, _) in missing_fields.iter() {
                 let field = make::record_pat_field_shorthand(make::name_ref(
-                    &f.name(ctx.sema.db).display_no_db().to_smolstr(),
+                    &f.name(ctx.sema.db).display_no_db(ctx.edition).to_smolstr(),
                 ));
                 new_field_list.add_field(field.clone_for_update());
             }
@@ -169,9 +170,14 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
     }
 }
 
-fn make_ty(ty: &hir::Type, db: &dyn HirDatabase, module: hir::Module) -> ast::Type {
+fn make_ty(
+    ty: &hir::Type,
+    db: &dyn HirDatabase,
+    module: hir::Module,
+    edition: Edition,
+) -> ast::Type {
     let ty_str = match ty.as_adt() {
-        Some(adt) => adt.name(db).display(db.upcast()).to_string(),
+        Some(adt) => adt.name(db).display(db.upcast(), edition).to_string(),
         None => {
             ty.display_source_code(db, module.into(), false).ok().unwrap_or_else(|| "_".to_owned())
         }
@@ -223,13 +229,13 @@ fn get_default_constructor(
 
     let famous_defs = FamousDefs(&ctx.sema, krate);
     if has_new_func {
-        Some(make::ext::expr_ty_new(&make_ty(ty, ctx.sema.db, module)))
+        Some(make::ext::expr_ty_new(&make_ty(ty, ctx.sema.db, module, ctx.edition)))
     } else if ty.as_adt() == famous_defs.core_option_Option()?.ty(ctx.sema.db).as_adt() {
         Some(make::ext::option_none())
     } else if !ty.is_array()
         && ty.impls_trait(ctx.sema.db, famous_defs.core_default_Default()?, &[])
     {
-        Some(make::ext::expr_ty_default(&make_ty(ty, ctx.sema.db, module)))
+        Some(make::ext::expr_ty_default(&make_ty(ty, ctx.sema.db, module, ctx.edition)))
     } else {
         None
     }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
index fa9a6577fcf35..06c6b0f3e4c38 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
@@ -8,7 +8,7 @@ pub(crate) fn moved_out_of_ref(ctx: &DiagnosticsContext<'_>, d: &hir::MovedOutOf
     Diagnostic::new_with_syntax_node_ptr(
         ctx,
         DiagnosticCode::RustcHardError("E0507"),
-        format!("cannot move `{}` out of reference", d.ty.display(ctx.sema.db)),
+        format!("cannot move `{}` out of reference", d.ty.display(ctx.sema.db, ctx.edition)),
         d.span,
     )
     .experimental() // spans are broken, and I'm not sure how precise we can detect copy types
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
index 00352266ddbdc..e4b1f3ca95998 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
@@ -40,7 +40,7 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Option
             DiagnosticCode::RustcHardError("E0384"),
             format!(
                 "cannot mutate immutable variable `{}`",
-                d.local.name(ctx.sema.db).display(ctx.sema.db)
+                d.local.name(ctx.sema.db).display(ctx.sema.db, ctx.edition)
             ),
             d.span,
         )
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
index f6ed0d7226a79..fe32c590492d1 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
@@ -12,7 +12,7 @@ pub(crate) fn private_assoc_item(
     let name = d
         .item
         .name(ctx.sema.db)
-        .map(|name| format!("`{}` ", name.display(ctx.sema.db)))
+        .map(|name| format!("`{}` ", name.display(ctx.sema.db, ctx.edition)))
         .unwrap_or_default();
     Diagnostic::new_with_syntax_node_ptr(
         ctx,
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs
index e91e64c81b0b2..237a9b87871c9 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs
@@ -10,8 +10,8 @@ pub(crate) fn private_field(ctx: &DiagnosticsContext<'_>, d: &hir::PrivateField)
         DiagnosticCode::RustcHardError("E0616"),
         format!(
             "field `{}` of `{}` is private",
-            d.field.name(ctx.sema.db).display(ctx.sema.db),
-            d.field.parent_def(ctx.sema.db).name(ctx.sema.db).display(ctx.sema.db)
+            d.field.name(ctx.sema.db).display(ctx.sema.db, ctx.edition),
+            d.field.parent_def(ctx.sema.db).name(ctx.sema.db).display(ctx.sema.db, ctx.edition)
         ),
         d.expr.map(|it| it.into()),
     )
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs
index 58d1b7f31d2fe..a35b67ce98781 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs
@@ -17,7 +17,7 @@ pub(crate) fn trait_impl_missing_assoc_item(
             hir::AssocItem::Const(_) => "`const ",
             hir::AssocItem::TypeAlias(_) => "`type ",
         })?;
-        f(&name.display(ctx.sema.db))?;
+        f(&name.display(ctx.sema.db, ctx.edition))?;
         f(&"`")
     });
     Diagnostic::new(
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
index 6d756484ebc0f..3de51ca4a30a9 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
@@ -18,11 +18,11 @@ pub(crate) fn trait_impl_redundant_assoc_item(
 ) -> Diagnostic {
     let db = ctx.sema.db;
     let name = d.assoc_item.0.clone();
-    let redundant_assoc_item_name = name.display(db);
+    let redundant_assoc_item_name = name.display(db, ctx.edition);
     let assoc_item = d.assoc_item.1;
 
     let default_range = d.impl_.syntax_node_ptr().text_range();
-    let trait_name = d.trait_.name(db).display_no_db().to_smolstr();
+    let trait_name = d.trait_.name(db).display_no_db(ctx.edition).to_smolstr();
 
     let (redundant_item_name, diagnostic_range, redundant_item_def) = match assoc_item {
         hir::AssocItem::Function(id) => {
@@ -30,7 +30,7 @@ pub(crate) fn trait_impl_redundant_assoc_item(
             (
                 format!("`fn {redundant_assoc_item_name}`"),
                 function.source(db).map(|it| it.syntax().text_range()).unwrap_or(default_range),
-                format!("\n    {};", function.display(db)),
+                format!("\n    {};", function.display(db, ctx.edition)),
             )
         }
         hir::AssocItem::Const(id) => {
@@ -38,7 +38,7 @@ pub(crate) fn trait_impl_redundant_assoc_item(
             (
                 format!("`const {redundant_assoc_item_name}`"),
                 constant.source(db).map(|it| it.syntax().text_range()).unwrap_or(default_range),
-                format!("\n    {};", constant.display(db)),
+                format!("\n    {};", constant.display(db, ctx.edition)),
             )
         }
         hir::AssocItem::TypeAlias(id) => {
@@ -48,7 +48,7 @@ pub(crate) fn trait_impl_redundant_assoc_item(
                 type_alias.source(db).map(|it| it.syntax().text_range()).unwrap_or(default_range),
                 format!(
                     "\n    type {};",
-                    type_alias.name(ctx.sema.db).display_no_db().to_smolstr()
+                    type_alias.name(ctx.sema.db).display_no_db(ctx.edition).to_smolstr()
                 ),
             )
         }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index 6f5c68d4b5c86..5cce7c4aed565 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -40,8 +40,12 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch)
         DiagnosticCode::RustcHardError("E0308"),
         format!(
             "expected {}, found {}",
-            d.expected.display(ctx.sema.db).with_closure_style(ClosureStyle::ClosureWithId),
-            d.actual.display(ctx.sema.db).with_closure_style(ClosureStyle::ClosureWithId),
+            d.expected
+                .display(ctx.sema.db, ctx.edition)
+                .with_closure_style(ClosureStyle::ClosureWithId),
+            d.actual
+                .display(ctx.sema.db, ctx.edition)
+                .with_closure_style(ClosureStyle::ClosureWithId),
         ),
         display_range,
     )
@@ -199,8 +203,8 @@ fn str_ref_to_owned(
     expr_ptr: &InFile<AstPtr<ast::Expr>>,
     acc: &mut Vec<Assist>,
 ) -> Option<()> {
-    let expected = d.expected.display(ctx.sema.db);
-    let actual = d.actual.display(ctx.sema.db);
+    let expected = d.expected.display(ctx.sema.db, ctx.edition);
+    let actual = d.actual.display(ctx.sema.db, ctx.edition);
 
     // FIXME do this properly
     if expected.to_string() != "String" || actual.to_string() != "&str" {
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs
index b4a566e318810..b5c242e1e9f82 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs
@@ -26,7 +26,9 @@ pub(crate) fn typed_hole(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Di
         (
             format!(
                 "invalid `_` expression, expected type `{}`",
-                d.expected.display(ctx.sema.db).with_closure_style(ClosureStyle::ClosureWithId),
+                d.expected
+                    .display(ctx.sema.db, ctx.edition)
+                    .with_closure_style(ClosureStyle::ClosureWithId),
             ),
             fixes(ctx, d),
         )
@@ -69,6 +71,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option<Vec<Assist>
                     prefer_prelude: ctx.config.prefer_prelude,
                     prefer_absolute: ctx.config.prefer_absolute,
                 },
+                ctx.edition,
             )
             .ok()
         })
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs
index 97943b7e8b347..6af36fb9e7398 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs
@@ -9,7 +9,7 @@ pub(crate) fn undeclared_label(
     Diagnostic::new_with_syntax_node_ptr(
         ctx,
         DiagnosticCode::RustcHardError("undeclared-label"),
-        format!("use of undeclared label `{}`", name.display(ctx.sema.db)),
+        format!("use of undeclared label `{}`", name.display(ctx.sema.db, ctx.edition)),
         d.node.map(|it| it.into()),
     )
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs
index a1573bab8ae9b..e0822fc5b3385 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs
@@ -11,7 +11,7 @@ use ide_db::{
 use paths::Utf8Component;
 use syntax::{
     ast::{self, edit::IndentLevel, HasModuleItem, HasName},
-    AstNode, TextRange, ToSmolStr,
+    AstNode, TextRange,
 };
 use text_edit::TextEdit;
 
@@ -112,8 +112,7 @@ fn fixes(
                 // shouldn't occur
                 _ => continue 'crates,
             };
-            match current.children.iter().find(|(name, _)| name.display_no_db().to_smolstr() == seg)
-            {
+            match current.children.iter().find(|(name, _)| name.eq_ident(seg)) {
                 Some((_, &child)) => current = &crate_def_map[child],
                 None => continue 'crates,
             }
@@ -162,11 +161,7 @@ fn fixes(
             // try finding a parent that has an inline tree from here on
             let mut current = module;
             for s in stack.iter().rev() {
-                match module
-                    .children
-                    .iter()
-                    .find(|(name, _)| name.display_no_db().to_smolstr() == s)
-                {
+                match module.children.iter().find(|(name, _)| name.eq_ident(s)) {
                     Some((_, child)) => {
                         current = &crate_def_map[*child];
                     }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs
index 3601041fc735b..bdff2417ca114 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs
@@ -9,7 +9,7 @@ pub(crate) fn unreachable_label(
     Diagnostic::new_with_syntax_node_ptr(
         ctx,
         DiagnosticCode::RustcHardError("E0767"),
-        format!("use of unreachable label `{}`", name.display(ctx.sema.db)),
+        format!("use of unreachable label `{}`", name.display(ctx.sema.db, ctx.edition)),
         d.node.map(|it| it.into()),
     )
 }
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 eb8eea69f67f6..76d624c47abfd 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
@@ -36,8 +36,8 @@ pub(crate) fn unresolved_field(
         DiagnosticCode::RustcHardError("E0559"),
         format!(
             "no field `{}` on type `{}`{method_suffix}",
-            d.name.display(ctx.sema.db),
-            d.receiver.display(ctx.sema.db)
+            d.name.display(ctx.sema.db, ctx.edition),
+            d.receiver.display(ctx.sema.db, ctx.edition)
         ),
         adjusted_display_range(ctx, d.expr, &|expr| {
             Some(
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
index c8ff54cba3a74..5b596123e75fd 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
@@ -13,7 +13,7 @@ pub(crate) fn unresolved_macro_call(
     let bang = if d.is_bang { "!" } else { "" };
     Diagnostic::new(
         DiagnosticCode::RustcHardError("unresolved-macro-call"),
-        format!("unresolved macro `{}{bang}`", d.path.display(ctx.sema.db)),
+        format!("unresolved macro `{}{bang}`", d.path.display(ctx.sema.db, ctx.edition)),
         display_range,
     )
     .experimental()
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs
index 387d56b890b94..c0d038a238ba8 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs
@@ -30,8 +30,8 @@ pub(crate) fn unresolved_method(
         DiagnosticCode::RustcHardError("E0599"),
         format!(
             "no method `{}` on type `{}`{suffix}",
-            d.name.display(ctx.sema.db),
-            d.receiver.display(ctx.sema.db)
+            d.name.display(ctx.sema.db, ctx.edition),
+            d.receiver.display(ctx.sema.db, ctx.edition)
         ),
         adjusted_display_range(ctx, d.expr, &|expr| {
             Some(
@@ -154,9 +154,10 @@ fn assoc_func_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -
         };
 
         let mut receiver_type_adt_name =
-            receiver_type.as_adt()?.name(db).display_no_db().to_smolstr();
+            receiver_type.as_adt()?.name(db).display_no_db(ctx.edition).to_smolstr();
 
-        let generic_parameters: Vec<SmolStr> = receiver_type.generic_parameters(db).collect();
+        let generic_parameters: Vec<SmolStr> =
+            receiver_type.generic_parameters(db, ctx.edition).collect();
         // if receiver should be pass as first arg in the assoc func,
         // we could omit generic parameters cause compiler can deduce it automatically
         if !need_to_take_receiver_as_first_arg && !generic_parameters.is_empty() {
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs
index bf19331d9fdd5..84007b16aa67c 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs
@@ -5,7 +5,7 @@ use ide_db::{
     source_change::SourceChange,
     FileRange, RootDatabase,
 };
-use syntax::TextRange;
+use syntax::{Edition, TextRange};
 use text_edit::TextEdit;
 
 use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
@@ -23,7 +23,7 @@ pub(crate) fn unused_variables(
         return None;
     }
     let diagnostic_range = ctx.sema.diagnostics_display_range(ast);
-    // The range for the Actual Name. We don't want to replace the entire declarition. Using the diagnostic range causes issues within in Array Destructuring.
+    // The range for the Actual Name. We don't want to replace the entire declaration. Using the diagnostic range causes issues within in Array Destructuring.
     let name_range = d
         .local
         .primary_source(ctx.sema.db)
@@ -42,7 +42,14 @@ pub(crate) fn unused_variables(
             ast,
         )
         .with_fixes(name_range.and_then(|it| {
-            fixes(ctx.sema.db, var_name, it.range, diagnostic_range.into(), ast.file_id.is_macro())
+            fixes(
+                ctx.sema.db,
+                var_name,
+                it.range,
+                diagnostic_range.into(),
+                ast.file_id.is_macro(),
+                ctx.edition,
+            )
         }))
         .experimental(),
     )
@@ -54,6 +61,7 @@ fn fixes(
     name_range: TextRange,
     diagnostic_range: FileRange,
     is_in_marco: bool,
+    edition: Edition,
 ) -> Option<Vec<Assist>> {
     if is_in_marco {
         return None;
@@ -63,14 +71,14 @@ fn fixes(
         id: AssistId("unscore_unused_variable_name", AssistKind::QuickFix),
         label: Label::new(format!(
             "Rename unused {} to _{}",
-            var_name.display(db),
-            var_name.display(db)
+            var_name.display(db, edition),
+            var_name.display(db, edition)
         )),
         group: None,
         target: diagnostic_range.range,
         source_change: Some(SourceChange::from_text_edit(
             diagnostic_range.file_id,
-            TextEdit::replace(name_range, format!("_{}", var_name.display(db))),
+            TextEdit::replace(name_range, format!("_{}", var_name.display(db, edition))),
         )),
         command: None,
     }])
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
index a61c5f0cd4db2..9b50a435e4c3a 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
@@ -75,6 +75,8 @@ mod handlers {
 #[cfg(test)]
 mod tests;
 
+use std::sync::LazyLock;
+
 use hir::{diagnostics::AnyDiagnostic, InFile, Semantics};
 use ide_db::{
     assists::{Assist, AssistId, AssistKind, AssistResolveStrategy},
@@ -86,11 +88,10 @@ use ide_db::{
     syntax_helpers::node_ext::parse_tt_as_comma_sep_paths,
     EditionedFileId, FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, SnippetCap,
 };
-use once_cell::sync::Lazy;
 use stdx::never;
 use syntax::{
     ast::{self, AstNode},
-    AstPtr, SyntaxNode, SyntaxNodePtr, TextRange,
+    AstPtr, Edition, SyntaxNode, SyntaxNodePtr, TextRange,
 };
 
 // FIXME: Make this an enum
@@ -279,6 +280,7 @@ struct DiagnosticsContext<'a> {
     config: &'a DiagnosticsConfig,
     sema: Semantics<'a, RootDatabase>,
     resolve: &'a AssistResolveStrategy,
+    edition: Edition,
 }
 
 impl DiagnosticsContext<'_> {
@@ -359,12 +361,19 @@ pub fn semantic_diagnostics(
     for node in parse.syntax().descendants() {
         handlers::useless_braces::useless_braces(&mut res, file_id, &node);
         handlers::field_shorthand::field_shorthand(&mut res, file_id, &node);
-        handlers::json_is_not_rust::json_in_items(&sema, &mut res, file_id, &node, config);
+        handlers::json_is_not_rust::json_in_items(
+            &sema,
+            &mut res,
+            file_id,
+            &node,
+            config,
+            file_id.edition(),
+        );
     }
 
     let module = sema.file_to_module_def(file_id);
 
-    let ctx = DiagnosticsContext { config, sema, resolve };
+    let ctx = DiagnosticsContext { config, sema, resolve, edition: file_id.edition() };
 
     let mut diags = Vec::new();
     match module {
@@ -490,6 +499,7 @@ pub fn semantic_diagnostics(
         &mut rustc_stack,
         &mut clippy_stack,
         &mut diagnostics_of_range,
+        ctx.edition,
     );
 
     res.retain(|d| d.severity != Severity::Allow);
@@ -512,11 +522,11 @@ pub fn full_diagnostics(
 
 // `__RA_EVERY_LINT` is a fake lint group to allow every lint in proc macros
 
-static RUSTC_LINT_GROUPS_DICT: Lazy<FxHashMap<&str, Vec<&str>>> =
-    Lazy::new(|| build_group_dict(DEFAULT_LINT_GROUPS, &["warnings", "__RA_EVERY_LINT"], ""));
+static RUSTC_LINT_GROUPS_DICT: LazyLock<FxHashMap<&str, Vec<&str>>> =
+    LazyLock::new(|| build_group_dict(DEFAULT_LINT_GROUPS, &["warnings", "__RA_EVERY_LINT"], ""));
 
-static CLIPPY_LINT_GROUPS_DICT: Lazy<FxHashMap<&str, Vec<&str>>> =
-    Lazy::new(|| build_group_dict(CLIPPY_LINT_GROUPS, &["__RA_EVERY_LINT"], "clippy::"));
+static CLIPPY_LINT_GROUPS_DICT: LazyLock<FxHashMap<&str, Vec<&str>>> =
+    LazyLock::new(|| build_group_dict(CLIPPY_LINT_GROUPS, &["__RA_EVERY_LINT"], "clippy::"));
 
 fn build_group_dict(
     lint_group: &'static [LintGroup],
@@ -544,6 +554,7 @@ fn handle_lint_attributes(
     rustc_stack: &mut FxHashMap<String, Vec<Severity>>,
     clippy_stack: &mut FxHashMap<String, Vec<Severity>>,
     diagnostics_of_range: &mut FxHashMap<InFile<SyntaxNode>, &mut Diagnostic>,
+    edition: Edition,
 ) {
     let _g = tracing::info_span!("handle_lint_attributes").entered();
     let file_id = sema.hir_file_for(root);
@@ -552,9 +563,15 @@ fn handle_lint_attributes(
         match ev {
             syntax::WalkEvent::Enter(node) => {
                 for attr in node.children().filter_map(ast::Attr::cast) {
-                    parse_lint_attribute(attr, rustc_stack, clippy_stack, |stack, severity| {
-                        stack.push(severity);
-                    });
+                    parse_lint_attribute(
+                        attr,
+                        rustc_stack,
+                        clippy_stack,
+                        |stack, severity| {
+                            stack.push(severity);
+                        },
+                        edition,
+                    );
                 }
                 if let Some(it) =
                     diagnostics_of_range.get_mut(&InFile { file_id, value: node.clone() })
@@ -591,6 +608,7 @@ fn handle_lint_attributes(
                             rustc_stack,
                             clippy_stack,
                             diagnostics_of_range,
+                            edition,
                         );
                         for stack in [&mut *rustc_stack, &mut *clippy_stack] {
                             stack.entry("__RA_EVERY_LINT".to_owned()).or_default().pop();
@@ -605,17 +623,24 @@ fn handle_lint_attributes(
                             rustc_stack,
                             clippy_stack,
                             diagnostics_of_range,
+                            edition,
                         );
                     }
                 }
             }
             syntax::WalkEvent::Leave(node) => {
                 for attr in node.children().filter_map(ast::Attr::cast) {
-                    parse_lint_attribute(attr, rustc_stack, clippy_stack, |stack, severity| {
-                        if stack.pop() != Some(severity) {
-                            never!("Mismatched serevity in walking lint attributes");
-                        }
-                    });
+                    parse_lint_attribute(
+                        attr,
+                        rustc_stack,
+                        clippy_stack,
+                        |stack, severity| {
+                            if stack.pop() != Some(severity) {
+                                never!("Mismatched serevity in walking lint attributes");
+                            }
+                        },
+                        edition,
+                    );
                 }
             }
         }
@@ -627,6 +652,7 @@ fn parse_lint_attribute(
     rustc_stack: &mut FxHashMap<String, Vec<Severity>>,
     clippy_stack: &mut FxHashMap<String, Vec<Severity>>,
     job: impl Fn(&mut Vec<Severity>, Severity),
+    edition: Edition,
 ) {
     let Some((tag, args_tt)) = attr.as_simple_call() else {
         return;
@@ -637,7 +663,7 @@ fn parse_lint_attribute(
         "forbid" | "deny" => Severity::Error,
         _ => return,
     };
-    for lint in parse_tt_as_comma_sep_paths(args_tt).into_iter().flatten() {
+    for lint in parse_tt_as_comma_sep_paths(args_tt, edition).into_iter().flatten() {
         if let Some(lint) = lint.as_single_name_ref() {
             job(rustc_stack.entry(lint.to_string()).or_default(), severity);
         }
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs
index 5f6d77c064ca3..6569f0f5552f9 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs
+++ b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs
@@ -8,6 +8,7 @@ use crate::{
 };
 use hir::{FileRange, ImportPathConfig, Semantics};
 use ide_db::FxHashMap;
+use parser::Edition;
 use std::{cell::Cell, iter::Peekable};
 use syntax::{
     ast::{self, AstNode, AstToken, HasGenericArgs},
@@ -626,6 +627,11 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
                 match_error!("Failed to get receiver type for `{}`", expr.syntax().text())
             })?
             .original;
+        let edition = self
+            .sema
+            .scope(expr.syntax())
+            .map(|it| it.krate().edition(self.sema.db))
+            .unwrap_or(Edition::CURRENT);
         // Temporary needed to make the borrow checker happy.
         let res = code_type
             .autoderef(self.sema.db)
@@ -635,8 +641,8 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
             .ok_or_else(|| {
                 match_error!(
                     "Pattern type `{}` didn't match code type `{}`",
-                    pattern_type.display(self.sema.db),
-                    code_type.display(self.sema.db)
+                    pattern_type.display(self.sema.db, edition),
+                    code_type.display(self.sema.db, edition)
                 )
             });
         res
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs
index b4b83f62da240..65756601f66a7 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs
+++ b/src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs
@@ -2,6 +2,7 @@
 
 use ide_db::{FxHashMap, FxHashSet};
 use itertools::Itertools;
+use parser::Edition;
 use syntax::{
     ast::{self, AstNode, AstToken},
     SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize,
@@ -33,7 +34,7 @@ fn matches_to_edit_at_offset(
     for m in &matches.matches {
         edit_builder.replace(
             m.range.range.checked_sub(relative_start).unwrap(),
-            render_replace(db, m, file_src, rules),
+            render_replace(db, m, file_src, rules, m.range.file_id.edition()),
         );
     }
     edit_builder.finish()
@@ -54,6 +55,7 @@ struct ReplacementRenderer<'a> {
     // is parsed, placeholders don't get split. e.g. if a template of `$a.to_string()` results in `1
     // + 2.to_string()` then the placeholder value `1 + 2` was split and needs parenthesis.
     placeholder_tokens_requiring_parenthesis: FxHashSet<SyntaxToken>,
+    edition: Edition,
 }
 
 fn render_replace(
@@ -61,6 +63,7 @@ fn render_replace(
     match_info: &Match,
     file_src: &str,
     rules: &[ResolvedRule],
+    edition: Edition,
 ) -> String {
     let rule = &rules[match_info.rule_index];
     let template = rule
@@ -76,6 +79,7 @@ fn render_replace(
         out: String::new(),
         placeholder_tokens_requiring_parenthesis: FxHashSet::default(),
         placeholder_tokens_by_range: FxHashMap::default(),
+        edition,
     };
     renderer.render_node(&template.node);
     renderer.maybe_rerender_with_extra_parenthesis(&template.node);
@@ -105,7 +109,7 @@ impl ReplacementRenderer<'_> {
 
     fn render_node(&mut self, node: &SyntaxNode) {
         if let Some(mod_path) = self.match_info.rendered_template_paths.get(node) {
-            self.out.push_str(&mod_path.display(self.db).to_string());
+            self.out.push_str(&mod_path.display(self.db, self.edition).to_string());
             // Emit everything except for the segment's name-ref, since we already effectively
             // emitted that as part of `mod_path`.
             if let Some(path) = ast::Path::cast(node.clone()) {
diff --git a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
index 87093104852f3..155259a138040 100644
--- a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
@@ -2,7 +2,7 @@
 
 use std::iter;
 
-use hir::{DescendPreference, Semantics};
+use hir::Semantics;
 use ide_db::{
     defs::{Definition, NameClass, NameRefClass},
     helpers::pick_best_token,
@@ -86,7 +86,7 @@ pub(crate) fn outgoing_calls(
     })?;
     let mut calls = CallLocations::default();
 
-    sema.descend_into_macros(DescendPreference::None, token)
+    sema.descend_into_macros_exact(token)
         .into_iter()
         .filter_map(|it| it.parent_ancestors().nth(1).and_then(ast::Item::cast))
         .filter_map(|item| match item {
diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
index e9e5240897e17..925ae620231d2 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
@@ -10,10 +10,7 @@ use pulldown_cmark_to_cmark::{cmark_resume_with_options, Options as CMarkOptions
 use stdx::format_to;
 use url::Url;
 
-use hir::{
-    db::HirDatabase, sym, Adt, AsAssocItem, AssocItem, AssocItemContainer, DescendPreference,
-    HasAttrs,
-};
+use hir::{db::HirDatabase, sym, Adt, AsAssocItem, AssocItem, AssocItemContainer, HasAttrs};
 use ide_db::{
     base_db::{CrateOrigin, LangCrateOrigin, ReleaseChannel, SourceDatabase},
     defs::{Definition, NameClass, NameRefClass},
@@ -144,7 +141,7 @@ pub(crate) fn external_docs(
         kind if kind.is_trivia() => 0,
         _ => 1,
     })?;
-    let token = sema.descend_into_macros_single(DescendPreference::None, token);
+    let token = sema.descend_into_macros_single_exact(token);
 
     let node = token.parent()?;
     let definition = match_ast! {
@@ -289,7 +286,7 @@ impl DocCommentToken {
         let original_start = doc_token.text_range().start();
         let relative_comment_offset = offset - original_start - prefix_len;
 
-        sema.descend_into_macros(DescendPreference::None, doc_token).into_iter().find_map(|t| {
+        sema.descend_into_macros(doc_token).into_iter().find_map(|t| {
             let (node, descended_prefix_len) = match_ast! {
                 match t {
                     ast::Comment(comment) => (t.parent()?, TextSize::try_from(comment.prefix().len()).ok()?),
@@ -413,7 +410,8 @@ fn rewrite_url_link(db: &RootDatabase, def: Definition, target: &str) -> Option<
 fn mod_path_of_def(db: &RootDatabase, def: Definition) -> Option<String> {
     def.canonical_module_path(db).map(|it| {
         let mut path = String::new();
-        it.flat_map(|it| it.name(db)).for_each(|name| format_to!(path, "{}/", name.display(db)));
+        it.flat_map(|it| it.name(db))
+            .for_each(|name| format_to!(path, "{}/", name.unescaped().display(db)));
         path
     })
 }
@@ -588,9 +586,11 @@ fn filename_and_frag_for_def(
 
     let res = match def {
         Definition::Adt(adt) => match adt {
-            Adt::Struct(s) => format!("struct.{}.html", s.name(db).display(db.upcast())),
-            Adt::Enum(e) => format!("enum.{}.html", e.name(db).display(db.upcast())),
-            Adt::Union(u) => format!("union.{}.html", u.name(db).display(db.upcast())),
+            Adt::Struct(s) => {
+                format!("struct.{}.html", s.name(db).unescaped().display(db.upcast()))
+            }
+            Adt::Enum(e) => format!("enum.{}.html", e.name(db).unescaped().display(db.upcast())),
+            Adt::Union(u) => format!("union.{}.html", u.name(db).unescaped().display(db.upcast())),
         },
         Definition::Module(m) => match m.name(db) {
             // `#[doc(keyword = "...")]` is internal used only by rust compiler
@@ -599,34 +599,48 @@ fn filename_and_frag_for_def(
                     Some(kw) => {
                         format!("keyword.{}.html", kw)
                     }
-                    None => format!("{}/index.html", name.display(db.upcast())),
+                    None => format!("{}/index.html", name.unescaped().display(db.upcast())),
                 }
             }
             None => String::from("index.html"),
         },
-        Definition::Trait(t) => format!("trait.{}.html", t.name(db).display(db.upcast())),
-        Definition::TraitAlias(t) => format!("traitalias.{}.html", t.name(db).display(db.upcast())),
-        Definition::TypeAlias(t) => format!("type.{}.html", t.name(db).display(db.upcast())),
-        Definition::BuiltinType(t) => format!("primitive.{}.html", t.name().display(db.upcast())),
-        Definition::Function(f) => format!("fn.{}.html", f.name(db).display(db.upcast())),
+        Definition::Trait(t) => {
+            format!("trait.{}.html", t.name(db).unescaped().display(db.upcast()))
+        }
+        Definition::TraitAlias(t) => {
+            format!("traitalias.{}.html", t.name(db).unescaped().display(db.upcast()))
+        }
+        Definition::TypeAlias(t) => {
+            format!("type.{}.html", t.name(db).unescaped().display(db.upcast()))
+        }
+        Definition::BuiltinType(t) => {
+            format!("primitive.{}.html", t.name().unescaped().display(db.upcast()))
+        }
+        Definition::Function(f) => {
+            format!("fn.{}.html", f.name(db).unescaped().display(db.upcast()))
+        }
         Definition::Variant(ev) => {
             format!(
                 "enum.{}.html#variant.{}",
-                ev.parent_enum(db).name(db).display(db.upcast()),
-                ev.name(db).display(db.upcast())
+                ev.parent_enum(db).name(db).unescaped().display(db.upcast()),
+                ev.name(db).unescaped().display(db.upcast())
             )
         }
-        Definition::Const(c) => format!("const.{}.html", c.name(db)?.display(db.upcast())),
-        Definition::Static(s) => format!("static.{}.html", s.name(db).display(db.upcast())),
+        Definition::Const(c) => {
+            format!("const.{}.html", c.name(db)?.unescaped().display(db.upcast()))
+        }
+        Definition::Static(s) => {
+            format!("static.{}.html", s.name(db).unescaped().display(db.upcast()))
+        }
         Definition::Macro(mac) => match mac.kind(db) {
             hir::MacroKind::Declarative
             | hir::MacroKind::BuiltIn
             | hir::MacroKind::Attr
             | hir::MacroKind::ProcMacro => {
-                format!("macro.{}.html", mac.name(db).display(db.upcast()))
+                format!("macro.{}.html", mac.name(db).unescaped().display(db.upcast()))
             }
             hir::MacroKind::Derive => {
-                format!("derive.{}.html", mac.name(db).display(db.upcast()))
+                format!("derive.{}.html", mac.name(db).unescaped().display(db.upcast()))
             }
         },
         Definition::Field(field) => {
@@ -639,7 +653,7 @@ fn filename_and_frag_for_def(
             return Some((
                 def,
                 file,
-                Some(format!("structfield.{}", field.name(db).display(db.upcast()))),
+                Some(format!("structfield.{}", field.name(db).unescaped().display(db.upcast()))),
             ));
         }
         Definition::SelfType(impl_) => {
@@ -649,7 +663,7 @@ fn filename_and_frag_for_def(
             return Some((adt, file, Some(String::from("impl"))));
         }
         Definition::ExternCrateDecl(it) => {
-            format!("{}/index.html", it.name(db).display(db.upcast()))
+            format!("{}/index.html", it.name(db).unescaped().display(db.upcast()))
         }
         Definition::Local(_)
         | Definition::GenericParam(_)
@@ -679,14 +693,16 @@ fn get_assoc_item_fragment(db: &dyn HirDatabase, assoc_item: hir::AssocItem) ->
             // Rustdoc makes this decision based on whether a method 'has defaultness'.
             // Currently this is only the case for provided trait methods.
             if is_trait_method && !function.has_body(db) {
-                format!("tymethod.{}", function.name(db).display(db.upcast()))
+                format!("tymethod.{}", function.name(db).unescaped().display(db.upcast()))
             } else {
-                format!("method.{}", function.name(db).display(db.upcast()))
+                format!("method.{}", function.name(db).unescaped().display(db.upcast()))
             }
         }
         AssocItem::Const(constant) => {
-            format!("associatedconstant.{}", constant.name(db)?.display(db.upcast()))
+            format!("associatedconstant.{}", constant.name(db)?.unescaped().display(db.upcast()))
+        }
+        AssocItem::TypeAlias(ty) => {
+            format!("associatedtype.{}", ty.name(db).unescaped().display(db.upcast()))
         }
-        AssocItem::TypeAlias(ty) => format!("associatedtype.{}", ty.name(db).display(db.upcast())),
     })
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
index c8fe45c9cf0f8..a939ed214ad85 100644
--- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
@@ -1,8 +1,9 @@
-use hir::{DescendPreference, InFile, MacroFileIdExt, Semantics};
+use hir::{InFile, MacroFileIdExt, Semantics};
 use ide_db::{
     helpers::pick_best_token, syntax_helpers::insert_whitespace_into_node::insert_ws_into, FileId,
     RootDatabase,
 };
+use span::Edition;
 use syntax::{ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T};
 
 use crate::FilePosition;
@@ -40,37 +41,30 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
     // struct Bar;
     // ```
 
-    let derive = sema
-        .descend_into_macros(DescendPreference::None, tok.clone())
-        .into_iter()
-        .find_map(|descended| {
-            let macro_file = sema.hir_file_for(&descended.parent()?).macro_file()?;
-            if !macro_file.is_derive_attr_pseudo_expansion(db) {
-                return None;
-            }
+    let derive = sema.descend_into_macros_exact(tok.clone()).into_iter().find_map(|descended| {
+        let macro_file = sema.hir_file_for(&descended.parent()?).macro_file()?;
+        if !macro_file.is_derive_attr_pseudo_expansion(db) {
+            return None;
+        }
 
-            let name = descended.parent_ancestors().filter_map(ast::Path::cast).last()?.to_string();
-            // up map out of the #[derive] expansion
-            let InFile { file_id, value: tokens } =
-                hir::InMacroFile::new(macro_file, descended).upmap_once(db);
-            let token = sema.parse_or_expand(file_id).covering_element(tokens[0]).into_token()?;
-            let attr = token.parent_ancestors().find_map(ast::Attr::cast)?;
-            let expansions = sema.expand_derive_macro(&attr)?;
-            let idx = attr
-                .token_tree()?
-                .token_trees_and_tokens()
-                .filter_map(NodeOrToken::into_token)
-                .take_while(|it| it != &token)
-                .filter(|it| it.kind() == T![,])
-                .count();
-            let expansion = format(
-                db,
-                SyntaxKind::MACRO_ITEMS,
-                position.file_id,
-                expansions.get(idx).cloned()?,
-            );
-            Some(ExpandedMacro { name, expansion })
-        });
+        let name = descended.parent_ancestors().filter_map(ast::Path::cast).last()?.to_string();
+        // up map out of the #[derive] expansion
+        let InFile { file_id, value: tokens } =
+            hir::InMacroFile::new(macro_file, descended).upmap_once(db);
+        let token = sema.parse_or_expand(file_id).covering_element(tokens[0]).into_token()?;
+        let attr = token.parent_ancestors().find_map(ast::Attr::cast)?;
+        let expansions = sema.expand_derive_macro(&attr)?;
+        let idx = attr
+            .token_tree()?
+            .token_trees_and_tokens()
+            .filter_map(NodeOrToken::into_token)
+            .take_while(|it| it != &token)
+            .filter(|it| it.kind() == T![,])
+            .count();
+        let expansion =
+            format(db, SyntaxKind::MACRO_ITEMS, position.file_id, expansions.get(idx).cloned()?);
+        Some(ExpandedMacro { name, expansion })
+    });
 
     if derive.is_some() {
         return derive;
@@ -83,7 +77,14 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
         if let Some(item) = ast::Item::cast(node.clone()) {
             if let Some(def) = sema.resolve_attr_macro_call(&item) {
                 break (
-                    def.name(db).display(db).to_string(),
+                    def.name(db)
+                        .display(
+                            db,
+                            sema.attach_first_edition(position.file_id)
+                                .map(|it| it.edition())
+                                .unwrap_or(Edition::CURRENT),
+                        )
+                        .to_string(),
                     expand_macro_recur(&sema, &item)?,
                     SyntaxKind::MACRO_ITEMS,
                 );
diff --git a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs
index 5f6aaeaabb60e..3d49082f2858d 100644
--- a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs
@@ -1,6 +1,6 @@
 use std::iter::successors;
 
-use hir::{DescendPreference, Semantics};
+use hir::Semantics;
 use ide_db::RootDatabase;
 use syntax::{
     algo::{self, skip_trivia_token},
@@ -140,10 +140,8 @@ fn extend_tokens_from_range(
 
     // compute original mapped token range
     let extended = {
-        let fst_expanded =
-            sema.descend_into_macros_single(DescendPreference::None, first_token.clone());
-        let lst_expanded =
-            sema.descend_into_macros_single(DescendPreference::None, last_token.clone());
+        let fst_expanded = sema.descend_into_macros_single_exact(first_token.clone());
+        let lst_expanded = sema.descend_into_macros_single_exact(last_token.clone());
         let mut lca =
             algo::least_common_ancestor(&fst_expanded.parent()?, &lst_expanded.parent()?)?;
         lca = shallowest_node(&lca);
@@ -157,7 +155,7 @@ fn extend_tokens_from_range(
     let validate = || {
         let extended = &extended;
         move |token: &SyntaxToken| -> bool {
-            let expanded = sema.descend_into_macros_single(DescendPreference::None, token.clone());
+            let expanded = sema.descend_into_macros_single_exact(token.clone());
             let parent = match expanded.parent() {
                 Some(it) => it,
                 None => return false,
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs
index 6076de54ebaf8..6ae9dde84be5e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs
@@ -1,4 +1,4 @@
-use hir::{AsAssocItem, DescendPreference, Semantics};
+use hir::{AsAssocItem, Semantics};
 use ide_db::{
     defs::{Definition, NameClass, NameRefClass},
     RootDatabase,
@@ -29,7 +29,7 @@ pub(crate) fn goto_declaration(
         .find(|it| matches!(it.kind(), IDENT | T![self] | T![super] | T![crate] | T![Self]))?;
     let range = original_token.text_range();
     let info: Vec<NavigationTarget> = sema
-        .descend_into_macros(DescendPreference::None, original_token)
+        .descend_into_macros(original_token)
         .iter()
         .filter_map(|token| {
             let parent = token.parent()?;
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
index 8a8bc07945f1a..971cd3ef585c4 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
@@ -5,10 +5,7 @@ use crate::{
     navigation_target::{self, ToNav},
     FilePosition, NavigationTarget, RangeInfo, TryToNav, UpmappingResult,
 };
-use hir::{
-    AsAssocItem, AssocItem, DescendPreference, FileRange, InFile, MacroFileIdExt, ModuleDef,
-    Semantics,
-};
+use hir::{AsAssocItem, AssocItem, FileRange, InFile, MacroFileIdExt, ModuleDef, Semantics};
 use ide_db::{
     base_db::{AnchoredPath, FileLoader, SourceDatabase},
     defs::{Definition, IdentClass},
@@ -17,7 +14,7 @@ use ide_db::{
 };
 use itertools::Itertools;
 
-use span::FileId;
+use span::{Edition, FileId};
 use syntax::{
     ast::{self, HasLoopBody},
     match_ast, AstNode, AstToken,
@@ -44,6 +41,8 @@ pub(crate) fn goto_definition(
 ) -> Option<RangeInfo<Vec<NavigationTarget>>> {
     let sema = &Semantics::new(db);
     let file = sema.parse_guess_edition(file_id).syntax().clone();
+    let edition =
+        sema.attach_first_edition(file_id).map(|it| it.edition()).unwrap_or(Edition::CURRENT);
     let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
         IDENT
         | INT_NUMBER
@@ -55,7 +54,7 @@ pub(crate) fn goto_definition(
         | COMMENT => 4,
         // index and prefix ops
         T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3,
-        kind if kind.is_keyword() => 2,
+        kind if kind.is_keyword(edition) => 2,
         T!['('] | T![')'] => 2,
         kind if kind.is_trivia() => 0,
         _ => 1,
@@ -84,7 +83,7 @@ pub(crate) fn goto_definition(
     }
 
     let navs = sema
-        .descend_into_macros(DescendPreference::None, original_token.clone())
+        .descend_into_macros(original_token.clone())
         .into_iter()
         .filter_map(|token| {
             let parent = token.parent()?;
@@ -249,10 +248,7 @@ pub(crate) fn find_fn_or_blocks(
         None
     };
 
-    sema.descend_into_macros(DescendPreference::None, token.clone())
-        .into_iter()
-        .filter_map(find_ancestors)
-        .collect_vec()
+    sema.descend_into_macros(token.clone()).into_iter().filter_map(find_ancestors).collect_vec()
 }
 
 fn nav_for_exit_points(
@@ -367,7 +363,7 @@ pub(crate) fn find_loops(
         None
     };
 
-    sema.descend_into_macros(DescendPreference::None, token.clone())
+    sema.descend_into_macros(token.clone())
         .into_iter()
         .filter_map(find_ancestors)
         .collect_vec()
@@ -2735,4 +2731,23 @@ fn main() {
 "#,
         )
     }
+
+    #[test]
+    fn shadow_builtin_macro() {
+        check(
+            r#"
+//- minicore: column
+//- /a.rs crate:a
+#[macro_export]
+macro_rules! column { () => {} }
+          // ^^^^^^
+
+//- /b.rs crate:b deps:a
+use a::column;
+fn foo() {
+    $0column!();
+}
+        "#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs
index 2eff7796d548b..e36c8ee2f3f73 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs
@@ -1,4 +1,4 @@
-use hir::{AsAssocItem, DescendPreference, Impl, Semantics};
+use hir::{AsAssocItem, Impl, Semantics};
 use ide_db::{
     defs::{Definition, NameClass, NameRefClass},
     helpers::pick_best_token,
@@ -10,7 +10,7 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav};
 
 // Feature: Go to Implementation
 //
-// Navigates to the impl blocks of types.
+// Navigates to the impl items of types.
 //
 // |===
 // | Editor  | Shortcut
@@ -32,48 +32,55 @@ pub(crate) fn goto_implementation(
         _ => 0,
     })?;
     let range = original_token.text_range();
-    let navs =
-        sema.descend_into_macros_single(DescendPreference::SameText, original_token)
-            .parent()
-            .and_then(ast::NameLike::cast)
-            .and_then(|node| match &node {
-                ast::NameLike::Name(name) => {
-                    NameClass::classify(&sema, name).and_then(|class| match class {
-                        NameClass::Definition(it) | NameClass::ConstReference(it) => Some(it),
-                        NameClass::PatFieldShorthand { .. } => None,
-                    })
-                }
-                ast::NameLike::NameRef(name_ref) => NameRefClass::classify(&sema, name_ref)
-                    .and_then(|class| match class {
-                        NameRefClass::Definition(def) => Some(def),
-                        NameRefClass::FieldShorthand { .. }
-                        | NameRefClass::ExternCrateShorthand { .. } => None,
-                    }),
-                ast::NameLike::Lifetime(_) => None,
-            })
-            .and_then(|def| {
-                let navs = match def {
-                    Definition::Trait(trait_) => impls_for_trait(&sema, trait_),
-                    Definition::Adt(adt) => impls_for_ty(&sema, adt.ty(sema.db)),
-                    Definition::TypeAlias(alias) => impls_for_ty(&sema, alias.ty(sema.db)),
-                    Definition::BuiltinType(builtin) => impls_for_ty(&sema, builtin.ty(sema.db)),
-                    Definition::Function(f) => {
-                        let assoc = f.as_assoc_item(sema.db)?;
-                        let name = assoc.name(sema.db)?;
-                        let trait_ = assoc.container_or_implemented_trait(sema.db)?;
-                        impls_for_trait_item(&sema, trait_, name)
+    let navs = sema
+        .descend_into_macros_exact(original_token)
+        .iter()
+        .filter_map(|token| {
+            token
+                .parent()
+                .and_then(ast::NameLike::cast)
+                .and_then(|node| match &node {
+                    ast::NameLike::Name(name) => {
+                        NameClass::classify(&sema, name).and_then(|class| match class {
+                            NameClass::Definition(it) | NameClass::ConstReference(it) => Some(it),
+                            NameClass::PatFieldShorthand { .. } => None,
+                        })
                     }
-                    Definition::Const(c) => {
-                        let assoc = c.as_assoc_item(sema.db)?;
-                        let name = assoc.name(sema.db)?;
-                        let trait_ = assoc.container_or_implemented_trait(sema.db)?;
-                        impls_for_trait_item(&sema, trait_, name)
-                    }
-                    _ => return None,
-                };
-                Some(navs)
-            })
-            .unwrap_or_default();
+                    ast::NameLike::NameRef(name_ref) => NameRefClass::classify(&sema, name_ref)
+                        .and_then(|class| match class {
+                            NameRefClass::Definition(def) => Some(def),
+                            NameRefClass::FieldShorthand { .. }
+                            | NameRefClass::ExternCrateShorthand { .. } => None,
+                        }),
+                    ast::NameLike::Lifetime(_) => None,
+                })
+                .and_then(|def| {
+                    let navs = match def {
+                        Definition::Trait(trait_) => impls_for_trait(&sema, trait_),
+                        Definition::Adt(adt) => impls_for_ty(&sema, adt.ty(sema.db)),
+                        Definition::TypeAlias(alias) => impls_for_ty(&sema, alias.ty(sema.db)),
+                        Definition::BuiltinType(builtin) => {
+                            impls_for_ty(&sema, builtin.ty(sema.db))
+                        }
+                        Definition::Function(f) => {
+                            let assoc = f.as_assoc_item(sema.db)?;
+                            let name = assoc.name(sema.db)?;
+                            let trait_ = assoc.container_or_implemented_trait(sema.db)?;
+                            impls_for_trait_item(&sema, trait_, name)
+                        }
+                        Definition::Const(c) => {
+                            let assoc = c.as_assoc_item(sema.db)?;
+                            let name = assoc.name(sema.db)?;
+                            let trait_ = assoc.container_or_implemented_trait(sema.db)?;
+                            impls_for_trait_item(&sema, trait_, name)
+                        }
+                        _ => return None,
+                    };
+                    Some(navs)
+                })
+        })
+        .flatten()
+        .collect();
 
     Some(RangeInfo { range, info: navs })
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
index f75b8fb7d0224..ca04b7bb5a9b3 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
@@ -1,4 +1,4 @@
-use hir::{DescendPreference, GenericParam};
+use hir::GenericParam;
 use ide_db::{base_db::Upcast, defs::Definition, helpers::pick_best_token, RootDatabase};
 use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, T};
 
@@ -69,7 +69,7 @@ pub(crate) fn goto_type_definition(
     }
 
     let range = token.text_range();
-    sema.descend_into_macros(DescendPreference::None, token)
+    sema.descend_into_macros(token)
         .into_iter()
         .filter_map(|token| {
             let ty = sema
diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
index 8fcd38b4e3435..5348e855be4b0 100644
--- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
@@ -1,6 +1,6 @@
 use std::iter;
 
-use hir::{db, DescendPreference, FilePosition, FileRange, HirFileId, InFile, Semantics};
+use hir::{db, FilePosition, FileRange, HirFileId, InFile, Semantics};
 use ide_db::{
     defs::{Definition, IdentClass},
     helpers::pick_best_token,
@@ -65,7 +65,7 @@ pub(crate) fn highlight_related(
     let token = pick_best_token(syntax.token_at_offset(offset), |kind| match kind {
         T![?] => 4, // prefer `?` when the cursor is sandwiched like in `await$0?`
         T![->] => 4,
-        kind if kind.is_keyword() => 3,
+        kind if kind.is_keyword(file_id.edition()) => 3,
         IDENT | INT_NUMBER => 2,
         T![|] => 1,
         _ => 0,
@@ -542,7 +542,7 @@ fn cover_range(r0: Option<TextRange>, r1: Option<TextRange>) -> Option<TextRange
 }
 
 fn find_defs(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> FxHashSet<Definition> {
-    sema.descend_into_macros(DescendPreference::None, token)
+    sema.descend_into_macros_exact(token)
         .into_iter()
         .filter_map(|token| IdentClass::classify_token(sema, &token))
         .flat_map(IdentClass::definitions_no_ops)
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs
index 500674e32b30a..124db2985bf00 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs
@@ -6,7 +6,7 @@ mod tests;
 use std::{iter, ops::Not};
 
 use either::Either;
-use hir::{db::DefDatabase, DescendPreference, HasCrate, HasSource, LangItem, Semantics};
+use hir::{db::DefDatabase, HasCrate, HasSource, LangItem, Semantics};
 use ide_db::{
     defs::{Definition, IdentClass, NameRefClass, OperatorClass},
     famous_defs::FamousDefs,
@@ -14,6 +14,7 @@ use ide_db::{
     FileRange, FxIndexSet, RootDatabase,
 };
 use itertools::{multizip, Itertools};
+use span::Edition;
 use syntax::{ast, AstNode, SyntaxKind::*, SyntaxNode, T};
 
 use crate::{
@@ -57,7 +58,7 @@ pub enum HoverDocFormat {
     PlainText,
 }
 
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub enum HoverAction {
     Runnable(Runnable),
     Implementation(FilePosition),
@@ -66,7 +67,11 @@ pub enum HoverAction {
 }
 
 impl HoverAction {
-    fn goto_type_from_targets(db: &RootDatabase, targets: Vec<hir::ModuleDef>) -> Option<Self> {
+    fn goto_type_from_targets(
+        db: &RootDatabase,
+        targets: Vec<hir::ModuleDef>,
+        edition: Edition,
+    ) -> Option<Self> {
         let targets = targets
             .into_iter()
             .filter_map(|it| {
@@ -74,7 +79,8 @@ impl HoverAction {
                     mod_path: render::path(
                         db,
                         it.module(db)?,
-                        it.name(db).map(|name| name.display(db).to_string()),
+                        it.name(db).map(|name| name.display(db, edition).to_string()),
+                        edition,
                     ),
                     nav: it.try_to_nav(db)?.call_site(),
                 })
@@ -91,7 +97,7 @@ pub struct HoverGotoTypeData {
 }
 
 /// Contains the results when hovering over an item
-#[derive(Debug, Default)]
+#[derive(Clone, Debug, Default, Hash, PartialEq, Eq)]
 pub struct HoverResult {
     pub markup: Markup,
     pub actions: Vec<HoverAction>,
@@ -110,10 +116,12 @@ pub(crate) fn hover(
 ) -> Option<RangeInfo<HoverResult>> {
     let sema = &hir::Semantics::new(db);
     let file = sema.parse_guess_edition(file_id).syntax().clone();
+    let edition =
+        sema.attach_first_edition(file_id).map(|it| it.edition()).unwrap_or(Edition::CURRENT);
     let mut res = if range.is_empty() {
-        hover_simple(sema, FilePosition { file_id, offset: range.start() }, file, config)
+        hover_offset(sema, FilePosition { file_id, offset: range.start() }, file, config, edition)
     } else {
-        hover_ranged(sema, frange, file, config)
+        hover_ranged(sema, frange, file, config, edition)
     }?;
 
     if let HoverDocFormat::PlainText = config.format {
@@ -123,11 +131,12 @@ pub(crate) fn hover(
 }
 
 #[allow(clippy::field_reassign_with_default)]
-fn hover_simple(
+fn hover_offset(
     sema: &Semantics<'_, RootDatabase>,
     FilePosition { file_id, offset }: FilePosition,
     file: SyntaxNode,
     config: &HoverConfig,
+    edition: Edition,
 ) -> Option<RangeInfo<HoverResult>> {
     let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
         IDENT
@@ -140,7 +149,7 @@ fn hover_simple(
         | T![_] => 4,
         // index and prefix ops and closure pipe
         T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] | T![|] => 3,
-        kind if kind.is_keyword() => 2,
+        kind if kind.is_keyword(edition) => 2,
         T!['('] | T![')'] => 2,
         kind if kind.is_trivia() => 0,
         _ => 1,
@@ -149,7 +158,7 @@ fn hover_simple(
     if let Some(doc_comment) = token_as_doc_comment(&original_token) {
         cov_mark::hit!(no_highlight_on_comment_hover);
         return doc_comment.get_definition_with_descend_at(sema, offset, |def, node, range| {
-            let res = hover_for_definition(sema, file_id, def, &node, None, config);
+            let res = hover_for_definition(sema, file_id, def, &node, None, config, edition);
             Some(RangeInfo::new(range, res))
         });
     }
@@ -164,38 +173,48 @@ fn hover_simple(
             &original_token.parent()?,
             None,
             config,
+            edition,
         );
         return Some(RangeInfo::new(range, res));
     }
 
-    let in_attr = original_token
-        .parent_ancestors()
-        .filter_map(ast::Item::cast)
-        .any(|item| sema.is_attr_macro_call(&item))
-        && !matches!(
-            original_token.parent().and_then(ast::TokenTree::cast),
-            Some(tt) if tt.syntax().ancestors().any(|it| ast::Meta::can_cast(it.kind()))
-        );
-
     // prefer descending the same token kind in attribute expansions, in normal macros text
     // equivalency is more important
-    let descended = sema.descend_into_macros(
-        if in_attr { DescendPreference::SameKind } else { DescendPreference::SameText },
-        original_token.clone(),
-    );
-    let descended = || descended.iter();
+    let mut descended = sema.descend_into_macros(original_token.clone());
+
+    let kind = original_token.kind();
+    let text = original_token.text();
+    let ident_kind = kind.is_any_identifier();
+
+    descended.sort_by_cached_key(|tok| {
+        let tok_kind = tok.kind();
+
+        let exact_same_kind = tok_kind == kind;
+        let both_idents = exact_same_kind || (tok_kind.is_any_identifier() && ident_kind);
+        let same_text = tok.text() == text;
+        // anything that mapped into a token tree has likely no semantic information
+        let no_tt_parent = tok.parent().map_or(false, |it| it.kind() != TOKEN_TREE);
+        !((both_idents as usize)
+            | ((exact_same_kind as usize) << 1)
+            | ((same_text as usize) << 2)
+            | ((no_tt_parent as usize) << 3))
+    });
 
-    let result = descended()
-        // try lint hover
-        .find_map(|token| {
+    let mut res = vec![];
+    for token in descended {
+        let is_same_kind = token.kind() == kind;
+        let lint_hover = (|| {
             // FIXME: Definition should include known lints and the like instead of having this special case here
             let attr = token.parent_ancestors().find_map(ast::Attr::cast)?;
-            render::try_for_lint(&attr, token)
-        })
-        // try definitions
-        .or_else(|| {
-            descended()
-                .filter_map(|token| {
+            render::try_for_lint(&attr, &token)
+        })();
+        if let Some(lint_hover) = lint_hover {
+            res.push(lint_hover);
+            continue;
+        }
+        let definitions = (|| {
+            Some(
+                'a: {
                     let node = token.parent()?;
 
                     // special case macro calls, we wanna render the invoked arm index
@@ -210,11 +229,11 @@ fn hover_simple(
                                 .and_then(ast::MacroCall::cast)
                             {
                                 if let Some(macro_) = sema.resolve_macro_call(&macro_call) {
-                                    return Some(vec![(
+                                    break 'a vec![(
                                         Definition::Macro(macro_),
                                         sema.resolve_macro_call_arm(&macro_call),
                                         node,
-                                    )]);
+                                    )];
                                 }
                             }
                         }
@@ -223,88 +242,101 @@ fn hover_simple(
                     match IdentClass::classify_node(sema, &node)? {
                         // It's better for us to fall back to the keyword hover here,
                         // rendering poll is very confusing
-                        IdentClass::Operator(OperatorClass::Await(_)) => None,
+                        IdentClass::Operator(OperatorClass::Await(_)) => return None,
 
                         IdentClass::NameRefClass(NameRefClass::ExternCrateShorthand {
                             decl,
                             ..
-                        }) => Some(vec![(Definition::ExternCrateDecl(decl), None, node)]),
+                        }) => {
+                            vec![(Definition::ExternCrateDecl(decl), None, node)]
+                        }
 
-                        class => Some(
+                        class => {
                             multizip((class.definitions(), iter::repeat(None), iter::repeat(node)))
-                                .collect::<Vec<_>>(),
-                        ),
+                                .collect::<Vec<_>>()
+                        }
                     }
-                })
-                .flatten()
+                }
+                .into_iter()
                 .unique_by(|&(def, _, _)| def)
                 .map(|(def, macro_arm, node)| {
-                    hover_for_definition(sema, file_id, def, &node, macro_arm, config)
-                })
-                .reduce(|mut acc: HoverResult, HoverResult { markup, actions }| {
-                    acc.actions.extend(actions);
-                    acc.markup = Markup::from(format!("{}\n---\n{markup}", acc.markup));
-                    acc
+                    hover_for_definition(sema, file_id, def, &node, macro_arm, config, edition)
                 })
-        })
-        // try keywords
-        .or_else(|| descended().find_map(|token| render::keyword(sema, config, token)))
-        // try _ hovers
-        .or_else(|| descended().find_map(|token| render::underscore(sema, config, token)))
-        // try rest pattern hover
-        .or_else(|| {
-            descended().find_map(|token| {
-                if token.kind() != DOT2 {
-                    return None;
-                }
+                .collect::<Vec<_>>(),
+            )
+        })();
+        if let Some(definitions) = definitions {
+            res.extend(definitions);
+            continue;
+        }
+        let keywords = || render::keyword(sema, config, &token, edition);
+        let underscore = || {
+            if !is_same_kind {
+                return None;
+            }
+            render::underscore(sema, config, &token, edition)
+        };
+        let rest_pat = || {
+            if !is_same_kind || token.kind() != DOT2 {
+                return None;
+            }
 
-                let rest_pat = token.parent().and_then(ast::RestPat::cast)?;
-                let record_pat_field_list =
-                    rest_pat.syntax().parent().and_then(ast::RecordPatFieldList::cast)?;
+            let rest_pat = token.parent().and_then(ast::RestPat::cast)?;
+            let record_pat_field_list =
+                rest_pat.syntax().parent().and_then(ast::RecordPatFieldList::cast)?;
 
-                let record_pat =
-                    record_pat_field_list.syntax().parent().and_then(ast::RecordPat::cast)?;
+            let record_pat =
+                record_pat_field_list.syntax().parent().and_then(ast::RecordPat::cast)?;
 
-                Some(render::struct_rest_pat(sema, config, &record_pat))
-            })
-        })
-        // try () call hovers
-        .or_else(|| {
-            descended().find_map(|token| {
-                if token.kind() != T!['('] && token.kind() != T![')'] {
-                    return None;
-                }
-                let arg_list = token.parent().and_then(ast::ArgList::cast)?.syntax().parent()?;
-                let call_expr = syntax::match_ast! {
-                    match arg_list {
-                        ast::CallExpr(expr) => expr.into(),
-                        ast::MethodCallExpr(expr) => expr.into(),
-                        _ => return None,
-                    }
-                };
-                render::type_info_of(sema, config, &Either::Left(call_expr))
-            })
-        })
-        // try closure
-        .or_else(|| {
-            descended().find_map(|token| {
-                if token.kind() != T![|] {
-                    return None;
+            Some(render::struct_rest_pat(sema, config, &record_pat, edition))
+        };
+        let call = || {
+            if !is_same_kind || token.kind() != T!['('] && token.kind() != T![')'] {
+                return None;
+            }
+            let arg_list = token.parent().and_then(ast::ArgList::cast)?.syntax().parent()?;
+            let call_expr = syntax::match_ast! {
+                match arg_list {
+                    ast::CallExpr(expr) => expr.into(),
+                    ast::MethodCallExpr(expr) => expr.into(),
+                    _ => return None,
                 }
-                let c = token.parent().and_then(|x| x.parent()).and_then(ast::ClosureExpr::cast)?;
-                render::closure_expr(sema, config, c)
-            })
-        })
-        // tokens
-        .or_else(|| {
-            render::literal(sema, original_token.clone())
+            };
+            render::type_info_of(sema, config, &Either::Left(call_expr), edition)
+        };
+        let closure = || {
+            if !is_same_kind || token.kind() != T![|] {
+                return None;
+            }
+            let c = token.parent().and_then(|x| x.parent()).and_then(ast::ClosureExpr::cast)?;
+            render::closure_expr(sema, config, c, edition)
+        };
+        let literal = || {
+            render::literal(sema, original_token.clone(), edition)
                 .map(|markup| HoverResult { markup, actions: vec![] })
-        });
+        };
+        if let Some(result) = keywords()
+            .or_else(underscore)
+            .or_else(rest_pat)
+            .or_else(call)
+            .or_else(closure)
+            .or_else(literal)
+        {
+            res.push(result)
+        }
+    }
 
-    result.map(|mut res: HoverResult| {
-        res.actions = dedupe_or_merge_hover_actions(res.actions);
-        RangeInfo::new(original_token.text_range(), res)
-    })
+    res.into_iter()
+        .unique()
+        .reduce(|mut acc: HoverResult, HoverResult { markup, actions }| {
+            acc.actions.extend(actions);
+            acc.markup = Markup::from(format!("{}\n---\n{markup}", acc.markup));
+            acc
+        })
+        .map(|mut res: HoverResult| {
+            res.actions = dedupe_or_merge_hover_actions(res.actions);
+            RangeInfo::new(original_token.text_range(), res)
+        })
 }
 
 fn hover_ranged(
@@ -312,6 +344,7 @@ fn hover_ranged(
     FileRange { range, .. }: FileRange,
     file: SyntaxNode,
     config: &HoverConfig,
+    edition: Edition,
 ) -> Option<RangeInfo<HoverResult>> {
     // FIXME: make this work in attributes
     let expr_or_pat = file
@@ -320,15 +353,17 @@ fn hover_ranged(
         .take_while(|it| ast::MacroCall::can_cast(it.kind()) || !ast::Item::can_cast(it.kind()))
         .find_map(Either::<ast::Expr, ast::Pat>::cast)?;
     let res = match &expr_or_pat {
-        Either::Left(ast::Expr::TryExpr(try_expr)) => render::try_expr(sema, config, try_expr),
+        Either::Left(ast::Expr::TryExpr(try_expr)) => {
+            render::try_expr(sema, config, try_expr, edition)
+        }
         Either::Left(ast::Expr::PrefixExpr(prefix_expr))
             if prefix_expr.op_kind() == Some(ast::UnaryOp::Deref) =>
         {
-            render::deref_expr(sema, config, prefix_expr)
+            render::deref_expr(sema, config, prefix_expr, edition)
         }
         _ => None,
     };
-    let res = res.or_else(|| render::type_info_of(sema, config, &expr_or_pat));
+    let res = res.or_else(|| render::type_info_of(sema, config, &expr_or_pat, edition));
     res.map(|it| {
         let range = match expr_or_pat {
             Either::Left(it) => it.syntax().text_range(),
@@ -346,6 +381,7 @@ pub(crate) fn hover_for_definition(
     scope_node: &SyntaxNode,
     macro_arm: Option<u32>,
     config: &HoverConfig,
+    edition: Edition,
 ) -> HoverResult {
     let famous_defs = match &def {
         Definition::BuiltinType(_) => sema.scope(scope_node).map(|it| FamousDefs(sema, it.krate())),
@@ -369,15 +405,22 @@ pub(crate) fn hover_for_definition(
     };
     let notable_traits = def_ty.map(|ty| notable_traits(db, &ty)).unwrap_or_default();
 
-    let markup =
-        render::definition(sema.db, def, famous_defs.as_ref(), &notable_traits, macro_arm, config);
+    let markup = render::definition(
+        sema.db,
+        def,
+        famous_defs.as_ref(),
+        &notable_traits,
+        macro_arm,
+        config,
+        edition,
+    );
     HoverResult {
         markup: render::process_markup(sema.db, def, &markup, config),
         actions: [
             show_fn_references_action(sema.db, def),
             show_implementations_action(sema.db, def),
             runnable_action(sema, def, file_id),
-            goto_type_action_for_def(sema.db, def, &notable_traits),
+            goto_type_action_for_def(sema.db, def, &notable_traits, edition),
         ]
         .into_iter()
         .flatten()
@@ -469,6 +512,7 @@ fn goto_type_action_for_def(
     db: &RootDatabase,
     def: Definition,
     notable_traits: &[(hir::Trait, Vec<(Option<hir::Type>, hir::Name)>)],
+    edition: Edition,
 ) -> Option<HoverAction> {
     let mut targets: Vec<hir::ModuleDef> = Vec::new();
     let mut push_new_def = |item: hir::ModuleDef| {
@@ -499,13 +543,13 @@ fn goto_type_action_for_def(
             Definition::GenericParam(hir::GenericParam::ConstParam(it)) => it.ty(db),
             Definition::Field(field) => field.ty(db),
             Definition::Function(function) => function.ret_type(db),
-            _ => return HoverAction::goto_type_from_targets(db, targets),
+            _ => return HoverAction::goto_type_from_targets(db, targets, edition),
         };
 
         walk_and_push_ty(db, &ty, &mut push_new_def);
     }
 
-    HoverAction::goto_type_from_targets(db, targets)
+    HoverAction::goto_type_from_targets(db, targets, edition)
 }
 
 fn walk_and_push_ty(
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
index 7091b15b8a4ca..3e41b42be44b1 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
@@ -20,6 +20,7 @@ use rustc_apfloat::{
     ieee::{Half as f16, Quad as f128},
     Float,
 };
+use span::Edition;
 use stdx::format_to;
 use syntax::{algo, ast, match_ast, AstNode, AstToken, Direction, SyntaxToken, T};
 
@@ -34,27 +35,30 @@ pub(super) fn type_info_of(
     sema: &Semantics<'_, RootDatabase>,
     _config: &HoverConfig,
     expr_or_pat: &Either<ast::Expr, ast::Pat>,
+    edition: Edition,
 ) -> Option<HoverResult> {
     let ty_info = match expr_or_pat {
         Either::Left(expr) => sema.type_of_expr(expr)?,
         Either::Right(pat) => sema.type_of_pat(pat)?,
     };
-    type_info(sema, _config, ty_info)
+    type_info(sema, _config, ty_info, edition)
 }
 
 pub(super) fn closure_expr(
     sema: &Semantics<'_, RootDatabase>,
     config: &HoverConfig,
     c: ast::ClosureExpr,
+    edition: Edition,
 ) -> Option<HoverResult> {
     let TypeInfo { original, .. } = sema.type_of_expr(&c.into())?;
-    closure_ty(sema, config, &TypeInfo { original, adjusted: None })
+    closure_ty(sema, config, &TypeInfo { original, adjusted: None }, edition)
 }
 
 pub(super) fn try_expr(
     sema: &Semantics<'_, RootDatabase>,
     _config: &HoverConfig,
     try_expr: &ast::TryExpr,
+    edition: Edition,
 ) -> Option<HoverResult> {
     let inner_ty = sema.type_of_expr(&try_expr.expr()?)?.original;
     let mut ancestors = try_expr.syntax().ancestors();
@@ -117,12 +121,12 @@ pub(super) fn try_expr(
     };
     walk_and_push_ty(sema.db, &inner_ty, &mut push_new_def);
     walk_and_push_ty(sema.db, &body_ty, &mut push_new_def);
-    if let Some(actions) = HoverAction::goto_type_from_targets(sema.db, targets) {
+    if let Some(actions) = HoverAction::goto_type_from_targets(sema.db, targets, edition) {
         res.actions.push(actions);
     }
 
-    let inner_ty = inner_ty.display(sema.db).to_string();
-    let body_ty = body_ty.display(sema.db).to_string();
+    let inner_ty = inner_ty.display(sema.db, edition).to_string();
+    let body_ty = body_ty.display(sema.db, edition).to_string();
     let ty_len_max = inner_ty.len().max(body_ty.len());
 
     let l = "Propagated as: ".len() - " Type: ".len();
@@ -146,6 +150,7 @@ pub(super) fn deref_expr(
     sema: &Semantics<'_, RootDatabase>,
     _config: &HoverConfig,
     deref_expr: &ast::PrefixExpr,
+    edition: Edition,
 ) -> Option<HoverResult> {
     let inner_ty = sema.type_of_expr(&deref_expr.expr()?)?.original;
     let TypeInfo { original, adjusted } =
@@ -163,9 +168,9 @@ pub(super) fn deref_expr(
 
     res.markup = if let Some(adjusted_ty) = adjusted {
         walk_and_push_ty(sema.db, &adjusted_ty, &mut push_new_def);
-        let original = original.display(sema.db).to_string();
-        let adjusted = adjusted_ty.display(sema.db).to_string();
-        let inner = inner_ty.display(sema.db).to_string();
+        let original = original.display(sema.db, edition).to_string();
+        let adjusted = adjusted_ty.display(sema.db, edition).to_string();
+        let inner = inner_ty.display(sema.db, edition).to_string();
         let type_len = "To type: ".len();
         let coerced_len = "Coerced to: ".len();
         let deref_len = "Dereferenced from: ".len();
@@ -183,8 +188,8 @@ pub(super) fn deref_expr(
         )
         .into()
     } else {
-        let original = original.display(sema.db).to_string();
-        let inner = inner_ty.display(sema.db).to_string();
+        let original = original.display(sema.db, edition).to_string();
+        let inner = inner_ty.display(sema.db, edition).to_string();
         let type_len = "To type: ".len();
         let deref_len = "Dereferenced from: ".len();
         let max_len = (original.len() + type_len).max(inner.len() + deref_len);
@@ -197,7 +202,7 @@ pub(super) fn deref_expr(
         )
         .into()
     };
-    if let Some(actions) = HoverAction::goto_type_from_targets(sema.db, targets) {
+    if let Some(actions) = HoverAction::goto_type_from_targets(sema.db, targets, edition) {
         res.actions.push(actions);
     }
 
@@ -208,6 +213,7 @@ pub(super) fn underscore(
     sema: &Semantics<'_, RootDatabase>,
     config: &HoverConfig,
     token: &SyntaxToken,
+    edition: Edition,
 ) -> Option<HoverResult> {
     if token.kind() != T![_] {
         return None;
@@ -216,8 +222,8 @@ pub(super) fn underscore(
     let _it = match_ast! {
         match parent {
             ast::InferType(it) => it,
-            ast::UnderscoreExpr(it) => return type_info_of(sema, config, &Either::Left(ast::Expr::UnderscoreExpr(it))),
-            ast::WildcardPat(it) => return type_info_of(sema, config, &Either::Right(ast::Pat::WildcardPat(it))),
+            ast::UnderscoreExpr(it) => return type_info_of(sema, config, &Either::Left(ast::Expr::UnderscoreExpr(it)),edition),
+            ast::WildcardPat(it) => return type_info_of(sema, config, &Either::Right(ast::Pat::WildcardPat(it)),edition),
             _ => return None,
         }
     };
@@ -250,16 +256,18 @@ pub(super) fn keyword(
     sema: &Semantics<'_, RootDatabase>,
     config: &HoverConfig,
     token: &SyntaxToken,
+    edition: Edition,
 ) -> Option<HoverResult> {
-    if !token.kind().is_keyword() || !config.documentation || !config.keywords {
+    if !token.kind().is_keyword(edition) || !config.documentation || !config.keywords {
         return None;
     }
     let parent = token.parent()?;
     let famous_defs = FamousDefs(sema, sema.scope(&parent)?.krate());
 
-    let KeywordHint { description, keyword_mod, actions } = keyword_hints(sema, token, parent);
+    let KeywordHint { description, keyword_mod, actions } =
+        keyword_hints(sema, token, parent, edition);
 
-    let doc_owner = find_std_module(&famous_defs, &keyword_mod)?;
+    let doc_owner = find_std_module(&famous_defs, &keyword_mod, edition)?;
     let docs = doc_owner.docs(sema.db)?;
     let markup = process_markup(
         sema.db,
@@ -277,6 +285,7 @@ pub(super) fn struct_rest_pat(
     sema: &Semantics<'_, RootDatabase>,
     _config: &HoverConfig,
     pattern: &ast::RecordPat,
+    edition: Edition,
 ) -> HoverResult {
     let missing_fields = sema.record_pattern_missing_fields(pattern);
 
@@ -298,7 +307,7 @@ pub(super) fn struct_rest_pat(
     res.markup = {
         let mut s = String::from(".., ");
         for (f, _) in &missing_fields {
-            s += f.display(sema.db).to_string().as_ref();
+            s += f.display(sema.db, edition).to_string().as_ref();
             s += ", ";
         }
         // get rid of trailing comma
@@ -306,7 +315,7 @@ pub(super) fn struct_rest_pat(
 
         Markup::fenced_block(&s)
     };
-    if let Some(actions) = HoverAction::goto_type_from_targets(sema.db, targets) {
+    if let Some(actions) = HoverAction::goto_type_from_targets(sema.db, targets, edition) {
         res.actions.push(actions);
     }
     res
@@ -365,7 +374,7 @@ pub(super) fn process_markup(
     Markup::from(markup)
 }
 
-fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String> {
+fn definition_owner_name(db: &RootDatabase, def: &Definition, edition: Edition) -> Option<String> {
     match def {
         Definition::Field(f) => Some(f.parent_def(db).name(db)),
         Definition::Local(l) => l.parent(db).name(db),
@@ -384,17 +393,22 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String>
             }
         }
     }
-    .map(|name| name.display(db).to_string())
+    .map(|name| name.display(db, edition).to_string())
 }
 
-pub(super) fn path(db: &RootDatabase, module: hir::Module, item_name: Option<String>) -> String {
+pub(super) fn path(
+    db: &RootDatabase,
+    module: hir::Module,
+    item_name: Option<String>,
+    edition: Edition,
+) -> String {
     let crate_name =
         db.crate_graph()[module.krate().into()].display_name.as_ref().map(|it| it.to_string());
     let module_path = module
         .path_to_root(db)
         .into_iter()
         .rev()
-        .flat_map(|it| it.name(db).map(|name| name.display(db).to_string()));
+        .flat_map(|it| it.name(db).map(|name| name.display(db, edition).to_string()));
     crate_name.into_iter().chain(module_path).chain(item_name).join("::")
 }
 
@@ -405,39 +419,42 @@ pub(super) fn definition(
     notable_traits: &[(Trait, Vec<(Option<Type>, Name)>)],
     macro_arm: Option<u32>,
     config: &HoverConfig,
+    edition: Edition,
 ) -> Markup {
-    let mod_path = definition_mod_path(db, &def);
+    let mod_path = definition_mod_path(db, &def, edition);
     let label = match def {
         Definition::Trait(trait_) => {
-            trait_.display_limited(db, config.max_trait_assoc_items_count).to_string()
+            trait_.display_limited(db, config.max_trait_assoc_items_count, edition).to_string()
         }
         Definition::Adt(adt @ (Adt::Struct(_) | Adt::Union(_))) => {
-            adt.display_limited(db, config.max_fields_count).to_string()
+            adt.display_limited(db, config.max_fields_count, edition).to_string()
         }
         Definition::Variant(variant) => {
-            variant.display_limited(db, config.max_fields_count).to_string()
+            variant.display_limited(db, config.max_fields_count, edition).to_string()
         }
         Definition::Adt(adt @ Adt::Enum(_)) => {
-            adt.display_limited(db, config.max_enum_variants_count).to_string()
+            adt.display_limited(db, config.max_enum_variants_count, edition).to_string()
         }
         Definition::SelfType(impl_def) => {
             let self_ty = &impl_def.self_ty(db);
             match self_ty.as_adt() {
-                Some(adt) => adt.display_limited(db, config.max_fields_count).to_string(),
-                None => self_ty.display(db).to_string(),
+                Some(adt) => adt.display_limited(db, config.max_fields_count, edition).to_string(),
+                None => self_ty.display(db, edition).to_string(),
             }
         }
         Definition::Macro(it) => {
-            let mut label = it.display(db).to_string();
+            let mut label = it.display(db, edition).to_string();
             if let Some(macro_arm) = macro_arm {
                 format_to!(label, " // matched arm #{}", macro_arm);
             }
             label
         }
-        Definition::Function(fn_) => fn_.display_with_container_bounds(db, true).to_string(),
-        _ => def.label(db),
+        Definition::Function(fn_) => {
+            fn_.display_with_container_bounds(db, true, edition).to_string()
+        }
+        _ => def.label(db, edition),
     };
-    let docs = def.docs(db, famous_defs);
+    let docs = def.docs(db, famous_defs, edition);
     let value = (|| match def {
         Definition::Variant(it) => {
             if !it.parent_enum(db).is_data_carrying(db) {
@@ -452,7 +469,7 @@ pub(super) fn definition(
             }
         }
         Definition::Const(it) => {
-            let body = it.render_eval(db);
+            let body = it.render_eval(db, edition);
             match body {
                 Ok(it) => Some(it),
                 Err(_) => {
@@ -510,7 +527,7 @@ pub(super) fn definition(
     };
 
     let mut desc = String::new();
-    if let Some(notable_traits) = render_notable_trait_comment(db, notable_traits) {
+    if let Some(notable_traits) = render_notable_trait_comment(db, notable_traits, edition) {
         desc.push_str(&notable_traits);
         desc.push('\n');
     }
@@ -527,7 +544,11 @@ pub(super) fn definition(
     markup(docs.map(Into::into), desc, mod_path)
 }
 
-pub(super) fn literal(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> Option<Markup> {
+pub(super) fn literal(
+    sema: &Semantics<'_, RootDatabase>,
+    token: SyntaxToken,
+    edition: Edition,
+) -> Option<Markup> {
     let lit = token.parent().and_then(ast::Literal::cast)?;
     let ty = if let Some(p) = lit.syntax().parent().and_then(ast::Pat::cast) {
         sema.type_of_pat(&p)?
@@ -574,7 +595,7 @@ pub(super) fn literal(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) ->
             _ => return None
         }
     };
-    let ty = ty.display(sema.db);
+    let ty = ty.display(sema.db, edition);
 
     let mut s = format!("```rust\n{ty}\n```\n___\n\n");
     match value {
@@ -593,6 +614,7 @@ pub(super) fn literal(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) ->
 fn render_notable_trait_comment(
     db: &RootDatabase,
     notable_traits: &[(Trait, Vec<(Option<Type>, Name)>)],
+    edition: Edition,
 ) -> Option<String> {
     let mut desc = String::new();
     let mut needs_impl_header = true;
@@ -602,17 +624,17 @@ fn render_notable_trait_comment(
         } else {
             ", "
         });
-        format_to!(desc, "{}", trait_.name(db).display(db),);
+        format_to!(desc, "{}", trait_.name(db).display(db, edition));
         if !assoc_types.is_empty() {
             desc.push('<');
             format_to!(
                 desc,
                 "{}",
                 assoc_types.iter().format_with(", ", |(ty, name), f| {
-                    f(&name.display(db))?;
+                    f(&name.display(db, edition))?;
                     f(&" = ")?;
                     match ty {
-                        Some(ty) => f(&ty.display(db)),
+                        Some(ty) => f(&ty.display(db, edition)),
                         None => f(&"?"),
                     }
                 })
@@ -627,8 +649,9 @@ fn type_info(
     sema: &Semantics<'_, RootDatabase>,
     config: &HoverConfig,
     ty: TypeInfo,
+    edition: Edition,
 ) -> Option<HoverResult> {
-    if let Some(res) = closure_ty(sema, config, &ty) {
+    if let Some(res) = closure_ty(sema, config, &ty, edition) {
         return Some(res);
     };
     let db = sema.db;
@@ -654,17 +677,17 @@ fn type_info(
                 } else {
                     ", "
                 });
-                format_to!(desc, "{}", trait_.name(db).display(db),);
+                format_to!(desc, "{}", trait_.name(db).display(db, edition));
                 if !assoc_types.is_empty() {
                     desc.push('<');
                     format_to!(
                         desc,
                         "{}",
                         assoc_types.into_iter().format_with(", ", |(ty, name), f| {
-                            f(&name.display(db))?;
+                            f(&name.display(db, edition))?;
                             f(&" = ")?;
                             match ty {
-                                Some(ty) => f(&ty.display(db)),
+                                Some(ty) => f(&ty.display(db, edition)),
                                 None => f(&"?"),
                             }
                         })
@@ -678,8 +701,8 @@ fn type_info(
             desc
         };
 
-        let original = original.display(db).to_string();
-        let adjusted = adjusted_ty.display(db).to_string();
+        let original = original.display(db, edition).to_string();
+        let adjusted = adjusted_ty.display(db, edition).to_string();
         let static_text_diff_len = "Coerced to: ".len() - "Type: ".len();
         format!(
             "```text\nType: {:>apad$}\nCoerced to: {:>opad$}\n{notable}```\n",
@@ -690,14 +713,15 @@ fn type_info(
         )
         .into()
     } else {
-        let mut desc = match render_notable_trait_comment(db, &notable_traits(db, &original)) {
-            Some(desc) => desc + "\n",
-            None => String::new(),
-        };
-        format_to!(desc, "{}", original.display(db));
+        let mut desc =
+            match render_notable_trait_comment(db, &notable_traits(db, &original), edition) {
+                Some(desc) => desc + "\n",
+                None => String::new(),
+            };
+        format_to!(desc, "{}", original.display(db, edition));
         Markup::fenced_block(&desc)
     };
-    if let Some(actions) = HoverAction::goto_type_from_targets(db, targets) {
+    if let Some(actions) = HoverAction::goto_type_from_targets(db, targets, edition) {
         res.actions.push(actions);
     }
     Some(res)
@@ -707,6 +731,7 @@ fn closure_ty(
     sema: &Semantics<'_, RootDatabase>,
     config: &HoverConfig,
     TypeInfo { original, adjusted }: &TypeInfo,
+    edition: Edition,
 ) -> Option<HoverResult> {
     let c = original.as_closure()?;
     let mut captures_rendered = c.captured_items(sema.db)
@@ -739,12 +764,12 @@ fn closure_ty(
         walk_and_push_ty(sema.db, adjusted_ty, &mut push_new_def);
         format!(
             "\nCoerced to: {}",
-            adjusted_ty.display(sema.db).with_closure_style(hir::ClosureStyle::ImplFn)
+            adjusted_ty.display(sema.db, edition).with_closure_style(hir::ClosureStyle::ImplFn)
         )
     } else {
         String::new()
     };
-    let mut markup = format!("```rust\n{}", c.display_with_id(sema.db),);
+    let mut markup = format!("```rust\n{}", c.display_with_id(sema.db, edition));
 
     if let Some(layout) =
         render_memory_layout(config.memory_layout, || original.layout(sema.db), |_| None, |_| None)
@@ -757,23 +782,23 @@ fn closure_ty(
     format_to!(
         markup,
         "\n{}\n```{adjusted}\n\n## Captures\n{}",
-        c.display_with_impl(sema.db),
+        c.display_with_impl(sema.db, edition),
         captures_rendered,
     );
 
     let mut res = HoverResult::default();
-    if let Some(actions) = HoverAction::goto_type_from_targets(sema.db, targets) {
+    if let Some(actions) = HoverAction::goto_type_from_targets(sema.db, targets, edition) {
         res.actions.push(actions);
     }
     res.markup = markup.into();
     Some(res)
 }
 
-fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
+fn definition_mod_path(db: &RootDatabase, def: &Definition, edition: Edition) -> Option<String> {
     if matches!(def, Definition::GenericParam(_) | Definition::Local(_) | Definition::Label(_)) {
         return None;
     }
-    def.module(db).map(|module| path(db, module, definition_owner_name(db, def)))
+    def.module(db).map(|module| path(db, module, definition_owner_name(db, def, edition), edition))
 }
 
 fn markup(docs: Option<String>, desc: String, mod_path: Option<String>) -> Markup {
@@ -792,12 +817,16 @@ fn markup(docs: Option<String>, desc: String, mod_path: Option<String>) -> Marku
     buf.into()
 }
 
-fn find_std_module(famous_defs: &FamousDefs<'_, '_>, name: &str) -> Option<hir::Module> {
+fn find_std_module(
+    famous_defs: &FamousDefs<'_, '_>,
+    name: &str,
+    edition: Edition,
+) -> Option<hir::Module> {
     let db = famous_defs.0.db;
     let std_crate = famous_defs.std()?;
     let std_root_module = std_crate.root_module();
     std_root_module.children(db).find(|module| {
-        module.name(db).map_or(false, |module| module.display(db).to_string() == name)
+        module.name(db).map_or(false, |module| module.display(db, edition).to_string() == name)
     })
 }
 
@@ -888,6 +917,7 @@ fn keyword_hints(
     sema: &Semantics<'_, RootDatabase>,
     token: &SyntaxToken,
     parent: syntax::SyntaxNode,
+    edition: Edition,
 ) -> KeywordHint {
     match token.kind() {
         T![await] | T![loop] | T![match] | T![unsafe] | T![as] | T![try] | T![if] | T![else] => {
@@ -905,12 +935,12 @@ fn keyword_hints(
                     walk_and_push_ty(sema.db, &ty.original, &mut push_new_def);
 
                     let ty = ty.adjusted();
-                    let description = format!("{}: {}", token.text(), ty.display(sema.db));
+                    let description = format!("{}: {}", token.text(), ty.display(sema.db, edition));
 
                     KeywordHint {
                         description,
                         keyword_mod,
-                        actions: HoverAction::goto_type_from_targets(sema.db, targets)
+                        actions: HoverAction::goto_type_from_targets(sema.db, targets, edition)
                             .into_iter()
                             .collect(),
                     }
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 516e32ef91725..9585bdbe4c545 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -8465,7 +8465,7 @@ impl Iterator for S {
                                 file_id: FileId(
                                     1,
                                 ),
-                                full_range: 7800..8008,
+                                full_range: 7800..8042,
                                 focus_range: 7865..7871,
                                 name: "Future",
                                 kind: Trait,
@@ -8479,8 +8479,8 @@ impl Iterator for S {
                                 file_id: FileId(
                                     1,
                                 ),
-                                full_range: 8638..9104,
-                                focus_range: 8682..8690,
+                                full_range: 8672..9171,
+                                focus_range: 8749..8757,
                                 name: "Iterator",
                                 kind: Trait,
                                 container_name: "iterator",
@@ -8602,3 +8602,103 @@ fn test() {
         "#]],
     );
 }
+
+#[test]
+fn issue_17871() {
+    check(
+        r#"
+trait T {
+    fn f<A>();
+}
+
+struct S {}
+impl T for S {
+    fn f<A>() {}
+}
+
+fn main() {
+    let x$0 = S::f::<i32>;
+}
+"#,
+        expect![[r#"
+            *x*
+
+            ```rust
+            // size = 0, align = 1
+            let x: fn f<S, i32>()
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn raw_keyword_different_editions() {
+    check(
+        r#"
+//- /lib1.rs crate:with_edition_2015 edition:2015
+pub fn dyn() {}
+
+//- /lib2.rs crate:with_edition_2018 edition:2018 deps:with_edition_2015 new_source_root:local
+fn foo() {
+    with_edition_2015::r#dyn$0();
+}
+    "#,
+        expect![[r#"
+            *r#dyn*
+
+            ```rust
+            with_edition_2015
+            ```
+
+            ```rust
+            pub fn r#dyn()
+            ```
+        "#]],
+    );
+
+    check(
+        r#"
+//- /lib1.rs crate:with_edition_2018 edition:2018
+pub fn r#dyn() {}
+
+//- /lib2.rs crate:with_edition_2015 edition:2015 deps:with_edition_2018 new_source_root:local
+fn foo() {
+    with_edition_2018::dyn$0();
+}
+    "#,
+        expect![[r#"
+            *dyn*
+
+            ```rust
+            with_edition_2018
+            ```
+
+            ```rust
+            pub fn dyn()
+            ```
+        "#]],
+    );
+
+    check(
+        r#"
+//- /lib1.rs crate:escaping_needlessly edition:2015
+pub fn r#dyn() {}
+
+//- /lib2.rs crate:dependent edition:2015 deps:escaping_needlessly new_source_root:local
+fn foo() {
+    escaping_needlessly::dyn$0();
+}
+    "#,
+        expect![[r#"
+            *dyn*
+
+            ```rust
+            escaping_needlessly
+            ```
+
+            ```rust
+            pub fn dyn()
+            ```
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
index 0a8d2727575d2..6a5d5e26a4f05 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
@@ -11,7 +11,7 @@ use hir::{
 use ide_db::{famous_defs::FamousDefs, FileRange, RootDatabase};
 use itertools::Itertools;
 use smallvec::{smallvec, SmallVec};
-use span::EditionedFileId;
+use span::{Edition, EditionedFileId};
 use stdx::never;
 use syntax::{
     ast::{self, AstNode},
@@ -372,6 +372,7 @@ fn label_of_ty(
     famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
     ty: &hir::Type,
+    edition: Edition,
 ) -> Option<InlayHintLabel> {
     fn rec(
         sema: &Semantics<'_, RootDatabase>,
@@ -380,6 +381,7 @@ fn label_of_ty(
         ty: &hir::Type,
         label_builder: &mut InlayHintLabelBuilder<'_>,
         config: &InlayHintsConfig,
+        edition: Edition,
     ) -> Result<(), HirDisplayError> {
         let iter_item_type = hint_iterator(sema, famous_defs, ty);
         match iter_item_type {
@@ -410,12 +412,12 @@ fn label_of_ty(
                 label_builder.write_str(LABEL_ITEM)?;
                 label_builder.end_location_link();
                 label_builder.write_str(LABEL_MIDDLE2)?;
-                rec(sema, famous_defs, max_length, &ty, label_builder, config)?;
+                rec(sema, famous_defs, max_length, &ty, label_builder, config, edition)?;
                 label_builder.write_str(LABEL_END)?;
                 Ok(())
             }
             None => ty
-                .display_truncated(sema.db, max_length)
+                .display_truncated(sema.db, max_length, edition)
                 .with_closure_style(config.closure_style)
                 .write_to(label_builder),
         }
@@ -427,7 +429,7 @@ fn label_of_ty(
         location: None,
         result: InlayHintLabel::default(),
     };
-    let _ = rec(sema, famous_defs, config.max_length, ty, &mut label_builder, config);
+    let _ = rec(sema, famous_defs, config.max_length, ty, &mut label_builder, config, edition);
     let r = label_builder.finish();
     Some(r)
 }
@@ -569,7 +571,7 @@ fn hints(
         match node {
             ast::Expr(expr) => {
                 chaining::hints(hints, famous_defs, config, file_id, &expr);
-                adjustment::hints(hints, sema, config, &expr);
+                adjustment::hints(hints, sema, config, file_id, &expr);
                 match expr {
                     ast::Expr::CallExpr(it) => param_name::hints(hints, sema, config, ast::Expr::from(it)),
                     ast::Expr::MethodCallExpr(it) => {
@@ -600,7 +602,7 @@ fn hints(
                 // FIXME: record impl lifetimes so they aren't being reused in assoc item lifetime inlay hints
                 ast::Item::Impl(_) => None,
                 ast::Item::Fn(it) => {
-                    implicit_drop::hints(hints, sema, config, &it);
+                    implicit_drop::hints(hints, sema, config, file_id, &it);
                     fn_lifetime_fn::hints(hints, config, it)
                 },
                 // static type elisions
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
index 7932d8efbcf18..756198d0c0110 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
@@ -10,6 +10,7 @@ use hir::{
 };
 use ide_db::RootDatabase;
 
+use span::EditionedFileId;
 use stdx::never;
 use syntax::{
     ast::{self, make, AstNode},
@@ -25,6 +26,7 @@ pub(super) fn hints(
     acc: &mut Vec<InlayHint>,
     sema: &Semantics<'_, RootDatabase>,
     config: &InlayHintsConfig,
+    file_id: EditionedFileId,
     expr: &ast::Expr,
 ) -> Option<()> {
     if config.adjustment_hints_hide_outside_unsafe && !sema.is_inside_unsafe(expr) {
@@ -141,8 +143,8 @@ pub(super) fn hints(
             if postfix { format!(".{}", text.trim_end()) } else { text.to_owned() },
             Some(InlayTooltip::Markdown(format!(
                 "`{}` → `{}` ({coercion} coercion)",
-                source.display(sema.db),
-                target.display(sema.db),
+                source.display(sema.db, file_id.edition()),
+                target.display(sema.db, file_id.edition()),
             ))),
             None,
         );
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
index 7310852b8ed57..82b0a6ffcf135 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
@@ -22,7 +22,7 @@ pub(super) fn hints(
     acc: &mut Vec<InlayHint>,
     famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
-    _file_id: EditionedFileId,
+    file_id: EditionedFileId,
     pat: &ast::IdentPat,
 ) -> Option<()> {
     if !config.type_hints {
@@ -67,7 +67,7 @@ pub(super) fn hints(
         return None;
     }
 
-    let mut label = label_of_ty(famous_defs, config, &ty)?;
+    let mut label = label_of_ty(famous_defs, config, &ty, file_id.edition())?;
 
     if config.hide_named_constructor_hints
         && is_named_constructor(sema, pat, &label.to_string()).is_some()
@@ -342,7 +342,7 @@ fn f<'a>() {
     let x = S::<'static>;
       //^ S<'static>
     let y = S::<'_>;
-      //^ S
+      //^ S<'_>
     let z = S::<'a>;
       //^ S<'a>
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
index 4e15213b8bbf0..35f4d46e187c4 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
@@ -14,7 +14,7 @@ pub(super) fn hints(
     acc: &mut Vec<InlayHint>,
     famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
-    _file_id: EditionedFileId,
+    file_id: EditionedFileId,
     expr: &ast::Expr,
 ) -> Option<()> {
     if !config.chaining_hints {
@@ -58,7 +58,7 @@ pub(super) fn hints(
                     }
                 }
             }
-            let label = label_of_ty(famous_defs, config, &ty)?;
+            let label = label_of_ty(famous_defs, config, &ty, file_id.edition())?;
             acc.push(InlayHint {
                 range: expr.syntax().text_range(),
                 kind: InlayKind::Chaining,
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
index 8f2777f3928d1..d78fd64bdf4dc 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
@@ -36,8 +36,12 @@ pub(super) fn hints(
                     let ty = imp.self_ty(sema.db);
                     let trait_ = imp.trait_(sema.db);
                     let hint_text = match trait_ {
-                        Some(tr) => format!("impl {} for {}", tr.name(sema.db).display(sema.db), ty.display_truncated(sema.db, config.max_length)),
-                        None => format!("impl {}", ty.display_truncated(sema.db, config.max_length)),
+                        Some(tr) => format!(
+                            "impl {} for {}",
+                            tr.name(sema.db).display(sema.db, file_id.edition()),
+                            ty.display_truncated(sema.db, config.max_length, file_id.edition(),
+                        )),
+                        None => format!("impl {}", ty.display_truncated(sema.db, config.max_length, file_id.edition())),
                     };
                     (hint_text, None)
                 },
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs
index f6bd7ca064fda..325c2040691b1 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs
@@ -14,7 +14,7 @@ pub(super) fn hints(
     acc: &mut Vec<InlayHint>,
     famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
-    _file_id: EditionedFileId,
+    file_id: EditionedFileId,
     closure: ast::ClosureExpr,
 ) -> Option<()> {
     if config.closure_return_type_hints == ClosureReturnTypeHints::Never {
@@ -43,7 +43,7 @@ pub(super) fn hints(
         return None;
     }
 
-    let mut label = label_of_ty(famous_defs, config, &ty)?;
+    let mut label = label_of_ty(famous_defs, config, &ty, file_id.edition())?;
 
     if arrow.is_none() {
         label.prepend_str(" -> ");
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs
index 7f901db28d368..b4695a2b3519a 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs
@@ -12,6 +12,7 @@ use hir::{
 };
 use ide_db::{FileRange, RootDatabase};
 
+use span::EditionedFileId;
 use syntax::{
     ast::{self, AstNode},
     match_ast, ToSmolStr,
@@ -23,6 +24,7 @@ pub(super) fn hints(
     acc: &mut Vec<InlayHint>,
     sema: &Semantics<'_, RootDatabase>,
     config: &InlayHintsConfig,
+    file_id: EditionedFileId,
     def: &ast::Fn,
 ) -> Option<()> {
     if !config.implicit_drop_hints {
@@ -100,7 +102,7 @@ pub(super) fn hints(
                     })
                 });
             let binding = &hir.bindings[*binding];
-            let name = binding.name.display_no_db().to_smolstr();
+            let name = binding.name.display_no_db(file_id.edition()).to_smolstr();
             if name.starts_with("<ra@") {
                 continue; // Ignore desugared variables
             }
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
index 70d790efad3c9..0f3142ef3f88b 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
@@ -121,7 +121,9 @@ fn should_hide_param_name_hint(
     }
 
     let fn_name = match callable.kind() {
-        hir::CallableKind::Function(it) => Some(it.name(sema.db).display_no_db().to_smolstr()),
+        hir::CallableKind::Function(it) => {
+            Some(it.name(sema.db).unescaped().display_no_db().to_smolstr())
+        }
         _ => None,
     };
     let fn_name = fn_name.as_deref();
diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs
index eff4bc3d3766d..ba0aaae19c9fd 100644
--- a/src/tools/rust-analyzer/crates/ide/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs
@@ -104,7 +104,9 @@ pub use crate::{
     rename::RenameError,
     runnables::{Runnable, RunnableKind, TestId},
     signature_help::SignatureHelp,
-    static_index::{StaticIndex, StaticIndexedFile, TokenId, TokenStaticData},
+    static_index::{
+        StaticIndex, StaticIndexedFile, TokenId, TokenStaticData, VendoredLibrariesConfig,
+    },
     syntax_highlighting::{
         tags::{Highlight, HlMod, HlMods, HlOperator, HlPunct, HlTag},
         HighlightConfig, HlRange,
diff --git a/src/tools/rust-analyzer/crates/ide/src/markup.rs b/src/tools/rust-analyzer/crates/ide/src/markup.rs
index 4a4e29fa33b8b..750d12542605c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/markup.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/markup.rs
@@ -5,7 +5,7 @@
 //! what is used by LSP, so let's keep it simple.
 use std::fmt;
 
-#[derive(Default, Debug)]
+#[derive(Clone, Default, Debug, Hash, PartialEq, Eq)]
 pub struct Markup {
     text: String,
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs
index 1b64bc926039e..4be1b570981f6 100644
--- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs
@@ -3,7 +3,7 @@
 
 use core::fmt;
 
-use hir::{Adt, AsAssocItem, AssocItemContainer, Crate, DescendPreference, MacroKind, Semantics};
+use hir::{Adt, AsAssocItem, AssocItemContainer, Crate, MacroKind, Semantics};
 use ide_db::{
     base_db::{CrateOrigin, LangCrateOrigin},
     defs::{Definition, IdentClass},
@@ -154,7 +154,7 @@ pub(crate) fn moniker(
         });
     }
     let navs = sema
-        .descend_into_macros(DescendPreference::None, original_token.clone())
+        .descend_into_macros_exact(original_token.clone())
         .into_iter()
         .filter_map(|token| {
             IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops).map(|it| {
@@ -249,10 +249,11 @@ pub(crate) fn def_to_moniker(
 
     let module = def.module(db)?;
     let krate = module.krate();
+    let edition = krate.edition(db);
     let mut description = vec![];
     description.extend(module.path_to_root(db).into_iter().filter_map(|x| {
         Some(MonikerDescriptor {
-            name: x.name(db)?.display(db).to_string(),
+            name: x.name(db)?.display(db, edition).to_string(),
             desc: def_to_kind(db, x.into()).into(),
         })
     }));
@@ -265,7 +266,7 @@ pub(crate) fn def_to_moniker(
                 // Because different traits can have functions with the same name,
                 // we have to include the trait name as part of the moniker for uniqueness.
                 description.push(MonikerDescriptor {
-                    name: trait_.name(db).display(db).to_string(),
+                    name: trait_.name(db).display(db, edition).to_string(),
                     desc: def_to_kind(db, trait_.into()).into(),
                 });
             }
@@ -274,14 +275,14 @@ pub(crate) fn def_to_moniker(
                 // we add both the struct name and the trait name to the path
                 if let Some(adt) = impl_.self_ty(db).as_adt() {
                     description.push(MonikerDescriptor {
-                        name: adt.name(db).display(db).to_string(),
+                        name: adt.name(db).display(db, edition).to_string(),
                         desc: def_to_kind(db, adt.into()).into(),
                     });
                 }
 
                 if let Some(trait_) = impl_.trait_(db) {
                     description.push(MonikerDescriptor {
-                        name: trait_.name(db).display(db).to_string(),
+                        name: trait_.name(db).display(db, edition).to_string(),
                         desc: def_to_kind(db, trait_.into()).into(),
                     });
                 }
@@ -291,7 +292,7 @@ pub(crate) fn def_to_moniker(
 
     if let Definition::Field(it) = def {
         description.push(MonikerDescriptor {
-            name: it.parent_def(db).name(db).display(db).to_string(),
+            name: it.parent_def(db).name(db).display(db, edition).to_string(),
             desc: def_to_kind(db, it.parent_def(db).into()).into(),
         });
     }
@@ -303,7 +304,7 @@ pub(crate) fn def_to_moniker(
             let parent_name = parent.name(db);
             if let Some(name) = parent_name {
                 description.push(MonikerDescriptor {
-                    name: name.display(db).to_string(),
+                    name: name.display(db, edition).to_string(),
                     desc: def_to_kind(db, parent).into(),
                 });
             }
@@ -326,53 +327,53 @@ pub(crate) fn def_to_moniker(
                 return None;
             }
 
-            MonikerDescriptor { name: local.name(db).display(db).to_string(), desc }
+            MonikerDescriptor { name: local.name(db).display(db, edition).to_string(), desc }
         }
         Definition::Macro(m) => {
-            MonikerDescriptor { name: m.name(db).display(db).to_string(), desc }
+            MonikerDescriptor { name: m.name(db).display(db, edition).to_string(), desc }
         }
         Definition::Function(f) => {
-            MonikerDescriptor { name: f.name(db).display(db).to_string(), desc }
+            MonikerDescriptor { name: f.name(db).display(db, edition).to_string(), desc }
         }
         Definition::Variant(v) => {
-            MonikerDescriptor { name: v.name(db).display(db).to_string(), desc }
+            MonikerDescriptor { name: v.name(db).display(db, edition).to_string(), desc }
         }
         Definition::Const(c) => {
-            MonikerDescriptor { name: c.name(db)?.display(db).to_string(), desc }
+            MonikerDescriptor { name: c.name(db)?.display(db, edition).to_string(), desc }
         }
         Definition::Trait(trait_) => {
-            MonikerDescriptor { name: trait_.name(db).display(db).to_string(), desc }
+            MonikerDescriptor { name: trait_.name(db).display(db, edition).to_string(), desc }
         }
         Definition::TraitAlias(ta) => {
-            MonikerDescriptor { name: ta.name(db).display(db).to_string(), desc }
+            MonikerDescriptor { name: ta.name(db).display(db, edition).to_string(), desc }
         }
         Definition::TypeAlias(ta) => {
-            MonikerDescriptor { name: ta.name(db).display(db).to_string(), desc }
+            MonikerDescriptor { name: ta.name(db).display(db, edition).to_string(), desc }
         }
         Definition::Module(m) => {
-            MonikerDescriptor { name: m.name(db)?.display(db).to_string(), desc }
+            MonikerDescriptor { name: m.name(db)?.display(db, edition).to_string(), desc }
         }
         Definition::BuiltinType(b) => {
-            MonikerDescriptor { name: b.name().display(db).to_string(), desc }
+            MonikerDescriptor { name: b.name().display(db, edition).to_string(), desc }
         }
         Definition::SelfType(imp) => MonikerDescriptor {
-            name: imp.self_ty(db).as_adt()?.name(db).display(db).to_string(),
+            name: imp.self_ty(db).as_adt()?.name(db).display(db, edition).to_string(),
             desc,
         },
         Definition::Field(it) => {
-            MonikerDescriptor { name: it.name(db).display(db).to_string(), desc }
+            MonikerDescriptor { name: it.name(db).display(db, edition).to_string(), desc }
         }
         Definition::TupleField(it) => {
-            MonikerDescriptor { name: it.name().display(db).to_string(), desc }
+            MonikerDescriptor { name: it.name().display(db, edition).to_string(), desc }
         }
         Definition::Adt(adt) => {
-            MonikerDescriptor { name: adt.name(db).display(db).to_string(), desc }
+            MonikerDescriptor { name: adt.name(db).display(db, edition).to_string(), desc }
         }
         Definition::Static(s) => {
-            MonikerDescriptor { name: s.name(db).display(db).to_string(), desc }
+            MonikerDescriptor { name: s.name(db).display(db, edition).to_string(), desc }
         }
         Definition::ExternCrateDecl(m) => {
-            MonikerDescriptor { name: m.name(db).display(db).to_string(), desc }
+            MonikerDescriptor { name: m.name(db).display(db, edition).to_string(), desc }
         }
     };
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
index 066141d36f16a..9ace9fda62b97 100644
--- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
@@ -5,14 +5,15 @@ use std::fmt;
 use arrayvec::ArrayVec;
 use either::Either;
 use hir::{
-    db::ExpandDatabase, symbols::FileSymbol, AssocItem, FieldSource, HasContainer, HasSource,
-    HirDisplay, HirFileId, InFile, LocalSource, ModuleSource,
+    db::ExpandDatabase, symbols::FileSymbol, AssocItem, FieldSource, HasContainer, HasCrate,
+    HasSource, HirDisplay, HirFileId, HirFileIdExt, InFile, LocalSource, ModuleSource,
 };
 use ide_db::{
     defs::Definition,
     documentation::{Documentation, HasDocs},
     FileId, FileRange, RootDatabase, SymbolKind,
 };
+use span::Edition;
 use stdx::never;
 use syntax::{
     ast::{self, HasName},
@@ -97,7 +98,9 @@ impl NavigationTarget {
         db: &RootDatabase,
         module: hir::Module,
     ) -> UpmappingResult<NavigationTarget> {
-        let name = module.name(db).map(|it| it.display_no_db().to_smolstr()).unwrap_or_default();
+        let edition = module.krate().edition(db);
+        let name =
+            module.name(db).map(|it| it.display_no_db(edition).to_smolstr()).unwrap_or_default();
         match module.declaration_source(db) {
             Some(InFile { value, file_id }) => {
                 orig_range_with_focus(db, file_id, value.syntax(), value.name()).map(
@@ -110,7 +113,7 @@ impl NavigationTarget {
                             SymbolKind::Module,
                         );
                         res.docs = module.docs(db);
-                        res.description = Some(module.display(db).to_string());
+                        res.description = Some(module.display(db, edition).to_string());
                         res
                     },
                 )
@@ -175,6 +178,8 @@ impl NavigationTarget {
 
 impl TryToNav for FileSymbol {
     fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> {
+        let edition =
+            self.def.module(db).map(|it| it.krate().edition(db)).unwrap_or(Edition::CURRENT);
         Some(
             orig_range_with_focus_r(
                 db,
@@ -185,27 +190,26 @@ impl TryToNav for FileSymbol {
             .map(|(FileRange { file_id, range: full_range }, focus_range)| {
                 NavigationTarget {
                     file_id,
-                    name: self
-                        .is_alias
-                        .then(|| self.def.name(db))
-                        .flatten()
-                        .map_or_else(|| self.name.clone(), |it| it.display_no_db().to_smolstr()),
+                    name: self.is_alias.then(|| self.def.name(db)).flatten().map_or_else(
+                        || self.name.clone(),
+                        |it| it.display_no_db(edition).to_smolstr(),
+                    ),
                     alias: self.is_alias.then(|| self.name.clone()),
                     kind: Some(hir::ModuleDefId::from(self.def).into()),
                     full_range,
                     focus_range,
                     container_name: self.container_name.clone(),
                     description: match self.def {
-                        hir::ModuleDef::Module(it) => Some(it.display(db).to_string()),
-                        hir::ModuleDef::Function(it) => Some(it.display(db).to_string()),
-                        hir::ModuleDef::Adt(it) => Some(it.display(db).to_string()),
-                        hir::ModuleDef::Variant(it) => Some(it.display(db).to_string()),
-                        hir::ModuleDef::Const(it) => Some(it.display(db).to_string()),
-                        hir::ModuleDef::Static(it) => Some(it.display(db).to_string()),
-                        hir::ModuleDef::Trait(it) => Some(it.display(db).to_string()),
-                        hir::ModuleDef::TraitAlias(it) => Some(it.display(db).to_string()),
-                        hir::ModuleDef::TypeAlias(it) => Some(it.display(db).to_string()),
-                        hir::ModuleDef::Macro(it) => Some(it.display(db).to_string()),
+                        hir::ModuleDef::Module(it) => Some(it.display(db, edition).to_string()),
+                        hir::ModuleDef::Function(it) => Some(it.display(db, edition).to_string()),
+                        hir::ModuleDef::Adt(it) => Some(it.display(db, edition).to_string()),
+                        hir::ModuleDef::Variant(it) => Some(it.display(db, edition).to_string()),
+                        hir::ModuleDef::Const(it) => Some(it.display(db, edition).to_string()),
+                        hir::ModuleDef::Static(it) => Some(it.display(db, edition).to_string()),
+                        hir::ModuleDef::Trait(it) => Some(it.display(db, edition).to_string()),
+                        hir::ModuleDef::TraitAlias(it) => Some(it.display(db, edition).to_string()),
+                        hir::ModuleDef::TypeAlias(it) => Some(it.display(db, edition).to_string()),
+                        hir::ModuleDef::Macro(it) => Some(it.display(db, edition).to_string()),
                         hir::ModuleDef::BuiltinType(_) => None,
                     },
                     docs: None,
@@ -271,11 +275,13 @@ pub(crate) trait ToNavFromAst: Sized {
     }
 }
 
-fn container_name(db: &RootDatabase, t: impl HasContainer) -> Option<SmolStr> {
+fn container_name(db: &RootDatabase, t: impl HasContainer, edition: Edition) -> Option<SmolStr> {
     match t.container(db) {
-        hir::ItemContainer::Trait(it) => Some(it.name(db).display_no_db().to_smolstr()),
+        hir::ItemContainer::Trait(it) => Some(it.name(db).display_no_db(edition).to_smolstr()),
         // FIXME: Handle owners of blocks correctly here
-        hir::ItemContainer::Module(it) => it.name(db).map(|name| name.display_no_db().to_smolstr()),
+        hir::ItemContainer::Module(it) => {
+            it.name(db).map(|name| name.display_no_db(edition).to_smolstr())
+        }
         _ => None,
     }
 }
@@ -283,32 +289,32 @@ fn container_name(db: &RootDatabase, t: impl HasContainer) -> Option<SmolStr> {
 impl ToNavFromAst for hir::Function {
     const KIND: SymbolKind = SymbolKind::Function;
     fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
-        container_name(db, self)
+        container_name(db, self, self.krate(db).edition(db))
     }
 }
 
 impl ToNavFromAst for hir::Const {
     const KIND: SymbolKind = SymbolKind::Const;
     fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
-        container_name(db, self)
+        container_name(db, self, self.krate(db).edition(db))
     }
 }
 impl ToNavFromAst for hir::Static {
     const KIND: SymbolKind = SymbolKind::Static;
     fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
-        container_name(db, self)
+        container_name(db, self, self.krate(db).edition(db))
     }
 }
 impl ToNavFromAst for hir::Struct {
     const KIND: SymbolKind = SymbolKind::Struct;
     fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
-        container_name(db, self)
+        container_name(db, self, self.krate(db).edition(db))
     }
 }
 impl ToNavFromAst for hir::Enum {
     const KIND: SymbolKind = SymbolKind::Enum;
     fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
-        container_name(db, self)
+        container_name(db, self, self.krate(db).edition(db))
     }
 }
 impl ToNavFromAst for hir::Variant {
@@ -317,25 +323,25 @@ impl ToNavFromAst for hir::Variant {
 impl ToNavFromAst for hir::Union {
     const KIND: SymbolKind = SymbolKind::Union;
     fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
-        container_name(db, self)
+        container_name(db, self, self.krate(db).edition(db))
     }
 }
 impl ToNavFromAst for hir::TypeAlias {
     const KIND: SymbolKind = SymbolKind::TypeAlias;
     fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
-        container_name(db, self)
+        container_name(db, self, self.krate(db).edition(db))
     }
 }
 impl ToNavFromAst for hir::Trait {
     const KIND: SymbolKind = SymbolKind::Trait;
     fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
-        container_name(db, self)
+        container_name(db, self, self.krate(db).edition(db))
     }
 }
 impl ToNavFromAst for hir::TraitAlias {
     const KIND: SymbolKind = SymbolKind::TraitAlias;
     fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
-        container_name(db, self)
+        container_name(db, self, self.krate(db).edition(db))
     }
 }
 
@@ -346,6 +352,7 @@ where
 {
     fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> {
         let src = self.source(db)?;
+        let edition = src.file_id.original_file(db).edition();
         Some(
             NavigationTarget::from_named(
                 db,
@@ -354,7 +361,7 @@ where
             )
             .map(|mut res| {
                 res.docs = self.docs(db);
-                res.description = Some(self.display(db).to_string());
+                res.description = Some(self.display(db, edition).to_string());
                 res.container_name = self.container_name(db);
                 res
             }),
@@ -365,8 +372,10 @@ where
 impl ToNav for hir::Module {
     fn to_nav(&self, db: &RootDatabase) -> UpmappingResult<NavigationTarget> {
         let InFile { file_id, value } = self.definition_source(db);
+        let edition = self.krate(db).edition(db);
 
-        let name = self.name(db).map(|it| it.display_no_db().to_smolstr()).unwrap_or_default();
+        let name =
+            self.name(db).map(|it| it.display_no_db(edition).to_smolstr()).unwrap_or_default();
         let (syntax, focus) = match &value {
             ModuleSource::SourceFile(node) => (node.syntax(), None),
             ModuleSource::Module(node) => (node.syntax(), node.name()),
@@ -418,6 +427,7 @@ impl TryToNav for hir::ExternCrateDecl {
         let focus = value
             .rename()
             .map_or_else(|| value.name_ref().map(Either::Left), |it| it.name().map(Either::Right));
+        let edition = self.module(db).krate().edition(db);
 
         Some(orig_range_with_focus(db, file_id, value.syntax(), focus).map(
             |(FileRange { file_id, range: full_range }, focus_range)| {
@@ -425,7 +435,7 @@ impl TryToNav for hir::ExternCrateDecl {
                     file_id,
                     self.alias_or_name(db)
                         .unwrap_or_else(|| self.name(db))
-                        .display_no_db()
+                        .display_no_db(edition)
                         .to_smolstr(),
                     focus_range,
                     full_range,
@@ -433,8 +443,8 @@ impl TryToNav for hir::ExternCrateDecl {
                 );
 
                 res.docs = self.docs(db);
-                res.description = Some(self.display(db).to_string());
-                res.container_name = container_name(db, *self);
+                res.description = Some(self.display(db, edition).to_string());
+                res.container_name = container_name(db, *self, edition);
                 res
             },
         ))
@@ -444,13 +454,14 @@ impl TryToNav for hir::ExternCrateDecl {
 impl TryToNav for hir::Field {
     fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> {
         let src = self.source(db)?;
+        let edition = self.parent_def(db).module(db).krate().edition(db);
 
         let field_source = match &src.value {
             FieldSource::Named(it) => {
                 NavigationTarget::from_named(db, src.with_value(it), SymbolKind::Field).map(
                     |mut res| {
                         res.docs = self.docs(db);
-                        res.description = Some(self.display(db).to_string());
+                        res.description = Some(self.display(db, edition).to_string());
                         res
                     },
                 )
@@ -531,10 +542,11 @@ impl ToNav for LocalSource {
             Either::Left(bind_pat) => (bind_pat.syntax(), bind_pat.name()),
             Either::Right(it) => (it.syntax(), it.name()),
         };
+        let edition = self.local.parent(db).module(db).krate().edition(db);
 
         orig_range_with_focus(db, file_id, node, name).map(
             |(FileRange { file_id, range: full_range }, focus_range)| {
-                let name = local.name(db).display_no_db().to_smolstr();
+                let name = local.name(db).display_no_db(edition).to_smolstr();
                 let kind = if local.is_self(db) {
                     SymbolKind::SelfParam
                 } else if local.is_param(db) {
@@ -567,7 +579,8 @@ impl ToNav for hir::Local {
 impl TryToNav for hir::Label {
     fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> {
         let InFile { file_id, value } = self.source(db)?;
-        let name = self.name(db).display_no_db().to_smolstr();
+        // Labels can't be keywords, so no escaping needed.
+        let name = self.name(db).display_no_db(Edition::Edition2015).to_smolstr();
 
         Some(orig_range_with_focus(db, file_id, value.syntax(), value.lifetime()).map(
             |(FileRange { file_id, range: full_range }, focus_range)| NavigationTarget {
@@ -588,7 +601,8 @@ impl TryToNav for hir::Label {
 impl TryToNav for hir::TypeParam {
     fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> {
         let InFile { file_id, value } = self.merge().source(db)?;
-        let name = self.name(db).display_no_db().to_smolstr();
+        let edition = self.module(db).krate().edition(db);
+        let name = self.name(db).display_no_db(edition).to_smolstr();
 
         let value = match value {
             Either::Left(ast::TypeOrConstParam::Type(x)) => Either::Left(x),
@@ -630,7 +644,8 @@ impl TryToNav for hir::TypeOrConstParam {
 impl TryToNav for hir::LifetimeParam {
     fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> {
         let InFile { file_id, value } = self.source(db)?;
-        let name = self.name(db).display_no_db().to_smolstr();
+        // Lifetimes cannot be keywords, so not escaping needed.
+        let name = self.name(db).display_no_db(Edition::Edition2015).to_smolstr();
 
         Some(orig_range(db, file_id, value.syntax()).map(
             |(FileRange { file_id, range: full_range }, focus_range)| NavigationTarget {
@@ -651,7 +666,8 @@ impl TryToNav for hir::LifetimeParam {
 impl TryToNav for hir::ConstParam {
     fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> {
         let InFile { file_id, value } = self.merge().source(db)?;
-        let name = self.name(db).display_no_db().to_smolstr();
+        let edition = self.module(db).krate().edition(db);
+        let name = self.name(db).display_no_db(edition).to_smolstr();
 
         let value = match value {
             Either::Left(ast::TypeOrConstParam::Const(x)) => x,
diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs
index 64b82b31c7417..64d717f88ddd9 100644
--- a/src/tools/rust-analyzer/crates/ide/src/references.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/references.rs
@@ -9,7 +9,7 @@
 //! at the index that the match starts at and its tree parent is
 //! resolved to the search element definition, we get a reference.
 
-use hir::{DescendPreference, PathResolution, Semantics};
+use hir::{PathResolution, Semantics};
 use ide_db::{
     defs::{Definition, NameClass, NameRefClass},
     search::{ReferenceCategory, SearchScope, UsageSearchResult},
@@ -17,6 +17,7 @@ use ide_db::{
 };
 use itertools::Itertools;
 use nohash_hasher::IntMap;
+use span::Edition;
 use syntax::{
     ast::{self, HasName},
     match_ast, AstNode,
@@ -148,7 +149,7 @@ pub(crate) fn find_defs<'a>(
     }
 
     Some(
-        sema.descend_into_macros(DescendPreference::SameText, token)
+        sema.descend_into_macros_exact(token)
             .into_iter()
             .filter_map(|it| ast::NameLike::cast(it.parent()?))
             .filter_map(move |name_like| {
@@ -305,7 +306,9 @@ fn handle_control_flow_keywords(
     FilePosition { file_id, offset }: FilePosition,
 ) -> Option<ReferenceSearchResult> {
     let file = sema.parse_guess_edition(file_id);
-    let token = file.syntax().token_at_offset(offset).find(|t| t.kind().is_keyword())?;
+    let edition =
+        sema.attach_first_edition(file_id).map(|it| it.edition()).unwrap_or(Edition::CURRENT);
+    let token = file.syntax().token_at_offset(offset).find(|t| t.kind().is_keyword(edition))?;
 
     let references = match token.kind() {
         T![fn] | T![return] | T![try] => highlight_related::highlight_exit_points(sema, token),
@@ -2507,4 +2510,244 @@ fn main() {
             "#]],
         )
     }
+
+    // The following are tests for short_associated_function_fast_search() in crates/ide-db/src/search.rs, because find all references
+    // use `FindUsages` and I found it easy to test it here.
+
+    #[test]
+    fn goto_ref_on_short_associated_function() {
+        cov_mark::check!(short_associated_function_fast_search);
+        check(
+            r#"
+struct Foo;
+impl Foo {
+    fn new$0() {}
+}
+
+fn bar() {
+    Foo::new();
+}
+fn baz() {
+    Foo::new;
+}
+        "#,
+            expect![[r#"
+                new Function FileId(0) 27..38 30..33
+
+                FileId(0) 62..65
+                FileId(0) 91..94
+            "#]],
+        );
+    }
+
+    #[test]
+    fn goto_ref_on_short_associated_function_with_aliases() {
+        cov_mark::check!(short_associated_function_fast_search);
+        cov_mark::check!(container_use_rename);
+        cov_mark::check!(container_type_alias);
+        check(
+            r#"
+//- /lib.rs
+mod a;
+mod b;
+
+struct Foo;
+impl Foo {
+    fn new$0() {}
+}
+
+fn bar() {
+    b::c::Baz::new();
+}
+
+//- /a.rs
+use crate::Foo as Bar;
+
+fn baz() { Bar::new(); }
+fn quux() { <super::b::Other as super::b::Trait>::Assoc::new(); }
+
+//- /b.rs
+pub(crate) mod c;
+
+pub(crate) struct Other;
+pub(crate) trait Trait {
+    type Assoc;
+}
+impl Trait for Other {
+    type Assoc = super::Foo;
+}
+
+//- /b/c.rs
+type Itself<T> = T;
+pub(in super::super) type Baz = Itself<crate::Foo>;
+        "#,
+            expect![[r#"
+                new Function FileId(0) 42..53 45..48
+
+                FileId(0) 83..86
+                FileId(1) 40..43
+                FileId(1) 106..109
+            "#]],
+        );
+    }
+
+    #[test]
+    fn goto_ref_on_short_associated_function_self_works() {
+        cov_mark::check!(short_associated_function_fast_search);
+        cov_mark::check!(self_type_alias);
+        check(
+            r#"
+//- /lib.rs
+mod module;
+
+struct Foo;
+impl Foo {
+    fn new$0() {}
+    fn bar() { Self::new(); }
+}
+trait Trait {
+    type Assoc;
+    fn baz();
+}
+impl Trait for Foo {
+    type Assoc = Self;
+    fn baz() { Self::new(); }
+}
+
+//- /module.rs
+impl super::Foo {
+    fn quux() { Self::new(); }
+}
+fn foo() { <super::Foo as super::Trait>::Assoc::new(); }
+                "#,
+            expect![[r#"
+                new Function FileId(0) 40..51 43..46
+
+                FileId(0) 73..76
+                FileId(0) 195..198
+                FileId(1) 40..43
+                FileId(1) 99..102
+            "#]],
+        );
+    }
+
+    #[test]
+    fn goto_ref_on_short_associated_function_overlapping_self_ranges() {
+        check(
+            r#"
+struct Foo;
+impl Foo {
+    fn new$0() {}
+    fn bar() {
+        Self::new();
+        impl Foo {
+            fn baz() { Self::new(); }
+        }
+    }
+}
+            "#,
+            expect![[r#"
+                new Function FileId(0) 27..38 30..33
+
+                FileId(0) 68..71
+                FileId(0) 123..126
+            "#]],
+        );
+    }
+
+    #[test]
+    fn goto_ref_on_short_associated_function_no_direct_self_but_path_contains_self() {
+        cov_mark::check!(short_associated_function_fast_search);
+        check(
+            r#"
+struct Foo;
+impl Foo {
+    fn new$0() {}
+}
+trait Trait {
+    type Assoc;
+}
+impl<A, B> Trait for (A, B) {
+    type Assoc = B;
+}
+impl Foo {
+    fn bar() {
+        <((), Foo) as Trait>::Assoc::new();
+        <((), Self) as Trait>::Assoc::new();
+    }
+}
+            "#,
+            expect![[r#"
+                new Function FileId(0) 27..38 30..33
+
+                FileId(0) 188..191
+                FileId(0) 233..236
+            "#]],
+        );
+    }
+
+    // Checks that we can circumvent our fast path logic using complicated type level functions.
+    // This mainly exists as a documentation. I don't believe it is fixable.
+    // Usages search is not 100% accurate anyway; we miss macros.
+    #[test]
+    fn goto_ref_on_short_associated_function_complicated_type_magic_can_confuse_our_logic() {
+        cov_mark::check!(short_associated_function_fast_search);
+        cov_mark::check!(same_name_different_def_type_alias);
+        check(
+            r#"
+struct Foo;
+impl Foo {
+    fn new$0() {}
+}
+
+struct ChoiceA;
+struct ChoiceB;
+trait Choice {
+    type Choose<A, B>;
+}
+impl Choice for ChoiceA {
+    type Choose<A, B> = A;
+}
+impl Choice for ChoiceB {
+    type Choose<A, B> = B;
+}
+type Choose<A, C> = <C as Choice>::Choose<A, Foo>;
+
+fn bar() {
+    Choose::<(), ChoiceB>::new();
+}
+                "#,
+            expect![[r#"
+                new Function FileId(0) 27..38 30..33
+
+                (no references)
+            "#]],
+        );
+    }
+
+    #[test]
+    fn goto_ref_on_short_associated_function_same_path_mention_alias_and_self() {
+        cov_mark::check!(short_associated_function_fast_search);
+        check(
+            r#"
+struct Foo;
+impl Foo {
+    fn new$0() {}
+}
+
+type IgnoreFirst<A, B> = B;
+
+impl Foo {
+    fn bar() {
+        <IgnoreFirst<Foo, Self>>::new();
+    }
+}
+                "#,
+            expect![[r#"
+                new Function FileId(0) 27..38 30..33
+
+                FileId(0) 131..134
+            "#]],
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs
index 9581474ca7bd2..42b7472c645f3 100644
--- a/src/tools/rust-analyzer/crates/ide/src/rename.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs
@@ -12,12 +12,8 @@ use ide_db::{
     FileId, FileRange, RootDatabase,
 };
 use itertools::Itertools;
-use span::Edition;
 use stdx::{always, never};
-use syntax::{
-    ast, utils::is_raw_identifier, AstNode, SmolStr, SyntaxKind, SyntaxNode, TextRange, TextSize,
-    ToSmolStr,
-};
+use syntax::{ast, AstNode, SyntaxKind, SyntaxNode, TextRange, TextSize};
 
 use text_edit::TextEdit;
 
@@ -102,7 +98,7 @@ pub(crate) fn rename(
             // FIXME: This can use the `ide_db::rename_reference` (or def.rename) method once we can
             // properly find "direct" usages/references.
             .map(|(.., def)| {
-                match IdentifierKind::classify(Edition::CURRENT_FIXME, new_name)? {
+                match IdentifierKind::classify(new_name)? {
                     IdentifierKind::Ident => (),
                     IdentifierKind::Lifetime => {
                         bail!("Cannot alias reference to a lifetime identifier")
@@ -124,7 +120,10 @@ pub(crate) fn rename(
 
                 let mut source_change = SourceChange::default();
                 source_change.extend(usages.references.get_mut(&file_id).iter().map(|refs| {
-                    (position.file_id, source_edit_from_references(refs, def, new_name))
+                    (
+                        position.file_id,
+                        source_edit_from_references(refs, def, new_name, file_id.edition()),
+                    )
                 }));
 
                 Ok(source_change)
@@ -162,11 +161,7 @@ pub(crate) fn will_rename_file(
     let sema = Semantics::new(db);
     let module = sema.file_to_module_def(file_id)?;
     let def = Definition::Module(module);
-    let mut change = if is_raw_identifier(new_name_stem) {
-        def.rename(&sema, &SmolStr::from_iter(["r#", new_name_stem])).ok()?
-    } else {
-        def.rename(&sema, new_name_stem).ok()?
-    };
+    let mut change = def.rename(&sema, new_name_stem).ok()?;
     change.file_system_edits.clear();
     Some(change)
 }
@@ -270,7 +265,7 @@ fn find_definitions(
                             // if the name differs from the definitions name it has to be an alias
                             if def
                                 .name(sema.db)
-                                .map_or(false, |it| it.display_no_db().to_smolstr() != name_ref.text().as_str())
+                                .map_or(false, |it| !it.eq_ident(name_ref.text().as_str()))
                             {
                                 Err(format_err!("Renaming aliases is currently unsupported"))
                             } else {
@@ -377,7 +372,7 @@ fn rename_to_self(
     let usages = def.usages(sema).all();
     let mut source_change = SourceChange::default();
     source_change.extend(usages.iter().map(|(file_id, references)| {
-        (file_id.into(), source_edit_from_references(references, def, "self"))
+        (file_id.into(), source_edit_from_references(references, def, "self", file_id.edition()))
     }));
     source_change.insert_source_edit(
         file_id.original_file(sema.db),
@@ -398,7 +393,7 @@ fn rename_self_to_param(
         return Ok(SourceChange::default());
     }
 
-    let identifier_kind = IdentifierKind::classify(Edition::CURRENT_FIXME, new_name)?;
+    let identifier_kind = IdentifierKind::classify(new_name)?;
 
     let InFile { file_id, value: self_param } =
         sema.source(self_param).ok_or_else(|| format_err!("cannot find function source"))?;
@@ -413,7 +408,7 @@ fn rename_self_to_param(
     let mut source_change = SourceChange::default();
     source_change.insert_source_edit(file_id.original_file(sema.db), edit);
     source_change.extend(usages.iter().map(|(file_id, references)| {
-        (file_id.into(), source_edit_from_references(references, def, new_name))
+        (file_id.into(), source_edit_from_references(references, def, new_name, file_id.edition()))
     }));
     Ok(source_change)
 }
@@ -634,9 +629,9 @@ impl Foo {
     #[test]
     fn test_rename_to_invalid_identifier3() {
         check(
-            "let",
+            "super",
             r#"fn main() { let i$0 = 1; }"#,
-            "error: Invalid name `let`: not an identifier",
+            "error: Invalid name `super`: not an identifier",
         );
     }
 
@@ -685,11 +680,7 @@ impl Foo {
 
     #[test]
     fn test_rename_mod_invalid_raw_ident() {
-        check(
-            "r#self",
-            r#"mod foo$0 {}"#,
-            "error: Invalid name: `self` cannot be a raw identifier",
-        );
+        check("r#self", r#"mod foo$0 {}"#, "error: Invalid name `self`: not an identifier");
     }
 
     #[test]
@@ -1543,6 +1534,228 @@ pub fn baz() {}
         );
     }
 
+    #[test]
+    fn test_rename_each_usage_gets_appropriate_rawness() {
+        check_expect(
+            "dyn",
+            r#"
+//- /a.rs crate:a edition:2015
+pub fn foo() {}
+
+//- /b.rs crate:b edition:2018 deps:a new_source_root:local
+fn bar() {
+    a::foo$0();
+}
+    "#,
+            expect![[r#"
+                source_file_edits: [
+                    (
+                        FileId(
+                            0,
+                        ),
+                        [
+                            Indel {
+                                insert: "dyn",
+                                delete: 7..10,
+                            },
+                        ],
+                    ),
+                    (
+                        FileId(
+                            1,
+                        ),
+                        [
+                            Indel {
+                                insert: "r#dyn",
+                                delete: 18..21,
+                            },
+                        ],
+                    ),
+                ]
+                file_system_edits: []
+            "#]],
+        );
+
+        check_expect(
+            "dyn",
+            r#"
+//- /a.rs crate:a edition:2018
+pub fn foo() {}
+
+//- /b.rs crate:b edition:2015 deps:a new_source_root:local
+fn bar() {
+    a::foo$0();
+}
+    "#,
+            expect![[r#"
+                source_file_edits: [
+                    (
+                        FileId(
+                            0,
+                        ),
+                        [
+                            Indel {
+                                insert: "r#dyn",
+                                delete: 7..10,
+                            },
+                        ],
+                    ),
+                    (
+                        FileId(
+                            1,
+                        ),
+                        [
+                            Indel {
+                                insert: "dyn",
+                                delete: 18..21,
+                            },
+                        ],
+                    ),
+                ]
+                file_system_edits: []
+            "#]],
+        );
+
+        check_expect(
+            "r#dyn",
+            r#"
+//- /a.rs crate:a edition:2018
+pub fn foo$0() {}
+
+//- /b.rs crate:b edition:2015 deps:a new_source_root:local
+fn bar() {
+    a::foo();
+}
+    "#,
+            expect![[r#"
+                source_file_edits: [
+                    (
+                        FileId(
+                            0,
+                        ),
+                        [
+                            Indel {
+                                insert: "r#dyn",
+                                delete: 7..10,
+                            },
+                        ],
+                    ),
+                    (
+                        FileId(
+                            1,
+                        ),
+                        [
+                            Indel {
+                                insert: "dyn",
+                                delete: 18..21,
+                            },
+                        ],
+                    ),
+                ]
+                file_system_edits: []
+            "#]],
+        );
+    }
+
+    #[test]
+    fn rename_raw_identifier() {
+        check_expect(
+            "abc",
+            r#"
+//- /a.rs crate:a edition:2015
+pub fn dyn() {}
+
+fn foo() {
+    dyn$0();
+}
+
+//- /b.rs crate:b edition:2018 deps:a new_source_root:local
+fn bar() {
+    a::r#dyn();
+}
+    "#,
+            expect![[r#"
+                source_file_edits: [
+                    (
+                        FileId(
+                            0,
+                        ),
+                        [
+                            Indel {
+                                insert: "abc",
+                                delete: 7..10,
+                            },
+                            Indel {
+                                insert: "abc",
+                                delete: 32..35,
+                            },
+                        ],
+                    ),
+                    (
+                        FileId(
+                            1,
+                        ),
+                        [
+                            Indel {
+                                insert: "abc",
+                                delete: 18..23,
+                            },
+                        ],
+                    ),
+                ]
+                file_system_edits: []
+            "#]],
+        );
+
+        check_expect(
+            "abc",
+            r#"
+//- /a.rs crate:a edition:2018
+pub fn r#dyn() {}
+
+fn foo() {
+    r#dyn$0();
+}
+
+//- /b.rs crate:b edition:2015 deps:a new_source_root:local
+fn bar() {
+    a::dyn();
+}
+    "#,
+            expect![[r#"
+                source_file_edits: [
+                    (
+                        FileId(
+                            0,
+                        ),
+                        [
+                            Indel {
+                                insert: "abc",
+                                delete: 7..12,
+                            },
+                            Indel {
+                                insert: "abc",
+                                delete: 34..39,
+                            },
+                        ],
+                    ),
+                    (
+                        FileId(
+                            1,
+                        ),
+                        [
+                            Indel {
+                                insert: "abc",
+                                delete: 18..21,
+                            },
+                        ],
+                    ),
+                ]
+                file_system_edits: []
+            "#]],
+        );
+    }
+
     #[test]
     fn test_enum_variant_from_module_1() {
         cov_mark::check!(rename_non_local);
diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
index 5d4b8b3643943..38dc522789d71 100644
--- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
@@ -3,7 +3,8 @@ use std::fmt;
 use ast::HasName;
 use cfg::{CfgAtom, CfgExpr};
 use hir::{
-    db::HirDatabase, sym, AsAssocItem, AttrsWithOwner, HasAttrs, HasSource, HirFileIdExt, Semantics,
+    db::HirDatabase, sym, AsAssocItem, AttrsWithOwner, HasAttrs, HasCrate, HasSource, HirFileIdExt,
+    Semantics,
 };
 use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn};
 use ide_db::{
@@ -14,7 +15,7 @@ use ide_db::{
     FilePosition, FxHashMap, FxHashSet, RootDatabase, SymbolKind,
 };
 use itertools::Itertools;
-use span::TextSize;
+use span::{Edition, TextSize};
 use stdx::{always, format_to};
 use syntax::{
     ast::{self, AstNode},
@@ -321,6 +322,7 @@ pub(crate) fn runnable_fn(
     sema: &Semantics<'_, RootDatabase>,
     def: hir::Function,
 ) -> Option<Runnable> {
+    let edition = def.krate(sema.db).edition(sema.db);
     let under_cfg_test = has_cfg_test(def.module(sema.db).attrs(sema.db));
     let kind = if !under_cfg_test && def.is_main(sema.db) {
         RunnableKind::Bin
@@ -328,11 +330,11 @@ pub(crate) fn runnable_fn(
         let test_id = || {
             let canonical_path = {
                 let def: hir::ModuleDef = def.into();
-                def.canonical_path(sema.db)
+                def.canonical_path(sema.db, edition)
             };
             canonical_path
                 .map(TestId::Path)
-                .unwrap_or(TestId::Name(def.name(sema.db).display_no_db().to_smolstr()))
+                .unwrap_or(TestId::Name(def.name(sema.db).display_no_db(edition).to_smolstr()))
         };
 
         if def.is_test(sema.db) {
@@ -367,8 +369,11 @@ pub(crate) fn runnable_mod(
         .path_to_root(sema.db)
         .into_iter()
         .rev()
-        .filter_map(|it| it.name(sema.db))
-        .map(|it| it.display(sema.db).to_string())
+        .filter_map(|module| {
+            module.name(sema.db).map(|mod_name| {
+                mod_name.display(sema.db, module.krate().edition(sema.db)).to_string()
+            })
+        })
         .join("::");
 
     let attrs = def.attrs(sema.db);
@@ -381,6 +386,7 @@ pub(crate) fn runnable_impl(
     sema: &Semantics<'_, RootDatabase>,
     def: &hir::Impl,
 ) -> Option<Runnable> {
+    let edition = def.module(sema.db).krate().edition(sema.db);
     let attrs = def.attrs(sema.db);
     if !has_runnable_doc_test(&attrs) {
         return None;
@@ -389,13 +395,13 @@ pub(crate) fn runnable_impl(
     let nav = def.try_to_nav(sema.db)?.call_site();
     let ty = def.self_ty(sema.db);
     let adt_name = ty.as_adt()?.name(sema.db);
-    let mut ty_args = ty.generic_parameters(sema.db).peekable();
+    let mut ty_args = ty.generic_parameters(sema.db, edition).peekable();
     let params = if ty_args.peek().is_some() {
         format!("<{}>", ty_args.format_with(",", |ty, cb| cb(&ty)))
     } else {
         String::new()
     };
-    let mut test_id = format!("{}{params}", adt_name.display(sema.db));
+    let mut test_id = format!("{}{params}", adt_name.display(sema.db, edition));
     test_id.retain(|c| c != ' ');
     let test_id = TestId::Path(test_id);
 
@@ -419,8 +425,11 @@ fn runnable_mod_outline_definition(
         .path_to_root(sema.db)
         .into_iter()
         .rev()
-        .filter_map(|it| it.name(sema.db))
-        .map(|it| it.display(sema.db).to_string())
+        .filter_map(|module| {
+            module.name(sema.db).map(|mod_name| {
+                mod_name.display(sema.db, module.krate().edition(sema.db)).to_string()
+            })
+        })
         .join("::");
 
     let attrs = def.attrs(sema.db);
@@ -452,6 +461,7 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> {
         Definition::SelfType(it) => it.attrs(db),
         _ => return None,
     };
+    let edition = def.krate(db).map(|it| it.edition(db)).unwrap_or(Edition::CURRENT);
     if !has_runnable_doc_test(&attrs) {
         return None;
     }
@@ -460,29 +470,29 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> {
         let mut path = String::new();
         def.canonical_module_path(db)?
             .flat_map(|it| it.name(db))
-            .for_each(|name| format_to!(path, "{}::", name.display(db)));
+            .for_each(|name| format_to!(path, "{}::", name.display(db, edition)));
         // This probably belongs to canonical_path?
         if let Some(assoc_item) = def.as_assoc_item(db) {
             if let Some(ty) = assoc_item.implementing_ty(db) {
                 if let Some(adt) = ty.as_adt() {
                     let name = adt.name(db);
-                    let mut ty_args = ty.generic_parameters(db).peekable();
-                    format_to!(path, "{}", name.display(db));
+                    let mut ty_args = ty.generic_parameters(db, edition).peekable();
+                    format_to!(path, "{}", name.display(db, edition));
                     if ty_args.peek().is_some() {
                         format_to!(path, "<{}>", ty_args.format_with(",", |ty, cb| cb(&ty)));
                     }
-                    format_to!(path, "::{}", def_name.display(db));
+                    format_to!(path, "::{}", def_name.display(db, edition));
                     path.retain(|c| c != ' ');
                     return Some(path);
                 }
             }
         }
-        format_to!(path, "{}", def_name.display(db));
+        format_to!(path, "{}", def_name.display(db, edition));
         Some(path)
     })();
 
-    let test_id =
-        path.map_or_else(|| TestId::Name(def_name.display_no_db().to_smolstr()), TestId::Path);
+    let test_id = path
+        .map_or_else(|| TestId::Name(def_name.display_no_db(edition).to_smolstr()), TestId::Path);
 
     let mut nav = match def {
         Definition::Module(def) => NavigationTarget::from_module_to_decl(db, def),
diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
index b6c9e2f6366a7..516f64959cefa 100644
--- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
@@ -4,15 +4,13 @@
 use std::collections::BTreeSet;
 
 use either::Either;
-use hir::{
-    AssocItem, DescendPreference, GenericParam, HirDisplay, ModuleDef, PathResolution, Semantics,
-    Trait,
-};
+use hir::{AssocItem, GenericParam, HirDisplay, ModuleDef, PathResolution, Semantics, Trait};
 use ide_db::{
     active_parameter::{callable_for_node, generic_def_for_node},
     documentation::{Documentation, HasDocs},
     FilePosition, FxIndexMap,
 };
+use span::Edition;
 use stdx::format_to;
 use syntax::{
     algo,
@@ -81,7 +79,9 @@ pub(crate) fn signature_help(
         // if the cursor is sandwiched between two space tokens and the call is unclosed
         // this prevents us from leaving the CallExpression
         .and_then(|tok| algo::skip_trivia_token(tok, Direction::Prev))?;
-    let token = sema.descend_into_macros_single(DescendPreference::None, token);
+    let token = sema.descend_into_macros_single_exact(token);
+    let edition =
+        sema.attach_first_edition(file_id).map(|it| it.edition()).unwrap_or(Edition::CURRENT);
 
     for node in token.parent_ancestors() {
         match_ast! {
@@ -91,49 +91,49 @@ pub(crate) fn signature_help(
                     if cursor_outside {
                         continue;
                     }
-                    return signature_help_for_call(&sema, arg_list, token);
+                    return signature_help_for_call(&sema, arg_list, token, edition);
                 },
                 ast::GenericArgList(garg_list) => {
                     let cursor_outside = garg_list.r_angle_token().as_ref() == Some(&token);
                     if cursor_outside {
                         continue;
                     }
-                    return signature_help_for_generics(&sema, garg_list, token);
+                    return signature_help_for_generics(&sema, garg_list, token, edition);
                 },
                 ast::RecordExpr(record) => {
                     let cursor_outside = record.record_expr_field_list().and_then(|list| list.r_curly_token()).as_ref() == Some(&token);
                     if cursor_outside {
                         continue;
                     }
-                    return signature_help_for_record_lit(&sema, record, token);
+                    return signature_help_for_record_lit(&sema, record, token, edition);
                 },
                 ast::RecordPat(record) => {
                     let cursor_outside = record.record_pat_field_list().and_then(|list| list.r_curly_token()).as_ref() == Some(&token);
                     if cursor_outside {
                         continue;
                     }
-                    return signature_help_for_record_pat(&sema, record, token);
+                    return signature_help_for_record_pat(&sema, record, token, edition);
                 },
                 ast::TupleStructPat(tuple_pat) => {
                     let cursor_outside = tuple_pat.r_paren_token().as_ref() == Some(&token);
                     if cursor_outside {
                         continue;
                     }
-                    return signature_help_for_tuple_struct_pat(&sema, tuple_pat, token);
+                    return signature_help_for_tuple_struct_pat(&sema, tuple_pat, token, edition);
                 },
                 ast::TuplePat(tuple_pat) => {
                     let cursor_outside = tuple_pat.r_paren_token().as_ref() == Some(&token);
                     if cursor_outside {
                         continue;
                     }
-                    return signature_help_for_tuple_pat(&sema, tuple_pat, token);
+                    return signature_help_for_tuple_pat(&sema, tuple_pat, token, edition);
                 },
                 ast::TupleExpr(tuple_expr) => {
                     let cursor_outside = tuple_expr.r_paren_token().as_ref() == Some(&token);
                     if cursor_outside {
                         continue;
                     }
-                    return signature_help_for_tuple_expr(&sema, tuple_expr, token);
+                    return signature_help_for_tuple_expr(&sema, tuple_expr, token, edition);
                 },
                 _ => (),
             }
@@ -157,6 +157,7 @@ fn signature_help_for_call(
     sema: &Semantics<'_, RootDatabase>,
     arg_list: ast::ArgList,
     token: SyntaxToken,
+    edition: Edition,
 ) -> Option<SignatureHelp> {
     // Find the calling expression and its NameRef
     let mut nodes = arg_list.syntax().ancestors().skip(1);
@@ -181,7 +182,7 @@ fn signature_help_for_call(
     match callable.kind() {
         hir::CallableKind::Function(func) => {
             res.doc = func.docs(db);
-            format_to!(res.signature, "fn {}", func.name(db).display(db));
+            format_to!(res.signature, "fn {}", func.name(db).display(db, edition));
             fn_params = Some(match callable.receiver_param(db) {
                 Some(_self) => func.params_without_self(db),
                 None => func.assoc_fn_params(db),
@@ -189,15 +190,15 @@ fn signature_help_for_call(
         }
         hir::CallableKind::TupleStruct(strukt) => {
             res.doc = strukt.docs(db);
-            format_to!(res.signature, "struct {}", strukt.name(db).display(db));
+            format_to!(res.signature, "struct {}", strukt.name(db).display(db, edition));
         }
         hir::CallableKind::TupleEnumVariant(variant) => {
             res.doc = variant.docs(db);
             format_to!(
                 res.signature,
                 "enum {}::{}",
-                variant.parent_enum(db).name(db).display(db),
-                variant.name(db).display(db)
+                variant.parent_enum(db).name(db).display(db, edition),
+                variant.name(db).display(db, edition)
             );
         }
         hir::CallableKind::Closure(closure) => {
@@ -210,7 +211,7 @@ fn signature_help_for_call(
             Some(adt) => format_to!(
                 res.signature,
                 "<{} as {fn_trait}>::{}",
-                adt.name(db).display(db),
+                adt.name(db).display(db, edition),
                 fn_trait.function_name()
             ),
             None => format_to!(res.signature, "impl {fn_trait}"),
@@ -220,7 +221,7 @@ fn signature_help_for_call(
     res.signature.push('(');
     {
         if let Some((self_param, _)) = callable.receiver_param(db) {
-            format_to!(res.signature, "{}", self_param.display(db))
+            format_to!(res.signature, "{}", self_param.display(db, edition))
         }
         let mut buf = String::new();
         for (idx, p) in callable.params().into_iter().enumerate() {
@@ -240,8 +241,10 @@ fn signature_help_for_call(
             // This is overly conservative: we do not substitute known type vars
             // (see FIXME in tests::impl_trait) and falling back on any unknowns.
             match (p.ty().contains_unknown(), fn_params.as_deref()) {
-                (true, Some(fn_params)) => format_to!(buf, "{}", fn_params[idx].ty().display(db)),
-                _ => format_to!(buf, "{}", p.ty().display(db)),
+                (true, Some(fn_params)) => {
+                    format_to!(buf, "{}", fn_params[idx].ty().display(db, edition))
+                }
+                _ => format_to!(buf, "{}", p.ty().display(db, edition)),
             }
             res.push_call_param(&buf);
         }
@@ -250,7 +253,7 @@ fn signature_help_for_call(
 
     let mut render = |ret_type: hir::Type| {
         if !ret_type.is_unit() {
-            format_to!(res.signature, " -> {}", ret_type.display(db));
+            format_to!(res.signature, " -> {}", ret_type.display(db, edition));
         }
     };
     match callable.kind() {
@@ -270,6 +273,7 @@ fn signature_help_for_generics(
     sema: &Semantics<'_, RootDatabase>,
     arg_list: ast::GenericArgList,
     token: SyntaxToken,
+    edition: Edition,
 ) -> Option<SignatureHelp> {
     let (generics_def, mut active_parameter, first_arg_is_non_lifetime, variant) =
         generic_def_for_node(sema, &arg_list, &token)?;
@@ -284,11 +288,11 @@ fn signature_help_for_generics(
     match generics_def {
         hir::GenericDef::Function(it) => {
             res.doc = it.docs(db);
-            format_to!(res.signature, "fn {}", it.name(db).display(db));
+            format_to!(res.signature, "fn {}", it.name(db).display(db, edition));
         }
         hir::GenericDef::Adt(hir::Adt::Enum(it)) => {
             res.doc = it.docs(db);
-            format_to!(res.signature, "enum {}", it.name(db).display(db));
+            format_to!(res.signature, "enum {}", it.name(db).display(db, edition));
             if let Some(variant) = variant {
                 // In paths, generics of an enum can be specified *after* one of its variants.
                 // eg. `None::<u8>`
@@ -298,23 +302,23 @@ fn signature_help_for_generics(
         }
         hir::GenericDef::Adt(hir::Adt::Struct(it)) => {
             res.doc = it.docs(db);
-            format_to!(res.signature, "struct {}", it.name(db).display(db));
+            format_to!(res.signature, "struct {}", it.name(db).display(db, edition));
         }
         hir::GenericDef::Adt(hir::Adt::Union(it)) => {
             res.doc = it.docs(db);
-            format_to!(res.signature, "union {}", it.name(db).display(db));
+            format_to!(res.signature, "union {}", it.name(db).display(db, edition));
         }
         hir::GenericDef::Trait(it) => {
             res.doc = it.docs(db);
-            format_to!(res.signature, "trait {}", it.name(db).display(db));
+            format_to!(res.signature, "trait {}", it.name(db).display(db, edition));
         }
         hir::GenericDef::TraitAlias(it) => {
             res.doc = it.docs(db);
-            format_to!(res.signature, "trait {}", it.name(db).display(db));
+            format_to!(res.signature, "trait {}", it.name(db).display(db, edition));
         }
         hir::GenericDef::TypeAlias(it) => {
             res.doc = it.docs(db);
-            format_to!(res.signature, "type {}", it.name(db).display(db));
+            format_to!(res.signature, "type {}", it.name(db).display(db, edition));
         }
         // These don't have generic args that can be specified
         hir::GenericDef::Impl(_) | hir::GenericDef::Const(_) => return None,
@@ -339,11 +343,11 @@ fn signature_help_for_generics(
         }
 
         buf.clear();
-        format_to!(buf, "{}", param.display(db));
+        format_to!(buf, "{}", param.display(db, edition));
         res.push_generic_param(&buf);
     }
     if let hir::GenericDef::Trait(tr) = generics_def {
-        add_assoc_type_bindings(db, &mut res, tr, arg_list);
+        add_assoc_type_bindings(db, &mut res, tr, arg_list, edition);
     }
     res.signature.push('>');
 
@@ -355,6 +359,7 @@ fn add_assoc_type_bindings(
     res: &mut SignatureHelp,
     tr: Trait,
     args: ast::GenericArgList,
+    edition: Edition,
 ) {
     if args.syntax().ancestors().find_map(ast::TypeBound::cast).is_none() {
         // Assoc type bindings are only valid in type bound position.
@@ -378,7 +383,7 @@ fn add_assoc_type_bindings(
 
     for item in tr.items_with_supertraits(db) {
         if let AssocItem::TypeAlias(ty) = item {
-            let name = ty.name(db).display_no_db().to_smolstr();
+            let name = ty.name(db).display_no_db(edition).to_smolstr();
             if !present_bindings.contains(&*name) {
                 buf.clear();
                 format_to!(buf, "{} = …", name);
@@ -392,6 +397,7 @@ fn signature_help_for_record_lit(
     sema: &Semantics<'_, RootDatabase>,
     record: ast::RecordExpr,
     token: SyntaxToken,
+    edition: Edition,
 ) -> Option<SignatureHelp> {
     signature_help_for_record_(
         sema,
@@ -403,6 +409,7 @@ fn signature_help_for_record_lit(
             .filter_map(|field| sema.resolve_record_field(&field))
             .map(|(field, _, ty)| (field, ty)),
         token,
+        edition,
     )
 }
 
@@ -410,6 +417,7 @@ fn signature_help_for_record_pat(
     sema: &Semantics<'_, RootDatabase>,
     record: ast::RecordPat,
     token: SyntaxToken,
+    edition: Edition,
 ) -> Option<SignatureHelp> {
     signature_help_for_record_(
         sema,
@@ -420,6 +428,7 @@ fn signature_help_for_record_pat(
             .fields()
             .filter_map(|field| sema.resolve_record_pat_field(&field)),
         token,
+        edition,
     )
 }
 
@@ -427,6 +436,7 @@ fn signature_help_for_tuple_struct_pat(
     sema: &Semantics<'_, RootDatabase>,
     pat: ast::TupleStructPat,
     token: SyntaxToken,
+    edition: Edition,
 ) -> Option<SignatureHelp> {
     let path = pat.path()?;
     let path_res = sema.resolve_path(&path)?;
@@ -445,8 +455,8 @@ fn signature_help_for_tuple_struct_pat(
         format_to!(
             res.signature,
             "enum {}::{} (",
-            en.name(db).display(db),
-            variant.name(db).display(db)
+            en.name(db).display(db, edition),
+            variant.name(db).display(db, edition)
         );
         variant.fields(db)
     } else {
@@ -459,7 +469,7 @@ fn signature_help_for_tuple_struct_pat(
         match adt {
             hir::Adt::Struct(it) => {
                 res.doc = it.docs(db);
-                format_to!(res.signature, "struct {} (", it.name(db).display(db));
+                format_to!(res.signature, "struct {} (", it.name(db).display(db, edition));
                 it.fields(db)
             }
             _ => return None,
@@ -472,6 +482,7 @@ fn signature_help_for_tuple_struct_pat(
         token,
         pat.fields(),
         fields.into_iter().map(|it| it.ty(db)),
+        edition,
     ))
 }
 
@@ -479,6 +490,7 @@ fn signature_help_for_tuple_pat(
     sema: &Semantics<'_, RootDatabase>,
     pat: ast::TuplePat,
     token: SyntaxToken,
+    edition: Edition,
 ) -> Option<SignatureHelp> {
     let db = sema.db;
     let field_pats = pat.fields();
@@ -498,6 +510,7 @@ fn signature_help_for_tuple_pat(
         token,
         field_pats,
         fields.into_iter(),
+        edition,
     ))
 }
 
@@ -505,6 +518,7 @@ fn signature_help_for_tuple_expr(
     sema: &Semantics<'_, RootDatabase>,
     expr: ast::TupleExpr,
     token: SyntaxToken,
+    edition: Edition,
 ) -> Option<SignatureHelp> {
     let active_parameter = Some(
         expr.syntax()
@@ -526,7 +540,7 @@ fn signature_help_for_tuple_expr(
     let fields = expr.original.tuple_fields(db);
     let mut buf = String::new();
     for ty in fields {
-        format_to!(buf, "{}", ty.display_truncated(db, Some(20)));
+        format_to!(buf, "{}", ty.display_truncated(db, Some(20), edition));
         res.push_call_param(&buf);
         buf.clear();
     }
@@ -540,6 +554,7 @@ fn signature_help_for_record_(
     path: &ast::Path,
     fields2: impl Iterator<Item = (hir::Field, hir::Type)>,
     token: SyntaxToken,
+    edition: Edition,
 ) -> Option<SignatureHelp> {
     let active_parameter = field_list_children
         .filter_map(NodeOrToken::into_token)
@@ -566,8 +581,8 @@ fn signature_help_for_record_(
         format_to!(
             res.signature,
             "enum {}::{} {{ ",
-            en.name(db).display(db),
-            variant.name(db).display(db)
+            en.name(db).display(db, edition),
+            variant.name(db).display(db, edition)
         );
     } else {
         let adt = match path_res {
@@ -580,12 +595,12 @@ fn signature_help_for_record_(
             hir::Adt::Struct(it) => {
                 fields = it.fields(db);
                 res.doc = it.docs(db);
-                format_to!(res.signature, "struct {} {{ ", it.name(db).display(db));
+                format_to!(res.signature, "struct {} {{ ", it.name(db).display(db, edition));
             }
             hir::Adt::Union(it) => {
                 fields = it.fields(db);
                 res.doc = it.docs(db);
-                format_to!(res.signature, "union {} {{ ", it.name(db).display(db));
+                format_to!(res.signature, "union {} {{ ", it.name(db).display(db, edition));
             }
             _ => return None,
         }
@@ -596,7 +611,12 @@ fn signature_help_for_record_(
     let mut buf = String::new();
     for (field, ty) in fields2 {
         let name = field.name(db);
-        format_to!(buf, "{}: {}", name.display(db), ty.display_truncated(db, Some(20)));
+        format_to!(
+            buf,
+            "{}: {}",
+            name.display(db, edition),
+            ty.display_truncated(db, Some(20), edition)
+        );
         res.push_record_field(&buf);
         buf.clear();
 
@@ -606,7 +626,12 @@ fn signature_help_for_record_(
     }
     for (name, field) in fields {
         let Some(field) = field else { continue };
-        format_to!(buf, "{}: {}", name.display(db), field.ty(db).display_truncated(db, Some(20)));
+        format_to!(
+            buf,
+            "{}: {}",
+            name.display(db, edition),
+            field.ty(db).display_truncated(db, Some(20), edition)
+        );
         res.push_record_field(&buf);
         buf.clear();
     }
@@ -621,6 +646,7 @@ fn signature_help_for_tuple_pat_ish(
     token: SyntaxToken,
     mut field_pats: AstChildren<ast::Pat>,
     fields: impl ExactSizeIterator<Item = hir::Type>,
+    edition: Edition,
 ) -> SignatureHelp {
     let rest_pat = field_pats.find(|it| matches!(it, ast::Pat::RestPat(_)));
     let is_left_of_rest_pat =
@@ -647,7 +673,7 @@ fn signature_help_for_tuple_pat_ish(
 
     let mut buf = String::new();
     for ty in fields {
-        format_to!(buf, "{}", ty.display_truncated(db, Some(20)));
+        format_to!(buf, "{}", ty.display_truncated(db, Some(20), edition));
         res.push_call_param(&buf);
         buf.clear();
     }
diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
index eaccee08e8c01..1cbe8c62a812f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
@@ -10,6 +10,7 @@ use ide_db::{
     helpers::get_definition,
     FileId, FileRange, FxHashMap, FxHashSet, RootDatabase,
 };
+use span::Edition;
 use syntax::{AstNode, SyntaxKind::*, SyntaxNode, TextRange, T};
 
 use crate::inlay_hints::InlayFieldsToResolve;
@@ -116,7 +117,16 @@ fn documentation_for_definition(
         _ => None,
     };
 
-    def.docs(sema.db, famous_defs.as_ref())
+    def.docs(
+        sema.db,
+        famous_defs.as_ref(),
+        def.krate(sema.db).map(|it| it.edition(sema.db)).unwrap_or(Edition::CURRENT),
+    )
+}
+
+pub enum VendoredLibrariesConfig<'a> {
+    Included { workspace_root: &'a VfsPath },
+    Excluded,
 }
 
 impl StaticIndex<'_> {
@@ -161,6 +171,8 @@ impl StaticIndex<'_> {
         // hovers
         let sema = hir::Semantics::new(self.db);
         let tokens_or_nodes = sema.parse_guess_edition(file_id).syntax().clone();
+        let edition =
+            sema.attach_first_edition(file_id).map(|it| it.edition()).unwrap_or(Edition::CURRENT);
         let tokens = tokens_or_nodes.descendants_with_tokens().filter_map(|it| match it {
             syntax::NodeOrToken::Node(_) => None,
             syntax::NodeOrToken::Token(it) => Some(it),
@@ -201,17 +213,20 @@ impl StaticIndex<'_> {
                         &node,
                         None,
                         &hover_config,
+                        edition,
                     )),
                     definition: def.try_to_nav(self.db).map(UpmappingResult::call_site).map(|it| {
                         FileRange { file_id: it.file_id, range: it.focus_or_full_range() }
                     }),
                     references: vec![],
                     moniker: current_crate.and_then(|cc| def_to_moniker(self.db, def, cc)),
-                    display_name: def.name(self.db).map(|name| name.display(self.db).to_string()),
+                    display_name: def
+                        .name(self.db)
+                        .map(|name| name.display(self.db, edition).to_string()),
                     enclosing_moniker: current_crate
                         .zip(def.enclosing_definition(self.db))
                         .and_then(|(cc, enclosing_def)| def_to_moniker(self.db, enclosing_def, cc)),
-                    signature: Some(def.label(self.db)),
+                    signature: Some(def.label(self.db, edition)),
                     kind: def_to_kind(self.db, def),
                 });
                 self.def_map.insert(def, it);
@@ -230,15 +245,22 @@ impl StaticIndex<'_> {
         self.files.push(result);
     }
 
-    pub fn compute<'a>(analysis: &'a Analysis, workspace_root: &VfsPath) -> StaticIndex<'a> {
+    pub fn compute<'a>(
+        analysis: &'a Analysis,
+        vendored_libs_config: VendoredLibrariesConfig<'_>,
+    ) -> StaticIndex<'a> {
         let db = &*analysis.db;
         let work = all_modules(db).into_iter().filter(|module| {
             let file_id = module.definition_source_file_id(db).original_file(db);
             let source_root = db.file_source_root(file_id.into());
             let source_root = db.source_root(source_root);
-            let is_vendored = source_root
-                .path_for_file(&file_id.into())
-                .is_some_and(|module_path| module_path.starts_with(workspace_root));
+            let is_vendored = match vendored_libs_config {
+                VendoredLibrariesConfig::Included { workspace_root } => source_root
+                    .path_for_file(&file_id.into())
+                    .is_some_and(|module_path| module_path.starts_with(workspace_root)),
+                VendoredLibrariesConfig::Excluded => false,
+            };
+
             !source_root.is_library || is_vendored
         });
         let mut this = StaticIndex {
@@ -268,10 +290,11 @@ mod tests {
     use ide_db::{base_db::VfsPath, FileRange, FxHashSet};
     use syntax::TextSize;
 
-    fn check_all_ranges(ra_fixture: &str) {
+    use super::VendoredLibrariesConfig;
+
+    fn check_all_ranges(ra_fixture: &str, vendored_libs_config: VendoredLibrariesConfig<'_>) {
         let (analysis, ranges) = fixture::annotations_without_marker(ra_fixture);
-        let s =
-            StaticIndex::compute(&analysis, &VfsPath::new_virtual_path("/workspace".to_owned()));
+        let s = StaticIndex::compute(&analysis, vendored_libs_config);
         let mut range_set: FxHashSet<_> = ranges.iter().map(|it| it.0).collect();
         for f in s.files {
             for (range, _) in f.tokens {
@@ -288,10 +311,9 @@ mod tests {
     }
 
     #[track_caller]
-    fn check_definitions(ra_fixture: &str) {
+    fn check_definitions(ra_fixture: &str, vendored_libs_config: VendoredLibrariesConfig<'_>) {
         let (analysis, ranges) = fixture::annotations_without_marker(ra_fixture);
-        let s =
-            StaticIndex::compute(&analysis, &VfsPath::new_virtual_path("/workspace".to_owned()));
+        let s = StaticIndex::compute(&analysis, vendored_libs_config);
         let mut range_set: FxHashSet<_> = ranges.iter().map(|it| it.0).collect();
         for (_, t) in s.tokens.iter() {
             if let Some(t) = t.definition {
@@ -319,6 +341,9 @@ struct Foo;
 enum E { X(Foo) }
    //^   ^ ^^^
 "#,
+            VendoredLibrariesConfig::Included {
+                workspace_root: &VfsPath::new_virtual_path("/workspace".to_owned()),
+            },
         );
         check_definitions(
             r#"
@@ -327,6 +352,9 @@ struct Foo;
 enum E { X(Foo) }
    //^   ^
 "#,
+            VendoredLibrariesConfig::Included {
+                workspace_root: &VfsPath::new_virtual_path("/workspace".to_owned()),
+            },
         );
     }
 
@@ -349,6 +377,9 @@ pub func() {
 
 }
 "#,
+            VendoredLibrariesConfig::Included {
+                workspace_root: &VfsPath::new_virtual_path("/workspace".to_owned()),
+            },
         );
     }
 
@@ -367,9 +398,30 @@ struct ExternalLibrary(i32);
 struct VendoredLibrary(i32);
      //^^^^^^^^^^^^^^^ ^^^
 "#,
+            VendoredLibrariesConfig::Included {
+                workspace_root: &VfsPath::new_virtual_path("/workspace".to_owned()),
+            },
         );
     }
 
+    #[test]
+    fn vendored_crate_excluded() {
+        check_all_ranges(
+            r#"
+//- /workspace/main.rs crate:main deps:external,vendored
+struct Main(i32);
+     //^^^^ ^^^
+
+//- /external/lib.rs new_source_root:library crate:external@0.1.0,https://a.b/foo.git library
+struct ExternalLibrary(i32);
+
+//- /workspace/vendored/lib.rs new_source_root:library crate:vendored@0.1.0,https://a.b/bar.git library
+struct VendoredLibrary(i32);
+"#,
+            VendoredLibrariesConfig::Excluded,
+        )
+    }
+
     #[test]
     fn derives() {
         check_all_ranges(
@@ -384,6 +436,9 @@ pub macro Copy {}
 struct Hello(i32);
      //^^^^^ ^^^
 "#,
+            VendoredLibrariesConfig::Included {
+                workspace_root: &VfsPath::new_virtual_path("/workspace".to_owned()),
+            },
         );
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
index 23185920058b7..927fdaa178ca2 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
@@ -13,7 +13,9 @@ mod html;
 #[cfg(test)]
 mod tests;
 
-use hir::{DescendPreference, Name, Semantics};
+use std::ops::ControlFlow;
+
+use hir::{InRealFile, Name, Semantics};
 use ide_db::{FxHashMap, RootDatabase, SymbolKind};
 use span::EditionedFileId;
 use syntax::{
@@ -399,19 +401,55 @@ fn traverse(
             // Attempt to descend tokens into macro-calls.
             let res = match element {
                 NodeOrToken::Token(token) if token.kind() != COMMENT => {
-                    let token = if token.kind() == STRING {
-                        // for strings, try to prefer a string that has not been lost in a token
-                        // tree
-                        // FIXME: This should be done for everything, but check perf first
-                        sema.descend_into_macros(DescendPreference::SameKind, token)
-                            .into_iter()
-                            .max_by_key(|it| {
-                                it.parent().map_or(false, |it| it.kind() != TOKEN_TREE)
-                            })
-                            .unwrap()
-                    } else {
-                        sema.descend_into_macros_single(DescendPreference::SameKind, token)
-                    };
+                    let kind = token.kind();
+                    let text = token.text();
+                    let ident_kind = kind.is_any_identifier();
+
+                    let mut t = None;
+                    let mut r = 0;
+                    sema.descend_into_macros_breakable(
+                        InRealFile::new(file_id, token.clone()),
+                        |tok| {
+                            let tok = tok.value;
+                            let tok_kind = tok.kind();
+
+                            let exact_same_kind = tok_kind == kind;
+                            let both_idents =
+                                exact_same_kind || (tok_kind.is_any_identifier() && ident_kind);
+                            let same_text = tok.text() == text;
+                            // anything that mapped into a token tree has likely no semantic information
+                            let no_tt_parent =
+                                tok.parent().map_or(false, |it| it.kind() != TOKEN_TREE);
+                            let my_rank = (both_idents as usize)
+                                | ((exact_same_kind as usize) << 1)
+                                | ((same_text as usize) << 2)
+                                | ((no_tt_parent as usize) << 3);
+
+                            if my_rank > 0b1110 {
+                                // a rank of 0b1110 means that we have found a maximally interesting
+                                // token so stop early.
+                                t = Some(tok);
+                                return ControlFlow::Break(());
+                            }
+
+                            // r = r.max(my_rank);
+                            // t = Some(t.take_if(|_| r < my_rank).unwrap_or(tok));
+                            match &mut t {
+                                Some(prev) if r < my_rank => {
+                                    *prev = tok;
+                                    r = my_rank;
+                                }
+                                Some(_) => (),
+                                None => {
+                                    r = my_rank;
+                                    t = Some(tok)
+                                }
+                            }
+                            ControlFlow::Continue(())
+                        },
+                    );
+
+                    let token = t.unwrap_or(token);
                     match token.parent().and_then(ast::NameLike::cast) {
                         // Remap the token into the wrapping single token nodes
                         Some(parent) => match (token.kind(), parent.syntax().kind()) {
@@ -501,7 +539,9 @@ fn traverse(
                 config.syntactic_name_ref_highlighting,
                 name_like,
             ),
-            NodeOrToken::Token(token) => highlight::token(sema, token).zip(Some(None)),
+            NodeOrToken::Token(token) => {
+                highlight::token(sema, token, file_id.edition()).zip(Some(None))
+            }
         };
         if let Some((mut highlight, binding_hash)) = element {
             if is_unlinked && highlight.tag == HlTag::UnresolvedReference {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
index 291073f87735c..eeba9cf35c997 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
@@ -6,6 +6,7 @@ use ide_db::{
     defs::{Definition, IdentClass, NameClass, NameRefClass},
     FxHashMap, RootDatabase, SymbolKind,
 };
+use span::Edition;
 use stdx::hash_once;
 use syntax::{
     ast, match_ast, AstNode, AstToken, NodeOrToken,
@@ -18,7 +19,11 @@ use crate::{
     Highlight, HlMod, HlTag,
 };
 
-pub(super) fn token(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> Option<Highlight> {
+pub(super) fn token(
+    sema: &Semantics<'_, RootDatabase>,
+    token: SyntaxToken,
+    edition: Edition,
+) -> Option<Highlight> {
     if let Some(comment) = ast::Comment::cast(token.clone()) {
         let h = HlTag::Comment;
         return Some(match comment.kind().doc {
@@ -41,7 +46,7 @@ pub(super) fn token(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> O
             HlTag::None.into()
         }
         p if p.is_punct() => punctuation(sema, token, p),
-        k if k.is_keyword() => keyword(sema, token, k)?,
+        k if k.is_keyword(edition) => keyword(sema, token, k)?,
         _ => return None,
     };
     Some(highlight)
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/macro_.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/macro_.rs
index 1099d9c23b721..b441b4cc90eb6 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/macro_.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/macro_.rs
@@ -117,7 +117,7 @@ fn update_macro_state(state: &mut MacroMatcherParseState, tok: &SyntaxToken) {
 
 fn is_metavariable(token: &SyntaxToken) -> Option<TextRange> {
     match token.kind() {
-        kind if kind == SyntaxKind::IDENT || kind.is_keyword() => {
+        kind if kind.is_any_identifier() => {
             if let Some(_dollar) = token.prev_token().filter(|t| t.kind() == T![$]) {
                 return Some(token.text_range());
             }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
index 17411fefbd978..196552020ab66 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
@@ -100,7 +100,7 @@
     <span class="brace">}</span><span class="semicolon">;</span>
 <span class="brace">}</span>
 
-<span class="macro default_library library">include</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="macro default_library library macro">concat</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"foo/"</span><span class="string_literal macro">,</span> <span class="string_literal macro">"foo.rs"</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro default_library library">include</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="macro default_library library macro">concat</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"foo/"</span><span class="comma macro">,</span> <span class="string_literal macro">"foo.rs"</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 
 <span class="keyword">struct</span> <span class="struct declaration">S</span><span class="angle">&lt;</span><span class="type_param declaration">T</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="type_param">T</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
index dae79998dc4eb..a6352b99d4f52 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
@@ -16,5 +16,5 @@ pub(crate) fn view_item_tree(db: &RootDatabase, file_id: FileId) -> String {
     let file_id = sema
         .attach_first_edition(file_id)
         .unwrap_or_else(|| EditionedFileId::current_edition(file_id));
-    db.file_item_tree(file_id.into()).pretty_print(db)
+    db.file_item_tree(file_id.into()).pretty_print(db, file_id.edition())
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs
index df3f2f18b4cdf..830c39e21ea53 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs
@@ -6,6 +6,7 @@ use ide_db::{
     helpers::{get_definition, pick_best_token},
     RootDatabase,
 };
+use span::Edition;
 use syntax::{AstNode, SyntaxKind};
 
 use crate::FilePosition;
@@ -85,6 +86,10 @@ pub(crate) fn view_memory_layout(
 ) -> Option<RecursiveMemoryLayout> {
     let sema = Semantics::new(db);
     let file = sema.parse_guess_edition(position.file_id);
+    let edition = sema
+        .attach_first_edition(position.file_id)
+        .map(|it| it.edition())
+        .unwrap_or(Edition::CURRENT);
     let token =
         pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind {
             SyntaxKind::IDENT => 3,
@@ -111,6 +116,7 @@ pub(crate) fn view_memory_layout(
         ty: &Type,
         layout: &Layout,
         parent_idx: usize,
+        edition: Edition,
     ) {
         let mut fields = ty
             .fields(db)
@@ -141,7 +147,7 @@ pub(crate) fn view_memory_layout(
             if let Ok(child_layout) = child_ty.layout(db) {
                 nodes.push(MemoryLayoutNode {
                     item_name: field.name(db),
-                    typename: child_ty.display(db).to_string(),
+                    typename: child_ty.display(db, edition).to_string(),
                     size: child_layout.size(),
                     alignment: child_layout.align(),
                     offset: match *field {
@@ -157,7 +163,7 @@ pub(crate) fn view_memory_layout(
                     item_name: field.name(db)
                         + format!("(no layout data: {:?})", child_ty.layout(db).unwrap_err())
                             .as_ref(),
-                    typename: child_ty.display(db).to_string(),
+                    typename: child_ty.display(db, edition).to_string(),
                     size: 0,
                     offset: 0,
                     alignment: 0,
@@ -170,7 +176,7 @@ pub(crate) fn view_memory_layout(
 
         for (i, (_, child_ty)) in fields.iter().enumerate() {
             if let Ok(child_layout) = child_ty.layout(db) {
-                read_layout(nodes, db, child_ty, &child_layout, children_start + i);
+                read_layout(nodes, db, child_ty, &child_layout, children_start + i, edition);
             }
         }
     }
@@ -188,7 +194,7 @@ pub(crate) fn view_memory_layout(
                 def => def.name(db).map(|n| n.as_str().to_owned()).unwrap_or("[ROOT]".to_owned()),
             };
 
-            let typename = ty.display(db).to_string();
+            let typename = ty.display(db, edition).to_string();
 
             let mut nodes = vec![MemoryLayoutNode {
                 item_name,
@@ -200,7 +206,7 @@ pub(crate) fn view_memory_layout(
                 children_start: -1,
                 children_len: 0,
             }];
-            read_layout(&mut nodes, db, &ty, &layout, 0);
+            read_layout(&mut nodes, db, &ty, &layout, 0, edition);
 
             RecursiveMemoryLayout { nodes }
         })
diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
index 2feca32ff8686..7eb8e4a5e2e52 100644
--- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
+++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
@@ -239,6 +239,7 @@ define_symbols! {
     fundamental,
     future_trait,
     future,
+    future_output,
     Future,
     ge,
     get_context,
@@ -273,6 +274,7 @@ define_symbols! {
     iter_mut,
     iter,
     Iterator,
+    iterator,
     keyword,
     lang,
     le,
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_args.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_args.rs
index 249be2a333559..c62c8a9d3f929 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_args.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_args.rs
@@ -102,13 +102,18 @@ fn generic_arg(p: &mut Parser<'_>) -> bool {
         IDENT if p.nth_at(1, T!['(']) => {
             let m = p.start();
             name_ref(p);
-            params::param_list_fn_trait(p);
-            if p.at(T![:]) && !p.at(T![::]) {
-                // test associated_return_type_bounds
-                // fn foo<T: Foo<foo(): Send, bar(i32): Send, baz(i32, i32): Send>>() {}
+            if p.nth_at(1, T![..]) {
+                let rtn = p.start();
+                p.bump(T!['(']);
+                p.bump(T![..]);
+                p.expect(T![')']);
+                rtn.complete(p, RETURN_TYPE_SYNTAX);
+                // test return_type_syntax_assoc_type_bound
+                // fn foo<T: Trait<method(..): Send>>() {}
                 generic_params::bounds(p);
                 m.complete(p, ASSOC_TYPE_ARG);
             } else {
+                params::param_list_fn_trait(p);
                 // test bare_dyn_types_with_paren_as_generic_args
                 // type A = S<Fn(i32)>;
                 // type A = S<Fn(i32) + Send>;
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs
index cf80a535ac55f..e0fa753fa70a4 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs
@@ -119,8 +119,7 @@ fn lifetime_bounds(p: &mut Parser<'_>) {
 // test type_param_bounds
 // struct S<T: 'a + ?Sized + (Copy) + ~const Drop>;
 pub(super) fn bounds(p: &mut Parser<'_>) {
-    assert!(p.at(T![:]));
-    p.bump(T![:]);
+    p.expect(T![:]);
     bounds_without_colon(p);
 }
 
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs
index 01b8f9e918714..09db921803f9b 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs
@@ -140,11 +140,24 @@ fn opt_path_type_args(p: &mut Parser<'_>, mode: Mode) {
             if p.at(T![::]) && p.nth_at(2, T!['(']) {
                 p.bump(T![::]);
             }
-            // test path_fn_trait_args
-            // type F = Box<Fn(i32) -> ()>;
             if p.at(T!['(']) {
-                params::param_list_fn_trait(p);
-                opt_ret_type(p);
+                if p.nth_at(1, T![..]) {
+                    // test return_type_syntax_in_path
+                    // fn foo<T>()
+                    // where
+                    //     T::method(..): Send,
+                    // {}
+                    let rtn = p.start();
+                    p.bump(T!['(']);
+                    p.bump(T![..]);
+                    p.expect(T![')']);
+                    rtn.complete(p, RETURN_TYPE_SYNTAX);
+                } else {
+                    // test path_fn_trait_args
+                    // type F = Box<Fn(i32) -> ()>;
+                    params::param_list_fn_trait(p);
+                    opt_ret_type(p);
+                }
             } else {
                 generic_args::opt_generic_arg_list(p, false);
             }
diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
index 13fc61074d091..ff924830ae017 100644
--- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
@@ -178,19 +178,8 @@ impl<'a> Converter<'a> {
                 rustc_lexer::TokenKind::Whitespace => WHITESPACE,
 
                 rustc_lexer::TokenKind::Ident if token_text == "_" => UNDERSCORE,
-                rustc_lexer::TokenKind::Ident
-                    if ["async", "await", "dyn", "try"].contains(&token_text)
-                        && !self.edition.at_least_2018() =>
-                {
-                    IDENT
-                }
-                rustc_lexer::TokenKind::Ident
-                    if token_text == "gen" && !self.edition.at_least_2024() =>
-                {
-                    IDENT
-                }
                 rustc_lexer::TokenKind::Ident => {
-                    SyntaxKind::from_keyword(token_text).unwrap_or(IDENT)
+                    SyntaxKind::from_keyword(token_text, self.edition).unwrap_or(IDENT)
                 }
                 rustc_lexer::TokenKind::InvalidPrefix | rustc_lexer::TokenKind::InvalidIdent => {
                     err = "Ident contains invalid characters";
diff --git a/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs b/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs
index 1cf81e79b03cf..7adedba7c4382 100644
--- a/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs
@@ -35,12 +35,10 @@ impl LexedStr<'_> {
                 was_joint = false
             } else if kind == SyntaxKind::IDENT {
                 let token_text = self.text(i);
-                let contextual_kw = if !edition.at_least_2018() && token_text == "dyn" {
-                    SyntaxKind::DYN_KW
-                } else {
-                    SyntaxKind::from_contextual_keyword(token_text).unwrap_or(SyntaxKind::IDENT)
-                };
-                res.push_ident(contextual_kw);
+                res.push_ident(
+                    SyntaxKind::from_contextual_keyword(token_text, edition)
+                        .unwrap_or(SyntaxKind::IDENT),
+                )
             } else {
                 if was_joint {
                     res.was_joint();
diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind.rs
index 3ca6bd4cb111c..6a8cca9ccc79d 100644
--- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind.rs
@@ -3,6 +3,8 @@
 
 mod generated;
 
+use crate::Edition;
+
 #[allow(unreachable_pub)]
 pub use self::generated::SyntaxKind;
 
@@ -26,4 +28,11 @@ impl SyntaxKind {
     pub fn is_trivia(self) -> bool {
         matches!(self, SyntaxKind::WHITESPACE | SyntaxKind::COMMENT)
     }
+
+    /// Returns true if this is an identifier or a keyword.
+    #[inline]
+    pub fn is_any_identifier(self) -> bool {
+        // Assuming no edition removed keywords...
+        self == SyntaxKind::IDENT || self.is_keyword(Edition::LATEST)
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
index 7bddf88740136..00f212487ae68 100644
--- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
@@ -1,6 +1,7 @@
 //! Generated by `cargo codegen grammar`, do not edit by hand.
 
 #![allow(bad_style, missing_docs, unreachable_pub)]
+use crate::Edition;
 #[doc = r" The kind of syntax node, e.g. `IDENT`, `USE_KW`, or `STRUCT`."]
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
 #[repr(u16)]
@@ -64,8 +65,6 @@ pub enum SyntaxKind {
     SELF_TYPE_KW,
     ABSTRACT_KW,
     AS_KW,
-    ASYNC_KW,
-    AWAIT_KW,
     BECOME_KW,
     BOX_KW,
     BREAK_KW,
@@ -73,7 +72,6 @@ pub enum SyntaxKind {
     CONTINUE_KW,
     CRATE_KW,
     DO_KW,
-    DYN_KW,
     ELSE_KW,
     ENUM_KW,
     EXTERN_KW,
@@ -81,7 +79,6 @@ pub enum SyntaxKind {
     FINAL_KW,
     FN_KW,
     FOR_KW,
-    GEN_KW,
     IF_KW,
     IMPL_KW,
     IN_KW,
@@ -103,7 +100,6 @@ pub enum SyntaxKind {
     SUPER_KW,
     TRAIT_KW,
     TRUE_KW,
-    TRY_KW,
     TYPE_KW,
     TYPEOF_KW,
     UNSAFE_KW,
@@ -114,13 +110,18 @@ pub enum SyntaxKind {
     WHILE_KW,
     YIELD_KW,
     ASM_KW,
+    ASYNC_KW,
     AUTO_KW,
+    AWAIT_KW,
     BUILTIN_KW,
     DEFAULT_KW,
+    DYN_KW,
     FORMAT_ARGS_KW,
+    GEN_KW,
     MACRO_RULES_KW,
     OFFSET_OF_KW,
     RAW_KW,
+    TRY_KW,
     UNION_KW,
     YEET_KW,
     BYTE,
@@ -252,6 +253,7 @@ pub enum SyntaxKind {
     RENAME,
     REST_PAT,
     RETURN_EXPR,
+    RETURN_TYPE_SYNTAX,
     RET_TYPE,
     SELF_PARAM,
     SLICE_PAT,
@@ -296,14 +298,14 @@ pub enum SyntaxKind {
 }
 use self::SyntaxKind::*;
 impl SyntaxKind {
-    pub fn is_keyword(self) -> bool {
+    #[doc = r" Checks whether this syntax kind is a strict keyword for the given edition."]
+    #[doc = r" Strict keywords are identifiers that are always considered keywords."]
+    pub fn is_strict_keyword(self, edition: Edition) -> bool {
         matches!(
             self,
             SELF_TYPE_KW
                 | ABSTRACT_KW
                 | AS_KW
-                | ASYNC_KW
-                | AWAIT_KW
                 | BECOME_KW
                 | BOX_KW
                 | BREAK_KW
@@ -311,7 +313,6 @@ impl SyntaxKind {
                 | CONTINUE_KW
                 | CRATE_KW
                 | DO_KW
-                | DYN_KW
                 | ELSE_KW
                 | ENUM_KW
                 | EXTERN_KW
@@ -319,7 +320,6 @@ impl SyntaxKind {
                 | FINAL_KW
                 | FN_KW
                 | FOR_KW
-                | GEN_KW
                 | IF_KW
                 | IMPL_KW
                 | IN_KW
@@ -341,7 +341,6 @@ impl SyntaxKind {
                 | SUPER_KW
                 | TRAIT_KW
                 | TRUE_KW
-                | TRY_KW
                 | TYPE_KW
                 | TYPEOF_KW
                 | UNSAFE_KW
@@ -351,17 +350,103 @@ impl SyntaxKind {
                 | WHERE_KW
                 | WHILE_KW
                 | YIELD_KW
-                | ASM_KW
-                | AUTO_KW
-                | BUILTIN_KW
-                | DEFAULT_KW
-                | FORMAT_ARGS_KW
-                | MACRO_RULES_KW
-                | OFFSET_OF_KW
-                | RAW_KW
-                | UNION_KW
-                | YEET_KW
-        )
+        ) || match self {
+            ASYNC_KW if Edition::Edition2018 <= edition => true,
+            AWAIT_KW if Edition::Edition2018 <= edition => true,
+            DYN_KW if Edition::Edition2018 <= edition => true,
+            GEN_KW if Edition::Edition2024 <= edition => true,
+            TRY_KW if Edition::Edition2018 <= edition => true,
+            _ => false,
+        }
+    }
+    #[doc = r" Checks whether this syntax kind is a weak keyword for the given edition."]
+    #[doc = r" Weak keywords are identifiers that are considered keywords only in certain contexts."]
+    pub fn is_contextual_keyword(self, edition: Edition) -> bool {
+        match self {
+            ASM_KW => true,
+            AUTO_KW => true,
+            BUILTIN_KW => true,
+            DEFAULT_KW => true,
+            DYN_KW if edition < Edition::Edition2018 => true,
+            FORMAT_ARGS_KW => true,
+            MACRO_RULES_KW => true,
+            OFFSET_OF_KW => true,
+            RAW_KW => true,
+            UNION_KW => true,
+            YEET_KW => true,
+            _ => false,
+        }
+    }
+    #[doc = r" Checks whether this syntax kind is a strict or weak keyword for the given edition."]
+    pub fn is_keyword(self, edition: Edition) -> bool {
+        matches!(
+            self,
+            SELF_TYPE_KW
+                | ABSTRACT_KW
+                | AS_KW
+                | BECOME_KW
+                | BOX_KW
+                | BREAK_KW
+                | CONST_KW
+                | CONTINUE_KW
+                | CRATE_KW
+                | DO_KW
+                | ELSE_KW
+                | ENUM_KW
+                | EXTERN_KW
+                | FALSE_KW
+                | FINAL_KW
+                | FN_KW
+                | FOR_KW
+                | IF_KW
+                | IMPL_KW
+                | IN_KW
+                | LET_KW
+                | LOOP_KW
+                | MACRO_KW
+                | MATCH_KW
+                | MOD_KW
+                | MOVE_KW
+                | MUT_KW
+                | OVERRIDE_KW
+                | PRIV_KW
+                | PUB_KW
+                | REF_KW
+                | RETURN_KW
+                | SELF_KW
+                | STATIC_KW
+                | STRUCT_KW
+                | SUPER_KW
+                | TRAIT_KW
+                | TRUE_KW
+                | TYPE_KW
+                | TYPEOF_KW
+                | UNSAFE_KW
+                | UNSIZED_KW
+                | USE_KW
+                | VIRTUAL_KW
+                | WHERE_KW
+                | WHILE_KW
+                | YIELD_KW
+        ) || match self {
+            ASYNC_KW if Edition::Edition2018 <= edition => true,
+            AWAIT_KW if Edition::Edition2018 <= edition => true,
+            DYN_KW if Edition::Edition2018 <= edition => true,
+            GEN_KW if Edition::Edition2024 <= edition => true,
+            TRY_KW if Edition::Edition2018 <= edition => true,
+            ASM_KW => true,
+            AUTO_KW => true,
+            BUILTIN_KW => true,
+            DEFAULT_KW => true,
+            DYN_KW if edition < Edition::Edition2018 => true,
+            FORMAT_ARGS_KW => true,
+            MACRO_RULES_KW => true,
+            OFFSET_OF_KW => true,
+            RAW_KW => true,
+            UNION_KW => true,
+            YEET_KW => true,
+            _ => false,
+        }
     }
     pub fn is_punct(self) -> bool {
         matches!(
@@ -434,13 +519,11 @@ impl SyntaxKind {
                 | STRING
         )
     }
-    pub fn from_keyword(ident: &str) -> Option<SyntaxKind> {
+    pub fn from_keyword(ident: &str, edition: Edition) -> Option<SyntaxKind> {
         let kw = match ident {
             "Self" => SELF_TYPE_KW,
             "abstract" => ABSTRACT_KW,
             "as" => AS_KW,
-            "async" => ASYNC_KW,
-            "await" => AWAIT_KW,
             "become" => BECOME_KW,
             "box" => BOX_KW,
             "break" => BREAK_KW,
@@ -448,7 +531,6 @@ impl SyntaxKind {
             "continue" => CONTINUE_KW,
             "crate" => CRATE_KW,
             "do" => DO_KW,
-            "dyn" => DYN_KW,
             "else" => ELSE_KW,
             "enum" => ENUM_KW,
             "extern" => EXTERN_KW,
@@ -456,7 +538,6 @@ impl SyntaxKind {
             "final" => FINAL_KW,
             "fn" => FN_KW,
             "for" => FOR_KW,
-            "gen" => GEN_KW,
             "if" => IF_KW,
             "impl" => IMPL_KW,
             "in" => IN_KW,
@@ -478,7 +559,6 @@ impl SyntaxKind {
             "super" => SUPER_KW,
             "trait" => TRAIT_KW,
             "true" => TRUE_KW,
-            "try" => TRY_KW,
             "type" => TYPE_KW,
             "typeof" => TYPEOF_KW,
             "unsafe" => UNSAFE_KW,
@@ -488,16 +568,22 @@ impl SyntaxKind {
             "where" => WHERE_KW,
             "while" => WHILE_KW,
             "yield" => YIELD_KW,
+            "async" if Edition::Edition2018 <= edition => ASYNC_KW,
+            "await" if Edition::Edition2018 <= edition => AWAIT_KW,
+            "dyn" if Edition::Edition2018 <= edition => DYN_KW,
+            "gen" if Edition::Edition2024 <= edition => GEN_KW,
+            "try" if Edition::Edition2018 <= edition => TRY_KW,
             _ => return None,
         };
         Some(kw)
     }
-    pub fn from_contextual_keyword(ident: &str) -> Option<SyntaxKind> {
+    pub fn from_contextual_keyword(ident: &str, edition: Edition) -> Option<SyntaxKind> {
         let kw = match ident {
             "asm" => ASM_KW,
             "auto" => AUTO_KW,
             "builtin" => BUILTIN_KW,
             "default" => DEFAULT_KW,
+            "dyn" if edition < Edition::Edition2018 => DYN_KW,
             "format_args" => FORMAT_ARGS_KW,
             "macro_rules" => MACRO_RULES_KW,
             "offset_of" => OFFSET_OF_KW,
@@ -544,4 +630,4 @@ impl SyntaxKind {
     }
 }
 #[macro_export]
-macro_rules ! T { [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [abstract] => { $ crate :: SyntaxKind :: ABSTRACT_KW } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [become] => { $ crate :: SyntaxKind :: BECOME_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [final] => { $ crate :: SyntaxKind :: FINAL_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [gen] => { $ crate :: SyntaxKind :: GEN_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [override] => { $ crate :: SyntaxKind :: OVERRIDE_KW } ; [priv] => { $ crate :: SyntaxKind :: PRIV_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [typeof] => { $ crate :: SyntaxKind :: TYPEOF_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [unsized] => { $ crate :: SyntaxKind :: UNSIZED_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [virtual] => { $ crate :: SyntaxKind :: VIRTUAL_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [int_number] => { $ crate :: SyntaxKind :: INT_NUMBER } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [string] => { $ crate :: SyntaxKind :: STRING } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; }
+macro_rules ! T { [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [abstract] => { $ crate :: SyntaxKind :: ABSTRACT_KW } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [become] => { $ crate :: SyntaxKind :: BECOME_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [final] => { $ crate :: SyntaxKind :: FINAL_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [override] => { $ crate :: SyntaxKind :: OVERRIDE_KW } ; [priv] => { $ crate :: SyntaxKind :: PRIV_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [typeof] => { $ crate :: SyntaxKind :: TYPEOF_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [unsized] => { $ crate :: SyntaxKind :: UNSIZED_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [virtual] => { $ crate :: SyntaxKind :: VIRTUAL_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [gen] => { $ crate :: SyntaxKind :: GEN_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [int_number] => { $ crate :: SyntaxKind :: INT_NUMBER } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [string] => { $ crate :: SyntaxKind :: STRING } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; }
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
index 1907f03b44d11..9ce5a2ae748fd 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
@@ -37,10 +37,6 @@ mod ok {
     #[test]
     fn assoc_type_eq() { run_and_expect_no_errors("test_data/parser/inline/ok/assoc_type_eq.rs"); }
     #[test]
-    fn associated_return_type_bounds() {
-        run_and_expect_no_errors("test_data/parser/inline/ok/associated_return_type_bounds.rs");
-    }
-    #[test]
     fn associated_type_bounds() {
         run_and_expect_no_errors("test_data/parser/inline/ok/associated_type_bounds.rs");
     }
@@ -519,6 +515,16 @@ mod ok {
     #[test]
     fn return_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/return_expr.rs"); }
     #[test]
+    fn return_type_syntax_assoc_type_bound() {
+        run_and_expect_no_errors(
+            "test_data/parser/inline/ok/return_type_syntax_assoc_type_bound.rs",
+        );
+    }
+    #[test]
+    fn return_type_syntax_in_path() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/return_type_syntax_in_path.rs");
+    }
+    #[test]
     fn self_param() { run_and_expect_no_errors("test_data/parser/inline/ok/self_param.rs"); }
     #[test]
     fn self_param_outer_attr() {
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/associated_return_type_bounds.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/associated_return_type_bounds.rs
deleted file mode 100644
index 42029ac592702..0000000000000
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/associated_return_type_bounds.rs
+++ /dev/null
@@ -1 +0,0 @@
-fn foo<T: Foo<foo(): Send, bar(i32): Send, baz(i32, i32): Send>>() {}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_type_syntax_assoc_type_bound.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_type_syntax_assoc_type_bound.rast
new file mode 100644
index 0000000000000..30e0e73bbd6c0
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_type_syntax_assoc_type_bound.rast
@@ -0,0 +1,49 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "foo"
+    GENERIC_PARAM_LIST
+      L_ANGLE "<"
+      TYPE_PARAM
+        NAME
+          IDENT "T"
+        COLON ":"
+        WHITESPACE " "
+        TYPE_BOUND_LIST
+          TYPE_BOUND
+            PATH_TYPE
+              PATH
+                PATH_SEGMENT
+                  NAME_REF
+                    IDENT "Trait"
+                  GENERIC_ARG_LIST
+                    L_ANGLE "<"
+                    ASSOC_TYPE_ARG
+                      NAME_REF
+                        IDENT "method"
+                      RETURN_TYPE_SYNTAX
+                        L_PAREN "("
+                        DOT2 ".."
+                        R_PAREN ")"
+                      COLON ":"
+                      WHITESPACE " "
+                      TYPE_BOUND_LIST
+                        TYPE_BOUND
+                          PATH_TYPE
+                            PATH
+                              PATH_SEGMENT
+                                NAME_REF
+                                  IDENT "Send"
+                    R_ANGLE ">"
+      R_ANGLE ">"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        R_CURLY "}"
+  WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_type_syntax_assoc_type_bound.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_type_syntax_assoc_type_bound.rs
new file mode 100644
index 0000000000000..8a4cf4c3a07fa
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_type_syntax_assoc_type_bound.rs
@@ -0,0 +1 @@
+fn foo<T: Trait<method(..): Send>>() {}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_type_syntax_in_path.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_type_syntax_in_path.rast
new file mode 100644
index 0000000000000..501dccd79db21
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_type_syntax_in_path.rast
@@ -0,0 +1,50 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "foo"
+    GENERIC_PARAM_LIST
+      L_ANGLE "<"
+      TYPE_PARAM
+        NAME
+          IDENT "T"
+      R_ANGLE ">"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE "\n"
+    WHERE_CLAUSE
+      WHERE_KW "where"
+      WHITESPACE "\n    "
+      WHERE_PRED
+        PATH_TYPE
+          PATH
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "T"
+            COLON2 "::"
+            PATH_SEGMENT
+              NAME_REF
+                IDENT "method"
+              RETURN_TYPE_SYNTAX
+                L_PAREN "("
+                DOT2 ".."
+                R_PAREN ")"
+        COLON ":"
+        WHITESPACE " "
+        TYPE_BOUND_LIST
+          TYPE_BOUND
+            PATH_TYPE
+              PATH
+                PATH_SEGMENT
+                  NAME_REF
+                    IDENT "Send"
+      COMMA ","
+    WHITESPACE "\n"
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        R_CURLY "}"
+  WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_type_syntax_in_path.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_type_syntax_in_path.rs
new file mode 100644
index 0000000000000..a9b63fb01c859
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_type_syntax_in_path.rs
@@ -0,0 +1,4 @@
+fn foo<T>()
+where
+    T::method(..): Send,
+{}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
index 54c1475b8b1c8..d50a3cdbf72da 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
@@ -154,7 +154,8 @@ impl ProcMacro {
         mixed_site: Span,
     ) -> Result<Result<tt::Subtree<Span>, PanicMessage>, ServerError> {
         let version = self.process.version();
-        let current_dir = env.get("CARGO_MANIFEST_DIR");
+        let current_dir =
+            env.get("CARGO_RUSTC_CURRENT_DIR").or_else(|| env.get("CARGO_MANIFEST_DIR"));
 
         let mut span_data_table = SpanDataIndexMap::default();
         let def_site = span_data_table.insert_full(def_site).0;
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
index 552d99f51ba12..d508c19dd7195 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
@@ -142,7 +142,13 @@ impl server::TokenStream for RaSpanServer {
         stream.is_empty()
     }
     fn from_str(&mut self, src: &str) -> Self::TokenStream {
-        Self::TokenStream::from_str(src, self.call_site).expect("cannot parse string")
+        Self::TokenStream::from_str(src, self.call_site).unwrap_or_else(|e| {
+            Self::TokenStream::from_str(
+                &format!("compile_error!(\"failed to parse str to token stream: {e}\")"),
+                self.call_site,
+            )
+            .unwrap()
+        })
     }
     fn to_string(&mut self, stream: &Self::TokenStream) -> String {
         stream.to_string()
@@ -501,12 +507,17 @@ mod tests {
                         close: span,
                         kind: tt::DelimiterKind::Brace,
                     },
-                    token_trees: Box::new([]),
+                    token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
+                        kind: tt::LitKind::Str,
+                        symbol: Symbol::intern("string"),
+                        suffix: None,
+                        span,
+                    }))]),
                 }),
             ],
         };
 
-        assert_eq!(s.to_string(), "struct T {}");
+        assert_eq!(s.to_string(), "struct T {\"string\"}");
     }
 
     #[test]
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs
index 7720c6d83c38d..e478b1c853be7 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs
@@ -131,7 +131,13 @@ impl server::TokenStream for TokenIdServer {
         stream.is_empty()
     }
     fn from_str(&mut self, src: &str) -> Self::TokenStream {
-        Self::TokenStream::from_str(src, self.call_site).expect("cannot parse string")
+        Self::TokenStream::from_str(src, self.call_site).unwrap_or_else(|e| {
+            Self::TokenStream::from_str(
+                &format!("compile_error!(\"failed to parse str to token stream: {e}\")"),
+                self.call_site,
+            )
+            .unwrap()
+        })
     }
     fn to_string(&mut self, stream: &Self::TokenStream) -> String {
         stream.to_string()
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs
index 4d8d496418bf8..dbcb5a3143a65 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs
@@ -131,7 +131,7 @@ pub(super) mod token_stream {
                 call_site,
                 src,
             )
-            .ok_or("lexing error")?;
+            .ok_or_else(|| format!("lexing error: {src}"))?;
 
             Ok(TokenStream::with_subtree(subtree))
         }
diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs
index e7a4b8f39f7a5..dc71b13eeec69 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs
@@ -19,8 +19,8 @@ use serde::Deserialize;
 use toolchain::Tool;
 
 use crate::{
-    utf8_stdout, CargoConfig, CargoFeatures, CargoWorkspace, InvocationLocation,
-    InvocationStrategy, ManifestPath, Package, Sysroot, TargetKind,
+    utf8_stdout, CargoConfig, CargoFeatures, CargoWorkspace, InvocationStrategy, ManifestPath,
+    Package, Sysroot, TargetKind,
 };
 
 /// Output of the build script and proc-macro building steps for a workspace.
@@ -63,10 +63,7 @@ impl WorkspaceBuildScripts {
         progress: &dyn Fn(String),
         sysroot: &Sysroot,
     ) -> io::Result<WorkspaceBuildScripts> {
-        let current_dir = match &config.invocation_location {
-            InvocationLocation::Root(root) if config.run_build_script_command.is_some() => root,
-            _ => workspace.workspace_root(),
-        };
+        let current_dir = workspace.workspace_root();
 
         let allowed_features = workspace.workspace_features();
         let cmd = Self::build_command(
@@ -85,25 +82,16 @@ impl WorkspaceBuildScripts {
         config: &CargoConfig,
         workspaces: &[&CargoWorkspace],
         progress: &dyn Fn(String),
-        workspace_root: &AbsPathBuf,
+        working_directory: &AbsPathBuf,
     ) -> io::Result<Vec<WorkspaceBuildScripts>> {
         assert_eq!(config.invocation_strategy, InvocationStrategy::Once);
 
-        let current_dir = match &config.invocation_location {
-            InvocationLocation::Root(root) => root,
-            InvocationLocation::Workspace => {
-                return Err(io::Error::new(
-                    io::ErrorKind::Other,
-                    "Cannot run build scripts from workspace with invocation strategy `once`",
-                ))
-            }
-        };
         let cmd = Self::build_command(
             config,
             &Default::default(),
             // This is not gonna be used anyways, so just construct a dummy here
-            &ManifestPath::try_from(workspace_root.clone()).unwrap(),
-            current_dir,
+            &ManifestPath::try_from(working_directory.clone()).unwrap(),
+            working_directory,
             &Sysroot::empty(),
         )?;
         // NB: Cargo.toml could have been modified between `cargo metadata` and
diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
index 38eeedec6217f..7cc21bcf13191 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
@@ -13,7 +13,7 @@ use serde_json::from_value;
 use span::Edition;
 use toolchain::Tool;
 
-use crate::{utf8_stdout, InvocationLocation, ManifestPath, Sysroot};
+use crate::{utf8_stdout, ManifestPath, Sysroot};
 use crate::{CfgOverrides, InvocationStrategy};
 
 /// [`CargoWorkspace`] represents the logical structure of, well, a Cargo
@@ -98,7 +98,6 @@ pub struct CargoConfig {
     /// Extra env vars to set when invoking the cargo command
     pub extra_env: FxHashMap<String, String>,
     pub invocation_strategy: InvocationStrategy,
-    pub invocation_location: InvocationLocation,
     /// Optional path to use instead of `target` when building
     pub target_dir: Option<Utf8PathBuf>,
 }
@@ -242,6 +241,10 @@ impl TargetKind {
     pub fn is_executable(self) -> bool {
         matches!(self, TargetKind::Bin | TargetKind::Example)
     }
+
+    pub fn is_proc_macro(self) -> bool {
+        matches!(self, TargetKind::Lib { is_proc_macro: true })
+    }
 }
 
 // Deserialize helper for the cargo metadata
@@ -252,6 +255,9 @@ struct PackageMetadata {
 }
 
 impl CargoWorkspace {
+    /// Fetches the metadata for the given `cargo_toml` manifest.
+    /// A successful result may contain another metadata error if the initial fetching failed but
+    /// the `--no-deps` retry succeeded.
     pub fn fetch_metadata(
         cargo_toml: &ManifestPath,
         current_dir: &AbsPath,
@@ -259,7 +265,19 @@ impl CargoWorkspace {
         sysroot: &Sysroot,
         locked: bool,
         progress: &dyn Fn(String),
-    ) -> anyhow::Result<cargo_metadata::Metadata> {
+    ) -> anyhow::Result<(cargo_metadata::Metadata, Option<anyhow::Error>)> {
+        Self::fetch_metadata_(cargo_toml, current_dir, config, sysroot, locked, false, progress)
+    }
+
+    fn fetch_metadata_(
+        cargo_toml: &ManifestPath,
+        current_dir: &AbsPath,
+        config: &CargoConfig,
+        sysroot: &Sysroot,
+        locked: bool,
+        no_deps: bool,
+        progress: &dyn Fn(String),
+    ) -> anyhow::Result<(cargo_metadata::Metadata, Option<anyhow::Error>)> {
         let targets = find_list_of_build_targets(config, cargo_toml, sysroot);
 
         let cargo = sysroot.tool(Tool::Cargo);
@@ -314,6 +332,9 @@ impl CargoWorkspace {
         if locked {
             other_options.push("--locked".to_owned());
         }
+        if no_deps {
+            other_options.push("--no-deps".to_owned());
+        }
         meta.other_options(other_options);
 
         // FIXME: Fetching metadata is a slow process, as it might require
@@ -321,19 +342,42 @@ impl CargoWorkspace {
         // unclear whether cargo itself supports it.
         progress("metadata".to_owned());
 
-        (|| -> Result<cargo_metadata::Metadata, cargo_metadata::Error> {
+        (|| -> anyhow::Result<(_, _)> {
             let output = meta.cargo_command().output()?;
             if !output.status.success() {
-                return Err(cargo_metadata::Error::CargoMetadata {
+                let error = cargo_metadata::Error::CargoMetadata {
                     stderr: String::from_utf8(output.stderr)?,
-                });
+                }
+                .into();
+                if !no_deps {
+                    // If we failed to fetch metadata with deps, try again without them.
+                    // This makes r-a still work partially when offline.
+                    if let Ok((metadata, _)) = Self::fetch_metadata_(
+                        cargo_toml,
+                        current_dir,
+                        config,
+                        sysroot,
+                        locked,
+                        true,
+                        progress,
+                    ) {
+                        return Ok((metadata, Some(error)));
+                    }
+                }
+                return Err(error);
             }
             let stdout = from_utf8(&output.stdout)?
                 .lines()
                 .find(|line| line.starts_with('{'))
                 .ok_or(cargo_metadata::Error::NoJson)?;
-            cargo_metadata::MetadataCommand::parse(stdout)
+            Ok((cargo_metadata::MetadataCommand::parse(stdout)?, None))
         })()
+        .map(|(metadata, error)| {
+            (
+                metadata,
+                error.map(|e| e.context(format!("Failed to run `{:?}`", meta.cargo_command()))),
+            )
+        })
         .with_context(|| format!("Failed to run `{:?}`", meta.cargo_command()))
     }
 
@@ -431,8 +475,7 @@ impl CargoWorkspace {
                 pkg_data.targets.push(tgt);
             }
         }
-        let resolve = meta.resolve.expect("metadata executed with deps");
-        for mut node in resolve.nodes {
+        for mut node in meta.resolve.map_or_else(Vec::new, |it| it.nodes) {
             let &source = pkg_by_id.get(&node.id).unwrap();
             node.deps.sort_by(|a, b| a.pkg.cmp(&b.pkg));
             let dependencies = node
diff --git a/src/tools/rust-analyzer/crates/project-model/src/env.rs b/src/tools/rust-analyzer/crates/project-model/src/env.rs
index ac7246acc592f..ff9d2035f60a7 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/env.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/env.rs
@@ -3,7 +3,7 @@ use base_db::Env;
 use rustc_hash::FxHashMap;
 use toolchain::Tool;
 
-use crate::{utf8_stdout, ManifestPath, PackageData, Sysroot, TargetKind};
+use crate::{utf8_stdout, CargoWorkspace, ManifestPath, PackageData, Sysroot, TargetKind};
 
 /// Recreates the compile-time environment variables that Cargo sets.
 ///
@@ -50,13 +50,23 @@ pub(crate) fn inject_cargo_env(env: &mut Env) {
     env.set("CARGO", Tool::Cargo.path().to_string());
 }
 
-pub(crate) fn inject_rustc_tool_env(env: &mut Env, cargo_name: &str, kind: TargetKind) {
+pub(crate) fn inject_rustc_tool_env(
+    env: &mut Env,
+    cargo: &CargoWorkspace,
+    cargo_name: &str,
+    kind: TargetKind,
+) {
     _ = kind;
     // FIXME
     // if kind.is_executable() {
     //     env.set("CARGO_BIN_NAME", cargo_name);
     // }
     env.set("CARGO_CRATE_NAME", cargo_name.replace('-', "_"));
+    // NOTE: Technically we should set this for all crates, but that will worsen the deduplication
+    // logic so for now just keeping it proc-macros ought to be fine.
+    if kind.is_proc_macro() {
+        env.set("CARGO_RUSTC_CURRENT_DIR", cargo.manifest_path().parent().to_string());
+    }
 }
 
 pub(crate) fn cargo_config_env(
diff --git a/src/tools/rust-analyzer/crates/project-model/src/lib.rs b/src/tools/rust-analyzer/crates/project-model/src/lib.rs
index 4fa70508bbd4d..b8ac55ed0d5d1 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs
@@ -186,20 +186,13 @@ fn utf8_stdout(mut cmd: Command) -> anyhow::Result<String> {
     Ok(stdout.trim().to_owned())
 }
 
-#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
+#[derive(Clone, Debug, Default, PartialEq, Eq)]
 pub enum InvocationStrategy {
     Once,
     #[default]
     PerWorkspace,
 }
 
-#[derive(Clone, Debug, Default, PartialEq, Eq)]
-pub enum InvocationLocation {
-    Root(AbsPathBuf),
-    #[default]
-    Workspace,
-}
-
 /// A set of cfg-overrides per crate.
 #[derive(Default, Debug, Clone, Eq, PartialEq)]
 pub struct CfgOverrides {
diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
index 7dea0c3839841..a09c7a77abceb 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
@@ -52,7 +52,7 @@
 use base_db::{CrateDisplayName, CrateName};
 use cfg::CfgAtom;
 use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
-use rustc_hash::FxHashMap;
+use rustc_hash::{FxHashMap, FxHashSet};
 use serde::{de, Deserialize, Serialize};
 use span::Edition;
 
@@ -122,6 +122,25 @@ impl ProjectJson {
                         None => None,
                     };
 
+                    let cfg = crate_data
+                        .cfg_groups
+                        .iter()
+                        .flat_map(|cfg_extend| {
+                            let cfg_group = data.cfg_groups.get(cfg_extend);
+                            match cfg_group {
+                                Some(cfg_group) => cfg_group.0.iter().cloned(),
+                                None => {
+                                    tracing::error!(
+                                        "Unknown cfg group `{cfg_extend}` in crate `{}`",
+                                        crate_data.display_name.as_deref().unwrap_or("<unknown>"),
+                                    );
+                                    [].iter().cloned()
+                                }
+                            }
+                        })
+                        .chain(crate_data.cfg.0)
+                        .collect();
+
                     Crate {
                         display_name: crate_data
                             .display_name
@@ -131,7 +150,7 @@ impl ProjectJson {
                         edition: crate_data.edition.into(),
                         version: crate_data.version.as_ref().map(ToString::to_string),
                         deps: crate_data.deps,
-                        cfg: crate_data.cfg,
+                        cfg,
                         target: crate_data.target,
                         env: crate_data.env,
                         proc_macro_dylib_path: crate_data
@@ -306,11 +325,17 @@ pub enum RunnableKind {
 pub struct ProjectJsonData {
     sysroot: Option<Utf8PathBuf>,
     sysroot_src: Option<Utf8PathBuf>,
+    #[serde(default)]
+    cfg_groups: FxHashMap<String, CfgList>,
     crates: Vec<CrateData>,
     #[serde(default)]
     runnables: Vec<RunnableData>,
 }
 
+#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq, Default)]
+#[serde(transparent)]
+struct CfgList(#[serde(with = "cfg_")] Vec<CfgAtom>);
+
 #[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
 struct CrateData {
     display_name: Option<String>,
@@ -320,8 +345,9 @@ struct CrateData {
     version: Option<semver::Version>,
     deps: Vec<Dep>,
     #[serde(default)]
-    #[serde(with = "cfg_")]
-    cfg: Vec<CfgAtom>,
+    cfg_groups: FxHashSet<String>,
+    #[serde(default)]
+    cfg: CfgList,
     target: Option<String>,
     #[serde(default)]
     env: FxHashMap<String, String>,
diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
index 419fac3f41f75..19f4c35b5addd 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
@@ -325,7 +325,7 @@ impl Sysroot {
             "nightly".to_owned(),
         );
 
-        let mut res = match CargoWorkspace::fetch_metadata(
+        let (mut res, _) = match CargoWorkspace::fetch_metadata(
             &library_manifest,
             sysroot_src_dir,
             &cargo_config,
@@ -372,18 +372,19 @@ impl Sysroot {
                 .flatten()
         };
 
-        let resolve = res.resolve.as_mut().expect("metadata executed with deps");
-        resolve.nodes.retain_mut(|node| {
-            // Replace `rustc-std-workspace` crate with the actual one in the dependency list
-            node.deps.iter_mut().for_each(|dep| {
-                let real_pkg = patches.clone().find(|((_, fake_id), _)| *fake_id == dep.pkg);
-                if let Some((_, real)) = real_pkg {
-                    dep.pkg = real;
-                }
+        if let Some(resolve) = res.resolve.as_mut() {
+            resolve.nodes.retain_mut(|node| {
+                // Replace `rustc-std-workspace` crate with the actual one in the dependency list
+                node.deps.iter_mut().for_each(|dep| {
+                    let real_pkg = patches.clone().find(|((_, fake_id), _)| *fake_id == dep.pkg);
+                    if let Some((_, real)) = real_pkg {
+                        dep.pkg = real;
+                    }
+                });
+                // Remove this node if it's a fake one
+                !patches.clone().any(|((_, fake), _)| fake == node.id)
             });
-            // Remove this node if it's a fake one
-            !patches.clone().any(|((_, fake), _)| fake == node.id)
-        });
+        }
         // Remove the fake ones from the package list
         patches.map(|((idx, _), _)| idx).sorted().rev().for_each(|idx| {
             res.packages.remove(idx);
diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
index e3bc81e1963d9..f540bb94c1963 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
@@ -34,6 +34,7 @@ fn load_cargo_with_overrides(
             build_scripts: WorkspaceBuildScripts::default(),
             rustc: Err(None),
             cargo_config_extra_env: Default::default(),
+            error: None,
         },
         cfg_overrides,
         sysroot: Sysroot::empty(),
@@ -58,6 +59,7 @@ fn load_cargo_with_fake_sysroot(
             build_scripts: WorkspaceBuildScripts::default(),
             rustc: Err(None),
             cargo_config_extra_env: Default::default(),
+            error: None,
         },
         sysroot: get_fake_sysroot(),
         rustc_cfg: Vec::new(),
@@ -233,6 +235,12 @@ fn rust_project_hello_world_project_model() {
     );
 }
 
+#[test]
+fn rust_project_cfg_groups() {
+    let (crate_graph, _proc_macros) = load_rust_project("cfg-groups.json");
+    check_crate_graph(crate_graph, expect_file!["../test_data/output/rust_project_cfg_groups.txt"]);
+}
+
 #[test]
 fn rust_project_is_proc_macro_has_proc_macro_dep() {
     let (crate_graph, _proc_macros) = load_rust_project("is-proc-macro-project.json");
@@ -294,6 +302,7 @@ fn smoke_test_real_sysroot_cargo() {
             build_scripts: WorkspaceBuildScripts::default(),
             rustc: Err(None),
             cargo_config_extra_env: Default::default(),
+            error: None,
         },
         sysroot,
         rustc_cfg: Vec::new(),
diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
index 5620dfade2d22..7834238acefdf 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
@@ -69,6 +69,8 @@ pub enum ProjectWorkspaceKind {
     Cargo {
         /// The workspace as returned by `cargo metadata`.
         cargo: CargoWorkspace,
+        /// Additional `cargo metadata` error. (only populated if retried fetching via `--no-deps` succeeded).
+        error: Option<Arc<anyhow::Error>>,
         /// The build script results for the workspace.
         build_scripts: WorkspaceBuildScripts,
         /// The rustc workspace loaded for this workspace. An `Err(None)` means loading has been
@@ -93,7 +95,7 @@ pub enum ProjectWorkspaceKind {
         /// The file in question.
         file: ManifestPath,
         /// Is this file a cargo script file?
-        cargo: Option<(CargoWorkspace, WorkspaceBuildScripts)>,
+        cargo: Option<(CargoWorkspace, WorkspaceBuildScripts, Option<Arc<anyhow::Error>>)>,
         /// Environment variables set in the `.cargo/config` file.
         cargo_config_extra_env: FxHashMap<String, String>,
     },
@@ -106,6 +108,7 @@ impl fmt::Debug for ProjectWorkspace {
         match kind {
             ProjectWorkspaceKind::Cargo {
                 cargo,
+                error: _,
                 build_scripts: _,
                 rustc,
                 cargo_config_extra_env,
@@ -256,7 +259,7 @@ impl ProjectWorkspace {
                         false,
                         progress,
                     ) {
-                        Ok(meta) => {
+                        Ok((meta, _error)) => {
                             let workspace = CargoWorkspace::new(meta, cargo_toml.clone());
                             let buildscripts = WorkspaceBuildScripts::rustc_crates(
                                 &workspace,
@@ -301,7 +304,7 @@ impl ProjectWorkspace {
                     tracing::error!(%e, "failed fetching data layout for {cargo_toml:?} workspace");
                 }
 
-                let meta = CargoWorkspace::fetch_metadata(
+                let (meta, error) = CargoWorkspace::fetch_metadata(
                     cargo_toml,
                     cargo_toml.parent(),
                     config,
@@ -324,6 +327,7 @@ impl ProjectWorkspace {
                         build_scripts: WorkspaceBuildScripts::default(),
                         rustc,
                         cargo_config_extra_env,
+                        error: error.map(Arc::new),
                     },
                     sysroot,
                     rustc_cfg,
@@ -404,10 +408,11 @@ impl ProjectWorkspace {
         let cargo_script =
             CargoWorkspace::fetch_metadata(detached_file, dir, config, &sysroot, false, &|_| ())
                 .ok()
-                .map(|ws| {
+                .map(|(ws, error)| {
                     (
                         CargoWorkspace::new(ws, detached_file.clone()),
                         WorkspaceBuildScripts::default(),
+                        error.map(Arc::new),
                     )
                 });
 
@@ -440,15 +445,14 @@ impl ProjectWorkspace {
         progress: &dyn Fn(String),
     ) -> anyhow::Result<WorkspaceBuildScripts> {
         match &self.kind {
-            ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _)), .. }
-            | ProjectWorkspaceKind::Cargo { cargo, .. } => {
+            ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, None)), .. }
+            | ProjectWorkspaceKind::Cargo { cargo, error: None, .. } => {
                 WorkspaceBuildScripts::run_for_workspace(config, cargo, progress, &self.sysroot)
                     .with_context(|| {
                         format!("Failed to run build scripts for {}", cargo.workspace_root())
                     })
             }
-            ProjectWorkspaceKind::DetachedFile { cargo: None, .. }
-            | ProjectWorkspaceKind::Json { .. } => Ok(WorkspaceBuildScripts::default()),
+            _ => Ok(WorkspaceBuildScripts::default()),
         }
     }
 
@@ -458,7 +462,7 @@ impl ProjectWorkspace {
         workspaces: &[ProjectWorkspace],
         config: &CargoConfig,
         progress: &dyn Fn(String),
-        workspace_root: &AbsPathBuf,
+        working_directory: &AbsPathBuf,
     ) -> Vec<anyhow::Result<WorkspaceBuildScripts>> {
         if matches!(config.invocation_strategy, InvocationStrategy::PerWorkspace)
             || config.run_build_script_command.is_none()
@@ -473,13 +477,16 @@ impl ProjectWorkspace {
                 _ => None,
             })
             .collect();
-        let outputs =
-            &mut match WorkspaceBuildScripts::run_once(config, &cargo_ws, progress, workspace_root)
-            {
-                Ok(it) => Ok(it.into_iter()),
-                // io::Error is not Clone?
-                Err(e) => Err(sync::Arc::new(e)),
-            };
+        let outputs = &mut match WorkspaceBuildScripts::run_once(
+            config,
+            &cargo_ws,
+            progress,
+            working_directory,
+        ) {
+            Ok(it) => Ok(it.into_iter()),
+            // io::Error is not Clone?
+            Err(e) => Err(sync::Arc::new(e)),
+        };
 
         workspaces
             .iter()
@@ -498,7 +505,7 @@ impl ProjectWorkspace {
     pub fn set_build_scripts(&mut self, bs: WorkspaceBuildScripts) {
         match &mut self.kind {
             ProjectWorkspaceKind::Cargo { build_scripts, .. }
-            | ProjectWorkspaceKind::DetachedFile { cargo: Some((_, build_scripts)), .. } => {
+            | ProjectWorkspaceKind::DetachedFile { cargo: Some((_, build_scripts, _)), .. } => {
                 *build_scripts = bs
             }
             _ => assert_eq!(bs, WorkspaceBuildScripts::default()),
@@ -589,6 +596,7 @@ impl ProjectWorkspace {
                 rustc,
                 build_scripts,
                 cargo_config_extra_env: _,
+                error: _,
             } => {
                 cargo
                     .packages()
@@ -644,7 +652,7 @@ impl ProjectWorkspace {
                     include: vec![file.to_path_buf()],
                     exclude: Vec::new(),
                 })
-                .chain(cargo_script.iter().flat_map(|(cargo, build_scripts)| {
+                .chain(cargo_script.iter().flat_map(|(cargo, build_scripts, _)| {
                     cargo.packages().map(|pkg| {
                         let is_local = cargo[pkg].is_local;
                         let pkg_root = cargo[pkg].manifest.parent().to_path_buf();
@@ -699,7 +707,7 @@ impl ProjectWorkspace {
             }
             ProjectWorkspaceKind::DetachedFile { cargo: cargo_script, .. } => {
                 sysroot_package_len
-                    + cargo_script.as_ref().map_or(1, |(cargo, _)| cargo.packages().len())
+                    + cargo_script.as_ref().map_or(1, |(cargo, _, _)| cargo.packages().len())
             }
         }
     }
@@ -729,6 +737,7 @@ impl ProjectWorkspace {
                 rustc,
                 build_scripts,
                 cargo_config_extra_env: _,
+                error: _,
             } => (
                 cargo_to_crate_graph(
                     load,
@@ -742,7 +751,7 @@ impl ProjectWorkspace {
                 sysroot,
             ),
             ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, .. } => (
-                if let Some((cargo, build_scripts)) = cargo_script {
+                if let Some((cargo, build_scripts, _)) = cargo_script {
                     cargo_to_crate_graph(
                         &mut |path| load(path),
                         None,
@@ -791,12 +800,14 @@ impl ProjectWorkspace {
                     rustc,
                     cargo_config_extra_env,
                     build_scripts: _,
+                    error: _,
                 },
                 ProjectWorkspaceKind::Cargo {
                     cargo: o_cargo,
                     rustc: o_rustc,
                     cargo_config_extra_env: o_cargo_config_extra_env,
                     build_scripts: _,
+                    error: _,
                 },
             ) => {
                 cargo == o_cargo
@@ -809,12 +820,12 @@ impl ProjectWorkspace {
             (
                 ProjectWorkspaceKind::DetachedFile {
                     file,
-                    cargo: Some((cargo_script, _)),
+                    cargo: Some((cargo_script, _, _)),
                     cargo_config_extra_env,
                 },
                 ProjectWorkspaceKind::DetachedFile {
                     file: o_file,
-                    cargo: Some((o_cargo_script, _)),
+                    cargo: Some((o_cargo_script, _, _)),
                     cargo_config_extra_env: o_cargo_config_extra_env,
                 },
             ) => {
@@ -1016,6 +1027,7 @@ fn cargo_to_crate_graph(
             let crate_id = add_target_crate_root(
                 crate_graph,
                 proc_macros,
+                cargo,
                 pkg_data,
                 build_data,
                 cfg_options.clone(),
@@ -1235,6 +1247,7 @@ fn handle_rustc_crates(
                     let crate_id = add_target_crate_root(
                         crate_graph,
                         proc_macros,
+                        rustc_workspace,
                         &rustc_workspace[pkg],
                         build_scripts.get_output(pkg),
                         cfg_options.clone(),
@@ -1294,6 +1307,7 @@ fn handle_rustc_crates(
 fn add_target_crate_root(
     crate_graph: &mut CrateGraph,
     proc_macros: &mut ProcMacroPaths,
+    cargo: &CargoWorkspace,
     pkg: &PackageData,
     build_data: Option<&BuildScriptOutput>,
     cfg_options: CfgOptions,
@@ -1327,7 +1341,7 @@ fn add_target_crate_root(
     let mut env = Env::default();
     inject_cargo_package_env(&mut env, pkg);
     inject_cargo_env(&mut env);
-    inject_rustc_tool_env(&mut env, cargo_name, kind);
+    inject_rustc_tool_env(&mut env, cargo, cargo_name, kind);
 
     if let Some(envs) = build_data.map(|it| &it.envs) {
         for (k, v) in envs {
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/cfg-groups.json b/src/tools/rust-analyzer/crates/project-model/test_data/cfg-groups.json
new file mode 100644
index 0000000000000..29f83afd9d4e5
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/cfg-groups.json
@@ -0,0 +1,26 @@
+{
+    "sysroot_src": null,
+    "cfg_groups": {
+        "group1": ["group1_cfg=\"some_config\"", "group1_other_cfg=\"other_config\""],
+        "group2": ["group2_cfg=\"yet_another_config\""]
+    },
+    "crates": [
+        {
+            "display_name": "hello_world",
+            "root_module": "$ROOT$src/lib.rs",
+            "edition": "2018",
+            "cfg_groups": ["group1", "group2"],
+            "deps": [],
+            "is_workspace_member": true
+        },
+        {
+            "display_name": "other_crate",
+            "root_module": "$ROOT$src/lib.rs",
+            "edition": "2018",
+            "cfg_groups": ["group2"],
+            "cfg": ["group2_cfg=\"fourth_config\"", "unrelated_cfg"],
+            "deps": [],
+            "is_workspace_member": true
+        }
+    ]
+}
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt
new file mode 100644
index 0000000000000..8261e5a2d907a
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt
@@ -0,0 +1,545 @@
+{
+    0: CrateData {
+        root_file_id: FileId(
+            1,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "alloc",
+                ),
+                canonical_name: "alloc",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [
+            Dependency {
+                crate_id: Idx::<CrateData>(1),
+                name: CrateName(
+                    "core",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+        ],
+        origin: Lang(
+            Alloc,
+        ),
+        is_proc_macro: false,
+    },
+    1: CrateData {
+        root_file_id: FileId(
+            2,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "core",
+                ),
+                canonical_name: "core",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [],
+        origin: Lang(
+            Core,
+        ),
+        is_proc_macro: false,
+    },
+    2: CrateData {
+        root_file_id: FileId(
+            3,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "panic_abort",
+                ),
+                canonical_name: "panic_abort",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [],
+        origin: Lang(
+            Other,
+        ),
+        is_proc_macro: false,
+    },
+    3: CrateData {
+        root_file_id: FileId(
+            4,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "panic_unwind",
+                ),
+                canonical_name: "panic_unwind",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [],
+        origin: Lang(
+            Other,
+        ),
+        is_proc_macro: false,
+    },
+    4: CrateData {
+        root_file_id: FileId(
+            5,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "proc_macro",
+                ),
+                canonical_name: "proc_macro",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [
+            Dependency {
+                crate_id: Idx::<CrateData>(6),
+                name: CrateName(
+                    "std",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(1),
+                name: CrateName(
+                    "core",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+        ],
+        origin: Lang(
+            ProcMacro,
+        ),
+        is_proc_macro: false,
+    },
+    5: CrateData {
+        root_file_id: FileId(
+            6,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "profiler_builtins",
+                ),
+                canonical_name: "profiler_builtins",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [],
+        origin: Lang(
+            Other,
+        ),
+        is_proc_macro: false,
+    },
+    6: CrateData {
+        root_file_id: FileId(
+            7,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "std",
+                ),
+                canonical_name: "std",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [
+            Dependency {
+                crate_id: Idx::<CrateData>(0),
+                name: CrateName(
+                    "alloc",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(3),
+                name: CrateName(
+                    "panic_unwind",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(2),
+                name: CrateName(
+                    "panic_abort",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(1),
+                name: CrateName(
+                    "core",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(5),
+                name: CrateName(
+                    "profiler_builtins",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(9),
+                name: CrateName(
+                    "unwind",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(7),
+                name: CrateName(
+                    "std_detect",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(8),
+                name: CrateName(
+                    "test",
+                ),
+                prelude: true,
+                sysroot: false,
+            },
+        ],
+        origin: Lang(
+            Std,
+        ),
+        is_proc_macro: false,
+    },
+    7: CrateData {
+        root_file_id: FileId(
+            8,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "std_detect",
+                ),
+                canonical_name: "std_detect",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [],
+        origin: Lang(
+            Other,
+        ),
+        is_proc_macro: false,
+    },
+    8: CrateData {
+        root_file_id: FileId(
+            9,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "test",
+                ),
+                canonical_name: "test",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [],
+        origin: Lang(
+            Test,
+        ),
+        is_proc_macro: false,
+    },
+    9: CrateData {
+        root_file_id: FileId(
+            10,
+        ),
+        edition: Edition2021,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "unwind",
+                ),
+                canonical_name: "unwind",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "debug_assertions",
+                "miri",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [],
+        origin: Lang(
+            Other,
+        ),
+        is_proc_macro: false,
+    },
+    10: CrateData {
+        root_file_id: FileId(
+            11,
+        ),
+        edition: Edition2018,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "hello_world",
+                ),
+                canonical_name: "hello_world",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "group1_cfg=some_config",
+                "group1_other_cfg=other_config",
+                "group2_cfg=yet_another_config",
+                "rust_analyzer",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [
+            Dependency {
+                crate_id: Idx::<CrateData>(1),
+                name: CrateName(
+                    "core",
+                ),
+                prelude: true,
+                sysroot: true,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(0),
+                name: CrateName(
+                    "alloc",
+                ),
+                prelude: false,
+                sysroot: true,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(6),
+                name: CrateName(
+                    "std",
+                ),
+                prelude: true,
+                sysroot: true,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(8),
+                name: CrateName(
+                    "test",
+                ),
+                prelude: false,
+                sysroot: true,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(4),
+                name: CrateName(
+                    "proc_macro",
+                ),
+                prelude: false,
+                sysroot: true,
+            },
+        ],
+        origin: Local {
+            repo: None,
+            name: Some(
+                "hello_world",
+            ),
+        },
+        is_proc_macro: false,
+    },
+    11: CrateData {
+        root_file_id: FileId(
+            12,
+        ),
+        edition: Edition2018,
+        version: None,
+        display_name: Some(
+            CrateDisplayName {
+                crate_name: CrateName(
+                    "other_crate",
+                ),
+                canonical_name: "other_crate",
+            },
+        ),
+        cfg_options: CfgOptions(
+            [
+                "group2_cfg=fourth_config",
+                "group2_cfg=yet_another_config",
+                "rust_analyzer",
+                "unrelated_cfg",
+            ],
+        ),
+        potential_cfg_options: None,
+        env: Env {
+            entries: {},
+        },
+        dependencies: [
+            Dependency {
+                crate_id: Idx::<CrateData>(1),
+                name: CrateName(
+                    "core",
+                ),
+                prelude: true,
+                sysroot: true,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(0),
+                name: CrateName(
+                    "alloc",
+                ),
+                prelude: false,
+                sysroot: true,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(6),
+                name: CrateName(
+                    "std",
+                ),
+                prelude: true,
+                sysroot: true,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(8),
+                name: CrateName(
+                    "test",
+                ),
+                prelude: false,
+                sysroot: true,
+            },
+            Dependency {
+                crate_id: Idx::<CrateData>(4),
+                name: CrateName(
+                    "proc_macro",
+                ),
+                prelude: false,
+                sysroot: true,
+            },
+        ],
+        origin: Local {
+            repo: None,
+            name: Some(
+                "other_crate",
+            ),
+        },
+        is_proc_macro: false,
+    },
+}
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
index 42953d3b833c2..21b481c1fa29e 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
@@ -227,7 +227,7 @@ fn run_server() -> anyhow::Result<()> {
         .filter(|workspaces| !workspaces.is_empty())
         .unwrap_or_else(|| vec![root_path.clone()]);
     let mut config =
-        Config::new(root_path, capabilities, workspace_roots, visual_studio_code_version, None);
+        Config::new(root_path, capabilities, workspace_roots, visual_studio_code_version);
     if let Some(json) = initialization_options {
         let mut change = ConfigChange::default();
         change.change_client_config(json);
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs
index 0bd6677b66237..5eb6ff664f665 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs
@@ -21,7 +21,7 @@ use std::io::Read;
 use anyhow::Result;
 use hir::{Module, Name};
 use hir_ty::db::HirDatabase;
-use ide::AnalysisHost;
+use ide::{AnalysisHost, Edition};
 use itertools::Itertools;
 use vfs::Vfs;
 
@@ -85,6 +85,6 @@ fn full_name_of_item(db: &dyn HirDatabase, module: Module, name: Name) -> String
         .rev()
         .filter_map(|it| it.name(db))
         .chain(Some(name))
-        .map(|it| it.display(db.upcast()).to_string())
+        .map(|it| it.display(db.upcast(), Edition::LATEST).to_string())
         .join("::")
 }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
index 06f4ba815d0dd..44e56645ba3c3 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -17,7 +17,7 @@ use hir_def::{
 };
 use hir_ty::{Interner, Substitution, TyExt, TypeFlags};
 use ide::{
-    Analysis, AnalysisHost, AnnotationConfig, DiagnosticsConfig, InlayFieldsToResolve,
+    Analysis, AnalysisHost, AnnotationConfig, DiagnosticsConfig, Edition, InlayFieldsToResolve,
     InlayHintsConfig, LineCol, RootDatabase,
 };
 use ide_db::{
@@ -309,7 +309,7 @@ impl flags::AnalysisStats {
         let mut fail = 0;
         for &c in consts {
             all += 1;
-            let Err(error) = c.render_eval(db) else {
+            let Err(error) = c.render_eval(db, Edition::LATEST) else {
                 continue;
             };
             if verbosity.is_spammy() {
@@ -442,6 +442,7 @@ impl flags::AnalysisStats {
                                 prefer_prelude: true,
                                 prefer_absolute: false,
                             },
+                            Edition::LATEST,
                         )
                         .unwrap();
                     syntax_hit_found |= trim(&original_text) == trim(&generated);
@@ -563,7 +564,7 @@ impl flags::AnalysisStats {
                     .rev()
                     .filter_map(|it| it.name(db))
                     .chain(Some(body.name(db).unwrap_or_else(Name::missing)))
-                    .map(|it| it.display(db).to_string())
+                    .map(|it| it.display(db, Edition::LATEST).to_string())
                     .join("::");
                 println!("Mir body for {full_name} failed due {e:?}");
             }
@@ -628,12 +629,14 @@ impl flags::AnalysisStats {
                             .filter_map(|it| it.name(db))
                             .rev()
                             .chain(Some(body_id.name(db).unwrap_or_else(Name::missing)))
-                            .map(|it| it.display(db).to_string()),
+                            .map(|it| it.display(db, Edition::LATEST).to_string()),
                     )
                     .join("::")
             };
             if let Some(only_name) = self.only.as_deref() {
-                if name.display(db).to_string() != only_name && full_name() != only_name {
+                if name.display(db, Edition::LATEST).to_string() != only_name
+                    && full_name() != only_name
+                {
                     continue;
                 }
             }
@@ -687,7 +690,10 @@ impl flags::AnalysisStats {
                                 end.col,
                             ));
                         } else {
-                            bar.println(format!("{}: Unknown type", name.display(db)));
+                            bar.println(format!(
+                                "{}: Unknown type",
+                                name.display(db, Edition::LATEST)
+                            ));
                         }
                     }
                     true
@@ -708,17 +714,20 @@ impl flags::AnalysisStats {
                             start.col,
                             end.line + 1,
                             end.col,
-                            ty.display(db)
+                            ty.display(db, Edition::LATEST)
                         ));
                     } else {
-                        bar.println(format!("unknown location: {}", ty.display(db)));
+                        bar.println(format!(
+                            "unknown location: {}",
+                            ty.display(db, Edition::LATEST)
+                        ));
                     }
                 }
                 if unknown_or_partial && self.output == Some(OutputFormat::Csv) {
                     println!(
                         r#"{},type,"{}""#,
                         location_csv_expr(db, vfs, &sm(), expr_id),
-                        ty.display(db)
+                        ty.display(db, Edition::LATEST)
                     );
                 }
                 if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) {
@@ -733,15 +742,15 @@ impl flags::AnalysisStats {
                                 start.col,
                                 end.line + 1,
                                 end.col,
-                                mismatch.expected.display(db),
-                                mismatch.actual.display(db)
+                                mismatch.expected.display(db, Edition::LATEST),
+                                mismatch.actual.display(db, Edition::LATEST)
                             ));
                         } else {
                             bar.println(format!(
                                 "{}: Expected {}, got {}",
-                                name.display(db),
-                                mismatch.expected.display(db),
-                                mismatch.actual.display(db)
+                                name.display(db, Edition::LATEST),
+                                mismatch.expected.display(db, Edition::LATEST),
+                                mismatch.actual.display(db, Edition::LATEST)
                             ));
                         }
                     }
@@ -749,8 +758,8 @@ impl flags::AnalysisStats {
                         println!(
                             r#"{},mismatch,"{}","{}""#,
                             location_csv_expr(db, vfs, &sm(), expr_id),
-                            mismatch.expected.display(db),
-                            mismatch.actual.display(db)
+                            mismatch.expected.display(db, Edition::LATEST),
+                            mismatch.actual.display(db, Edition::LATEST)
                         );
                     }
                 }
@@ -785,7 +794,10 @@ impl flags::AnalysisStats {
                                 end.col,
                             ));
                         } else {
-                            bar.println(format!("{}: Unknown type", name.display(db)));
+                            bar.println(format!(
+                                "{}: Unknown type",
+                                name.display(db, Edition::LATEST)
+                            ));
                         }
                     }
                     true
@@ -806,17 +818,20 @@ impl flags::AnalysisStats {
                             start.col,
                             end.line + 1,
                             end.col,
-                            ty.display(db)
+                            ty.display(db, Edition::LATEST)
                         ));
                     } else {
-                        bar.println(format!("unknown location: {}", ty.display(db)));
+                        bar.println(format!(
+                            "unknown location: {}",
+                            ty.display(db, Edition::LATEST)
+                        ));
                     }
                 }
                 if unknown_or_partial && self.output == Some(OutputFormat::Csv) {
                     println!(
                         r#"{},type,"{}""#,
                         location_csv_pat(db, vfs, &sm(), pat_id),
-                        ty.display(db)
+                        ty.display(db, Edition::LATEST)
                     );
                 }
                 if let Some(mismatch) = inference_result.type_mismatch_for_pat(pat_id) {
@@ -830,15 +845,15 @@ impl flags::AnalysisStats {
                                 start.col,
                                 end.line + 1,
                                 end.col,
-                                mismatch.expected.display(db),
-                                mismatch.actual.display(db)
+                                mismatch.expected.display(db, Edition::LATEST),
+                                mismatch.actual.display(db, Edition::LATEST)
                             ));
                         } else {
                             bar.println(format!(
                                 "{}: Expected {}, got {}",
-                                name.display(db),
-                                mismatch.expected.display(db),
-                                mismatch.actual.display(db)
+                                name.display(db, Edition::LATEST),
+                                mismatch.expected.display(db, Edition::LATEST),
+                                mismatch.actual.display(db, Edition::LATEST)
                             ));
                         }
                     }
@@ -846,8 +861,8 @@ impl flags::AnalysisStats {
                         println!(
                             r#"{},mismatch,"{}","{}""#,
                             location_csv_pat(db, vfs, &sm(), pat_id),
-                            mismatch.expected.display(db),
-                            mismatch.actual.display(db)
+                            mismatch.expected.display(db, Edition::LATEST),
+                            mismatch.actual.display(db, Edition::LATEST)
                         );
                     }
                 }
@@ -923,12 +938,16 @@ impl flags::AnalysisStats {
                             .filter_map(|it| it.name(db))
                             .rev()
                             .chain(Some(body_id.name(db).unwrap_or_else(Name::missing)))
-                            .map(|it| it.display(db).to_string()),
+                            .map(|it| it.display(db, Edition::LATEST).to_string()),
                     )
                     .join("::")
             };
             if let Some(only_name) = self.only.as_deref() {
-                if body_id.name(db).unwrap_or_else(Name::missing).display(db).to_string()
+                if body_id
+                    .name(db)
+                    .unwrap_or_else(Name::missing)
+                    .display(db, Edition::LATEST)
+                    .to_string()
                     != only_name
                     && full_name() != only_name
                 {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
index 2a3e74c680b92..16d90de661ad1 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
@@ -138,6 +138,9 @@ xflags::xflags! {
 
         cmd lsif {
             required path: PathBuf
+
+            /// Exclude code from vendored libraries from the resulting index.
+            optional --exclude-vendored-libraries
         }
 
         cmd scip {
@@ -148,6 +151,9 @@ xflags::xflags! {
 
             /// A path to an json configuration file that can be used to customize cargo behavior.
             optional --config-path config_path: PathBuf
+
+            /// Exclude code from vendored libraries from the resulting index.
+            optional --exclude-vendored-libraries
         }
     }
 }
@@ -259,6 +265,8 @@ pub struct Search {
 #[derive(Debug)]
 pub struct Lsif {
     pub path: PathBuf,
+
+    pub exclude_vendored_libraries: bool,
 }
 
 #[derive(Debug)]
@@ -267,6 +275,7 @@ pub struct Scip {
 
     pub output: Option<PathBuf>,
     pub config_path: Option<PathBuf>,
+    pub exclude_vendored_libraries: bool,
 }
 
 impl RustAnalyzer {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
index 016f0edc2dd3c..89fe712ced020 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
@@ -5,7 +5,7 @@ use std::time::Instant;
 
 use ide::{
     Analysis, AnalysisHost, FileId, FileRange, MonikerKind, PackageInformation, RootDatabase,
-    StaticIndex, StaticIndexedFile, TokenId, TokenStaticData,
+    StaticIndex, StaticIndexedFile, TokenId, TokenStaticData, VendoredLibrariesConfig,
 };
 use ide_db::{line_index::WideEncoding, LineIndexDatabase};
 use load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice};
@@ -296,7 +296,13 @@ impl flags::Lsif {
         let db = host.raw_database();
         let analysis = host.analysis();
 
-        let si = StaticIndex::compute(&analysis, &path.clone().into());
+        let vendored_libs_config = if self.exclude_vendored_libraries {
+            VendoredLibrariesConfig::Excluded
+        } else {
+            VendoredLibrariesConfig::Included { workspace_root: &path.clone().into() }
+        };
+
+        let si = StaticIndex::compute(&analysis, vendored_libs_config);
 
         let mut lsif = LsifManager::new(&analysis, db, &vfs);
         lsif.add_vertex(lsif::Vertex::MetaData(lsif::MetaData {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
index 2fc0ef8d4daac..ceb8534fdf553 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
@@ -4,7 +4,7 @@ use std::{path::PathBuf, time::Instant};
 
 use ide::{
     AnalysisHost, LineCol, MonikerDescriptorKind, MonikerResult, StaticIndex, StaticIndexedFile,
-    SymbolInformationKind, TextRange, TokenId,
+    SymbolInformationKind, TextRange, TokenId, VendoredLibrariesConfig,
 };
 use ide_db::LineIndexDatabase;
 use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice};
@@ -37,7 +37,6 @@ impl flags::Scip {
             lsp_types::ClientCapabilities::default(),
             vec![],
             None,
-            None,
         );
 
         if let Some(p) = self.config_path {
@@ -63,7 +62,13 @@ impl flags::Scip {
         let db = host.raw_database();
         let analysis = host.analysis();
 
-        let si = StaticIndex::compute(&analysis, &root.clone().into());
+        let vendored_libs_config = if self.exclude_vendored_libraries {
+            VendoredLibrariesConfig::Excluded
+        } else {
+            VendoredLibrariesConfig::Included { workspace_root: &root.clone().into() }
+        };
+
+        let si = StaticIndex::compute(&analysis, vendored_libs_config);
 
         let metadata = scip_types::Metadata {
             version: scip_types::ProtocolVersion::UnspecifiedProtocolVersion.into(),
@@ -137,7 +142,9 @@ impl flags::Scip {
                 let mut symbol_roles = Default::default();
 
                 if let Some(def) = token.definition {
-                    if def.range == text_range {
+                    // if the the range of the def and the range of the token are the same, this must be the definition.
+                    // they also must be in the same file. See https://github.com/rust-lang/rust-analyzer/pull/17988
+                    if def.file_id == file_id && def.range == text_range {
                         symbol_roles |= scip_types::SymbolRole::Definition as i32;
                     }
 
@@ -352,8 +359,12 @@ mod test {
         let (host, position) = position(ra_fixture);
 
         let analysis = host.analysis();
-        let si =
-            StaticIndex::compute(&analysis, &VfsPath::new_virtual_path("/workspace".to_owned()));
+        let si = StaticIndex::compute(
+            &analysis,
+            VendoredLibrariesConfig::Included {
+                workspace_root: &VfsPath::new_virtual_path("/workspace".to_owned()),
+            },
+        );
 
         let FilePosition { file_id, offset } = position;
 
@@ -617,8 +628,12 @@ pub mod example_mod {
         host.raw_database_mut().apply_change(change_fixture.change);
 
         let analysis = host.analysis();
-        let si =
-            StaticIndex::compute(&analysis, &VfsPath::new_virtual_path("/workspace".to_owned()));
+        let si = StaticIndex::compute(
+            &analysis,
+            VendoredLibrariesConfig::Included {
+                workspace_root: &VfsPath::new_virtual_path("/workspace".to_owned()),
+            },
+        );
 
         let file = si.files.first().unwrap();
         let (_, token_id) = file.tokens.first().unwrap();
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs
index f1009eb46602f..b19a1b8d16700 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs
@@ -148,7 +148,6 @@ impl<T: ParseFromLine> CommandHandle<T> {
     }
 
     pub(crate) fn join(mut self) -> io::Result<()> {
-        let _ = self.child.0.kill();
         let exit_status = self.child.0.wait()?;
         let (read_at_least_one_message, error) = self.thread.join()?;
         if read_at_least_one_message || exit_status.success() {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index 02f5d75136e9e..2889af844b14e 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -3,10 +3,13 @@
 //! Of particular interest is the `feature_flags` hash map: while other fields
 //! configure the server itself, feature flags are passed into analysis, and
 //! tweak things like automatic insertion of `()` in completions.
-use std::{fmt, iter, ops::Not, sync::OnceLock};
+use std::{
+    env, fmt, iter,
+    ops::Not,
+    sync::{LazyLock, OnceLock},
+};
 
 use cfg::{CfgAtom, CfgDiff};
-use dirs::config_dir;
 use hir::Symbol;
 use ide::{
     AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode,
@@ -80,16 +83,11 @@ config_data! {
         pub(crate) cargo_autoreload: bool           = true,
         /// Run build scripts (`build.rs`) for more precise code analysis.
         cargo_buildScripts_enable: bool  = true,
-        /// Specifies the working directory for running build scripts.
-        /// - "workspace": run build scripts for a workspace in the workspace's root directory.
-        ///   This is incompatible with `#rust-analyzer.cargo.buildScripts.invocationStrategy#` set to `once`.
-        /// - "root": run build scripts in the project's root directory.
-        /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
-        /// is set.
-        cargo_buildScripts_invocationLocation: InvocationLocation = InvocationLocation::Workspace,
         /// Specifies the invocation strategy to use when running the build scripts command.
-        /// If `per_workspace` is set, the command will be executed for each workspace.
-        /// If `once` is set, the command will be executed once.
+        /// If `per_workspace` is set, the command will be executed for each Rust workspace with the
+        /// workspace as the working directory.
+        /// If `once` is set, the command will be executed once with the opened project as the
+        /// working directory.
         /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
         /// is set.
         cargo_buildScripts_invocationStrategy: InvocationStrategy = InvocationStrategy::PerWorkspace,
@@ -101,8 +99,7 @@ config_data! {
         /// If there are multiple linked projects/workspaces, this command is invoked for
         /// each of them, with the working directory being the workspace root
         /// (i.e., the folder containing the `Cargo.toml`). This can be overwritten
-        /// by changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#` and
-        /// `#rust-analyzer.cargo.buildScripts.invocationLocation#`.
+        /// by changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#`.
         ///
         /// By default, a cargo invocation will be constructed for the configured
         /// targets and features, with the following base command line:
@@ -182,14 +179,6 @@ config_data! {
         ///
         /// For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,...
         check_ignore: FxHashSet<String> = FxHashSet::default(),
-        /// Specifies the working directory for running checks.
-        /// - "workspace": run checks for workspaces in the corresponding workspaces' root directories.
-        // FIXME: Ideally we would support this in some way
-        ///   This falls back to "root" if `#rust-analyzer.check.invocationStrategy#` is set to `once`.
-        /// - "root": run checks in the project's root directory.
-        /// This config only has an effect when `#rust-analyzer.check.overrideCommand#`
-        /// is set.
-        check_invocationLocation | checkOnSave_invocationLocation: InvocationLocation = InvocationLocation::Workspace,
         /// Specifies the invocation strategy to use when running the check command.
         /// If `per_workspace` is set, the command will be executed for each workspace.
         /// If `once` is set, the command will be executed once.
@@ -212,8 +201,7 @@ config_data! {
         /// If there are multiple linked projects/workspaces, this command is invoked for
         /// each of them, with the working directory being the workspace root
         /// (i.e., the folder containing the `Cargo.toml`). This can be overwritten
-        /// by changing `#rust-analyzer.check.invocationStrategy#` and
-        /// `#rust-analyzer.check.invocationLocation#`.
+        /// by changing `#rust-analyzer.check.invocationStrategy#`.
         ///
         /// If `$saved_file` is part of the command, rust-analyzer will pass
         /// the absolute path of the saved file to the provided command. This is
@@ -309,18 +297,6 @@ config_data! {
         /// This option does not take effect until rust-analyzer is restarted.
         rustc_source: Option<String> = None,
 
-        /// Additional arguments to `rustfmt`.
-        rustfmt_extraArgs: Vec<String>               = vec![],
-        /// Advanced option, fully override the command rust-analyzer uses for
-        /// formatting. This should be the equivalent of `rustfmt` here, and
-        /// not that of `cargo fmt`. The file contents will be passed on the
-        /// standard input and the formatted result will be read from the
-        /// standard output.
-        rustfmt_overrideCommand: Option<Vec<String>> = None,
-        /// Enables the use of rustfmt's unstable range formatting command for the
-        /// `textDocument/rangeFormatting` request. The rustfmt option is unstable and only
-        /// available on a nightly build.
-        rustfmt_rangeFormatting_enable: bool = false,
 
         /// Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].
         ///
@@ -451,6 +427,25 @@ config_data! {
     }
 }
 
+config_data! {
+    workspace: struct WorkspaceDefaultConfigData <- WorkspaceConfigInput -> {
+
+        /// Additional arguments to `rustfmt`.
+        rustfmt_extraArgs: Vec<String>               = vec![],
+        /// Advanced option, fully override the command rust-analyzer uses for
+        /// formatting. This should be the equivalent of `rustfmt` here, and
+        /// not that of `cargo fmt`. The file contents will be passed on the
+        /// standard input and the formatted result will be read from the
+        /// standard output.
+        rustfmt_overrideCommand: Option<Vec<String>> = None,
+        /// Enables the use of rustfmt's unstable range formatting command for the
+        /// `textDocument/rangeFormatting` request. The rustfmt option is unstable and only
+        /// available on a nightly build.
+        rustfmt_rangeFormatting_enable: bool = false,
+
+    }
+}
+
 config_data! {
     /// Configs that only make sense when they are set by a client. As such they can only be defined
     /// by setting them using client's settings (e.g `settings.json` on VS Code).
@@ -750,10 +745,9 @@ pub enum RatomlFileKind {
 }
 
 #[derive(Debug, Clone)]
-// FIXME @alibektas : Seems like a clippy warning of this sort should tell that combining different ConfigInputs into one enum was not a good idea.
 #[allow(clippy::large_enum_variant)]
 enum RatomlFile {
-    Workspace(GlobalLocalConfigInput),
+    Workspace(WorkspaceLocalConfigInput),
     Crate(LocalConfigInput),
 }
 
@@ -772,18 +766,8 @@ pub struct Config {
     /// by receiving a `lsp_types::notification::DidChangeConfiguration`.
     client_config: (FullConfigInput, ConfigErrors),
 
-    /// Path to the root configuration file. This can be seen as a generic way to define what would be `$XDG_CONFIG_HOME/rust-analyzer/rust-analyzer.toml` in Linux.
-    /// If not specified by init of a `Config` object this value defaults to :
-    ///
-    /// |Platform | Value                                 | Example                                  |
-    /// | ------- | ------------------------------------- | ---------------------------------------- |
-    /// | Linux   | `$XDG_CONFIG_HOME` or `$HOME`/.config | /home/alice/.config                      |
-    /// | macOS   | `$HOME`/Library/Application Support   | /Users/Alice/Library/Application Support |
-    /// | Windows | `{FOLDERID_RoamingAppData}`           | C:\Users\Alice\AppData\Roaming           |
-    user_config_path: VfsPath,
-
     /// Config node whose values apply to **every** Rust project.
-    user_config: Option<(GlobalLocalConfigInput, ConfigErrors)>,
+    user_config: Option<(GlobalWorkspaceLocalConfigInput, ConfigErrors)>,
 
     ratoml_file: FxHashMap<SourceRootId, (RatomlFile, ConfigErrors)>,
 
@@ -809,8 +793,25 @@ impl std::ops::Deref for Config {
 }
 
 impl Config {
-    pub fn user_config_path(&self) -> &VfsPath {
-        &self.user_config_path
+    /// Path to the root configuration file. This can be seen as a generic way to define what would be `$XDG_CONFIG_HOME/rust-analyzer/rust-analyzer.toml` in Linux.
+    /// This path is equal to:
+    ///
+    /// |Platform | Value                                 | Example                                  |
+    /// | ------- | ------------------------------------- | ---------------------------------------- |
+    /// | Linux   | `$XDG_CONFIG_HOME` or `$HOME`/.config | /home/alice/.config                      |
+    /// | macOS   | `$HOME`/Library/Application Support   | /Users/Alice/Library/Application Support |
+    /// | Windows | `{FOLDERID_RoamingAppData}`           | C:\Users\Alice\AppData\Roaming           |
+    pub fn user_config_path() -> Option<&'static AbsPath> {
+        static USER_CONFIG_PATH: LazyLock<Option<AbsPathBuf>> = LazyLock::new(|| {
+            let user_config_path = if let Some(path) = env::var_os("__TEST_RA_USER_CONFIG_DIR") {
+                std::path::PathBuf::from(path)
+            } else {
+                dirs::config_dir()?.join("rust-analyzer")
+            }
+            .join("rust-analyzer.toml");
+            Some(AbsPathBuf::assert_utf8(user_config_path))
+        });
+        USER_CONFIG_PATH.as_deref()
     }
 
     pub fn same_source_root_parent_map(
@@ -833,13 +834,13 @@ impl Config {
             if let Ok(table) = toml::from_str(&change) {
                 let mut toml_errors = vec![];
                 validate_toml_table(
-                    GlobalLocalConfigInput::FIELDS,
+                    GlobalWorkspaceLocalConfigInput::FIELDS,
                     &table,
                     &mut String::new(),
                     &mut toml_errors,
                 );
                 config.user_config = Some((
-                    GlobalLocalConfigInput::from_toml(table, &mut toml_errors),
+                    GlobalWorkspaceLocalConfigInput::from_toml(table, &mut toml_errors),
                     ConfigErrors(
                         toml_errors
                             .into_iter()
@@ -968,7 +969,7 @@ impl Config {
                             match toml::from_str(&text) {
                                 Ok(table) => {
                                     validate_toml_table(
-                                        GlobalLocalConfigInput::FIELDS,
+                                        WorkspaceLocalConfigInput::FIELDS,
                                         &table,
                                         &mut String::new(),
                                         &mut toml_errors,
@@ -977,7 +978,7 @@ impl Config {
                                         source_root_id,
                                         (
                                             RatomlFile::Workspace(
-                                                GlobalLocalConfigInput::from_toml(
+                                                WorkspaceLocalConfigInput::from_toml(
                                                     table,
                                                     &mut toml_errors,
                                                 ),
@@ -1015,7 +1016,7 @@ impl Config {
             config.source_root_parent_map = source_root_map;
         }
 
-        if config.check_command(None).is_empty() {
+        if config.check_command().is_empty() {
             config.validation_errors.0.push(Arc::new(ConfigErrorInner::Json {
                 config_key: "/check/command".to_owned(),
                 error: serde_json::Error::custom("expected a non-empty string"),
@@ -1330,24 +1331,8 @@ impl Config {
         caps: lsp_types::ClientCapabilities,
         workspace_roots: Vec<AbsPathBuf>,
         visual_studio_code_version: Option<Version>,
-        user_config_path: Option<Utf8PathBuf>,
     ) -> Self {
         static DEFAULT_CONFIG_DATA: OnceLock<&'static DefaultConfigData> = OnceLock::new();
-        let user_config_path = if let Some(user_config_path) = user_config_path {
-            user_config_path.join("rust-analyzer").join("rust-analyzer.toml")
-        } else {
-            let p = config_dir()
-                .expect("A config dir is expected to existed on all platforms ra supports.")
-                .join("rust-analyzer")
-                .join("rust-analyzer.toml");
-            Utf8PathBuf::from_path_buf(p).expect("Config dir expected to be abs.")
-        };
-
-        // A user config cannot be a virtual path as rust-analyzer cannot support watching changes in virtual paths.
-        // See `GlobalState::process_changes` to get more info.
-        // FIXME @alibektas : Temporary solution. I don't think this is right as at some point we may allow users to specify
-        // custom USER_CONFIG_PATHs which may also be relative.
-        let user_config_path = VfsPath::from(AbsPathBuf::assert(user_config_path));
 
         Config {
             caps: ClientCapabilities::new(caps),
@@ -1360,7 +1345,6 @@ impl Config {
             default_config: DEFAULT_CONFIG_DATA.get_or_init(|| Box::leak(Box::default())),
             source_root_parent_map: Arc::new(FxHashMap::default()),
             user_config: None,
-            user_config_path,
             detached_files: Default::default(),
             validation_errors: Default::default(),
             ratoml_file: Default::default(),
@@ -1459,11 +1443,11 @@ impl Config {
 
     pub fn diagnostics(&self, source_root: Option<SourceRootId>) -> DiagnosticsConfig {
         DiagnosticsConfig {
-            enabled: *self.diagnostics_enable(source_root),
+            enabled: *self.diagnostics_enable(),
             proc_attr_macros_enabled: self.expand_proc_attr_macros(),
             proc_macros_enabled: *self.procMacro_enable(),
-            disable_experimental: !self.diagnostics_experimental_enable(source_root),
-            disabled: self.diagnostics_disabled(source_root).clone(),
+            disable_experimental: !self.diagnostics_experimental_enable(),
+            disabled: self.diagnostics_disabled().clone(),
             expr_fill_default: match self.assist_expressionFillDefault(source_root) {
                 ExprFillDefaultDef::Todo => ExprFillDefaultMode::Todo,
                 ExprFillDefaultDef::Default => ExprFillDefaultMode::Default,
@@ -1473,7 +1457,7 @@ impl Config {
             prefer_no_std: self.imports_preferNoStd(source_root).to_owned(),
             prefer_prelude: self.imports_preferPrelude(source_root).to_owned(),
             prefer_absolute: self.imports_prefixExternPrelude(source_root).to_owned(),
-            style_lints: self.diagnostics_styleLints_enable(source_root).to_owned(),
+            style_lints: self.diagnostics_styleLints_enable().to_owned(),
             term_search_fuel: self.assist_termSearch_fuel(source_root).to_owned() as u64,
             term_search_borrowck: self.assist_termSearch_borrowcheck(source_root).to_owned(),
         }
@@ -1666,11 +1650,11 @@ impl Config {
     }
 
     pub fn has_linked_projects(&self) -> bool {
-        !self.linkedProjects(None).is_empty()
+        !self.linkedProjects().is_empty()
     }
 
     pub fn linked_manifests(&self) -> impl Iterator<Item = &Utf8Path> + '_ {
-        self.linkedProjects(None).iter().filter_map(|it| match it {
+        self.linkedProjects().iter().filter_map(|it| match it {
             ManifestOrProjectJson::Manifest(p) => Some(&**p),
             // despite having a buildfile, using this variant as a manifest
             // will fail.
@@ -1680,20 +1664,20 @@ impl Config {
     }
 
     pub fn has_linked_project_jsons(&self) -> bool {
-        self.linkedProjects(None)
+        self.linkedProjects()
             .iter()
             .any(|it| matches!(it, ManifestOrProjectJson::ProjectJson { .. }))
     }
 
     pub fn discover_workspace_config(&self) -> Option<&DiscoverWorkspaceConfig> {
-        self.workspace_discoverConfig(None).as_ref()
+        self.workspace_discoverConfig().as_ref()
     }
 
     pub fn linked_or_discovered_projects(&self) -> Vec<LinkedProject> {
-        match self.linkedProjects(None).as_slice() {
+        match self.linkedProjects().as_slice() {
             [] => {
                 let exclude_dirs: Vec<_> =
-                    self.files_excludeDirs(None).iter().map(|p| self.root_path.join(p)).collect();
+                    self.files_excludeDirs().iter().map(|p| self.root_path.join(p)).collect();
                 self.discovered_projects
                     .iter()
                     .filter(|project| {
@@ -1728,48 +1712,48 @@ impl Config {
     }
 
     pub fn prefill_caches(&self) -> bool {
-        self.cachePriming_enable(None).to_owned()
+        self.cachePriming_enable().to_owned()
     }
 
     pub fn publish_diagnostics(&self) -> bool {
-        self.diagnostics_enable(None).to_owned()
+        self.diagnostics_enable().to_owned()
     }
 
     pub fn diagnostics_map(&self) -> DiagnosticsMapConfig {
         DiagnosticsMapConfig {
-            remap_prefix: self.diagnostics_remapPrefix(None).clone(),
-            warnings_as_info: self.diagnostics_warningsAsInfo(None).clone(),
-            warnings_as_hint: self.diagnostics_warningsAsHint(None).clone(),
-            check_ignore: self.check_ignore(None).clone(),
+            remap_prefix: self.diagnostics_remapPrefix().clone(),
+            warnings_as_info: self.diagnostics_warningsAsInfo().clone(),
+            warnings_as_hint: self.diagnostics_warningsAsHint().clone(),
+            check_ignore: self.check_ignore().clone(),
         }
     }
 
     pub fn extra_args(&self) -> &Vec<String> {
-        self.cargo_extraArgs(None)
+        self.cargo_extraArgs()
     }
 
     pub fn extra_env(&self) -> &FxHashMap<String, String> {
-        self.cargo_extraEnv(None)
+        self.cargo_extraEnv()
     }
 
     pub fn check_extra_args(&self) -> Vec<String> {
         let mut extra_args = self.extra_args().clone();
-        extra_args.extend_from_slice(self.check_extraArgs(None));
+        extra_args.extend_from_slice(self.check_extraArgs());
         extra_args
     }
 
     pub fn check_extra_env(&self) -> FxHashMap<String, String> {
-        let mut extra_env = self.cargo_extraEnv(None).clone();
-        extra_env.extend(self.check_extraEnv(None).clone());
+        let mut extra_env = self.cargo_extraEnv().clone();
+        extra_env.extend(self.check_extraEnv().clone());
         extra_env
     }
 
     pub fn lru_parse_query_capacity(&self) -> Option<u16> {
-        self.lru_capacity(None).to_owned()
+        self.lru_capacity().to_owned()
     }
 
     pub fn lru_query_capacities_config(&self) -> Option<&FxHashMap<Box<str>, u16>> {
-        self.lru_query_capacities(None).is_empty().not().then(|| self.lru_query_capacities(None))
+        self.lru_query_capacities().is_empty().not().then(|| self.lru_query_capacities())
     }
 
     pub fn proc_macro_srv(&self) -> Option<AbsPathBuf> {
@@ -1778,7 +1762,7 @@ impl Config {
     }
 
     pub fn ignored_proc_macros(&self) -> &FxHashMap<Box<str>, Box<[Box<str>]>> {
-        self.procMacro_ignored(None)
+        self.procMacro_ignored()
     }
 
     pub fn expand_proc_macros(&self) -> bool {
@@ -1793,11 +1777,7 @@ impl Config {
                 }
                 _ => FilesWatcher::Server,
             },
-            exclude: self
-                .files_excludeDirs(None)
-                .iter()
-                .map(|it| self.root_path.join(it))
-                .collect(),
+            exclude: self.files_excludeDirs().iter().map(|it| self.root_path.join(it)).collect(),
         }
     }
 
@@ -1808,22 +1788,22 @@ impl Config {
     }
 
     pub fn cargo_autoreload_config(&self) -> bool {
-        self.cargo_autoreload(None).to_owned()
+        self.cargo_autoreload().to_owned()
     }
 
     pub fn run_build_scripts(&self) -> bool {
-        self.cargo_buildScripts_enable(None).to_owned() || self.procMacro_enable().to_owned()
+        self.cargo_buildScripts_enable().to_owned() || self.procMacro_enable().to_owned()
     }
 
     pub fn cargo(&self) -> CargoConfig {
-        let rustc_source = self.rustc_source(None).as_ref().map(|rustc_src| {
+        let rustc_source = self.rustc_source().as_ref().map(|rustc_src| {
             if rustc_src == "discover" {
                 RustLibSource::Discover
             } else {
                 RustLibSource::Path(self.root_path.join(rustc_src))
             }
         });
-        let sysroot = self.cargo_sysroot(None).as_ref().map(|sysroot| {
+        let sysroot = self.cargo_sysroot().as_ref().map(|sysroot| {
             if sysroot == "discover" {
                 RustLibSource::Discover
             } else {
@@ -1831,24 +1811,24 @@ impl Config {
             }
         });
         let sysroot_src =
-            self.cargo_sysrootSrc(None).as_ref().map(|sysroot| self.root_path.join(sysroot));
+            self.cargo_sysrootSrc().as_ref().map(|sysroot| self.root_path.join(sysroot));
 
         CargoConfig {
-            all_targets: *self.cargo_allTargets(None),
-            features: match &self.cargo_features(None) {
+            all_targets: *self.cargo_allTargets(),
+            features: match &self.cargo_features() {
                 CargoFeaturesDef::All => CargoFeatures::All,
                 CargoFeaturesDef::Selected(features) => CargoFeatures::Selected {
                     features: features.clone(),
-                    no_default_features: self.cargo_noDefaultFeatures(None).to_owned(),
+                    no_default_features: self.cargo_noDefaultFeatures().to_owned(),
                 },
             },
-            target: self.cargo_target(None).clone(),
+            target: self.cargo_target().clone(),
             sysroot,
             sysroot_src,
             rustc_source,
             cfg_overrides: project_model::CfgOverrides {
                 global: CfgDiff::new(
-                    self.cargo_cfgs(None)
+                    self.cargo_cfgs()
                         .iter()
                         .map(|(key, val)| match val {
                             Some(val) => CfgAtom::KeyValue {
@@ -1863,20 +1843,14 @@ impl Config {
                 .unwrap(),
                 selective: Default::default(),
             },
-            wrap_rustc_in_build_scripts: *self.cargo_buildScripts_useRustcWrapper(None),
-            invocation_strategy: match self.cargo_buildScripts_invocationStrategy(None) {
+            wrap_rustc_in_build_scripts: *self.cargo_buildScripts_useRustcWrapper(),
+            invocation_strategy: match self.cargo_buildScripts_invocationStrategy() {
                 InvocationStrategy::Once => project_model::InvocationStrategy::Once,
                 InvocationStrategy::PerWorkspace => project_model::InvocationStrategy::PerWorkspace,
             },
-            invocation_location: match self.cargo_buildScripts_invocationLocation(None) {
-                InvocationLocation::Root => {
-                    project_model::InvocationLocation::Root(self.root_path.clone())
-                }
-                InvocationLocation::Workspace => project_model::InvocationLocation::Workspace,
-            },
-            run_build_script_command: self.cargo_buildScripts_overrideCommand(None).clone(),
-            extra_args: self.cargo_extraArgs(None).clone(),
-            extra_env: self.cargo_extraEnv(None).clone(),
+            run_build_script_command: self.cargo_buildScripts_overrideCommand().clone(),
+            extra_args: self.cargo_extraArgs().clone(),
+            extra_env: self.cargo_extraEnv().clone(),
             target_dir: self.target_dir_from_config(),
         }
     }
@@ -1896,27 +1870,28 @@ impl Config {
     }
 
     pub fn flycheck_workspace(&self) -> bool {
-        *self.check_workspace(None)
+        *self.check_workspace()
     }
 
     pub(crate) fn cargo_test_options(&self) -> CargoOptions {
         CargoOptions {
-            target_triples: self.cargo_target(None).clone().into_iter().collect(),
+            target_triples: self.cargo_target().clone().into_iter().collect(),
             all_targets: false,
-            no_default_features: *self.cargo_noDefaultFeatures(None),
-            all_features: matches!(self.cargo_features(None), CargoFeaturesDef::All),
-            features: match self.cargo_features(None).clone() {
+            no_default_features: *self.cargo_noDefaultFeatures(),
+            all_features: matches!(self.cargo_features(), CargoFeaturesDef::All),
+            features: match self.cargo_features().clone() {
                 CargoFeaturesDef::All => vec![],
                 CargoFeaturesDef::Selected(it) => it,
             },
             extra_args: self.extra_args().clone(),
+            extra_test_bin_args: self.runnables_extraTestBinaryArgs().clone(),
             extra_env: self.extra_env().clone(),
             target_dir: self.target_dir_from_config(),
         }
     }
 
     pub(crate) fn flycheck(&self) -> FlycheckConfig {
-        match &self.check_overrideCommand(None) {
+        match &self.check_overrideCommand() {
             Some(args) if !args.is_empty() => {
                 let mut args = args.clone();
                 let command = args.remove(0);
@@ -1924,52 +1899,43 @@ impl Config {
                     command,
                     args,
                     extra_env: self.check_extra_env(),
-                    invocation_strategy: match self.check_invocationStrategy(None) {
+                    invocation_strategy: match self.check_invocationStrategy() {
                         InvocationStrategy::Once => crate::flycheck::InvocationStrategy::Once,
                         InvocationStrategy::PerWorkspace => {
                             crate::flycheck::InvocationStrategy::PerWorkspace
                         }
                     },
-                    invocation_location: match self.check_invocationLocation(None) {
-                        InvocationLocation::Root => {
-                            crate::flycheck::InvocationLocation::Root(self.root_path.clone())
-                        }
-                        InvocationLocation::Workspace => {
-                            crate::flycheck::InvocationLocation::Workspace
-                        }
-                    },
                 }
             }
             Some(_) | None => FlycheckConfig::CargoCommand {
-                command: self.check_command(None).clone(),
+                command: self.check_command().clone(),
                 options: CargoOptions {
                     target_triples: self
-                        .check_targets(None)
+                        .check_targets()
                         .clone()
                         .and_then(|targets| match &targets.0[..] {
                             [] => None,
                             targets => Some(targets.into()),
                         })
-                        .unwrap_or_else(|| self.cargo_target(None).clone().into_iter().collect()),
-                    all_targets: self
-                        .check_allTargets(None)
-                        .unwrap_or(*self.cargo_allTargets(None)),
+                        .unwrap_or_else(|| self.cargo_target().clone().into_iter().collect()),
+                    all_targets: self.check_allTargets().unwrap_or(*self.cargo_allTargets()),
                     no_default_features: self
-                        .check_noDefaultFeatures(None)
-                        .unwrap_or(*self.cargo_noDefaultFeatures(None)),
+                        .check_noDefaultFeatures()
+                        .unwrap_or(*self.cargo_noDefaultFeatures()),
                     all_features: matches!(
-                        self.check_features(None).as_ref().unwrap_or(self.cargo_features(None)),
+                        self.check_features().as_ref().unwrap_or(self.cargo_features()),
                         CargoFeaturesDef::All
                     ),
                     features: match self
-                        .check_features(None)
+                        .check_features()
                         .clone()
-                        .unwrap_or_else(|| self.cargo_features(None).clone())
+                        .unwrap_or_else(|| self.cargo_features().clone())
                     {
                         CargoFeaturesDef::All => vec![],
                         CargoFeaturesDef::Selected(it) => it,
                     },
                     extra_args: self.check_extra_args(),
+                    extra_test_bin_args: self.runnables_extraTestBinaryArgs().clone(),
                     extra_env: self.check_extra_env(),
                     target_dir: self.target_dir_from_config(),
                 },
@@ -1979,7 +1945,7 @@ impl Config {
     }
 
     fn target_dir_from_config(&self) -> Option<Utf8PathBuf> {
-        self.cargo_targetDir(None).as_ref().and_then(|target_dir| match target_dir {
+        self.cargo_targetDir().as_ref().and_then(|target_dir| match target_dir {
             TargetDirectory::UseSubdirectory(true) => {
                 Some(Utf8PathBuf::from("target/rust-analyzer"))
             }
@@ -1990,18 +1956,18 @@ impl Config {
     }
 
     pub fn check_on_save(&self) -> bool {
-        *self.checkOnSave(None)
+        *self.checkOnSave()
     }
 
     pub fn script_rebuild_on_save(&self) -> bool {
-        *self.cargo_buildScripts_rebuildOnSave(None)
+        *self.cargo_buildScripts_rebuildOnSave()
     }
 
     pub fn runnables(&self) -> RunnablesConfig {
         RunnablesConfig {
-            override_cargo: self.runnables_command(None).clone(),
-            cargo_extra_args: self.runnables_extraArgs(None).clone(),
-            extra_test_binary_args: self.runnables_extraTestBinaryArgs(None).clone(),
+            override_cargo: self.runnables_command().clone(),
+            cargo_extra_args: self.runnables_extraArgs().clone(),
+            extra_test_binary_args: self.runnables_extraTestBinaryArgs().clone(),
         }
     }
 
@@ -2072,7 +2038,7 @@ impl Config {
     }
 
     pub fn prime_caches_num_threads(&self) -> usize {
-        match self.cachePriming_numThreads(None) {
+        match self.cachePriming_numThreads() {
             NumThreads::Concrete(0) | NumThreads::Physical => num_cpus::get_physical(),
             &NumThreads::Concrete(n) => n,
             NumThreads::Logical => num_cpus::get(),
@@ -2348,13 +2314,6 @@ pub(crate) enum InvocationStrategy {
 #[derive(Serialize, Deserialize, Debug, Clone)]
 struct CheckOnSaveTargets(#[serde(with = "single_or_array")] Vec<String>);
 
-#[derive(Serialize, Deserialize, Debug, Clone)]
-#[serde(rename_all = "snake_case")]
-enum InvocationLocation {
-    Root,
-    Workspace,
-}
-
 #[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(rename_all = "snake_case")]
 enum LifetimeElisionDef {
@@ -2578,7 +2537,7 @@ macro_rules! _impl_for_config_data {
             )*
         }
     };
-    (global, $(
+    (workspace, $(
             $(#[doc=$doc:literal])*
             $vis:vis $field:ident : $ty:ty = $default:expr,
         )*
@@ -2587,18 +2546,42 @@ macro_rules! _impl_for_config_data {
             $(
                 $($doc)*
                 #[allow(non_snake_case)]
-                $vis fn $field(&self, source_root : Option<SourceRootId>) -> &$ty {
+                $vis fn $field(&self, source_root: Option<SourceRootId>) -> &$ty {
                     let mut source_root = source_root.as_ref();
                     while let Some(sr) = source_root {
                         if let Some((RatomlFile::Workspace(config), _)) = self.ratoml_file.get(&sr) {
-                            if let Some(v) = config.global.$field.as_ref() {
+                            if let Some(v) = config.workspace.$field.as_ref() {
                                 return &v;
                             }
                         }
-
                         source_root = self.source_root_parent_map.get(&sr);
                     }
 
+                    if let Some(v) = self.client_config.0.workspace.$field.as_ref() {
+                        return &v;
+                    }
+
+                    if let Some((user_config, _)) = self.user_config.as_ref() {
+                        if let Some(v) = user_config.workspace.$field.as_ref() {
+                            return &v;
+                        }
+                    }
+
+                    &self.default_config.workspace.$field
+                }
+            )*
+        }
+    };
+    (global, $(
+            $(#[doc=$doc:literal])*
+            $vis:vis $field:ident : $ty:ty = $default:expr,
+        )*
+    ) => {
+        impl Config {
+            $(
+                $($doc)*
+                #[allow(non_snake_case)]
+                $vis fn $field(&self) -> &$ty {
                     if let Some(v) = self.client_config.0.global.$field.as_ref() {
                         return &v;
                     }
@@ -2736,6 +2719,7 @@ use _config_data as config_data;
 #[derive(Default, Debug, Clone)]
 struct DefaultConfigData {
     global: GlobalDefaultConfigData,
+    workspace: WorkspaceDefaultConfigData,
     local: LocalDefaultConfigData,
     client: ClientDefaultConfigData,
 }
@@ -2746,6 +2730,7 @@ struct DefaultConfigData {
 #[derive(Debug, Clone, Default)]
 struct FullConfigInput {
     global: GlobalConfigInput,
+    workspace: WorkspaceConfigInput,
     local: LocalConfigInput,
     client: ClientConfigInput,
 }
@@ -2759,6 +2744,7 @@ impl FullConfigInput {
             global: GlobalConfigInput::from_json(&mut json, error_sink),
             local: LocalConfigInput::from_json(&mut json, error_sink),
             client: ClientConfigInput::from_json(&mut json, error_sink),
+            workspace: WorkspaceConfigInput::from_json(&mut json, error_sink),
         }
     }
 
@@ -2767,6 +2753,7 @@ impl FullConfigInput {
         GlobalConfigInput::schema_fields(&mut fields);
         LocalConfigInput::schema_fields(&mut fields);
         ClientConfigInput::schema_fields(&mut fields);
+        WorkspaceConfigInput::schema_fields(&mut fields);
         fields.sort_by_key(|&(x, ..)| x);
         fields
             .iter()
@@ -2789,21 +2776,45 @@ impl FullConfigInput {
 /// some rust-analyzer.toml file or JSON blob. An empty rust-analyzer.toml corresponds to
 /// all fields being None.
 #[derive(Debug, Clone, Default)]
-struct GlobalLocalConfigInput {
+struct GlobalWorkspaceLocalConfigInput {
     global: GlobalConfigInput,
     local: LocalConfigInput,
+    workspace: WorkspaceConfigInput,
 }
 
-impl GlobalLocalConfigInput {
+impl GlobalWorkspaceLocalConfigInput {
     const FIELDS: &'static [&'static [&'static str]] =
         &[GlobalConfigInput::FIELDS, LocalConfigInput::FIELDS];
     fn from_toml(
         toml: toml::Table,
         error_sink: &mut Vec<(String, toml::de::Error)>,
-    ) -> GlobalLocalConfigInput {
-        GlobalLocalConfigInput {
+    ) -> GlobalWorkspaceLocalConfigInput {
+        GlobalWorkspaceLocalConfigInput {
             global: GlobalConfigInput::from_toml(&toml, error_sink),
             local: LocalConfigInput::from_toml(&toml, error_sink),
+            workspace: WorkspaceConfigInput::from_toml(&toml, error_sink),
+        }
+    }
+}
+
+/// Workspace and local config levels, all fields `Option<T>`, to describe fields that are actually set by
+/// some rust-analyzer.toml file or JSON blob. An empty rust-analyzer.toml corresponds to
+/// all fields being None.
+#[derive(Debug, Clone, Default)]
+#[allow(dead_code)]
+struct WorkspaceLocalConfigInput {
+    workspace: WorkspaceConfigInput,
+    local: LocalConfigInput,
+}
+
+impl WorkspaceLocalConfigInput {
+    #[allow(dead_code)]
+    const FIELDS: &'static [&'static [&'static str]] =
+        &[WorkspaceConfigInput::FIELDS, LocalConfigInput::FIELDS];
+    fn from_toml(toml: toml::Table, error_sink: &mut Vec<(String, toml::de::Error)>) -> Self {
+        Self {
+            workspace: WorkspaceConfigInput::from_toml(&toml, error_sink),
+            local: LocalConfigInput::from_toml(&toml, error_sink),
         }
     }
 }
@@ -3192,16 +3203,8 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
             "type": "string",
             "enum": ["per_workspace", "once"],
             "enumDescriptions": [
-                "The command will be executed for each workspace.",
-                "The command will be executed once."
-            ],
-        },
-        "InvocationLocation" => set! {
-            "type": "string",
-            "enum": ["workspace", "root"],
-            "enumDescriptions": [
-                "The command will be executed in the corresponding workspace root.",
-                "The command will be executed in the project root."
+                "The command will be executed for each Rust workspace with the workspace as the working directory.",
+                "The command will be executed once with the opened project as the working directory."
             ],
         },
         "Option<CheckOnSaveTargets>" => set! {
@@ -3461,7 +3464,7 @@ mod tests {
     #[test]
     fn proc_macro_srv_null() {
         let mut config =
-            Config::new(AbsPathBuf::assert(project_root()), Default::default(), vec![], None, None);
+            Config::new(AbsPathBuf::assert(project_root()), Default::default(), vec![], None);
 
         let mut change = ConfigChange::default();
         change.change_client_config(serde_json::json!({
@@ -3476,7 +3479,7 @@ mod tests {
     #[test]
     fn proc_macro_srv_abs() {
         let mut config =
-            Config::new(AbsPathBuf::assert(project_root()), Default::default(), vec![], None, None);
+            Config::new(AbsPathBuf::assert(project_root()), Default::default(), vec![], None);
         let mut change = ConfigChange::default();
         change.change_client_config(serde_json::json!({
         "procMacro" : {
@@ -3490,7 +3493,7 @@ mod tests {
     #[test]
     fn proc_macro_srv_rel() {
         let mut config =
-            Config::new(AbsPathBuf::assert(project_root()), Default::default(), vec![], None, None);
+            Config::new(AbsPathBuf::assert(project_root()), Default::default(), vec![], None);
 
         let mut change = ConfigChange::default();
 
@@ -3510,7 +3513,7 @@ mod tests {
     #[test]
     fn cargo_target_dir_unset() {
         let mut config =
-            Config::new(AbsPathBuf::assert(project_root()), Default::default(), vec![], None, None);
+            Config::new(AbsPathBuf::assert(project_root()), Default::default(), vec![], None);
 
         let mut change = ConfigChange::default();
 
@@ -3519,7 +3522,7 @@ mod tests {
         }));
 
         (config, _, _) = config.apply_change(change);
-        assert_eq!(config.cargo_targetDir(None), &None);
+        assert_eq!(config.cargo_targetDir(), &None);
         assert!(
             matches!(config.flycheck(), FlycheckConfig::CargoCommand { options, .. } if options.target_dir.is_none())
         );
@@ -3528,7 +3531,7 @@ mod tests {
     #[test]
     fn cargo_target_dir_subdir() {
         let mut config =
-            Config::new(AbsPathBuf::assert(project_root()), Default::default(), vec![], None, None);
+            Config::new(AbsPathBuf::assert(project_root()), Default::default(), vec![], None);
 
         let mut change = ConfigChange::default();
         change.change_client_config(serde_json::json!({
@@ -3537,7 +3540,7 @@ mod tests {
 
         (config, _, _) = config.apply_change(change);
 
-        assert_eq!(config.cargo_targetDir(None), &Some(TargetDirectory::UseSubdirectory(true)));
+        assert_eq!(config.cargo_targetDir(), &Some(TargetDirectory::UseSubdirectory(true)));
         assert!(
             matches!(config.flycheck(), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(Utf8PathBuf::from("target/rust-analyzer")))
         );
@@ -3546,7 +3549,7 @@ mod tests {
     #[test]
     fn cargo_target_dir_relative_dir() {
         let mut config =
-            Config::new(AbsPathBuf::assert(project_root()), Default::default(), vec![], None, None);
+            Config::new(AbsPathBuf::assert(project_root()), Default::default(), vec![], None);
 
         let mut change = ConfigChange::default();
         change.change_client_config(serde_json::json!({
@@ -3556,7 +3559,7 @@ mod tests {
         (config, _, _) = config.apply_change(change);
 
         assert_eq!(
-            config.cargo_targetDir(None),
+            config.cargo_targetDir(),
             &Some(TargetDirectory::Directory(Utf8PathBuf::from("other_folder")))
         );
         assert!(
@@ -3567,7 +3570,7 @@ mod tests {
     #[test]
     fn toml_unknown_key() {
         let config =
-            Config::new(AbsPathBuf::assert(project_root()), Default::default(), vec![], None, None);
+            Config::new(AbsPathBuf::assert(project_root()), Default::default(), vec![], None);
 
         let mut change = ConfigChange::default();
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs
index 034c49c3d5c69..5f2871ac99226 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs
@@ -75,7 +75,7 @@ impl DiagnosticCollection {
         flycheck_id: usize,
         file_id: FileId,
         diagnostic: lsp_types::Diagnostic,
-        fix: Option<Fix>,
+        fix: Option<Box<Fix>>,
     ) {
         let diagnostics = self.check.entry(flycheck_id).or_default().entry(file_id).or_default();
         for existing_diagnostic in diagnostics.iter() {
@@ -84,8 +84,10 @@ impl DiagnosticCollection {
             }
         }
 
-        let check_fixes = Arc::make_mut(&mut self.check_fixes);
-        check_fixes.entry(flycheck_id).or_default().entry(file_id).or_default().extend(fix);
+        if let Some(fix) = fix {
+            let check_fixes = Arc::make_mut(&mut self.check_fixes);
+            check_fixes.entry(flycheck_id).or_default().entry(file_id).or_default().push(*fix);
+        }
         diagnostics.push(diagnostic);
         self.changes.insert(file_id);
     }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 208a70bc02ad9..c3ab7f3ae7156 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -170,7 +170,7 @@ fn resolve_path(
 
 struct SubDiagnostic {
     related: lsp_types::DiagnosticRelatedInformation,
-    suggested_fix: Option<Fix>,
+    suggested_fix: Option<Box<Fix>>,
 }
 
 enum MappedRustChildDiagnostic {
@@ -241,7 +241,7 @@ fn map_rust_child_diagnostic(
                 location: location(config, workspace_root, spans[0], snap),
                 message: message.clone(),
             },
-            suggested_fix: Some(Fix {
+            suggested_fix: Some(Box::new(Fix {
                 ranges: spans
                     .iter()
                     .map(|&span| location(config, workspace_root, span, snap).range)
@@ -260,7 +260,7 @@ fn map_rust_child_diagnostic(
                     data: None,
                     command: None,
                 },
-            }),
+            })),
         })
     }
 }
@@ -269,7 +269,7 @@ fn map_rust_child_diagnostic(
 pub(crate) struct MappedRustDiagnostic {
     pub(crate) url: lsp_types::Url,
     pub(crate) diagnostic: lsp_types::Diagnostic,
-    pub(crate) fix: Option<Fix>,
+    pub(crate) fix: Option<Box<Fix>>,
 }
 
 /// Converts a Rust root diagnostic to LSP form
@@ -548,7 +548,6 @@ mod tests {
                 ClientCapabilities::default(),
                 Vec::new(),
                 None,
-                None,
             ),
         );
         let snap = state.snapshot();
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs
index 8f2e7d1ca26b9..b035d779a7d59 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs
@@ -15,20 +15,13 @@ use toolchain::Tool;
 
 use crate::command::{CommandHandle, ParseFromLine};
 
-#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
+#[derive(Clone, Debug, Default, PartialEq, Eq)]
 pub(crate) enum InvocationStrategy {
     Once,
     #[default]
     PerWorkspace,
 }
 
-#[derive(Clone, Debug, Default, PartialEq, Eq)]
-pub(crate) enum InvocationLocation {
-    Root(AbsPathBuf),
-    #[default]
-    Workspace,
-}
-
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub(crate) struct CargoOptions {
     pub(crate) target_triples: Vec<String>,
@@ -37,10 +30,19 @@ pub(crate) struct CargoOptions {
     pub(crate) all_features: bool,
     pub(crate) features: Vec<String>,
     pub(crate) extra_args: Vec<String>,
+    pub(crate) extra_test_bin_args: Vec<String>,
     pub(crate) extra_env: FxHashMap<String, String>,
     pub(crate) target_dir: Option<Utf8PathBuf>,
 }
 
+#[derive(Clone)]
+pub(crate) enum Target {
+    Bin(String),
+    Example(String),
+    Benchmark(String),
+    Test(String),
+}
+
 impl CargoOptions {
     pub(crate) fn apply_on_command(&self, cmd: &mut Command) {
         for target in &self.target_triples {
@@ -79,7 +81,6 @@ pub(crate) enum FlycheckConfig {
         args: Vec<String>,
         extra_env: FxHashMap<String, String>,
         invocation_strategy: InvocationStrategy,
-        invocation_location: InvocationLocation,
     },
 }
 
@@ -127,13 +128,13 @@ impl FlycheckHandle {
 
     /// Schedule a re-start of the cargo check worker to do a workspace wide check.
     pub(crate) fn restart_workspace(&self, saved_file: Option<AbsPathBuf>) {
-        self.sender.send(StateChange::Restart { package: None, saved_file }).unwrap();
+        self.sender.send(StateChange::Restart { package: None, saved_file, target: None }).unwrap();
     }
 
     /// Schedule a re-start of the cargo check worker to do a package wide check.
-    pub(crate) fn restart_for_package(&self, package: String) {
+    pub(crate) fn restart_for_package(&self, package: String, target: Option<Target>) {
         self.sender
-            .send(StateChange::Restart { package: Some(package), saved_file: None })
+            .send(StateChange::Restart { package: Some(package), saved_file: None, target })
             .unwrap();
     }
 
@@ -191,7 +192,7 @@ pub(crate) enum Progress {
 }
 
 enum StateChange {
-    Restart { package: Option<String>, saved_file: Option<AbsPathBuf> },
+    Restart { package: Option<String>, saved_file: Option<AbsPathBuf>, target: Option<Target> },
     Cancel,
 }
 
@@ -218,6 +219,7 @@ struct FlycheckActor {
     status: FlycheckStatus,
 }
 
+#[allow(clippy::large_enum_variant)]
 enum Event {
     RequestStateChange(StateChange),
     CheckEvent(Option<CargoCheckMessage>),
@@ -278,7 +280,7 @@ impl FlycheckActor {
                     tracing::debug!(flycheck_id = self.id, "flycheck cancelled");
                     self.cancel_check_process();
                 }
-                Event::RequestStateChange(StateChange::Restart { package, saved_file }) => {
+                Event::RequestStateChange(StateChange::Restart { package, saved_file, target }) => {
                     // Cancel the previously spawned process
                     self.cancel_check_process();
                     while let Ok(restart) = inbox.recv_timeout(Duration::from_millis(50)) {
@@ -288,11 +290,12 @@ impl FlycheckActor {
                         }
                     }
 
-                    let command =
-                        match self.check_command(package.as_deref(), saved_file.as_deref()) {
-                            Some(c) => c,
-                            None => continue,
-                        };
+                    let Some(command) =
+                        self.check_command(package.as_deref(), saved_file.as_deref(), target)
+                    else {
+                        continue;
+                    };
+
                     let formatted_command = format!("{command:?}");
 
                     tracing::debug!(?command, "will restart flycheck");
@@ -388,6 +391,7 @@ impl FlycheckActor {
         &self,
         package: Option<&str>,
         saved_file: Option<&AbsPath>,
+        target: Option<Target>,
     ) -> Option<Command> {
         match &self.config {
             FlycheckConfig::CargoCommand { command, options, ansi_color_output } => {
@@ -403,6 +407,15 @@ impl FlycheckActor {
                     None => cmd.arg("--workspace"),
                 };
 
+                if let Some(tgt) = target {
+                    match tgt {
+                        Target::Bin(tgt) => cmd.arg("--bin").arg(tgt),
+                        Target::Example(tgt) => cmd.arg("--example").arg(tgt),
+                        Target::Test(tgt) => cmd.arg("--test").arg(tgt),
+                        Target::Benchmark(tgt) => cmd.arg("--bench").arg(tgt),
+                    };
+                }
+
                 cmd.arg(if *ansi_color_output {
                     "--message-format=json-diagnostic-rendered-ansi"
                 } else {
@@ -423,30 +436,17 @@ impl FlycheckActor {
                 cmd.args(&options.extra_args);
                 Some(cmd)
             }
-            FlycheckConfig::CustomCommand {
-                command,
-                args,
-                extra_env,
-                invocation_strategy,
-                invocation_location,
-            } => {
+            FlycheckConfig::CustomCommand { command, args, extra_env, invocation_strategy } => {
                 let mut cmd = Command::new(command);
                 cmd.envs(extra_env);
 
-                match invocation_location {
-                    InvocationLocation::Workspace => {
-                        match invocation_strategy {
-                            InvocationStrategy::Once => {
-                                cmd.current_dir(&self.root);
-                            }
-                            InvocationStrategy::PerWorkspace => {
-                                // FIXME: cmd.current_dir(&affected_workspace);
-                                cmd.current_dir(&self.root);
-                            }
-                        }
+                match invocation_strategy {
+                    InvocationStrategy::Once => {
+                        cmd.current_dir(&self.root);
                     }
-                    InvocationLocation::Root(root) => {
-                        cmd.current_dir(root);
+                    InvocationStrategy::PerWorkspace => {
+                        // FIXME: cmd.current_dir(&affected_workspace);
+                        cmd.current_dir(&self.root);
                     }
                 }
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
index 7a7ec1d77e09b..9d0082c370c7e 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
@@ -380,7 +380,7 @@ impl GlobalState {
             || !self.config.same_source_root_parent_map(&self.local_roots_parent_map)
         {
             let config_change = {
-                let user_config_path = self.config.user_config_path();
+                let user_config_path = Config::user_config_path();
                 let mut change = ConfigChange::default();
                 let db = self.analysis_host.raw_database();
 
@@ -399,7 +399,7 @@ impl GlobalState {
                     .collect_vec();
 
                 for (file_id, (_change_kind, vfs_path)) in modified_ratoml_files {
-                    if vfs_path == *user_config_path {
+                    if vfs_path.as_path() == user_config_path {
                         change.change_user_config(Some(db.file_text(file_id)));
                         continue;
                     }
@@ -667,7 +667,7 @@ impl GlobalStateSnapshot {
         for workspace in self.workspaces.iter() {
             match &workspace.kind {
                 ProjectWorkspaceKind::Cargo { cargo, .. }
-                | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _)), .. } => {
+                | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, _)), .. } => {
                     let Some(target_idx) = cargo.target_by_root(path) else {
                         continue;
                     };
@@ -696,6 +696,7 @@ impl GlobalStateSnapshot {
                     };
 
                     return Some(TargetSpec::ProjectJson(ProjectJsonTargetSpec {
+                        crate_id,
                         label: build.label,
                         target_kind: build.target_kind,
                         shell_runnables: project.runnables().to_owned(),
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs
index a105ec638203b..f03de8ce0f038 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs
@@ -139,16 +139,26 @@ impl RequestDispatcher<'_> {
         self.on_with_thread_intent::<true, ALLOW_RETRYING, R>(ThreadIntent::Worker, f)
     }
 
-    /// Dispatches a latency-sensitive request onto the thread pool.
+    /// Dispatches a latency-sensitive request onto the thread pool. When the VFS is marked not
+    /// ready this will return a default constructed [`R::Result`].
     pub(crate) fn on_latency_sensitive<const ALLOW_RETRYING: bool, R>(
         &mut self,
         f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result<R::Result>,
     ) -> &mut Self
     where
-        R: lsp_types::request::Request + 'static,
-        R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
-        R::Result: Serialize,
+        R: lsp_types::request::Request<
+                Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
+                Result: Serialize + Default,
+            > + 'static,
     {
+        if !self.global_state.vfs_done {
+            if let Some(lsp_server::Request { id, .. }) =
+                self.req.take_if(|it| it.method == R::METHOD)
+            {
+                self.global_state.respond(lsp_server::Response::new_ok(id, R::Result::default()));
+            }
+            return self;
+        }
         self.on_with_thread_intent::<true, ALLOW_RETRYING, R>(ThreadIntent::LatencySensitive, f)
     }
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
index de5d1f231368b..38b88ff2d0403 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
@@ -10,16 +10,19 @@ use lsp_types::{
     DidOpenTextDocumentParams, DidSaveTextDocumentParams, WorkDoneProgressCancelParams,
 };
 use paths::Utf8PathBuf;
+use stdx::TupleExt;
 use triomphe::Arc;
 use vfs::{AbsPathBuf, ChangeKind, VfsPath};
 
 use crate::{
     config::{Config, ConfigChange},
+    flycheck::Target,
     global_state::{FetchWorkspaceRequest, GlobalState},
     lsp::{from_proto, utils::apply_document_changes},
     lsp_ext::{self, RunFlycheckParams},
     mem_docs::DocumentData,
     reload,
+    target_spec::TargetSpec,
 };
 
 pub(crate) fn handle_cancel(state: &mut GlobalState, params: CancelParams) -> anyhow::Result<()> {
@@ -287,17 +290,41 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
         let world = state.snapshot();
         let mut updated = false;
         let task = move || -> std::result::Result<(), ide::Cancelled> {
-            // Trigger flychecks for all workspaces that depend on the saved file
-            // Crates containing or depending on the saved file
-            let crate_ids: Vec<_> = world
-                .analysis
-                .crates_for(file_id)?
-                .into_iter()
-                .flat_map(|id| world.analysis.transitive_rev_deps(id))
-                .flatten()
-                .unique()
-                .collect();
+            // Is the target binary? If so we let flycheck run only for the workspace that contains the crate.
+            let target = TargetSpec::for_file(&world, file_id)?.and_then(|it| {
+                let tgt_kind = it.target_kind();
+                let (tgt_name, crate_id) = match it {
+                    TargetSpec::Cargo(c) => (c.target, c.crate_id),
+                    TargetSpec::ProjectJson(p) => (p.label, p.crate_id),
+                };
+
+                let tgt = match tgt_kind {
+                    project_model::TargetKind::Bin => Target::Bin(tgt_name),
+                    project_model::TargetKind::Example => Target::Example(tgt_name),
+                    project_model::TargetKind::Test => Target::Test(tgt_name),
+                    project_model::TargetKind::Bench => Target::Benchmark(tgt_name),
+                    _ => return None,
+                };
+
+                Some((tgt, crate_id))
+            });
 
+            let crate_ids = match target {
+                // Trigger flychecks for the only crate which the target belongs to
+                Some((_, krate)) => vec![krate],
+                None => {
+                    // Trigger flychecks for all workspaces that depend on the saved file
+                    // Crates containing or depending on the saved file
+                    world
+                        .analysis
+                        .crates_for(file_id)?
+                        .into_iter()
+                        .flat_map(|id| world.analysis.transitive_rev_deps(id))
+                        .flatten()
+                        .unique()
+                        .collect::<Vec<_>>()
+                }
+            };
             let crate_root_paths: Vec<_> = crate_ids
                 .iter()
                 .filter_map(|&crate_id| {
@@ -317,7 +344,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
                 let package = match &ws.kind {
                     project_model::ProjectWorkspaceKind::Cargo { cargo, .. }
                     | project_model::ProjectWorkspaceKind::DetachedFile {
-                        cargo: Some((cargo, _)),
+                        cargo: Some((cargo, _, _)),
                         ..
                     } => cargo.packages().find_map(|pkg| {
                         let has_target_with_root = cargo[pkg]
@@ -346,8 +373,11 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
                 for (id, package) in workspace_ids.clone() {
                     if id == flycheck.id() {
                         updated = true;
-                        match package.filter(|_| !world.config.flycheck_workspace()) {
-                            Some(package) => flycheck.restart_for_package(package),
+                        match package
+                            .filter(|_| !world.config.flycheck_workspace() || target.is_some())
+                        {
+                            Some(package) => flycheck
+                                .restart_for_package(package, target.clone().map(TupleExt::head)),
                             None => flycheck.restart_workspace(saved_file.clone()),
                         }
                         continue;
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
index 34325ac7a93aa..1ad5ff0c8cdc1 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
@@ -799,7 +799,7 @@ pub(crate) fn handle_parent_module(
                 .iter()
                 .filter_map(|ws| match &ws.kind {
                     ProjectWorkspaceKind::Cargo { cargo, .. }
-                    | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _)), .. } => {
+                    | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, _)), .. } => {
                         cargo.parent_manifests(&manifest_path)
                     }
                     _ => None,
@@ -1839,7 +1839,7 @@ pub(crate) fn handle_open_docs(
 
     let ws_and_sysroot = snap.workspaces.iter().find_map(|ws| match &ws.kind {
         ProjectWorkspaceKind::Cargo { cargo, .. }
-        | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _)), .. } => {
+        | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, _)), .. } => {
             Some((cargo, &ws.sysroot))
         }
         ProjectWorkspaceKind::Json { .. } => None,
@@ -2113,9 +2113,9 @@ fn run_rustfmt(
     let edition = editions.iter().copied().max();
 
     let line_index = snap.file_line_index(file_id)?;
-    let sr = snap.analysis.source_root_id(file_id)?;
+    let source_root_id = snap.analysis.source_root_id(file_id).ok();
 
-    let mut command = match snap.config.rustfmt(Some(sr)) {
+    let mut command = match snap.config.rustfmt(source_root_id) {
         RustfmtConfig::Rustfmt { extra_args, enable_range_formatting } => {
             // FIXME: Set RUSTUP_TOOLCHAIN
             let mut cmd = process::Command::new(toolchain::Tool::Rustfmt.path());
@@ -2303,7 +2303,7 @@ pub(crate) fn internal_testing_fetch_config(
         .transpose()?;
     serde_json::to_value(match &*params.config {
         "local" => state.config.assist(source_root).assist_emit_must_use,
-        "global" => matches!(
+        "workspace" => matches!(
             state.config.rustfmt(source_root),
             RustfmtConfig::Rustfmt { enable_range_formatting: true, .. }
         ),
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
index 1d4ee71e5c1a0..616d6b49351c1 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
@@ -689,7 +689,7 @@ impl GlobalState {
                         self.fetch_workspaces_queue
                             .op_completed(Some((workspaces, force_reload_crate_graph)));
                         if let Err(e) = self.fetch_workspace_error() {
-                            error!("FetchWorkspaceError:\n{e}");
+                            error!("FetchWorkspaceError: {e}");
                         }
                         self.wants_to_switch = Some("fetched workspace".to_owned());
                         (Progress::End, None)
@@ -729,7 +729,7 @@ impl GlobalState {
                     BuildDataProgress::End(build_data_result) => {
                         self.fetch_build_data_queue.op_completed(build_data_result);
                         if let Err(e) = self.fetch_build_data_error() {
-                            error!("FetchBuildDataError:\n{e}");
+                            error!("FetchBuildDataError: {e}");
                         }
 
                         if self.wants_to_switch.is_none() {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs
index 99f9e9829c936..eab9733872402 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs
@@ -3,6 +3,26 @@
 
 pub(crate) type Cause = String;
 
+/// A single-item queue that allows callers to request an operation to
+/// be performed later.
+///
+/// ```
+/// let queue = OpQueue::default();
+///
+/// // Request work to be done.
+/// queue.request_op("user pushed a button", ());
+///
+/// // In a later iteration of the server loop, we start the work.
+/// if let Some((_cause, ())) = queue.should_start_op() {
+///     dbg!("Some slow operation here");
+/// }
+///
+/// // In an even later iteration of the server loop, we can see that the work
+/// // was completed.
+/// if !queue.op_in_progress() {
+///     dbg!("Work has been done!");
+/// }
+/// ```
 #[derive(Debug)]
 pub(crate) struct OpQueue<Args = (), Output = ()> {
     op_requested: Option<(Cause, Args)>,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
index dee34b1b393d2..68366136eda60 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -61,6 +61,10 @@ pub(crate) enum ProcMacroProgress {
 }
 
 impl GlobalState {
+    /// Is the server quiescent?
+    ///
+    /// This indicates that we've fully loaded the projects and
+    /// are ready to do semantic work.
     pub(crate) fn is_quiescent(&self) -> bool {
         self.vfs_done
             && self.last_reported_status.is_some()
@@ -71,6 +75,15 @@ impl GlobalState {
             && self.vfs_progress_config_version >= self.vfs_config_version
     }
 
+    /// Is the server ready to respond to analysis dependent LSP requests?
+    ///
+    /// Unlike `is_quiescent`, this returns false when we're indexing
+    /// the project, because we're holding the salsa lock and cannot
+    /// respond to LSP requests that depend on salsa data.
+    fn is_fully_ready(&self) -> bool {
+        self.is_quiescent() && !self.prime_caches_queue.op_in_progress()
+    }
+
     pub(crate) fn update_configuration(&mut self, config: Config) {
         let _p = tracing::info_span!("GlobalState::update_configuration").entered();
         let old_config = mem::replace(&mut self.config, Arc::new(config));
@@ -102,13 +115,15 @@ impl GlobalState {
     }
 
     pub(crate) fn current_status(&self) -> lsp_ext::ServerStatusParams {
-        let quiescent = self.is_quiescent();
-        let mut status =
-            lsp_ext::ServerStatusParams { health: lsp_ext::Health::Ok, quiescent, message: None };
+        let mut status = lsp_ext::ServerStatusParams {
+            health: lsp_ext::Health::Ok,
+            quiescent: self.is_fully_ready(),
+            message: None,
+        };
         let mut message = String::new();
 
-        if !self.config.cargo_autoreload(None)
-            && quiescent
+        if !self.config.cargo_autoreload()
+            && self.is_quiescent()
             && self.fetch_workspaces_queue.op_requested()
             && self.config.discover_workspace_config().is_none()
         {
@@ -165,6 +180,19 @@ impl GlobalState {
                 self.proc_macro_clients.iter().map(Some).chain(iter::repeat_with(|| None));
 
             for (ws, proc_macro_client) in self.workspaces.iter().zip(proc_macro_clients) {
+                if let ProjectWorkspaceKind::Cargo { error: Some(error), .. }
+                | ProjectWorkspaceKind::DetachedFile {
+                    cargo: Some((_, _, Some(error))), ..
+                } = &ws.kind
+                {
+                    status.health |= lsp_ext::Health::Warning;
+                    format_to!(
+                        message,
+                        "Failed to read Cargo metadata with dependencies for `{}`: {:#}\n\n",
+                        ws.manifest_or_root(),
+                        error
+                    );
+                }
                 if let Some(err) = ws.sysroot.error() {
                     status.health |= lsp_ext::Health::Warning;
                     format_to!(
@@ -537,8 +565,25 @@ impl GlobalState {
                         .collect()
                 };
 
+            // Also explicitly watch any build files configured in JSON project files.
+            for ws in self.workspaces.iter() {
+                if let ProjectWorkspaceKind::Json(project_json) = &ws.kind {
+                    for (_, krate) in project_json.crates() {
+                        let Some(build) = &krate.build else {
+                            continue;
+                        };
+                        watchers.push(lsp_types::FileSystemWatcher {
+                            glob_pattern: lsp_types::GlobPattern::String(
+                                build.build_file.to_string(),
+                            ),
+                            kind: None,
+                        });
+                    }
+                }
+            }
+
             watchers.extend(
-                iter::once(self.config.user_config_path().as_path())
+                iter::once(Config::user_config_path())
                     .chain(self.workspaces.iter().map(|ws| ws.manifest().map(ManifestPath::as_ref)))
                     .flatten()
                     .map(|glob_pattern| lsp_types::FileSystemWatcher {
@@ -752,18 +797,22 @@ impl GlobalState {
             FlycheckConfig::CargoCommand { .. } => {
                 crate::flycheck::InvocationStrategy::PerWorkspace
             }
-            FlycheckConfig::CustomCommand { invocation_strategy, .. } => invocation_strategy,
+            FlycheckConfig::CustomCommand { ref invocation_strategy, .. } => {
+                invocation_strategy.clone()
+            }
         };
 
         self.flycheck = match invocation_strategy {
-            crate::flycheck::InvocationStrategy::Once => vec![FlycheckHandle::spawn(
-                0,
-                sender,
-                config,
-                None,
-                self.config.root_path().clone(),
-                None,
-            )],
+            crate::flycheck::InvocationStrategy::Once => {
+                vec![FlycheckHandle::spawn(
+                    0,
+                    sender,
+                    config,
+                    None,
+                    self.config.root_path().clone(),
+                    None,
+                )]
+            }
             crate::flycheck::InvocationStrategy::PerWorkspace => {
                 self.workspaces
                     .iter()
@@ -774,7 +823,7 @@ impl GlobalState {
                             match &ws.kind {
                                 ProjectWorkspaceKind::Cargo { cargo, .. }
                                 | ProjectWorkspaceKind::DetachedFile {
-                                    cargo: Some((cargo, _)),
+                                    cargo: Some((cargo, _, _)),
                                     ..
                                 } => (cargo.workspace_root(), Some(cargo.manifest_path())),
                                 ProjectWorkspaceKind::Json(project) => {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs
index 965fd415e9967..954e13cbf2727 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs
@@ -62,6 +62,7 @@ pub(crate) struct CargoTargetSpec {
 
 #[derive(Clone, Debug)]
 pub(crate) struct ProjectJsonTargetSpec {
+    pub(crate) crate_id: CrateId,
     pub(crate) label: String,
     pub(crate) target_kind: TargetKind,
     pub(crate) shell_runnables: Vec<Runnable>,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs
index 293cff474337d..5e43a3c60d86e 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs
@@ -102,6 +102,11 @@ impl CargoTestHandle {
         }
         cmd.args(["-Z", "unstable-options"]);
         cmd.arg("--format=json");
+
+        for extra_arg in options.extra_test_bin_args {
+            cmd.arg(extra_arg);
+        }
+
         Ok(Self { _handle: CommandHandle::spawn(cmd, sender)? })
     }
 }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/crate_graph.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/crate_graph.rs
index b8a82fd6a72ee..04b6713b8d188 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/crate_graph.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/crate_graph.rs
@@ -20,6 +20,7 @@ fn load_cargo_with_fake_sysroot(file: &str) -> ProjectWorkspace {
             build_scripts: WorkspaceBuildScripts::default(),
             rustc: Err(None),
             cargo_config_extra_env: Default::default(),
+            error: None,
         },
         sysroot: get_fake_sysroot(),
         rustc_cfg: Vec::new(),
@@ -93,7 +94,7 @@ fn test_deduplicate_origin_dev() {
         }
     }
 
-    assert!(crates_named_p2.len() == 1);
+    assert_eq!(crates_named_p2.len(), 1);
     let p2 = crates_named_p2[0];
     assert!(p2.origin.is_local());
 }
@@ -119,7 +120,7 @@ fn test_deduplicate_origin_dev_rev() {
         }
     }
 
-    assert!(crates_named_p2.len() == 1);
+    assert_eq!(crates_named_p2.len(), 1);
     let p2 = crates_named_p2[0];
     assert!(p2.origin.is_local());
 }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs
index c06ba9eee1477..295d1d4e8e909 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs
@@ -8,6 +8,7 @@ use lsp_types::{
 };
 use paths::Utf8PathBuf;
 
+use rust_analyzer::config::Config;
 use rust_analyzer::lsp::ext::{InternalTestingFetchConfig, InternalTestingFetchConfigParams};
 use serde_json::json;
 use test_utils::skip_slow_tests;
@@ -17,14 +18,13 @@ enum QueryType {
     /// A query whose config key is a part of the global configs, so that
     /// testing for changes to this config means testing if global changes
     /// take affect.
-    Global,
+    Workspace,
 }
 
 struct RatomlTest {
     urls: Vec<Url>,
     server: Server,
     tmp_path: Utf8PathBuf,
-    user_config_dir: Utf8PathBuf,
 }
 
 impl RatomlTest {
@@ -41,11 +41,7 @@ impl RatomlTest {
 
         let full_fixture = fixtures.join("\n");
 
-        let user_cnf_dir = TestDir::new();
-        let user_config_dir = user_cnf_dir.path().to_owned();
-
-        let mut project =
-            Project::with_fixture(&full_fixture).tmp_dir(tmp_dir).user_config_dir(user_cnf_dir);
+        let mut project = Project::with_fixture(&full_fixture).tmp_dir(tmp_dir);
 
         for root in roots {
             project = project.root(root);
@@ -57,7 +53,7 @@ impl RatomlTest {
 
         let server = project.server().wait_until_workspace_is_loaded();
 
-        let mut case = Self { urls: vec![], server, tmp_path, user_config_dir };
+        let mut case = Self { urls: vec![], server, tmp_path };
         let urls = fixtures.iter().map(|fixture| case.fixture_path(fixture)).collect::<Vec<_>>();
         case.urls = urls;
         case
@@ -81,7 +77,7 @@ impl RatomlTest {
         let mut spl = spl.into_iter();
         if let Some(first) = spl.next() {
             if first == "$$CONFIG_DIR$$" {
-                path = self.user_config_dir.clone();
+                path = Config::user_config_path().unwrap().to_path_buf().into();
             } else {
                 path = path.join(first);
             }
@@ -165,7 +161,7 @@ impl RatomlTest {
     fn query(&self, query: QueryType, source_file_idx: usize) -> bool {
         let config = match query {
             QueryType::Local => "local".to_owned(),
-            QueryType::Global => "global".to_owned(),
+            QueryType::Workspace => "workspace".to_owned(),
         };
         let res = self.server.send_request::<InternalTestingFetchConfig>(
             InternalTestingFetchConfigParams {
@@ -823,10 +819,8 @@ fn ratoml_multiple_ratoml_in_single_source_root() {
 //         assert!(!server.query(QueryType::AssistEmitMustUse, 5));
 //     }
 
-/// Having a ratoml file at the root of a project enables
-/// configuring global level configurations as well.
 #[test]
-fn ratoml_in_root_is_global() {
+fn ratoml_in_root_is_workspace() {
     if skip_slow_tests() {
         return;
     }
@@ -854,7 +848,7 @@ fn main() {
         None,
     );
 
-    assert!(server.query(QueryType::Global, 2));
+    assert!(server.query(QueryType::Workspace, 2));
 }
 
 #[test]
@@ -886,9 +880,9 @@ fn main() {
         None,
     );
 
-    assert!(server.query(QueryType::Global, 2));
+    assert!(server.query(QueryType::Workspace, 2));
     server.edit(1, "rustfmt.rangeFormatting.enable = false".to_owned());
-    assert!(!server.query(QueryType::Global, 2));
+    assert!(!server.query(QueryType::Workspace, 2));
 }
 
 #[test]
@@ -920,7 +914,7 @@ fn main() {
         None,
     );
 
-    assert!(server.query(QueryType::Global, 2));
+    assert!(server.query(QueryType::Workspace, 2));
     server.delete(1);
-    assert!(!server.query(QueryType::Global, 2));
+    assert!(!server.query(QueryType::Workspace, 2));
 }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
index 081ee5fa3e4e7..06ce984681833 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
@@ -8,6 +8,7 @@ use std::{
 use crossbeam_channel::{after, select, Receiver};
 use lsp_server::{Connection, Message, Notification, Request};
 use lsp_types::{notification::Exit, request::Shutdown, TextDocumentIdentifier, Url};
+use parking_lot::{Mutex, MutexGuard};
 use paths::{Utf8Path, Utf8PathBuf};
 use rust_analyzer::{
     config::{Config, ConfigChange, ConfigErrors},
@@ -27,7 +28,6 @@ pub(crate) struct Project<'a> {
     roots: Vec<Utf8PathBuf>,
     config: serde_json::Value,
     root_dir_contains_symlink: bool,
-    user_config_path: Option<Utf8PathBuf>,
 }
 
 impl Project<'_> {
@@ -51,15 +51,9 @@ impl Project<'_> {
                 }
             }),
             root_dir_contains_symlink: false,
-            user_config_path: None,
         }
     }
 
-    pub(crate) fn user_config_dir(mut self, config_path_dir: TestDir) -> Self {
-        self.user_config_path = Some(config_path_dir.path().to_owned());
-        self
-    }
-
     pub(crate) fn tmp_dir(mut self, tmp_dir: TestDir) -> Self {
         self.tmp_dir = Some(tmp_dir);
         self
@@ -91,6 +85,7 @@ impl Project<'_> {
     }
 
     pub(crate) fn server(self) -> Server {
+        static CONFIG_DIR_LOCK: Mutex<()> = Mutex::new(());
         let tmp_dir = self.tmp_dir.unwrap_or_else(|| {
             if self.root_dir_contains_symlink {
                 TestDir::new_symlink()
@@ -122,9 +117,13 @@ impl Project<'_> {
         assert!(mini_core.is_none());
         assert!(toolchain.is_none());
 
+        let mut config_dir_guard = None;
         for entry in fixture {
             if let Some(pth) = entry.path.strip_prefix("/$$CONFIG_DIR$$") {
-                let path = self.user_config_path.clone().unwrap().join(&pth['/'.len_utf8()..]);
+                if config_dir_guard.is_none() {
+                    config_dir_guard = Some(CONFIG_DIR_LOCK.lock());
+                }
+                let path = Config::user_config_path().unwrap().join(&pth['/'.len_utf8()..]);
                 fs::create_dir_all(path.parent().unwrap()).unwrap();
                 fs::write(path.as_path(), entry.text.as_bytes()).unwrap();
             } else {
@@ -201,7 +200,6 @@ impl Project<'_> {
             },
             roots,
             None,
-            self.user_config_path,
         );
         let mut change = ConfigChange::default();
 
@@ -213,7 +211,7 @@ impl Project<'_> {
 
         config.rediscover_workspaces();
 
-        Server::new(tmp_dir.keep(), config)
+        Server::new(config_dir_guard, tmp_dir.keep(), config)
     }
 }
 
@@ -228,10 +226,15 @@ pub(crate) struct Server {
     client: Connection,
     /// XXX: remove the tempdir last
     dir: TestDir,
+    _config_dir_guard: Option<MutexGuard<'static, ()>>,
 }
 
 impl Server {
-    fn new(dir: TestDir, config: Config) -> Server {
+    fn new(
+        config_dir_guard: Option<MutexGuard<'static, ()>>,
+        dir: TestDir,
+        config: Config,
+    ) -> Server {
         let (connection, client) = Connection::memory();
 
         let _thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
@@ -239,7 +242,14 @@ impl Server {
             .spawn(move || main_loop(config, connection).unwrap())
             .expect("failed to spawn a thread");
 
-        Server { req_id: Cell::new(1), dir, messages: Default::default(), client, _thread }
+        Server {
+            req_id: Cell::new(1),
+            dir,
+            messages: Default::default(),
+            client,
+            _thread,
+            _config_dir_guard: config_dir_guard,
+        }
     }
 
     pub(crate) fn doc_id(&self, rel_path: &str) -> TextDocumentIdentifier {
diff --git a/src/tools/rust-analyzer/crates/salsa/LICENSE-APACHE b/src/tools/rust-analyzer/crates/salsa/LICENSE-APACHE
index 16fe87b06e802..1b5ec8b78e237 100644
--- a/src/tools/rust-analyzer/crates/salsa/LICENSE-APACHE
+++ b/src/tools/rust-analyzer/crates/salsa/LICENSE-APACHE
@@ -174,28 +174,3 @@ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
    of your accepting any such warranty or additional liability.
 
 END OF TERMS AND CONDITIONS
-
-APPENDIX: How to apply the Apache License to your work.
-
-   To apply the Apache License to your work, attach the following
-   boilerplate notice, with the fields enclosed by brackets "[]"
-   replaced with your own identifying information. (Don't include
-   the brackets!)  The text should be enclosed in the appropriate
-   comment syntax for the file format. We also recommend that a
-   file or class name and description of purpose be included on the
-   same "printed page" as the copyright notice for easier
-   identification within third-party archives.
-
-Copyright [yyyy] [name of copyright owner]
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-	http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs
index b0afd245c52e3..56e43e82ed27a 100644
--- a/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs
@@ -301,13 +301,11 @@ where
                         };
                     }
                     let leaf: tt::Leaf<_> = match kind {
-                        T![true] | T![false] => make_ident!(),
-                        IDENT => {
+                        k if k.is_any_identifier() => {
                             let text = token.to_text(conv);
                             tt::Ident::new(&text, conv.span_for(abs_range)).into()
                         }
                         UNDERSCORE => make_ident!(),
-                        k if k.is_keyword() => make_ident!(),
                         k if k.is_literal() => {
                             let text = token.to_text(conv);
                             let span = conv.span_for(abs_range);
diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/to_parser_input.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/to_parser_input.rs
index 2c54899268834..14216e3093285 100644
--- a/src/tools/rust-analyzer/crates/syntax-bridge/src/to_parser_input.rs
+++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/to_parser_input.rs
@@ -64,14 +64,12 @@ pub fn to_parser_input<S: Copy + fmt::Debug>(
                         "_" => res.push(T![_]),
                         i if i.starts_with('\'') => res.push(LIFETIME_IDENT),
                         _ if ident.is_raw.yes() => res.push(IDENT),
-                        "gen" if !edition.at_least_2024() => res.push(IDENT),
-                        "dyn" if !edition.at_least_2018() => res.push_ident(DYN_KW),
-                        "async" | "await" | "try" if !edition.at_least_2018() => res.push(IDENT),
-                        text => match SyntaxKind::from_keyword(text) {
+                        text => match SyntaxKind::from_keyword(text, edition) {
                             Some(kind) => res.push(kind),
                             None => {
-                                let contextual_keyword = SyntaxKind::from_contextual_keyword(text)
-                                    .unwrap_or(SyntaxKind::IDENT);
+                                let contextual_keyword =
+                                    SyntaxKind::from_contextual_keyword(text, edition)
+                                        .unwrap_or(SyntaxKind::IDENT);
                                 res.push_ident(contextual_keyword);
                             }
                         },
diff --git a/src/tools/rust-analyzer/crates/syntax/Cargo.toml b/src/tools/rust-analyzer/crates/syntax/Cargo.toml
index 994c21469ff45..fcb9b0ea354f1 100644
--- a/src/tools/rust-analyzer/crates/syntax/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/syntax/Cargo.toml
@@ -16,9 +16,8 @@ doctest = false
 cov-mark = "2.0.0-pre.1"
 either.workspace = true
 itertools.workspace = true
-rowan = "0.15.15"
+rowan = "=0.15.15"
 rustc-hash.workspace = true
-once_cell = "1.17.0"
 indexmap.workspace = true
 smol_str.workspace = true
 triomphe.workspace = true
diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram
index c23bcd6914910..43375ce6ae051 100644
--- a/src/tools/rust-analyzer/crates/syntax/rust.ungram
+++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram
@@ -9,8 +9,6 @@
 //   //          -- comment
 //   Name =      -- non-terminal definition
 //   'ident'     -- keyword or punct token (terminal)
-//   '?ident'    -- contextual keyword (terminal)
-//   too)
 //   '#ident'    -- generic token (terminal)
 //   '@ident'    -- literal token (terminal)
 //   A B         -- sequence
@@ -40,8 +38,12 @@ PathSegment =
   '::'? NameRef
 | NameRef GenericArgList?
 | NameRef ParamList RetType?
+| NameRef ReturnTypeSyntax
 | '<' Type ('as' PathType)? '>'
 
+ReturnTypeSyntax =
+  '(' '..' ')'
+
 
 //*************************//
 //        Generics         //
@@ -61,7 +63,7 @@ TypeArg =
 
 AssocTypeArg =
   NameRef
-  (GenericArgList | ParamList RetType?)?
+  (GenericArgList | ParamList RetType? | ReturnTypeSyntax)?
   (':' TypeBoundList | ('=' Type | ConstArg))
 
 LifetimeArg =
@@ -152,7 +154,7 @@ Item =
 
 MacroRules =
   Attr* Visibility?
-  '?macro_rules' '!' Name
+  'macro_rules' '!' Name
   TokenTree
 
 MacroDef =
@@ -188,7 +190,7 @@ UseTreeList =
 
 Fn =
  Attr* Visibility?
- '?default'? 'const'? 'async'? 'unsafe'? Abi?
+ 'default'? 'const'? 'async'? 'gen'? 'unsafe'? Abi?
  'fn' Name GenericParamList? ParamList RetType? WhereClause?
  (body:BlockExpr | ';')
 
@@ -220,7 +222,7 @@ RetType =
 
 TypeAlias =
   Attr* Visibility?
-  '?default'?
+  'default'?
   'type' Name GenericParamList? (':' TypeBoundList?)? WhereClause?
   ('=' Type)? ';'
 
@@ -263,7 +265,7 @@ Variant =
 
 Union =
   Attr* Visibility?
-  '?union' Name GenericParamList? WhereClause?
+  'union' Name GenericParamList? WhereClause?
   RecordFieldList
 
 // A Data Type.
@@ -276,7 +278,7 @@ Adt =
 
 Const =
   Attr* Visibility?
-  '?default'?
+  'default'?
   'const' (Name | '_') ':' Type
   ('=' body:Expr)? ';'
 
@@ -287,7 +289,7 @@ Static =
 
 Trait =
   Attr* Visibility?
-  'unsafe'? '?auto'?
+  'unsafe'? 'auto'?
   'trait' Name GenericParamList?
   (':' TypeBoundList?)? WhereClause? AssocItemList
 
@@ -306,7 +308,7 @@ AssocItem =
 
 Impl =
   Attr* Visibility?
-  '?default'? 'unsafe'?
+  'default'? 'unsafe'?
   'impl' GenericParamList? ('const'? '!'? trait:Type 'for')? self_ty:Type WhereClause?
   AssocItemList
 
@@ -387,13 +389,13 @@ Expr =
 | UnderscoreExpr
 
 OffsetOfExpr =
-  Attr* '?builtin' '#' '?offset_of' '(' Type ',' fields:(NameRef ('.' NameRef)* ) ')'
+  Attr* 'builtin' '#' 'offset_of' '(' Type ',' fields:(NameRef ('.' NameRef)* ) ')'
 
 AsmExpr =
-  Attr* '?builtin' '#' '?asm' '(' Expr ')'
+  Attr* 'builtin' '#' 'asm' '(' Expr ')'
 
 FormatArgsExpr =
-  Attr* '?builtin' '#' '?format_args' '('
+  Attr* 'builtin' '#' 'format_args' '('
   template:Expr
   (',' args:(FormatArgsArg (',' FormatArgsArg)* ','?)? )?
   ')'
@@ -425,7 +427,7 @@ StmtList =
   '}'
 
 RefExpr =
-  Attr* '&' (('?raw' 'const'?)| ('?raw'? 'mut') ) Expr
+  Attr* '&' (('raw' 'const'?)| ('raw'? 'mut') ) Expr
 
 TryExpr =
   Attr* Expr '?'
@@ -550,7 +552,7 @@ YieldExpr =
   Attr* 'yield' Expr?
 
 YeetExpr =
-  Attr* 'do' '?yeet' Expr?
+  Attr* 'do' 'yeet' Expr?
 
 LetExpr =
   Attr* 'let' Pat '=' Expr
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs
index 5bc6b780e47fa..de40d638be393 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs
@@ -8,7 +8,7 @@ use crate::{
     ted, AstToken, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken,
 };
 
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub struct IndentLevel(pub u8);
 
 impl From<u8> for IndentLevel {
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
index 01886d119d675..c9b39e9922c92 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
@@ -114,6 +114,8 @@ impl AssocTypeArg {
     #[inline]
     pub fn ret_type(&self) -> Option<RetType> { support::child(&self.syntax) }
     #[inline]
+    pub fn return_type_syntax(&self) -> Option<ReturnTypeSyntax> { support::child(&self.syntax) }
+    #[inline]
     pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
     #[inline]
     pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
@@ -484,6 +486,8 @@ impl Fn {
     #[inline]
     pub fn fn_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![fn]) }
     #[inline]
+    pub fn gen_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![gen]) }
+    #[inline]
     pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) }
 }
 
@@ -1221,6 +1225,8 @@ impl PathSegment {
     #[inline]
     pub fn ret_type(&self) -> Option<RetType> { support::child(&self.syntax) }
     #[inline]
+    pub fn return_type_syntax(&self) -> Option<ReturnTypeSyntax> { support::child(&self.syntax) }
+    #[inline]
     pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
     #[inline]
     pub fn coloncolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![::]) }
@@ -1485,6 +1491,19 @@ impl ReturnExpr {
     pub fn return_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![return]) }
 }
 
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ReturnTypeSyntax {
+    pub(crate) syntax: SyntaxNode,
+}
+impl ReturnTypeSyntax {
+    #[inline]
+    pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
+    #[inline]
+    pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
+    #[inline]
+    pub fn dotdot_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![..]) }
+}
+
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct SelfParam {
     pub(crate) syntax: SyntaxNode,
@@ -3697,6 +3716,20 @@ impl AstNode for ReturnExpr {
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
+impl AstNode for ReturnTypeSyntax {
+    #[inline]
+    fn can_cast(kind: SyntaxKind) -> bool { kind == RETURN_TYPE_SYNTAX }
+    #[inline]
+    fn cast(syntax: SyntaxNode) -> Option<Self> {
+        if Self::can_cast(syntax.kind()) {
+            Some(Self { syntax })
+        } else {
+            None
+        }
+    }
+    #[inline]
+    fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
 impl AstNode for SelfParam {
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == SELF_PARAM }
@@ -6609,6 +6642,11 @@ impl std::fmt::Display for ReturnExpr {
         std::fmt::Display::fmt(self.syntax(), f)
     }
 }
+impl std::fmt::Display for ReturnTypeSyntax {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        std::fmt::Display::fmt(self.syntax(), f)
+    }
+}
 impl std::fmt::Display for SelfParam {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         std::fmt::Display::fmt(self.syntax(), f)
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
index 0228d9dd7135c..abf1a1f38207f 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
@@ -117,7 +117,7 @@ pub fn name_ref(name_ref: &str) -> ast::NameRef {
     ast_from_text(&format!("fn f() {{ {raw_escape}{name_ref}; }}"))
 }
 fn raw_ident_esc(ident: &str) -> &'static str {
-    if is_raw_identifier(ident) {
+    if is_raw_identifier(ident, Edition::CURRENT) {
         "r#"
     } else {
         ""
@@ -1035,6 +1035,7 @@ pub fn fn_(
     is_async: bool,
     is_const: bool,
     is_unsafe: bool,
+    is_gen: bool,
 ) -> ast::Fn {
     let type_params = match type_params {
         Some(type_params) => format!("{type_params}"),
@@ -1056,9 +1057,10 @@ pub fn fn_(
     let async_literal = if is_async { "async " } else { "" };
     let const_literal = if is_const { "const " } else { "" };
     let unsafe_literal = if is_unsafe { "unsafe " } else { "" };
+    let gen_literal = if is_gen { "gen " } else { "" };
 
     ast_from_text(&format!(
-        "{visibility}{async_literal}{const_literal}{unsafe_literal}fn {fn_name}{type_params}{params} {ret_type}{where_clause}{body}",
+        "{visibility}{const_literal}{async_literal}{gen_literal}{unsafe_literal}fn {fn_name}{type_params}{params} {ret_type}{where_clause}{body}",
     ))
 }
 pub fn struct_(
@@ -1152,12 +1154,13 @@ pub fn token(kind: SyntaxKind) -> SyntaxToken {
 }
 
 pub mod tokens {
-    use once_cell::sync::Lazy;
+    use std::sync::LazyLock;
+
     use parser::Edition;
 
     use crate::{ast, AstNode, Parse, SourceFile, SyntaxKind::*, SyntaxToken};
 
-    pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> = Lazy::new(|| {
+    pub(super) static SOURCE_FILE: LazyLock<Parse<SourceFile>> = LazyLock::new(|| {
         SourceFile::parse(
             "const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p, { let _ @ [] })\n;\n\nimpl A for B where: {}", Edition::CURRENT,
         )
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
index 5447906206c7f..693bfe330bd9c 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
@@ -17,7 +17,7 @@ use crate::{
     ted, NodeOrToken, SmolStr, SyntaxElement, SyntaxToken, TokenText, T,
 };
 
-use super::{RangeItem, RangeOp};
+use super::{GenericParam, RangeItem, RangeOp};
 
 impl ast::Lifetime {
     pub fn text(&self) -> TokenText<'_> {
@@ -822,6 +822,15 @@ pub enum TypeOrConstParam {
     Const(ast::ConstParam),
 }
 
+impl From<TypeOrConstParam> for GenericParam {
+    fn from(value: TypeOrConstParam) -> Self {
+        match value {
+            TypeOrConstParam::Type(it) => GenericParam::TypeParam(it),
+            TypeOrConstParam::Const(it) => GenericParam::ConstParam(it),
+        }
+    }
+}
+
 impl TypeOrConstParam {
     pub fn name(&self) -> Option<ast::Name> {
         match self {
diff --git a/src/tools/rust-analyzer/crates/syntax/src/hacks.rs b/src/tools/rust-analyzer/crates/syntax/src/hacks.rs
index 36615d11d85f3..9e63448ce9630 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/hacks.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/hacks.rs
@@ -6,9 +6,9 @@ use parser::Edition;
 
 use crate::{ast, AstNode};
 
-pub fn parse_expr_from_str(s: &str) -> Option<ast::Expr> {
+pub fn parse_expr_from_str(s: &str, edition: Edition) -> Option<ast::Expr> {
     let s = s.trim();
-    let file = ast::SourceFile::parse(&format!("const _: () = {s};"), Edition::CURRENT);
+    let file = ast::SourceFile::parse(&format!("const _: () = {s};"), edition);
     let expr = file.syntax_node().descendants().find_map(ast::Expr::cast)?;
     if expr.syntax().text() != s {
         return None;
diff --git a/src/tools/rust-analyzer/crates/syntax/src/utils.rs b/src/tools/rust-analyzer/crates/syntax/src/utils.rs
index a38f8b2b55d4e..d1f60f0b71bcc 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/utils.rs
@@ -2,7 +2,8 @@
 
 use crate::SyntaxKind;
 
-pub fn is_raw_identifier(name: &str) -> bool {
-    let is_keyword = SyntaxKind::from_keyword(name).is_some();
+#[inline]
+pub fn is_raw_identifier(name: &str, edition: parser::Edition) -> bool {
+    let is_keyword = SyntaxKind::from_keyword(name, edition).is_some();
     is_keyword && !matches!(name, "self" | "crate" | "super" | "Self")
 }
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
index 2d615c34a3520..3be4469beef0a 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
@@ -63,6 +63,7 @@
 //!     unsize: sized
 //!     todo: panic
 //!     unimplemented: panic
+//!     column:
 
 #![rustc_coherence_is_core]
 
@@ -1195,6 +1196,7 @@ pub mod future {
     #[doc(notable_trait)]
     #[lang = "future_trait"]
     pub trait Future {
+        #[lang = "future_output"]
         type Output;
         #[lang = "poll"]
         fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
@@ -1292,6 +1294,7 @@ pub mod iter {
     mod traits {
         mod iterator {
             #[doc(notable_trait)]
+            #[lang = "iterator"]
             pub trait Iterator {
                 type Item;
                 #[lang = "next"]
@@ -1615,6 +1618,14 @@ pub mod error {
 }
 // endregion:error
 
+// region:column
+#[rustc_builtin_macro]
+#[macro_export]
+macro_rules! column {
+    () => {};
+}
+// endregion:column
+
 pub mod prelude {
     pub mod v1 {
         pub use crate::{
diff --git a/src/tools/rust-analyzer/crates/tt/src/lib.rs b/src/tools/rust-analyzer/crates/tt/src/lib.rs
index 7b72f9ff108d3..8d915d0a51e32 100644
--- a/src/tools/rust-analyzer/crates/tt/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/tt/src/lib.rs
@@ -603,7 +603,7 @@ pub fn pretty<S>(tkns: &[TokenTree<S>]) -> String {
             TokenTree::Leaf(Leaf::Ident(ident)) => {
                 format!("{}{}", ident.is_raw.as_str(), ident.sym)
             }
-            TokenTree::Leaf(Leaf::Literal(literal)) => literal.symbol.as_str().to_owned(),
+            TokenTree::Leaf(Leaf::Literal(literal)) => format!("{literal}"),
             TokenTree::Leaf(Leaf::Punct(punct)) => format!("{}", punct.char),
             TokenTree::Subtree(subtree) => {
                 let content = pretty(&subtree.token_trees);
diff --git a/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs b/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs
index 92a49e07936d6..3c8e37413f68f 100644
--- a/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs
+++ b/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs
@@ -313,6 +313,20 @@ impl fmt::Debug for VfsPathRepr {
     }
 }
 
+impl PartialEq<AbsPath> for VfsPath {
+    fn eq(&self, other: &AbsPath) -> bool {
+        match &self.0 {
+            VfsPathRepr::PathBuf(lhs) => lhs == other,
+            VfsPathRepr::VirtualPath(_) => false,
+        }
+    }
+}
+impl PartialEq<VfsPath> for AbsPath {
+    fn eq(&self, other: &VfsPath) -> bool {
+        other == self
+    }
+}
+
 /// `/`-separated virtual path.
 ///
 /// This is used to describe files that do not reside on the file system.
diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc
index 2be338dd4d1d9..e4a8c6493a883 100644
--- a/src/tools/rust-analyzer/docs/user/generated_config.adoc
+++ b/src/tools/rust-analyzer/docs/user/generated_config.adoc
@@ -45,22 +45,14 @@ Automatically refresh project info via `cargo metadata` on
 --
 Run build scripts (`build.rs`) for more precise code analysis.
 --
-[[rust-analyzer.cargo.buildScripts.invocationLocation]]rust-analyzer.cargo.buildScripts.invocationLocation (default: `"workspace"`)::
-+
---
-Specifies the working directory for running build scripts.
-- "workspace": run build scripts for a workspace in the workspace's root directory.
-  This is incompatible with `#rust-analyzer.cargo.buildScripts.invocationStrategy#` set to `once`.
-- "root": run build scripts in the project's root directory.
-This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
-is set.
---
 [[rust-analyzer.cargo.buildScripts.invocationStrategy]]rust-analyzer.cargo.buildScripts.invocationStrategy (default: `"per_workspace"`)::
 +
 --
 Specifies the invocation strategy to use when running the build scripts command.
-If `per_workspace` is set, the command will be executed for each workspace.
-If `once` is set, the command will be executed once.
+If `per_workspace` is set, the command will be executed for each Rust workspace with the
+workspace as the working directory.
+If `once` is set, the command will be executed once with the opened project as the
+working directory.
 This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
 is set.
 --
@@ -75,8 +67,7 @@ option.
 If there are multiple linked projects/workspaces, this command is invoked for
 each of them, with the working directory being the workspace root
 (i.e., the folder containing the `Cargo.toml`). This can be overwritten
-by changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#` and
-`#rust-analyzer.cargo.buildScripts.invocationLocation#`.
+by changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#`.
 
 By default, a cargo invocation will be constructed for the configured
 targets and features, with the following base command line:
@@ -209,16 +200,6 @@ List of `cargo check` (or other command specified in `check.command`) diagnostic
 
 For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,...
 --
-[[rust-analyzer.check.invocationLocation]]rust-analyzer.check.invocationLocation (default: `"workspace"`)::
-+
---
-Specifies the working directory for running checks.
-- "workspace": run checks for workspaces in the corresponding workspaces' root directories.
-  This falls back to "root" if `#rust-analyzer.check.invocationStrategy#` is set to `once`.
-- "root": run checks in the project's root directory.
-This config only has an effect when `#rust-analyzer.check.overrideCommand#`
-is set.
---
 [[rust-analyzer.check.invocationStrategy]]rust-analyzer.check.invocationStrategy (default: `"per_workspace"`)::
 +
 --
@@ -250,8 +231,7 @@ Cargo, you might also want to change
 If there are multiple linked projects/workspaces, this command is invoked for
 each of them, with the working directory being the workspace root
 (i.e., the folder containing the `Cargo.toml`). This can be overwritten
-by changing `#rust-analyzer.check.invocationStrategy#` and
-`#rust-analyzer.check.invocationLocation#`.
+by changing `#rust-analyzer.check.invocationStrategy#`.
 
 If `$saved_file` is part of the command, rust-analyzer will pass
 the absolute path of the saved file to the provided command. This is
diff --git a/src/tools/rust-analyzer/docs/user/manual.adoc b/src/tools/rust-analyzer/docs/user/manual.adoc
index 703ec66921c4c..246ebdab2c91a 100644
--- a/src/tools/rust-analyzer/docs/user/manual.adoc
+++ b/src/tools/rust-analyzer/docs/user/manual.adoc
@@ -705,6 +705,12 @@ interface JsonProject {
     /// several different "sysroots" in one graph of
     /// crates.
     sysroot_src?: string;
+    /// List of groups of common cfg values, to allow
+    /// sharing them between crates.
+    ///
+    /// Maps from group name to its cfgs. Cfg follow
+    /// the same format as `Crate.cfg`.
+    cfg_groups?: { [key: string]: string[]; };
     /// The set of crates comprising the current
     /// project. Must include all transitive
     /// dependencies as well as sysroot crate (libstd,
@@ -754,6 +760,12 @@ interface Crate {
         include_dirs: string[],
         exclude_dirs: string[],
     },
+    /// List of cfg groups this crate inherits.
+    ///
+    /// All cfg in these groups will be concatenated to
+    /// `cfg`. It is impossible to replace a value from
+    /// the groups.
+    cfg_groups?: string[];
     /// The set of cfgs activated for a given crate, like
     /// `["unix", "feature=\"foo\"", "feature=\"bar\""]`.
     cfg: string[];
diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json
index bf9c4a366d422..98e8bbf02aa55 100644
--- a/src/tools/rust-analyzer/editors/code/package.json
+++ b/src/tools/rust-analyzer/editors/code/package.json
@@ -330,14 +330,6 @@
                         "default": false,
                         "type": "boolean"
                     },
-                    "rust-analyzer.discoverProjectRunner": {
-                        "markdownDescription": "Sets the extension responsible for determining which extension the rust-analyzer extension uses to generate `rust-project.json` files. This should should only be used\n if a build system like Buck or Bazel is also in use.",
-                        "default": null,
-                        "type": [
-                            "null",
-                            "string"
-                        ]
-                    },
                     "rust-analyzer.showUnlinkedFileNotification": {
                         "markdownDescription": "Whether to show a notification for unlinked files asking the user to add the corresponding Cargo.toml to the linked projects setting.",
                         "default": true,
@@ -671,29 +663,11 @@
                     }
                 }
             },
-            {
-                "title": "cargo",
-                "properties": {
-                    "rust-analyzer.cargo.buildScripts.invocationLocation": {
-                        "markdownDescription": "Specifies the working directory for running build scripts.\n- \"workspace\": run build scripts for a workspace in the workspace's root directory.\n    This is incompatible with `#rust-analyzer.cargo.buildScripts.invocationStrategy#` set to `once`.\n- \"root\": run build scripts in the project's root directory.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.",
-                        "default": "workspace",
-                        "type": "string",
-                        "enum": [
-                            "workspace",
-                            "root"
-                        ],
-                        "enumDescriptions": [
-                            "The command will be executed in the corresponding workspace root.",
-                            "The command will be executed in the project root."
-                        ]
-                    }
-                }
-            },
             {
                 "title": "cargo",
                 "properties": {
                     "rust-analyzer.cargo.buildScripts.invocationStrategy": {
-                        "markdownDescription": "Specifies the invocation strategy to use when running the build scripts command.\nIf `per_workspace` is set, the command will be executed for each workspace.\nIf `once` is set, the command will be executed once.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.",
+                        "markdownDescription": "Specifies the invocation strategy to use when running the build scripts command.\nIf `per_workspace` is set, the command will be executed for each Rust workspace with the\nworkspace as the working directory.\nIf `once` is set, the command will be executed once with the opened project as the\nworking directory.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.",
                         "default": "per_workspace",
                         "type": "string",
                         "enum": [
@@ -701,8 +675,8 @@
                             "once"
                         ],
                         "enumDescriptions": [
-                            "The command will be executed for each workspace.",
-                            "The command will be executed once."
+                            "The command will be executed for each Rust workspace with the workspace as the working directory.",
+                            "The command will be executed once with the opened project as the working directory."
                         ]
                     }
                 }
@@ -711,7 +685,7 @@
                 "title": "cargo",
                 "properties": {
                     "rust-analyzer.cargo.buildScripts.overrideCommand": {
-                        "markdownDescription": "Override the command rust-analyzer uses to run build scripts and\nbuild procedural macros. The command is required to output json\nand should therefore include `--message-format=json` or a similar\noption.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#` and\n`#rust-analyzer.cargo.buildScripts.invocationLocation#`.\n\nBy default, a cargo invocation will be constructed for the configured\ntargets and features, with the following base command line:\n\n```bash\ncargo check --quiet --workspace --message-format=json --all-targets --keep-going\n```\n.",
+                        "markdownDescription": "Override the command rust-analyzer uses to run build scripts and\nbuild procedural macros. The command is required to output json\nand should therefore include `--message-format=json` or a similar\noption.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#`.\n\nBy default, a cargo invocation will be constructed for the configured\ntargets and features, with the following base command line:\n\n```bash\ncargo check --quiet --workspace --message-format=json --all-targets --keep-going\n```\n.",
                         "default": null,
                         "type": [
                             "null",
@@ -973,24 +947,6 @@
                     }
                 }
             },
-            {
-                "title": "check",
-                "properties": {
-                    "rust-analyzer.check.invocationLocation": {
-                        "markdownDescription": "Specifies the working directory for running checks.\n- \"workspace\": run checks for workspaces in the corresponding workspaces' root directories.\n    This falls back to \"root\" if `#rust-analyzer.check.invocationStrategy#` is set to `once`.\n- \"root\": run checks in the project's root directory.\nThis config only has an effect when `#rust-analyzer.check.overrideCommand#`\nis set.",
-                        "default": "workspace",
-                        "type": "string",
-                        "enum": [
-                            "workspace",
-                            "root"
-                        ],
-                        "enumDescriptions": [
-                            "The command will be executed in the corresponding workspace root.",
-                            "The command will be executed in the project root."
-                        ]
-                    }
-                }
-            },
             {
                 "title": "check",
                 "properties": {
@@ -1003,8 +959,8 @@
                             "once"
                         ],
                         "enumDescriptions": [
-                            "The command will be executed for each workspace.",
-                            "The command will be executed once."
+                            "The command will be executed for each Rust workspace with the workspace as the working directory.",
+                            "The command will be executed once with the opened project as the working directory."
                         ]
                     }
                 }
@@ -1026,7 +982,7 @@
                 "title": "check",
                 "properties": {
                     "rust-analyzer.check.overrideCommand": {
-                        "markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefore include `--message-format=json` or a similar option\n(if your client supports the `colorDiagnosticOutput` experimental\ncapability, you can use `--message-format=json-diagnostic-rendered-ansi`).\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.check.invocationStrategy#` and\n`#rust-analyzer.check.invocationLocation#`.\n\nIf `$saved_file` is part of the command, rust-analyzer will pass\nthe absolute path of the saved file to the provided command. This is\nintended to be used with non-Cargo build systems.\nNote that `$saved_file` is experimental and may be removed in the future.\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n.",
+                        "markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefore include `--message-format=json` or a similar option\n(if your client supports the `colorDiagnosticOutput` experimental\ncapability, you can use `--message-format=json-diagnostic-rendered-ansi`).\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.check.invocationStrategy#`.\n\nIf `$saved_file` is part of the command, rust-analyzer will pass\nthe absolute path of the saved file to the provided command. This is\nintended to be used with non-Cargo build systems.\nNote that `$saved_file` is experimental and may be removed in the future.\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n.",
                         "default": null,
                         "type": [
                             "null",
diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts
index dc0165df71ea9..1e3dc60809588 100644
--- a/src/tools/rust-analyzer/editors/code/src/config.ts
+++ b/src/tools/rust-analyzer/editors/code/src/config.ts
@@ -252,10 +252,6 @@ export class Config {
         await this.cfg.update("checkOnSave", !(value || false), target || null, overrideInLanguage);
     }
 
-    get discoverProjectRunner(): string | undefined {
-        return this.get<string | undefined>("discoverProjectRunner");
-    }
-
     get problemMatcher(): string[] {
         return this.get<string[]>("runnables.problemMatcher") || [];
     }
diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version
index d4f1703d850c5..5f21a43f3be5e 100644
--- a/src/tools/rust-analyzer/rust-version
+++ b/src/tools/rust-analyzer/rust-version
@@ -1 +1 @@
-80eb5a8e910e5185d47cdefe3732d839c78a5e7e
+6cf068db566de080dfa7ed24a216ea3aed2b98ce
diff --git a/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs b/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs
index 0352539754b05..39e06f9642db1 100644
--- a/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs
+++ b/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs
@@ -396,24 +396,66 @@ fn generate_syntax_kinds(grammar: KindsSrc) -> String {
     let punctuation =
         grammar.punct.iter().map(|(_token, name)| format_ident!("{}", name)).collect::<Vec<_>>();
 
-    let x = |&name| match name {
+    let fmt_kw_as_variant = |&name| match name {
         "Self" => format_ident!("SELF_TYPE_KW"),
         name => format_ident!("{}_KW", to_upper_snake_case(name)),
     };
-    let full_keywords_values = grammar.keywords;
-    let full_keywords = full_keywords_values.iter().map(x);
+    let strict_keywords = grammar.keywords;
+    let strict_keywords_variants =
+        strict_keywords.iter().map(fmt_kw_as_variant).collect::<Vec<_>>();
+    let strict_keywords_tokens = strict_keywords.iter().map(|it| format_ident!("{it}"));
 
-    let contextual_keywords_values = &grammar.contextual_keywords;
-    let contextual_keywords = contextual_keywords_values.iter().map(x);
+    let edition_dependent_keywords_variants_match_arm = grammar
+        .edition_dependent_keywords
+        .iter()
+        .map(|(kw, ed)| {
+            let kw = fmt_kw_as_variant(kw);
+            quote! { #kw if #ed <= edition }
+        })
+        .collect::<Vec<_>>();
+    let edition_dependent_keywords_str_match_arm = grammar
+        .edition_dependent_keywords
+        .iter()
+        .map(|(kw, ed)| {
+            quote! { #kw if #ed <= edition }
+        })
+        .collect::<Vec<_>>();
+    let edition_dependent_keywords_variants = grammar
+        .edition_dependent_keywords
+        .iter()
+        .map(|(kw, _)| fmt_kw_as_variant(kw))
+        .collect::<Vec<_>>();
+    let edition_dependent_keywords_tokens =
+        grammar.edition_dependent_keywords.iter().map(|(it, _)| format_ident!("{it}"));
+
+    let contextual_keywords = grammar.contextual_keywords;
+    let contextual_keywords_variants =
+        contextual_keywords.iter().map(fmt_kw_as_variant).collect::<Vec<_>>();
+    let contextual_keywords_tokens = contextual_keywords.iter().map(|it| format_ident!("{it}"));
+    let contextual_keywords_str_match_arm = grammar.contextual_keywords.iter().map(|kw| {
+        match grammar.edition_dependent_keywords.iter().find(|(ed_kw, _)| ed_kw == kw) {
+            Some((_, ed)) => quote! { #kw if edition < #ed },
+            None => quote! { #kw },
+        }
+    });
+    let contextual_keywords_variants_match_arm = grammar
+        .contextual_keywords
+        .iter()
+        .map(|kw_s| {
+            let kw = fmt_kw_as_variant(kw_s);
+            match grammar.edition_dependent_keywords.iter().find(|(ed_kw, _)| ed_kw == kw_s) {
+                Some((_, ed)) => quote! { #kw if edition < #ed },
+                None => quote! { #kw },
+            }
+        })
+        .collect::<Vec<_>>();
 
-    let all_keywords_values = grammar
-        .keywords
+    let non_strict_keyword_variants = contextual_keywords_variants
         .iter()
-        .chain(grammar.contextual_keywords.iter())
-        .copied()
+        .chain(edition_dependent_keywords_variants.iter())
+        .sorted()
+        .dedup()
         .collect::<Vec<_>>();
-    let all_keywords_idents = all_keywords_values.iter().map(|kw| format_ident!("{}", kw));
-    let all_keywords = all_keywords_values.iter().map(x).collect::<Vec<_>>();
 
     let literals =
         grammar.literals.iter().map(|name| format_ident!("{}", name)).collect::<Vec<_>>();
@@ -424,6 +466,8 @@ fn generate_syntax_kinds(grammar: KindsSrc) -> String {
 
     let ast = quote! {
         #![allow(bad_style, missing_docs, unreachable_pub)]
+        use crate::Edition;
+
         /// The kind of syntax node, e.g. `IDENT`, `USE_KW`, or `STRUCT`.
         #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
         #[repr(u16)]
@@ -435,7 +479,8 @@ fn generate_syntax_kinds(grammar: KindsSrc) -> String {
             #[doc(hidden)]
             EOF,
             #(#punctuation,)*
-            #(#all_keywords,)*
+            #(#strict_keywords_variants,)*
+            #(#non_strict_keyword_variants,)*
             #(#literals,)*
             #(#tokens,)*
             #(#nodes,)*
@@ -447,31 +492,55 @@ fn generate_syntax_kinds(grammar: KindsSrc) -> String {
         use self::SyntaxKind::*;
 
         impl SyntaxKind {
-            pub fn is_keyword(self) -> bool {
-                matches!(self, #(#all_keywords)|*)
+            /// Checks whether this syntax kind is a strict keyword for the given edition.
+            /// Strict keywords are identifiers that are always considered keywords.
+            pub fn is_strict_keyword(self, edition: Edition) -> bool {
+                matches!(self, #(#strict_keywords_variants)|*)
+                || match self {
+                    #(#edition_dependent_keywords_variants_match_arm => true,)*
+                    _ => false,
+                }
             }
 
-            pub fn is_punct(self) -> bool {
+            /// Checks whether this syntax kind is a weak keyword for the given edition.
+            /// Weak keywords are identifiers that are considered keywords only in certain contexts.
+            pub fn is_contextual_keyword(self, edition: Edition) -> bool {
+                match self {
+                    #(#contextual_keywords_variants_match_arm => true,)*
+                    _ => false,
+                }
+            }
 
-                matches!(self, #(#punctuation)|*)
+            /// Checks whether this syntax kind is a strict or weak keyword for the given edition.
+            pub fn is_keyword(self, edition: Edition) -> bool {
+                matches!(self, #(#strict_keywords_variants)|*)
+                || match self {
+                    #(#edition_dependent_keywords_variants_match_arm => true,)*
+                    #(#contextual_keywords_variants_match_arm => true,)*
+                    _ => false,
+                }
+            }
 
+            pub fn is_punct(self) -> bool {
+                matches!(self, #(#punctuation)|*)
             }
 
             pub fn is_literal(self) -> bool {
                 matches!(self, #(#literals)|*)
             }
 
-            pub fn from_keyword(ident: &str) -> Option<SyntaxKind> {
+            pub fn from_keyword(ident: &str, edition: Edition) -> Option<SyntaxKind> {
                 let kw = match ident {
-                    #(#full_keywords_values => #full_keywords,)*
+                    #(#strict_keywords => #strict_keywords_variants,)*
+                    #(#edition_dependent_keywords_str_match_arm => #edition_dependent_keywords_variants,)*
                     _ => return None,
                 };
                 Some(kw)
             }
 
-            pub fn from_contextual_keyword(ident: &str) -> Option<SyntaxKind> {
+            pub fn from_contextual_keyword(ident: &str, edition: Edition) -> Option<SyntaxKind> {
                 let kw = match ident {
-                    #(#contextual_keywords_values => #contextual_keywords,)*
+                    #(#contextual_keywords_str_match_arm => #contextual_keywords_variants,)*
                     _ => return None,
                 };
                 Some(kw)
@@ -489,7 +558,9 @@ fn generate_syntax_kinds(grammar: KindsSrc) -> String {
         #[macro_export]
         macro_rules! T {
             #([#punctuation_values] => { $crate::SyntaxKind::#punctuation };)*
-            #([#all_keywords_idents] => { $crate::SyntaxKind::#all_keywords };)*
+            #([#strict_keywords_tokens] => { $crate::SyntaxKind::#strict_keywords_variants };)*
+            #([#contextual_keywords_tokens] => { $crate::SyntaxKind::#contextual_keywords_variants };)*
+            #([#edition_dependent_keywords_tokens] => { $crate::SyntaxKind::#edition_dependent_keywords_variants };)*
             [lifetime_ident] => { $crate::SyntaxKind::LIFETIME_IDENT };
             [int_number] => { $crate::SyntaxKind::INT_NUMBER };
             [ident] => { $crate::SyntaxKind::IDENT };
diff --git a/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs b/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs
index 3444f89908b93..34151bd958765 100644
--- a/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs
+++ b/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs
@@ -1,5 +1,7 @@
 //! Defines input for code generation process.
 
+use quote::ToTokens;
+
 use crate::codegen::grammar::to_upper_snake_case;
 
 #[derive(Copy, Clone, Debug)]
@@ -10,6 +12,35 @@ pub(crate) struct KindsSrc {
     pub(crate) literals: &'static [&'static str],
     pub(crate) tokens: &'static [&'static str],
     pub(crate) nodes: &'static [&'static str],
+    pub(crate) edition_dependent_keywords: &'static [(&'static str, Edition)],
+}
+
+#[allow(dead_code)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub(super) enum Edition {
+    Edition2015,
+    Edition2018,
+    Edition2021,
+    Edition2024,
+}
+
+impl ToTokens for Edition {
+    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
+        match self {
+            Edition::Edition2015 => {
+                tokens.extend(quote::quote! { Edition::Edition2015 });
+            }
+            Edition::Edition2018 => {
+                tokens.extend(quote::quote! { Edition::Edition2018 });
+            }
+            Edition::Edition2021 => {
+                tokens.extend(quote::quote! { Edition::Edition2021 });
+            }
+            Edition::Edition2024 => {
+                tokens.extend(quote::quote! { Edition::Edition2024 });
+            }
+        }
+    }
 }
 
 /// The punctuations of the language.
@@ -75,17 +106,32 @@ const EOF: &str = "EOF";
 
 const RESERVED: &[&str] = &[
     "abstract", "become", "box", "do", "final", "macro", "override", "priv", "typeof", "unsized",
-    "virtual", "yield", "try",
+    "virtual", "yield",
+];
+// keywords that are keywords only in specific parse contexts
+#[doc(alias = "WEAK_KEYWORDS")]
+const CONTEXTUAL_KEYWORDS: &[&str] =
+    &["macro_rules", "union", "default", "raw", "dyn", "auto", "yeet"];
+// keywords we use for special macro expansions
+const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &["builtin", "offset_of", "format_args", "asm"];
+// keywords that are keywords depending on the edition
+const EDITION_DEPENDENT_KEYWORDS: &[(&str, Edition)] = &[
+    ("try", Edition::Edition2018),
+    ("dyn", Edition::Edition2018),
+    ("async", Edition::Edition2018),
+    ("await", Edition::Edition2018),
+    ("gen", Edition::Edition2024),
 ];
-const CONTEXTUAL_RESERVED: &[&str] = &[];
 
 pub(crate) fn generate_kind_src(
     nodes: &[AstNodeSrc],
     enums: &[AstEnumSrc],
     grammar: &ungrammar::Grammar,
 ) -> KindsSrc {
+    let mut contextual_keywords: Vec<&_> =
+        CONTEXTUAL_KEYWORDS.iter().chain(CONTEXTUAL_BUILTIN_KEYWORDS).copied().collect();
+
     let mut keywords: Vec<&_> = Vec::new();
-    let mut contextual_keywords: Vec<&_> = Vec::new();
     let mut tokens: Vec<&_> = TOKENS.to_vec();
     let mut literals: Vec<&_> = Vec::new();
     let mut used_puncts = vec![false; PUNCT.len()];
@@ -103,9 +149,7 @@ pub(crate) fn generate_kind_src(
             ("#", token) if !token.is_empty() => {
                 tokens.push(String::leak(to_upper_snake_case(token)));
             }
-            ("?", kw) if !kw.is_empty() => {
-                contextual_keywords.push(String::leak(kw.to_owned()));
-            }
+            _ if contextual_keywords.contains(&name) => {}
             _ if name.chars().all(char::is_alphabetic) => {
                 keywords.push(String::leak(name.to_owned()));
             }
@@ -124,9 +168,14 @@ pub(crate) fn generate_kind_src(
     keywords.extend(RESERVED.iter().copied());
     keywords.sort();
     keywords.dedup();
-    contextual_keywords.extend(CONTEXTUAL_RESERVED.iter().copied());
     contextual_keywords.sort();
     contextual_keywords.dedup();
+    let mut edition_dependent_keywords: Vec<(&_, _)> = EDITION_DEPENDENT_KEYWORDS.to_vec();
+    edition_dependent_keywords.sort();
+    edition_dependent_keywords.dedup();
+
+    keywords.retain(|&it| !contextual_keywords.contains(&it));
+    keywords.retain(|&it| !edition_dependent_keywords.iter().any(|&(kw, _)| kw == it));
 
     // we leak things here for simplicity, that way we don't have to deal with lifetimes
     // The execution is a one shot job so thats fine
@@ -142,12 +191,21 @@ pub(crate) fn generate_kind_src(
     nodes.sort();
     let keywords = Vec::leak(keywords);
     let contextual_keywords = Vec::leak(contextual_keywords);
+    let edition_dependent_keywords = Vec::leak(edition_dependent_keywords);
     let literals = Vec::leak(literals);
     literals.sort();
     let tokens = Vec::leak(tokens);
     tokens.sort();
 
-    KindsSrc { punct: PUNCT, nodes, keywords, contextual_keywords, literals, tokens }
+    KindsSrc {
+        punct: PUNCT,
+        nodes,
+        keywords,
+        contextual_keywords,
+        edition_dependent_keywords,
+        literals,
+        tokens,
+    }
 }
 
 #[derive(Default, Debug)]
diff --git a/tests/assembly/targets/targets-elf.rs b/tests/assembly/targets/targets-elf.rs
index 6f0a200a08cf3..b0c5eec1fe490 100644
--- a/tests/assembly/targets/targets-elf.rs
+++ b/tests/assembly/targets/targets-elf.rs
@@ -54,6 +54,9 @@
 //@ revisions: aarch64_unknown_none_softfloat
 //@ [aarch64_unknown_none_softfloat] compile-flags: --target aarch64-unknown-none-softfloat
 //@ [aarch64_unknown_none_softfloat] needs-llvm-components: aarch64
+//@ revisions: aarch64_unknown_nto_qnx700
+//@ [aarch64_unknown_nto_qnx700] compile-flags: --target aarch64-unknown-nto-qnx700
+//@ [aarch64_unknown_nto_qnx700] needs-llvm-components: aarch64
 //@ revisions: aarch64_unknown_nto_qnx710
 //@ [aarch64_unknown_nto_qnx710] compile-flags: --target aarch64-unknown-nto-qnx710
 //@ [aarch64_unknown_nto_qnx710] needs-llvm-components: aarch64
diff --git a/tests/rustdoc-json/traits/self.rs b/tests/rustdoc-json/traits/self.rs
new file mode 100644
index 0000000000000..c7d952ae567d4
--- /dev/null
+++ b/tests/rustdoc-json/traits/self.rs
@@ -0,0 +1,58 @@
+// ignore-tidy-linelength
+
+pub struct Foo;
+
+// Check that Self is represented uniformly between inherent impls, trait impls,
+// and trait definitions, even though it uses both SelfTyParam and SelfTyAlias
+// internally.
+//
+// Each assertion matches 3 times, and should be the same each time.
+
+impl Foo {
+    //@ ismany '$.index[*][?(@.name=="by_ref")].inner.function.decl.inputs[0][0]' '"self"' '"self"' '"self"'
+    //@ ismany '$.index[*][?(@.name=="by_ref")].inner.function.decl.inputs[0][1].borrowed_ref.type.generic' '"Self"' '"Self"' '"Self"'
+    //@ ismany '$.index[*][?(@.name=="by_ref")].inner.function.decl.inputs[0][1].borrowed_ref.lifetime' null null null
+    //@ ismany '$.index[*][?(@.name=="by_ref")].inner.function.decl.inputs[0][1].borrowed_ref.mutable' false false false
+    pub fn by_ref(&self) {}
+
+    //@ ismany '$.index[*][?(@.name=="by_exclusive_ref")].inner.function.decl.inputs[0][0]' '"self"' '"self"' '"self"'
+    //@ ismany '$.index[*][?(@.name=="by_exclusive_ref")].inner.function.decl.inputs[0][1].borrowed_ref.type.generic' '"Self"' '"Self"' '"Self"'
+    //@ ismany '$.index[*][?(@.name=="by_exclusive_ref")].inner.function.decl.inputs[0][1].borrowed_ref.lifetime' null null null
+    //@ ismany '$.index[*][?(@.name=="by_exclusive_ref")].inner.function.decl.inputs[0][1].borrowed_ref.mutable' true true true
+    pub fn by_exclusive_ref(&mut self) {}
+
+    //@ ismany '$.index[*][?(@.name=="by_value")].inner.function.decl.inputs[0][0]' '"self"' '"self"' '"self"'
+    //@ ismany '$.index[*][?(@.name=="by_value")].inner.function.decl.inputs[0][1].generic' '"Self"' '"Self"' '"Self"'
+    pub fn by_value(self) {}
+
+    //@ ismany '$.index[*][?(@.name=="with_lifetime")].inner.function.decl.inputs[0][0]' '"self"' '"self"' '"self"'
+    //@ ismany '$.index[*][?(@.name=="with_lifetime")].inner.function.decl.inputs[0][1].borrowed_ref.type.generic' '"Self"' '"Self"' '"Self"'
+    //@ ismany '$.index[*][?(@.name=="with_lifetime")].inner.function.decl.inputs[0][1].borrowed_ref.lifetime' \"\'a\" \"\'a\" \"\'a\"
+    //@ ismany '$.index[*][?(@.name=="with_lifetime")].inner.function.decl.inputs[0][1].borrowed_ref.mutable' false false false
+    pub fn with_lifetime<'a>(&'a self) {}
+
+    //@ ismany '$.index[*][?(@.name=="build")].inner.function.decl.output.generic' '"Self"' '"Self"' '"Self"'
+    pub fn build() -> Self {
+        Self
+    }
+}
+
+pub struct Bar;
+
+pub trait SelfParams {
+    fn by_ref(&self);
+    fn by_exclusive_ref(&mut self);
+    fn by_value(self);
+    fn with_lifetime<'a>(&'a self);
+    fn build() -> Self;
+}
+
+impl SelfParams for Bar {
+    fn by_ref(&self) {}
+    fn by_exclusive_ref(&mut self) {}
+    fn by_value(self) {}
+    fn with_lifetime<'a>(&'a self) {}
+    fn build() -> Self {
+        Self
+    }
+}
diff --git a/tests/ui/pattern/patterns-dont-match-nt-statement.rs b/tests/ui/pattern/patterns-dont-match-nt-statement.rs
new file mode 100644
index 0000000000000..c8d41459383ac
--- /dev/null
+++ b/tests/ui/pattern/patterns-dont-match-nt-statement.rs
@@ -0,0 +1,19 @@
+//@ check-pass
+
+// Make sure that a `stmt` nonterminal does not eagerly match against
+// a `pat`, since this will always cause a parse error...
+
+macro_rules! m {
+    ($pat:pat) => {};
+    ($stmt:stmt) => {};
+}
+
+macro_rules! m2 {
+    ($stmt:stmt) => {
+        m! { $stmt }
+    };
+}
+
+m2! { let x = 1 }
+
+fn main() {}