about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLaurențiu Nicola <lnicola@dend.ro>2024-01-04 12:44:34 +0200
committerLaurențiu Nicola <lnicola@dend.ro>2024-01-04 12:44:34 +0200
commita66777cc71c90a69e296f2b84a7f8b3177600043 (patch)
treef4c5cc0bae8ba5253a65a6997ccffab450386ce1
parentadf052c2ba2c568b91fec15c718fe8d13e8ae602 (diff)
parent9279c6566b78c7dd9a11a648a7f287bbd37a0eea (diff)
downloadrust-a66777cc71c90a69e296f2b84a7f8b3177600043.tar.gz
rust-a66777cc71c90a69e296f2b84a7f8b3177600043.zip
Merge branch 'master' into sync-from-rust
-rw-r--r--crates/hir-def/src/body/lower.rs3
-rw-r--r--crates/hir-def/src/body/pretty.rs2
-rw-r--r--crates/hir-def/src/hir.rs3
-rw-r--r--crates/hir-expand/src/db.rs48
-rw-r--r--crates/hir-expand/src/lib.rs2
-rw-r--r--crates/hir-ty/src/infer/closure.rs2
-rw-r--r--crates/hir-ty/src/infer/expr.rs25
-rw-r--r--crates/hir-ty/src/infer/mutability.rs2
-rw-r--r--crates/hir-ty/src/mir/lower/as_place.rs2
-rw-r--r--crates/hir-ty/src/tests/traits.rs47
-rw-r--r--crates/hir/src/attrs.rs94
-rw-r--r--crates/hir/src/lib.rs4
-rw-r--r--crates/ide-db/src/defs.rs157
-rw-r--r--crates/ide/src/doc_links/tests.rs16
-rw-r--r--crates/ide/src/hover/render.rs292
-rw-r--r--crates/ide/src/lib.rs5
-rw-r--r--crates/ide/src/moniker.rs246
-rw-r--r--crates/ide/src/navigation_target.rs26
-rw-r--r--crates/ide/src/static_index.rs12
-rw-r--r--crates/rust-analyzer/src/cli/scip.rs104
-rw-r--r--docs/user/manual.adoc14
-rw-r--r--editors/code/src/config.ts1
-rw-r--r--editors/code/src/ctx.ts1
-rw-r--r--editors/code/src/main.ts1
-rw-r--r--editors/code/src/rust_project.ts6
-rw-r--r--editors/code/tsconfig.json2
26 files changed, 738 insertions, 379 deletions
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index a45ec844aba..bc4da360c5a 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -622,7 +622,8 @@ impl ExprCollector<'_> {
             ast::Expr::IndexExpr(e) => {
                 let base = self.collect_expr_opt(e.base());
                 let index = self.collect_expr_opt(e.index());
-                self.alloc_expr(Expr::Index { base, index }, syntax_ptr)
+                let is_assignee_expr = self.is_lowering_assignee_expr;
+                self.alloc_expr(Expr::Index { base, index, is_assignee_expr }, syntax_ptr)
             }
             ast::Expr::RangeExpr(e) => {
                 let lhs = e.start().map(|lhs| self.collect_expr(lhs));
diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs
index 6ecf1c20d6c..02b19ade44b 100644
--- a/crates/hir-def/src/body/pretty.rs
+++ b/crates/hir-def/src/body/pretty.rs
@@ -376,7 +376,7 @@ impl Printer<'_> {
                     w!(self, ") ");
                 }
             }
-            Expr::Index { base, index } => {
+            Expr::Index { base, index, is_assignee_expr: _ } => {
                 self.print_expr(*base);
                 w!(self, "[");
                 self.print_expr(*index);
diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs
index 591ee77c70a..5890e818c46 100644
--- a/crates/hir-def/src/hir.rs
+++ b/crates/hir-def/src/hir.rs
@@ -265,6 +265,7 @@ pub enum Expr {
     Index {
         base: ExprId,
         index: ExprId,
+        is_assignee_expr: bool,
     },
     Closure {
         args: Box<[PatId]>,
@@ -432,7 +433,7 @@ impl Expr {
                     f(rhs);
                 }
             }
-            Expr::Index { base, index } => {
+            Expr::Index { base, index, .. } => {
                 f(*base);
                 f(*index);
             }
diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs
index f7a26e436de..ed04582cb0a 100644
--- a/crates/hir-expand/src/db.rs
+++ b/crates/hir-expand/src/db.rs
@@ -390,7 +390,13 @@ fn parse_macro_expansion(
     let expand_to = loc.expand_to();
     let mbe::ValueResult { value: tt, err } = macro_expand(db, macro_file.macro_call_id, loc);
 
-    let (parse, rev_token_map) = token_tree_to_syntax_node(&tt, expand_to);
+    let (parse, rev_token_map) = token_tree_to_syntax_node(
+        match &tt {
+            CowArc::Arc(it) => it,
+            CowArc::Owned(it) => it,
+        },
+        expand_to,
+    );
 
     ExpandResult { value: (parse, Arc::new(rev_token_map)), err }
 }
@@ -669,15 +675,20 @@ fn macro_expander(db: &dyn ExpandDatabase, id: MacroDefId) -> TokenExpander {
     }
 }
 
+enum CowArc<T> {
+    Arc(Arc<T>),
+    Owned(T),
+}
+
 fn macro_expand(
     db: &dyn ExpandDatabase,
     macro_call_id: MacroCallId,
     loc: MacroCallLoc,
-) -> ExpandResult<Arc<tt::Subtree>> {
+) -> ExpandResult<CowArc<tt::Subtree>> {
     let _p = profile::span("macro_expand");
 
     let ExpandResult { value: tt, mut err } = match loc.def.kind {
-        MacroDefKind::ProcMacro(..) => return db.expand_proc_macro(macro_call_id),
+        MacroDefKind::ProcMacro(..) => return db.expand_proc_macro(macro_call_id).map(CowArc::Arc),
         MacroDefKind::BuiltInDerive(expander, ..) => {
             let (root, map) = parse_with_map(db, loc.kind.file_id());
             let root = root.syntax_node();
@@ -692,7 +703,7 @@ fn macro_expand(
             let ValueResult { value, err } = db.macro_arg(macro_call_id);
             let Some((macro_arg, undo_info)) = value else {
                 return ExpandResult {
-                    value: Arc::new(tt::Subtree {
+                    value: CowArc::Owned(tt::Subtree {
                         delimiter: tt::Delimiter::invisible_spanned(loc.call_site),
                         token_trees: Vec::new(),
                     }),
@@ -718,7 +729,7 @@ fn macro_expand(
                 // As such we just return the input subtree here.
                 MacroDefKind::BuiltInEager(..) if loc.eager.is_none() => {
                     return ExpandResult {
-                        value: macro_arg.clone(),
+                        value: CowArc::Arc(macro_arg.clone()),
                         err: err.map(|err| {
                             let mut buf = String::new();
                             for err in &**err {
@@ -752,12 +763,17 @@ fn macro_expand(
     // Skip checking token tree limit for include! macro call
     if !loc.def.is_include() {
         // Set a hard limit for the expanded tt
-        if let Err(value) = check_tt_count(&tt, loc.call_site) {
-            return value;
+        if let Err(value) = check_tt_count(&tt) {
+            return value.map(|()| {
+                CowArc::Owned(tt::Subtree {
+                    delimiter: tt::Delimiter::invisible_spanned(loc.call_site),
+                    token_trees: vec![],
+                })
+            });
         }
     }
 
-    ExpandResult { value: Arc::new(tt), err }
+    ExpandResult { value: CowArc::Owned(tt), err }
 }
 
 fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt::Subtree>> {
@@ -796,8 +812,13 @@ fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<A
     );
 
     // Set a hard limit for the expanded tt
-    if let Err(value) = check_tt_count(&tt, loc.call_site) {
-        return value;
+    if let Err(value) = check_tt_count(&tt) {
+        return value.map(|()| {
+            Arc::new(tt::Subtree {
+                delimiter: tt::Delimiter::invisible_spanned(loc.call_site),
+                token_trees: vec![],
+            })
+        });
     }
 
     fixup::reverse_fixups(&mut tt, &undo_info);
@@ -819,14 +840,11 @@ fn token_tree_to_syntax_node(
     mbe::token_tree_to_syntax_node(tt, entry_point)
 }
 
-fn check_tt_count(tt: &tt::Subtree, call_site: Span) -> Result<(), ExpandResult<Arc<tt::Subtree>>> {
+fn check_tt_count(tt: &tt::Subtree) -> Result<(), ExpandResult<()>> {
     let count = tt.count();
     if TOKEN_LIMIT.check(count).is_err() {
         Err(ExpandResult {
-            value: Arc::new(tt::Subtree {
-                delimiter: tt::Delimiter::invisible_spanned(call_site),
-                token_trees: vec![],
-            }),
+            value: (),
             err: Some(ExpandError::other(format!(
                 "macro invocation exceeds token limit: produced {} tokens, limit is {}",
                 count,
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs
index b5197d4c25d..6a122e0859c 100644
--- a/crates/hir-expand/src/lib.rs
+++ b/crates/hir-expand/src/lib.rs
@@ -220,6 +220,8 @@ pub enum MacroCallKind {
     },
     Attr {
         ast_id: AstId<ast::Item>,
+        // FIXME: This is being interned, subtrees can very quickly differ just slightly causing
+        // leakage problems here
         attr_args: Option<Arc<tt::Subtree>>,
         /// Syntactical index of the invoking `#[attribute]`.
         ///
diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs
index af74df1032c..58b4f29ec8c 100644
--- a/crates/hir-ty/src/infer/closure.rs
+++ b/crates/hir-ty/src/infer/closure.rs
@@ -598,7 +598,7 @@ impl InferenceContext<'_> {
                     self.consume_expr(expr);
                 }
             }
-            Expr::Index { base, index } => {
+            Expr::Index { base, index, is_assignee_expr: _ } => {
                 self.select_from_expr(*base);
                 self.consume_expr(*index);
             }
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 84954ca7e90..b8a7d3ebf79 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -744,7 +744,7 @@ impl InferenceContext<'_> {
                     (RangeOp::Inclusive, _, None) => self.err_ty(),
                 }
             }
-            Expr::Index { base, index } => {
+            Expr::Index { base, index, is_assignee_expr } => {
                 let base_ty = self.infer_expr_inner(*base, &Expectation::none());
                 let index_ty = self.infer_expr(*index, &Expectation::none());
 
@@ -772,11 +772,24 @@ impl InferenceContext<'_> {
                             .build();
                         self.write_method_resolution(tgt_expr, func, substs);
                     }
-                    self.resolve_associated_type_with_params(
-                        self_ty,
-                        self.resolve_ops_index_output(),
-                        &[index_ty.cast(Interner)],
-                    )
+                    let assoc = self.resolve_ops_index_output();
+                    let res = self.resolve_associated_type_with_params(
+                        self_ty.clone(),
+                        assoc,
+                        &[index_ty.clone().cast(Interner)],
+                    );
+
+                    if *is_assignee_expr {
+                        if let Some(index_trait) = self.resolve_lang_trait(LangItem::IndexMut) {
+                            let trait_ref = TyBuilder::trait_ref(self.db, index_trait)
+                                .push(self_ty)
+                                .fill(|_| index_ty.clone().cast(Interner))
+                                .build();
+                            self.push_obligation(trait_ref.cast(Interner));
+                        }
+                    }
+
+                    res
                 } else {
                     self.err_ty()
                 }
diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs
index b8a1af96fba..663ea853231 100644
--- a/crates/hir-ty/src/infer/mutability.rs
+++ b/crates/hir-ty/src/infer/mutability.rs
@@ -96,7 +96,7 @@ impl InferenceContext<'_> {
             Expr::RecordLit { path: _, fields, spread, ellipsis: _, is_assignee_expr: _ } => {
                 self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(*spread))
             }
-            &Expr::Index { base, index } => {
+            &Expr::Index { base, index, is_assignee_expr: _ } => {
                 if mutability == Mutability::Mut {
                     if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) {
                         if let Some(index_trait) = self
diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs
index 8c078eb4ad7..cb5588a5c13 100644
--- a/crates/hir-ty/src/mir/lower/as_place.rs
+++ b/crates/hir-ty/src/mir/lower/as_place.rs
@@ -218,7 +218,7 @@ impl MirLowerCtx<'_> {
                 self.push_field_projection(&mut r, expr_id)?;
                 Ok(Some((r, current)))
             }
-            Expr::Index { base, index } => {
+            Expr::Index { base, index, is_assignee_expr: _ } => {
                 let base_ty = self.expr_ty_after_adjustments(*base);
                 let index_ty = self.expr_ty_after_adjustments(*index);
                 if index_ty != TyBuilder::usize()
diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs
index 003ae60e8e5..d270328605a 100644
--- a/crates/hir-ty/src/tests/traits.rs
+++ b/crates/hir-ty/src/tests/traits.rs
@@ -4506,3 +4506,50 @@ fn ttt() {
 "#,
     );
 }
+
+#[test]
+fn infer_borrow() {
+    check_types(
+        r#"
+//- minicore: index
+pub struct SomeMap<K>;
+
+pub trait Borrow<Borrowed: ?Sized> {
+    fn borrow(&self) -> &Borrowed;
+}
+
+impl<T: ?Sized> Borrow<T> for T {
+    fn borrow(&self) -> &T {
+        self
+    }
+}
+
+impl<T: ?Sized> Borrow<T> for &T {
+    fn borrow(&self) -> &T {
+        &**self
+    }
+}
+
+impl<K, KB: Borrow<K>> core::ops::Index<KB> for SomeMap<K> {
+    type Output = ();
+
+    fn index(&self, _: KB) -> &() {
+        &()
+    }
+}
+
+impl<K> core::ops::IndexMut<K> for SomeMap<K> {
+    fn index_mut(&mut self, _: K) -> &mut () {
+        &mut ()
+    }
+}
+
+fn foo() {
+    let mut map = SomeMap;
+    map["a"] = ();
+    map;
+  //^^^ SomeMap<&str>
+}
+"#,
+    );
+}
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs
index d60d20f5b7e..bc4d9b9be36 100644
--- a/crates/hir/src/attrs.rs
+++ b/crates/hir/src/attrs.rs
@@ -1,5 +1,7 @@
 //! Attributes & documentation for hir types.
 
+use std::ops::ControlFlow;
+
 use base_db::FileId;
 use hir_def::{
     attr::AttrsWithOwner,
@@ -13,13 +15,13 @@ use hir_expand::{
     name::Name,
     span_map::{RealSpanMap, SpanMapRef},
 };
-use hir_ty::db::HirDatabase;
+use hir_ty::{db::HirDatabase, method_resolution};
 use syntax::{ast, AstNode};
 
 use crate::{
     Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl,
-    Field, Function, GenericParam, Impl, LifetimeParam, Macro, Module, ModuleDef, Static, Struct,
-    Trait, TraitAlias, TypeAlias, TypeParam, Union, Variant, VariantDef,
+    Field, Function, GenericParam, HasCrate, Impl, LifetimeParam, Macro, Module, ModuleDef, Static,
+    Struct, Trait, TraitAlias, Type, TypeAlias, TypeParam, Union, Variant, VariantDef,
 };
 
 pub trait HasAttrs {
@@ -205,8 +207,14 @@ fn resolve_assoc_or_field(
         }
     };
 
-    // FIXME: Resolve associated items here, e.g. `Option::map`. Note that associated items take
-    // precedence over fields.
+    // Resolve inherent items first, then trait items, then fields.
+    if let Some(assoc_item_def) = resolve_assoc_item(db, &ty, &name, ns) {
+        return Some(assoc_item_def);
+    }
+
+    if let Some(impl_trait_item_def) = resolve_impl_trait_item(db, resolver, &ty, &name, ns) {
+        return Some(impl_trait_item_def);
+    }
 
     let variant_def = match ty.as_adt()? {
         Adt::Struct(it) => it.into(),
@@ -216,6 +224,69 @@ fn resolve_assoc_or_field(
     resolve_field(db, variant_def, name, ns)
 }
 
+fn resolve_assoc_item(
+    db: &dyn HirDatabase,
+    ty: &Type,
+    name: &Name,
+    ns: Option<Namespace>,
+) -> Option<DocLinkDef> {
+    ty.iterate_assoc_items(db, ty.krate(db), move |assoc_item| {
+        if assoc_item.name(db)? != *name {
+            return None;
+        }
+        as_module_def_if_namespace_matches(assoc_item, ns)
+    })
+}
+
+fn resolve_impl_trait_item(
+    db: &dyn HirDatabase,
+    resolver: Resolver,
+    ty: &Type,
+    name: &Name,
+    ns: Option<Namespace>,
+) -> Option<DocLinkDef> {
+    let canonical = ty.canonical();
+    let krate = ty.krate(db);
+    let environment = resolver.generic_def().map_or_else(
+        || crate::TraitEnvironment::empty(krate.id).into(),
+        |d| db.trait_environment(d),
+    );
+    let traits_in_scope = resolver.traits_in_scope(db.upcast());
+
+    let mut result = None;
+
+    // `ty.iterate_path_candidates()` require a scope, which is not available when resolving
+    // attributes here. Use path resolution directly instead.
+    //
+    // FIXME: resolve type aliases (which are not yielded by iterate_path_candidates)
+    method_resolution::iterate_path_candidates(
+        &canonical,
+        db,
+        environment,
+        &traits_in_scope,
+        method_resolution::VisibleFromModule::None,
+        Some(name),
+        &mut |assoc_item_id| {
+            let assoc_item: AssocItem = assoc_item_id.into();
+
+            debug_assert_eq!(assoc_item.name(db).as_ref(), Some(name));
+
+            // If two traits in scope define the same item, Rustdoc links to no specific trait (for
+            // instance, given two methods `a`, Rustdoc simply links to `method.a` with no
+            // disambiguation) so we just pick the first one we find as well.
+            result = as_module_def_if_namespace_matches(assoc_item, ns);
+
+            if result.is_some() {
+                ControlFlow::Break(())
+            } else {
+                ControlFlow::Continue(())
+            }
+        },
+    );
+
+    result
+}
+
 fn resolve_field(
     db: &dyn HirDatabase,
     def: VariantDef,
@@ -228,6 +299,19 @@ fn resolve_field(
     def.fields(db).into_iter().find(|f| f.name(db) == name).map(DocLinkDef::Field)
 }
 
+fn as_module_def_if_namespace_matches(
+    assoc_item: AssocItem,
+    ns: Option<Namespace>,
+) -> Option<DocLinkDef> {
+    let (def, expected_ns) = match assoc_item {
+        AssocItem::Function(it) => (ModuleDef::Function(it), Namespace::Values),
+        AssocItem::Const(it) => (ModuleDef::Const(it), Namespace::Values),
+        AssocItem::TypeAlias(it) => (ModuleDef::TypeAlias(it), Namespace::Types),
+    };
+
+    (ns.unwrap_or(expected_ns) == expected_ns).then(|| DocLinkDef::ModuleDef(def))
+}
+
 fn modpath_from_str(db: &dyn HirDatabase, link: &str) -> Option<ModPath> {
     // FIXME: this is not how we should get a mod path here.
     let try_get_modpath = |link: &str| {
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 09b56e13824..fc6c2aeb3be 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -4121,6 +4121,10 @@ impl Type {
         }
     }
 
+    pub(crate) fn canonical(&self) -> Canonical<Ty> {
+        hir_ty::replace_errors_with_variables(&self.ty)
+    }
+
     /// Returns types that this type dereferences to (including this type itself). The returned
     /// iterator won't yield the same type more than once even if the deref chain contains a cycle.
     pub fn autoderef(&self, db: &dyn HirDatabase) -> impl Iterator<Item = Type> + '_ {
diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs
index ded5d4e3db5..410b8304592 100644
--- a/crates/ide-db/src/defs.rs
+++ b/crates/ide-db/src/defs.rs
@@ -7,17 +7,19 @@
 
 use arrayvec::ArrayVec;
 use hir::{
-    Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper, DocLinkDef,
-    ExternCrateDecl, Field, Function, GenericParam, HasVisibility, Impl, Label, Local, Macro,
-    Module, ModuleDef, Name, PathResolution, Semantics, Static, ToolModule, Trait, TraitAlias,
-    TypeAlias, Variant, Visibility,
+    Adt, AsAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType, Const, Crate,
+    DefWithBody, DeriveHelper, DocLinkDef, ExternCrateDecl, Field, Function, GenericParam,
+    HasVisibility, HirDisplay, Impl, Label, Local, Macro, Module, ModuleDef, Name, PathResolution,
+    Semantics, Static, ToolModule, Trait, TraitAlias, TypeAlias, Variant, VariantDef, Visibility,
 };
-use stdx::impl_from;
+use stdx::{format_to, impl_from};
 use syntax::{
     ast::{self, AstNode},
     match_ast, SyntaxKind, SyntaxNode, SyntaxToken,
 };
 
+use crate::documentation::{Documentation, HasDocs};
+use crate::famous_defs::FamousDefs;
 use crate::RootDatabase;
 
 // FIXME: a more precise name would probably be `Symbol`?
@@ -83,6 +85,13 @@ impl Definition {
         Some(module)
     }
 
+    pub fn enclosing_definition(&self, db: &RootDatabase) -> Option<Definition> {
+        match self {
+            Definition::Local(it) => it.parent(db).try_into().ok(),
+            _ => None,
+        }
+    }
+
     pub fn visibility(&self, db: &RootDatabase) -> Option<Visibility> {
         let vis = match self {
             Definition::Field(sf) => sf.visibility(db),
@@ -134,6 +143,125 @@ impl Definition {
         };
         Some(name)
     }
+
+    pub fn docs(
+        &self,
+        db: &RootDatabase,
+        famous_defs: Option<&FamousDefs<'_, '_>>,
+    ) -> Option<Documentation> {
+        let docs = match self {
+            Definition::Macro(it) => it.docs(db),
+            Definition::Field(it) => it.docs(db),
+            Definition::Module(it) => it.docs(db),
+            Definition::Function(it) => it.docs(db),
+            Definition::Adt(it) => it.docs(db),
+            Definition::Variant(it) => it.docs(db),
+            Definition::Const(it) => it.docs(db),
+            Definition::Static(it) => it.docs(db),
+            Definition::Trait(it) => it.docs(db),
+            Definition::TraitAlias(it) => it.docs(db),
+            Definition::TypeAlias(it) => it.docs(db),
+            Definition::BuiltinType(it) => {
+                famous_defs.and_then(|fd| {
+                    // std exposes prim_{} modules with docstrings on the root to document the builtins
+                    let primitive_mod = format!("prim_{}", it.name().display(fd.0.db));
+                    let doc_owner = find_std_module(fd, &primitive_mod)?;
+                    doc_owner.docs(fd.0.db)
+                })
+            }
+            Definition::Local(_) => None,
+            Definition::SelfType(impl_def) => {
+                impl_def.self_ty(db).as_adt().map(|adt| adt.docs(db))?
+            }
+            Definition::GenericParam(_) => None,
+            Definition::Label(_) => None,
+            Definition::ExternCrateDecl(it) => it.docs(db),
+
+            Definition::BuiltinAttr(it) => {
+                let name = it.name(db);
+                let AttributeTemplate { word, list, name_value_str } = it.template(db)?;
+                let mut docs = "Valid forms are:".to_owned();
+                if word {
+                    format_to!(docs, "\n - #\\[{}]", name);
+                }
+                if let Some(list) = list {
+                    format_to!(docs, "\n - #\\[{}({})]", name, list);
+                }
+                if let Some(name_value_str) = name_value_str {
+                    format_to!(docs, "\n - #\\[{} = {}]", name, name_value_str);
+                }
+                Some(Documentation::new(docs.replace('*', "\\*")))
+            }
+            Definition::ToolModule(_) => None,
+            Definition::DeriveHelper(_) => None,
+        };
+
+        docs.or_else(|| {
+            // docs are missing, for assoc items of trait impls try to fall back to the docs of the
+            // original item of the trait
+            let assoc = self.as_assoc_item(db)?;
+            let trait_ = assoc.containing_trait_impl(db)?;
+            let name = Some(assoc.name(db)?);
+            let item = trait_.items(db).into_iter().find(|it| it.name(db) == name)?;
+            item.docs(db)
+        })
+    }
+
+    pub fn label(&self, db: &RootDatabase) -> Option<String> {
+        let label = match *self {
+            Definition::Macro(it) => it.display(db).to_string(),
+            Definition::Field(it) => it.display(db).to_string(),
+            Definition::Module(it) => it.display(db).to_string(),
+            Definition::Function(it) => it.display(db).to_string(),
+            Definition::Adt(it) => it.display(db).to_string(),
+            Definition::Variant(it) => it.display(db).to_string(),
+            Definition::Const(it) => it.display(db).to_string(),
+            Definition::Static(it) => it.display(db).to_string(),
+            Definition::Trait(it) => it.display(db).to_string(),
+            Definition::TraitAlias(it) => it.display(db).to_string(),
+            Definition::TypeAlias(it) => it.display(db).to_string(),
+            Definition::BuiltinType(it) => it.name().display(db).to_string(),
+            Definition::Local(it) => {
+                let ty = it.ty(db);
+                let ty = ty.display_truncated(db, None);
+                let is_mut = if it.is_mut(db) { "mut " } else { "" };
+                let desc = match it.primary_source(db).into_ident_pat() {
+                    Some(ident) => {
+                        let name = it.name(db);
+                        let let_kw = if ident.syntax().parent().map_or(false, |p| {
+                            p.kind() == SyntaxKind::LET_STMT || p.kind() == SyntaxKind::LET_EXPR
+                        }) {
+                            "let "
+                        } else {
+                            ""
+                        };
+                        format!("{let_kw}{is_mut}{}: {ty}", name.display(db))
+                    }
+                    None => format!("{is_mut}self: {ty}"),
+                };
+                desc
+            }
+            Definition::SelfType(impl_def) => {
+                impl_def.self_ty(db).as_adt().and_then(|adt| Definition::Adt(adt).label(db))?
+            }
+            Definition::GenericParam(it) => it.display(db).to_string(),
+            Definition::Label(it) => it.name(db).display(db).to_string(),
+            Definition::ExternCrateDecl(it) => it.display(db).to_string(),
+            Definition::BuiltinAttr(it) => format!("#[{}]", it.name(db)),
+            Definition::ToolModule(it) => it.name(db).to_string(),
+            Definition::DeriveHelper(it) => format!("derive_helper {}", it.name(db).display(db)),
+        };
+        Some(label)
+    }
+}
+
+fn find_std_module(famous_defs: &FamousDefs<'_, '_>, name: &str) -> Option<hir::Module> {
+    let db = famous_defs.0.db;
+    let std_crate = famous_defs.std()?;
+    let std_root_module = std_crate.root_module();
+    std_root_module.children(db).find(|module| {
+        module.name(db).map_or(false, |module| module.display(db).to_string() == name)
+    })
 }
 
 // FIXME: IdentClass as a name no longer fits
@@ -662,3 +790,22 @@ impl From<DocLinkDef> for Definition {
         }
     }
 }
+
+impl From<VariantDef> for Definition {
+    fn from(def: VariantDef) -> Self {
+        ModuleDef::from(def).into()
+    }
+}
+
+impl TryFrom<DefWithBody> for Definition {
+    type Error = ();
+    fn try_from(def: DefWithBody) -> Result<Self, Self::Error> {
+        match def {
+            DefWithBody::Function(it) => Ok(it.into()),
+            DefWithBody::Static(it) => Ok(it.into()),
+            DefWithBody::Const(it) => Ok(it.into()),
+            DefWithBody::Variant(it) => Ok(it.into()),
+            DefWithBody::InTypeConst(_) => Err(()),
+        }
+    }
+}
diff --git a/crates/ide/src/doc_links/tests.rs b/crates/ide/src/doc_links/tests.rs
index f388aea4c37..e1f5ccc228b 100644
--- a/crates/ide/src/doc_links/tests.rs
+++ b/crates/ide/src/doc_links/tests.rs
@@ -462,14 +462,15 @@ mod module {}
 fn doc_links_inherent_impl_items() {
     check_doc_links(
         r#"
-// /// [`Struct::CONST`]
-// /// [`Struct::function`]
-/// FIXME #9694
+/// [`Struct::CONST`]
+/// [`Struct::function`]
 struct Struct$0;
 
 impl Struct {
     const CONST: () = ();
+       // ^^^^^ Struct::CONST
     fn function() {}
+    // ^^^^^^^^ Struct::function
 }
 "#,
     )
@@ -482,12 +483,13 @@ fn doc_links_trait_impl_items() {
 trait Trait {
     type Type;
     const CONST: usize;
+       // ^^^^^ Struct::CONST
     fn function();
+    // ^^^^^^^^ Struct::function
 }
-// /// [`Struct::Type`]
-// /// [`Struct::CONST`]
-// /// [`Struct::function`]
-/// FIXME #9694
+// FIXME #9694: [`Struct::Type`]
+/// [`Struct::CONST`]
+/// [`Struct::function`]
 struct Struct$0;
 
 impl Trait for Struct {
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index d0a02fd0dba..ee2f15c5a6a 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -1,15 +1,12 @@
 //! Logic for rendering the different hover messages
-use std::fmt::Display;
-
 use either::Either;
 use hir::{
-    Adt, AsAssocItem, AttributeTemplate, CaptureKind, HasSource, HirDisplay, Layout, LayoutError,
-    Semantics, TypeInfo,
+    Adt, AsAssocItem, CaptureKind, HasSource, HirDisplay, Layout, LayoutError, Semantics, TypeInfo,
 };
 use ide_db::{
     base_db::SourceDatabase,
     defs::Definition,
-    documentation::{Documentation, HasDocs},
+    documentation::HasDocs,
     famous_defs::FamousDefs,
     generated::lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES},
     syntax_helpers::insert_whitespace_into_node,
@@ -20,9 +17,7 @@ use stdx::format_to;
 use syntax::{
     algo,
     ast::{self, RecordPat},
-    match_ast, AstNode, Direction,
-    SyntaxKind::{LET_EXPR, LET_STMT},
-    SyntaxToken, T,
+    match_ast, AstNode, Direction, SyntaxToken, T,
 };
 
 use crate::{
@@ -393,48 +388,23 @@ pub(super) fn definition(
     config: &HoverConfig,
 ) -> Option<Markup> {
     let mod_path = definition_mod_path(db, &def);
-    let (label, docs) = match def {
-        Definition::Macro(it) => label_and_docs(db, it),
-        Definition::Field(it) => label_and_layout_info_and_docs(
-            db,
-            it,
-            config,
-            |&it| it.layout(db),
-            |_| {
-                let var_def = it.parent_def(db);
-                match var_def {
-                    hir::VariantDef::Struct(s) => {
-                        Adt::from(s).layout(db).ok().and_then(|layout| layout.field_offset(it))
+    let label = def.label(db)?;
+    let docs = def.docs(db, famous_defs);
+
+    let value = match def {
+        Definition::Variant(it) => {
+            if !it.parent_enum(db).is_data_carrying(db) {
+                match it.eval(db) {
+                    Ok(it) => {
+                        Some(if it >= 10 { format!("{it} ({it:#X})") } else { format!("{it}") })
                     }
-                    _ => None,
+                    Err(_) => it.value(db).map(|it| format!("{it:?}")),
                 }
-            },
-        ),
-        Definition::Module(it) => label_and_docs(db, it),
-        Definition::Function(it) => label_and_docs(db, it),
-        Definition::Adt(it) => {
-            label_and_layout_info_and_docs(db, it, config, |&it| it.layout(db), |_| None)
+            } else {
+                None
+            }
         }
-        Definition::Variant(it) => label_value_and_layout_info_and_docs(
-            db,
-            it,
-            config,
-            |&it| {
-                if !it.parent_enum(db).is_data_carrying(db) {
-                    match it.eval(db) {
-                        Ok(it) => {
-                            Some(if it >= 10 { format!("{it} ({it:#X})") } else { format!("{it}") })
-                        }
-                        Err(_) => it.value(db).map(|it| format!("{it:?}")),
-                    }
-                } else {
-                    None
-                }
-            },
-            |it| it.layout(db),
-            |layout| layout.enum_tag_size(),
-        ),
-        Definition::Const(it) => label_value_and_docs(db, it, |it| {
+        Definition::Const(it) => {
             let body = it.render_eval(db);
             match body {
                 Ok(it) => Some(it),
@@ -447,53 +417,59 @@ pub(super) fn definition(
                     Some(body.to_string())
                 }
             }
-        }),
-        Definition::Static(it) => label_value_and_docs(db, it, |it| {
+        }
+        Definition::Static(it) => {
             let source = it.source(db)?;
             let mut body = source.value.body()?.syntax().clone();
             if source.file_id.is_macro() {
                 body = insert_whitespace_into_node::insert_ws_into(body);
             }
             Some(body.to_string())
-        }),
-        Definition::Trait(it) => label_and_docs(db, it),
-        Definition::TraitAlias(it) => label_and_docs(db, it),
-        Definition::TypeAlias(it) => {
-            label_and_layout_info_and_docs(db, it, config, |&it| it.ty(db).layout(db), |_| None)
         }
-        Definition::BuiltinType(it) => {
-            return famous_defs
-                .and_then(|fd| builtin(fd, it))
-                .or_else(|| Some(Markup::fenced_block(&it.name().display(db))))
+        _ => None,
+    };
+
+    let layout_info = match def {
+        Definition::Field(it) => render_memory_layout(
+            config.memory_layout,
+            || it.layout(db),
+            |_| {
+                let var_def = it.parent_def(db);
+                match var_def {
+                    hir::VariantDef::Struct(s) => {
+                        Adt::from(s).layout(db).ok().and_then(|layout| layout.field_offset(it))
+                    }
+                    _ => None,
+                }
+            },
+            |_| None,
+        ),
+        Definition::Adt(it) => {
+            render_memory_layout(config.memory_layout, || it.layout(db), |_| None, |_| None)
         }
-        Definition::Local(it) => return local(db, it, config),
-        Definition::SelfType(impl_def) => {
-            impl_def.self_ty(db).as_adt().map(|adt| label_and_docs(db, adt))?
+        Definition::Variant(it) => render_memory_layout(
+            config.memory_layout,
+            || it.layout(db),
+            |_| None,
+            |layout| layout.enum_tag_size(),
+        ),
+        Definition::TypeAlias(it) => {
+            render_memory_layout(config.memory_layout, || it.ty(db).layout(db), |_| None, |_| None)
         }
-        Definition::GenericParam(it) => (it.display(db).to_string(), None),
-        Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db).display(db))),
-        Definition::ExternCrateDecl(it) => label_and_docs(db, it),
-        // FIXME: We should be able to show more info about these
-        Definition::BuiltinAttr(it) => return render_builtin_attr(db, it),
-        Definition::ToolModule(it) => return Some(Markup::fenced_block(&it.name(db))),
-        Definition::DeriveHelper(it) => {
-            (format!("derive_helper {}", it.name(db).display(db)), None)
+        Definition::Local(it) => {
+            render_memory_layout(config.memory_layout, || it.ty(db).layout(db), |_| None, |_| None)
         }
+        _ => None,
     };
 
-    let docs = docs
-        .filter(|_| config.documentation)
-        .or_else(|| {
-            // docs are missing, for assoc items of trait impls try to fall back to the docs of the
-            // original item of the trait
-            let assoc = def.as_assoc_item(db)?;
-            let trait_ = assoc.containing_trait_impl(db)?;
-            let name = Some(assoc.name(db)?);
-            let item = trait_.items(db).into_iter().find(|it| it.name(db) == name)?;
-            item.docs(db)
-        })
-        .map(Into::into);
-    markup(docs, label, mod_path)
+    let label = match (value, layout_info) {
+        (Some(value), Some(layout_info)) => format!("{label} = {value}{layout_info}"),
+        (Some(value), None) => format!("{label} = {value}"),
+        (None, Some(layout_info)) => format!("{label}{layout_info}"),
+        (None, None) => label,
+    };
+
+    markup(docs.map(Into::into), label, mod_path)
 }
 
 fn type_info(
@@ -595,114 +571,16 @@ fn closure_ty(
     Some(res)
 }
 
-fn render_builtin_attr(db: &RootDatabase, attr: hir::BuiltinAttr) -> Option<Markup> {
-    let name = attr.name(db);
-    let desc = format!("#[{name}]");
-
-    let AttributeTemplate { word, list, name_value_str } = match attr.template(db) {
-        Some(template) => template,
-        None => return Some(Markup::fenced_block(&attr.name(db))),
-    };
-    let mut docs = "Valid forms are:".to_owned();
-    if word {
-        format_to!(docs, "\n - #\\[{}]", name);
-    }
-    if let Some(list) = list {
-        format_to!(docs, "\n - #\\[{}({})]", name, list);
-    }
-    if let Some(name_value_str) = name_value_str {
-        format_to!(docs, "\n - #\\[{} = {}]", name, name_value_str);
-    }
-    markup(Some(docs.replace('*', "\\*")), desc, None)
-}
-
-fn label_and_docs<D>(db: &RootDatabase, def: D) -> (String, Option<Documentation>)
-where
-    D: HasDocs + HirDisplay,
-{
-    let label = def.display(db).to_string();
-    let docs = def.docs(db);
-    (label, docs)
-}
-
-fn label_and_layout_info_and_docs<D, E, E2>(
-    db: &RootDatabase,
-    def: D,
-    config: &HoverConfig,
-    layout_extractor: E,
-    layout_offset_extractor: E2,
-) -> (String, Option<Documentation>)
-where
-    D: HasDocs + HirDisplay,
-    E: Fn(&D) -> Result<Layout, LayoutError>,
-    E2: Fn(&Layout) -> Option<u64>,
-{
-    let mut label = def.display(db).to_string();
-    if let Some(layout) = render_memory_layout(
-        config.memory_layout,
-        || layout_extractor(&def),
-        layout_offset_extractor,
-        |_| None,
-    ) {
-        format_to!(label, "{layout}");
-    }
-    let docs = def.docs(db);
-    (label, docs)
-}
-
-fn label_value_and_layout_info_and_docs<D, E, E2, E3, V>(
-    db: &RootDatabase,
-    def: D,
-    config: &HoverConfig,
-    value_extractor: E,
-    layout_extractor: E2,
-    layout_tag_extractor: E3,
-) -> (String, Option<Documentation>)
-where
-    D: HasDocs + HirDisplay,
-    E: Fn(&D) -> Option<V>,
-    E2: Fn(&D) -> Result<Layout, LayoutError>,
-    E3: Fn(&Layout) -> Option<usize>,
-    V: Display,
-{
-    let value = value_extractor(&def);
-    let mut label = match value {
-        Some(value) => format!("{} = {value}", def.display(db)),
-        None => def.display(db).to_string(),
-    };
-    if let Some(layout) = render_memory_layout(
-        config.memory_layout,
-        || layout_extractor(&def),
-        |_| None,
-        layout_tag_extractor,
-    ) {
-        format_to!(label, "{layout}");
-    }
-    let docs = def.docs(db);
-    (label, docs)
-}
-
-fn label_value_and_docs<D, E, V>(
-    db: &RootDatabase,
-    def: D,
-    value_extractor: E,
-) -> (String, Option<Documentation>)
-where
-    D: HasDocs + HirDisplay,
-    E: Fn(&D) -> Option<V>,
-    V: Display,
-{
-    let label = if let Some(value) = value_extractor(&def) {
-        format!("{} = {value}", def.display(db))
-    } else {
-        def.display(db).to_string()
-    };
-    let docs = def.docs(db);
-    (label, docs)
-}
-
 fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
-    if let Definition::GenericParam(_) = def {
+    if matches!(
+        def,
+        Definition::GenericParam(_)
+            | Definition::BuiltinType(_)
+            | Definition::Local(_)
+            | Definition::Label(_)
+            | Definition::BuiltinAttr(_)
+            | Definition::ToolModule(_)
+    ) {
         return None;
     }
     def.module(db).map(|module| path(db, module, definition_owner_name(db, def)))
@@ -724,14 +602,6 @@ fn markup(docs: Option<String>, desc: String, mod_path: Option<String>) -> Optio
     Some(buf.into())
 }
 
-fn builtin(famous_defs: &FamousDefs<'_, '_>, builtin: hir::BuiltinType) -> Option<Markup> {
-    // std exposes prim_{} modules with docstrings on the root to document the builtins
-    let primitive_mod = format!("prim_{}", builtin.name().display(famous_defs.0.db));
-    let doc_owner = find_std_module(famous_defs, &primitive_mod)?;
-    let docs = doc_owner.docs(famous_defs.0.db)?;
-    markup(Some(docs.into()), builtin.name().display(famous_defs.0.db).to_string(), None)
-}
-
 fn find_std_module(famous_defs: &FamousDefs<'_, '_>, name: &str) -> Option<hir::Module> {
     let db = famous_defs.0.db;
     let std_crate = famous_defs.std()?;
@@ -741,34 +611,6 @@ fn find_std_module(famous_defs: &FamousDefs<'_, '_>, name: &str) -> Option<hir::
     })
 }
 
-fn local(db: &RootDatabase, it: hir::Local, config: &HoverConfig) -> Option<Markup> {
-    let ty = it.ty(db);
-    let ty = ty.display_truncated(db, None);
-    let is_mut = if it.is_mut(db) { "mut " } else { "" };
-    let mut desc = match it.primary_source(db).into_ident_pat() {
-        Some(ident) => {
-            let name = it.name(db);
-            let let_kw = if ident
-                .syntax()
-                .parent()
-                .map_or(false, |p| p.kind() == LET_STMT || p.kind() == LET_EXPR)
-            {
-                "let "
-            } else {
-                ""
-            };
-            format!("{let_kw}{is_mut}{}: {ty}", name.display(db))
-        }
-        None => format!("{is_mut}self: {ty}"),
-    };
-    if let Some(layout) =
-        render_memory_layout(config.memory_layout, || it.ty(db).layout(db), |_| None, |_| None)
-    {
-        format_to!(desc, "{layout}");
-    }
-    markup(None, desc, None)
-}
-
 fn render_memory_layout(
     config: Option<MemoryLayoutHoverConfig>,
     layout: impl FnOnce() -> Result<Layout, LayoutError>,
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 6ff16b9e2f7..e5da1a24c84 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -99,7 +99,10 @@ pub use crate::{
     },
     join_lines::JoinLinesConfig,
     markup::Markup,
-    moniker::{MonikerDescriptorKind, MonikerKind, MonikerResult, PackageInformation},
+    moniker::{
+        MonikerDescriptorKind, MonikerKind, MonikerResult, PackageInformation,
+        SymbolInformationKind,
+    },
     move_item::Direction,
     navigation_target::{NavigationTarget, UpmappingResult},
     prime_caches::ParallelPrimeCachesProgress,
diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs
index 8e8bb5e0139..94ddd162de4 100644
--- a/crates/ide/src/moniker.rs
+++ b/crates/ide/src/moniker.rs
@@ -1,7 +1,7 @@
 //! This module generates [moniker](https://microsoft.github.io/language-server-protocol/specifications/lsif/0.6.0/specification/#exportsImports)
 //! for LSIF and LSP.
 
-use hir::{AsAssocItem, AssocItemContainer, Crate, DescendPreference, Semantics};
+use hir::{Adt, AsAssocItem, AssocItemContainer, Crate, DescendPreference, MacroKind, Semantics};
 use ide_db::{
     base_db::{CrateOrigin, FilePosition, LangCrateOrigin},
     defs::{Definition, IdentClass},
@@ -25,6 +25,62 @@ pub enum MonikerDescriptorKind {
     Meta,
 }
 
+// Subset of scip_types::SymbolInformation::Kind
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum SymbolInformationKind {
+    AssociatedType,
+    Attribute,
+    Constant,
+    Enum,
+    EnumMember,
+    Field,
+    Function,
+    Macro,
+    Method,
+    Module,
+    Parameter,
+    SelfParameter,
+    StaticMethod,
+    StaticVariable,
+    Struct,
+    Trait,
+    TraitMethod,
+    Type,
+    TypeAlias,
+    TypeParameter,
+    Union,
+    Variable,
+}
+
+impl From<SymbolInformationKind> for MonikerDescriptorKind {
+    fn from(value: SymbolInformationKind) -> Self {
+        match value {
+            SymbolInformationKind::AssociatedType => Self::TypeParameter,
+            SymbolInformationKind::Attribute => Self::Macro,
+            SymbolInformationKind::Constant => Self::Term,
+            SymbolInformationKind::Enum => Self::Type,
+            SymbolInformationKind::EnumMember => Self::Type,
+            SymbolInformationKind::Field => Self::Term,
+            SymbolInformationKind::Function => Self::Method,
+            SymbolInformationKind::Macro => Self::Macro,
+            SymbolInformationKind::Method => Self::Method,
+            SymbolInformationKind::Module => Self::Namespace,
+            SymbolInformationKind::Parameter => Self::Parameter,
+            SymbolInformationKind::SelfParameter => Self::Parameter,
+            SymbolInformationKind::StaticMethod => Self::Method,
+            SymbolInformationKind::StaticVariable => Self::Meta,
+            SymbolInformationKind::Struct => Self::Type,
+            SymbolInformationKind::Trait => Self::Type,
+            SymbolInformationKind::TraitMethod => Self::Method,
+            SymbolInformationKind::Type => Self::Type,
+            SymbolInformationKind::TypeAlias => Self::Type,
+            SymbolInformationKind::TypeParameter => Self::TypeParameter,
+            SymbolInformationKind::Union => Self::Type,
+            SymbolInformationKind::Variable => Self::Term,
+        }
+    }
+}
+
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct MonikerDescriptor {
     pub name: String,
@@ -112,6 +168,69 @@ pub(crate) fn moniker(
     Some(RangeInfo::new(original_token.text_range(), navs))
 }
 
+pub(crate) fn def_to_kind(db: &RootDatabase, def: Definition) -> SymbolInformationKind {
+    use SymbolInformationKind::*;
+
+    match def {
+        Definition::Macro(it) => match it.kind(db) {
+            MacroKind::Declarative => Macro,
+            MacroKind::Derive => Attribute,
+            MacroKind::BuiltIn => Macro,
+            MacroKind::Attr => Attribute,
+            MacroKind::ProcMacro => Macro,
+        },
+        Definition::Field(..) => Field,
+        Definition::Module(..) => Module,
+        Definition::Function(it) => {
+            if it.as_assoc_item(db).is_some() {
+                if it.has_self_param(db) {
+                    if it.has_body(db) {
+                        Method
+                    } else {
+                        TraitMethod
+                    }
+                } else {
+                    StaticMethod
+                }
+            } else {
+                Function
+            }
+        }
+        Definition::Adt(Adt::Struct(..)) => Struct,
+        Definition::Adt(Adt::Union(..)) => Union,
+        Definition::Adt(Adt::Enum(..)) => Enum,
+        Definition::Variant(..) => EnumMember,
+        Definition::Const(..) => Constant,
+        Definition::Static(..) => StaticVariable,
+        Definition::Trait(..) => Trait,
+        Definition::TraitAlias(..) => Trait,
+        Definition::TypeAlias(it) => {
+            if it.as_assoc_item(db).is_some() {
+                AssociatedType
+            } else {
+                TypeAlias
+            }
+        }
+        Definition::BuiltinType(..) => Type,
+        Definition::SelfType(..) => TypeAlias,
+        Definition::GenericParam(..) => TypeParameter,
+        Definition::Local(it) => {
+            if it.is_self(db) {
+                SelfParameter
+            } else if it.is_param(db) {
+                Parameter
+            } else {
+                Variable
+            }
+        }
+        Definition::Label(..) => Variable, // For lack of a better variant
+        Definition::DeriveHelper(..) => Attribute,
+        Definition::BuiltinAttr(..) => Attribute,
+        Definition::ToolModule(..) => Module,
+        Definition::ExternCrateDecl(..) => Module,
+    }
+}
+
 pub(crate) fn def_to_moniker(
     db: &RootDatabase,
     def: Definition,
@@ -134,7 +253,7 @@ pub(crate) fn def_to_moniker(
     description.extend(module.path_to_root(db).into_iter().filter_map(|x| {
         Some(MonikerDescriptor {
             name: x.name(db)?.display(db).to_string(),
-            desc: MonikerDescriptorKind::Namespace,
+            desc: def_to_kind(db, x.into()).into(),
         })
     }));
 
@@ -147,7 +266,7 @@ pub(crate) fn def_to_moniker(
                 // we have to include the trait name as part of the moniker for uniqueness.
                 description.push(MonikerDescriptor {
                     name: trait_.name(db).display(db).to_string(),
-                    desc: MonikerDescriptorKind::Type,
+                    desc: def_to_kind(db, trait_.into()).into(),
                 });
             }
             AssocItemContainer::Impl(impl_) => {
@@ -156,14 +275,14 @@ pub(crate) fn def_to_moniker(
                 if let Some(adt) = impl_.self_ty(db).as_adt() {
                     description.push(MonikerDescriptor {
                         name: adt.name(db).display(db).to_string(),
-                        desc: MonikerDescriptorKind::Type,
+                        desc: def_to_kind(db, adt.into()).into(),
                     });
                 }
 
                 if let Some(trait_) = impl_.trait_(db) {
                     description.push(MonikerDescriptor {
                         name: trait_.name(db).display(db).to_string(),
-                        desc: MonikerDescriptorKind::Type,
+                        desc: def_to_kind(db, trait_.into()).into(),
                     });
                 }
             }
@@ -173,21 +292,26 @@ pub(crate) fn def_to_moniker(
     if let Definition::Field(it) = def {
         description.push(MonikerDescriptor {
             name: it.parent_def(db).name(db).display(db).to_string(),
-            desc: MonikerDescriptorKind::Type,
+            desc: def_to_kind(db, it.parent_def(db).into()).into(),
         });
     }
 
     // Qualify locals/parameters by their parent definition name.
     if let Definition::Local(it) = def {
-        let parent_name = it.parent(db).name(db);
-        if let Some(name) = parent_name {
-            description.push(MonikerDescriptor {
-                name: name.display(db).to_string(),
-                desc: MonikerDescriptorKind::Method,
-            });
+        let parent = Definition::try_from(it.parent(db)).ok();
+        if let Some(parent) = parent {
+            let parent_name = parent.name(db);
+            if let Some(name) = parent_name {
+                description.push(MonikerDescriptor {
+                    name: name.display(db).to_string(),
+                    desc: def_to_kind(db, parent).into(),
+                });
+            }
         }
     }
 
+    let desc = def_to_kind(db, def).into();
+
     let name_desc = match def {
         // These are handled by top-level guard (for performance).
         Definition::GenericParam(_)
@@ -201,67 +325,51 @@ pub(crate) fn def_to_moniker(
                 return None;
             }
 
-            MonikerDescriptor {
-                name: local.name(db).display(db).to_string(),
-                desc: MonikerDescriptorKind::Parameter,
-            }
+            MonikerDescriptor { name: local.name(db).display(db).to_string(), desc }
+        }
+        Definition::Macro(m) => {
+            MonikerDescriptor { name: m.name(db).display(db).to_string(), desc }
+        }
+        Definition::Function(f) => {
+            MonikerDescriptor { name: f.name(db).display(db).to_string(), desc }
+        }
+        Definition::Variant(v) => {
+            MonikerDescriptor { name: v.name(db).display(db).to_string(), desc }
+        }
+        Definition::Const(c) => {
+            MonikerDescriptor { name: c.name(db)?.display(db).to_string(), desc }
+        }
+        Definition::Trait(trait_) => {
+            MonikerDescriptor { name: trait_.name(db).display(db).to_string(), desc }
+        }
+        Definition::TraitAlias(ta) => {
+            MonikerDescriptor { name: ta.name(db).display(db).to_string(), desc }
+        }
+        Definition::TypeAlias(ta) => {
+            MonikerDescriptor { name: ta.name(db).display(db).to_string(), desc }
+        }
+        Definition::Module(m) => {
+            MonikerDescriptor { name: m.name(db)?.display(db).to_string(), desc }
+        }
+        Definition::BuiltinType(b) => {
+            MonikerDescriptor { name: b.name().display(db).to_string(), desc }
         }
-        Definition::Macro(m) => MonikerDescriptor {
-            name: m.name(db).display(db).to_string(),
-            desc: MonikerDescriptorKind::Macro,
-        },
-        Definition::Function(f) => MonikerDescriptor {
-            name: f.name(db).display(db).to_string(),
-            desc: MonikerDescriptorKind::Method,
-        },
-        Definition::Variant(v) => MonikerDescriptor {
-            name: v.name(db).display(db).to_string(),
-            desc: MonikerDescriptorKind::Type,
-        },
-        Definition::Const(c) => MonikerDescriptor {
-            name: c.name(db)?.display(db).to_string(),
-            desc: MonikerDescriptorKind::Term,
-        },
-        Definition::Trait(trait_) => MonikerDescriptor {
-            name: trait_.name(db).display(db).to_string(),
-            desc: MonikerDescriptorKind::Type,
-        },
-        Definition::TraitAlias(ta) => MonikerDescriptor {
-            name: ta.name(db).display(db).to_string(),
-            desc: MonikerDescriptorKind::Type,
-        },
-        Definition::TypeAlias(ta) => MonikerDescriptor {
-            name: ta.name(db).display(db).to_string(),
-            desc: MonikerDescriptorKind::TypeParameter,
-        },
-        Definition::Module(m) => MonikerDescriptor {
-            name: m.name(db)?.display(db).to_string(),
-            desc: MonikerDescriptorKind::Namespace,
-        },
-        Definition::BuiltinType(b) => MonikerDescriptor {
-            name: b.name().display(db).to_string(),
-            desc: MonikerDescriptorKind::Type,
-        },
         Definition::SelfType(imp) => MonikerDescriptor {
             name: imp.self_ty(db).as_adt()?.name(db).display(db).to_string(),
-            desc: MonikerDescriptorKind::Type,
-        },
-        Definition::Field(it) => MonikerDescriptor {
-            name: it.name(db).display(db).to_string(),
-            desc: MonikerDescriptorKind::Term,
-        },
-        Definition::Adt(adt) => MonikerDescriptor {
-            name: adt.name(db).display(db).to_string(),
-            desc: MonikerDescriptorKind::Type,
-        },
-        Definition::Static(s) => MonikerDescriptor {
-            name: s.name(db).display(db).to_string(),
-            desc: MonikerDescriptorKind::Meta,
-        },
-        Definition::ExternCrateDecl(m) => MonikerDescriptor {
-            name: m.name(db).display(db).to_string(),
-            desc: MonikerDescriptorKind::Namespace,
+            desc,
         },
+        Definition::Field(it) => {
+            MonikerDescriptor { name: it.name(db).display(db).to_string(), desc }
+        }
+        Definition::Adt(adt) => {
+            MonikerDescriptor { name: adt.name(db).display(db).to_string(), desc }
+        }
+        Definition::Static(s) => {
+            MonikerDescriptor { name: s.name(db).display(db).to_string(), desc }
+        }
+        Definition::ExternCrateDecl(m) => {
+            MonikerDescriptor { name: m.name(db).display(db).to_string(), desc }
+        }
     };
 
     description.push(name_desc);
diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs
index 6cb7d7724d5..8dcbea50920 100644
--- a/crates/ide/src/navigation_target.rs
+++ b/crates/ide/src/navigation_target.rs
@@ -724,11 +724,8 @@ fn orig_range_with_focus(
 ) -> UpmappingResult<(FileRange, Option<TextRange>)> {
     let Some(name) = name else { return orig_range(db, hir_file, value) };
 
-    let call_range = || {
-        db.lookup_intern_macro_call(hir_file.macro_file().unwrap().macro_call_id)
-            .kind
-            .original_call_range(db)
-    };
+    let call_kind =
+        || db.lookup_intern_macro_call(hir_file.macro_file().unwrap().macro_call_id).kind;
 
     let def_range = || {
         db.lookup_intern_macro_call(hir_file.macro_file().unwrap().macro_call_id)
@@ -755,7 +752,22 @@ fn orig_range_with_focus(
                             }
                             // name lies outside the node, so instead point to the macro call which
                             // *should* contain the name
-                            _ => call_range(),
+                            _ => {
+                                let kind = call_kind();
+                                let range = kind.clone().original_call_range_with_body(db);
+                                //If the focus range is in the attribute/derive body, we
+                                // need to point the call site to the entire body, if not, fall back
+                                // to the name range of the attribute/derive call
+                                // FIXME: Do this differently, this is very inflexible the caller
+                                // should choose this behavior
+                                if range.file_id == focus_range.file_id
+                                    && range.range.contains_range(focus_range.range)
+                                {
+                                    range
+                                } else {
+                                    kind.original_call_range(db)
+                                }
+                            }
                         },
                         Some(focus_range),
                     ),
@@ -784,7 +796,7 @@ fn orig_range_with_focus(
                     // node is in macro def, just show the focus
                     _ => (
                         // show the macro call
-                        (call_range(), None),
+                        (call_kind().original_call_range(db), None),
                         Some((focus_range, Some(focus_range))),
                     ),
                 }
diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs
index 3724dc28221..47fe2472a5e 100644
--- a/crates/ide/src/static_index.rs
+++ b/crates/ide/src/static_index.rs
@@ -17,7 +17,7 @@ use crate::navigation_target::UpmappingResult;
 use crate::{
     hover::hover_for_definition,
     inlay_hints::AdjustmentHintsMode,
-    moniker::{def_to_moniker, MonikerResult},
+    moniker::{def_to_kind, def_to_moniker, MonikerResult, SymbolInformationKind},
     parent_module::crates_for,
     Analysis, Fold, HoverConfig, HoverResult, InlayHint, InlayHintsConfig, TryToNav,
 };
@@ -46,6 +46,10 @@ pub struct TokenStaticData {
     pub definition: Option<FileRange>,
     pub references: Vec<ReferenceData>,
     pub moniker: Option<MonikerResult>,
+    pub display_name: Option<String>,
+    pub enclosing_moniker: Option<MonikerResult>,
+    pub signature: Option<String>,
+    pub kind: SymbolInformationKind,
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -172,6 +176,12 @@ impl StaticIndex<'_> {
                     }),
                     references: vec![],
                     moniker: current_crate.and_then(|cc| def_to_moniker(self.db, def, cc)),
+                    display_name: def.name(self.db).map(|name| name.display(self.db).to_string()),
+                    enclosing_moniker: current_crate
+                        .zip(def.enclosing_definition(self.db))
+                        .and_then(|(cc, enclosing_def)| def_to_moniker(self.db, enclosing_def, cc)),
+                    signature: def.label(self.db),
+                    kind: def_to_kind(self.db, def),
                 });
                 self.def_map.insert(def, it);
                 it
diff --git a/crates/rust-analyzer/src/cli/scip.rs b/crates/rust-analyzer/src/cli/scip.rs
index 95c8798d43c..c86b2c0ba40 100644
--- a/crates/rust-analyzer/src/cli/scip.rs
+++ b/crates/rust-analyzer/src/cli/scip.rs
@@ -7,8 +7,8 @@ use std::{
 };
 
 use ide::{
-    LineCol, MonikerDescriptorKind, StaticIndex, StaticIndexedFile, TextRange, TokenId,
-    TokenStaticData,
+    LineCol, MonikerDescriptorKind, MonikerResult, StaticIndex, StaticIndexedFile,
+    SymbolInformationKind, TextRange, TokenId,
 };
 use ide_db::LineIndexDatabase;
 use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice};
@@ -78,6 +78,7 @@ impl flags::Scip {
 
         let mut symbols_emitted: HashSet<TokenId> = HashSet::default();
         let mut tokens_to_symbol: HashMap<TokenId, String> = HashMap::new();
+        let mut tokens_to_enclosing_symbol: HashMap<TokenId, Option<String>> = HashMap::new();
 
         for StaticIndexedFile { file_id, tokens, .. } in si.files {
             let mut local_count = 0;
@@ -109,10 +110,24 @@ impl flags::Scip {
                 let symbol = tokens_to_symbol
                     .entry(id)
                     .or_insert_with(|| {
-                        let symbol = token_to_symbol(token).unwrap_or_else(&mut new_local_symbol);
+                        let symbol = token
+                            .moniker
+                            .as_ref()
+                            .map(moniker_to_symbol)
+                            .unwrap_or_else(&mut new_local_symbol);
                         scip::symbol::format_symbol(symbol)
                     })
                     .clone();
+                let enclosing_symbol = tokens_to_enclosing_symbol
+                    .entry(id)
+                    .or_insert_with(|| {
+                        token
+                            .enclosing_moniker
+                            .as_ref()
+                            .map(moniker_to_symbol)
+                            .map(scip::symbol::format_symbol)
+                    })
+                    .clone();
 
                 let mut symbol_roles = Default::default();
 
@@ -128,15 +143,22 @@ impl flags::Scip {
                             .map(|hover| hover.markup.as_str())
                             .filter(|it| !it.is_empty())
                             .map(|it| vec![it.to_owned()]);
+                        let signature_documentation =
+                            token.signature.clone().map(|text| scip_types::Document {
+                                relative_path: relative_path.clone(),
+                                language: "rust".to_string(),
+                                text,
+                                ..Default::default()
+                            });
                         let symbol_info = scip_types::SymbolInformation {
                             symbol: symbol.clone(),
                             documentation: documentation.unwrap_or_default(),
                             relationships: Vec::new(),
                             special_fields: Default::default(),
-                            kind: Default::default(),
-                            display_name: String::new(),
-                            signature_documentation: Default::default(),
-                            enclosing_symbol: String::new(),
+                            kind: symbol_kind(token.kind).into(),
+                            display_name: token.display_name.clone().unwrap_or_default(),
+                            signature_documentation: signature_documentation.into(),
+                            enclosing_symbol: enclosing_symbol.unwrap_or_default(),
                         };
 
                         symbols.push(symbol_info)
@@ -228,14 +250,36 @@ fn new_descriptor(name: &str, suffix: scip_types::descriptor::Suffix) -> scip_ty
     }
 }
 
-/// Loosely based on `def_to_moniker`
-///
-/// Only returns a Symbol when it's a non-local symbol.
-///     So if the visibility isn't outside of a document, then it will return None
-fn token_to_symbol(token: &TokenStaticData) -> Option<scip_types::Symbol> {
-    use scip_types::descriptor::Suffix::*;
+fn symbol_kind(kind: SymbolInformationKind) -> scip_types::symbol_information::Kind {
+    use scip_types::symbol_information::Kind as ScipKind;
+    match kind {
+        SymbolInformationKind::AssociatedType => ScipKind::AssociatedType,
+        SymbolInformationKind::Attribute => ScipKind::Attribute,
+        SymbolInformationKind::Constant => ScipKind::Constant,
+        SymbolInformationKind::Enum => ScipKind::Enum,
+        SymbolInformationKind::EnumMember => ScipKind::EnumMember,
+        SymbolInformationKind::Field => ScipKind::Field,
+        SymbolInformationKind::Function => ScipKind::Function,
+        SymbolInformationKind::Macro => ScipKind::Macro,
+        SymbolInformationKind::Method => ScipKind::Method,
+        SymbolInformationKind::Module => ScipKind::Module,
+        SymbolInformationKind::Parameter => ScipKind::Parameter,
+        SymbolInformationKind::SelfParameter => ScipKind::SelfParameter,
+        SymbolInformationKind::StaticMethod => ScipKind::StaticMethod,
+        SymbolInformationKind::StaticVariable => ScipKind::StaticVariable,
+        SymbolInformationKind::Struct => ScipKind::Struct,
+        SymbolInformationKind::Trait => ScipKind::Trait,
+        SymbolInformationKind::TraitMethod => ScipKind::TraitMethod,
+        SymbolInformationKind::Type => ScipKind::Type,
+        SymbolInformationKind::TypeAlias => ScipKind::TypeAlias,
+        SymbolInformationKind::TypeParameter => ScipKind::TypeParameter,
+        SymbolInformationKind::Union => ScipKind::Union,
+        SymbolInformationKind::Variable => ScipKind::Variable,
+    }
+}
 
-    let moniker = token.moniker.as_ref()?;
+fn moniker_to_symbol(moniker: &MonikerResult) -> scip_types::Symbol {
+    use scip_types::descriptor::Suffix::*;
 
     let package_name = moniker.package_information.name.clone();
     let version = moniker.package_information.version.clone();
@@ -260,7 +304,7 @@ fn token_to_symbol(token: &TokenStaticData) -> Option<scip_types::Symbol> {
         })
         .collect();
 
-    Some(scip_types::Symbol {
+    scip_types::Symbol {
         scheme: "rust-analyzer".into(),
         package: Some(scip_types::Package {
             manager: "cargo".to_string(),
@@ -271,7 +315,7 @@ fn token_to_symbol(token: &TokenStaticData) -> Option<scip_types::Symbol> {
         .into(),
         descriptors,
         special_fields: Default::default(),
-    })
+    }
 }
 
 #[cfg(test)]
@@ -309,7 +353,7 @@ mod test {
             for &(range, id) in &file.tokens {
                 if range.contains(offset - TextSize::from(1)) {
                     let token = si.tokens.get(id).unwrap();
-                    found_symbol = token_to_symbol(token);
+                    found_symbol = token.moniker.as_ref().map(moniker_to_symbol);
                     break;
                 }
             }
@@ -360,6 +404,21 @@ pub mod module {
     }
 
     #[test]
+    fn symbol_for_trait_alias() {
+        check_symbol(
+            r#"
+//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library
+#![feature(trait_alias)]
+pub mod module {
+    pub trait MyTrait {}
+    pub trait MyTraitAlias$0 = MyTrait;
+}
+"#,
+            "rust-analyzer cargo foo 0.1.0 module/MyTraitAlias#",
+        );
+    }
+
+    #[test]
     fn symbol_for_trait_constant() {
         check_symbol(
             r#"
@@ -525,4 +584,15 @@ pub mod example_mod {
             "rust-analyzer cargo main . foo/Bar#",
         );
     }
+
+    #[test]
+    fn symbol_for_for_type_alias() {
+        check_symbol(
+            r#"
+    //- /lib.rs crate:main
+    pub type MyTypeAlias$0 = u8;
+    "#,
+            "rust-analyzer cargo main . MyTypeAlias#",
+        );
+    }
 }
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index 9fc19a7d074..2a65fba7b6b 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -186,18 +186,10 @@ $ pacman -S rust-analyzer
 
 ==== Gentoo Linux
 
-`rust-analyzer` is available in the GURU repository:
+`rust-analyzer` is installed when the `rust-analyzer` USE flag is set for `dev-lang/rust` or `dev-lang/rust-bin`.
+You also need to set the `rust-src` USE flag.
 
-- https://gitweb.gentoo.org/repo/proj/guru.git/tree/dev-util/rust-analyzer?id=9895cea62602cfe599bd48e0fb02127411ca6e81[`dev-util/rust-analyzer`] builds from source
-- https://gitweb.gentoo.org/repo/proj/guru.git/tree/dev-util/rust-analyzer-bin?id=9895cea62602cfe599bd48e0fb02127411ca6e81[`dev-util/rust-analyzer-bin`] installs an official binary release
-
-If not already, GURU must be enabled (e.g. using `app-eselect/eselect-repository`) and sync'd before running `emerge`:
-
-[source,bash]
-----
-$ eselect repository enable guru && emaint sync -r guru
-$ emerge rust-analyzer-bin
-----
+This is similar to using the `rustup` component, so it will install a version that lags behind the official releases on GitHub .
 
 ==== macOS
 
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 987d936943a..51a0aece820 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -5,6 +5,7 @@ import * as vscode from "vscode";
 import type { Env } from "./client";
 import { log } from "./util";
 import { expectNotUndefined, unwrapUndefinable } from "./undefinable";
+import type { JsonProject } from "./rust_project";
 
 export type RunnableEnvCfgItem = {
     mask?: string;
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index 63ae386c8ad..55163241c2a 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -23,6 +23,7 @@ import { execRevealDependency } from "./commands";
 import { PersistentState } from "./persistent_state";
 import { bootstrap } from "./bootstrap";
 import type { RustAnalyzerExtensionApi } from "./main";
+import type { JsonProject } from "./rust_project";
 
 // We only support local folders, not eg. Live Share (`vlsl:` scheme), so don't activate if
 // only those are in use. We use "Empty" to represent these scenarios
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 3073353674c..599cfb4ff77 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -6,6 +6,7 @@ import { type CommandFactory, Ctx, fetchWorkspace } from "./ctx";
 import * as diagnostics from "./diagnostics";
 import { activateTaskProvider } from "./tasks";
 import { setContextValue } from "./util";
+import type { JsonProject } from "./rust_project";
 
 const RUST_PROJECT_CONTEXT_NAME = "inRustProject";
 
diff --git a/editors/code/src/rust_project.ts b/editors/code/src/rust_project.ts
index 187a1a96c10..bf65ad43ba5 100644
--- a/editors/code/src/rust_project.ts
+++ b/editors/code/src/rust_project.ts
@@ -1,4 +1,4 @@
-interface JsonProject {
+export interface JsonProject {
     /// Path to the directory with *source code* of
     /// sysroot crates.
     ///
@@ -21,7 +21,7 @@ interface JsonProject {
     crates: Crate[];
 }
 
-interface Crate {
+export interface Crate {
     /// Optional crate name used for display purposes,
     /// without affecting semantics. See the `deps`
     /// key for semantically-significant crate names.
@@ -82,7 +82,7 @@ interface Crate {
     proc_macro_dylib_path?: string;
 }
 
-interface Dep {
+export interface Dep {
     /// Index of a crate in the `crates` array.
     crate: number;
     /// Name as should appear in the (implicit)
diff --git a/editors/code/tsconfig.json b/editors/code/tsconfig.json
index c74284a00d9..87cfd1b2ee1 100644
--- a/editors/code/tsconfig.json
+++ b/editors/code/tsconfig.json
@@ -2,7 +2,7 @@
     "extends": "@tsconfig/strictest/tsconfig.json",
     "compilerOptions": {
         "esModuleInterop": false,
-        "module": "CommonJS",
+        "module": "Node16",
         "moduleResolution": "Node16",
         "target": "ES2021",
         "outDir": "out",