about summary refs log tree commit diff
path: root/src/tools/rust-analyzer
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2025-02-17 08:30:10 +0000
committerGitHub <noreply@github.com>2025-02-17 08:30:10 +0000
commit123f17c57ba3062e6b641fbd089bd6343826ba22 (patch)
tree2d2a985708708779d318337439f1bc554b5318a4 /src/tools/rust-analyzer
parent6b46937305053be16f222c078b5f30ce46c78ec2 (diff)
parent2e7158b4587a42043c88ab653b305c378b602b76 (diff)
downloadrust-123f17c57ba3062e6b641fbd089bd6343826ba22.tar.gz
rust-123f17c57ba3062e6b641fbd089bd6343826ba22.zip
Merge pull request #19127 from ChayimFriedman2/different-generic-args
feat: Refactor path lowering and serve a new path diagnostic
Diffstat (limited to 'src/tools/rust-analyzer')
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/data.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/path.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/resolver.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/name.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer.rs53
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs76
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs14
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs150
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower.rs983
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower/diagnostics.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs911
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/diagnostics.rs16
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs59
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html15
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs15
-rw-r--r--src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs33
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/minicore.rs6
19 files changed, 1222 insertions, 1144 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
index c5bbd4edba9..bec66278772 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
@@ -250,6 +250,7 @@ bitflags::bitflags! {
         const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 3;
         const SKIP_ARRAY_DURING_METHOD_DISPATCH = 1 << 4;
         const SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH = 1 << 5;
+        const RUSTC_PAREN_SUGAR = 1 << 6;
     }
 }
 
@@ -294,6 +295,9 @@ impl TraitData {
         if attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists() {
             flags |= TraitFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS;
         }
+        if attrs.by_key(&sym::rustc_paren_sugar).exists() {
+            flags |= TraitFlags::RUSTC_PAREN_SUGAR;
+        }
 
         let mut skip_array_during_method_dispatch =
             attrs.by_key(&sym::rustc_skip_array_during_method_dispatch).exists();
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 e59c37104dd..e6c2504d07a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/path.rs
@@ -173,10 +173,7 @@ impl Path {
                 segments: path.mod_path().segments(),
                 generic_args: Some(path.generic_args()),
             },
-            Path::LangItem(_, seg) => PathSegments {
-                segments: seg.as_ref().map_or(&[], |seg| std::slice::from_ref(seg)),
-                generic_args: None,
-            },
+            Path::LangItem(_, seg) => PathSegments { segments: seg.as_slice(), generic_args: None },
         }
     }
 
@@ -240,6 +237,11 @@ pub struct PathSegment<'a> {
     pub args_and_bindings: Option<&'a GenericArgs>,
 }
 
+impl PathSegment<'_> {
+    pub const MISSING: PathSegment<'static> =
+        PathSegment { name: &Name::missing(), args_and_bindings: None };
+}
+
 #[derive(Debug, Clone, Copy)]
 pub struct PathSegments<'a> {
     segments: &'a [Name],
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
index 7e13ae2f7a1..9dfb6e3cc4b 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
@@ -327,8 +327,9 @@ impl Resolver {
                     | LangItemTarget::ImplDef(_)
                     | LangItemTarget::Static(_) => return None,
                 };
+                // Remaining segments start from 0 because lang paths have no segments other than the remaining.
                 return Some((
-                    ResolveValueResult::Partial(type_ns, 1, None),
+                    ResolveValueResult::Partial(type_ns, 0, None),
                     ResolvePathResultPrefixInfo::default(),
                 ));
             }
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 21e5fb5ef9e..0758bd4515e 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
@@ -142,8 +142,8 @@ impl Name {
     /// Ideally, we want a `gensym` semantics for missing names -- each missing
     /// name is equal only to itself. It's not clear how to implement this in
     /// salsa though, so we punt on that bit for a moment.
-    pub fn missing() -> Name {
-        Name { symbol: sym::MISSING_NAME.clone(), ctx: () }
+    pub const fn missing() -> Name {
+        Name { symbol: sym::consts::MISSING_NAME, ctx: () }
     }
 
     /// Returns true if this is a fake name for things missing in the source code. See
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 a8cd971b057..0cb7002f446 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -16,7 +16,7 @@
 pub(crate) mod cast;
 pub(crate) mod closure;
 mod coerce;
-mod diagnostics;
+pub(crate) mod diagnostics;
 mod expr;
 mod mutability;
 mod pat;
@@ -1502,21 +1502,22 @@ impl<'a> InferenceContext<'a> {
             &self.diagnostics,
             InferenceTyDiagnosticSource::Body,
         );
+        let mut path_ctx = ctx.at_path(path, node);
         let (resolution, unresolved) = if value_ns {
-            let Some(res) = ctx.resolve_path_in_value_ns(path, node, HygieneId::ROOT) else {
+            let Some(res) = path_ctx.resolve_path_in_value_ns(HygieneId::ROOT) else {
                 return (self.err_ty(), None);
             };
             match res {
                 ResolveValueResult::ValueNs(value, _) => match value {
                     ValueNs::EnumVariantId(var) => {
-                        let substs = ctx.substs_from_path(path, var.into(), true);
+                        let substs = path_ctx.substs_from_path(var.into(), true);
                         drop(ctx);
                         let ty = self.db.ty(var.lookup(self.db.upcast()).parent.into());
                         let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
                         return (ty, Some(var.into()));
                     }
                     ValueNs::StructId(strukt) => {
-                        let substs = ctx.substs_from_path(path, strukt.into(), true);
+                        let substs = path_ctx.substs_from_path(strukt.into(), true);
                         drop(ctx);
                         let ty = self.db.ty(strukt.into());
                         let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
@@ -1531,7 +1532,7 @@ impl<'a> InferenceContext<'a> {
                 ResolveValueResult::Partial(typens, unresolved, _) => (typens, Some(unresolved)),
             }
         } else {
-            match ctx.resolve_path_in_type_ns(path, node) {
+            match path_ctx.resolve_path_in_type_ns() {
                 Some((it, idx)) => (it, idx),
                 None => return (self.err_ty(), None),
             }
@@ -1542,21 +1543,21 @@ impl<'a> InferenceContext<'a> {
         };
         return match resolution {
             TypeNs::AdtId(AdtId::StructId(strukt)) => {
-                let substs = ctx.substs_from_path(path, strukt.into(), true);
+                let substs = path_ctx.substs_from_path(strukt.into(), true);
                 drop(ctx);
                 let ty = self.db.ty(strukt.into());
                 let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
                 forbid_unresolved_segments((ty, Some(strukt.into())), unresolved)
             }
             TypeNs::AdtId(AdtId::UnionId(u)) => {
-                let substs = ctx.substs_from_path(path, u.into(), true);
+                let substs = path_ctx.substs_from_path(u.into(), true);
                 drop(ctx);
                 let ty = self.db.ty(u.into());
                 let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
                 forbid_unresolved_segments((ty, Some(u.into())), unresolved)
             }
             TypeNs::EnumVariantId(var) => {
-                let substs = ctx.substs_from_path(path, var.into(), true);
+                let substs = path_ctx.substs_from_path(var.into(), true);
                 drop(ctx);
                 let ty = self.db.ty(var.lookup(self.db.upcast()).parent.into());
                 let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
@@ -1567,31 +1568,32 @@ impl<'a> InferenceContext<'a> {
                 let substs = generics.placeholder_subst(self.db);
                 let mut ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
 
-                let Some(mut remaining_idx) = unresolved else {
+                let Some(remaining_idx) = unresolved else {
                     drop(ctx);
                     return self.resolve_variant_on_alias(ty, None, mod_path);
                 };
 
                 let mut remaining_segments = path.segments().skip(remaining_idx);
 
+                if remaining_segments.len() >= 2 {
+                    path_ctx.ignore_last_segment();
+                }
+
                 // We need to try resolving unresolved segments one by one because each may resolve
                 // to a projection, which `TyLoweringContext` cannot handle on its own.
                 let mut tried_resolving_once = false;
-                while !remaining_segments.is_empty() {
-                    let resolved_segment = path.segments().get(remaining_idx - 1).unwrap();
-                    let current_segment = remaining_segments.take(1);
-
+                while let Some(current_segment) = remaining_segments.first() {
                     // If we can resolve to an enum variant, it takes priority over associated type
                     // of the same name.
                     if let Some((AdtId::EnumId(id), _)) = ty.as_adt() {
                         let enum_data = self.db.enum_data(id);
-                        let name = current_segment.first().unwrap().name;
-                        if let Some(variant) = enum_data.variant(name) {
+                        if let Some(variant) = enum_data.variant(current_segment.name) {
                             return if remaining_segments.len() == 1 {
                                 (ty, Some(variant.into()))
                             } else {
                                 // We still have unresolved paths, but enum variants never have
                                 // associated types!
+                                // FIXME: Report an error.
                                 (self.err_ty(), None)
                             };
                         }
@@ -1600,23 +1602,13 @@ impl<'a> InferenceContext<'a> {
                     if tried_resolving_once {
                         // FIXME: with `inherent_associated_types` this is allowed, but our `lower_partly_resolved_path()`
                         // will need to be updated to err at the correct segment.
-                        //
-                        // We need to stop here because otherwise the segment index passed to `lower_partly_resolved_path()`
-                        // will be incorrect, and that can mess up error reporting.
                         break;
                     }
 
                     // `lower_partly_resolved_path()` returns `None` as type namespace unless
                     // `remaining_segments` is empty, which is never the case here. We don't know
                     // which namespace the new `ty` is in until normalized anyway.
-                    (ty, _) = ctx.lower_partly_resolved_path(
-                        node,
-                        resolution,
-                        resolved_segment,
-                        current_segment,
-                        (remaining_idx - 1) as u32,
-                        false,
-                    );
+                    (ty, _) = path_ctx.lower_partly_resolved_path(resolution, false);
                     tried_resolving_once = true;
 
                     ty = self.table.insert_type_vars(ty);
@@ -1626,8 +1618,6 @@ impl<'a> InferenceContext<'a> {
                         return (self.err_ty(), None);
                     }
 
-                    // FIXME(inherent_associated_types): update `resolution` based on `ty` here.
-                    remaining_idx += 1;
                     remaining_segments = remaining_segments.skip(1);
                 }
                 drop(ctx);
@@ -1643,12 +1633,7 @@ impl<'a> InferenceContext<'a> {
                 (ty, variant)
             }
             TypeNs::TypeAliasId(it) => {
-                let resolved_seg = match unresolved {
-                    None => path.segments().last().unwrap(),
-                    Some(n) => path.segments().get(path.segments().len() - n - 1).unwrap(),
-                };
-                let substs =
-                    ctx.substs_from_path_segment(resolved_seg, Some(it.into()), true, None);
+                let substs = path_ctx.substs_from_path_segment(it.into(), true, None);
                 drop(ctx);
                 let ty = self.db.ty(it.into());
                 let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs
index b85378531ad..8c0446953a6 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs
@@ -5,16 +5,13 @@
 use std::cell::RefCell;
 use std::ops::{Deref, DerefMut};
 
-use hir_def::expr_store::HygieneId;
-use hir_def::hir::ExprOrPatId;
-use hir_def::path::{Path, PathSegment, PathSegments};
-use hir_def::resolver::{ResolveValueResult, Resolver, TypeNs};
-use hir_def::type_ref::TypesMap;
-use hir_def::TypeOwnerId;
+use either::Either;
+use hir_def::{hir::ExprOrPatId, path::Path, resolver::Resolver, type_ref::TypesMap, TypeOwnerId};
 
-use crate::db::HirDatabase;
 use crate::{
-    InferenceDiagnostic, InferenceTyDiagnosticSource, Ty, TyLoweringContext, TyLoweringDiagnostic,
+    db::HirDatabase,
+    lower::path::{PathDiagnosticCallback, PathLoweringContext},
+    InferenceDiagnostic, InferenceTyDiagnosticSource, TyLoweringContext, TyLoweringDiagnostic,
 };
 
 // Unfortunately, this struct needs to use interior mutability (but we encapsulate it)
@@ -44,6 +41,11 @@ impl Diagnostics {
     }
 }
 
+pub(crate) struct PathDiagnosticCallbackData<'a> {
+    node: ExprOrPatId,
+    diagnostics: &'a Diagnostics,
+}
+
 pub(super) struct InferenceTyLoweringContext<'a> {
     ctx: TyLoweringContext<'a>,
     diagnostics: &'a Diagnostics,
@@ -51,6 +53,7 @@ pub(super) struct InferenceTyLoweringContext<'a> {
 }
 
 impl<'a> InferenceTyLoweringContext<'a> {
+    #[inline]
     pub(super) fn new(
         db: &'a dyn HirDatabase,
         resolver: &'a Resolver,
@@ -62,65 +65,42 @@ impl<'a> InferenceTyLoweringContext<'a> {
         Self { ctx: TyLoweringContext::new(db, resolver, types_map, owner), diagnostics, source }
     }
 
-    pub(super) fn resolve_path_in_type_ns(
-        &mut self,
-        path: &Path,
-        node: ExprOrPatId,
-    ) -> Option<(TypeNs, Option<usize>)> {
-        let diagnostics = self.diagnostics;
-        self.ctx.resolve_path_in_type_ns(path, &mut |_, diag| {
-            diagnostics.push(InferenceDiagnostic::PathDiagnostic { node, diag })
-        })
-    }
-
-    pub(super) fn resolve_path_in_value_ns(
-        &mut self,
-        path: &Path,
-        node: ExprOrPatId,
-        hygiene_id: HygieneId,
-    ) -> Option<ResolveValueResult> {
-        let diagnostics = self.diagnostics;
-        self.ctx.resolve_path_in_value_ns(path, hygiene_id, &mut |_, diag| {
-            diagnostics.push(InferenceDiagnostic::PathDiagnostic { node, diag })
-        })
-    }
-
-    pub(super) fn lower_partly_resolved_path(
-        &mut self,
+    #[inline]
+    pub(super) fn at_path<'b>(
+        &'b mut self,
+        path: &'b Path,
         node: ExprOrPatId,
-        resolution: TypeNs,
-        resolved_segment: PathSegment<'_>,
-        remaining_segments: PathSegments<'_>,
-        resolved_segment_idx: u32,
-        infer_args: bool,
-    ) -> (Ty, Option<TypeNs>) {
-        let diagnostics = self.diagnostics;
-        self.ctx.lower_partly_resolved_path(
-            resolution,
-            resolved_segment,
-            remaining_segments,
-            resolved_segment_idx,
-            infer_args,
-            &mut |_, diag| diagnostics.push(InferenceDiagnostic::PathDiagnostic { node, diag }),
-        )
+    ) -> PathLoweringContext<'b, 'a> {
+        let on_diagnostic = PathDiagnosticCallback {
+            data: Either::Right(PathDiagnosticCallbackData { diagnostics: self.diagnostics, node }),
+            callback: |data, _, diag| {
+                let data = data.as_ref().right().unwrap();
+                data.diagnostics
+                    .push(InferenceDiagnostic::PathDiagnostic { node: data.node, diag });
+            },
+        };
+        PathLoweringContext::new(&mut self.ctx, on_diagnostic, path)
     }
 }
 
 impl<'a> Deref for InferenceTyLoweringContext<'a> {
     type Target = TyLoweringContext<'a>;
 
+    #[inline]
     fn deref(&self) -> &Self::Target {
         &self.ctx
     }
 }
 
 impl DerefMut for InferenceTyLoweringContext<'_> {
+    #[inline]
     fn deref_mut(&mut self) -> &mut Self::Target {
         &mut self.ctx
     }
 }
 
 impl Drop for InferenceTyLoweringContext<'_> {
+    #[inline]
     fn drop(&mut self) {
         self.diagnostics
             .push_ty_diagnostics(self.source, std::mem::take(&mut self.ctx.diagnostics));
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 bff88143475..80e3ca1fa28 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
@@ -2201,8 +2201,8 @@ impl InferenceContext<'_> {
             for kind_id in def_generics.iter_self_id().take(self_params) {
                 let arg = args.peek();
                 let arg = match (kind_id, arg) {
-                    // Lifetimes can be elided.
-                    // Once we have implemented lifetime elision correctly,
+                    // Lifetimes can be inferred.
+                    // Once we have implemented lifetime inference correctly,
                     // this should be handled in a proper way.
                     (
                         GenericParamId::LifetimeParamId(_),
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 5ff22bea34d..d0fe00ecebd 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
@@ -564,9 +564,17 @@ impl InferenceContext<'_> {
             | Pat::Range { .. }
             | Pat::Slice { .. } => true,
             Pat::Or(pats) => pats.iter().all(|p| self.is_non_ref_pat(body, *p)),
-            Pat::Path(p) => {
-                let v = self.resolve_value_path_inner(p, pat.into());
-                v.is_some_and(|x| !matches!(x.0, hir_def::resolver::ValueNs::ConstId(_)))
+            Pat::Path(path) => {
+                // A const is a reference pattern, but other value ns things aren't (see #16131). We don't need more than
+                // the hir-def resolver for this, because if there are segments left, this can only be an (associated) const.
+                //
+                // Do not use `TyLoweringContext`'s resolution, we want to ignore errors here (they'll be reported elsewhere).
+                let resolution = self.resolver.resolve_path_in_value_ns_fully(
+                    self.db.upcast(),
+                    path,
+                    body.pat_path_hygiene(pat),
+                );
+                resolution.is_some_and(|it| !matches!(it, hir_def::resolver::ValueNs::ConstId(_)))
             }
             Pat::ConstBlock(..) => false,
             Pat::Lit(expr) => !matches!(
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
index 36ec60a7a2f..3794912ee98 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
@@ -7,7 +7,6 @@ use hir_def::{
     AdtId, AssocItemId, GenericDefId, ItemContainerId, Lookup,
 };
 use hir_expand::name::Name;
-use intern::sym;
 use stdx::never;
 
 use crate::{
@@ -94,7 +93,14 @@ impl InferenceContext<'_> {
             return Some(ValuePathResolution::NonGeneric(ty));
         };
 
-        let substs = self.with_body_ty_lowering(|ctx| ctx.substs_from_path(path, value_def, true));
+        let substs = self.with_body_ty_lowering(|ctx| {
+            let mut path_ctx = ctx.at_path(path, id);
+            let last_segment = path.segments().len().checked_sub(1);
+            if let Some(last_segment) = last_segment {
+                path_ctx.set_current_segment(last_segment)
+            }
+            path_ctx.substs_from_path(value_def, true)
+        });
         let substs = substs.as_slice(Interner);
 
         if let ValueNs::EnumVariantId(_) = value {
@@ -156,15 +162,16 @@ impl InferenceContext<'_> {
             &self.diagnostics,
             InferenceTyDiagnosticSource::Body,
         );
+        let mut path_ctx = ctx.at_path(path, id);
         let (value, self_subst) = if let Some(type_ref) = path.type_anchor() {
             let last = path.segments().last()?;
 
-            let (ty, orig_ns) = ctx.lower_ty_ext(type_ref);
+            let (ty, orig_ns) = path_ctx.ty_ctx().lower_ty_ext(type_ref);
             let ty = self.table.insert_type_vars(ty);
             let ty = self.table.normalize_associated_types_in(ty);
 
-            let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
-            let (ty, _) = ctx.lower_ty_relative_path(ty, orig_ns, remaining_segments_for_ty);
+            path_ctx.ignore_last_segment();
+            let (ty, _) = path_ctx.lower_ty_relative_path(ty, orig_ns);
             drop(ctx);
             let ty = self.table.insert_type_vars(ty);
             let ty = self.table.normalize_associated_types_in(ty);
@@ -172,14 +179,52 @@ impl InferenceContext<'_> {
         } else {
             let hygiene = self.body.expr_or_pat_path_hygiene(id);
             // FIXME: report error, unresolved first path segment
-            let value_or_partial = ctx.resolve_path_in_value_ns(path, id, hygiene)?;
-            drop(ctx);
+            let value_or_partial = path_ctx.resolve_path_in_value_ns(hygiene)?;
 
             match value_or_partial {
-                ResolveValueResult::ValueNs(it, _) => (it, None),
-                ResolveValueResult::Partial(def, remaining_index, _) => self
-                    .resolve_assoc_item(id, def, path, remaining_index, id)
-                    .map(|(it, substs)| (it, Some(substs)))?,
+                ResolveValueResult::ValueNs(it, _) => {
+                    drop(ctx);
+                    (it, None)
+                }
+                ResolveValueResult::Partial(def, remaining_index, _) => {
+                    // there may be more intermediate segments between the resolved one and
+                    // the end. Only the last segment needs to be resolved to a value; from
+                    // the segments before that, we need to get either a type or a trait ref.
+
+                    let remaining_segments = path.segments().skip(remaining_index);
+                    let is_before_last = remaining_segments.len() == 1;
+                    let last_segment = remaining_segments
+                        .last()
+                        .expect("there should be at least one segment here");
+
+                    let (resolution, substs) = match (def, is_before_last) {
+                        (TypeNs::TraitId(trait_), true) => {
+                            let self_ty = self.table.new_type_var();
+                            let trait_ref =
+                                path_ctx.lower_trait_ref_from_resolved_path(trait_, self_ty);
+                            drop(ctx);
+                            self.resolve_trait_assoc_item(trait_ref, last_segment, id)
+                        }
+                        (def, _) => {
+                            // Either we already have a type (e.g. `Vec::new`), or we have a
+                            // trait but it's not the last segment, so the next segment
+                            // should resolve to an associated type of that trait (e.g. `<T
+                            // as Iterator>::Item::default`)
+                            path_ctx.ignore_last_segment();
+                            let (ty, _) = path_ctx.lower_partly_resolved_path(def, true);
+                            drop(ctx);
+                            if ty.is_unknown() {
+                                return None;
+                            }
+
+                            let ty = self.insert_type_vars(ty);
+                            let ty = self.normalize_associated_types_in(ty);
+
+                            self.resolve_ty_assoc_item(ty, last_segment.name, id)
+                        }
+                    }?;
+                    (resolution, Some(substs))
+                }
             }
         };
         Some((value, self_subst))
@@ -212,89 +257,6 @@ impl InferenceContext<'_> {
         }
     }
 
-    fn resolve_assoc_item(
-        &mut self,
-        node: ExprOrPatId,
-        def: TypeNs,
-        path: &Path,
-        remaining_index: usize,
-        id: ExprOrPatId,
-    ) -> Option<(ValueNs, Substitution)> {
-        // there may be more intermediate segments between the resolved one and
-        // the end. Only the last segment needs to be resolved to a value; from
-        // the segments before that, we need to get either a type or a trait ref.
-
-        let _d;
-        let (resolved_segment, remaining_segments) = match path {
-            Path::Normal { .. } | Path::BarePath(_) => {
-                assert!(remaining_index < path.segments().len());
-                (
-                    path.segments().get(remaining_index - 1).unwrap(),
-                    path.segments().skip(remaining_index),
-                )
-            }
-            Path::LangItem(..) => (
-                PathSegment {
-                    name: {
-                        _d = Name::new_symbol_root(sym::Unknown.clone());
-                        &_d
-                    },
-                    args_and_bindings: None,
-                },
-                path.segments(),
-            ),
-        };
-        let is_before_last = remaining_segments.len() == 1;
-
-        match (def, is_before_last) {
-            (TypeNs::TraitId(trait_), true) => {
-                let segment =
-                    remaining_segments.last().expect("there should be at least one segment here");
-                let self_ty = self.table.new_type_var();
-                let trait_ref = self.with_body_ty_lowering(|ctx| {
-                    ctx.lower_trait_ref_from_resolved_path(trait_, resolved_segment, self_ty)
-                });
-                self.resolve_trait_assoc_item(trait_ref, segment, id)
-            }
-            (def, _) => {
-                // Either we already have a type (e.g. `Vec::new`), or we have a
-                // trait but it's not the last segment, so the next segment
-                // should resolve to an associated type of that trait (e.g. `<T
-                // as Iterator>::Item::default`)
-                let remaining_segments_for_ty =
-                    remaining_segments.take(remaining_segments.len() - 1);
-                let mut ctx = TyLoweringContext::new(
-                    self.db,
-                    &self.resolver,
-                    &self.body.types,
-                    self.owner.into(),
-                    &self.diagnostics,
-                    InferenceTyDiagnosticSource::Body,
-                );
-                let (ty, _) = ctx.lower_partly_resolved_path(
-                    node,
-                    def,
-                    resolved_segment,
-                    remaining_segments_for_ty,
-                    (remaining_index - 1) as u32,
-                    true,
-                );
-                drop(ctx);
-                if ty.is_unknown() {
-                    return None;
-                }
-
-                let ty = self.insert_type_vars(ty);
-                let ty = self.normalize_associated_types_in(ty);
-
-                let segment =
-                    remaining_segments.last().expect("there should be at least one segment here");
-
-                self.resolve_ty_assoc_item(ty, segment.name, id)
-            }
-        }
-    }
-
     fn resolve_trait_assoc_item(
         &mut self,
         trait_ref: TraitRef,
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 db13e1fd354..af73b5ed9a7 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
@@ -6,6 +6,7 @@
 //!
 //! This usually involves resolving names, collecting generic arguments etc.
 pub(crate) mod diagnostics;
+pub(crate) mod path;
 
 use std::{
     cell::OnceCell,
@@ -26,29 +27,26 @@ use hir_def::{
     builtin_type::BuiltinType,
     data::{adt::StructKind, TraitFlags},
     expander::Expander,
-    expr_store::HygieneId,
     generics::{
         GenericParamDataRef, TypeOrConstParamData, TypeParamProvenance, WherePredicate,
         WherePredicateTypeTarget,
     },
     lang_item::LangItem,
     nameres::MacroSubNs,
-    path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments},
-    resolver::{HasResolver, LifetimeNs, ResolveValueResult, Resolver, TypeNs, ValueNs},
+    path::{GenericArg, ModPath, Path, PathKind},
+    resolver::{HasResolver, LifetimeNs, Resolver, TypeNs},
     type_ref::{
         ConstRef, LifetimeRef, PathId, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound,
         TypeRef, TypeRefId, TypesMap, TypesSourceMap,
     },
     AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId,
-    FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstLoc, ItemContainerId,
-    LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId,
-    TypeOwnerId, UnionId, VariantId,
+    FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstLoc, LocalFieldId,
+    Lookup, StaticId, StructId, TypeAliasId, TypeOrConstParamId, TypeOwnerId, UnionId, VariantId,
 };
 use hir_expand::{name::Name, ExpandResult};
 use la_arena::{Arena, ArenaMap};
 use rustc_hash::FxHashSet;
 use rustc_pattern_analysis::Captures;
-use smallvec::SmallVec;
 use stdx::{impl_from, never};
 use syntax::ast;
 use triomphe::{Arc, ThinArc};
@@ -62,18 +60,19 @@ use crate::{
     db::HirDatabase,
     error_lifetime,
     generics::{generics, trait_self_param_idx, Generics},
-    lower::diagnostics::*,
+    lower::{
+        diagnostics::*,
+        path::{PathDiagnosticCallback, PathLoweringContext},
+    },
     make_binders,
     mapping::{from_chalk_trait_id, lt_to_placeholder_idx, ToChalk},
-    static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
-    utils::{
-        all_super_trait_refs, associated_type_by_name_including_super_traits, InTypeConstIdMetadata,
-    },
-    AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy,
-    FnAbi, FnPointer, FnSig, FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime,
-    LifetimeData, LifetimeOutlives, ParamKind, PolyFnSig, ProgramClause, ProjectionTy,
-    QuantifiedWhereClause, QuantifiedWhereClauses, Substitution, TraitEnvironment, TraitRef,
-    TraitRefExt, Ty, TyBuilder, TyKind, WhereClause,
+    static_lifetime, to_chalk_trait_id, to_placeholder_idx,
+    utils::{all_super_trait_refs, InTypeConstIdMetadata},
+    AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy, FnAbi,
+    FnPointer, FnSig, FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime,
+    LifetimeData, LifetimeOutlives, ParamKind, PolyFnSig, ProgramClause, QuantifiedWhereClause,
+    QuantifiedWhereClauses, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder,
+    TyKind, WhereClause,
 };
 
 #[derive(Debug, Default)]
@@ -106,6 +105,8 @@ impl ImplTraitLoweringState {
     }
 }
 
+pub(crate) struct PathDiagnosticCallbackData(TypeRefId);
+
 #[derive(Debug)]
 pub struct TyLoweringContext<'a> {
     pub db: &'a dyn HirDatabase,
@@ -527,9 +528,8 @@ impl<'a> TyLoweringContext<'a> {
         if path.segments().len() > 1 {
             return None;
         }
-        let resolution = match self
-            .resolve_path_in_type_ns(path, &mut Self::on_path_diagnostic_callback(type_ref_id))
-        {
+        let mut ctx = self.at_path(PathId::from_type_ref_unchecked(type_ref_id));
+        let resolution = match ctx.resolve_path_in_type_ns() {
             Some((it, None)) => it,
             _ => return None,
         };
@@ -539,409 +539,36 @@ impl<'a> TyLoweringContext<'a> {
         }
     }
 
-    pub(crate) fn lower_ty_relative_path(
-        &mut self,
-        ty: Ty,
-        // We need the original resolution to lower `Self::AssocTy` correctly
-        res: Option<TypeNs>,
-        remaining_segments: PathSegments<'_>,
-    ) -> (Ty, Option<TypeNs>) {
-        match remaining_segments.len() {
-            0 => (ty, res),
-            1 => {
-                // resolve unselected assoc types
-                let segment = remaining_segments.first().unwrap();
-                (self.select_associated_type(res, segment), None)
-            }
-            _ => {
-                // FIXME report error (ambiguous associated type)
-                (TyKind::Error.intern(Interner), None)
-            }
-        }
-    }
-
-    pub(crate) fn lower_partly_resolved_path(
-        &mut self,
-        resolution: TypeNs,
-        resolved_segment: PathSegment<'_>,
-        remaining_segments: PathSegments<'_>,
-        _resolved_segment_idx: u32,
-        infer_args: bool,
-        _on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
-    ) -> (Ty, Option<TypeNs>) {
-        let ty = match resolution {
-            TypeNs::TraitId(trait_) => {
-                let ty = match remaining_segments.len() {
-                    1 => {
-                        let trait_ref = self.lower_trait_ref_from_resolved_path(
-                            trait_,
-                            resolved_segment,
-                            TyKind::Error.intern(Interner),
-                        );
-                        let segment = remaining_segments.first().unwrap();
-                        let found = self
-                            .db
-                            .trait_data(trait_ref.hir_trait_id())
-                            .associated_type_by_name(segment.name);
-
-                        match found {
-                            Some(associated_ty) => {
-                                // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
-                                // generic params. It's inefficient to splice the `Substitution`s, so we may want
-                                // that method to optionally take parent `Substitution` as we already know them at
-                                // this point (`trait_ref.substitution`).
-                                let substitution = self.substs_from_path_segment(
-                                    segment,
-                                    Some(associated_ty.into()),
-                                    false,
-                                    None,
-                                );
-                                let len_self =
-                                    generics(self.db.upcast(), associated_ty.into()).len_self();
-                                let substitution = Substitution::from_iter(
-                                    Interner,
-                                    substitution
-                                        .iter(Interner)
-                                        .take(len_self)
-                                        .chain(trait_ref.substitution.iter(Interner)),
-                                );
-                                TyKind::Alias(AliasTy::Projection(ProjectionTy {
-                                    associated_ty_id: to_assoc_type_id(associated_ty),
-                                    substitution,
-                                }))
-                                .intern(Interner)
-                            }
-                            None => {
-                                // FIXME: report error (associated type not found)
-                                TyKind::Error.intern(Interner)
-                            }
-                        }
-                    }
-                    0 => {
-                        // Trait object type without dyn; this should be handled in upstream. See
-                        // `lower_path()`.
-                        stdx::never!("unexpected fully resolved trait path");
-                        TyKind::Error.intern(Interner)
-                    }
-                    _ => {
-                        // FIXME report error (ambiguous associated type)
-                        TyKind::Error.intern(Interner)
-                    }
-                };
-                return (ty, None);
-            }
-            TypeNs::TraitAliasId(_) => {
-                // FIXME(trait_alias): Implement trait alias.
-                return (TyKind::Error.intern(Interner), None);
-            }
-            TypeNs::GenericParam(param_id) => match self.type_param_mode {
-                ParamLoweringMode::Placeholder => {
-                    TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into()))
-                }
-                ParamLoweringMode::Variable => {
-                    let idx = match self
-                        .generics()
-                        .expect("generics in scope")
-                        .type_or_const_param_idx(param_id.into())
-                    {
-                        None => {
-                            never!("no matching generics");
-                            return (TyKind::Error.intern(Interner), None);
-                        }
-                        Some(idx) => idx,
-                    };
-
-                    TyKind::BoundVar(BoundVar::new(self.in_binders, idx))
-                }
-            }
-            .intern(Interner),
-            TypeNs::SelfType(impl_id) => {
-                let generics = self.generics().expect("impl should have generic param scope");
-
-                match self.type_param_mode {
-                    ParamLoweringMode::Placeholder => {
-                        // `def` can be either impl itself or item within, and we need impl itself
-                        // now.
-                        let generics = generics.parent_or_self();
-                        let subst = generics.placeholder_subst(self.db);
-                        self.db.impl_self_ty(impl_id).substitute(Interner, &subst)
-                    }
-                    ParamLoweringMode::Variable => {
-                        let starting_from = match generics.def() {
-                            GenericDefId::ImplId(_) => 0,
-                            // `def` is an item within impl. We need to substitute `BoundVar`s but
-                            // remember that they are for parent (i.e. impl) generic params so they
-                            // come after our own params.
-                            _ => generics.len_self(),
-                        };
-                        TyBuilder::impl_self_ty(self.db, impl_id)
-                            .fill_with_bound_vars(self.in_binders, starting_from)
-                            .build()
-                    }
-                }
-            }
-            TypeNs::AdtSelfType(adt) => {
-                let generics = generics(self.db.upcast(), adt.into());
-                let substs = match self.type_param_mode {
-                    ParamLoweringMode::Placeholder => generics.placeholder_subst(self.db),
-                    ParamLoweringMode::Variable => {
-                        generics.bound_vars_subst(self.db, self.in_binders)
-                    }
-                };
-                self.db.ty(adt.into()).substitute(Interner, &substs)
-            }
-
-            TypeNs::AdtId(it) => self.lower_path_inner(resolved_segment, it.into(), infer_args),
-            TypeNs::BuiltinType(it) => {
-                self.lower_path_inner(resolved_segment, it.into(), infer_args)
-            }
-            TypeNs::TypeAliasId(it) => {
-                self.lower_path_inner(resolved_segment, it.into(), infer_args)
-            }
-            // FIXME: report error
-            TypeNs::EnumVariantId(_) => return (TyKind::Error.intern(Interner), None),
-        };
-        self.lower_ty_relative_path(ty, Some(resolution), remaining_segments)
-    }
-
-    fn handle_type_ns_resolution(
-        &mut self,
-        resolution: &TypeNs,
-        resolved_segment: PathSegment<'_>,
-        resolved_segment_idx: usize,
-        on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
-    ) {
-        let mut prohibit_generics_on_resolved = |reason| {
-            if resolved_segment.args_and_bindings.is_some() {
-                on_diagnostic(
-                    self,
-                    PathLoweringDiagnostic::GenericArgsProhibited {
-                        segment: resolved_segment_idx as u32,
-                        reason,
-                    },
-                );
-            }
-        };
-
-        match resolution {
-            TypeNs::SelfType(_) => {
-                prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy)
-            }
-            TypeNs::GenericParam(_) => {
-                prohibit_generics_on_resolved(GenericArgsProhibitedReason::TyParam)
-            }
-            TypeNs::AdtSelfType(_) => {
-                prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy)
-            }
-            TypeNs::BuiltinType(_) => {
-                prohibit_generics_on_resolved(GenericArgsProhibitedReason::PrimitiveTy)
-            }
-            TypeNs::AdtId(_)
-            | TypeNs::EnumVariantId(_)
-            | TypeNs::TypeAliasId(_)
-            | TypeNs::TraitId(_)
-            | TypeNs::TraitAliasId(_) => {}
-        }
-    }
-
-    pub(crate) fn resolve_path_in_type_ns_fully(
-        &mut self,
-        path: &Path,
-        on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
-    ) -> Option<TypeNs> {
-        let (res, unresolved) = self.resolve_path_in_type_ns(path, on_diagnostic)?;
-        if unresolved.is_some() {
-            return None;
-        }
-        Some(res)
-    }
-
-    pub(crate) fn resolve_path_in_type_ns(
-        &mut self,
-        path: &Path,
-        on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
-    ) -> Option<(TypeNs, Option<usize>)> {
-        let (resolution, remaining_index, _, prefix_info) =
-            self.resolver.resolve_path_in_type_ns_with_prefix_info(self.db.upcast(), path)?;
-        let segments = path.segments();
-
-        match path {
-            // `segments.is_empty()` can occur with `self`.
-            Path::Normal(..) if !segments.is_empty() => (),
-            _ => return Some((resolution, remaining_index)),
-        };
-
-        let (module_segments, resolved_segment_idx, enum_segment) = match remaining_index {
-            None if prefix_info.enum_variant => {
-                (segments.strip_last_two(), segments.len() - 1, Some(segments.len() - 2))
-            }
-            None => (segments.strip_last(), segments.len() - 1, None),
-            Some(i) => (segments.take(i - 1), i - 1, None),
-        };
-
-        for (i, mod_segment) in module_segments.iter().enumerate() {
-            if mod_segment.args_and_bindings.is_some() {
-                on_diagnostic(
-                    self,
-                    PathLoweringDiagnostic::GenericArgsProhibited {
-                        segment: i as u32,
-                        reason: GenericArgsProhibitedReason::Module,
-                    },
-                );
-            }
-        }
-
-        if let Some(enum_segment) = enum_segment {
-            if segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some())
-                && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some())
-            {
-                on_diagnostic(
-                    self,
-                    PathLoweringDiagnostic::GenericArgsProhibited {
-                        segment: (enum_segment + 1) as u32,
-                        reason: GenericArgsProhibitedReason::EnumVariant,
-                    },
-                );
-            }
-        }
-
-        self.handle_type_ns_resolution(
-            &resolution,
-            segments.get(resolved_segment_idx).expect("should have resolved segment"),
-            resolved_segment_idx,
-            on_diagnostic,
-        );
-
-        Some((resolution, remaining_index))
-    }
-
-    pub(crate) fn resolve_path_in_value_ns(
-        &mut self,
-        path: &Path,
-        hygiene_id: HygieneId,
-        on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
-    ) -> Option<ResolveValueResult> {
-        let (res, prefix_info) = self.resolver.resolve_path_in_value_ns_with_prefix_info(
-            self.db.upcast(),
-            path,
-            hygiene_id,
-        )?;
-
-        let segments = path.segments();
-        match path {
-            // `segments.is_empty()` can occur with `self`.
-            Path::Normal(..) if !segments.is_empty() => (),
-            _ => return Some(res),
-        };
-
-        let (mod_segments, enum_segment) = match res {
-            ResolveValueResult::Partial(_, unresolved_segment, _) => {
-                (segments.take(unresolved_segment - 1), None)
-            }
-            ResolveValueResult::ValueNs(ValueNs::EnumVariantId(_), _)
-                if prefix_info.enum_variant =>
-            {
-                (segments.strip_last_two(), segments.len().checked_sub(2))
-            }
-            ResolveValueResult::ValueNs(..) => (segments.strip_last(), None),
-        };
-        for (i, mod_segment) in mod_segments.iter().enumerate() {
-            if mod_segment.args_and_bindings.is_some() {
-                on_diagnostic(
-                    self,
-                    PathLoweringDiagnostic::GenericArgsProhibited {
-                        segment: i as u32,
-                        reason: GenericArgsProhibitedReason::Module,
-                    },
-                );
-            }
-        }
-
-        if let Some(enum_segment) = enum_segment {
-            if segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some())
-                && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some())
-            {
-                on_diagnostic(
-                    self,
-                    PathLoweringDiagnostic::GenericArgsProhibited {
-                        segment: (enum_segment + 1) as u32,
-                        reason: GenericArgsProhibitedReason::EnumVariant,
-                    },
-                );
-            }
+    #[inline]
+    fn on_path_diagnostic_callback(type_ref: TypeRefId) -> PathDiagnosticCallback<'static> {
+        PathDiagnosticCallback {
+            data: Either::Left(PathDiagnosticCallbackData(type_ref)),
+            callback: |data, this, diag| {
+                let type_ref = data.as_ref().left().unwrap().0;
+                this.push_diagnostic(type_ref, TyLoweringDiagnosticKind::PathDiagnostic(diag))
+            },
         }
-
-        match &res {
-            ResolveValueResult::ValueNs(resolution, _) => {
-                let resolved_segment_idx =
-                    segments.len().checked_sub(1).unwrap_or_else(|| panic!("{path:?}"));
-                let resolved_segment = segments.last().unwrap();
-
-                let mut prohibit_generics_on_resolved = |reason| {
-                    if resolved_segment.args_and_bindings.is_some() {
-                        on_diagnostic(
-                            self,
-                            PathLoweringDiagnostic::GenericArgsProhibited {
-                                segment: resolved_segment_idx as u32,
-                                reason,
-                            },
-                        );
-                    }
-                };
-
-                match resolution {
-                    ValueNs::ImplSelf(_) => {
-                        prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy)
-                    }
-                    // FIXME: rustc generates E0107 (incorrect number of generic arguments) and not
-                    // E0109 (generic arguments provided for a type that doesn't accept them) for
-                    // consts and statics, presumably as a defense against future in which consts
-                    // and statics can be generic, or just because it was easier for rustc implementors.
-                    // That means we'll show the wrong error code. Because of us it's easier to do it
-                    // this way :)
-                    ValueNs::GenericParam(_) | ValueNs::ConstId(_) => {
-                        prohibit_generics_on_resolved(GenericArgsProhibitedReason::Const)
-                    }
-                    ValueNs::StaticId(_) => {
-                        prohibit_generics_on_resolved(GenericArgsProhibitedReason::Static)
-                    }
-                    ValueNs::FunctionId(_) | ValueNs::StructId(_) | ValueNs::EnumVariantId(_) => {}
-                    ValueNs::LocalBinding(_) => {}
-                }
-            }
-            ResolveValueResult::Partial(resolution, unresolved_idx, _) => {
-                let resolved_segment_idx = unresolved_idx - 1;
-                let resolved_segment = segments.get(resolved_segment_idx).unwrap();
-                self.handle_type_ns_resolution(
-                    resolution,
-                    resolved_segment,
-                    resolved_segment_idx,
-                    on_diagnostic,
-                );
-            }
-        };
-        Some(res)
     }
 
-    fn on_path_diagnostic_callback(
-        type_ref: TypeRefId,
-    ) -> impl FnMut(&mut Self, PathLoweringDiagnostic) {
-        move |this, diag| {
-            this.push_diagnostic(type_ref, TyLoweringDiagnosticKind::PathDiagnostic(diag))
-        }
+    #[inline]
+    fn at_path(&mut self, path_id: PathId) -> PathLoweringContext<'_, 'a> {
+        PathLoweringContext::new(
+            self,
+            Self::on_path_diagnostic_callback(path_id.type_ref()),
+            &self.types_map[path_id],
+        )
     }
 
     pub(crate) fn lower_path(&mut self, path: &Path, path_id: PathId) -> (Ty, Option<TypeNs>) {
         // Resolve the path (in type namespace)
         if let Some(type_ref) = path.type_anchor() {
             let (ty, res) = self.lower_ty_ext(type_ref);
-            return self.lower_ty_relative_path(ty, res, path.segments());
+            let mut ctx = self.at_path(path_id);
+            return ctx.lower_ty_relative_path(ty, res);
         }
 
-        let (resolution, remaining_index) = match self.resolve_path_in_type_ns(
-            path,
-            &mut Self::on_path_diagnostic_callback(path_id.type_ref()),
-        ) {
+        let mut ctx = self.at_path(path_id);
+        let (resolution, remaining_index) = match ctx.resolve_path_in_type_ns() {
             Some(it) => it,
             None => return (TyKind::Error.intern(Interner), None),
         };
@@ -953,354 +580,21 @@ impl<'a> TyLoweringContext<'a> {
             return (ty, None);
         }
 
-        let (resolved_segment_idx, resolved_segment, remaining_segments) = match remaining_index {
-            None => (
-                path.segments().len() - 1,
-                path.segments().last().expect("resolved path has at least one element"),
-                PathSegments::EMPTY,
-            ),
-            Some(i) => (i - 1, path.segments().get(i - 1).unwrap(), path.segments().skip(i)),
-        };
-
-        self.lower_partly_resolved_path(
-            resolution,
-            resolved_segment,
-            remaining_segments,
-            resolved_segment_idx as u32,
-            false,
-            &mut Self::on_path_diagnostic_callback(path_id.type_ref()),
-        )
-    }
-
-    fn select_associated_type(&mut self, res: Option<TypeNs>, segment: PathSegment<'_>) -> Ty {
-        let Some((generics, res)) = self.generics().zip(res) else {
-            return TyKind::Error.intern(Interner);
-        };
-        let ty = named_associated_type_shorthand_candidates(
-            self.db,
-            generics.def(),
-            res,
-            Some(segment.name.clone()),
-            move |name, t, associated_ty| {
-                let generics = self.generics().unwrap();
-
-                if name != segment.name {
-                    return None;
-                }
-
-                let parent_subst = t.substitution.clone();
-                let parent_subst = match self.type_param_mode {
-                    ParamLoweringMode::Placeholder => {
-                        // if we're lowering to placeholders, we have to put them in now.
-                        let s = generics.placeholder_subst(self.db);
-                        s.apply(parent_subst, Interner)
-                    }
-                    ParamLoweringMode::Variable => {
-                        // We need to shift in the bound vars, since
-                        // `named_associated_type_shorthand_candidates` does not do that.
-                        parent_subst.shifted_in_from(Interner, self.in_binders)
-                    }
-                };
-
-                // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
-                // generic params. It's inefficient to splice the `Substitution`s, so we may want
-                // that method to optionally take parent `Substitution` as we already know them at
-                // this point (`t.substitution`).
-                let substs =
-                    self.substs_from_path_segment(segment, Some(associated_ty.into()), false, None);
-
-                let len_self =
-                    crate::generics::generics(self.db.upcast(), associated_ty.into()).len_self();
-
-                let substs = Substitution::from_iter(
-                    Interner,
-                    substs.iter(Interner).take(len_self).chain(parent_subst.iter(Interner)),
-                );
-
-                Some(
-                    TyKind::Alias(AliasTy::Projection(ProjectionTy {
-                        associated_ty_id: to_assoc_type_id(associated_ty),
-                        substitution: substs,
-                    }))
-                    .intern(Interner),
-                )
-            },
-        );
-
-        ty.unwrap_or_else(|| TyKind::Error.intern(Interner))
-    }
-
-    fn lower_path_inner(
-        &mut self,
-        segment: PathSegment<'_>,
-        typeable: TyDefId,
-        infer_args: bool,
-    ) -> Ty {
-        let generic_def = match typeable {
-            TyDefId::BuiltinType(_) => None,
-            TyDefId::AdtId(it) => Some(it.into()),
-            TyDefId::TypeAliasId(it) => Some(it.into()),
-        };
-        let substs = self.substs_from_path_segment(segment, generic_def, infer_args, None);
-        self.db.ty(typeable).substitute(Interner, &substs)
-    }
-
-    /// Collect generic arguments from a path into a `Substs`. See also
-    /// `create_substs_for_ast_path` and `def_to_ty` in rustc.
-    pub(super) fn substs_from_path(
-        &mut self,
-        path: &Path,
-        // Note that we don't call `db.value_type(resolved)` here,
-        // `ValueTyDefId` is just a convenient way to pass generics and
-        // special-case enum variants
-        resolved: ValueTyDefId,
-        infer_args: bool,
-    ) -> Substitution {
-        let last = path.segments().last();
-        let (segment, generic_def) = match resolved {
-            ValueTyDefId::FunctionId(it) => (last, Some(it.into())),
-            ValueTyDefId::StructId(it) => (last, Some(it.into())),
-            ValueTyDefId::UnionId(it) => (last, Some(it.into())),
-            ValueTyDefId::ConstId(it) => (last, Some(it.into())),
-            ValueTyDefId::StaticId(_) => (last, None),
-            ValueTyDefId::EnumVariantId(var) => {
-                // the generic args for an enum variant may be either specified
-                // on the segment referring to the enum, or on the segment
-                // referring to the variant. So `Option::<T>::None` and
-                // `Option::None::<T>` are both allowed (though the former is
-                // preferred). See also `def_ids_for_path_segments` in rustc.
-                let len = path.segments().len();
-                let penultimate = len.checked_sub(2).and_then(|idx| path.segments().get(idx));
-                let segment = match penultimate {
-                    Some(segment) if segment.args_and_bindings.is_some() => Some(segment),
-                    _ => last,
-                };
-                (segment, Some(var.lookup(self.db.upcast()).parent.into()))
-            }
-        };
-        if let Some(segment) = segment {
-            self.substs_from_path_segment(segment, generic_def, infer_args, None)
-        } else if let Some(generic_def) = generic_def {
-            // lang item
-            self.substs_from_args_and_bindings(None, Some(generic_def), infer_args, None)
-        } else {
-            Substitution::empty(Interner)
-        }
-    }
-
-    pub(super) fn substs_from_path_segment(
-        &mut self,
-        segment: PathSegment<'_>,
-        def: Option<GenericDefId>,
-        infer_args: bool,
-        explicit_self_ty: Option<Ty>,
-    ) -> Substitution {
-        self.substs_from_args_and_bindings(
-            segment.args_and_bindings,
-            def,
-            infer_args,
-            explicit_self_ty,
-        )
-    }
-
-    fn substs_from_args_and_bindings(
-        &mut self,
-        args_and_bindings: Option<&GenericArgs>,
-        def: Option<GenericDefId>,
-        infer_args: bool,
-        explicit_self_ty: Option<Ty>,
-    ) -> Substitution {
-        let Some(def) = def else { return Substitution::empty(Interner) };
-
-        // Order is
-        // - Optional Self parameter
-        // - Lifetime parameters
-        // - Type or Const parameters
-        // - Parent parameters
-        let def_generics = generics(self.db.upcast(), def);
-        let (
-            parent_params,
-            self_param,
-            type_params,
-            const_params,
-            impl_trait_params,
-            lifetime_params,
-        ) = def_generics.provenance_split();
-        let item_len =
-            self_param as usize + type_params + const_params + impl_trait_params + lifetime_params;
-        let total_len = parent_params + item_len;
-
-        let mut substs = Vec::new();
-
-        // we need to iterate the lifetime and type/const params separately as our order of them
-        // differs from the supplied syntax
-
-        let ty_error = || TyKind::Error.intern(Interner).cast(Interner);
-        let mut def_toc_iter = def_generics.iter_self_type_or_consts_id();
-        let fill_self_param = || {
-            if self_param {
-                let self_ty = explicit_self_ty.map(|x| x.cast(Interner)).unwrap_or_else(ty_error);
-
-                if let Some(id) = def_toc_iter.next() {
-                    assert!(matches!(id, GenericParamId::TypeParamId(_)));
-                    substs.push(self_ty);
-                }
-            }
-        };
-        let mut had_explicit_args = false;
-
-        if let Some(&GenericArgs { ref args, has_self_type, .. }) = args_and_bindings {
-            // Fill in the self param first
-            if has_self_type && self_param {
-                had_explicit_args = true;
-                if let Some(id) = def_toc_iter.next() {
-                    assert!(matches!(id, GenericParamId::TypeParamId(_)));
-                    had_explicit_args = true;
-                    if let GenericArg::Type(ty) = &args[0] {
-                        substs.push(self.lower_ty(*ty).cast(Interner));
-                    }
-                }
-            } else {
-                fill_self_param()
-            };
-
-            // Then fill in the supplied lifetime args, or error lifetimes if there are too few
-            // (default lifetimes aren't a thing)
-            for arg in args
-                .iter()
-                .filter_map(|arg| match arg {
-                    GenericArg::Lifetime(arg) => Some(self.lower_lifetime(arg)),
-                    _ => None,
-                })
-                .chain(iter::repeat(error_lifetime()))
-                .take(lifetime_params)
-            {
-                substs.push(arg.cast(Interner));
-            }
-
-            let skip = if has_self_type { 1 } else { 0 };
-            // Fill in supplied type and const args
-            // Note if non-lifetime args are provided, it should be all of them, but we can't rely on that
-            for (arg, id) in args
-                .iter()
-                .filter(|arg| !matches!(arg, GenericArg::Lifetime(_)))
-                .skip(skip)
-                .take(type_params + const_params)
-                .zip(def_toc_iter)
-            {
-                had_explicit_args = true;
-                let arg = generic_arg_to_chalk(
-                    self.db,
-                    id,
-                    arg,
-                    self,
-                    self.types_map,
-                    |this, type_ref| this.lower_ty(type_ref),
-                    |this, const_ref, ty| this.lower_const(const_ref, ty),
-                    |this, lifetime_ref| this.lower_lifetime(lifetime_ref),
-                );
-                substs.push(arg);
-            }
-        } else {
-            fill_self_param();
-        }
-
-        let param_to_err = |id| match id {
-            GenericParamId::ConstParamId(x) => unknown_const_as_generic(self.db.const_param_ty(x)),
-            GenericParamId::TypeParamId(_) => ty_error(),
-            GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner),
-        };
-        // handle defaults. In expression or pattern path segments without
-        // explicitly specified type arguments, missing type arguments are inferred
-        // (i.e. defaults aren't used).
-        // Generic parameters for associated types are not supposed to have defaults, so we just
-        // ignore them.
-        let is_assoc_ty = || match def {
-            GenericDefId::TypeAliasId(id) => {
-                matches!(id.lookup(self.db.upcast()).container, ItemContainerId::TraitId(_))
-            }
-            _ => false,
-        };
-        let fill_defaults = (!infer_args || had_explicit_args) && !is_assoc_ty();
-        if fill_defaults {
-            let defaults = &*self.db.generic_defaults(def);
-            let (item, _parent) = defaults.split_at(item_len);
-            let parent_from = item_len - substs.len();
-
-            let mut rem =
-                def_generics.iter_id().skip(substs.len()).map(param_to_err).collect::<Vec<_>>();
-            // Fill in defaults for type/const params
-            for (idx, default_ty) in item[substs.len()..].iter().enumerate() {
-                // each default can depend on the previous parameters
-                let substs_so_far = Substitution::from_iter(
-                    Interner,
-                    substs.iter().cloned().chain(rem[idx..].iter().cloned()),
-                );
-                substs.push(default_ty.clone().substitute(Interner, &substs_so_far));
-            }
-            // Fill in remaining parent params
-            substs.extend(rem.drain(parent_from..));
-        } else {
-            // Fill in remaining def params and parent params
-            substs.extend(def_generics.iter_id().skip(substs.len()).map(param_to_err));
-        }
-
-        assert_eq!(substs.len(), total_len, "expected {} substs, got {}", total_len, substs.len());
-        Substitution::from_iter(Interner, substs)
-    }
-
-    pub(crate) fn lower_trait_ref_from_resolved_path(
-        &mut self,
-        resolved: TraitId,
-        segment: PathSegment<'_>,
-        explicit_self_ty: Ty,
-    ) -> TraitRef {
-        let substs = self.trait_ref_substs_from_path(segment, resolved, explicit_self_ty);
-        TraitRef { trait_id: to_chalk_trait_id(resolved), substitution: substs }
-    }
-
-    fn prohibit_generics(
-        &mut self,
-        path_id: PathId,
-        idx: u32,
-        segments: PathSegments<'_>,
-        reason: GenericArgsProhibitedReason,
-    ) {
-        segments.iter().zip(idx..).for_each(|(segment, idx)| {
-            if segment.args_and_bindings.is_some() {
-                self.push_diagnostic(
-                    path_id.type_ref(),
-                    TyLoweringDiagnosticKind::PathDiagnostic(
-                        PathLoweringDiagnostic::GenericArgsProhibited { segment: idx, reason },
-                    ),
-                );
-            }
-        });
+        ctx.lower_partly_resolved_path(resolution, false)
     }
 
     fn lower_trait_ref_from_path(
         &mut self,
         path_id: PathId,
         explicit_self_ty: Ty,
-    ) -> Option<TraitRef> {
-        let path = &self.types_map[path_id];
-        let resolved = match self.resolve_path_in_type_ns_fully(
-            path,
-            &mut Self::on_path_diagnostic_callback(path_id.type_ref()),
-        )? {
+    ) -> Option<(TraitRef, PathLoweringContext<'_, 'a>)> {
+        let mut ctx = self.at_path(path_id);
+        let resolved = match ctx.resolve_path_in_type_ns_fully()? {
             // FIXME(trait_alias): We need to handle trait alias here.
             TypeNs::TraitId(tr) => tr,
             _ => return None,
         };
-        // Do this after we verify it's indeed a trait to not confuse the user if they're not modules.
-        self.prohibit_generics(
-            path_id,
-            0,
-            path.segments().strip_last(),
-            GenericArgsProhibitedReason::Module,
-        );
-        let segment = path.segments().last().expect("path should have at least one segment");
-        Some(self.lower_trait_ref_from_resolved_path(resolved, segment, explicit_self_ty))
+        Some((ctx.lower_trait_ref_from_resolved_path(resolved, explicit_self_ty), ctx))
     }
 
     fn lower_trait_ref(
@@ -1308,16 +602,7 @@ impl<'a> TyLoweringContext<'a> {
         trait_ref: &HirTraitRef,
         explicit_self_ty: Ty,
     ) -> Option<TraitRef> {
-        self.lower_trait_ref_from_path(trait_ref.path, explicit_self_ty)
-    }
-
-    fn trait_ref_substs_from_path(
-        &mut self,
-        segment: PathSegment<'_>,
-        resolved: TraitId,
-        explicit_self_ty: Ty,
-    ) -> Substitution {
-        self.substs_from_path_segment(segment, Some(resolved.into()), false, Some(explicit_self_ty))
+        self.lower_trait_ref_from_path(trait_ref.path, explicit_self_ty).map(|it| it.0)
     }
 
     pub(crate) fn lower_where_predicate<'b>(
@@ -1365,11 +650,18 @@ impl<'a> TyLoweringContext<'a> {
         self_ty: Ty,
         ignore_bindings: bool,
     ) -> impl Iterator<Item = QuantifiedWhereClause> + use<'b, 'a> {
-        let mut trait_ref = None;
-        let clause = match bound {
-            &TypeBound::Path(path, TraitBoundModifier::None) => {
-                trait_ref = self.lower_trait_ref_from_path(path, self_ty);
-                trait_ref.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders)
+        let mut assoc_bounds = None;
+        let mut clause = None;
+        match bound {
+            &TypeBound::Path(path, TraitBoundModifier::None) | &TypeBound::ForLifetime(_, path) => {
+                // FIXME Don't silently drop the hrtb lifetimes here
+                if let Some((trait_ref, ctx)) = self.lower_trait_ref_from_path(path, self_ty) {
+                    if !ignore_bindings {
+                        assoc_bounds =
+                            ctx.assoc_type_bindings_from_type_bound(bound, trait_ref.clone());
+                    }
+                    clause = Some(crate::wrap_empty_binders(WhereClause::Implemented(trait_ref)));
+                }
             }
             &TypeBound::Path(path, TraitBoundModifier::Maybe) => {
                 let sized_trait = self
@@ -1381,170 +673,21 @@ impl<'a> TyLoweringContext<'a> {
                 // If we got another trait here ignore the bound completely.
                 let trait_id = self
                     .lower_trait_ref_from_path(path, self_ty.clone())
-                    .map(|trait_ref| trait_ref.hir_trait_id());
+                    .map(|(trait_ref, _)| trait_ref.hir_trait_id());
                 if trait_id == sized_trait {
                     self.unsized_types.insert(self_ty);
                 }
-                None
-            }
-            &TypeBound::ForLifetime(_, path) => {
-                // FIXME Don't silently drop the hrtb lifetimes here
-                trait_ref = self.lower_trait_ref_from_path(path, self_ty);
-                trait_ref.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders)
             }
             TypeBound::Lifetime(l) => {
                 let lifetime = self.lower_lifetime(l);
-                Some(crate::wrap_empty_binders(WhereClause::TypeOutlives(TypeOutlives {
+                clause = Some(crate::wrap_empty_binders(WhereClause::TypeOutlives(TypeOutlives {
                     ty: self_ty,
                     lifetime,
-                })))
+                })));
             }
-            TypeBound::Use(_) | TypeBound::Error => None,
-        };
-        clause.into_iter().chain(
-            trait_ref
-                .filter(move |_| !ignore_bindings)
-                .map(move |tr| self.assoc_type_bindings_from_type_bound(bound, tr))
-                .into_iter()
-                .flatten(),
-        )
-    }
-
-    fn assoc_type_bindings_from_type_bound<'b>(
-        &'b mut self,
-        bound: &'b TypeBound,
-        trait_ref: TraitRef,
-    ) -> impl Iterator<Item = QuantifiedWhereClause> + use<'b, 'a> {
-        let last_segment = match bound {
-            &TypeBound::Path(path, TraitBoundModifier::None) | &TypeBound::ForLifetime(_, path) => {
-                self.types_map[path].segments().last()
-            }
-            TypeBound::Path(_, TraitBoundModifier::Maybe)
-            | TypeBound::Use(_)
-            | TypeBound::Error
-            | TypeBound::Lifetime(_) => None,
-        };
-        last_segment
-            .into_iter()
-            .filter_map(|segment| segment.args_and_bindings)
-            .flat_map(|args_and_bindings| args_and_bindings.bindings.iter())
-            .flat_map(move |binding| {
-                let found = associated_type_by_name_including_super_traits(
-                    self.db,
-                    trait_ref.clone(),
-                    &binding.name,
-                );
-                let (super_trait_ref, associated_ty) = match found {
-                    None => return SmallVec::new(),
-                    Some(t) => t,
-                };
-                // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
-                // generic params. It's inefficient to splice the `Substitution`s, so we may want
-                // that method to optionally take parent `Substitution` as we already know them at
-                // this point (`super_trait_ref.substitution`).
-                let substitution = self.substs_from_path_segment(
-                    // FIXME: This is hack. We shouldn't really build `PathSegment` directly.
-                    PathSegment { name: &binding.name, args_and_bindings: binding.args.as_ref() },
-                    Some(associated_ty.into()),
-                    false, // this is not relevant
-                    Some(super_trait_ref.self_type_parameter(Interner)),
-                );
-                let self_params = generics(self.db.upcast(), associated_ty.into()).len_self();
-                let substitution = Substitution::from_iter(
-                    Interner,
-                    substitution
-                        .iter(Interner)
-                        .take(self_params)
-                        .chain(super_trait_ref.substitution.iter(Interner)),
-                );
-                let projection_ty = ProjectionTy {
-                    associated_ty_id: to_assoc_type_id(associated_ty),
-                    substitution,
-                };
-                let mut predicates: SmallVec<[_; 1]> = SmallVec::with_capacity(
-                    binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
-                );
-                if let Some(type_ref) = binding.type_ref {
-                    match (&self.types_map[type_ref], self.impl_trait_mode.mode) {
-                        (TypeRef::ImplTrait(_), ImplTraitLoweringMode::Disallowed) => (),
-                        (_, ImplTraitLoweringMode::Disallowed | ImplTraitLoweringMode::Opaque) => {
-                            let ty = self.lower_ty(type_ref);
-                            let alias_eq =
-                                AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty };
-                            predicates
-                                .push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq)));
-                        }
-                        (_, ImplTraitLoweringMode::Param | ImplTraitLoweringMode::Variable) => {
-                            // Find the generic index for the target of our `bound`
-                            let target_param_idx = self
-                                .resolver
-                                .where_predicates_in_scope()
-                                .find_map(|(p, _)| match p {
-                                    WherePredicate::TypeBound {
-                                        target: WherePredicateTypeTarget::TypeOrConstParam(idx),
-                                        bound: b,
-                                    } if b == bound => Some(idx),
-                                    _ => None,
-                                });
-                            let ty = if let Some(target_param_idx) = target_param_idx {
-                                let mut counter = 0;
-                                let generics = self.generics().expect("generics in scope");
-                                for (idx, data) in generics.iter_self_type_or_consts() {
-                                    // Count the number of `impl Trait` things that appear before
-                                    // the target of our `bound`.
-                                    // Our counter within `impl_trait_mode` should be that number
-                                    // to properly lower each types within `type_ref`
-                                    if data.type_param().is_some_and(|p| {
-                                        p.provenance == TypeParamProvenance::ArgumentImplTrait
-                                    }) {
-                                        counter += 1;
-                                    }
-                                    if idx == *target_param_idx {
-                                        break;
-                                    }
-                                }
-                                let mut ext = TyLoweringContext::new_maybe_unowned(
-                                    self.db,
-                                    self.resolver,
-                                    self.types_map,
-                                    self.types_source_map,
-                                    self.owner,
-                                )
-                                .with_type_param_mode(self.type_param_mode);
-                                match self.impl_trait_mode.mode {
-                                    ImplTraitLoweringMode::Param => {
-                                        ext.impl_trait_mode =
-                                            ImplTraitLoweringState::param(counter);
-                                    }
-                                    ImplTraitLoweringMode::Variable => {
-                                        ext.impl_trait_mode =
-                                            ImplTraitLoweringState::variable(counter);
-                                    }
-                                    _ => unreachable!(),
-                                }
-                                let ty = ext.lower_ty(type_ref);
-                                self.diagnostics.extend(ext.diagnostics);
-                                ty
-                            } else {
-                                self.lower_ty(type_ref)
-                            };
-
-                            let alias_eq =
-                                AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty };
-                            predicates
-                                .push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq)));
-                        }
-                    }
-                }
-                for bound in binding.bounds.iter() {
-                    predicates.extend(self.lower_type_bound(
-                        bound,
-                        TyKind::Alias(AliasTy::Projection(projection_ty.clone())).intern(Interner),
-                        false,
-                    ));
-                }
-                predicates
-            })
+            TypeBound::Use(_) | TypeBound::Error => {}
+        }
+        clause.into_iter().chain(assoc_bounds.into_iter().flatten())
     }
 
     fn lower_dyn_trait(&mut self, bounds: &[TypeBound]) -> Ty {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/diagnostics.rs
index 7fe196cdbb5..5c77bcd0736 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/diagnostics.rs
@@ -26,11 +26,11 @@ pub enum GenericArgsProhibitedReason {
     Static,
     /// When there is a generic enum, within the expression `Enum::Variant`,
     /// either `Enum` or `Variant` are allowed to have generic arguments, but not both.
-    // FIXME: This is not used now but it should be.
     EnumVariant,
 }
 
 #[derive(Debug, PartialEq, Eq, Clone)]
 pub enum PathLoweringDiagnostic {
     GenericArgsProhibited { segment: u32, reason: GenericArgsProhibitedReason },
+    ParenthesizedGenericArgsWithoutFnTrait { segment: u32 },
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs
new file mode 100644
index 00000000000..22c5bb9923f
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs
@@ -0,0 +1,911 @@
+//! A wrapper around [`TyLoweringContext`] specifically for lowering paths.
+
+use std::iter;
+
+use chalk_ir::{cast::Cast, fold::Shift, BoundVar};
+use either::Either;
+use hir_def::{
+    data::TraitFlags,
+    expr_store::HygieneId,
+    generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget},
+    path::{GenericArg, GenericArgs, Path, PathSegment, PathSegments},
+    resolver::{ResolveValueResult, TypeNs, ValueNs},
+    type_ref::{TypeBound, TypeRef},
+    GenericDefId, GenericParamId, ItemContainerId, Lookup, TraitId,
+};
+use smallvec::SmallVec;
+use stdx::never;
+
+use crate::{
+    consteval::unknown_const_as_generic,
+    error_lifetime,
+    generics::generics,
+    lower::{
+        generic_arg_to_chalk, named_associated_type_shorthand_candidates, ImplTraitLoweringState,
+    },
+    to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
+    utils::associated_type_by_name_including_super_traits,
+    AliasEq, AliasTy, GenericArgsProhibitedReason, ImplTraitLoweringMode, Interner,
+    ParamLoweringMode, PathLoweringDiagnostic, ProjectionTy, QuantifiedWhereClause, Substitution,
+    TraitRef, Ty, TyBuilder, TyDefId, TyKind, TyLoweringContext, ValueTyDefId, WhereClause,
+};
+
+type CallbackData<'a> = Either<
+    super::PathDiagnosticCallbackData,
+    crate::infer::diagnostics::PathDiagnosticCallbackData<'a>,
+>;
+
+// We cannot use `&mut dyn FnMut()` because of lifetime issues, and we don't want to use `Box<dyn FnMut()>`
+// because of the allocation, so we create a lifetime-less callback, tailored for our needs.
+pub(crate) struct PathDiagnosticCallback<'a> {
+    pub(crate) data: CallbackData<'a>,
+    pub(crate) callback: fn(&CallbackData<'_>, &mut TyLoweringContext<'_>, PathLoweringDiagnostic),
+}
+
+pub(crate) struct PathLoweringContext<'a, 'b> {
+    ctx: &'a mut TyLoweringContext<'b>,
+    on_diagnostic: PathDiagnosticCallback<'a>,
+    path: &'a Path,
+    segments: PathSegments<'a>,
+    current_segment_idx: usize,
+    /// Contains the previous segment if `current_segment_idx == segments.len()`
+    current_or_prev_segment: PathSegment<'a>,
+}
+
+impl<'a, 'b> PathLoweringContext<'a, 'b> {
+    #[inline]
+    pub(crate) fn new(
+        ctx: &'a mut TyLoweringContext<'b>,
+        on_diagnostic: PathDiagnosticCallback<'a>,
+        path: &'a Path,
+    ) -> Self {
+        let segments = path.segments();
+        let first_segment = segments.first().unwrap_or(PathSegment::MISSING);
+        Self {
+            ctx,
+            on_diagnostic,
+            path,
+            segments,
+            current_segment_idx: 0,
+            current_or_prev_segment: first_segment,
+        }
+    }
+
+    #[inline]
+    #[cold]
+    fn on_diagnostic(&mut self, diag: PathLoweringDiagnostic) {
+        (self.on_diagnostic.callback)(&self.on_diagnostic.data, self.ctx, diag);
+    }
+
+    #[inline]
+    pub(crate) fn ty_ctx(&mut self) -> &mut TyLoweringContext<'b> {
+        self.ctx
+    }
+
+    #[inline]
+    fn current_segment_u32(&self) -> u32 {
+        self.current_segment_idx as u32
+    }
+
+    #[inline]
+    fn skip_resolved_segment(&mut self) {
+        if !matches!(self.path, Path::LangItem(..)) {
+            // In lang items, the resolved "segment" is not one of the segments. Perhaps we should've put it
+            // point at -1, but I don't feel this is clearer.
+            self.current_segment_idx += 1;
+        }
+        self.update_current_segment();
+    }
+
+    #[inline]
+    fn update_current_segment(&mut self) {
+        self.current_or_prev_segment =
+            self.segments.get(self.current_segment_idx).unwrap_or(self.current_or_prev_segment);
+    }
+
+    #[inline]
+    pub(crate) fn ignore_last_segment(&mut self) {
+        self.segments = self.segments.strip_last();
+    }
+
+    #[inline]
+    pub(crate) fn set_current_segment(&mut self, segment: usize) {
+        self.current_segment_idx = segment;
+        self.current_or_prev_segment = self
+            .segments
+            .get(segment)
+            .expect("invalid segment passed to PathLoweringContext::set_current_segment()");
+    }
+
+    pub(crate) fn lower_ty_relative_path(
+        &mut self,
+        ty: Ty,
+        // We need the original resolution to lower `Self::AssocTy` correctly
+        res: Option<TypeNs>,
+    ) -> (Ty, Option<TypeNs>) {
+        match self.segments.len() - self.current_segment_idx {
+            0 => (ty, res),
+            1 => {
+                // resolve unselected assoc types
+                (self.select_associated_type(res), None)
+            }
+            _ => {
+                // FIXME report error (ambiguous associated type)
+                (TyKind::Error.intern(Interner), None)
+            }
+        }
+    }
+
+    fn prohibit_parenthesized_generic_args(&mut self) -> bool {
+        if let Some(generic_args) = self.current_or_prev_segment.args_and_bindings {
+            if generic_args.desugared_from_fn {
+                let segment = self.current_segment_u32();
+                self.on_diagnostic(
+                    PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment },
+                );
+                return true;
+            }
+        }
+        false
+    }
+
+    // When calling this, the current segment is the resolved segment (we don't advance it yet).
+    pub(crate) fn lower_partly_resolved_path(
+        &mut self,
+        resolution: TypeNs,
+        infer_args: bool,
+    ) -> (Ty, Option<TypeNs>) {
+        let remaining_segments = self.segments.skip(self.current_segment_idx + 1);
+
+        let ty = match resolution {
+            TypeNs::TraitId(trait_) => {
+                let ty = match remaining_segments.len() {
+                    1 => {
+                        let trait_ref = self.lower_trait_ref_from_resolved_path(
+                            trait_,
+                            TyKind::Error.intern(Interner),
+                        );
+
+                        self.skip_resolved_segment();
+                        let segment = self.current_or_prev_segment;
+                        let found =
+                            self.ctx.db.trait_data(trait_).associated_type_by_name(segment.name);
+
+                        match found {
+                            Some(associated_ty) => {
+                                // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
+                                // generic params. It's inefficient to splice the `Substitution`s, so we may want
+                                // that method to optionally take parent `Substitution` as we already know them at
+                                // this point (`trait_ref.substitution`).
+                                let substitution = self.substs_from_path_segment(
+                                    associated_ty.into(),
+                                    false,
+                                    None,
+                                );
+                                let len_self =
+                                    generics(self.ctx.db.upcast(), associated_ty.into()).len_self();
+                                let substitution = Substitution::from_iter(
+                                    Interner,
+                                    substitution
+                                        .iter(Interner)
+                                        .take(len_self)
+                                        .chain(trait_ref.substitution.iter(Interner)),
+                                );
+                                TyKind::Alias(AliasTy::Projection(ProjectionTy {
+                                    associated_ty_id: to_assoc_type_id(associated_ty),
+                                    substitution,
+                                }))
+                                .intern(Interner)
+                            }
+                            None => {
+                                // FIXME: report error (associated type not found)
+                                TyKind::Error.intern(Interner)
+                            }
+                        }
+                    }
+                    0 => {
+                        // Trait object type without dyn; this should be handled in upstream. See
+                        // `lower_path()`.
+                        stdx::never!("unexpected fully resolved trait path");
+                        TyKind::Error.intern(Interner)
+                    }
+                    _ => {
+                        // FIXME report error (ambiguous associated type)
+                        TyKind::Error.intern(Interner)
+                    }
+                };
+                return (ty, None);
+            }
+            TypeNs::TraitAliasId(_) => {
+                // FIXME(trait_alias): Implement trait alias.
+                return (TyKind::Error.intern(Interner), None);
+            }
+            TypeNs::GenericParam(param_id) => match self.ctx.type_param_mode {
+                ParamLoweringMode::Placeholder => {
+                    TyKind::Placeholder(to_placeholder_idx(self.ctx.db, param_id.into()))
+                }
+                ParamLoweringMode::Variable => {
+                    let idx = match self
+                        .ctx
+                        .generics()
+                        .expect("generics in scope")
+                        .type_or_const_param_idx(param_id.into())
+                    {
+                        None => {
+                            never!("no matching generics");
+                            return (TyKind::Error.intern(Interner), None);
+                        }
+                        Some(idx) => idx,
+                    };
+
+                    TyKind::BoundVar(BoundVar::new(self.ctx.in_binders, idx))
+                }
+            }
+            .intern(Interner),
+            TypeNs::SelfType(impl_id) => {
+                let generics = self.ctx.generics().expect("impl should have generic param scope");
+
+                match self.ctx.type_param_mode {
+                    ParamLoweringMode::Placeholder => {
+                        // `def` can be either impl itself or item within, and we need impl itself
+                        // now.
+                        let generics = generics.parent_or_self();
+                        let subst = generics.placeholder_subst(self.ctx.db);
+                        self.ctx.db.impl_self_ty(impl_id).substitute(Interner, &subst)
+                    }
+                    ParamLoweringMode::Variable => {
+                        let starting_from = match generics.def() {
+                            GenericDefId::ImplId(_) => 0,
+                            // `def` is an item within impl. We need to substitute `BoundVar`s but
+                            // remember that they are for parent (i.e. impl) generic params so they
+                            // come after our own params.
+                            _ => generics.len_self(),
+                        };
+                        TyBuilder::impl_self_ty(self.ctx.db, impl_id)
+                            .fill_with_bound_vars(self.ctx.in_binders, starting_from)
+                            .build()
+                    }
+                }
+            }
+            TypeNs::AdtSelfType(adt) => {
+                let generics = generics(self.ctx.db.upcast(), adt.into());
+                let substs = match self.ctx.type_param_mode {
+                    ParamLoweringMode::Placeholder => generics.placeholder_subst(self.ctx.db),
+                    ParamLoweringMode::Variable => {
+                        generics.bound_vars_subst(self.ctx.db, self.ctx.in_binders)
+                    }
+                };
+                self.ctx.db.ty(adt.into()).substitute(Interner, &substs)
+            }
+
+            TypeNs::AdtId(it) => self.lower_path_inner(it.into(), infer_args),
+            TypeNs::BuiltinType(it) => self.lower_path_inner(it.into(), infer_args),
+            TypeNs::TypeAliasId(it) => self.lower_path_inner(it.into(), infer_args),
+            // FIXME: report error
+            TypeNs::EnumVariantId(_) => return (TyKind::Error.intern(Interner), None),
+        };
+
+        self.skip_resolved_segment();
+        self.lower_ty_relative_path(ty, Some(resolution))
+    }
+
+    fn handle_type_ns_resolution(&mut self, resolution: &TypeNs) {
+        let mut prohibit_generics_on_resolved = |reason| {
+            if self.current_or_prev_segment.args_and_bindings.is_some() {
+                let segment = self.current_segment_u32();
+                self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited {
+                    segment,
+                    reason,
+                });
+            }
+        };
+
+        match resolution {
+            TypeNs::SelfType(_) => {
+                prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy)
+            }
+            TypeNs::GenericParam(_) => {
+                prohibit_generics_on_resolved(GenericArgsProhibitedReason::TyParam)
+            }
+            TypeNs::AdtSelfType(_) => {
+                prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy)
+            }
+            TypeNs::BuiltinType(_) => {
+                prohibit_generics_on_resolved(GenericArgsProhibitedReason::PrimitiveTy)
+            }
+            TypeNs::AdtId(_)
+            | TypeNs::EnumVariantId(_)
+            | TypeNs::TypeAliasId(_)
+            | TypeNs::TraitId(_)
+            | TypeNs::TraitAliasId(_) => {}
+        }
+    }
+
+    pub(crate) fn resolve_path_in_type_ns_fully(&mut self) -> Option<TypeNs> {
+        let (res, unresolved) = self.resolve_path_in_type_ns()?;
+        if unresolved.is_some() {
+            return None;
+        }
+        Some(res)
+    }
+
+    pub(crate) fn resolve_path_in_type_ns(&mut self) -> Option<(TypeNs, Option<usize>)> {
+        let (resolution, remaining_index, _, prefix_info) = self
+            .ctx
+            .resolver
+            .resolve_path_in_type_ns_with_prefix_info(self.ctx.db.upcast(), self.path)?;
+
+        let segments = self.segments;
+        if segments.is_empty() || matches!(self.path, Path::LangItem(..)) {
+            // `segments.is_empty()` can occur with `self`.
+            return Some((resolution, remaining_index));
+        }
+
+        let (module_segments, resolved_segment_idx, enum_segment) = match remaining_index {
+            None if prefix_info.enum_variant => {
+                (segments.strip_last_two(), segments.len() - 1, Some(segments.len() - 2))
+            }
+            None => (segments.strip_last(), segments.len() - 1, None),
+            Some(i) => (segments.take(i - 1), i - 1, None),
+        };
+
+        self.current_segment_idx = resolved_segment_idx;
+        self.current_or_prev_segment =
+            segments.get(resolved_segment_idx).expect("should have resolved segment");
+
+        if matches!(self.path, Path::BarePath(..)) {
+            // Bare paths cannot have generics, so skip them as an optimization.
+            return Some((resolution, remaining_index));
+        }
+
+        for (i, mod_segment) in module_segments.iter().enumerate() {
+            if mod_segment.args_and_bindings.is_some() {
+                self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited {
+                    segment: i as u32,
+                    reason: GenericArgsProhibitedReason::Module,
+                });
+            }
+        }
+
+        if let Some(enum_segment) = enum_segment {
+            if segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some())
+                && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some())
+            {
+                self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited {
+                    segment: (enum_segment + 1) as u32,
+                    reason: GenericArgsProhibitedReason::EnumVariant,
+                });
+            }
+        }
+
+        self.handle_type_ns_resolution(&resolution);
+
+        Some((resolution, remaining_index))
+    }
+
+    pub(crate) fn resolve_path_in_value_ns(
+        &mut self,
+        hygiene_id: HygieneId,
+    ) -> Option<ResolveValueResult> {
+        let (res, prefix_info) = self.ctx.resolver.resolve_path_in_value_ns_with_prefix_info(
+            self.ctx.db.upcast(),
+            self.path,
+            hygiene_id,
+        )?;
+
+        let segments = self.segments;
+        if segments.is_empty() || matches!(self.path, Path::LangItem(..)) {
+            // `segments.is_empty()` can occur with `self`.
+            return Some(res);
+        }
+
+        let (mod_segments, enum_segment, resolved_segment_idx) = match res {
+            ResolveValueResult::Partial(_, unresolved_segment, _) => {
+                (segments.take(unresolved_segment - 1), None, unresolved_segment - 1)
+            }
+            ResolveValueResult::ValueNs(ValueNs::EnumVariantId(_), _)
+                if prefix_info.enum_variant =>
+            {
+                (segments.strip_last_two(), segments.len().checked_sub(2), segments.len() - 1)
+            }
+            ResolveValueResult::ValueNs(..) => (segments.strip_last(), None, segments.len() - 1),
+        };
+
+        self.current_segment_idx = resolved_segment_idx;
+        self.current_or_prev_segment =
+            segments.get(resolved_segment_idx).expect("should have resolved segment");
+
+        for (i, mod_segment) in mod_segments.iter().enumerate() {
+            if mod_segment.args_and_bindings.is_some() {
+                self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited {
+                    segment: i as u32,
+                    reason: GenericArgsProhibitedReason::Module,
+                });
+            }
+        }
+
+        if let Some(enum_segment) = enum_segment {
+            if segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some())
+                && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some())
+            {
+                self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited {
+                    segment: (enum_segment + 1) as u32,
+                    reason: GenericArgsProhibitedReason::EnumVariant,
+                });
+            }
+        }
+
+        match &res {
+            ResolveValueResult::ValueNs(resolution, _) => {
+                let resolved_segment_idx = self.current_segment_u32();
+                let resolved_segment = self.current_or_prev_segment;
+
+                let mut prohibit_generics_on_resolved = |reason| {
+                    if resolved_segment.args_and_bindings.is_some() {
+                        self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited {
+                            segment: resolved_segment_idx,
+                            reason,
+                        });
+                    }
+                };
+
+                match resolution {
+                    ValueNs::ImplSelf(_) => {
+                        prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy)
+                    }
+                    // FIXME: rustc generates E0107 (incorrect number of generic arguments) and not
+                    // E0109 (generic arguments provided for a type that doesn't accept them) for
+                    // consts and statics, presumably as a defense against future in which consts
+                    // and statics can be generic, or just because it was easier for rustc implementors.
+                    // That means we'll show the wrong error code. Because of us it's easier to do it
+                    // this way :)
+                    ValueNs::GenericParam(_) | ValueNs::ConstId(_) => {
+                        prohibit_generics_on_resolved(GenericArgsProhibitedReason::Const)
+                    }
+                    ValueNs::StaticId(_) => {
+                        prohibit_generics_on_resolved(GenericArgsProhibitedReason::Static)
+                    }
+                    ValueNs::FunctionId(_) | ValueNs::StructId(_) | ValueNs::EnumVariantId(_) => {}
+                    ValueNs::LocalBinding(_) => {}
+                }
+            }
+            ResolveValueResult::Partial(resolution, _, _) => {
+                self.handle_type_ns_resolution(resolution);
+            }
+        };
+        Some(res)
+    }
+
+    fn select_associated_type(&mut self, res: Option<TypeNs>) -> Ty {
+        let Some((generics, res)) = self.ctx.generics().zip(res) else {
+            return TyKind::Error.intern(Interner);
+        };
+        let segment = self.current_or_prev_segment;
+        let ty = named_associated_type_shorthand_candidates(
+            self.ctx.db,
+            generics.def(),
+            res,
+            Some(segment.name.clone()),
+            move |name, t, associated_ty| {
+                let generics = self.ctx.generics().unwrap();
+
+                if name != segment.name {
+                    return None;
+                }
+
+                let parent_subst = t.substitution.clone();
+                let parent_subst = match self.ctx.type_param_mode {
+                    ParamLoweringMode::Placeholder => {
+                        // if we're lowering to placeholders, we have to put them in now.
+                        let s = generics.placeholder_subst(self.ctx.db);
+                        s.apply(parent_subst, Interner)
+                    }
+                    ParamLoweringMode::Variable => {
+                        // We need to shift in the bound vars, since
+                        // `named_associated_type_shorthand_candidates` does not do that.
+                        parent_subst.shifted_in_from(Interner, self.ctx.in_binders)
+                    }
+                };
+
+                // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
+                // generic params. It's inefficient to splice the `Substitution`s, so we may want
+                // that method to optionally take parent `Substitution` as we already know them at
+                // this point (`t.substitution`).
+                let substs = self.substs_from_path_segment(associated_ty.into(), false, None);
+
+                let len_self =
+                    crate::generics::generics(self.ctx.db.upcast(), associated_ty.into())
+                        .len_self();
+
+                let substs = Substitution::from_iter(
+                    Interner,
+                    substs.iter(Interner).take(len_self).chain(parent_subst.iter(Interner)),
+                );
+
+                Some(
+                    TyKind::Alias(AliasTy::Projection(ProjectionTy {
+                        associated_ty_id: to_assoc_type_id(associated_ty),
+                        substitution: substs,
+                    }))
+                    .intern(Interner),
+                )
+            },
+        );
+
+        ty.unwrap_or_else(|| TyKind::Error.intern(Interner))
+    }
+
+    fn lower_path_inner(&mut self, typeable: TyDefId, infer_args: bool) -> Ty {
+        let generic_def = match typeable {
+            TyDefId::BuiltinType(builtin) => return TyBuilder::builtin(builtin),
+            TyDefId::AdtId(it) => it.into(),
+            TyDefId::TypeAliasId(it) => it.into(),
+        };
+        let substs = self.substs_from_path_segment(generic_def, infer_args, None);
+        self.ctx.db.ty(typeable).substitute(Interner, &substs)
+    }
+
+    /// Collect generic arguments from a path into a `Substs`. See also
+    /// `create_substs_for_ast_path` and `def_to_ty` in rustc.
+    pub(crate) fn substs_from_path(
+        &mut self,
+        // Note that we don't call `db.value_type(resolved)` here,
+        // `ValueTyDefId` is just a convenient way to pass generics and
+        // special-case enum variants
+        resolved: ValueTyDefId,
+        infer_args: bool,
+    ) -> Substitution {
+        let prev_current_segment_idx = self.current_segment_idx;
+        let prev_current_segment = self.current_or_prev_segment;
+
+        let generic_def = match resolved {
+            ValueTyDefId::FunctionId(it) => it.into(),
+            ValueTyDefId::StructId(it) => it.into(),
+            ValueTyDefId::UnionId(it) => it.into(),
+            ValueTyDefId::ConstId(it) => it.into(),
+            ValueTyDefId::StaticId(_) => return Substitution::empty(Interner),
+            ValueTyDefId::EnumVariantId(var) => {
+                // the generic args for an enum variant may be either specified
+                // on the segment referring to the enum, or on the segment
+                // referring to the variant. So `Option::<T>::None` and
+                // `Option::None::<T>` are both allowed (though the former is
+                // FIXME: This isn't strictly correct, enum variants may be used not through the enum
+                // (via `use Enum::Variant`). The resolver returns whether they were, but we don't have its result
+                // available here. The worst that can happen is that we will show some confusing diagnostics to the user,
+                // if generics exist on the module and they don't match with the variant.
+                // preferred). See also `def_ids_for_path_segments` in rustc.
+                //
+                // `wrapping_sub(1)` will return a number which `get` will return None for if current_segment_idx<2.
+                // This simplifies the code a bit.
+                let penultimate_idx = self.current_segment_idx.wrapping_sub(1);
+                let penultimate = self.segments.get(penultimate_idx);
+                if let Some(penultimate) = penultimate {
+                    if self.current_or_prev_segment.args_and_bindings.is_none()
+                        && penultimate.args_and_bindings.is_some()
+                    {
+                        self.current_segment_idx = penultimate_idx;
+                        self.current_or_prev_segment = penultimate;
+                    }
+                }
+                var.lookup(self.ctx.db.upcast()).parent.into()
+            }
+        };
+        let result = self.substs_from_path_segment(generic_def, infer_args, None);
+        self.current_segment_idx = prev_current_segment_idx;
+        self.current_or_prev_segment = prev_current_segment;
+        result
+    }
+
+    pub(crate) fn substs_from_path_segment(
+        &mut self,
+        def: GenericDefId,
+        infer_args: bool,
+        explicit_self_ty: Option<Ty>,
+    ) -> Substitution {
+        let prohibit_parens = match def {
+            GenericDefId::TraitId(trait_) => {
+                let trait_data = self.ctx.db.trait_data(trait_);
+                !trait_data.flags.contains(TraitFlags::RUSTC_PAREN_SUGAR)
+            }
+            _ => true,
+        };
+        if prohibit_parens && self.prohibit_parenthesized_generic_args() {
+            return TyBuilder::unknown_subst(self.ctx.db, def);
+        }
+
+        self.substs_from_args_and_bindings(
+            self.current_or_prev_segment.args_and_bindings,
+            def,
+            infer_args,
+            explicit_self_ty,
+        )
+    }
+
+    pub(super) fn substs_from_args_and_bindings(
+        &mut self,
+        args_and_bindings: Option<&GenericArgs>,
+        def: GenericDefId,
+        infer_args: bool,
+        explicit_self_ty: Option<Ty>,
+    ) -> Substitution {
+        // Order is
+        // - Optional Self parameter
+        // - Lifetime parameters
+        // - Type or Const parameters
+        // - Parent parameters
+        let def_generics = generics(self.ctx.db.upcast(), def);
+        let (
+            parent_params,
+            self_param,
+            type_params,
+            const_params,
+            impl_trait_params,
+            lifetime_params,
+        ) = def_generics.provenance_split();
+        let item_len =
+            self_param as usize + type_params + const_params + impl_trait_params + lifetime_params;
+        let total_len = parent_params + item_len;
+
+        let mut substs = Vec::new();
+
+        // we need to iterate the lifetime and type/const params separately as our order of them
+        // differs from the supplied syntax
+
+        let ty_error = || TyKind::Error.intern(Interner).cast(Interner);
+        let mut def_toc_iter = def_generics.iter_self_type_or_consts_id();
+        let fill_self_param = || {
+            if self_param {
+                let self_ty = explicit_self_ty.map(|x| x.cast(Interner)).unwrap_or_else(ty_error);
+
+                if let Some(id) = def_toc_iter.next() {
+                    assert!(matches!(id, GenericParamId::TypeParamId(_)));
+                    substs.push(self_ty);
+                }
+            }
+        };
+        let mut had_explicit_args = false;
+
+        if let Some(&GenericArgs { ref args, has_self_type, .. }) = args_and_bindings {
+            // Fill in the self param first
+            if has_self_type && self_param {
+                had_explicit_args = true;
+                if let Some(id) = def_toc_iter.next() {
+                    assert!(matches!(id, GenericParamId::TypeParamId(_)));
+                    had_explicit_args = true;
+                    if let GenericArg::Type(ty) = &args[0] {
+                        substs.push(self.ctx.lower_ty(*ty).cast(Interner));
+                    }
+                }
+            } else {
+                fill_self_param()
+            };
+
+            // Then fill in the supplied lifetime args, or error lifetimes if there are too few
+            // (default lifetimes aren't a thing)
+            for arg in args
+                .iter()
+                .filter_map(|arg| match arg {
+                    GenericArg::Lifetime(arg) => Some(self.ctx.lower_lifetime(arg)),
+                    _ => None,
+                })
+                .chain(iter::repeat(error_lifetime()))
+                .take(lifetime_params)
+            {
+                substs.push(arg.cast(Interner));
+            }
+
+            let skip = if has_self_type { 1 } else { 0 };
+            // Fill in supplied type and const args
+            // Note if non-lifetime args are provided, it should be all of them, but we can't rely on that
+            for (arg, id) in args
+                .iter()
+                .filter(|arg| !matches!(arg, GenericArg::Lifetime(_)))
+                .skip(skip)
+                .take(type_params + const_params)
+                .zip(def_toc_iter)
+            {
+                had_explicit_args = true;
+                let arg = generic_arg_to_chalk(
+                    self.ctx.db,
+                    id,
+                    arg,
+                    self.ctx,
+                    self.ctx.types_map,
+                    |ctx, type_ref| ctx.lower_ty(type_ref),
+                    |ctx, const_ref, ty| ctx.lower_const(const_ref, ty),
+                    |ctx, lifetime_ref| ctx.lower_lifetime(lifetime_ref),
+                );
+                substs.push(arg);
+            }
+        } else {
+            fill_self_param();
+        }
+
+        let param_to_err = |id| match id {
+            GenericParamId::ConstParamId(x) => {
+                unknown_const_as_generic(self.ctx.db.const_param_ty(x))
+            }
+            GenericParamId::TypeParamId(_) => ty_error(),
+            GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner),
+        };
+        // handle defaults. In expression or pattern path segments without
+        // explicitly specified type arguments, missing type arguments are inferred
+        // (i.e. defaults aren't used).
+        // Generic parameters for associated types are not supposed to have defaults, so we just
+        // ignore them.
+        let is_assoc_ty = || match def {
+            GenericDefId::TypeAliasId(id) => {
+                matches!(id.lookup(self.ctx.db.upcast()).container, ItemContainerId::TraitId(_))
+            }
+            _ => false,
+        };
+        let fill_defaults = (!infer_args || had_explicit_args) && !is_assoc_ty();
+        if fill_defaults {
+            let defaults = &*self.ctx.db.generic_defaults(def);
+            let (item, _parent) = defaults.split_at(item_len);
+            let parent_from = item_len - substs.len();
+
+            let mut rem =
+                def_generics.iter_id().skip(substs.len()).map(param_to_err).collect::<Vec<_>>();
+            // Fill in defaults for type/const params
+            for (idx, default_ty) in item[substs.len()..].iter().enumerate() {
+                // each default can depend on the previous parameters
+                let substs_so_far = Substitution::from_iter(
+                    Interner,
+                    substs.iter().cloned().chain(rem[idx..].iter().cloned()),
+                );
+                substs.push(default_ty.clone().substitute(Interner, &substs_so_far));
+            }
+            // Fill in remaining parent params
+            substs.extend(rem.drain(parent_from..));
+        } else {
+            // Fill in remaining def params and parent params
+            substs.extend(def_generics.iter_id().skip(substs.len()).map(param_to_err));
+        }
+
+        assert_eq!(substs.len(), total_len, "expected {} substs, got {}", total_len, substs.len());
+        Substitution::from_iter(Interner, substs)
+    }
+
+    pub(crate) fn lower_trait_ref_from_resolved_path(
+        &mut self,
+        resolved: TraitId,
+        explicit_self_ty: Ty,
+    ) -> TraitRef {
+        let substs = self.trait_ref_substs_from_path(resolved, explicit_self_ty);
+        TraitRef { trait_id: to_chalk_trait_id(resolved), substitution: substs }
+    }
+
+    fn trait_ref_substs_from_path(
+        &mut self,
+        resolved: TraitId,
+        explicit_self_ty: Ty,
+    ) -> Substitution {
+        self.substs_from_path_segment(resolved.into(), false, Some(explicit_self_ty))
+    }
+
+    pub(super) fn assoc_type_bindings_from_type_bound<'c>(
+        mut self,
+        bound: &'c TypeBound,
+        trait_ref: TraitRef,
+    ) -> Option<impl Iterator<Item = QuantifiedWhereClause> + use<'a, 'b, 'c>> {
+        self.current_or_prev_segment.args_and_bindings.map(|args_and_bindings| {
+            args_and_bindings.bindings.iter().flat_map(move |binding| {
+                let found = associated_type_by_name_including_super_traits(
+                    self.ctx.db,
+                    trait_ref.clone(),
+                    &binding.name,
+                );
+                let (super_trait_ref, associated_ty) = match found {
+                    None => return SmallVec::new(),
+                    Some(t) => t,
+                };
+                // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
+                // generic params. It's inefficient to splice the `Substitution`s, so we may want
+                // that method to optionally take parent `Substitution` as we already know them at
+                // this point (`super_trait_ref.substitution`).
+                let substitution = self.substs_from_args_and_bindings(
+                    binding.args.as_ref(),
+                    associated_ty.into(),
+                    false, // this is not relevant
+                    Some(super_trait_ref.self_type_parameter(Interner)),
+                );
+                let self_params = generics(self.ctx.db.upcast(), associated_ty.into()).len_self();
+                let substitution = Substitution::from_iter(
+                    Interner,
+                    substitution
+                        .iter(Interner)
+                        .take(self_params)
+                        .chain(super_trait_ref.substitution.iter(Interner)),
+                );
+                let projection_ty = ProjectionTy {
+                    associated_ty_id: to_assoc_type_id(associated_ty),
+                    substitution,
+                };
+                let mut predicates: SmallVec<[_; 1]> = SmallVec::with_capacity(
+                    binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
+                );
+                if let Some(type_ref) = binding.type_ref {
+                    match (&self.ctx.types_map[type_ref], self.ctx.impl_trait_mode.mode) {
+                        (TypeRef::ImplTrait(_), ImplTraitLoweringMode::Disallowed) => (),
+                        (_, ImplTraitLoweringMode::Disallowed | ImplTraitLoweringMode::Opaque) => {
+                            let ty = self.ctx.lower_ty(type_ref);
+                            let alias_eq =
+                                AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty };
+                            predicates
+                                .push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq)));
+                        }
+                        (_, ImplTraitLoweringMode::Param | ImplTraitLoweringMode::Variable) => {
+                            // Find the generic index for the target of our `bound`
+                            let target_param_idx =
+                                self.ctx.resolver.where_predicates_in_scope().find_map(|(p, _)| {
+                                    match p {
+                                        WherePredicate::TypeBound {
+                                            target: WherePredicateTypeTarget::TypeOrConstParam(idx),
+                                            bound: b,
+                                        } if b == bound => Some(idx),
+                                        _ => None,
+                                    }
+                                });
+                            let ty = if let Some(target_param_idx) = target_param_idx {
+                                let mut counter = 0;
+                                let generics = self.ctx.generics().expect("generics in scope");
+                                for (idx, data) in generics.iter_self_type_or_consts() {
+                                    // Count the number of `impl Trait` things that appear before
+                                    // the target of our `bound`.
+                                    // Our counter within `impl_trait_mode` should be that number
+                                    // to properly lower each types within `type_ref`
+                                    if data.type_param().is_some_and(|p| {
+                                        p.provenance == TypeParamProvenance::ArgumentImplTrait
+                                    }) {
+                                        counter += 1;
+                                    }
+                                    if idx == *target_param_idx {
+                                        break;
+                                    }
+                                }
+                                let mut ext = TyLoweringContext::new_maybe_unowned(
+                                    self.ctx.db,
+                                    self.ctx.resolver,
+                                    self.ctx.types_map,
+                                    self.ctx.types_source_map,
+                                    self.ctx.owner,
+                                )
+                                .with_type_param_mode(self.ctx.type_param_mode);
+                                match self.ctx.impl_trait_mode.mode {
+                                    ImplTraitLoweringMode::Param => {
+                                        ext.impl_trait_mode =
+                                            ImplTraitLoweringState::param(counter);
+                                    }
+                                    ImplTraitLoweringMode::Variable => {
+                                        ext.impl_trait_mode =
+                                            ImplTraitLoweringState::variable(counter);
+                                    }
+                                    _ => unreachable!(),
+                                }
+                                let ty = ext.lower_ty(type_ref);
+                                self.ctx.diagnostics.extend(ext.diagnostics);
+                                ty
+                            } else {
+                                self.ctx.lower_ty(type_ref)
+                            };
+
+                            let alias_eq =
+                                AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty };
+                            predicates
+                                .push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq)));
+                        }
+                    }
+                }
+                for bound in binding.bounds.iter() {
+                    predicates.extend(self.ctx.lower_type_bound(
+                        bound,
+                        TyKind::Alias(AliasTy::Projection(projection_ty.clone())).intern(Interner),
+                        false,
+                    ));
+                }
+                predicates
+            })
+        })
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
index 307673f52c7..1ed0daa3756 100644
--- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
@@ -112,6 +112,7 @@ diagnostics![
     UnusedMut,
     UnusedVariable,
     GenericArgsProhibited,
+    ParenthesizedGenericArgsWithoutFnTrait,
 ];
 
 #[derive(Debug)]
@@ -414,6 +415,11 @@ pub struct GenericArgsProhibited {
     pub reason: GenericArgsProhibitedReason,
 }
 
+#[derive(Debug)]
+pub struct ParenthesizedGenericArgsWithoutFnTrait {
+    pub args: InFile<AstPtr<ast::ParenthesizedArgList>>,
+}
+
 impl AnyDiagnostic {
     pub(crate) fn body_validation_diagnostic(
         db: &dyn HirDatabase,
@@ -703,8 +709,8 @@ impl AnyDiagnostic {
         diag: &PathLoweringDiagnostic,
         path: InFile<ast::Path>,
     ) -> Option<AnyDiagnostic> {
-        Some(match diag {
-            &PathLoweringDiagnostic::GenericArgsProhibited { segment, reason } => {
+        Some(match *diag {
+            PathLoweringDiagnostic::GenericArgsProhibited { segment, reason } => {
                 let segment = hir_segment_to_ast_segment(&path.value, segment)?;
                 let args = if let Some(generics) = segment.generic_arg_list() {
                     AstPtr::new(&generics).wrap_left()
@@ -714,6 +720,12 @@ impl AnyDiagnostic {
                 let args = path.with_value(args);
                 GenericArgsProhibited { args, reason }.into()
             }
+            PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment } => {
+                let segment = hir_segment_to_ast_segment(&path.value, segment)?;
+                let args = AstPtr::new(&segment.parenthesized_arg_list()?);
+                let args = path.with_value(args);
+                ParenthesizedGenericArgsWithoutFnTrait { args }.into()
+            }
         })
     }
 
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs
new file mode 100644
index 00000000000..ccf51723418
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs
@@ -0,0 +1,59 @@
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: parenthesized-generic-args-without-fn-trait
+//
+// This diagnostic is shown when a `Fn`-trait-style generic parameters (`Trait(A, B) -> C`)
+// was used on non-`Fn` trait/type.
+pub(crate) fn parenthesized_generic_args_without_fn_trait(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::ParenthesizedGenericArgsWithoutFnTrait,
+) -> Diagnostic {
+    Diagnostic::new_with_syntax_node_ptr(
+        ctx,
+        DiagnosticCode::RustcHardError("E0214"),
+        "parenthesized type parameters may only be used with a `Fn` trait",
+        d.args.map(Into::into),
+    )
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::check_diagnostics;
+
+    #[test]
+    fn fn_traits_work() {
+        check_diagnostics(
+            r#"
+//- minicore: async_fn, fn
+fn foo<
+    A: Fn(),
+    B: FnMut() -> i32,
+    C: FnOnce(&str, bool),
+    D: AsyncFn::(u32) -> u32,
+    E: AsyncFnMut(),
+    F: AsyncFnOnce() -> bool,
+>() {}
+        "#,
+        );
+    }
+
+    #[test]
+    fn non_fn_trait() {
+        check_diagnostics(
+            r#"
+struct Struct<T>(T);
+enum Enum<T> { EnumVariant(T) }
+type TypeAlias<T> = bool;
+
+type Foo = TypeAlias() -> bool;
+                 // ^^ error: parenthesized type parameters may only be used with a `Fn` trait
+
+fn foo(_a: Struct(i32)) {
+              // ^^^^^ error: parenthesized type parameters may only be used with a `Fn` trait
+    let _ = <Enum::(u32)>::EnumVariant(0);
+              // ^^^^^^^ error: parenthesized type parameters may only be used with a `Fn` trait
+}
+        "#,
+        );
+    }
+}
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 50c91a69602..3ea41aa7e85 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
@@ -43,6 +43,7 @@ mod handlers {
     pub(crate) mod mutability_errors;
     pub(crate) mod no_such_field;
     pub(crate) mod non_exhaustive_let;
+    pub(crate) mod parenthesized_generic_args_without_fn_trait;
     pub(crate) mod private_assoc_item;
     pub(crate) mod private_field;
     pub(crate) mod remove_trailing_return;
@@ -466,7 +467,12 @@ pub fn semantic_diagnostics(
                 Some(it) => it,
                 None => continue,
             },
-            AnyDiagnostic::GenericArgsProhibited(d) => handlers::generic_args_prohibited::generic_args_prohibited(&ctx, &d)
+            AnyDiagnostic::GenericArgsProhibited(d) => {
+                handlers::generic_args_prohibited::generic_args_prohibited(&ctx, &d)
+            }
+            AnyDiagnostic::ParenthesizedGenericArgsWithoutFnTrait(d) => {
+                handlers::parenthesized_generic_args_without_fn_trait::parenthesized_generic_args_without_fn_trait(&ctx, &d)
+            }
         };
         res.push(d)
     }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
index 9be7c92fc79..9477d0d1b87 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
@@ -48,17 +48,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <pre><code><span class="keyword">use</span> <span class="module">inner</span><span class="operator">::</span><span class="brace">{</span><span class="self_keyword">self</span> <span class="keyword">as</span> <span class="module declaration">inner_mod</span><span class="brace">}</span><span class="semicolon">;</span>
 <span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="brace">{</span><span class="brace">}</span>
 
-<span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration public">ops</span> <span class="brace">{</span>
-    <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn_once"</span><span class="attribute_bracket attribute">]</span>
-    <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration public">FnOnce</span><span class="angle">&lt;</span><span class="type_param declaration">Args</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
-
-    <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn_mut"</span><span class="attribute_bracket attribute">]</span>
-    <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration public">FnMut</span><span class="angle">&lt;</span><span class="type_param declaration">Args</span><span class="angle">&gt;</span><span class="colon">:</span> <span class="trait public">FnOnce</span><span class="angle">&lt;</span><span class="type_param">Args</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
-
-    <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn"</span><span class="attribute_bracket attribute">]</span>
-    <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration public">Fn</span><span class="angle">&lt;</span><span class="type_param declaration">Args</span><span class="angle">&gt;</span><span class="colon">:</span> <span class="trait public">FnMut</span><span class="angle">&lt;</span><span class="type_param">Args</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
-<span class="brace">}</span>
-
 <span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="brace">{</span>
     <span class="field declaration">x</span><span class="colon">:</span> <span class="builtin_type">u32</span><span class="comma">,</span>
 <span class="brace">}</span>
@@ -125,8 +114,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="const_param const">FOO</span>
 <span class="brace">}</span>
 
-<span class="keyword">use</span> <span class="module public">ops</span><span class="operator">::</span><span class="trait public">Fn</span><span class="semicolon">;</span>
-<span class="keyword">fn</span> <span class="function declaration">baz</span><span class="angle">&lt;</span><span class="type_param declaration">F</span><span class="colon">:</span> <span class="trait public">Fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="value_param callable declaration">f</span><span class="colon">:</span> <span class="type_param">F</span><span class="parenthesis">)</span> <span class="brace">{</span>
+<span class="keyword">use</span> <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">ops</span><span class="operator">::</span><span class="trait default_library library">Fn</span><span class="semicolon">;</span>
+<span class="keyword">fn</span> <span class="function declaration">baz</span><span class="angle">&lt;</span><span class="type_param declaration">F</span><span class="colon">:</span> <span class="trait default_library library">Fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="value_param callable declaration">f</span><span class="colon">:</span> <span class="type_param">F</span><span class="parenthesis">)</span> <span class="brace">{</span>
     <span class="value_param callable">f</span><span class="parenthesis">(</span><span class="parenthesis">)</span>
 <span class="brace">}</span>
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
index c6e83d0d5ea..3775265f234 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
@@ -136,22 +136,11 @@ use self::foo as bar;
 fn test_highlighting() {
     check_highlighting(
         r#"
-//- minicore: derive, copy
+//- minicore: derive, copy, fn
 //- /main.rs crate:main deps:foo
 use inner::{self as inner_mod};
 mod inner {}
 
-pub mod ops {
-    #[lang = "fn_once"]
-    pub trait FnOnce<Args> {}
-
-    #[lang = "fn_mut"]
-    pub trait FnMut<Args>: FnOnce<Args> {}
-
-    #[lang = "fn"]
-    pub trait Fn<Args>: FnMut<Args> {}
-}
-
 struct Foo {
     x: u32,
 }
@@ -218,7 +207,7 @@ fn const_param<const FOO: usize>() -> usize {
     FOO
 }
 
-use ops::Fn;
+use core::ops::Fn;
 fn baz<F: Fn() -> ()>(f: F) {
     f()
 }
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 66553a2661a..be0de6c9366 100644
--- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
+++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
@@ -13,15 +13,35 @@ use crate::{
 
 macro_rules! define_symbols {
     (@WITH_NAME: $($alias:ident = $value:literal,)* @PLAIN: $($name:ident,)*) => {
-        // Ideally we would be emitting `const` here, but then we no longer have stable addresses
-        // which is what we are relying on for equality! In the future if consts can refer to
-        // statics we should swap these for `const`s and have the string literal being pointed
-        // to be statics to refer to such that their address is stable.
+        // We define symbols as both `const`s and `static`s because some const code requires const symbols,
+        // but code from before the transition relies on the lifetime of the predefined symbols and making them
+        // `const`s make it error (because now they're temporaries). In the future we probably should only
+        // use consts.
+
+        /// Predefined symbols as `const`s (instead of the default `static`s).
+        pub mod consts {
+            use super::{Symbol, TaggedArcPtr};
+
+            // The strings should be in `static`s so that symbol equality holds.
+            $(
+                pub const $name: Symbol = {
+                    static SYMBOL_STR: &str = stringify!($name);
+                    Symbol { repr: TaggedArcPtr::non_arc(&SYMBOL_STR) }
+                };
+            )*
+            $(
+                pub const $alias: Symbol = {
+                    static SYMBOL_STR: &str = $value;
+                    Symbol { repr: TaggedArcPtr::non_arc(&SYMBOL_STR) }
+                };
+            )*
+        }
+
         $(
-            pub static $name: Symbol = Symbol { repr: TaggedArcPtr::non_arc(&stringify!($name)) };
+            pub static $name: Symbol = consts::$name;
         )*
         $(
-            pub static $alias: Symbol = Symbol { repr: TaggedArcPtr::non_arc(&$value) };
+            pub static $alias: Symbol = consts::$alias;
         )*
 
 
@@ -428,6 +448,7 @@ define_symbols! {
     rustc_layout_scalar_valid_range_start,
     rustc_legacy_const_generics,
     rustc_macro_transparency,
+    rustc_paren_sugar,
     rustc_reallocator,
     rustc_reservation_impl,
     rustc_safe_intrinsic,
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 4ed68d18e80..202afebde70 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
@@ -647,18 +647,21 @@ pub mod ops {
 
         #[lang = "fn"]
         #[fundamental]
+        #[rustc_paren_sugar]
         pub trait Fn<Args: Tuple>: FnMut<Args> {
             extern "rust-call" fn call(&self, args: Args) -> Self::Output;
         }
 
         #[lang = "fn_mut"]
         #[fundamental]
+        #[rustc_paren_sugar]
         pub trait FnMut<Args: Tuple>: FnOnce<Args> {
             extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
         }
 
         #[lang = "fn_once"]
         #[fundamental]
+        #[rustc_paren_sugar]
         pub trait FnOnce<Args: Tuple> {
             #[lang = "fn_once_output"]
             type Output;
@@ -736,12 +739,14 @@ pub mod ops {
 
         #[lang = "async_fn"]
         #[fundamental]
+        #[rustc_paren_sugar]
         pub trait AsyncFn<Args: Tuple>: AsyncFnMut<Args> {
             extern "rust-call" fn async_call(&self, args: Args) -> Self::CallRefFuture<'_>;
         }
 
         #[lang = "async_fn_mut"]
         #[fundamental]
+        #[rustc_paren_sugar]
         pub trait AsyncFnMut<Args: Tuple>: AsyncFnOnce<Args> {
             #[lang = "call_ref_future"]
             type CallRefFuture<'a>: Future<Output = Self::Output>
@@ -752,6 +757,7 @@ pub mod ops {
 
         #[lang = "async_fn_once"]
         #[fundamental]
+        #[rustc_paren_sugar]
         pub trait AsyncFnOnce<Args: Tuple> {
             #[lang = "async_fn_once_output"]
             type Output;