about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2025-04-22 12:06:28 +0000
committerGitHub <noreply@github.com>2025-04-22 12:06:28 +0000
commitc4e8514de1fc4f05a828b6ff337c27a19f634247 (patch)
tree36332157717719d81c0be2a03dff9398f383adc5
parent5db7548ad6a8dd866b5de7a191d90311ed72fa63 (diff)
parentf16cefb606fb8b99c62148712ae6caec1014a076 (diff)
downloadrust-c4e8514de1fc4f05a828b6ff337c27a19f634247.tar.gz
rust-c4e8514de1fc4f05a828b6ff337c27a19f634247.zip
Merge pull request #19479 from ChayimFriedman2/generic-mismatch
feat: Add two new diagnostics: one for mismatch in generic arguments count, and another for mismatch in their kind
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs32
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs1
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/builder.rs39
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/db.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/display.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/generics.rs18
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer.rs33
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs206
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lib.rs32
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower.rs172
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower/diagnostics.rs43
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs578
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/diagnostics.rs147
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs38
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs24
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs172
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs80
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs4
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/minicore.rs2
23 files changed, 1243 insertions, 405 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs
index aa26e8b3df2..3141fceeb0f 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs
@@ -35,7 +35,9 @@ use crate::{
 };
 
 pub use self::body::{Body, BodySourceMap};
-pub use self::lower::hir_segment_to_ast_segment;
+pub use self::lower::{
+    hir_assoc_type_binding_to_ast, hir_generic_arg_to_ast, hir_segment_to_ast_segment,
+};
 
 /// A wrapper around [`span::SyntaxContextId`] that is intended only for comparisons.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
index e7f2247c5bc..d49c283bee6 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
@@ -788,6 +788,7 @@ impl ExprCollector<'_> {
         node: ast::GenericArgList,
         impl_trait_lower_fn: &mut impl FnMut(ThinVec<TypeBound>) -> TypeRef,
     ) -> Option<GenericArgs> {
+        // This needs to be kept in sync with `hir_generic_arg_to_ast()`.
         let mut args = Vec::new();
         let mut bindings = Vec::new();
         for generic_arg in node.generic_args() {
@@ -797,6 +798,7 @@ impl ExprCollector<'_> {
                     args.push(GenericArg::Type(type_ref));
                 }
                 ast::GenericArg::AssocTypeArg(assoc_type_arg) => {
+                    // This needs to be kept in sync with `hir_assoc_type_binding_to_ast()`.
                     if assoc_type_arg.param_list().is_some() {
                         // We currently ignore associated return type bounds.
                         continue;
@@ -3228,3 +3230,33 @@ enum ArgumentType {
     Format(FormatTrait),
     Usize,
 }
+
+/// This function find the AST fragment that corresponds to an `AssociatedTypeBinding` in the HIR.
+pub fn hir_assoc_type_binding_to_ast(
+    segment_args: &ast::GenericArgList,
+    binding_idx: u32,
+) -> Option<ast::AssocTypeArg> {
+    segment_args
+        .generic_args()
+        .filter_map(|arg| match arg {
+            ast::GenericArg::AssocTypeArg(it) => Some(it),
+            _ => None,
+        })
+        .filter(|binding| binding.param_list().is_none() && binding.name_ref().is_some())
+        .nth(binding_idx as usize)
+}
+
+/// This function find the AST generic argument from the one in the HIR. Does not support the `Self` argument.
+pub fn hir_generic_arg_to_ast(
+    args: &ast::GenericArgList,
+    arg_idx: u32,
+    has_self_arg: bool,
+) -> Option<ast::GenericArg> {
+    args.generic_args()
+        .filter(|arg| match arg {
+            ast::GenericArg::AssocTypeArg(_) => false,
+            ast::GenericArg::LifetimeArg(arg) => arg.lifetime().is_some(),
+            ast::GenericArg::ConstArg(_) | ast::GenericArg::TypeArg(_) => true,
+        })
+        .nth(arg_idx as usize - has_self_arg as usize)
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path.rs
index 36b3d11d75f..14b0d3abd41 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path.rs
@@ -152,10 +152,8 @@ pub(super) fn lower_path(
                                 args: iter::once(self_type)
                                     .chain(it.args.iter().cloned())
                                     .collect(),
-
                                 has_self_type: true,
-                                bindings: it.bindings.clone(),
-                                parenthesized: it.parenthesized,
+                                ..it
                             },
                             None => GenericArgs {
                                 args: Box::new([self_type]),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs
index 890e7874a84..52c0c669ea4 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs
@@ -138,6 +138,7 @@ impl GenericParamData {
 
 impl_from!(TypeParamData, ConstParamData, LifetimeParamData for GenericParamData);
 
+#[derive(Debug, Clone, Copy)]
 pub enum GenericParamDataRef<'a> {
     TypeParamData(&'a TypeParamData),
     ConstParamData(&'a ConstParamData),
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs
index 8c3665dfc83..77d15a73af6 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs
@@ -306,29 +306,28 @@ impl TyBuilder<hir_def::AdtId> {
         // Note that we're building ADT, so we never have parent generic parameters.
         let defaults = db.generic_defaults(self.data.into());
 
-        for default_ty in &defaults[self.vec.len()..] {
-            // NOTE(skip_binders): we only check if the arg type is error type.
-            if let Some(x) = default_ty.skip_binders().ty(Interner) {
-                if x.is_unknown() {
-                    self.vec.push(fallback().cast(Interner));
-                    continue;
+        if let Some(defaults) = defaults.get(self.vec.len()..) {
+            for default_ty in defaults {
+                // NOTE(skip_binders): we only check if the arg type is error type.
+                if let Some(x) = default_ty.skip_binders().ty(Interner) {
+                    if x.is_unknown() {
+                        self.vec.push(fallback().cast(Interner));
+                        continue;
+                    }
                 }
+                // Each default can only depend on the previous parameters.
+                self.vec.push(default_ty.clone().substitute(Interner, &*self.vec).cast(Interner));
             }
-            // Each default can only depend on the previous parameters.
-            let subst_so_far = Substitution::from_iter(
-                Interner,
-                self.vec
-                    .iter()
-                    .cloned()
-                    .chain(self.param_kinds[self.vec.len()..].iter().map(|it| match it {
-                        ParamKind::Type => TyKind::Error.intern(Interner).cast(Interner),
-                        ParamKind::Lifetime => error_lifetime().cast(Interner),
-                        ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
-                    }))
-                    .take(self.param_kinds.len()),
-            );
-            self.vec.push(default_ty.clone().substitute(Interner, &subst_so_far).cast(Interner));
         }
+
+        // The defaults may be missing if no param has default, so fill that.
+        let filler = self.param_kinds[self.vec.len()..].iter().map(|x| match x {
+            ParamKind::Type => fallback().cast(Interner),
+            ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
+            ParamKind::Lifetime => error_lifetime().cast(Interner),
+        });
+        self.vec.extend(filler.casted(Interner));
+
         self
     }
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
index 563d3c71412..75a680b5885 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
@@ -200,6 +200,9 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
         def: GenericDefId,
     ) -> (GenericDefaults, Diagnostics);
 
+    /// This returns an empty list if no parameter has default.
+    ///
+    /// The binders of the returned defaults are only up to (not including) this parameter.
     #[salsa::invoke(crate::lower::generic_defaults_query)]
     #[salsa::transparent]
     fn generic_defaults(&self, def: GenericDefId) -> GenericDefaults;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
index a79b8cdf8ee..39d6083b35c 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
@@ -1640,7 +1640,7 @@ fn generic_args_sans_defaults<'ga>(
                         Some(default_parameter) => {
                             // !is_err(default_parameter.skip_binders())
                             // &&
-                            arg != &default_parameter.clone().substitute(Interner, &parameters)
+                            arg != &default_parameter.clone().substitute(Interner, &parameters[..i])
                         }
                     }
                 };
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs
index 8379636d5c6..bb4aaf78895 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs
@@ -21,7 +21,6 @@ use hir_def::{
     },
 };
 use itertools::chain;
-use stdx::TupleExt;
 use triomphe::Arc;
 
 use crate::{Interner, Substitution, db::HirDatabase, lt_to_placeholder_idx, to_placeholder_idx};
@@ -76,10 +75,13 @@ impl Generics {
         self.iter_parent().map(|(id, _)| id)
     }
 
-    pub(crate) fn iter_self_type_or_consts_id(
+    pub(crate) fn iter_self_type_or_consts(
         &self,
-    ) -> impl DoubleEndedIterator<Item = GenericParamId> + '_ {
-        self.params.iter_type_or_consts().map(from_toc_id(self)).map(TupleExt::head)
+    ) -> impl DoubleEndedIterator<Item = (LocalTypeOrConstParamId, &TypeOrConstParamData)> + '_
+    {
+        let mut toc = self.params.iter_type_or_consts();
+        let trait_self_param = self.has_trait_self_param.then(|| toc.next()).flatten();
+        chain!(trait_self_param, toc)
     }
 
     /// Iterate over the parent params followed by self params.
@@ -107,7 +109,7 @@ impl Generics {
     }
 
     /// Iterator over types and const params of parent.
-    fn iter_parent(
+    pub(crate) fn iter_parent(
         &self,
     ) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'_>)> + '_ {
         self.parent_generics().into_iter().flat_map(|it| {
@@ -129,6 +131,10 @@ impl Generics {
         self.params.len()
     }
 
+    pub(crate) fn len_lifetimes_self(&self) -> usize {
+        self.params.len_lifetimes()
+    }
+
     /// (parent total, self param, type params, const params, impl trait list, lifetimes)
     pub(crate) fn provenance_split(&self) -> (usize, bool, usize, usize, usize, usize) {
         let mut self_param = false;
@@ -144,7 +150,7 @@ impl Generics {
             TypeOrConstParamData::ConstParamData(_) => const_params += 1,
         });
 
-        let lifetime_params = self.params.iter_lt().count();
+        let lifetime_params = self.params.len_lifetimes();
 
         let parent_len = self.parent_generics().map_or(0, Generics::len);
         (parent_len, self_param, type_params, const_params, impl_trait_params, lifetime_params)
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 24b1909b6d1..cad2e3ce993 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -34,8 +34,8 @@ use chalk_ir::{
 };
 use either::Either;
 use hir_def::{
-    AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, GenericDefId, ImplId, ItemContainerId,
-    Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId,
+    AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, GenericDefId, GenericParamId, ImplId,
+    ItemContainerId, Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId,
     builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
     expr_store::{Body, ExpressionStore, HygieneId, path::Path},
     hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId},
@@ -55,8 +55,9 @@ use triomphe::Arc;
 
 use crate::{
     AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId,
-    ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ParamLoweringMode,
-    PathLoweringDiagnostic, ProjectionTy, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
+    ImplTraitIdx, InEnvironment, IncorrectGenericsLenKind, Interner, Lifetime, OpaqueTyId,
+    ParamLoweringMode, PathLoweringDiagnostic, ProjectionTy, Substitution, TraitEnvironment, Ty,
+    TyBuilder, TyExt,
     db::HirDatabase,
     fold_tys,
     generics::Generics,
@@ -66,7 +67,7 @@ use crate::{
         expr::ExprIsRead,
         unify::InferenceTable,
     },
-    lower::{ImplTraitLoweringMode, diagnostics::TyLoweringDiagnostic},
+    lower::{GenericArgsPosition, ImplTraitLoweringMode, diagnostics::TyLoweringDiagnostic},
     mir::MirSpan,
     to_assoc_type_id,
     traits::FnTrait,
@@ -275,6 +276,20 @@ pub enum InferenceDiagnostic {
         node: ExprOrPatId,
         diag: PathLoweringDiagnostic,
     },
+    MethodCallIncorrectGenericsLen {
+        expr: ExprId,
+        provided_count: u32,
+        expected_count: u32,
+        kind: IncorrectGenericsLenKind,
+        def: GenericDefId,
+    },
+    MethodCallIncorrectGenericsOrder {
+        expr: ExprId,
+        param_id: GenericParamId,
+        arg_idx: u32,
+        /// Whether the `GenericArgs` contains a `Self` arg.
+        has_self_arg: bool,
+    },
 }
 
 /// A mismatch between an expected and an inferred type.
@@ -909,6 +924,7 @@ impl<'a> InferenceContext<'a> {
         let mut param_tys =
             self.with_ty_lowering(&data.store, InferenceTyDiagnosticSource::Signature, |ctx| {
                 ctx.type_param_mode(ParamLoweringMode::Placeholder);
+                ctx.in_fn_signature = true;
                 data.params.iter().map(|&type_ref| ctx.lower_ty(type_ref)).collect::<Vec<_>>()
             });
 
@@ -953,8 +969,9 @@ impl<'a> InferenceContext<'a> {
                     InferenceTyDiagnosticSource::Signature,
                     |ctx| {
                         ctx.type_param_mode(ParamLoweringMode::Placeholder)
-                            .impl_trait_mode(ImplTraitLoweringMode::Opaque)
-                            .lower_ty(return_ty)
+                            .impl_trait_mode(ImplTraitLoweringMode::Opaque);
+                        ctx.in_fn_signature = true;
+                        ctx.lower_ty(return_ty)
                     },
                 );
                 let return_ty = self.insert_type_vars(return_ty);
@@ -1513,7 +1530,7 @@ impl<'a> InferenceContext<'a> {
             InferenceTyDiagnosticSource::Body,
             self.generic_def,
         );
-        let mut path_ctx = ctx.at_path(path, node);
+        let mut path_ctx = ctx.at_path(path, node, GenericArgsPosition::Value);
         let (resolution, unresolved) = if value_ns {
             let Some(res) = path_ctx.resolve_path_in_value_ns(HygieneId::ROOT) else {
                 return (self.err_ty(), None);
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 f613e2f69f7..2c633a03c54 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
@@ -12,6 +12,7 @@ use hir_def::expr_store::path::Path;
 use hir_def::{hir::ExprOrPatId, resolver::Resolver};
 use la_arena::{Idx, RawIdx};
 
+use crate::lower::GenericArgsPosition;
 use crate::{
     InferenceDiagnostic, InferenceTyDiagnosticSource, TyLoweringContext, TyLoweringDiagnostic,
     db::HirDatabase,
@@ -74,6 +75,7 @@ impl<'a> InferenceTyLoweringContext<'a> {
         &'b mut self,
         path: &'b Path,
         node: ExprOrPatId,
+        position: GenericArgsPosition,
     ) -> PathLoweringContext<'b, 'a> {
         let on_diagnostic = PathDiagnosticCallback {
             data: Either::Right(PathDiagnosticCallbackData { diagnostics: self.diagnostics, node }),
@@ -83,13 +85,14 @@ impl<'a> InferenceTyLoweringContext<'a> {
                     .push(InferenceDiagnostic::PathDiagnostic { node: data.node, diag });
             },
         };
-        PathLoweringContext::new(&mut self.ctx, on_diagnostic, path)
+        PathLoweringContext::new(&mut self.ctx, on_diagnostic, path, position)
     }
 
     #[inline]
     pub(super) fn at_path_forget_diagnostics<'b>(
         &'b mut self,
         path: &'b Path,
+        position: GenericArgsPosition,
     ) -> PathLoweringContext<'b, 'a> {
         let on_diagnostic = PathDiagnosticCallback {
             data: Either::Right(PathDiagnosticCallbackData {
@@ -98,7 +101,7 @@ impl<'a> InferenceTyLoweringContext<'a> {
             }),
             callback: |_data, _, _diag| {},
         };
-        PathLoweringContext::new(&mut self.ctx, on_diagnostic, path)
+        PathLoweringContext::new(&mut self.ctx, on_diagnostic, path, position)
     }
 
     #[inline]
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 17cd322a22e..2980549c236 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
@@ -12,7 +12,7 @@ use hir_def::{
     expr_store::path::{GenericArg, GenericArgs, Path},
     hir::{
         ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, Expr, ExprId, ExprOrPatId, LabelId,
-        Literal, Pat, PatId, Statement, UnaryOp,
+        Literal, Pat, PatId, Statement, UnaryOp, generics::GenericParamDataRef,
     },
     lang_item::{LangItem, LangItemTarget},
     resolver::ValueNs,
@@ -24,11 +24,11 @@ use syntax::ast::RangeOp;
 
 use crate::{
     Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, DeclContext,
-    DeclOrigin, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder,
-    TyExt, TyKind,
+    DeclOrigin, IncorrectGenericsLenKind, Interner, Rawness, Scalar, Substitution,
+    TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
     autoderef::{Autoderef, builtin_deref, deref_by_trait},
-    consteval, error_lifetime,
-    generics::{Generics, generics},
+    consteval,
+    generics::generics,
     infer::{
         BreakableKind,
         coerce::{CoerceMany, CoerceNever, CoercionCause},
@@ -36,7 +36,10 @@ use crate::{
         pat::contains_explicit_ref_binding,
     },
     lang_items::lang_items_for_bin_op,
-    lower::{ParamLoweringMode, generic_arg_to_chalk, lower_to_chalk_mutability},
+    lower::{
+        GenericArgsPosition, ParamLoweringMode, lower_to_chalk_mutability,
+        path::{GenericArgsLowerer, TypeLikeConst, substs_from_args_and_bindings},
+    },
     mapping::{ToChalk, from_chalk},
     method_resolution::{self, VisibleFromModule},
     primitive::{self, UintTy},
@@ -1658,8 +1661,7 @@ impl InferenceContext<'_> {
                 match resolved {
                     Some((adjust, func, _)) => {
                         let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty);
-                        let generics = generics(self.db, func.into());
-                        let substs = self.substs_for_method_call(generics, None);
+                        let substs = self.substs_for_method_call(tgt_expr, func.into(), None);
                         self.write_expr_adj(receiver, adjustments);
                         self.write_method_resolution(tgt_expr, func, substs.clone());
 
@@ -1809,8 +1811,7 @@ impl InferenceContext<'_> {
                 let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty);
                 self.write_expr_adj(receiver, adjustments);
 
-                let generics = generics(self.db, func.into());
-                let substs = self.substs_for_method_call(generics, generic_args);
+                let substs = self.substs_for_method_call(tgt_expr, func.into(), generic_args);
                 self.write_method_resolution(tgt_expr, func, substs.clone());
                 self.check_method_call(
                     tgt_expr,
@@ -1860,8 +1861,7 @@ impl InferenceContext<'_> {
 
                 let recovered = match assoc_func_with_same_name {
                     Some(f) => {
-                        let generics = generics(self.db, f.into());
-                        let substs = self.substs_for_method_call(generics, generic_args);
+                        let substs = self.substs_for_method_call(tgt_expr, f.into(), generic_args);
                         let f = self
                             .db
                             .value_ty(f.into())
@@ -2051,83 +2051,129 @@ impl InferenceContext<'_> {
 
     fn substs_for_method_call(
         &mut self,
-        def_generics: Generics,
+        expr: ExprId,
+        def: GenericDefId,
         generic_args: Option<&GenericArgs>,
     ) -> Substitution {
-        let (
-            parent_params,
-            has_self_param,
-            type_params,
-            const_params,
-            impl_trait_params,
-            lifetime_params,
-        ) = def_generics.provenance_split();
-        assert!(!has_self_param); // method shouldn't have another Self param
-        let total_len =
-            parent_params + type_params + const_params + impl_trait_params + lifetime_params;
-
-        let param_to_var = |id| match id {
-            GenericParamId::TypeParamId(_) => self.table.new_type_var().cast(Interner),
-            GenericParamId::ConstParamId(id) => {
-                self.table.new_const_var(self.db.const_param_ty(id)).cast(Interner)
-            }
-            GenericParamId::LifetimeParamId(_) => self.table.new_lifetime_var().cast(Interner),
-        };
+        struct LowererCtx<'a, 'b> {
+            ctx: &'a mut InferenceContext<'b>,
+            expr: ExprId,
+        }
 
-        let mut substs: Vec<_> = def_generics.iter_parent_id().map(param_to_var).collect();
-
-        // handle provided arguments
-        if let Some(generic_args) = generic_args {
-            // if args are provided, it should be all of them, but we can't rely on that
-            let self_params = type_params + const_params + lifetime_params;
-
-            let mut args = generic_args.args.iter().peekable();
-            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 inferred.
-                    // Once we have implemented lifetime inference correctly,
-                    // this should be handled in a proper way.
-                    (
-                        GenericParamId::LifetimeParamId(_),
-                        None | Some(GenericArg::Type(_) | GenericArg::Const(_)),
-                    ) => error_lifetime().cast(Interner),
-
-                    // If we run out of `generic_args`, stop pushing substs
-                    (_, None) => break,
-
-                    // Normal cases
-                    (_, Some(_)) => generic_arg_to_chalk(
-                        self.db,
-                        kind_id,
-                        args.next().unwrap(), // `peek()` is `Some(_)`, so guaranteed no panic
-                        self,
-                        &self.body.store,
-                        |this, type_ref| this.make_body_ty(type_ref),
-                        |this, c, ty| this.make_body_const(*c, ty),
-                        |this, path, ty| this.make_path_as_body_const(path, ty),
-                        |this, lt_ref| this.make_body_lifetime(lt_ref),
-                    ),
-                };
+        impl GenericArgsLowerer for LowererCtx<'_, '_> {
+            fn report_len_mismatch(
+                &mut self,
+                def: GenericDefId,
+                provided_count: u32,
+                expected_count: u32,
+                kind: IncorrectGenericsLenKind,
+            ) {
+                self.ctx.push_diagnostic(InferenceDiagnostic::MethodCallIncorrectGenericsLen {
+                    expr: self.expr,
+                    provided_count,
+                    expected_count,
+                    kind,
+                    def,
+                });
+            }
 
-                substs.push(arg);
+            fn report_arg_mismatch(
+                &mut self,
+                param_id: GenericParamId,
+                arg_idx: u32,
+                has_self_arg: bool,
+            ) {
+                self.ctx.push_diagnostic(InferenceDiagnostic::MethodCallIncorrectGenericsOrder {
+                    expr: self.expr,
+                    param_id,
+                    arg_idx,
+                    has_self_arg,
+                });
             }
-        };
 
-        let mut param_to_var = |id| match id {
-            GenericParamId::TypeParamId(_) => self.table.new_type_var().cast(Interner),
-            GenericParamId::ConstParamId(id) => {
-                self.table.new_const_var(self.db.const_param_ty(id)).cast(Interner)
+            fn provided_kind(
+                &mut self,
+                param_id: GenericParamId,
+                param: GenericParamDataRef<'_>,
+                arg: &GenericArg,
+            ) -> crate::GenericArg {
+                match (param, arg) {
+                    (GenericParamDataRef::LifetimeParamData(_), GenericArg::Lifetime(lifetime)) => {
+                        self.ctx.make_body_lifetime(lifetime).cast(Interner)
+                    }
+                    (GenericParamDataRef::TypeParamData(_), GenericArg::Type(type_ref)) => {
+                        self.ctx.make_body_ty(*type_ref).cast(Interner)
+                    }
+                    (GenericParamDataRef::ConstParamData(_), GenericArg::Const(konst)) => {
+                        let GenericParamId::ConstParamId(const_id) = param_id else {
+                            unreachable!("non-const param ID for const param");
+                        };
+                        let const_ty = self.ctx.db.const_param_ty(const_id);
+                        self.ctx.make_body_const(*konst, const_ty).cast(Interner)
+                    }
+                    _ => unreachable!("unmatching param kinds were passed to `provided_kind()`"),
+                }
+            }
+
+            fn provided_type_like_const(
+                &mut self,
+                const_ty: Ty,
+                arg: TypeLikeConst<'_>,
+            ) -> crate::Const {
+                match arg {
+                    TypeLikeConst::Path(path) => self.ctx.make_path_as_body_const(path, const_ty),
+                    TypeLikeConst::Infer => self.ctx.table.new_const_var(const_ty),
+                }
             }
-            GenericParamId::LifetimeParamId(_) => self.table.new_lifetime_var().cast(Interner),
-        };
 
-        // Handle everything else as unknown.
-        for (id, _data) in def_generics.iter().skip(substs.len()) {
-            substs.push(param_to_var(id));
+            fn inferred_kind(
+                &mut self,
+                _def: GenericDefId,
+                param_id: GenericParamId,
+                _param: GenericParamDataRef<'_>,
+                _infer_args: bool,
+                _preceding_args: &[crate::GenericArg],
+            ) -> crate::GenericArg {
+                // Always create an inference var, even when `infer_args == false`. This helps with diagnostics,
+                // and I think it's also required in the presence of `impl Trait` (that must be inferred).
+                match param_id {
+                    GenericParamId::TypeParamId(_) => self.ctx.table.new_type_var().cast(Interner),
+                    GenericParamId::ConstParamId(const_id) => self
+                        .ctx
+                        .table
+                        .new_const_var(self.ctx.db.const_param_ty(const_id))
+                        .cast(Interner),
+                    GenericParamId::LifetimeParamId(_) => {
+                        self.ctx.table.new_lifetime_var().cast(Interner)
+                    }
+                }
+            }
+
+            fn parent_arg(&mut self, param_id: GenericParamId) -> crate::GenericArg {
+                match param_id {
+                    GenericParamId::TypeParamId(_) => self.ctx.table.new_type_var().cast(Interner),
+                    GenericParamId::ConstParamId(const_id) => self
+                        .ctx
+                        .table
+                        .new_const_var(self.ctx.db.const_param_ty(const_id))
+                        .cast(Interner),
+                    GenericParamId::LifetimeParamId(_) => {
+                        self.ctx.table.new_lifetime_var().cast(Interner)
+                    }
+                }
+            }
         }
-        assert_eq!(substs.len(), total_len);
-        Substitution::from_iter(Interner, substs)
+
+        substs_from_args_and_bindings(
+            self.db,
+            self.body,
+            generic_args,
+            def,
+            true,
+            GenericArgsPosition::MethodCall,
+            None,
+            &mut LowererCtx { ctx: self, expr },
+        )
     }
 
     fn register_obligations_for_call(&mut self, callable_ty: &Ty) {
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 461cea4f143..0e1d23b6949 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
@@ -16,6 +16,7 @@ use crate::{
     consteval, error_lifetime,
     generics::generics,
     infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext,
+    lower::GenericArgsPosition,
     method_resolution::{self, VisibleFromModule},
     to_chalk_trait_id,
 };
@@ -95,7 +96,7 @@ impl InferenceContext<'_> {
         };
 
         let substs = self.with_body_ty_lowering(|ctx| {
-            let mut path_ctx = ctx.at_path(path, id);
+            let mut path_ctx = ctx.at_path(path, id, GenericArgsPosition::Value);
             let last_segment = path.segments().len().checked_sub(1);
             if let Some(last_segment) = last_segment {
                 path_ctx.set_current_segment(last_segment)
@@ -163,9 +164,9 @@ impl InferenceContext<'_> {
             self.generic_def,
         );
         let mut path_ctx = if no_diagnostics {
-            ctx.at_path_forget_diagnostics(path)
+            ctx.at_path_forget_diagnostics(path, GenericArgsPosition::Value)
         } else {
-            ctx.at_path(path, id)
+            ctx.at_path(path, id, GenericArgsPosition::Value)
         };
         let (value, self_subst) = if let Some(type_ref) = path.type_anchor() {
             let last = path.segments().last()?;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
index 4f60bb21b6a..2cb977b6340 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
@@ -347,20 +347,24 @@ pub(crate) fn make_binders<T: HasInterner<Interner = Interner>>(
     generics: &Generics,
     value: T,
 ) -> Binders<T> {
-    Binders::new(
-        VariableKinds::from_iter(
-            Interner,
-            generics.iter_id().map(|x| match x {
-                hir_def::GenericParamId::ConstParamId(id) => {
-                    chalk_ir::VariableKind::Const(db.const_param_ty(id))
-                }
-                hir_def::GenericParamId::TypeParamId(_) => {
-                    chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)
-                }
-                hir_def::GenericParamId::LifetimeParamId(_) => chalk_ir::VariableKind::Lifetime,
-            }),
-        ),
-        value,
+    Binders::new(variable_kinds_from_iter(db, generics.iter_id()), value)
+}
+
+pub(crate) fn variable_kinds_from_iter(
+    db: &dyn HirDatabase,
+    iter: impl Iterator<Item = hir_def::GenericParamId>,
+) -> VariableKinds {
+    VariableKinds::from_iter(
+        Interner,
+        iter.map(|x| match x {
+            hir_def::GenericParamId::ConstParamId(id) => {
+                chalk_ir::VariableKind::Const(db.const_param_ty(id))
+            }
+            hir_def::GenericParamId::TypeParamId(_) => {
+                chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)
+            }
+            hir_def::GenericParamId::LifetimeParamId(_) => chalk_ir::VariableKind::Lifetime,
+        }),
     )
 }
 
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 57849ff9139..ea42c592962 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
@@ -28,10 +28,7 @@ use hir_def::{
     FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, LocalFieldId, Lookup, StaticId,
     StructId, TypeAliasId, TypeOrConstParamId, UnionId, VariantId,
     builtin_type::BuiltinType,
-    expr_store::{
-        ExpressionStore,
-        path::{GenericArg, Path},
-    },
+    expr_store::{ExpressionStore, path::Path},
     hir::generics::{
         GenericParamDataRef, TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget,
     },
@@ -55,9 +52,9 @@ use triomphe::{Arc, ThinArc};
 use crate::{
     AliasTy, Binders, BoundVar, CallableSig, Const, 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, all_super_traits,
+    LifetimeOutlives, PolyFnSig, ProgramClause, QuantifiedWhereClause, QuantifiedWhereClauses,
+    Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause,
+    all_super_traits,
     consteval::{intern_const_ref, path_to_const, unknown_const, unknown_const_as_generic},
     db::HirDatabase,
     error_lifetime,
@@ -70,6 +67,7 @@ use crate::{
     mapping::{ToChalk, from_chalk_trait_id, lt_to_placeholder_idx},
     static_lifetime, to_chalk_trait_id, to_placeholder_idx,
     utils::all_super_trait_refs,
+    variable_kinds_from_iter,
 };
 
 #[derive(Debug, Default)]
@@ -89,6 +87,22 @@ impl ImplTraitLoweringState {
 
 pub(crate) struct PathDiagnosticCallbackData(TypeRefId);
 
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(crate) enum GenericArgsPosition {
+    Type,
+    /// E.g. functions.
+    Value,
+    MethodCall,
+    // FIXME: This is a temporary variant we need to work around the lack of lifetime elision.
+    // The reason for its existence is that in `check_generic_args_len()`, without this, we will
+    // not infer elide lifetimes.
+    // They indeed should not be inferred - they should be elided - but we won't elide them either,
+    // emitting an error instead. rustc elides them in late resolve, and the generics it passes
+    // to lowering already include them. We probably can't do that, but we will still need to
+    // account for them when we properly implement lifetime elision.
+    FnSignature,
+}
+
 #[derive(Debug)]
 pub struct TyLoweringContext<'a> {
     pub db: &'a dyn HirDatabase,
@@ -106,6 +120,7 @@ pub struct TyLoweringContext<'a> {
     /// Tracks types with explicit `?Sized` bounds.
     pub(crate) unsized_types: FxHashSet<Ty>,
     pub(crate) diagnostics: Vec<TyLoweringDiagnostic>,
+    pub(crate) in_fn_signature: bool,
 }
 
 impl<'a> TyLoweringContext<'a> {
@@ -129,6 +144,7 @@ impl<'a> TyLoweringContext<'a> {
             type_param_mode,
             unsized_types: FxHashSet::default(),
             diagnostics: Vec::new(),
+            in_fn_signature: false,
         }
     }
 
@@ -415,6 +431,11 @@ impl<'a> TyLoweringContext<'a> {
             self,
             Self::on_path_diagnostic_callback(path_id.type_ref()),
             &self.store[path_id],
+            if self.in_fn_signature {
+                GenericArgsPosition::FnSignature
+            } else {
+                GenericArgsPosition::Type
+            },
         )
     }
 
@@ -1172,22 +1193,30 @@ pub(crate) fn generic_defaults_with_diagnostics_query(
         .with_impl_trait_mode(ImplTraitLoweringMode::Disallowed)
         .with_type_param_mode(ParamLoweringMode::Variable);
     let mut idx = 0;
+    let mut has_any_default = false;
     let mut defaults = generic_params
-        .iter_self()
-        .map(|(id, p)| {
-            let result = handle_generic_param(&mut ctx, idx, id, p, &generic_params);
+        .iter_parents_with_store()
+        .map(|((id, p), store)| {
+            ctx.store = store;
+            let (result, has_default) = handle_generic_param(&mut ctx, idx, id, p, &generic_params);
+            has_any_default |= has_default;
             idx += 1;
             result
         })
         .collect::<Vec<_>>();
-    let diagnostics = create_diagnostics(mem::take(&mut ctx.diagnostics));
-    defaults.extend(generic_params.iter_parents_with_store().map(|((id, p), store)| {
-        ctx.store = store;
-        let result = handle_generic_param(&mut ctx, idx, id, p, &generic_params);
+    ctx.diagnostics.clear(); // Don't include diagnostics from the parent.
+    defaults.extend(generic_params.iter_self().map(|(id, p)| {
+        let (result, has_default) = handle_generic_param(&mut ctx, idx, id, p, &generic_params);
+        has_any_default |= has_default;
         idx += 1;
         result
     }));
-    let defaults = GenericDefaults(Some(Arc::from_iter(defaults)));
+    let diagnostics = create_diagnostics(mem::take(&mut ctx.diagnostics));
+    let defaults = if has_any_default {
+        GenericDefaults(Some(Arc::from_iter(defaults)))
+    } else {
+        GenericDefaults(None)
+    };
     return (defaults, diagnostics);
 
     fn handle_generic_param(
@@ -1196,16 +1225,20 @@ pub(crate) fn generic_defaults_with_diagnostics_query(
         id: GenericParamId,
         p: GenericParamDataRef<'_>,
         generic_params: &Generics,
-    ) -> Binders<crate::GenericArg> {
+    ) -> (Binders<crate::GenericArg>, bool) {
+        let binders = variable_kinds_from_iter(ctx.db, generic_params.iter_id().take(idx));
         match p {
             GenericParamDataRef::TypeParamData(p) => {
-                let ty = p.default.as_ref().map_or(TyKind::Error.intern(Interner), |ty| {
-                    // Each default can only refer to previous parameters.
-                    // Type variable default referring to parameter coming
-                    // after it is forbidden (FIXME: report diagnostic)
-                    fallback_bound_vars(ctx.lower_ty(*ty), idx, 0)
-                });
-                crate::make_binders(ctx.db, generic_params, ty.cast(Interner))
+                let ty = p.default.as_ref().map_or_else(
+                    || TyKind::Error.intern(Interner),
+                    |ty| {
+                        // Each default can only refer to previous parameters.
+                        // Type variable default referring to parameter coming
+                        // after it is forbidden (FIXME: report diagnostic)
+                        fallback_bound_vars(ctx.lower_ty(*ty), idx)
+                    },
+                );
+                (Binders::new(binders, ty.cast(Interner)), p.default.is_some())
             }
             GenericParamDataRef::ConstParamData(p) => {
                 let GenericParamId::ConstParamId(id) = id else {
@@ -1221,36 +1254,22 @@ pub(crate) fn generic_defaults_with_diagnostics_query(
                     },
                 );
                 // Each default can only refer to previous parameters, see above.
-                val = fallback_bound_vars(val, idx, 0);
-                make_binders(ctx.db, generic_params, val)
+                val = fallback_bound_vars(val, idx);
+                (Binders::new(binders, val), p.default.is_some())
             }
             GenericParamDataRef::LifetimeParamData(_) => {
-                make_binders(ctx.db, generic_params, error_lifetime().cast(Interner))
+                (Binders::new(binders, error_lifetime().cast(Interner)), false)
             }
         }
     }
 }
 
 pub(crate) fn generic_defaults_with_diagnostics_recover(
-    db: &dyn HirDatabase,
+    _db: &dyn HirDatabase,
     _cycle: &Cycle,
-    def: GenericDefId,
+    _def: GenericDefId,
 ) -> (GenericDefaults, Diagnostics) {
-    let generic_params = generics(db, def);
-    if generic_params.len() == 0 {
-        return (GenericDefaults(None), None);
-    }
-    // FIXME: this code is not covered in tests.
-    // we still need one default per parameter
-    let defaults = GenericDefaults(Some(Arc::from_iter(generic_params.iter_id().map(|id| {
-        let val = match id {
-            GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner),
-            GenericParamId::ConstParamId(id) => unknown_const_as_generic(db.const_param_ty(id)),
-            GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner),
-        };
-        crate::make_binders(db, &generic_params, val)
-    }))));
-    (defaults, None)
+    (GenericDefaults(None), None)
 }
 
 fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
@@ -1258,6 +1277,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
     let resolver = def.resolver(db);
     let mut ctx_params = TyLoweringContext::new(db, &resolver, &data.store, def.into())
         .with_type_param_mode(ParamLoweringMode::Variable);
+    ctx_params.in_fn_signature = true;
     let params = data.params.iter().map(|&tr| ctx_params.lower_ty(tr));
 
     let ret = match data.ret_type {
@@ -1265,6 +1285,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
             let mut ctx_ret = TyLoweringContext::new(db, &resolver, &data.store, def.into())
                 .with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
                 .with_type_param_mode(ParamLoweringMode::Variable);
+            ctx_ret.in_fn_signature = true;
             ctx_ret.lower_ty(ret_type)
         }
         None => TyKind::Tuple(0, Substitution::empty(Interner)).intern(Interner),
@@ -1612,73 +1633,14 @@ pub(crate) fn lower_to_chalk_mutability(m: hir_def::type_ref::Mutability) -> Mut
     }
 }
 
-/// Checks if the provided generic arg matches its expected kind, then lower them via
-/// provided closures. Use unknown if there was kind mismatch.
-///
-pub(crate) fn generic_arg_to_chalk<'a, T>(
-    db: &dyn HirDatabase,
-    kind_id: GenericParamId,
-    arg: &'a GenericArg,
-    this: &mut T,
-    store: &ExpressionStore,
-    for_type: impl FnOnce(&mut T, TypeRefId) -> Ty + 'a,
-    for_const: impl FnOnce(&mut T, &ConstRef, Ty) -> Const + 'a,
-    for_const_ty_path_fallback: impl FnOnce(&mut T, &Path, Ty) -> Const + 'a,
-    for_lifetime: impl FnOnce(&mut T, &LifetimeRef) -> Lifetime + 'a,
-) -> crate::GenericArg {
-    let kind = match kind_id {
-        GenericParamId::TypeParamId(_) => ParamKind::Type,
-        GenericParamId::ConstParamId(id) => {
-            let ty = db.const_param_ty(id);
-            ParamKind::Const(ty)
-        }
-        GenericParamId::LifetimeParamId(_) => ParamKind::Lifetime,
-    };
-    match (arg, kind) {
-        (GenericArg::Type(type_ref), ParamKind::Type) => for_type(this, *type_ref).cast(Interner),
-        (GenericArg::Const(c), ParamKind::Const(c_ty)) => for_const(this, c, c_ty).cast(Interner),
-        (GenericArg::Lifetime(lifetime_ref), ParamKind::Lifetime) => {
-            for_lifetime(this, lifetime_ref).cast(Interner)
-        }
-        (GenericArg::Const(_), ParamKind::Type) => TyKind::Error.intern(Interner).cast(Interner),
-        (GenericArg::Lifetime(_), ParamKind::Type) => TyKind::Error.intern(Interner).cast(Interner),
-        (GenericArg::Type(t), ParamKind::Const(c_ty)) => match &store[*t] {
-            TypeRef::Path(p) => for_const_ty_path_fallback(this, p, c_ty).cast(Interner),
-            _ => unknown_const_as_generic(c_ty),
-        },
-        (GenericArg::Lifetime(_), ParamKind::Const(c_ty)) => unknown_const_as_generic(c_ty),
-        (GenericArg::Type(_), ParamKind::Lifetime) => error_lifetime().cast(Interner),
-        (GenericArg::Const(_), ParamKind::Lifetime) => error_lifetime().cast(Interner),
-    }
-}
-
 /// Replaces any 'free' `BoundVar`s in `s` by `TyKind::Error` from the perspective of generic
-/// parameter whose index is `param_index`. A `BoundVar` is free when it is or (syntactically)
-/// appears after the generic parameter of `param_index`.
+/// parameter whose index is `param_index`. A `BoundVar` is free when it appears after the
+/// generic parameter of `param_index`.
 fn fallback_bound_vars<T: TypeFoldable<Interner> + HasInterner<Interner = Interner>>(
     s: T,
     param_index: usize,
-    parent_start: usize,
 ) -> T {
-    // Keep in mind that parent generic parameters, if any, come *after* those of the item in
-    // question. In the diagrams below, `c*` and `p*` represent generic parameters of the item and
-    // its parent respectively.
-    let is_allowed = |index| {
-        if param_index < parent_start {
-            // The parameter of `param_index` is one from the item in question. Any parent generic
-            // parameters or the item's generic parameters that come before `param_index` is
-            // allowed.
-            // [c1, .., cj, .., ck, p1, .., pl] where cj is `param_index`
-            //  ^^^^^^              ^^^^^^^^^^ these are allowed
-            !(param_index..parent_start).contains(&index)
-        } else {
-            // The parameter of `param_index` is one from the parent generics. Only parent generic
-            // parameters that come before `param_index` are allowed.
-            // [c1, .., ck, p1, .., pj, .., pl] where pj is `param_index`
-            //              ^^^^^^ these are allowed
-            (parent_start..param_index).contains(&index)
-        }
-    };
+    let is_allowed = |index| (0..param_index).contains(&index);
 
     crate::fold_free_vars(
         s,
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 5f299aca3ae..a5a13d64e0d 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
@@ -1,6 +1,7 @@
 //! This files contains the declaration of diagnostics kinds for ty and path lowering.
 
 use hir_def::type_ref::TypeRefId;
+use hir_def::{GenericDefId, GenericParamId};
 
 #[derive(Debug, PartialEq, Eq, Clone)]
 pub struct TyLoweringDiagnostic {
@@ -21,13 +22,51 @@ pub enum GenericArgsProhibitedReason {
     PrimitiveTy,
     Const,
     Static,
+    LocalVariable,
     /// When there is a generic enum, within the expression `Enum::Variant`,
     /// either `Enum` or `Variant` are allowed to have generic arguments, but not both.
     EnumVariant,
 }
 
+/// A path can have many generic arguments: each segment may have one associated with the
+/// segment, and in addition, each associated type binding may have generic arguments. This
+/// enum abstracts over both.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum PathGenericsSource {
+    /// Generic arguments directly on the segment.
+    Segment(u32),
+    /// Generic arguments on an associated type, e.g. `Foo<Assoc<A, B> = C>` or `Foo<Assoc<A, B>: Bound>`.
+    AssocType { segment: u32, assoc_type: u32 },
+}
+
 #[derive(Debug, PartialEq, Eq, Clone)]
 pub enum PathLoweringDiagnostic {
-    GenericArgsProhibited { segment: u32, reason: GenericArgsProhibitedReason },
-    ParenthesizedGenericArgsWithoutFnTrait { segment: u32 },
+    GenericArgsProhibited {
+        segment: u32,
+        reason: GenericArgsProhibitedReason,
+    },
+    ParenthesizedGenericArgsWithoutFnTrait {
+        segment: u32,
+    },
+    /// The expected lifetimes & types and consts counts can be found by inspecting the `GenericDefId`.
+    IncorrectGenericsLen {
+        generics_source: PathGenericsSource,
+        provided_count: u32,
+        expected_count: u32,
+        kind: IncorrectGenericsLenKind,
+        def: GenericDefId,
+    },
+    IncorrectGenericsOrder {
+        generics_source: PathGenericsSource,
+        param_id: GenericParamId,
+        arg_idx: u32,
+        /// Whether the `GenericArgs` contains a `Self` arg.
+        has_self_arg: bool,
+    },
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum IncorrectGenericsLenKind {
+    Lifetimes,
+    TypesAndConsts,
 }
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
index a059d9df1c2..be358163991 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs
@@ -1,30 +1,33 @@
 //! A wrapper around [`TyLoweringContext`] specifically for lowering paths.
 
-use std::iter;
-
 use chalk_ir::{BoundVar, cast::Cast, fold::Shift};
 use either::Either;
 use hir_def::{
-    GenericDefId, GenericParamId, ItemContainerId, Lookup, TraitId,
+    GenericDefId, GenericParamId, Lookup, TraitId,
     expr_store::{
-        HygieneId,
+        ExpressionStore, HygieneId,
         path::{GenericArg, GenericArgs, GenericArgsParentheses, Path, PathSegment, PathSegments},
     },
+    hir::generics::{
+        GenericParamDataRef, TypeOrConstParamData, TypeParamData, TypeParamProvenance,
+    },
     resolver::{ResolveValueResult, TypeNs, ValueNs},
     signatures::TraitFlags,
-    type_ref::TypeRef,
+    type_ref::{TypeRef, TypeRefId},
 };
 use smallvec::SmallVec;
 use stdx::never;
 
 use crate::{
-    AliasEq, AliasTy, GenericArgsProhibitedReason, ImplTraitLoweringMode, Interner,
-    ParamLoweringMode, PathLoweringDiagnostic, ProjectionTy, QuantifiedWhereClause, Substitution,
-    TraitRef, Ty, TyBuilder, TyDefId, TyKind, TyLoweringContext, ValueTyDefId, WhereClause,
-    consteval::unknown_const_as_generic,
+    AliasEq, AliasTy, GenericArgsProhibitedReason, ImplTraitLoweringMode, IncorrectGenericsLenKind,
+    Interner, ParamLoweringMode, PathGenericsSource, PathLoweringDiagnostic, ProjectionTy,
+    QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyDefId, TyKind,
+    TyLoweringContext, ValueTyDefId, WhereClause,
+    consteval::{unknown_const, unknown_const_as_generic},
+    db::HirDatabase,
     error_lifetime,
-    generics::generics,
-    lower::{generic_arg_to_chalk, named_associated_type_shorthand_candidates},
+    generics::{Generics, generics},
+    lower::{GenericArgsPosition, named_associated_type_shorthand_candidates},
     to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
     utils::associated_type_by_name_including_super_traits,
 };
@@ -49,6 +52,7 @@ pub(crate) struct PathLoweringContext<'a, 'b> {
     current_segment_idx: usize,
     /// Contains the previous segment if `current_segment_idx == segments.len()`
     current_or_prev_segment: PathSegment<'a>,
+    position: GenericArgsPosition,
 }
 
 impl<'a, 'b> PathLoweringContext<'a, 'b> {
@@ -57,6 +61,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
         ctx: &'a mut TyLoweringContext<'b>,
         on_diagnostic: PathDiagnosticCallback<'a>,
         path: &'a Path,
+        position: GenericArgsPosition,
     ) -> Self {
         let segments = path.segments();
         let first_segment = segments.first().unwrap_or(PathSegment::MISSING);
@@ -67,6 +72,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
             segments,
             current_segment_idx: 0,
             current_or_prev_segment: first_segment,
+            position,
         }
     }
 
@@ -449,14 +455,19 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
                     // 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(_) => {
+                    ValueNs::GenericParam(_) => {
                         prohibit_generics_on_resolved(GenericArgsProhibitedReason::Const)
                     }
                     ValueNs::StaticId(_) => {
                         prohibit_generics_on_resolved(GenericArgsProhibitedReason::Static)
                     }
-                    ValueNs::FunctionId(_) | ValueNs::StructId(_) | ValueNs::EnumVariantId(_) => {}
-                    ValueNs::LocalBinding(_) => {}
+                    ValueNs::LocalBinding(_) => {
+                        prohibit_generics_on_resolved(GenericArgsProhibitedReason::LocalVariable)
+                    }
+                    ValueNs::FunctionId(_)
+                    | ValueNs::StructId(_)
+                    | ValueNs::EnumVariantId(_)
+                    | ValueNs::ConstId(_) => {}
                 }
             }
             ResolveValueResult::Partial(resolution, _, _) => {
@@ -615,6 +626,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
             def,
             infer_args,
             explicit_self_ty,
+            PathGenericsSource::Segment(self.current_segment_u32()),
         )
     }
 
@@ -624,144 +636,143 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
         def: GenericDefId,
         infer_args: bool,
         explicit_self_ty: Option<Ty>,
+        generics_source: PathGenericsSource,
     ) -> Substitution {
-        // Order is
-        // - Parent parameters
-        // - Optional Self parameter
-        // - Lifetime parameters
-        // - Type or Const parameters
-        let def_generics = generics(self.ctx.db, 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 ty_error = || TyKind::Error.intern(Interner).cast(Interner);
-        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),
-        };
-
-        let mut substs: Vec<_> = def_generics.iter_parent_id().map(param_to_err).collect();
+        struct LowererCtx<'a, 'b, 'c> {
+            ctx: &'a mut PathLoweringContext<'b, 'c>,
+            generics_source: PathGenericsSource,
+        }
 
-        tracing::debug!(?substs, ?parent_params);
+        impl GenericArgsLowerer for LowererCtx<'_, '_, '_> {
+            fn report_len_mismatch(
+                &mut self,
+                def: GenericDefId,
+                provided_count: u32,
+                expected_count: u32,
+                kind: IncorrectGenericsLenKind,
+            ) {
+                self.ctx.on_diagnostic(PathLoweringDiagnostic::IncorrectGenericsLen {
+                    generics_source: self.generics_source,
+                    provided_count,
+                    expected_count,
+                    kind,
+                    def,
+                });
+            }
 
-        // we need to iterate the lifetime and type/const params separately as our order of them
-        // differs from the supplied syntax
+            fn report_arg_mismatch(
+                &mut self,
+                param_id: GenericParamId,
+                arg_idx: u32,
+                has_self_arg: bool,
+            ) {
+                self.ctx.on_diagnostic(PathLoweringDiagnostic::IncorrectGenericsOrder {
+                    generics_source: self.generics_source,
+                    param_id,
+                    arg_idx,
+                    has_self_arg,
+                });
+            }
 
-        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);
+            fn provided_kind(
+                &mut self,
+                param_id: GenericParamId,
+                param: GenericParamDataRef<'_>,
+                arg: &GenericArg,
+            ) -> crate::GenericArg {
+                match (param, arg) {
+                    (GenericParamDataRef::LifetimeParamData(_), GenericArg::Lifetime(lifetime)) => {
+                        self.ctx.ctx.lower_lifetime(lifetime).cast(Interner)
+                    }
+                    (GenericParamDataRef::TypeParamData(_), GenericArg::Type(type_ref)) => {
+                        self.ctx.ctx.lower_ty(*type_ref).cast(Interner)
+                    }
+                    (GenericParamDataRef::ConstParamData(_), GenericArg::Const(konst)) => {
+                        let GenericParamId::ConstParamId(const_id) = param_id else {
+                            unreachable!("non-const param ID for const param");
+                        };
+                        self.ctx
+                            .ctx
+                            .lower_const(konst, self.ctx.ctx.db.const_param_ty(const_id))
+                            .cast(Interner)
+                    }
+                    _ => unreachable!("unmatching param kinds were passed to `provided_kind()`"),
+                }
+            }
 
-                if let Some(id) = def_toc_iter.next() {
-                    assert!(matches!(id, GenericParamId::TypeParamId(_)));
-                    substs.push(self_ty);
+            fn provided_type_like_const(
+                &mut self,
+                const_ty: Ty,
+                arg: TypeLikeConst<'_>,
+            ) -> crate::Const {
+                match arg {
+                    TypeLikeConst::Path(path) => self.ctx.ctx.lower_path_as_const(path, const_ty),
+                    TypeLikeConst::Infer => unknown_const(const_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));
+
+            fn inferred_kind(
+                &mut self,
+                def: GenericDefId,
+                param_id: GenericParamId,
+                param: GenericParamDataRef<'_>,
+                infer_args: bool,
+                preceding_args: &[crate::GenericArg],
+            ) -> crate::GenericArg {
+                let default = || {
+                    self.ctx
+                        .ctx
+                        .db
+                        .generic_defaults(def)
+                        .get(preceding_args.len())
+                        .map(|default| default.clone().substitute(Interner, preceding_args))
+                };
+                match param {
+                    GenericParamDataRef::LifetimeParamData(_) => error_lifetime().cast(Interner),
+                    GenericParamDataRef::TypeParamData(param) => {
+                        if !infer_args && param.default.is_some() {
+                            if let Some(default) = default() {
+                                return default;
+                            }
+                        }
+                        TyKind::Error.intern(Interner).cast(Interner)
+                    }
+                    GenericParamDataRef::ConstParamData(param) => {
+                        if !infer_args && param.default.is_some() {
+                            if let Some(default) = default() {
+                                return default;
+                            }
+                        }
+                        let GenericParamId::ConstParamId(const_id) = param_id else {
+                            unreachable!("non-const param ID for const param");
+                        };
+                        unknown_const_as_generic(self.ctx.ctx.db.const_param_ty(const_id))
+                            .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.store,
-                    |ctx, type_ref| ctx.lower_ty(type_ref),
-                    |ctx, const_ref, ty| ctx.lower_const(const_ref, ty),
-                    |ctx, path, ty| ctx.lower_path_as_const(path, ty),
-                    |ctx, lifetime_ref| ctx.lower_lifetime(lifetime_ref),
-                );
-                substs.push(arg);
             }
-        } else {
-            fill_self_param();
-        }
 
-        // 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).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 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 defaults[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));
+            fn parent_arg(&mut self, param_id: GenericParamId) -> crate::GenericArg {
+                match param_id {
+                    GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner),
+                    GenericParamId::ConstParamId(const_id) => {
+                        unknown_const_as_generic(self.ctx.ctx.db.const_param_ty(const_id))
+                    }
+                    GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner),
+                }
             }
-        } 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)
+        substs_from_args_and_bindings(
+            self.ctx.db,
+            self.ctx.store,
+            args_and_bindings,
+            def,
+            infer_args,
+            self.position,
+            explicit_self_ty,
+            &mut LowererCtx { ctx: self, generics_source },
+        )
     }
 
     pub(crate) fn lower_trait_ref_from_resolved_path(
@@ -786,7 +797,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
         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| {
+            args_and_bindings.bindings.iter().enumerate().flat_map(move |(binding_idx, binding)| {
                 let found = associated_type_by_name_including_super_traits(
                     self.ctx.db,
                     trait_ref.clone(),
@@ -805,6 +816,10 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
                     associated_ty.into(),
                     false, // this is not relevant
                     Some(super_trait_ref.self_type_parameter(Interner)),
+                    PathGenericsSource::AssocType {
+                        segment: self.current_segment_u32(),
+                        assoc_type: binding_idx as u32,
+                    },
                 );
                 let substitution = Substitution::from_iter(
                     Interner,
@@ -845,3 +860,288 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
         })
     }
 }
+
+/// A const that were parsed like a type.
+pub(crate) enum TypeLikeConst<'a> {
+    Infer,
+    Path(&'a Path),
+}
+
+pub(crate) trait GenericArgsLowerer {
+    fn report_len_mismatch(
+        &mut self,
+        def: GenericDefId,
+        provided_count: u32,
+        expected_count: u32,
+        kind: IncorrectGenericsLenKind,
+    );
+
+    fn report_arg_mismatch(&mut self, param_id: GenericParamId, arg_idx: u32, has_self_arg: bool);
+
+    fn provided_kind(
+        &mut self,
+        param_id: GenericParamId,
+        param: GenericParamDataRef<'_>,
+        arg: &GenericArg,
+    ) -> crate::GenericArg;
+
+    fn provided_type_like_const(&mut self, const_ty: Ty, arg: TypeLikeConst<'_>) -> crate::Const;
+
+    fn inferred_kind(
+        &mut self,
+        def: GenericDefId,
+        param_id: GenericParamId,
+        param: GenericParamDataRef<'_>,
+        infer_args: bool,
+        preceding_args: &[crate::GenericArg],
+    ) -> crate::GenericArg;
+
+    fn parent_arg(&mut self, param_id: GenericParamId) -> crate::GenericArg;
+}
+
+/// Returns true if there was an error.
+fn check_generic_args_len(
+    args_and_bindings: Option<&GenericArgs>,
+    def: GenericDefId,
+    def_generics: &Generics,
+    infer_args: bool,
+    position: GenericArgsPosition,
+    ctx: &mut impl GenericArgsLowerer,
+) -> bool {
+    let mut had_error = false;
+
+    let (mut provided_lifetimes_count, mut provided_types_and_consts_count) = (0usize, 0usize);
+    if let Some(args_and_bindings) = args_and_bindings {
+        let args_no_self = &args_and_bindings.args[usize::from(args_and_bindings.has_self_type)..];
+        for arg in args_no_self {
+            match arg {
+                GenericArg::Lifetime(_) => provided_lifetimes_count += 1,
+                GenericArg::Type(_) | GenericArg::Const(_) => provided_types_and_consts_count += 1,
+            }
+        }
+    }
+
+    let infer_lifetimes =
+        (position != GenericArgsPosition::Type || infer_args) && provided_lifetimes_count == 0;
+
+    let min_expected_lifetime_args =
+        if infer_lifetimes { 0 } else { def_generics.len_lifetimes_self() };
+    let max_expected_lifetime_args = def_generics.len_lifetimes_self();
+    if !(min_expected_lifetime_args..=max_expected_lifetime_args)
+        .contains(&provided_lifetimes_count)
+    {
+        ctx.report_len_mismatch(
+            def,
+            provided_lifetimes_count as u32,
+            def_generics.len_lifetimes_self() as u32,
+            IncorrectGenericsLenKind::Lifetimes,
+        );
+        had_error = true;
+    }
+
+    let defaults_count =
+        def_generics.iter_self_type_or_consts().filter(|(_, param)| param.has_default()).count();
+    let named_type_and_const_params_count = def_generics
+        .iter_self_type_or_consts()
+        .filter(|(_, param)| match param {
+            TypeOrConstParamData::TypeParamData(param) => {
+                param.provenance == TypeParamProvenance::TypeParamList
+            }
+            TypeOrConstParamData::ConstParamData(_) => true,
+        })
+        .count();
+    let expected_min =
+        if infer_args { 0 } else { named_type_and_const_params_count - defaults_count };
+    let expected_max = named_type_and_const_params_count;
+    if !(expected_min..=expected_max).contains(&provided_types_and_consts_count) {
+        ctx.report_len_mismatch(
+            def,
+            provided_types_and_consts_count as u32,
+            named_type_and_const_params_count as u32,
+            IncorrectGenericsLenKind::TypesAndConsts,
+        );
+        had_error = true;
+    }
+
+    had_error
+}
+
+pub(crate) fn substs_from_args_and_bindings(
+    db: &dyn HirDatabase,
+    store: &ExpressionStore,
+    args_and_bindings: Option<&GenericArgs>,
+    def: GenericDefId,
+    mut infer_args: bool,
+    position: GenericArgsPosition,
+    explicit_self_ty: Option<Ty>,
+    ctx: &mut impl GenericArgsLowerer,
+) -> Substitution {
+    // Order is
+    // - Parent parameters
+    // - Optional Self parameter
+    // - Lifetime parameters
+    // - Type or Const parameters
+    let def_generics = generics(db, def);
+    let args_slice = args_and_bindings.map(|it| &*it.args).unwrap_or_default();
+
+    // We do not allow inference if there are specified args, i.e. we do not allow partial inference.
+    let has_non_lifetime_args =
+        args_slice.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_)));
+    infer_args &= !has_non_lifetime_args;
+
+    let had_count_error =
+        check_generic_args_len(args_and_bindings, def, &def_generics, infer_args, position, ctx);
+
+    let mut substs = Vec::with_capacity(def_generics.len());
+
+    substs.extend(def_generics.iter_parent_id().map(|id| ctx.parent_arg(id)));
+
+    let mut args = args_slice.iter().enumerate().peekable();
+    let mut params = def_generics.iter_self().peekable();
+
+    // If we encounter a type or const when we expect a lifetime, we infer the lifetimes.
+    // If we later encounter a lifetime, we know that the arguments were provided in the
+    // wrong order. `force_infer_lt` records the type or const that forced lifetimes to be
+    // inferred, so we can use it for diagnostics later.
+    let mut force_infer_lt = None;
+
+    let has_self_arg = args_and_bindings.is_some_and(|it| it.has_self_type);
+    // First, handle `Self` parameter. Consume it from the args if provided, otherwise from `explicit_self_ty`,
+    // and lastly infer it.
+    if let Some(&(
+        self_param_id,
+        self_param @ GenericParamDataRef::TypeParamData(TypeParamData {
+            provenance: TypeParamProvenance::TraitSelf,
+            ..
+        }),
+    )) = params.peek()
+    {
+        let self_ty = if has_self_arg {
+            let (_, self_ty) = args.next().expect("has_self_type=true, should have Self type");
+            ctx.provided_kind(self_param_id, self_param, self_ty)
+        } else {
+            explicit_self_ty.map(|it| it.cast(Interner)).unwrap_or_else(|| {
+                ctx.inferred_kind(def, self_param_id, self_param, infer_args, &substs)
+            })
+        };
+        params.next();
+        substs.push(self_ty);
+    }
+
+    loop {
+        // We're going to iterate through the generic arguments that the user
+        // provided, matching them with the generic parameters we expect.
+        // Mismatches can occur as a result of elided lifetimes, or for malformed
+        // input. We try to handle both sensibly.
+        match (args.peek(), params.peek()) {
+            (Some(&(arg_idx, arg)), Some(&(param_id, param))) => match (arg, param) {
+                (GenericArg::Type(_), GenericParamDataRef::TypeParamData(type_param))
+                    if type_param.provenance == TypeParamProvenance::ArgumentImplTrait =>
+                {
+                    // Do not allow specifying `impl Trait` explicitly. We already err at that, but if we won't handle it here
+                    // we will handle it as if it was specified, instead of inferring it.
+                    substs.push(ctx.inferred_kind(def, param_id, param, infer_args, &substs));
+                    params.next();
+                }
+                (GenericArg::Lifetime(_), GenericParamDataRef::LifetimeParamData(_))
+                | (GenericArg::Type(_), GenericParamDataRef::TypeParamData(_))
+                | (GenericArg::Const(_), GenericParamDataRef::ConstParamData(_)) => {
+                    substs.push(ctx.provided_kind(param_id, param, arg));
+                    args.next();
+                    params.next();
+                }
+                (
+                    GenericArg::Type(_) | GenericArg::Const(_),
+                    GenericParamDataRef::LifetimeParamData(_),
+                ) => {
+                    // We expected a lifetime argument, but got a type or const
+                    // argument. That means we're inferring the lifetime.
+                    substs.push(ctx.inferred_kind(def, param_id, param, infer_args, &substs));
+                    params.next();
+                    force_infer_lt = Some((arg_idx as u32, param_id));
+                }
+                (GenericArg::Type(type_ref), GenericParamDataRef::ConstParamData(_)) => {
+                    if let Some(konst) = type_looks_like_const(store, *type_ref) {
+                        let GenericParamId::ConstParamId(param_id) = param_id else {
+                            panic!("unmatching param kinds");
+                        };
+                        let const_ty = db.const_param_ty(param_id);
+                        substs.push(ctx.provided_type_like_const(const_ty, konst).cast(Interner));
+                        args.next();
+                        params.next();
+                    } else {
+                        // See the `_ => { ... }` branch.
+                        if !had_count_error {
+                            ctx.report_arg_mismatch(param_id, arg_idx as u32, has_self_arg);
+                        }
+                        while args.next().is_some() {}
+                    }
+                }
+                _ => {
+                    // We expected one kind of parameter, but the user provided
+                    // another. This is an error. However, if we already know that
+                    // the arguments don't match up with the parameters, we won't issue
+                    // an additional error, as the user already knows what's wrong.
+                    if !had_count_error {
+                        ctx.report_arg_mismatch(param_id, arg_idx as u32, has_self_arg);
+                    }
+
+                    // We've reported the error, but we want to make sure that this
+                    // problem doesn't bubble down and create additional, irrelevant
+                    // errors. In this case, we're simply going to ignore the argument
+                    // and any following arguments. The rest of the parameters will be
+                    // inferred.
+                    while args.next().is_some() {}
+                }
+            },
+
+            (Some(&(_, arg)), None) => {
+                // We should never be able to reach this point with well-formed input.
+                // There are two situations in which we can encounter this issue.
+                //
+                //  1. The number of arguments is incorrect. In this case, an error
+                //     will already have been emitted, and we can ignore it.
+                //  2. We've inferred some lifetimes, which have been provided later (i.e.
+                //     after a type or const). We want to throw an error in this case.
+                if !had_count_error {
+                    assert!(
+                        matches!(arg, GenericArg::Lifetime(_)),
+                        "the only possible situation here is incorrect lifetime order"
+                    );
+                    let (provided_arg_idx, param_id) =
+                        force_infer_lt.expect("lifetimes ought to have been inferred");
+                    ctx.report_arg_mismatch(param_id, provided_arg_idx, has_self_arg);
+                }
+
+                break;
+            }
+
+            (None, Some(&(param_id, param))) => {
+                // If there are fewer arguments than parameters, it means we're inferring the remaining arguments.
+                substs.push(ctx.inferred_kind(def, param_id, param, infer_args, &substs));
+                params.next();
+            }
+
+            (None, None) => break,
+        }
+    }
+
+    Substitution::from_iter(Interner, substs)
+}
+
+fn type_looks_like_const(
+    store: &ExpressionStore,
+    type_ref: TypeRefId,
+) -> Option<TypeLikeConst<'_>> {
+    // A path/`_` const will be parsed as a type, instead of a const, because when parsing/lowering
+    // in hir-def we don't yet know the expected argument kind. rustc does this a bit differently,
+    // when lowering to HIR it resolves the path, and if it doesn't resolve to the type namespace
+    // it is lowered as a const. Our behavior could deviate from rustc when the value is resolvable
+    // in both the type and value namespaces, but I believe we only allow more code.
+    let type_ref = &store[type_ref];
+    match type_ref {
+        TypeRef::Path(path) => Some(TypeLikeConst::Path(path)),
+        TypeRef::Placeholder => Some(TypeLikeConst::Infer),
+        _ => None,
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
index 4eaa9fb5014..d8bcaa0e74e 100644
--- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
@@ -6,14 +6,17 @@
 use cfg::{CfgExpr, CfgOptions};
 use either::Either;
 use hir_def::{
-    DefWithBodyId, SyntheticSyntax,
-    expr_store::{ExprOrPatPtr, ExpressionStoreSourceMap, hir_segment_to_ast_segment},
+    DefWithBodyId, GenericParamId, SyntheticSyntax,
+    expr_store::{
+        ExprOrPatPtr, ExpressionStoreSourceMap, hir_assoc_type_binding_to_ast,
+        hir_generic_arg_to_ast, hir_segment_to_ast_segment,
+    },
     hir::ExprOrPatId,
 };
 use hir_expand::{HirFileId, InFile, mod_path::ModPath, name::Name};
 use hir_ty::{
-    CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, PathLoweringDiagnostic,
-    TyLoweringDiagnostic, TyLoweringDiagnosticKind,
+    CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, PathGenericsSource,
+    PathLoweringDiagnostic, TyLoweringDiagnostic, TyLoweringDiagnosticKind,
     db::HirDatabase,
     diagnostics::{BodyValidationDiagnostic, UnsafetyReason},
 };
@@ -24,11 +27,11 @@ use syntax::{
 };
 use triomphe::Arc;
 
-use crate::{AssocItem, Field, Function, Local, Trait, Type};
+use crate::{AssocItem, Field, Function, GenericDef, Local, Trait, Type};
 
 pub use hir_def::VariantId;
 pub use hir_ty::{
-    GenericArgsProhibitedReason,
+    GenericArgsProhibitedReason, IncorrectGenericsLenKind,
     diagnostics::{CaseType, IncorrectCase},
 };
 
@@ -113,6 +116,8 @@ diagnostics![
     GenericArgsProhibited,
     ParenthesizedGenericArgsWithoutFnTrait,
     BadRtn,
+    IncorrectGenericsLen,
+    IncorrectGenericsOrder,
 ];
 
 #[derive(Debug)]
@@ -425,6 +430,39 @@ pub struct BadRtn {
     pub rtn: InFile<AstPtr<ast::ReturnTypeSyntax>>,
 }
 
+#[derive(Debug)]
+pub struct IncorrectGenericsLen {
+    /// Points at the name if there are no generics.
+    pub generics_or_segment: InFile<AstPtr<Either<ast::GenericArgList, ast::NameRef>>>,
+    pub kind: IncorrectGenericsLenKind,
+    pub provided: u32,
+    pub expected: u32,
+    pub def: GenericDef,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum GenericArgKind {
+    Lifetime,
+    Type,
+    Const,
+}
+
+impl GenericArgKind {
+    fn from_id(id: GenericParamId) -> Self {
+        match id {
+            GenericParamId::TypeParamId(_) => GenericArgKind::Type,
+            GenericParamId::ConstParamId(_) => GenericArgKind::Const,
+            GenericParamId::LifetimeParamId(_) => GenericArgKind::Lifetime,
+        }
+    }
+}
+
+#[derive(Debug)]
+pub struct IncorrectGenericsOrder {
+    pub provided_arg: InFile<AstPtr<ast::GenericArg>>,
+    pub expected_kind: GenericArgKind,
+}
+
 impl AnyDiagnostic {
     pub(crate) fn body_validation_diagnostic(
         db: &dyn HirDatabase,
@@ -714,6 +752,47 @@ impl AnyDiagnostic {
                 };
                 Self::path_diagnostic(diag, source.with_value(path))?
             }
+            &InferenceDiagnostic::MethodCallIncorrectGenericsLen {
+                expr,
+                provided_count,
+                expected_count,
+                kind,
+                def,
+            } => {
+                let syntax = expr_syntax(expr)?;
+                let file_id = syntax.file_id;
+                let syntax =
+                    syntax.with_value(syntax.value.cast::<ast::MethodCallExpr>()?).to_node(db);
+                let generics_or_name = syntax
+                    .generic_arg_list()
+                    .map(Either::Left)
+                    .or_else(|| syntax.name_ref().map(Either::Right))?;
+                let generics_or_name = InFile::new(file_id, AstPtr::new(&generics_or_name));
+                IncorrectGenericsLen {
+                    generics_or_segment: generics_or_name,
+                    kind,
+                    provided: provided_count,
+                    expected: expected_count,
+                    def: def.into(),
+                }
+                .into()
+            }
+            &InferenceDiagnostic::MethodCallIncorrectGenericsOrder {
+                expr,
+                param_id,
+                arg_idx,
+                has_self_arg,
+            } => {
+                let syntax = expr_syntax(expr)?;
+                let file_id = syntax.file_id;
+                let syntax =
+                    syntax.with_value(syntax.value.cast::<ast::MethodCallExpr>()?).to_node(db);
+                let generic_args = syntax.generic_arg_list()?;
+                let provided_arg = hir_generic_arg_to_ast(&generic_args, arg_idx, has_self_arg)?;
+                let provided_arg = InFile::new(file_id, AstPtr::new(&provided_arg));
+                let expected_kind = GenericArgKind::from_id(param_id);
+                IncorrectGenericsOrder { provided_arg, expected_kind }.into()
+            }
         })
     }
 
@@ -750,6 +829,38 @@ impl AnyDiagnostic {
                 let args = path.with_value(args);
                 ParenthesizedGenericArgsWithoutFnTrait { args }.into()
             }
+            PathLoweringDiagnostic::IncorrectGenericsLen {
+                generics_source,
+                provided_count,
+                expected_count,
+                kind,
+                def,
+            } => {
+                let generics_or_segment =
+                    path_generics_source_to_ast(&path.value, generics_source)?;
+                let generics_or_segment = path.with_value(AstPtr::new(&generics_or_segment));
+                IncorrectGenericsLen {
+                    generics_or_segment,
+                    kind,
+                    provided: provided_count,
+                    expected: expected_count,
+                    def: def.into(),
+                }
+                .into()
+            }
+            PathLoweringDiagnostic::IncorrectGenericsOrder {
+                generics_source,
+                param_id,
+                arg_idx,
+                has_self_arg,
+            } => {
+                let generic_args =
+                    path_generics_source_to_ast(&path.value, generics_source)?.left()?;
+                let provided_arg = hir_generic_arg_to_ast(&generic_args, arg_idx, has_self_arg)?;
+                let provided_arg = path.with_value(AstPtr::new(&provided_arg));
+                let expected_kind = GenericArgKind::from_id(param_id);
+                IncorrectGenericsOrder { provided_arg, expected_kind }.into()
+            }
         })
     }
 
@@ -771,3 +882,27 @@ impl AnyDiagnostic {
         })
     }
 }
+
+fn path_generics_source_to_ast(
+    path: &ast::Path,
+    generics_source: PathGenericsSource,
+) -> Option<Either<ast::GenericArgList, ast::NameRef>> {
+    Some(match generics_source {
+        PathGenericsSource::Segment(segment) => {
+            let segment = hir_segment_to_ast_segment(path, segment)?;
+            segment
+                .generic_arg_list()
+                .map(Either::Left)
+                .or_else(|| segment.name_ref().map(Either::Right))?
+        }
+        PathGenericsSource::AssocType { segment, assoc_type } => {
+            let segment = hir_segment_to_ast_segment(path, segment)?;
+            let segment_args = segment.generic_arg_list()?;
+            let assoc = hir_assoc_type_binding_to_ast(&segment_args, assoc_type)?;
+            assoc
+                .generic_arg_list()
+                .map(Either::Left)
+                .or_else(|| assoc.name_ref().map(Either::Right))?
+        }
+    })
+}
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 37c213c2afa..143c13069e4 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -1709,10 +1709,11 @@ impl_from!(Struct, Union, Enum for Adt);
 impl Adt {
     pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool {
         let subst = db.generic_defaults(self.into());
-        subst.iter().any(|ty| match ty.skip_binders().data(Interner) {
-            GenericArgData::Ty(it) => it.is_unknown(),
-            _ => false,
-        })
+        (subst.is_empty() && db.generic_params(self.into()).len_type_or_consts() != 0)
+            || subst.iter().any(|ty| match ty.skip_binders().data(Interner) {
+                GenericArgData::Ty(it) => it.is_unknown(),
+                _ => false,
+            })
     }
 
     pub fn layout(self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
@@ -3000,10 +3001,11 @@ pub struct TypeAlias {
 impl TypeAlias {
     pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool {
         let subst = db.generic_defaults(self.id.into());
-        subst.iter().any(|ty| match ty.skip_binders().data(Interner) {
-            GenericArgData::Ty(it) => it.is_unknown(),
-            _ => false,
-        })
+        (subst.is_empty() && db.generic_params(self.id.into()).len_type_or_consts() != 0)
+            || subst.iter().any(|ty| match ty.skip_binders().data(Interner) {
+                GenericArgData::Ty(it) => it.is_unknown(),
+                _ => false,
+            })
     }
 
     pub fn module(self, db: &dyn HirDatabase) -> Module {
@@ -3732,6 +3734,23 @@ impl GenericDef {
             }
         }
     }
+
+    /// Returns a string describing the kind of this type.
+    #[inline]
+    pub fn description(self) -> &'static str {
+        match self {
+            GenericDef::Function(_) => "function",
+            GenericDef::Adt(Adt::Struct(_)) => "struct",
+            GenericDef::Adt(Adt::Enum(_)) => "enum",
+            GenericDef::Adt(Adt::Union(_)) => "union",
+            GenericDef::Trait(_) => "trait",
+            GenericDef::TraitAlias(_) => "trait alias",
+            GenericDef::TypeAlias(_) => "type alias",
+            GenericDef::Impl(_) => "impl",
+            GenericDef::Const(_) => "constant",
+            GenericDef::Static(_) => "static",
+        }
+    }
 }
 
 // We cannot call this `Substitution` unfortunately...
@@ -4276,7 +4295,8 @@ fn generic_arg_from_param(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Optio
     let local_idx = hir_ty::param_idx(db, id)?;
     let defaults = db.generic_defaults(id.parent);
     let ty = defaults.get(local_idx)?.clone();
-    let subst = TyBuilder::placeholder_subst(db, id.parent);
+    let full_subst = TyBuilder::placeholder_subst(db, id.parent);
+    let subst = &full_subst.as_slice(Interner)[..local_idx];
     Some(ty.substitute(Interner, &subst))
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs
index b79894dd157..b617c094983 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs
@@ -36,6 +36,7 @@ fn describe_reason(reason: GenericArgsProhibitedReason) -> String {
         }
         GenericArgsProhibitedReason::Const => "constants",
         GenericArgsProhibitedReason::Static => "statics",
+        GenericArgsProhibitedReason::LocalVariable => "local variables",
     };
     format!("generic arguments are not allowed on {kind}")
 }
@@ -320,7 +321,7 @@ trait E<A: foo::<()>::Trait>
                // ^^^^^ 💡 error: generic arguments are not allowed on builtin types
 }
 
-impl<A: foo::<()>::Trait> E for ()
+impl<A: foo::<()>::Trait> E<()> for ()
         // ^^^^^^ 💡 error: generic arguments are not allowed on modules
     where bool<i32>: foo::Trait
            // ^^^^^ 💡 error: generic arguments are not allowed on builtin types
@@ -518,14 +519,14 @@ fn baz() {
     }
 
     #[test]
-    fn const_and_static() {
+    fn const_param_and_static() {
         check_diagnostics(
             r#"
 const CONST: i32 = 0;
 static STATIC: i32 = 0;
-fn baz() {
-    let _ = CONST::<()>;
-              // ^^^^^^ 💡 error: generic arguments are not allowed on constants
+fn baz<const CONST_PARAM: usize>() {
+    let _ = CONST_PARAM::<()>;
+                    // ^^^^^^ 💡 error: generic arguments are not allowed on constants
     let _ = STATIC::<()>;
                // ^^^^^^ 💡 error: generic arguments are not allowed on statics
 }
@@ -534,6 +535,19 @@ fn baz() {
     }
 
     #[test]
+    fn local_variable() {
+        check_diagnostics(
+            r#"
+fn baz() {
+    let x = 1;
+    let _ = x::<()>;
+          // ^^^^^^ 💡 error: generic arguments are not allowed on local variables
+}
+        "#,
+        );
+    }
+
+    #[test]
     fn enum_variant() {
         check_diagnostics(
             r#"
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs
new file mode 100644
index 00000000000..4fc894080ab
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs
@@ -0,0 +1,172 @@
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+use hir::IncorrectGenericsLenKind;
+
+// Diagnostic: incorrect-generics-len
+//
+// This diagnostic is triggered if the number of generic arguments does not match their declaration.
+pub(crate) fn incorrect_generics_len(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::IncorrectGenericsLen,
+) -> Diagnostic {
+    let owner_description = d.def.description();
+    let expected = d.expected;
+    let provided = d.provided;
+    let kind_description = match d.kind {
+        IncorrectGenericsLenKind::Lifetimes => "lifetime",
+        IncorrectGenericsLenKind::TypesAndConsts => "generic",
+    };
+    let message = format!(
+        "this {owner_description} takes {expected} {kind_description} argument{} \
+            but {provided} {kind_description} argument{} {} supplied",
+        if expected == 1 { "" } else { "s" },
+        if provided == 1 { "" } else { "s" },
+        if provided == 1 { "was" } else { "were" },
+    );
+    Diagnostic::new_with_syntax_node_ptr(
+        ctx,
+        DiagnosticCode::RustcHardError("E0107"),
+        message,
+        d.generics_or_segment.map(Into::into),
+    )
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::check_diagnostics;
+
+    #[test]
+    fn partially_specified_generics() {
+        check_diagnostics(
+            r#"
+struct Bar<T, U>(T, U);
+
+fn foo() {
+    let _ = Bar::<()>;
+            // ^^^^^^ error: this struct takes 2 generic arguments but 1 generic argument was supplied
+}
+
+        "#,
+        );
+    }
+
+    #[test]
+    fn enum_variant() {
+        check_diagnostics(
+            r#"
+enum Enum<T, U> {
+    Variant(T, U),
+}
+
+fn foo() {
+    let _ = Enum::<()>::Variant;
+             // ^^^^^^ error: this enum takes 2 generic arguments but 1 generic argument was supplied
+    let _ = Enum::Variant::<()>;
+                      // ^^^^^^ error: this enum takes 2 generic arguments but 1 generic argument was supplied
+}
+
+        "#,
+        );
+    }
+
+    #[test]
+    fn lifetimes() {
+        check_diagnostics(
+            r#"
+struct Foo<'a, 'b>(&'a &'b ());
+struct Bar<'a>(&'a ());
+
+fn foo() -> Foo {
+    let _ = Foo;
+    let _ = Foo::<>;
+    let _ = Foo::<'static>;
+            // ^^^^^^^^^^^ error: this struct takes 2 lifetime arguments but 1 lifetime argument was supplied
+    loop {}
+}
+
+fn bar(_v: Bar) -> Foo { loop {} }
+        "#,
+        );
+    }
+
+    #[test]
+    fn no_error_for_elided_lifetimes() {
+        check_diagnostics(
+            r#"
+struct Foo<'a>(&'a ());
+
+fn foo(_v: &()) -> Foo { loop {} }
+        "#,
+        );
+    }
+
+    #[test]
+    fn errs_for_elided_lifetimes_if_lifetimes_are_explicitly_provided() {
+        check_diagnostics(
+            r#"
+struct Foo<'a, 'b>(&'a &'b ());
+
+fn foo(_v: Foo<'_>
+           // ^^^^ error: this struct takes 2 lifetime arguments but 1 lifetime argument was supplied
+) -> Foo<'static> { loop {} }
+     // ^^^^^^^^^ error: this struct takes 2 lifetime arguments but 1 lifetime argument was supplied
+        "#,
+        );
+    }
+
+    #[test]
+    fn types_and_consts() {
+        check_diagnostics(
+            r#"
+struct Foo<'a, T>(&'a T);
+fn foo(_v: Foo) {}
+        // ^^^ error: this struct takes 1 generic argument but 0 generic arguments were supplied
+
+struct Bar<T, const N: usize>(T);
+fn bar() {
+    let _ = Bar::<()>;
+            // ^^^^^^ error: this struct takes 2 generic arguments but 1 generic argument was supplied
+}
+        "#,
+        );
+    }
+
+    #[test]
+    fn respects_defaults() {
+        check_diagnostics(
+            r#"
+struct Foo<T = (), const N: usize = 0>(T);
+fn foo(_v: Foo) {}
+
+struct Bar<T, const N: usize = 0>(T);
+fn bar(_v: Bar<()>) {}
+        "#,
+        );
+    }
+
+    #[test]
+    fn constant() {
+        check_diagnostics(
+            r#"
+const CONST: i32 = 0;
+fn baz() {
+    let _ = CONST::<()>;
+              // ^^^^^^ error: this constant takes 0 generic arguments but 1 generic argument was supplied
+}
+        "#,
+        );
+    }
+
+    #[test]
+    fn assoc_type() {
+        check_diagnostics(
+            r#"
+trait Trait {
+    type Assoc;
+}
+
+fn foo<T: Trait<Assoc<i32> = bool>>() {}
+                  // ^^^^^ error: this type alias takes 0 generic arguments but 1 generic argument was supplied
+        "#,
+        );
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs
new file mode 100644
index 00000000000..84496df2d7c
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs
@@ -0,0 +1,80 @@
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+use hir::GenericArgKind;
+use syntax::SyntaxKind;
+
+// Diagnostic: incorrect-generics-order
+//
+// This diagnostic is triggered the order of provided generic arguments does not match their declaration.
+pub(crate) fn incorrect_generics_order(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::IncorrectGenericsOrder,
+) -> Diagnostic {
+    let provided_description = match d.provided_arg.value.kind() {
+        SyntaxKind::CONST_ARG => "constant",
+        SyntaxKind::LIFETIME_ARG => "lifetime",
+        SyntaxKind::TYPE_ARG => "type",
+        _ => panic!("non-generic-arg passed to `incorrect_generics_order()`"),
+    };
+    let expected_description = match d.expected_kind {
+        GenericArgKind::Lifetime => "lifetime",
+        GenericArgKind::Type => "type",
+        GenericArgKind::Const => "constant",
+    };
+    let message =
+        format!("{provided_description} provided when a {expected_description} was expected");
+    Diagnostic::new_with_syntax_node_ptr(
+        ctx,
+        DiagnosticCode::RustcHardError("E0747"),
+        message,
+        d.provided_arg.map(Into::into),
+    )
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::check_diagnostics;
+
+    #[test]
+    fn lifetime_out_of_order() {
+        check_diagnostics(
+            r#"
+struct Foo<'a, T>(&'a T);
+
+fn bar(_v: Foo<(), 'static>) {}
+            // ^^ error: type provided when a lifetime was expected
+        "#,
+        );
+    }
+
+    #[test]
+    fn types_and_consts() {
+        check_diagnostics(
+            r#"
+struct Foo<T>(T);
+fn foo1(_v: Foo<1>) {}
+             // ^ error: constant provided when a type was expected
+fn foo2(_v: Foo<{ (1, 2) }>) {}
+             // ^^^^^^^^^^ error: constant provided when a type was expected
+
+struct Bar<const N: usize>;
+fn bar(_v: Bar<()>) {}
+            // ^^ error: type provided when a constant was expected
+
+struct Baz<T, const N: usize>(T);
+fn baz(_v: Baz<1, ()>) {}
+            // ^ error: constant provided when a type was expected
+        "#,
+        );
+    }
+
+    #[test]
+    fn no_error_when_num_incorrect() {
+        check_diagnostics(
+            r#"
+struct Baz<T, U>(T, U);
+fn baz(_v: Baz<1>) {}
+           // ^^^ error: this struct takes 2 generic arguments but 1 generic argument was supplied
+        "#,
+        );
+    }
+}
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 0d725b35634..ddaef57b537 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
@@ -32,6 +32,8 @@ mod handlers {
     pub(crate) mod inactive_code;
     pub(crate) mod incoherent_impl;
     pub(crate) mod incorrect_case;
+    pub(crate) mod incorrect_generics_len;
+    pub(crate) mod incorrect_generics_order;
     pub(crate) mod invalid_cast;
     pub(crate) mod invalid_derive_target;
     pub(crate) mod macro_error;
@@ -499,6 +501,8 @@ pub fn semantic_diagnostics(
                 handlers::parenthesized_generic_args_without_fn_trait::parenthesized_generic_args_without_fn_trait(&ctx, &d)
             }
             AnyDiagnostic::BadRtn(d) => handlers::bad_rtn::bad_rtn(&ctx, &d),
+            AnyDiagnostic::IncorrectGenericsLen(d) => handlers::incorrect_generics_len::incorrect_generics_len(&ctx, &d),
+            AnyDiagnostic::IncorrectGenericsOrder(d) => handlers::incorrect_generics_order::incorrect_generics_order(&ctx, &d),
         };
         res.push(d)
     }
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 4a2896ab148..1079dec0431 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
@@ -1063,7 +1063,7 @@ pub mod cmp {
 // region:fmt
 pub mod fmt {
     pub struct Error;
-    pub type Result = Result<(), Error>;
+    pub type Result = crate::result::Result<(), Error>;
     pub struct Formatter<'a>;
     pub struct DebugTuple;
     pub struct DebugStruct;