about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs55
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics.rs34
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs326
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/defs.rs143
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/search.rs20
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/doc_links.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_definition.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover.rs26
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/render.rs45
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/tests.rs415
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/references.rs16
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/rename.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/static_index.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs4
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs33
-rw-r--r--src/tools/rust-analyzer/docs/user/generated_config.adoc9
-rw-r--r--src/tools/rust-analyzer/editors/code/package.json23
29 files changed, 1015 insertions, 186 deletions
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 3bc2eee1e7c..8186af296a6 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -3574,6 +3574,61 @@ impl GenericDef {
     }
 }
 
+// We cannot call this `Substitution` unfortunately...
+#[derive(Debug)]
+pub struct GenericSubstitution {
+    def: GenericDefId,
+    subst: Substitution,
+    env: Arc<TraitEnvironment>,
+}
+
+impl GenericSubstitution {
+    fn new(def: GenericDefId, subst: Substitution, env: Arc<TraitEnvironment>) -> Self {
+        Self { def, subst, env }
+    }
+
+    pub fn types(&self, db: &dyn HirDatabase) -> Vec<(Symbol, Type)> {
+        let container = match self.def {
+            GenericDefId::ConstId(id) => Some(id.lookup(db.upcast()).container),
+            GenericDefId::FunctionId(id) => Some(id.lookup(db.upcast()).container),
+            GenericDefId::TypeAliasId(id) => Some(id.lookup(db.upcast()).container),
+            _ => None,
+        };
+        let container_type_params = container
+            .and_then(|container| match container {
+                ItemContainerId::ImplId(container) => Some(container.into()),
+                ItemContainerId::TraitId(container) => Some(container.into()),
+                _ => None,
+            })
+            .map(|container| {
+                db.generic_params(container)
+                    .iter_type_or_consts()
+                    .filter_map(|param| match param.1 {
+                        TypeOrConstParamData::TypeParamData(param) => Some(param.name.clone()),
+                        TypeOrConstParamData::ConstParamData(_) => None,
+                    })
+                    .collect::<Vec<_>>()
+            });
+        let generics = db.generic_params(self.def);
+        let type_params = generics.iter_type_or_consts().filter_map(|param| match param.1 {
+            TypeOrConstParamData::TypeParamData(param) => Some(param.name.clone()),
+            TypeOrConstParamData::ConstParamData(_) => None,
+        });
+        // The `Substitution` is first self then container, we want the reverse order.
+        let self_params = self.subst.type_parameters(Interner).zip(type_params);
+        let container_params = self.subst.as_slice(Interner)[generics.len()..]
+            .iter()
+            .filter_map(|param| param.ty(Interner).cloned())
+            .zip(container_type_params.into_iter().flatten());
+        container_params
+            .chain(self_params)
+            .filter_map(|(ty, name)| {
+                Some((name?.symbol().clone(), Type { ty, env: self.env.clone() }))
+            })
+            .collect()
+    }
+}
+
 /// A single local definition.
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub struct Local {
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index b896cda9ddf..17b670f6a46 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -49,10 +49,10 @@ use crate::{
     semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
     source_analyzer::{name_hygiene, resolve_hir_path, SourceAnalyzer},
     Access, Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const,
-    ConstParam, Crate, DeriveHelper, Enum, Field, Function, HasSource, HirFileId, Impl, InFile,
-    InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name,
-    OverloadedDeref, Path, ScopeDef, Static, Struct, ToolModule, Trait, TraitAlias, TupleField,
-    Type, TypeAlias, TypeParam, Union, Variant, VariantDef,
+    ConstParam, Crate, DeriveHelper, Enum, Field, Function, GenericSubstitution, HasSource,
+    HirFileId, Impl, InFile, InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro,
+    Module, ModuleDef, Name, OverloadedDeref, Path, ScopeDef, Static, Struct, ToolModule, Trait,
+    TraitAlias, TupleField, Type, TypeAlias, TypeParam, Union, Variant, VariantDef,
 };
 
 const CONTINUE_NO_BREAKS: ControlFlow<Infallible, ()> = ControlFlow::Continue(());
@@ -1415,7 +1415,7 @@ impl<'db> SemanticsImpl<'db> {
     pub fn resolve_method_call_fallback(
         &self,
         call: &ast::MethodCallExpr,
-    ) -> Option<Either<Function, Field>> {
+    ) -> Option<(Either<Function, Field>, Option<GenericSubstitution>)> {
         self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call)
     }
 
@@ -1458,7 +1458,7 @@ impl<'db> SemanticsImpl<'db> {
     pub fn resolve_field_fallback(
         &self,
         field: &ast::FieldExpr,
-    ) -> Option<Either<Either<Field, TupleField>, Function>> {
+    ) -> Option<(Either<Either<Field, TupleField>, Function>, Option<GenericSubstitution>)> {
         self.analyze(field.syntax())?.resolve_field_fallback(self.db, field)
     }
 
@@ -1466,10 +1466,25 @@ impl<'db> SemanticsImpl<'db> {
         &self,
         field: &ast::RecordExprField,
     ) -> Option<(Field, Option<Local>, Type)> {
+        self.resolve_record_field_with_substitution(field)
+            .map(|(field, local, ty, _)| (field, local, ty))
+    }
+
+    pub fn resolve_record_field_with_substitution(
+        &self,
+        field: &ast::RecordExprField,
+    ) -> Option<(Field, Option<Local>, Type, GenericSubstitution)> {
         self.analyze(field.syntax())?.resolve_record_field(self.db, field)
     }
 
     pub fn resolve_record_pat_field(&self, field: &ast::RecordPatField) -> Option<(Field, Type)> {
+        self.resolve_record_pat_field_with_subst(field).map(|(field, ty, _)| (field, ty))
+    }
+
+    pub fn resolve_record_pat_field_with_subst(
+        &self,
+        field: &ast::RecordPatField,
+    ) -> Option<(Field, Type, GenericSubstitution)> {
         self.analyze(field.syntax())?.resolve_record_pat_field(self.db, field)
     }
 
@@ -1525,6 +1540,13 @@ impl<'db> SemanticsImpl<'db> {
     }
 
     pub fn resolve_path(&self, path: &ast::Path) -> Option<PathResolution> {
+        self.resolve_path_with_subst(path).map(|(it, _)| it)
+    }
+
+    pub fn resolve_path_with_subst(
+        &self,
+        path: &ast::Path,
+    ) -> Option<(PathResolution, Option<GenericSubstitution>)> {
         self.analyze(path.syntax())?.resolve_path(self.db, path)
     }
 
diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
index 4329a888b39..2f21685fb59 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -9,8 +9,8 @@ use std::iter::{self, once};
 
 use crate::{
     db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr,
-    BuiltinType, Callable, Const, DeriveHelper, Field, Function, Local, Macro, ModuleDef, Static,
-    Struct, ToolModule, Trait, TraitAlias, TupleField, Type, TypeAlias, Variant,
+    BuiltinType, Callable, Const, DeriveHelper, Field, Function, GenericSubstitution, Local, Macro,
+    ModuleDef, Static, Struct, ToolModule, Trait, TraitAlias, TupleField, Type, TypeAlias, Variant,
 };
 use either::Either;
 use hir_def::{
@@ -18,15 +18,15 @@ use hir_def::{
         scope::{ExprScopes, ScopeId},
         Body, BodySourceMap, HygieneId,
     },
-    hir::{BindingId, ExprId, ExprOrPatId, Pat, PatId},
+    hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat, PatId},
     lang_item::LangItem,
     lower::LowerCtx,
     nameres::MacroSubNs,
     path::{ModPath, Path, PathKind},
     resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs},
     type_ref::{Mutability, TypesMap, TypesSourceMap},
-    AsMacroCall, AssocItemId, ConstId, DefWithBodyId, FieldId, FunctionId, ItemContainerId,
-    LocalFieldId, Lookup, ModuleDefId, StructId, TraitId, VariantId,
+    AsMacroCall, AssocItemId, CallableDefId, ConstId, DefWithBodyId, FieldId, FunctionId,
+    ItemContainerId, LocalFieldId, Lookup, ModuleDefId, StructId, TraitId, VariantId,
 };
 use hir_expand::{
     mod_path::path,
@@ -38,9 +38,10 @@ use hir_ty::{
         record_literal_missing_fields, record_pattern_missing_fields, unsafe_expressions,
         InsideUnsafeBlock,
     },
+    from_assoc_type_id,
     lang_items::lang_items_for_bin_op,
-    method_resolution, Adjustment, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind,
-    TyLoweringContext,
+    method_resolution, Adjustment, InferenceResult, Interner, Substitution, TraitEnvironment, Ty,
+    TyExt, TyKind, TyLoweringContext,
 };
 use intern::sym;
 use itertools::Itertools;
@@ -120,6 +121,13 @@ impl SourceAnalyzer {
         self.def.as_ref().map(|(_, body, _)| &**body)
     }
 
+    fn trait_environment(&self, db: &dyn HirDatabase) -> Arc<TraitEnvironment> {
+        self.def.as_ref().map(|(def, ..)| *def).map_or_else(
+            || TraitEnvironment::empty(self.resolver.krate()),
+            |def| db.trait_environment_for_body(def),
+        )
+    }
+
     fn expr_id(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<ExprOrPatId> {
         let src = match expr {
             ast::Expr::MacroExpr(expr) => {
@@ -294,18 +302,23 @@ impl SourceAnalyzer {
         &self,
         db: &dyn HirDatabase,
         call: &ast::MethodCallExpr,
-    ) -> Option<Either<Function, Field>> {
+    ) -> Option<(Either<Function, Field>, Option<GenericSubstitution>)> {
         let expr_id = self.expr_id(db, &call.clone().into())?.as_expr()?;
         let inference_result = self.infer.as_ref()?;
         match inference_result.method_resolution(expr_id) {
-            Some((f_in_trait, substs)) => Some(Either::Left(
-                self.resolve_impl_method_or_trait_def(db, f_in_trait, substs).into(),
-            )),
-            None => inference_result
-                .field_resolution(expr_id)
-                .and_then(Either::left)
-                .map(Into::into)
-                .map(Either::Right),
+            Some((f_in_trait, substs)) => {
+                let (fn_, subst) =
+                    self.resolve_impl_method_or_trait_def_with_subst(db, f_in_trait, substs);
+                Some((
+                    Either::Left(fn_.into()),
+                    Some(GenericSubstitution::new(fn_.into(), subst, self.trait_environment(db))),
+                ))
+            }
+            None => {
+                inference_result.field_resolution(expr_id).and_then(Either::left).map(|field| {
+                    (Either::Right(field.into()), self.field_subst(expr_id, inference_result, db))
+                })
+            }
         }
     }
 
@@ -330,22 +343,53 @@ impl SourceAnalyzer {
         })
     }
 
+    fn field_subst(
+        &self,
+        field_expr: ExprId,
+        infer: &InferenceResult,
+        db: &dyn HirDatabase,
+    ) -> Option<GenericSubstitution> {
+        let body = self.body()?;
+        if let Expr::Field { expr: object_expr, name: _ } = body[field_expr] {
+            let (adt, subst) = type_of_expr_including_adjust(infer, object_expr)?.as_adt()?;
+            return Some(GenericSubstitution::new(
+                adt.into(),
+                subst.clone(),
+                self.trait_environment(db),
+            ));
+        }
+        None
+    }
+
     pub(crate) fn resolve_field_fallback(
         &self,
         db: &dyn HirDatabase,
         field: &ast::FieldExpr,
-    ) -> Option<Either<Either<Field, TupleField>, Function>> {
+    ) -> Option<(Either<Either<Field, TupleField>, Function>, Option<GenericSubstitution>)> {
         let &(def, ..) = self.def.as_ref()?;
         let expr_id = self.expr_id(db, &field.clone().into())?.as_expr()?;
         let inference_result = self.infer.as_ref()?;
         match inference_result.field_resolution(expr_id) {
-            Some(field) => Some(Either::Left(field.map_either(Into::into, |f| TupleField {
-                owner: def,
-                tuple: f.tuple,
-                index: f.index,
-            }))),
+            Some(field) => match field {
+                Either::Left(field) => Some((
+                    Either::Left(Either::Left(field.into())),
+                    self.field_subst(expr_id, inference_result, db),
+                )),
+                Either::Right(field) => Some((
+                    Either::Left(Either::Right(TupleField {
+                        owner: def,
+                        tuple: field.tuple,
+                        index: field.index,
+                    })),
+                    None,
+                )),
+            },
             None => inference_result.method_resolution(expr_id).map(|(f, substs)| {
-                Either::Right(self.resolve_impl_method_or_trait_def(db, f, substs).into())
+                let (f, subst) = self.resolve_impl_method_or_trait_def_with_subst(db, f, substs);
+                (
+                    Either::Right(f.into()),
+                    Some(GenericSubstitution::new(f.into(), subst, self.trait_environment(db))),
+                )
             }),
         }
     }
@@ -557,7 +601,7 @@ impl SourceAnalyzer {
         &self,
         db: &dyn HirDatabase,
         field: &ast::RecordExprField,
-    ) -> Option<(Field, Option<Local>, Type)> {
+    ) -> Option<(Field, Option<Local>, Type, GenericSubstitution)> {
         let record_expr = ast::RecordExpr::cast(field.syntax().parent().and_then(|p| p.parent())?)?;
         let expr = ast::Expr::from(record_expr);
         let expr_id = self.body_source_map()?.node_expr(InFile::new(self.file_id, &expr))?;
@@ -583,30 +627,39 @@ impl SourceAnalyzer {
                 _ => None,
             }
         };
-        let (_, subst) = self.infer.as_ref()?.type_of_expr_or_pat(expr_id)?.as_adt()?;
+        let (adt, subst) = self.infer.as_ref()?.type_of_expr_or_pat(expr_id)?.as_adt()?;
         let variant = self.infer.as_ref()?.variant_resolution_for_expr_or_pat(expr_id)?;
         let variant_data = variant.variant_data(db.upcast());
         let field = FieldId { parent: variant, local_id: variant_data.field(&local_name)? };
         let field_ty =
             db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst);
-        Some((field.into(), local, Type::new_with_resolver(db, &self.resolver, field_ty)))
+        Some((
+            field.into(),
+            local,
+            Type::new_with_resolver(db, &self.resolver, field_ty),
+            GenericSubstitution::new(adt.into(), subst.clone(), self.trait_environment(db)),
+        ))
     }
 
     pub(crate) fn resolve_record_pat_field(
         &self,
         db: &dyn HirDatabase,
         field: &ast::RecordPatField,
-    ) -> Option<(Field, Type)> {
+    ) -> Option<(Field, Type, GenericSubstitution)> {
         let field_name = field.field_name()?.as_name();
         let record_pat = ast::RecordPat::cast(field.syntax().parent().and_then(|p| p.parent())?)?;
         let pat_id = self.pat_id(&record_pat.into())?;
         let variant = self.infer.as_ref()?.variant_resolution_for_pat(pat_id)?;
         let variant_data = variant.variant_data(db.upcast());
         let field = FieldId { parent: variant, local_id: variant_data.field(&field_name)? };
-        let (_, subst) = self.infer.as_ref()?.type_of_pat.get(pat_id)?.as_adt()?;
+        let (adt, subst) = self.infer.as_ref()?.type_of_pat.get(pat_id)?.as_adt()?;
         let field_ty =
             db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst);
-        Some((field.into(), Type::new_with_resolver(db, &self.resolver, field_ty)))
+        Some((
+            field.into(),
+            Type::new_with_resolver(db, &self.resolver, field_ty),
+            GenericSubstitution::new(adt.into(), subst.clone(), self.trait_environment(db)),
+        ))
     }
 
     pub(crate) fn resolve_macro_call(
@@ -654,7 +707,7 @@ impl SourceAnalyzer {
         &self,
         db: &dyn HirDatabase,
         path: &ast::Path,
-    ) -> Option<PathResolution> {
+    ) -> Option<(PathResolution, Option<GenericSubstitution>)> {
         let parent = path.syntax().parent();
         let parent = || parent.clone();
 
@@ -664,60 +717,106 @@ impl SourceAnalyzer {
             if let Some(path_expr) = parent().and_then(ast::PathExpr::cast) {
                 let expr_id = self.expr_id(db, &path_expr.into())?;
                 if let Some((assoc, subs)) = infer.assoc_resolutions_for_expr_or_pat(expr_id) {
-                    let assoc = match assoc {
+                    let (assoc, subst) = match assoc {
                         AssocItemId::FunctionId(f_in_trait) => {
                             match infer.type_of_expr_or_pat(expr_id) {
-                                None => assoc,
+                                None => {
+                                    let subst = GenericSubstitution::new(
+                                        f_in_trait.into(),
+                                        subs,
+                                        self.trait_environment(db),
+                                    );
+                                    (assoc, subst)
+                                }
                                 Some(func_ty) => {
                                     if let TyKind::FnDef(_fn_def, subs) = func_ty.kind(Interner) {
-                                        self.resolve_impl_method_or_trait_def(
-                                            db,
-                                            f_in_trait,
-                                            subs.clone(),
-                                        )
-                                        .into()
+                                        let (fn_, subst) = self
+                                            .resolve_impl_method_or_trait_def_with_subst(
+                                                db,
+                                                f_in_trait,
+                                                subs.clone(),
+                                            );
+                                        let subst = GenericSubstitution::new(
+                                            fn_.into(),
+                                            subst,
+                                            self.trait_environment(db),
+                                        );
+                                        (fn_.into(), subst)
                                     } else {
-                                        assoc
+                                        let subst = GenericSubstitution::new(
+                                            f_in_trait.into(),
+                                            subs,
+                                            self.trait_environment(db),
+                                        );
+                                        (assoc, subst)
                                     }
                                 }
                             }
                         }
                         AssocItemId::ConstId(const_id) => {
-                            self.resolve_impl_const_or_trait_def(db, const_id, subs).into()
+                            let (konst, subst) =
+                                self.resolve_impl_const_or_trait_def_with_subst(db, const_id, subs);
+                            let subst = GenericSubstitution::new(
+                                konst.into(),
+                                subst,
+                                self.trait_environment(db),
+                            );
+                            (konst.into(), subst)
                         }
-                        assoc => assoc,
+                        AssocItemId::TypeAliasId(type_alias) => (
+                            assoc,
+                            GenericSubstitution::new(
+                                type_alias.into(),
+                                subs,
+                                self.trait_environment(db),
+                            ),
+                        ),
                     };
 
-                    return Some(PathResolution::Def(AssocItem::from(assoc).into()));
+                    return Some((PathResolution::Def(AssocItem::from(assoc).into()), Some(subst)));
                 }
                 if let Some(VariantId::EnumVariantId(variant)) =
                     infer.variant_resolution_for_expr_or_pat(expr_id)
                 {
-                    return Some(PathResolution::Def(ModuleDef::Variant(variant.into())));
+                    return Some((PathResolution::Def(ModuleDef::Variant(variant.into())), None));
                 }
                 prefer_value_ns = true;
             } else if let Some(path_pat) = parent().and_then(ast::PathPat::cast) {
                 let pat_id = self.pat_id(&path_pat.into())?;
                 if let Some((assoc, subs)) = infer.assoc_resolutions_for_pat(pat_id) {
-                    let assoc = match assoc {
+                    let (assoc, subst) = match assoc {
                         AssocItemId::ConstId(const_id) => {
-                            self.resolve_impl_const_or_trait_def(db, const_id, subs).into()
+                            let (konst, subst) =
+                                self.resolve_impl_const_or_trait_def_with_subst(db, const_id, subs);
+                            let subst = GenericSubstitution::new(
+                                konst.into(),
+                                subst,
+                                self.trait_environment(db),
+                            );
+                            (konst.into(), subst)
                         }
-                        assoc => assoc,
+                        assoc => (
+                            assoc,
+                            GenericSubstitution::new(
+                                assoc.into(),
+                                subs,
+                                self.trait_environment(db),
+                            ),
+                        ),
                     };
-                    return Some(PathResolution::Def(AssocItem::from(assoc).into()));
+                    return Some((PathResolution::Def(AssocItem::from(assoc).into()), Some(subst)));
                 }
                 if let Some(VariantId::EnumVariantId(variant)) =
                     infer.variant_resolution_for_pat(pat_id)
                 {
-                    return Some(PathResolution::Def(ModuleDef::Variant(variant.into())));
+                    return Some((PathResolution::Def(ModuleDef::Variant(variant.into())), None));
                 }
             } else if let Some(rec_lit) = parent().and_then(ast::RecordExpr::cast) {
                 let expr_id = self.expr_id(db, &rec_lit.into())?;
                 if let Some(VariantId::EnumVariantId(variant)) =
                     infer.variant_resolution_for_expr_or_pat(expr_id)
                 {
-                    return Some(PathResolution::Def(ModuleDef::Variant(variant.into())));
+                    return Some((PathResolution::Def(ModuleDef::Variant(variant.into())), None));
                 }
             } else {
                 let record_pat = parent().and_then(ast::RecordPat::cast).map(ast::Pat::from);
@@ -727,7 +826,10 @@ impl SourceAnalyzer {
                     let pat_id = self.pat_id(&pat)?;
                     let variant_res_for_pat = infer.variant_resolution_for_pat(pat_id);
                     if let Some(VariantId::EnumVariantId(variant)) = variant_res_for_pat {
-                        return Some(PathResolution::Def(ModuleDef::Variant(variant.into())));
+                        return Some((
+                            PathResolution::Def(ModuleDef::Variant(variant.into())),
+                            None,
+                        ));
                     }
                 }
             }
@@ -747,7 +849,8 @@ impl SourceAnalyzer {
         // trying to resolve foo::bar.
         if let Some(use_tree) = parent().and_then(ast::UseTree::cast) {
             if use_tree.coloncolon_token().is_some() {
-                return resolve_hir_path_qualifier(db, &self.resolver, &hir_path, &types_map);
+                return resolve_hir_path_qualifier(db, &self.resolver, &hir_path, &types_map)
+                    .map(|it| (it, None));
             }
         }
 
@@ -765,13 +868,18 @@ impl SourceAnalyzer {
         // trying to resolve foo::bar.
         if path.parent_path().is_some() {
             return match resolve_hir_path_qualifier(db, &self.resolver, &hir_path, &types_map) {
-                None if meta_path.is_some() => {
-                    path.first_segment().and_then(|it| it.name_ref()).and_then(|name_ref| {
+                None if meta_path.is_some() => path
+                    .first_segment()
+                    .and_then(|it| it.name_ref())
+                    .and_then(|name_ref| {
                         ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text())
                             .map(PathResolution::ToolModule)
                     })
-                }
-                res => res,
+                    .map(|it| (it, None)),
+                // FIXME: We do not show substitutions for parts of path, because this is really complex
+                // due to the interactions with associated items of `impl`s and associated items of associated
+                // types.
+                res => res.map(|it| (it, None)),
             };
         } else if let Some(meta_path) = meta_path {
             // Case where we are resolving the final path segment of a path in an attribute
@@ -781,7 +889,7 @@ impl SourceAnalyzer {
                 let builtin =
                     BuiltinAttr::by_name(db, self.resolver.krate().into(), &name_ref.text());
                 if builtin.is_some() {
-                    return builtin.map(PathResolution::BuiltinAttr);
+                    return builtin.map(|it| (PathResolution::BuiltinAttr(it), None));
                 }
 
                 if let Some(attr) = meta_path.parent_attr() {
@@ -814,10 +922,13 @@ impl SourceAnalyzer {
                             {
                                 if let Some(idx) = helpers.position(|(name, ..)| *name == name_ref)
                                 {
-                                    return Some(PathResolution::DeriveHelper(DeriveHelper {
-                                        derive: *macro_id,
-                                        idx: idx as u32,
-                                    }));
+                                    return Some((
+                                        PathResolution::DeriveHelper(DeriveHelper {
+                                            derive: *macro_id,
+                                            idx: idx as u32,
+                                        }),
+                                        None,
+                                    ));
                                 }
                             }
                         }
@@ -825,26 +936,79 @@ impl SourceAnalyzer {
                 }
             }
             return match resolve_hir_path_as_attr_macro(db, &self.resolver, &hir_path) {
-                Some(m) => Some(PathResolution::Def(ModuleDef::Macro(m))),
+                Some(m) => Some((PathResolution::Def(ModuleDef::Macro(m)), None)),
                 // this labels any path that starts with a tool module as the tool itself, this is technically wrong
                 // but there is no benefit in differentiating these two cases for the time being
-                None => path.first_segment().and_then(|it| it.name_ref()).and_then(|name_ref| {
-                    ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text())
-                        .map(PathResolution::ToolModule)
-                }),
+                None => path
+                    .first_segment()
+                    .and_then(|it| it.name_ref())
+                    .and_then(|name_ref| {
+                        ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text())
+                            .map(PathResolution::ToolModule)
+                    })
+                    .map(|it| (it, None)),
             };
         }
         if parent().map_or(false, |it| ast::Visibility::can_cast(it.kind())) {
+            // No substitution because only modules can be inside visibilities, and those have no generics.
             resolve_hir_path_qualifier(db, &self.resolver, &hir_path, &types_map)
+                .map(|it| (it, None))
         } else {
-            resolve_hir_path_(
+            // Probably a type, no need to show substitutions for those.
+            let res = resolve_hir_path_(
                 db,
                 &self.resolver,
                 &hir_path,
                 prefer_value_ns,
                 name_hygiene(db, InFile::new(self.file_id, path.syntax())),
                 &types_map,
-            )
+            )?;
+            let subst = (|| {
+                let parent = parent()?;
+                let ty = if let Some(expr) = ast::Expr::cast(parent.clone()) {
+                    let expr_id = self.expr_id(db, &expr)?;
+                    self.infer.as_ref()?.type_of_expr_or_pat(expr_id)?
+                } else if let Some(pat) = ast::Pat::cast(parent) {
+                    let pat_id = self.pat_id(&pat)?;
+                    &self.infer.as_ref()?[pat_id]
+                } else {
+                    return None;
+                };
+                let env = self.trait_environment(db);
+                let (subst, expected_resolution) = match ty.kind(Interner) {
+                    TyKind::Adt(adt_id, subst) => (
+                        GenericSubstitution::new(adt_id.0.into(), subst.clone(), env),
+                        PathResolution::Def(ModuleDef::Adt(adt_id.0.into())),
+                    ),
+                    TyKind::AssociatedType(assoc_id, subst) => {
+                        let assoc_id = from_assoc_type_id(*assoc_id);
+                        (
+                            GenericSubstitution::new(assoc_id.into(), subst.clone(), env),
+                            PathResolution::Def(ModuleDef::TypeAlias(assoc_id.into())),
+                        )
+                    }
+                    TyKind::FnDef(fn_id, subst) => {
+                        let fn_id = hir_ty::db::InternedCallableDefId::from(*fn_id);
+                        let fn_id = db.lookup_intern_callable_def(fn_id);
+                        let generic_def_id = match fn_id {
+                            CallableDefId::StructId(id) => id.into(),
+                            CallableDefId::FunctionId(id) => id.into(),
+                            CallableDefId::EnumVariantId(_) => return None,
+                        };
+                        (
+                            GenericSubstitution::new(generic_def_id, subst.clone(), env),
+                            PathResolution::Def(ModuleDefId::from(fn_id).into()),
+                        )
+                    }
+                    _ => return None,
+                };
+                if res != expected_resolution {
+                    // The user will not understand where we're coming from. This can happen (I think) with type aliases.
+                    return None;
+                }
+                Some(subst)
+            })();
+            Some((res, subst))
         }
     }
 
@@ -1041,26 +1205,35 @@ impl SourceAnalyzer {
         func: FunctionId,
         substs: Substitution,
     ) -> FunctionId {
+        self.resolve_impl_method_or_trait_def_with_subst(db, func, substs).0
+    }
+
+    fn resolve_impl_method_or_trait_def_with_subst(
+        &self,
+        db: &dyn HirDatabase,
+        func: FunctionId,
+        substs: Substitution,
+    ) -> (FunctionId, Substitution) {
         let owner = match self.resolver.body_owner() {
             Some(it) => it,
-            None => return func,
+            None => return (func, substs),
         };
         let env = db.trait_environment_for_body(owner);
-        db.lookup_impl_method(env, func, substs).0
+        db.lookup_impl_method(env, func, substs)
     }
 
-    fn resolve_impl_const_or_trait_def(
+    fn resolve_impl_const_or_trait_def_with_subst(
         &self,
         db: &dyn HirDatabase,
         const_id: ConstId,
         subs: Substitution,
-    ) -> ConstId {
+    ) -> (ConstId, Substitution) {
         let owner = match self.resolver.body_owner() {
             Some(it) => it,
-            None => return const_id,
+            None => return (const_id, subs),
         };
         let env = db.trait_environment_for_body(owner);
-        method_resolution::lookup_impl_const(db, env, const_id, subs).0
+        method_resolution::lookup_impl_const(db, env, const_id, subs)
     }
 
     fn lang_trait_fn(
@@ -1413,3 +1586,10 @@ pub(crate) fn name_hygiene(db: &dyn HirDatabase, name: InFile<&SyntaxNode>) -> H
     let ctx = db.lookup_intern_syntax_context(ctx);
     HygieneId::new(ctx.opaque_and_semitransparent)
 }
+
+fn type_of_expr_including_adjust(infer: &InferenceResult, id: ExprId) -> Option<&Ty> {
+    match infer.expr_adjustments.get(&id).and_then(|adjustments| adjustments.last()) {
+        Some(adjustment) => Some(&adjustment.target),
+        None => infer.type_of_expr.get(id),
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs
index 0f6970d9403..62700ab1809 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs
@@ -69,7 +69,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
     let ident = name_ref.ident_token()?;
 
     let def = match NameRefClass::classify(&ctx.sema, &name_ref)? {
-        NameRefClass::Definition(def) => def,
+        NameRefClass::Definition(def, _) => def,
         NameRefClass::FieldShorthand { .. } | NameRefClass::ExternCrateShorthand { .. } => {
             return None
         }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
index c7f41ffce04..fd159eb824d 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
@@ -103,7 +103,7 @@ fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Opti
         ast::Expr::PathExpr(path) => {
             let name_ref = path.syntax().descendants().find_map(ast::NameRef::cast)?;
             match NameRefClass::classify(&ctx.sema, &name_ref)? {
-                NameRefClass::Definition(Definition::Local(local)) => {
+                NameRefClass::Definition(Definition::Local(local), _) => {
                     let source =
                         local.sources(ctx.db()).into_iter().map(|x| x.into_ident_pat()?.name());
                     source.collect()
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
index 83f4a6b123c..3c84f83906a 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
@@ -163,8 +163,8 @@ fn edit_struct_references(
                     // this also includes method calls like Foo::new(42), we should skip them
                     if let Some(name_ref) = path.segment().and_then(|s| s.name_ref()) {
                         match NameRefClass::classify(&ctx.sema, &name_ref) {
-                            Some(NameRefClass::Definition(Definition::SelfType(_))) => {},
-                            Some(NameRefClass::Definition(def)) if def == strukt_def => {},
+                            Some(NameRefClass::Definition(Definition::SelfType(_), _)) => {},
+                            Some(NameRefClass::Definition(def, _)) if def == strukt_def => {},
                             _ => return None,
                         };
                     }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs
index 3d6d37ad93d..094fdc46eb7 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs
@@ -275,6 +275,7 @@ fn find_imported_defs(ctx: &AssistContext<'_>, star: SyntaxToken) -> Option<Vec<
                     | Definition::Static(_)
                     | Definition::Trait(_)
                     | Definition::TypeAlias(_)),
+                    _,
                 ) => Some(def),
                 _ => None,
             })
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
index 438769a0a87..f9e54ca35c2 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
@@ -800,8 +800,8 @@ impl FunctionBody {
             let local_ref =
                 match name_ref.and_then(|name_ref| NameRefClass::classify(sema, &name_ref)) {
                     Some(
-                        NameRefClass::Definition(Definition::Local(local_ref))
-                        | NameRefClass::FieldShorthand { local_ref, field_ref: _ },
+                        NameRefClass::Definition(Definition::Local(local_ref), _)
+                        | NameRefClass::FieldShorthand { local_ref, field_ref: _, adt_subst: _ },
                     ) => local_ref,
                     _ => return,
                 };
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs
index e4cba666af7..6e3be0ce692 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs
@@ -425,7 +425,9 @@ impl Module {
                         })
                     } else if let Some(name_ref) = ast::NameRef::cast(x) {
                         NameRefClass::classify(&ctx.sema, &name_ref).and_then(|nc| match nc {
-                            NameRefClass::Definition(def) => Some((name_ref.syntax().clone(), def)),
+                            NameRefClass::Definition(def, _) => {
+                                Some((name_ref.syntax().clone(), def))
+                            }
                             _ => None,
                         })
                     } else {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs
index 25076dd5255..7f7db07152d 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs
@@ -64,7 +64,7 @@ pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
         let name_ref_value = name_ref?;
         let name_ref_class = NameRefClass::classify(&ctx.sema, &name_ref_value);
         match name_ref_class {
-            Some(NameRefClass::Definition(Definition::Module(m))) => {
+            Some(NameRefClass::Definition(Definition::Module(m), _)) => {
                 if !m.visibility(ctx.sema.db).is_visible_from(ctx.sema.db, constant_module.into()) {
                     return None;
                 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
index 7b95c124e62..8f5daa4125a 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
@@ -1077,7 +1077,7 @@ fn fn_arg_name(sema: &Semantics<'_, RootDatabase>, arg_expr: &ast::Expr) -> Stri
                 .filter_map(ast::NameRef::cast)
                 .filter(|name| name.ident_token().is_some())
                 .last()?;
-            if let Some(NameRefClass::Definition(Definition::Const(_) | Definition::Static(_))) =
+            if let Some(NameRefClass::Definition(Definition::Const(_) | Definition::Static(_), _)) =
                 NameRefClass::classify(sema, &name_ref)
             {
                 return Some(name_ref.to_string().to_lowercase());
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
index 932ca373020..73b73736ce8 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
@@ -13,9 +13,10 @@ use either::Either;
 use hir::{
     Adt, AsAssocItem, AsExternAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType,
     Const, Crate, DefWithBody, DeriveHelper, DocLinkDef, ExternAssocItem, ExternCrateDecl, Field,
-    Function, GenericParam, HasVisibility, HirDisplay, Impl, InlineAsmOperand, Label, Local, Macro,
-    Module, ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, Struct, ToolModule,
-    Trait, TraitAlias, TupleField, TypeAlias, Variant, VariantDef, Visibility,
+    Function, GenericParam, GenericSubstitution, HasVisibility, HirDisplay, Impl, InlineAsmOperand,
+    Label, Local, Macro, Module, ModuleDef, Name, PathResolution, Semantics, Static,
+    StaticLifetime, Struct, ToolModule, Trait, TraitAlias, TupleField, TypeAlias, Variant,
+    VariantDef, Visibility,
 };
 use span::Edition;
 use stdx::{format_to, impl_from};
@@ -359,24 +360,32 @@ impl IdentClass {
             .or_else(|| NameClass::classify_lifetime(sema, lifetime).map(IdentClass::NameClass))
     }
 
-    pub fn definitions(self) -> ArrayVec<Definition, 2> {
+    pub fn definitions(self) -> ArrayVec<(Definition, Option<GenericSubstitution>), 2> {
         let mut res = ArrayVec::new();
         match self {
             IdentClass::NameClass(NameClass::Definition(it) | NameClass::ConstReference(it)) => {
-                res.push(it)
+                res.push((it, None))
             }
-            IdentClass::NameClass(NameClass::PatFieldShorthand { local_def, field_ref }) => {
-                res.push(Definition::Local(local_def));
-                res.push(Definition::Field(field_ref));
+            IdentClass::NameClass(NameClass::PatFieldShorthand {
+                local_def,
+                field_ref,
+                adt_subst,
+            }) => {
+                res.push((Definition::Local(local_def), None));
+                res.push((Definition::Field(field_ref), Some(adt_subst)));
             }
-            IdentClass::NameRefClass(NameRefClass::Definition(it)) => res.push(it),
-            IdentClass::NameRefClass(NameRefClass::FieldShorthand { local_ref, field_ref }) => {
-                res.push(Definition::Local(local_ref));
-                res.push(Definition::Field(field_ref));
+            IdentClass::NameRefClass(NameRefClass::Definition(it, subst)) => res.push((it, subst)),
+            IdentClass::NameRefClass(NameRefClass::FieldShorthand {
+                local_ref,
+                field_ref,
+                adt_subst,
+            }) => {
+                res.push((Definition::Local(local_ref), None));
+                res.push((Definition::Field(field_ref), Some(adt_subst)));
             }
             IdentClass::NameRefClass(NameRefClass::ExternCrateShorthand { decl, krate }) => {
-                res.push(Definition::ExternCrateDecl(decl));
-                res.push(Definition::Module(krate.root_module()));
+                res.push((Definition::ExternCrateDecl(decl), None));
+                res.push((Definition::Module(krate.root_module()), None));
             }
             IdentClass::Operator(
                 OperatorClass::Await(func)
@@ -384,9 +393,9 @@ impl IdentClass {
                 | OperatorClass::Bin(func)
                 | OperatorClass::Index(func)
                 | OperatorClass::Try(func),
-            ) => res.push(Definition::Function(func)),
+            ) => res.push((Definition::Function(func), None)),
             IdentClass::Operator(OperatorClass::Range(struct0)) => {
-                res.push(Definition::Adt(Adt::Struct(struct0)))
+                res.push((Definition::Adt(Adt::Struct(struct0)), None))
             }
         }
         res
@@ -398,12 +407,20 @@ impl IdentClass {
             IdentClass::NameClass(NameClass::Definition(it) | NameClass::ConstReference(it)) => {
                 res.push(it)
             }
-            IdentClass::NameClass(NameClass::PatFieldShorthand { local_def, field_ref }) => {
+            IdentClass::NameClass(NameClass::PatFieldShorthand {
+                local_def,
+                field_ref,
+                adt_subst: _,
+            }) => {
                 res.push(Definition::Local(local_def));
                 res.push(Definition::Field(field_ref));
             }
-            IdentClass::NameRefClass(NameRefClass::Definition(it)) => res.push(it),
-            IdentClass::NameRefClass(NameRefClass::FieldShorthand { local_ref, field_ref }) => {
+            IdentClass::NameRefClass(NameRefClass::Definition(it, _)) => res.push(it),
+            IdentClass::NameRefClass(NameRefClass::FieldShorthand {
+                local_ref,
+                field_ref,
+                adt_subst: _,
+            }) => {
                 res.push(Definition::Local(local_ref));
                 res.push(Definition::Field(field_ref));
             }
@@ -437,6 +454,7 @@ pub enum NameClass {
     PatFieldShorthand {
         local_def: Local,
         field_ref: Field,
+        adt_subst: GenericSubstitution,
     },
 }
 
@@ -446,7 +464,7 @@ impl NameClass {
         let res = match self {
             NameClass::Definition(it) => it,
             NameClass::ConstReference(_) => return None,
-            NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
+            NameClass::PatFieldShorthand { local_def, field_ref: _, adt_subst: _ } => {
                 Definition::Local(local_def)
             }
         };
@@ -517,10 +535,13 @@ impl NameClass {
             let pat_parent = ident_pat.syntax().parent();
             if let Some(record_pat_field) = pat_parent.and_then(ast::RecordPatField::cast) {
                 if record_pat_field.name_ref().is_none() {
-                    if let Some((field, _)) = sema.resolve_record_pat_field(&record_pat_field) {
+                    if let Some((field, _, adt_subst)) =
+                        sema.resolve_record_pat_field_with_subst(&record_pat_field)
+                    {
                         return Some(NameClass::PatFieldShorthand {
                             local_def: local,
                             field_ref: field,
+                            adt_subst,
                         });
                     }
                 }
@@ -629,10 +650,11 @@ impl OperatorClass {
 /// reference to point to two different defs.
 #[derive(Debug)]
 pub enum NameRefClass {
-    Definition(Definition),
+    Definition(Definition, Option<GenericSubstitution>),
     FieldShorthand {
         local_ref: Local,
         field_ref: Field,
+        adt_subst: GenericSubstitution,
     },
     /// The specific situation where we have an extern crate decl without a rename
     /// Here we have both a declaration and a reference.
@@ -657,12 +679,16 @@ impl NameRefClass {
         let parent = name_ref.syntax().parent()?;
 
         if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) {
-            if let Some((field, local, _)) = sema.resolve_record_field(&record_field) {
+            if let Some((field, local, _, adt_subst)) =
+                sema.resolve_record_field_with_substitution(&record_field)
+            {
                 let res = match local {
-                    None => NameRefClass::Definition(Definition::Field(field)),
-                    Some(local) => {
-                        NameRefClass::FieldShorthand { field_ref: field, local_ref: local }
-                    }
+                    None => NameRefClass::Definition(Definition::Field(field), Some(adt_subst)),
+                    Some(local) => NameRefClass::FieldShorthand {
+                        field_ref: field,
+                        local_ref: local,
+                        adt_subst,
+                    },
                 };
                 return Some(res);
             }
@@ -674,44 +700,43 @@ impl NameRefClass {
                     // Only use this to resolve to macro calls for last segments as qualifiers resolve
                     // to modules below.
                     if let Some(macro_def) = sema.resolve_macro_call(&macro_call) {
-                        return Some(NameRefClass::Definition(Definition::Macro(macro_def)));
+                        return Some(NameRefClass::Definition(Definition::Macro(macro_def), None));
                     }
                 }
             }
-            return sema.resolve_path(&path).map(Into::into).map(NameRefClass::Definition);
+            return sema
+                .resolve_path_with_subst(&path)
+                .map(|(res, subst)| NameRefClass::Definition(res.into(), subst));
         }
 
         match_ast! {
             match parent {
                 ast::MethodCallExpr(method_call) => {
                     sema.resolve_method_call_fallback(&method_call)
-                        .map(|it| {
-                            it.map_left(Definition::Function)
-                                .map_right(Definition::Field)
-                                .either(NameRefClass::Definition, NameRefClass::Definition)
+                        .map(|(def, subst)| {
+                            match def {
+                                Either::Left(def) => NameRefClass::Definition(def.into(), subst),
+                                Either::Right(def) => NameRefClass::Definition(def.into(), subst),
+                            }
                         })
                 },
                 ast::FieldExpr(field_expr) => {
                     sema.resolve_field_fallback(&field_expr)
-                    .map(|it| {
-                        NameRefClass::Definition(match it {
-                            Either::Left(Either::Left(field)) => Definition::Field(field),
-                            Either::Left(Either::Right(field)) => Definition::TupleField(field),
-                            Either::Right(fun) => Definition::Function(fun),
+                        .map(|(def, subst)| {
+                            match def {
+                                Either::Left(Either::Left(def)) => NameRefClass::Definition(def.into(), subst),
+                                Either::Left(Either::Right(def)) => NameRefClass::Definition(Definition::TupleField(def), subst),
+                                Either::Right(def) => NameRefClass::Definition(def.into(), subst),
+                            }
                         })
-                    })
                 },
                 ast::RecordPatField(record_pat_field) => {
-                    sema.resolve_record_pat_field(&record_pat_field)
-                        .map(|(field, ..)|field)
-                        .map(Definition::Field)
-                        .map(NameRefClass::Definition)
+                    sema.resolve_record_pat_field_with_subst(&record_pat_field)
+                        .map(|(field, _, subst)| NameRefClass::Definition(Definition::Field(field), Some(subst)))
                 },
                 ast::RecordExprField(record_expr_field) => {
-                    sema.resolve_record_field(&record_expr_field)
-                        .map(|(field, ..)|field)
-                        .map(Definition::Field)
-                        .map(NameRefClass::Definition)
+                    sema.resolve_record_field_with_substitution(&record_expr_field)
+                        .map(|(field, _, _, subst)| NameRefClass::Definition(Definition::Field(field), Some(subst)))
                 },
                 ast::AssocTypeArg(_) => {
                     // `Trait<Assoc = Ty>`
@@ -728,28 +753,30 @@ impl NameRefClass {
                             })
                             .find(|alias| alias.name(sema.db).eq_ident(name_ref.text().as_str()))
                         {
-                            return Some(NameRefClass::Definition(Definition::TypeAlias(ty)));
+                            // No substitution, this can only occur in type position.
+                            return Some(NameRefClass::Definition(Definition::TypeAlias(ty), None));
                         }
                     }
                     None
                 },
                 ast::UseBoundGenericArgs(_) => {
+                    // No substitution, this can only occur in type position.
                     sema.resolve_use_type_arg(name_ref)
                         .map(GenericParam::TypeParam)
                         .map(Definition::GenericParam)
-                        .map(NameRefClass::Definition)
+                        .map(|it| NameRefClass::Definition(it, None))
                 },
                 ast::ExternCrate(extern_crate_ast) => {
                     let extern_crate = sema.to_def(&extern_crate_ast)?;
                     let krate = extern_crate.resolved_crate(sema.db)?;
                     Some(if extern_crate_ast.rename().is_some() {
-                        NameRefClass::Definition(Definition::Module(krate.root_module()))
+                        NameRefClass::Definition(Definition::Module(krate.root_module()), None)
                     } else {
                         NameRefClass::ExternCrateShorthand { krate, decl: extern_crate }
                     })
                 },
                 ast::AsmRegSpec(_) => {
-                    Some(NameRefClass::Definition(Definition::InlineAsmRegOrRegClass(())))
+                    Some(NameRefClass::Definition(Definition::InlineAsmRegOrRegClass(()), None))
                 },
                 _ => None
             }
@@ -762,13 +789,17 @@ impl NameRefClass {
     ) -> Option<NameRefClass> {
         let _p = tracing::info_span!("NameRefClass::classify_lifetime", ?lifetime).entered();
         if lifetime.text() == "'static" {
-            return Some(NameRefClass::Definition(Definition::BuiltinLifetime(StaticLifetime)));
+            return Some(NameRefClass::Definition(
+                Definition::BuiltinLifetime(StaticLifetime),
+                None,
+            ));
         }
         let parent = lifetime.syntax().parent()?;
         match parent.kind() {
-            SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => {
-                sema.resolve_label(lifetime).map(Definition::Label).map(NameRefClass::Definition)
-            }
+            SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => sema
+                .resolve_label(lifetime)
+                .map(Definition::Label)
+                .map(|it| NameRefClass::Definition(it, None)),
             SyntaxKind::LIFETIME_ARG
             | SyntaxKind::USE_BOUND_GENERIC_ARGS
             | SyntaxKind::SELF_PARAM
@@ -778,7 +809,7 @@ impl NameRefClass {
                 .resolve_lifetime_param(lifetime)
                 .map(GenericParam::LifetimeParam)
                 .map(Definition::GenericParam)
-                .map(NameRefClass::Definition),
+                .map(|it| NameRefClass::Definition(it, None)),
             _ => None,
         }
     }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
index c5215eb3e63..24b4d26ec72 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
@@ -1081,7 +1081,7 @@ impl<'a> FindUsages<'a> {
         };
 
         match NameRefClass::classify(self.sema, name_ref) {
-            Some(NameRefClass::Definition(Definition::SelfType(impl_)))
+            Some(NameRefClass::Definition(Definition::SelfType(impl_), _))
                 if ty_eq(impl_.self_ty(self.sema.db)) =>
             {
                 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
@@ -1102,7 +1102,7 @@ impl<'a> FindUsages<'a> {
         sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
     ) -> bool {
         match NameRefClass::classify(self.sema, name_ref) {
-            Some(NameRefClass::Definition(def @ Definition::Module(_))) if def == self.def => {
+            Some(NameRefClass::Definition(def @ Definition::Module(_), _)) if def == self.def => {
                 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
                 let category = if is_name_ref_in_import(name_ref) {
                     ReferenceCategory::IMPORT
@@ -1147,7 +1147,7 @@ impl<'a> FindUsages<'a> {
         sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
     ) -> bool {
         match NameRefClass::classify_lifetime(self.sema, lifetime) {
-            Some(NameRefClass::Definition(def)) if def == self.def => {
+            Some(NameRefClass::Definition(def, _)) if def == self.def => {
                 let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax());
                 let reference = FileReference {
                     range,
@@ -1166,7 +1166,7 @@ impl<'a> FindUsages<'a> {
         sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
     ) -> bool {
         match NameRefClass::classify(self.sema, name_ref) {
-            Some(NameRefClass::Definition(def))
+            Some(NameRefClass::Definition(def, _))
                 if self.def == def
                     // is our def a trait assoc item? then we want to find all assoc items from trait impls of our trait
                     || matches!(self.assoc_item_container, Some(hir::AssocItemContainer::Trait(_)))
@@ -1182,7 +1182,7 @@ impl<'a> FindUsages<'a> {
             }
             // FIXME: special case type aliases, we can't filter between impl and trait defs here as we lack the substitutions
             // so we always resolve all assoc type aliases to both their trait def and impl defs
-            Some(NameRefClass::Definition(def))
+            Some(NameRefClass::Definition(def, _))
                 if self.assoc_item_container.is_some()
                     && matches!(self.def, Definition::TypeAlias(_))
                     && convert_to_def_in_trait(self.sema.db, def)
@@ -1196,7 +1196,7 @@ impl<'a> FindUsages<'a> {
                 };
                 sink(file_id, reference)
             }
-            Some(NameRefClass::Definition(def)) if self.include_self_kw_refs.is_some() => {
+            Some(NameRefClass::Definition(def, _)) if self.include_self_kw_refs.is_some() => {
                 if self.include_self_kw_refs == def_to_ty(self.sema, &def) {
                     let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
                     let reference = FileReference {
@@ -1209,7 +1209,11 @@ impl<'a> FindUsages<'a> {
                     false
                 }
             }
-            Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => {
+            Some(NameRefClass::FieldShorthand {
+                local_ref: local,
+                field_ref: field,
+                adt_subst: _,
+            }) => {
                 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
 
                 let field = Definition::Field(field);
@@ -1240,7 +1244,7 @@ impl<'a> FindUsages<'a> {
         sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
     ) -> bool {
         match NameClass::classify(self.sema, name) {
-            Some(NameClass::PatFieldShorthand { local_def: _, field_ref })
+            Some(NameClass::PatFieldShorthand { local_def: _, field_ref, adt_subst: _ })
                 if matches!(
                     self.def, Definition::Field(_) if Definition::Field(field_ref) == self.def
                 ) =>
diff --git a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
index e5b4ed17b2a..8066894cd83 100644
--- a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
@@ -47,7 +47,7 @@ pub(crate) fn incoming_calls(
         .find_nodes_at_offset_with_descend(file, offset)
         .filter_map(move |node| match node {
             ast::NameLike::NameRef(name_ref) => match NameRefClass::classify(sema, &name_ref)? {
-                NameRefClass::Definition(def @ Definition::Function(_)) => Some(def),
+                NameRefClass::Definition(def @ Definition::Function(_), _) => Some(def),
                 _ => None,
             },
             ast::NameLike::Name(name) => match NameClass::classify(sema, &name)? {
diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
index ea16a11d56d..72fcac54177 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
@@ -147,8 +147,8 @@ pub(crate) fn external_docs(
     let definition = match_ast! {
         match node {
             ast::NameRef(name_ref) => match NameRefClass::classify(sema, &name_ref)? {
-                NameRefClass::Definition(def) => def,
-                NameRefClass::FieldShorthand { local_ref: _, field_ref } => {
+                NameRefClass::Definition(def, _) => def,
+                NameRefClass::FieldShorthand { local_ref: _, field_ref, adt_subst: _ } => {
                     Definition::Field(field_ref)
                 }
                 NameRefClass::ExternCrateShorthand { decl, .. } => {
@@ -157,7 +157,7 @@ pub(crate) fn external_docs(
             },
             ast::Name(name) => match NameClass::classify(sema, &name)? {
                 NameClass::Definition(it) | NameClass::ConstReference(it) => it,
-                NameClass::PatFieldShorthand { local_def: _, field_ref } => Definition::Field(field_ref),
+                NameClass::PatFieldShorthand { local_def: _, field_ref, adt_subst: _ } => Definition::Field(field_ref),
             },
             _ => return None
         }
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs
index 9dacbd8badf..7b6a5ef13e5 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs
@@ -36,7 +36,7 @@ pub(crate) fn goto_declaration(
             let def = match_ast! {
                 match parent {
                     ast::NameRef(name_ref) => match NameRefClass::classify(&sema, &name_ref)? {
-                        NameRefClass::Definition(it) => Some(it),
+                        NameRefClass::Definition(it, _) => Some(it),
                         NameRefClass::FieldShorthand { field_ref, .. } =>
                             return field_ref.try_to_nav(db),
                         NameRefClass::ExternCrateShorthand { decl, .. } =>
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
index 363f852e0e4..6c66907ec3e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
@@ -103,7 +103,7 @@ pub(crate) fn goto_definition(
                 IdentClass::classify_node(sema, &parent)?
                     .definitions()
                     .into_iter()
-                    .flat_map(|def| {
+                    .flat_map(|(def, _)| {
                         if let Definition::ExternCrateDecl(crate_def) = def {
                             return crate_def
                                 .resolved_crate(db)
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs
index e36c8ee2f3f..04da1f67e95 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs
@@ -48,7 +48,7 @@ pub(crate) fn goto_implementation(
                     }
                     ast::NameLike::NameRef(name_ref) => NameRefClass::classify(&sema, name_ref)
                         .and_then(|class| match class {
-                            NameRefClass::Definition(def) => Some(def),
+                            NameRefClass::Definition(def, _) => Some(def),
                             NameRefClass::FieldShorthand { .. }
                             | NameRefClass::ExternCrateShorthand { .. } => None,
                         }),
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs
index 332dfacbb43..e3d50fdfa90 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs
@@ -6,7 +6,7 @@ mod tests;
 use std::{iter, ops::Not};
 
 use either::Either;
-use hir::{db::DefDatabase, HasCrate, HasSource, LangItem, Semantics};
+use hir::{db::DefDatabase, GenericSubstitution, HasCrate, HasSource, LangItem, Semantics};
 use ide_db::{
     defs::{Definition, IdentClass, NameRefClass, OperatorClass},
     famous_defs::FamousDefs,
@@ -35,6 +35,14 @@ pub struct HoverConfig {
     pub max_trait_assoc_items_count: Option<usize>,
     pub max_fields_count: Option<usize>,
     pub max_enum_variants_count: Option<usize>,
+    pub max_subst_ty_len: SubstTyLen,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum SubstTyLen {
+    Unlimited,
+    LimitTo(usize),
+    Hide,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -158,7 +166,8 @@ fn hover_offset(
     if let Some(doc_comment) = token_as_doc_comment(&original_token) {
         cov_mark::hit!(no_highlight_on_comment_hover);
         return doc_comment.get_definition_with_descend_at(sema, offset, |def, node, range| {
-            let res = hover_for_definition(sema, file_id, def, &node, None, false, config, edition);
+            let res =
+                hover_for_definition(sema, file_id, def, None, &node, None, false, config, edition);
             Some(RangeInfo::new(range, res))
         });
     }
@@ -170,6 +179,7 @@ fn hover_offset(
             sema,
             file_id,
             Definition::from(resolution?),
+            None,
             &original_token.parent()?,
             None,
             false,
@@ -217,7 +227,7 @@ fn hover_offset(
                             {
                                 if let Some(macro_) = sema.resolve_macro_call(&macro_call) {
                                     break 'a vec![(
-                                        Definition::Macro(macro_),
+                                        (Definition::Macro(macro_), None),
                                         sema.resolve_macro_call_arm(&macro_call),
                                         false,
                                         node,
@@ -236,7 +246,7 @@ fn hover_offset(
                             decl,
                             ..
                         }) => {
-                            vec![(Definition::ExternCrateDecl(decl), None, false, node)]
+                            vec![((Definition::ExternCrateDecl(decl), None), None, false, node)]
                         }
 
                         class => {
@@ -252,12 +262,13 @@ fn hover_offset(
                     }
                 }
                 .into_iter()
-                .unique_by(|&(def, _, _, _)| def)
-                .map(|(def, macro_arm, hovered_definition, node)| {
+                .unique_by(|&((def, _), _, _, _)| def)
+                .map(|((def, subst), macro_arm, hovered_definition, node)| {
                     hover_for_definition(
                         sema,
                         file_id,
                         def,
+                        subst,
                         &node,
                         macro_arm,
                         hovered_definition,
@@ -381,6 +392,7 @@ pub(crate) fn hover_for_definition(
     sema: &Semantics<'_, RootDatabase>,
     file_id: FileId,
     def: Definition,
+    subst: Option<GenericSubstitution>,
     scope_node: &SyntaxNode,
     macro_arm: Option<u32>,
     hovered_definition: bool,
@@ -408,6 +420,7 @@ pub(crate) fn hover_for_definition(
         _ => None,
     };
     let notable_traits = def_ty.map(|ty| notable_traits(db, &ty)).unwrap_or_default();
+    let subst_types = subst.map(|subst| subst.types(db));
 
     let markup = render::definition(
         sema.db,
@@ -416,6 +429,7 @@ pub(crate) fn hover_for_definition(
         &notable_traits,
         macro_arm,
         hovered_definition,
+        subst_types,
         config,
         edition,
     );
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
index e617d462ecd..8c5c27c47cb 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
@@ -5,7 +5,7 @@ use either::Either;
 use hir::{
     db::ExpandDatabase, Adt, AsAssocItem, AsExternAssocItem, AssocItemContainer, CaptureKind,
     DynCompatibilityViolation, HasCrate, HasSource, HirDisplay, Layout, LayoutError,
-    MethodViolationCode, Name, Semantics, Trait, Type, TypeInfo,
+    MethodViolationCode, Name, Semantics, Symbol, Trait, Type, TypeInfo,
 };
 use ide_db::{
     base_db::SourceDatabase,
@@ -27,7 +27,7 @@ use syntax::{algo, ast, match_ast, AstNode, AstToken, Direction, SyntaxToken, T}
 
 use crate::{
     doc_links::{remove_links, rewrite_links},
-    hover::{notable_traits, walk_and_push_ty},
+    hover::{notable_traits, walk_and_push_ty, SubstTyLen},
     interpret::render_const_eval_error,
     HoverAction, HoverConfig, HoverResult, Markup, MemoryLayoutHoverConfig,
     MemoryLayoutHoverRenderKind,
@@ -274,7 +274,7 @@ pub(super) fn keyword(
     let markup = process_markup(
         sema.db,
         Definition::Module(doc_owner),
-        &markup(Some(docs.into()), description, None, None),
+        &markup(Some(docs.into()), description, None, None, String::new()),
         config,
     );
     Some(HoverResult { markup, actions })
@@ -421,6 +421,7 @@ pub(super) fn definition(
     notable_traits: &[(Trait, Vec<(Option<Type>, Name)>)],
     macro_arm: Option<u32>,
     hovered_definition: bool,
+    subst_types: Option<Vec<(Symbol, Type)>>,
     config: &HoverConfig,
     edition: Edition,
 ) -> Markup {
@@ -604,7 +605,38 @@ pub(super) fn definition(
         desc.push_str(&value);
     }
 
-    markup(docs.map(Into::into), desc, extra.is_empty().not().then_some(extra), mod_path)
+    let subst_types = match config.max_subst_ty_len {
+        SubstTyLen::Hide => String::new(),
+        SubstTyLen::LimitTo(_) | SubstTyLen::Unlimited => {
+            let limit = if let SubstTyLen::LimitTo(limit) = config.max_subst_ty_len {
+                Some(limit)
+            } else {
+                None
+            };
+            subst_types
+                .map(|subst_type| {
+                    subst_type
+                        .iter()
+                        .filter(|(_, ty)| !ty.is_unknown())
+                        .format_with(", ", |(name, ty), fmt| {
+                            fmt(&format_args!(
+                                "`{name}` = `{}`",
+                                ty.display_truncated(db, limit, edition)
+                            ))
+                        })
+                        .to_string()
+                })
+                .unwrap_or_default()
+        }
+    };
+
+    markup(
+        docs.map(Into::into),
+        desc,
+        extra.is_empty().not().then_some(extra),
+        mod_path,
+        subst_types,
+    )
 }
 
 pub(super) fn literal(
@@ -872,6 +904,7 @@ fn markup(
     rust: String,
     extra: Option<String>,
     mod_path: Option<String>,
+    subst_types: String,
 ) -> Markup {
     let mut buf = String::new();
 
@@ -886,6 +919,10 @@ fn markup(
         buf.push_str(&extra);
     }
 
+    if !subst_types.is_empty() {
+        format_to!(buf, "\n___\n{subst_types}");
+    }
+
     if let Some(doc) = docs {
         format_to!(buf, "\n___\n\n{}", doc);
     }
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
index 50d0d4c5df6..cc926a5b568 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -20,6 +20,7 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig {
     max_trait_assoc_items_count: None,
     max_fields_count: Some(5),
     max_enum_variants_count: Some(5),
+    max_subst_ty_len: super::SubstTyLen::Unlimited,
 };
 
 fn check_hover_no_result(ra_fixture: &str) {
@@ -5176,6 +5177,10 @@ fn main() {
 
             ---
 
+            `Self` = `()`
+
+            ---
+
             false
         "#]],
     );
@@ -5208,6 +5213,10 @@ fn main() {
 
             ---
 
+            `Self` = `i32`
+
+            ---
+
             false
         "#]],
     );
@@ -9501,3 +9510,409 @@ fn main() {
         "#]],
     );
 }
+
+#[test]
+fn subst_fn() {
+    check(
+        r#"
+struct Foo<T>(T);
+impl<T> Foo<T> {
+    fn foo<U>(v: T, u: U) {}
+}
+
+fn bar() {
+    Foo::fo$0o(123, false);
+}
+        "#,
+        expect![[r#"
+            *foo*
+
+            ```rust
+            ra_test_fixture::Foo
+            ```
+
+            ```rust
+            impl<T> Foo<T>
+            fn foo<U>(v: T, u: U)
+            ```
+
+            ---
+
+            `T` = `i32`, `U` = `bool`
+        "#]],
+    );
+    check(
+        r#"
+fn foo<T>(v: T) {}
+
+fn bar() {
+    fo$0o(123);
+}
+        "#,
+        expect![[r#"
+            *foo*
+
+            ```rust
+            ra_test_fixture
+            ```
+
+            ```rust
+            fn foo<T>(v: T)
+            ```
+
+            ---
+
+            `T` = `i32`
+        "#]],
+    );
+}
+
+#[test]
+fn subst_record_constructor() {
+    check(
+        r#"
+struct Foo<T> { field: T }
+
+fn bar() {
+    let v = $0Foo { field: 123 };
+}
+        "#,
+        expect![[r#"
+            *Foo*
+
+            ```rust
+            ra_test_fixture
+            ```
+
+            ```rust
+            struct Foo<T> {
+                field: T,
+            }
+            ```
+
+            ---
+
+            `T` = `i32`
+        "#]],
+    );
+    check(
+        r#"
+struct Foo<T> { field: T }
+
+fn bar() {
+    let v = Foo { field: 123 };
+    let $0Foo { field: _ } = v;
+}
+        "#,
+        expect![[r#"
+            *Foo*
+
+            ```rust
+            ra_test_fixture
+            ```
+
+            ```rust
+            struct Foo<T> {
+                field: T,
+            }
+            ```
+
+            ---
+
+            `T` = `i32`
+        "#]],
+    );
+}
+
+#[test]
+fn subst_method_call() {
+    check(
+        r#"
+struct Foo<T>(T);
+
+impl<U> Foo<U> {
+    fn bar<T>(self, v: T) {}
+}
+
+fn baz() {
+    Foo(123).bar$0("hello");
+}
+    "#,
+        expect![[r#"
+            *bar*
+
+            ```rust
+            ra_test_fixture::Foo
+            ```
+
+            ```rust
+            impl<U> Foo<U>
+            fn bar<T>(self, v: T)
+            ```
+
+            ---
+
+            `U` = `i32`, `T` = `&str`
+        "#]],
+    );
+}
+
+#[test]
+fn subst_type_alias_do_not_work() {
+    // It is very hard to support subst for type aliases properly in all places because they are eagerly evaluated.
+    // We can show the user the subst for the underlying type instead but that'll be very confusing.
+    check(
+        r#"
+struct Foo<T, U> { a: T, b: U }
+type Alias<T> = Foo<T, i32>;
+
+fn foo() {
+    let _ = Alias$0 { a: true, b: 123 };
+}
+    "#,
+        expect![[r#"
+            *Alias*
+
+            ```rust
+            ra_test_fixture
+            ```
+
+            ```rust
+            type Alias<T> = Foo<T, i32>
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn subst_self() {
+    check(
+        r#"
+trait Trait<T> {
+    fn foo<U>(&self, v: U) {}
+}
+struct Struct<T>(T);
+impl<T> Trait<i64> for Struct<T> {}
+
+fn bar() {
+    Struct(123).foo$0(true);
+}
+    "#,
+        expect![[r#"
+            *foo*
+
+            ```rust
+            ra_test_fixture::Trait
+            ```
+
+            ```rust
+            trait Trait<T>
+            fn foo<U>(&self, v: U)
+            ```
+
+            ---
+
+            `Self` = `Struct<i32>`, `T` = `i64`, `U` = `bool`
+        "#]],
+    );
+}
+
+#[test]
+fn subst_with_lifetimes_and_consts() {
+    check(
+        r#"
+struct Foo<'a, const N: usize, T>(&[T; N]);
+
+impl<'a, T, const N: usize> Foo<'a, N, T> {
+    fn foo<'b, const Z: u32, U>(&self, v: U) {}
+}
+
+fn bar() {
+    Foo(&[1i8]).fo$0o::<456, _>("");
+}
+    "#,
+        expect![[r#"
+            *foo*
+
+            ```rust
+            ra_test_fixture::Foo
+            ```
+
+            ```rust
+            impl<'a, T, const N: usize> Foo<'a, N, T>
+            fn foo<'b, const Z: u32, U>(&self, v: U)
+            ```
+
+            ---
+
+            `T` = `i8`, `U` = `&str`
+        "#]],
+    );
+}
+
+#[test]
+fn subst_field() {
+    check(
+        r#"
+struct Foo<T> { field: T }
+
+fn bar() {
+    let v = Foo { $0field: 123 };
+}
+    "#,
+        expect![[r#"
+            *field*
+
+            ```rust
+            ra_test_fixture::Foo
+            ```
+
+            ```rust
+            field: T
+            ```
+
+            ---
+
+            `T` = `i32`
+        "#]],
+    );
+    check(
+        r#"
+struct Foo<T> { field: T }
+
+fn bar() {
+    let field = 123;
+    let v = Foo { field$0 };
+}
+    "#,
+        expect![[r#"
+            *field*
+
+            ```rust
+            let field: i32
+            ```
+            ---
+
+            ```rust
+            ra_test_fixture::Foo
+            ```
+
+            ```rust
+            field: T
+            ```
+
+            ---
+
+            `T` = `i32`
+        "#]],
+    );
+    check(
+        r#"
+struct Foo<T> { field: T }
+
+fn bar() {
+    let v = Foo { field: 123 };
+    let Foo { field$0 } = v;
+}
+    "#,
+        expect![[r#"
+            *field*
+
+            ```rust
+            let field: i32
+            ```
+
+            ---
+
+            size = 4, align = 4
+            ---
+
+            ```rust
+            ra_test_fixture::Foo
+            ```
+
+            ```rust
+            field: T
+            ```
+
+            ---
+
+            `T` = `i32`
+        "#]],
+    );
+    check(
+        r#"
+struct Foo<T> { field: T }
+
+fn bar() {
+    let v = Foo { field: 123 };
+    let Foo { field$0: _ } = v;
+}
+    "#,
+        expect![[r#"
+            *field*
+
+            ```rust
+            ra_test_fixture::Foo
+            ```
+
+            ```rust
+            field: T
+            ```
+
+            ---
+
+            `T` = `i32`
+        "#]],
+    );
+    check(
+        r#"
+struct Foo<T> { field: T }
+
+fn bar() {
+    let v = Foo { field: 123 };
+    let _ = (&v).$0field;
+}
+    "#,
+        expect![[r#"
+            *field*
+
+            ```rust
+            ra_test_fixture::Foo
+            ```
+
+            ```rust
+            field: T
+            ```
+
+            ---
+
+            `T` = `i32`
+        "#]],
+    );
+    check(
+        r#"
+struct Foo<T>(T);
+
+fn bar() {
+    let v = Foo(123);
+    let _ = v.$00;
+}
+    "#,
+        expect![[r#"
+            *0*
+
+            ```rust
+            ra_test_fixture::Foo
+            ```
+
+            ```rust
+            0: T
+            ```
+
+            ---
+
+            `T` = `i32`
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs
index c13fc843568..fe2760d2ba6 100644
--- a/src/tools/rust-analyzer/crates/ide/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs
@@ -86,7 +86,7 @@ pub use crate::{
     highlight_related::{HighlightRelatedConfig, HighlightedRange},
     hover::{
         HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult,
-        MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind,
+        MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, SubstTyLen,
     },
     inlay_hints::{
         AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints,
diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs
index 339315db571..04b5f277037 100644
--- a/src/tools/rust-analyzer/crates/ide/src/references.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/references.rs
@@ -112,7 +112,7 @@ pub(crate) fn find_all_refs(
         Some(name) => {
             let def = match NameClass::classify(sema, &name)? {
                 NameClass::Definition(it) | NameClass::ConstReference(it) => it,
-                NameClass::PatFieldShorthand { local_def: _, field_ref } => {
+                NameClass::PatFieldShorthand { local_def: _, field_ref, adt_subst: _ } => {
                     Definition::Field(field_ref)
                 }
             };
@@ -156,10 +156,12 @@ pub(crate) fn find_defs<'a>(
                 let def = match name_like {
                     ast::NameLike::NameRef(name_ref) => {
                         match NameRefClass::classify(sema, &name_ref)? {
-                            NameRefClass::Definition(def) => def,
-                            NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
-                                Definition::Local(local_ref)
-                            }
+                            NameRefClass::Definition(def, _) => def,
+                            NameRefClass::FieldShorthand {
+                                local_ref,
+                                field_ref: _,
+                                adt_subst: _,
+                            } => Definition::Local(local_ref),
                             NameRefClass::ExternCrateShorthand { decl, .. } => {
                                 Definition::ExternCrateDecl(decl)
                             }
@@ -167,14 +169,14 @@ pub(crate) fn find_defs<'a>(
                     }
                     ast::NameLike::Name(name) => match NameClass::classify(sema, &name)? {
                         NameClass::Definition(it) | NameClass::ConstReference(it) => it,
-                        NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
+                        NameClass::PatFieldShorthand { local_def, field_ref: _, adt_subst: _ } => {
                             Definition::Local(local_def)
                         }
                     },
                     ast::NameLike::Lifetime(lifetime) => {
                         NameRefClass::classify_lifetime(sema, &lifetime)
                             .and_then(|class| match class {
-                                NameRefClass::Definition(it) => Some(it),
+                                NameRefClass::Definition(it, _) => Some(it),
                                 _ => None,
                             })
                             .or_else(|| {
diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs
index a9519c03b32..b146df6d0b4 100644
--- a/src/tools/rust-analyzer/crates/ide/src/rename.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs
@@ -242,7 +242,7 @@ fn find_definitions(
                 ast::NameLike::Name(name) => NameClass::classify(sema, name)
                     .map(|class| match class {
                         NameClass::Definition(it) | NameClass::ConstReference(it) => it,
-                        NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
+                        NameClass::PatFieldShorthand { local_def, field_ref: _, adt_subst: _ } => {
                             Definition::Local(local_def)
                         }
                     })
@@ -250,8 +250,8 @@ fn find_definitions(
                 ast::NameLike::NameRef(name_ref) => {
                     NameRefClass::classify(sema, name_ref)
                         .map(|class| match class {
-                            NameRefClass::Definition(def) => def,
-                            NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
+                            NameRefClass::Definition(def, _) => def,
+                            NameRefClass::FieldShorthand { local_ref, field_ref: _, adt_subst: _ } => {
                                 Definition::Local(local_ref)
                             }
                             NameRefClass::ExternCrateShorthand { decl, .. } => {
@@ -276,7 +276,7 @@ fn find_definitions(
                 ast::NameLike::Lifetime(lifetime) => {
                     NameRefClass::classify_lifetime(sema, lifetime)
                         .and_then(|class| match class {
-                            NameRefClass::Definition(def) => Some(def),
+                            NameRefClass::Definition(def, _) => Some(def),
                             _ => None,
                         })
                         .or_else(|| {
diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
index 0f4b5e7d87a..53eeffaf97d 100644
--- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
@@ -13,11 +13,10 @@ use ide_db::{
 use span::Edition;
 use syntax::{AstNode, SyntaxKind::*, SyntaxNode, TextRange, T};
 
-use crate::inlay_hints::InlayFieldsToResolve;
 use crate::navigation_target::UpmappingResult;
 use crate::{
-    hover::hover_for_definition,
-    inlay_hints::AdjustmentHintsMode,
+    hover::{hover_for_definition, SubstTyLen},
+    inlay_hints::{AdjustmentHintsMode, InlayFieldsToResolve},
     moniker::{def_to_kind, def_to_moniker, MonikerResult, SymbolInformationKind},
     parent_module::crates_for,
     Analysis, Fold, HoverConfig, HoverResult, InlayHint, InlayHintsConfig, TryToNav,
@@ -186,6 +185,7 @@ impl StaticIndex<'_> {
             max_trait_assoc_items_count: None,
             max_fields_count: Some(5),
             max_enum_variants_count: Some(5),
+            max_subst_ty_len: SubstTyLen::Unlimited,
         };
         let tokens = tokens.filter(|token| {
             matches!(
@@ -210,6 +210,7 @@ impl StaticIndex<'_> {
                         &sema,
                         file_id,
                         def,
+                        None,
                         &node,
                         None,
                         false,
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
index 3767a3917ce..89510e7062e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
@@ -76,7 +76,7 @@ pub(super) fn name_like(
             Some(IdentClass::NameClass(NameClass::Definition(def))) => {
                 highlight_def(sema, krate, def) | HlMod::Definition
             }
-            Some(IdentClass::NameRefClass(NameRefClass::Definition(def))) => {
+            Some(IdentClass::NameRefClass(NameRefClass::Definition(def, _))) => {
                 highlight_def(sema, krate, def)
             }
             // FIXME: Fallback for 'static and '_, as we do not resolve these yet
@@ -260,7 +260,7 @@ fn highlight_name_ref(
         None => return HlTag::UnresolvedReference.into(),
     };
     let mut h = match name_class {
-        NameRefClass::Definition(def) => {
+        NameRefClass::Definition(def, _) => {
             if let Definition::Local(local) = &def {
                 let name = local.name(db);
                 let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index 40fd294e72a..5358452a4ea 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -50,6 +50,14 @@ mod patch_old_style;
 //  - Don't use abbreviations unless really necessary
 //  - foo_command = overrides the subcommand, foo_overrideCommand allows full overwriting, extra args only applies for foo_command
 
+#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub enum MaxSubstitutionLength {
+    Hide,
+    #[serde(untagged)]
+    Limit(usize),
+}
+
 // Defines the server-side configuration of the rust-analyzer. We generate
 // *parts* of VS Code's `package.json` config from this. Run `cargo test` to
 // re-generate that file.
@@ -119,6 +127,12 @@ config_data! {
         hover_documentation_keywords_enable: bool  = true,
         /// Use markdown syntax for links on hover.
         hover_links_enable: bool = true,
+        /// Whether to show what types are used as generic arguments in calls etc. on hover, and what is their max length to show such types, beyond it they will be shown with ellipsis.
+        ///
+        /// This can take three values: `null` means "unlimited", the string `"hide"` means to not show generic substitutions at all, and a number means to limit them to X characters.
+        ///
+        /// The default is 20 characters.
+        hover_maxSubstitutionLength: Option<MaxSubstitutionLength> = Some(MaxSubstitutionLength::Limit(20)),
         /// How to render the align information in a memory layout hover.
         hover_memoryLayout_alignment: Option<MemoryLayoutHoverRenderKindDef> = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal),
         /// Whether to show memory layout data on hover.
@@ -1532,6 +1546,11 @@ impl Config {
             max_trait_assoc_items_count: self.hover_show_traitAssocItems().to_owned(),
             max_fields_count: self.hover_show_fields().to_owned(),
             max_enum_variants_count: self.hover_show_enumVariants().to_owned(),
+            max_subst_ty_len: match self.hover_maxSubstitutionLength() {
+                Some(MaxSubstitutionLength::Hide) => ide::SubstTyLen::Hide,
+                Some(MaxSubstitutionLength::Limit(limit)) => ide::SubstTyLen::LimitTo(*limit),
+                None => ide::SubstTyLen::Unlimited,
+            },
         }
     }
 
@@ -3433,6 +3452,20 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
                 "Use `cargo metadata` to query sysroot metadata."
             ],
         },
+        "Option<MaxSubstitutionLength>" => set! {
+            "anyOf": [
+                {
+                    "type": "null"
+                },
+                {
+                    "type": "string",
+                    "enum": ["hide"]
+                },
+                {
+                    "type": "integer"
+                }
+            ]
+        },
         _ => panic!("missing entry for {ty}: {default} (field {field})"),
     }
 
diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc
index 1195a85cf70..ce3d0e3d803 100644
--- a/src/tools/rust-analyzer/docs/user/generated_config.adoc
+++ b/src/tools/rust-analyzer/docs/user/generated_config.adoc
@@ -512,6 +512,15 @@ Whether to show keyword hover popups. Only applies when
 --
 Use markdown syntax for links on hover.
 --
+[[rust-analyzer.hover.maxSubstitutionLength]]rust-analyzer.hover.maxSubstitutionLength (default: `20`)::
++
+--
+Whether to show what types are used as generic arguments in calls etc. on hover, and what is their max length to show such types, beyond it they will be shown with ellipsis.
+
+This can take three values: `null` means "unlimited", the string `"hide"` means to not show generic substitutions at all, and a number means to limit them to X characters.
+
+The default is 20 characters.
+--
 [[rust-analyzer.hover.memoryLayout.alignment]]rust-analyzer.hover.memoryLayout.alignment (default: `"hexadecimal"`)::
 +
 --
diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json
index 469c1b458d5..b1daaff27a2 100644
--- a/src/tools/rust-analyzer/editors/code/package.json
+++ b/src/tools/rust-analyzer/editors/code/package.json
@@ -1533,6 +1533,29 @@
             {
                 "title": "hover",
                 "properties": {
+                    "rust-analyzer.hover.maxSubstitutionLength": {
+                        "markdownDescription": "Whether to show what types are used as generic arguments in calls etc. on hover, and what is their max length to show such types, beyond it they will be shown with ellipsis.\n\nThis can take three values: `null` means \"unlimited\", the string `\"hide\"` means to not show generic substitutions at all, and a number means to limit them to X characters.\n\nThe default is 20 characters.",
+                        "default": 20,
+                        "anyOf": [
+                            {
+                                "type": "null"
+                            },
+                            {
+                                "type": "string",
+                                "enum": [
+                                    "hide"
+                                ]
+                            },
+                            {
+                                "type": "integer"
+                            }
+                        ]
+                    }
+                }
+            },
+            {
+                "title": "hover",
+                "properties": {
                     "rust-analyzer.hover.memoryLayout.alignment": {
                         "markdownDescription": "How to render the align information in a memory layout hover.",
                         "default": "hexadecimal",