about summary refs log tree commit diff
path: root/src/tools/rust-analyzer/crates/ide-completion
diff options
context:
space:
mode:
authorLaurențiu Nicola <lnicola@dend.ro>2023-06-05 12:04:23 +0300
committerLaurențiu Nicola <lnicola@dend.ro>2023-06-05 12:04:23 +0300
commitb8a7d439db0cfd765ed4bfedd2bbaeeee58b05a5 (patch)
tree5adcbc6cf50af3bebc2cd4f42d5252a4d728690e /src/tools/rust-analyzer/crates/ide-completion
parent51f714c8c5021fe25442e46798b1cbef2f2249ed (diff)
parentaa9bc8612514d216f84eec218dfd19ab83f3598a (diff)
downloadrust-b8a7d439db0cfd765ed4bfedd2bbaeeee58b05a5.tar.gz
rust-b8a7d439db0cfd765ed4bfedd2bbaeeee58b05a5.zip
Merge commit 'aa9bc8612514d216f84eec218dfd19ab83f3598a' into sync-from-ra
Diffstat (limited to 'src/tools/rust-analyzer/crates/ide-completion')
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions.rs132
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs39
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs18
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs84
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs41
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs47
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs12
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs13
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context.rs45
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/item.rs49
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/lib.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render.rs52
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs20
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests.rs22
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs111
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs56
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs54
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs60
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs42
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs291
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs52
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs47
48 files changed, 1168 insertions, 268 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
index c3136f6df4b..480cb77b4fd 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
@@ -23,8 +23,8 @@ pub(crate) mod env_vars;
 
 use std::iter;
 
-use hir::{known, ScopeDef, Variant};
-use ide_db::{imports::import_assets::LocatedImport, SymbolKind};
+use hir::{known, HasAttrs, ScopeDef, Variant};
+use ide_db::{imports::import_assets::LocatedImport, RootDatabase, SymbolKind};
 use syntax::ast;
 
 use crate::{
@@ -62,8 +62,8 @@ impl From<Completions> for Vec<CompletionItem> {
 impl Builder {
     /// Convenience method, which allows to add a freshly created completion into accumulator
     /// without binding it to the variable.
-    pub(crate) fn add_to(self, acc: &mut Completions) {
-        acc.add(self.build())
+    pub(crate) fn add_to(self, acc: &mut Completions, db: &RootDatabase) {
+        acc.add(self.build(db))
     }
 }
 
@@ -78,17 +78,9 @@ impl Completions {
         }
     }
 
-    pub(crate) fn add_all<I>(&mut self, items: I)
-    where
-        I: IntoIterator,
-        I::Item: Into<CompletionItem>,
-    {
-        items.into_iter().for_each(|item| self.add(item.into()))
-    }
-
     pub(crate) fn add_keyword(&mut self, ctx: &CompletionContext<'_>, keyword: &'static str) {
         let item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), keyword);
-        item.add_to(self);
+        item.add_to(self, ctx.db);
     }
 
     pub(crate) fn add_nameref_keywords_with_colon(&mut self, ctx: &CompletionContext<'_>) {
@@ -142,7 +134,7 @@ impl Completions {
                 item.insert_text(if snippet.contains('$') { kw } else { snippet });
             }
         };
-        item.add_to(self);
+        item.add_to(self, ctx.db);
     }
 
     pub(crate) fn add_keyword_snippet(
@@ -157,7 +149,7 @@ impl Completions {
             Some(cap) => item.insert_snippet(cap, snippet),
             None => item.insert_text(if snippet.contains('$') { kw } else { snippet }),
         };
-        item.add_to(self);
+        item.add_to(self, ctx.db);
     }
 
     pub(crate) fn add_crate_roots(
@@ -165,9 +157,9 @@ impl Completions {
         ctx: &CompletionContext<'_>,
         path_ctx: &PathCompletionCtx,
     ) {
-        ctx.process_all_names(&mut |name, res| match res {
-            ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root(ctx.db) => {
-                self.add_module(ctx, path_ctx, m, name);
+        ctx.process_all_names(&mut |name, res, doc_aliases| match res {
+            ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root() => {
+                self.add_module(ctx, path_ctx, m, name, doc_aliases);
             }
             _ => (),
         });
@@ -179,7 +171,11 @@ impl Completions {
         path_ctx: &PathCompletionCtx,
         local_name: hir::Name,
         resolution: hir::ScopeDef,
+        doc_aliases: Vec<syntax::SmolStr>,
     ) {
+        if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) {
+            return;
+        }
         let is_private_editable = match ctx.def_is_visible(&resolution) {
             Visible::Yes => false,
             Visible::Editable => true,
@@ -187,12 +183,14 @@ impl Completions {
         };
         self.add(
             render_path_resolution(
-                RenderContext::new(ctx).private_editable(is_private_editable),
+                RenderContext::new(ctx)
+                    .private_editable(is_private_editable)
+                    .doc_aliases(doc_aliases),
                 path_ctx,
                 local_name,
                 resolution,
             )
-            .build(),
+            .build(ctx.db),
         );
     }
 
@@ -203,6 +201,9 @@ impl Completions {
         local_name: hir::Name,
         resolution: hir::ScopeDef,
     ) {
+        if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) {
+            return;
+        }
         let is_private_editable = match ctx.def_is_visible(&resolution) {
             Visible::Yes => false,
             Visible::Editable => true,
@@ -215,7 +216,7 @@ impl Completions {
                 local_name,
                 resolution,
             )
-            .build(),
+            .build(ctx.db),
         );
     }
 
@@ -225,6 +226,9 @@ impl Completions {
         path_ctx: &PathCompletionCtx,
         e: hir::Enum,
     ) {
+        if !ctx.check_stability(Some(&e.attrs(ctx.db))) {
+            return;
+        }
         e.variants(ctx.db)
             .into_iter()
             .for_each(|variant| self.add_enum_variant(ctx, path_ctx, variant, None));
@@ -236,12 +240,17 @@ impl Completions {
         path_ctx: &PathCompletionCtx,
         module: hir::Module,
         local_name: hir::Name,
+        doc_aliases: Vec<syntax::SmolStr>,
     ) {
+        if !ctx.check_stability(Some(&module.attrs(ctx.db))) {
+            return;
+        }
         self.add_path_resolution(
             ctx,
             path_ctx,
             local_name,
             hir::ScopeDef::ModuleDef(module.into()),
+            doc_aliases,
         );
     }
 
@@ -252,6 +261,9 @@ impl Completions {
         mac: hir::Macro,
         local_name: hir::Name,
     ) {
+        if !ctx.check_stability(Some(&mac.attrs(ctx.db))) {
+            return;
+        }
         let is_private_editable = match ctx.is_visible(&mac) {
             Visible::Yes => false,
             Visible::Editable => true,
@@ -264,7 +276,7 @@ impl Completions {
                 local_name,
                 mac,
             )
-            .build(),
+            .build(ctx.db),
         );
     }
 
@@ -275,19 +287,25 @@ impl Completions {
         func: hir::Function,
         local_name: Option<hir::Name>,
     ) {
+        if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
+            return;
+        }
         let is_private_editable = match ctx.is_visible(&func) {
             Visible::Yes => false,
             Visible::Editable => true,
             Visible::No => return,
         };
+        let doc_aliases = ctx.doc_aliases(&func);
         self.add(
             render_fn(
-                RenderContext::new(ctx).private_editable(is_private_editable),
+                RenderContext::new(ctx)
+                    .private_editable(is_private_editable)
+                    .doc_aliases(doc_aliases),
                 path_ctx,
                 local_name,
                 func,
             )
-            .build(),
+            .build(ctx.db),
         );
     }
 
@@ -299,20 +317,26 @@ impl Completions {
         receiver: Option<hir::Name>,
         local_name: Option<hir::Name>,
     ) {
+        if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
+            return;
+        }
         let is_private_editable = match ctx.is_visible(&func) {
             Visible::Yes => false,
             Visible::Editable => true,
             Visible::No => return,
         };
+        let doc_aliases = ctx.doc_aliases(&func);
         self.add(
             render_method(
-                RenderContext::new(ctx).private_editable(is_private_editable),
+                RenderContext::new(ctx)
+                    .private_editable(is_private_editable)
+                    .doc_aliases(doc_aliases),
                 dot_access,
                 receiver,
                 local_name,
                 func,
             )
-            .build(),
+            .build(ctx.db),
         );
     }
 
@@ -323,26 +347,34 @@ impl Completions {
         func: hir::Function,
         import: LocatedImport,
     ) {
+        if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
+            return;
+        }
         let is_private_editable = match ctx.is_visible(&func) {
             Visible::Yes => false,
             Visible::Editable => true,
             Visible::No => return,
         };
+        let doc_aliases = ctx.doc_aliases(&func);
         self.add(
             render_method(
                 RenderContext::new(ctx)
                     .private_editable(is_private_editable)
+                    .doc_aliases(doc_aliases)
                     .import_to_add(Some(import)),
                 dot_access,
                 None,
                 None,
                 func,
             )
-            .build(),
+            .build(ctx.db),
         );
     }
 
     pub(crate) fn add_const(&mut self, ctx: &CompletionContext<'_>, konst: hir::Const) {
+        if !ctx.check_stability(Some(&konst.attrs(ctx.db))) {
+            return;
+        }
         let is_private_editable = match ctx.is_visible(&konst) {
             Visible::Yes => false,
             Visible::Editable => true,
@@ -359,6 +391,9 @@ impl Completions {
         ctx: &CompletionContext<'_>,
         type_alias: hir::TypeAlias,
     ) {
+        if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) {
+            return;
+        }
         let is_private_editable = match ctx.is_visible(&type_alias) {
             Visible::Yes => false,
             Visible::Editable => true,
@@ -375,6 +410,9 @@ impl Completions {
         ctx: &CompletionContext<'_>,
         type_alias: hir::TypeAlias,
     ) {
+        if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) {
+            return;
+        }
         self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx), type_alias));
     }
 
@@ -385,10 +423,13 @@ impl Completions {
         variant: hir::Variant,
         path: hir::ModPath,
     ) {
+        if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
+            return;
+        }
         if let Some(builder) =
             render_variant_lit(RenderContext::new(ctx), path_ctx, None, variant, Some(path))
         {
-            self.add(builder.build());
+            self.add(builder.build(ctx.db));
         }
     }
 
@@ -399,6 +440,9 @@ impl Completions {
         variant: hir::Variant,
         local_name: Option<hir::Name>,
     ) {
+        if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
+            return;
+        }
         if let PathCompletionCtx { kind: PathKind::Pat { pat_ctx }, .. } = path_ctx {
             cov_mark::hit!(enum_variant_pattern_path);
             self.add_variant_pat(ctx, pat_ctx, Some(path_ctx), variant, local_name);
@@ -408,7 +452,7 @@ impl Completions {
         if let Some(builder) =
             render_variant_lit(RenderContext::new(ctx), path_ctx, local_name, variant, None)
         {
-            self.add(builder.build());
+            self.add(builder.build(ctx.db));
         }
     }
 
@@ -420,13 +464,17 @@ impl Completions {
         field: hir::Field,
         ty: &hir::Type,
     ) {
+        if !ctx.check_stability(Some(&field.attrs(ctx.db))) {
+            return;
+        }
         let is_private_editable = match ctx.is_visible(&field) {
             Visible::Yes => false,
             Visible::Editable => true,
             Visible::No => return,
         };
+        let doc_aliases = ctx.doc_aliases(&field);
         let item = render_field(
-            RenderContext::new(ctx).private_editable(is_private_editable),
+            RenderContext::new(ctx).private_editable(is_private_editable).doc_aliases(doc_aliases),
             dot_access,
             receiver,
             field,
@@ -443,10 +491,13 @@ impl Completions {
         path: Option<hir::ModPath>,
         local_name: Option<hir::Name>,
     ) {
+        if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) {
+            return;
+        }
         if let Some(builder) =
             render_struct_literal(RenderContext::new(ctx), path_ctx, strukt, path, local_name)
         {
-            self.add(builder.build());
+            self.add(builder.build(ctx.db));
         }
     }
 
@@ -457,6 +508,9 @@ impl Completions {
         path: Option<hir::ModPath>,
         local_name: Option<hir::Name>,
     ) {
+        if !ctx.check_stability(Some(&un.attrs(ctx.db))) {
+            return;
+        }
         let item = render_union_literal(RenderContext::new(ctx), un, path, local_name);
         self.add_opt(item);
     }
@@ -468,17 +522,20 @@ impl Completions {
         field: usize,
         ty: &hir::Type,
     ) {
+        // Only used for (unnamed) tuples, whose all fields *are* stable. No need to check
+        // stability here.
         let item = render_tuple_field(RenderContext::new(ctx), receiver, field, ty);
         self.add(item);
     }
 
     pub(crate) fn add_lifetime(&mut self, ctx: &CompletionContext<'_>, name: hir::Name) {
         CompletionItem::new(SymbolKind::LifetimeParam, ctx.source_range(), name.to_smol_str())
-            .add_to(self)
+            .add_to(self, ctx.db)
     }
 
     pub(crate) fn add_label(&mut self, ctx: &CompletionContext<'_>, name: hir::Name) {
-        CompletionItem::new(SymbolKind::Label, ctx.source_range(), name.to_smol_str()).add_to(self)
+        CompletionItem::new(SymbolKind::Label, ctx.source_range(), name.to_smol_str())
+            .add_to(self, ctx.db)
     }
 
     pub(crate) fn add_variant_pat(
@@ -489,6 +546,9 @@ impl Completions {
         variant: hir::Variant,
         local_name: Option<hir::Name>,
     ) {
+        if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
+            return;
+        }
         self.add_opt(render_variant_pat(
             RenderContext::new(ctx),
             pattern_ctx,
@@ -506,6 +566,9 @@ impl Completions {
         variant: hir::Variant,
         path: hir::ModPath,
     ) {
+        if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
+            return;
+        }
         let path = Some(&path);
         self.add_opt(render_variant_pat(
             RenderContext::new(ctx),
@@ -524,6 +587,9 @@ impl Completions {
         strukt: hir::Struct,
         local_name: Option<hir::Name>,
     ) {
+        if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) {
+            return;
+        }
         self.add_opt(render_struct_pat(RenderContext::new(ctx), pattern_ctx, strukt, local_name));
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs
index bb950c76f88..466f0b1fb7f 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs
@@ -93,7 +93,7 @@ pub(crate) fn complete_attribute_path(
                         acc.add_macro(ctx, path_ctx, m, name)
                     }
                     hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
-                        acc.add_module(ctx, path_ctx, m, name)
+                        acc.add_module(ctx, path_ctx, m, name, vec![])
                     }
                     _ => (),
                 }
@@ -104,12 +104,12 @@ pub(crate) fn complete_attribute_path(
         Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
         // only show modules in a fresh UseTree
         Qualified::No => {
-            ctx.process_all_names(&mut |name, def| match def {
+            ctx.process_all_names(&mut |name, def, doc_aliases| match def {
                 hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_attr(ctx.db) => {
                     acc.add_macro(ctx, path_ctx, m, name)
                 }
                 hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
-                    acc.add_module(ctx, path_ctx, m, name)
+                    acc.add_module(ctx, path_ctx, m, name, doc_aliases)
                 }
                 _ => (),
             });
@@ -139,7 +139,7 @@ pub(crate) fn complete_attribute_path(
         }
 
         if is_inner || !attr_completion.prefer_inner {
-            item.add_to(acc);
+            item.add_to(acc, ctx.db);
         }
     };
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs
index 7ef4ff30b56..19bfd294b25 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs
@@ -12,7 +12,7 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) {
     let add_completion = |item: &str| {
         let mut completion = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), item);
         completion.insert_text(format!(r#""{item}""#));
-        acc.add(completion.build());
+        acc.add(completion.build(ctx.db));
     };
 
     let previous = iter::successors(ctx.original_token.prev_token(), |t| {
@@ -33,11 +33,11 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) {
             let mut item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s);
             item.insert_text(insert_text);
 
-            acc.add(item.build());
+            acc.add(item.build(ctx.db));
         }),
         None => ctx.krate.potential_cfg(ctx.db).get_cfg_keys().cloned().unique().for_each(|s| {
             let item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s);
-            acc.add(item.build());
+            acc.add(item.build(ctx.db));
         }),
     };
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs
index 793c22630bf..9447bc7db0a 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs
@@ -34,7 +34,7 @@ pub(crate) fn complete_derive_path(
                         acc.add_macro(ctx, path_ctx, mac, name)
                     }
                     ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
-                        acc.add_module(ctx, path_ctx, m, name)
+                        acc.add_module(ctx, path_ctx, m, name, vec![])
                     }
                     _ => (),
                 }
@@ -43,7 +43,7 @@ pub(crate) fn complete_derive_path(
         Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
         // only show modules in a fresh UseTree
         Qualified::No => {
-            ctx.process_all_names(&mut |name, def| {
+            ctx.process_all_names(&mut |name, def, doc_aliases| {
                 let mac = match def {
                     ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac))
                         if !existing_derives.contains(&mac) && mac.is_derive(ctx.db) =>
@@ -51,7 +51,7 @@ pub(crate) fn complete_derive_path(
                         mac
                     }
                     ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
-                        return acc.add_module(ctx, path_ctx, m, name);
+                        return acc.add_module(ctx, path_ctx, m, name, doc_aliases);
                     }
                     _ => return,
                 };
@@ -90,7 +90,7 @@ pub(crate) fn complete_derive_path(
                             item.documentation(docs);
                         }
                         item.lookup_by(lookup);
-                        item.add_to(acc);
+                        item.add_to(acc, ctx.db);
                     }
                     None => acc.add_macro(ctx, path_ctx, mac, name),
                 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs
index 818c3cfd5fe..6bc6f34ed41 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs
@@ -56,6 +56,6 @@ pub(super) fn complete_lint(
         };
         let mut item = CompletionItem::new(SymbolKind::Attribute, ctx.source_range(), label);
         item.documentation(hir::Documentation::new(description.to_owned()));
-        item.add_to(acc)
+        item.add_to(acc, ctx.db)
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs
index a29417133e6..14f464b7753 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs
@@ -37,7 +37,7 @@ pub(super) fn complete_repr(
             if let Some((snippet, cap)) = snippet.zip(ctx.config.snippet_cap) {
                 item.insert_snippet(cap, snippet);
             }
-            item.add_to(acc);
+            item.add_to(acc, ctx.db);
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
index 77246379e7b..57a784c45b6 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
@@ -23,7 +23,7 @@ pub(crate) fn complete_dot(
         let mut item =
             CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), "await");
         item.detail("expr.await");
-        item.add_to(acc);
+        item.add_to(acc, ctx.db);
     }
 
     if let DotAccessKind::Method { .. } = dot_access.kind {
@@ -173,6 +173,43 @@ fn foo(s: S) { s.$0 }
     }
 
     #[test]
+    fn no_unstable_method_on_stable() {
+        check(
+            r#"
+//- /main.rs crate:main deps:std
+fn foo(s: std::S) { s.$0 }
+//- /std.rs crate:std
+pub struct S;
+impl S {
+    #[unstable]
+    pub fn bar(&self) {}
+}
+"#,
+            expect![""],
+        );
+    }
+
+    #[test]
+    fn unstable_method_on_nightly() {
+        check(
+            r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+fn foo(s: std::S) { s.$0 }
+//- /std.rs crate:std
+pub struct S;
+impl S {
+    #[unstable]
+    pub fn bar(&self) {}
+}
+"#,
+            expect![[r#"
+                me bar() fn(&self)
+            "#]],
+        );
+    }
+
+    #[test]
     fn test_struct_field_completion_self() {
         check(
             r#"
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs
index 1002be21131..419b8645655 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs
@@ -37,10 +37,10 @@ pub(crate) fn complete_cargo_env_vars(
     guard_env_macro(expanded, &ctx.sema)?;
     let range = expanded.text_range_between_quotes()?;
 
-    CARGO_DEFINED_VARS.iter().for_each(|(var, detail)| {
+    CARGO_DEFINED_VARS.into_iter().for_each(|&(var, detail)| {
         let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, var);
-        item.detail(*detail);
-        item.add_to(acc);
+        item.detail(detail);
+        item.add_to(acc, ctx.db);
     });
 
     Some(())
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
index cfe4787f734..9daa6984c3e 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
@@ -88,7 +88,13 @@ pub(crate) fn complete_expr_path(
                     let module_scope = module.scope(ctx.db, Some(ctx.module));
                     for (name, def) in module_scope {
                         if scope_def_applicable(def) {
-                            acc.add_path_resolution(ctx, path_ctx, name, def);
+                            acc.add_path_resolution(
+                                ctx,
+                                path_ctx,
+                                name,
+                                def,
+                                ctx.doc_aliases_in_scope(def),
+                            );
                         }
                     }
                 }
@@ -212,7 +218,7 @@ pub(crate) fn complete_expr_path(
                     }
                 }
             }
-            ctx.process_all_names(&mut |name, def| match def {
+            ctx.process_all_names(&mut |name, def, doc_aliases| match def {
                 ScopeDef::ModuleDef(hir::ModuleDef::Trait(t)) => {
                     let assocs = t.items_with_supertraits(ctx.db);
                     match &*assocs {
@@ -220,12 +226,14 @@ pub(crate) fn complete_expr_path(
                         // there is no associated item path that can be constructed with them
                         [] => (),
                         // FIXME: Render the assoc item with the trait qualified
-                        &[_item] => acc.add_path_resolution(ctx, path_ctx, name, def),
+                        &[_item] => acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases),
                         // FIXME: Append `::` to the thing here, since a trait on its own won't work
-                        [..] => acc.add_path_resolution(ctx, path_ctx, name, def),
+                        [..] => acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases),
                     }
                 }
-                _ if scope_def_applicable(def) => acc.add_path_resolution(ctx, path_ctx, name, def),
+                _ if scope_def_applicable(def) => {
+                    acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases)
+                }
                 _ => (),
             });
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs
index 4e89ef69608..c717a9cb55b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs
@@ -42,7 +42,7 @@ const SUPPORTED_CALLING_CONVENTIONS: &[&str] = &[
 
 pub(crate) fn complete_extern_abi(
     acc: &mut Completions,
-    _ctx: &CompletionContext<'_>,
+    ctx: &CompletionContext<'_>,
     expanded: &ast::String,
 ) -> Option<()> {
     if !expanded.syntax().parent().map_or(false, |it| ast::Abi::can_cast(it.kind())) {
@@ -51,7 +51,7 @@ pub(crate) fn complete_extern_abi(
     let abi_str = expanded;
     let source_range = abi_str.text_range_between_quotes()?;
     for &abi in SUPPORTED_CALLING_CONVENTIONS {
-        CompletionItem::new(CompletionItemKind::Keyword, source_range, abi).add_to(acc);
+        CompletionItem::new(CompletionItemKind::Keyword, source_range, abi).add_to(acc, ctx.db);
     }
     Some(())
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
index 0979f6a6dfc..39c1b7f7b3f 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
@@ -257,30 +257,24 @@ fn import_on_the_fly(
     };
     let user_input_lowercased = potential_import_name.to_lowercase();
 
-    acc.add_all(
-        import_assets
-            .search_for_imports(
-                &ctx.sema,
-                ctx.config.insert_use.prefix_kind,
-                ctx.config.prefer_no_std,
-            )
-            .into_iter()
-            .filter(ns_filter)
-            .filter(|import| {
-                !ctx.is_item_hidden(&import.item_to_import)
-                    && !ctx.is_item_hidden(&import.original_item)
-            })
-            .sorted_by_key(|located_import| {
-                compute_fuzzy_completion_order_key(
-                    &located_import.import_path,
-                    &user_input_lowercased,
-                )
-            })
-            .filter_map(|import| {
-                render_resolution_with_import(RenderContext::new(ctx), path_ctx, import)
-            })
-            .map(|builder| builder.build()),
-    );
+    import_assets
+        .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std)
+        .into_iter()
+        .filter(ns_filter)
+        .filter(|import| {
+            let original_item = &import.original_item;
+            !ctx.is_item_hidden(&import.item_to_import)
+                && !ctx.is_item_hidden(original_item)
+                && ctx.check_stability(original_item.attrs(ctx.db).as_deref())
+        })
+        .sorted_by_key(|located_import| {
+            compute_fuzzy_completion_order_key(&located_import.import_path, &user_input_lowercased)
+        })
+        .filter_map(|import| {
+            render_resolution_with_import(RenderContext::new(ctx), path_ctx, import)
+        })
+        .map(|builder| builder.build(ctx.db))
+        .for_each(|item| acc.add(item));
     Some(())
 }
 
@@ -305,30 +299,24 @@ fn import_on_the_fly_pat_(
     };
     let user_input_lowercased = potential_import_name.to_lowercase();
 
-    acc.add_all(
-        import_assets
-            .search_for_imports(
-                &ctx.sema,
-                ctx.config.insert_use.prefix_kind,
-                ctx.config.prefer_no_std,
-            )
-            .into_iter()
-            .filter(ns_filter)
-            .filter(|import| {
-                !ctx.is_item_hidden(&import.item_to_import)
-                    && !ctx.is_item_hidden(&import.original_item)
-            })
-            .sorted_by_key(|located_import| {
-                compute_fuzzy_completion_order_key(
-                    &located_import.import_path,
-                    &user_input_lowercased,
-                )
-            })
-            .filter_map(|import| {
-                render_resolution_with_import_pat(RenderContext::new(ctx), pattern_ctx, import)
-            })
-            .map(|builder| builder.build()),
-    );
+    import_assets
+        .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std)
+        .into_iter()
+        .filter(ns_filter)
+        .filter(|import| {
+            let original_item = &import.original_item;
+            !ctx.is_item_hidden(&import.item_to_import)
+                && !ctx.is_item_hidden(original_item)
+                && ctx.check_stability(original_item.attrs(ctx.db).as_deref())
+        })
+        .sorted_by_key(|located_import| {
+            compute_fuzzy_completion_order_key(&located_import.import_path, &user_input_lowercased)
+        })
+        .filter_map(|import| {
+            render_resolution_with_import_pat(RenderContext::new(ctx), pattern_ctx, import)
+        })
+        .map(|builder| builder.build(ctx.db))
+        .for_each(|item| acc.add(item));
     Some(())
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs
index d8b8a190eb8..8b38d4f01f6 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs
@@ -40,7 +40,7 @@ pub(crate) fn complete_fn_param(
         };
         // Completion lookup is omitted intentionally here.
         // See the full discussion: https://github.com/rust-lang/rust-analyzer/issues/12073
-        item.add_to(acc)
+        item.add_to(acc, ctx.db)
     };
 
     match kind {
@@ -50,7 +50,7 @@ pub(crate) fn complete_fn_param(
         ParamKind::Closure(closure) => {
             let stmt_list = closure.syntax().ancestors().find_map(ast::StmtList::cast)?;
             params_from_stmt_list_scope(ctx, stmt_list, |name, ty| {
-                add_new_item_to_acc(&format!("{name}: {ty}"));
+                add_new_item_to_acc(&format!("{}: {ty}", name.display(ctx.db)));
             });
         }
     }
@@ -100,7 +100,9 @@ fn fill_fn_params(
 
     if let Some(stmt_list) = function.syntax().parent().and_then(ast::StmtList::cast) {
         params_from_stmt_list_scope(ctx, stmt_list, |name, ty| {
-            file_params.entry(format!("{name}: {ty}")).or_insert(name.to_string());
+            file_params
+                .entry(format!("{}: {ty}", name.display(ctx.db)))
+                .or_insert(name.display(ctx.db).to_string());
         });
     }
     remove_duplicated(&mut file_params, param_list.params());
@@ -127,7 +129,7 @@ fn params_from_stmt_list_scope(
         let module = scope.module().into();
         scope.process_all_names(&mut |name, def| {
             if let hir::ScopeDef::Local(local) = def {
-                if let Ok(ty) = local.ty(ctx.db).display_source_code(ctx.db, module) {
+                if let Ok(ty) = local.ty(ctx.db).display_source_code(ctx.db, module, true) {
                     cb(name, ty);
                 }
             }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs
index 5c46c5806e6..8e904fd605a 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs
@@ -32,7 +32,7 @@ pub(crate) fn format_string(
     let source_range = TextRange::new(brace_offset, cursor);
     ctx.locals.iter().for_each(|(name, _)| {
         CompletionItem::new(CompletionItemKind::Binding, source_range, name.to_smol_str())
-            .add_to(acc);
+            .add_to(acc, ctx.db);
     })
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs
index 60d05ae46b9..5ea6a49b1ae 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs
@@ -45,7 +45,7 @@ pub(crate) fn complete_item_list(
                         acc.add_macro(ctx, path_ctx, m, name)
                     }
                     hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
-                        acc.add_module(ctx, path_ctx, m, name)
+                        acc.add_module(ctx, path_ctx, m, name, vec![])
                     }
                     _ => (),
                 }
@@ -55,12 +55,12 @@ pub(crate) fn complete_item_list(
         }
         Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
         Qualified::No if ctx.qualifier_ctx.none() => {
-            ctx.process_all_names(&mut |name, def| match def {
+            ctx.process_all_names(&mut |name, def, doc_aliases| match def {
                 hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_fn_like(ctx.db) => {
                     acc.add_macro(ctx, path_ctx, m, name)
                 }
                 hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
-                    acc.add_module(ctx, path_ctx, m, name)
+                    acc.add_module(ctx, path_ctx, m, name, doc_aliases)
                 }
                 _ => (),
             });
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
index 889d90095fa..7de1bf2dc13 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -150,21 +150,24 @@ fn complete_trait_impl(
     impl_def: &ast::Impl,
 ) {
     if let Some(hir_impl) = ctx.sema.to_def(impl_def) {
-        get_missing_assoc_items(&ctx.sema, impl_def).into_iter().for_each(|item| {
-            use self::ImplCompletionKind::*;
-            match (item, kind) {
-                (hir::AssocItem::Function(func), All | Fn) => {
-                    add_function_impl(acc, ctx, replacement_range, func, hir_impl)
+        get_missing_assoc_items(&ctx.sema, impl_def)
+            .into_iter()
+            .filter(|item| ctx.check_stability(Some(&item.attrs(ctx.db))))
+            .for_each(|item| {
+                use self::ImplCompletionKind::*;
+                match (item, kind) {
+                    (hir::AssocItem::Function(func), All | Fn) => {
+                        add_function_impl(acc, ctx, replacement_range, func, hir_impl)
+                    }
+                    (hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => {
+                        add_type_alias_impl(acc, ctx, replacement_range, type_alias, hir_impl)
+                    }
+                    (hir::AssocItem::Const(const_), All | Const) => {
+                        add_const_impl(acc, ctx, replacement_range, const_, hir_impl)
+                    }
+                    _ => {}
                 }
-                (hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => {
-                    add_type_alias_impl(acc, ctx, replacement_range, type_alias, hir_impl)
-                }
-                (hir::AssocItem::Const(const_), All | Const) => {
-                    add_const_impl(acc, ctx, replacement_range, const_, hir_impl)
-                }
-                _ => {}
-            }
-        });
+            });
     }
 }
 
@@ -179,7 +182,7 @@ fn add_function_impl(
 
     let label = format!(
         "fn {}({})",
-        fn_name,
+        fn_name.display(ctx.db),
         if func.assoc_fn_params(ctx.db).is_empty() { "" } else { ".." }
     );
 
@@ -190,7 +193,7 @@ fn add_function_impl(
     };
 
     let mut item = CompletionItem::new(completion_kind, replacement_range, label);
-    item.lookup_by(format!("fn {fn_name}"))
+    item.lookup_by(format!("fn {}", fn_name.display(ctx.db)))
         .set_documentation(func.docs(ctx.db))
         .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() });
 
@@ -213,7 +216,7 @@ fn add_function_impl(
                     item.text_edit(TextEdit::replace(replacement_range, header));
                 }
             };
-            item.add_to(acc);
+            item.add_to(acc, ctx.db);
         }
     }
 }
@@ -297,7 +300,7 @@ fn add_type_alias_impl(
                     item.text_edit(TextEdit::replace(replacement_range, decl));
                 }
             };
-            item.add_to(acc);
+            item.add_to(acc, ctx.db);
         }
     }
 }
@@ -337,7 +340,7 @@ fn add_const_impl(
                     ),
                     None => item.text_edit(TextEdit::replace(replacement_range, replacement)),
                 };
-                item.add_to(acc);
+                item.add_to(acc, ctx.db);
             }
         }
     }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs
index 3b79def639d..2c6cbf6146a 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs
@@ -329,6 +329,7 @@ fn foo() {
     fn complete_label_in_for_iterable() {
         check(
             r#"
+//- minicore: iterator
 fn foo() {
     'outer: for _ in [{ 'inner: loop { break '$0 } }] {}
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs
index 950731eb4ca..d3e75c6da47 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs
@@ -52,7 +52,7 @@ pub(crate) fn complete_mod(
 
     let existing_mod_declarations = current_module
         .children(ctx.db)
-        .filter_map(|module| Some(module.name(ctx.db)?.to_string()))
+        .filter_map(|module| Some(module.name(ctx.db)?.display(ctx.db).to_string()))
         .filter(|module| module != ctx.original_token.text())
         .collect::<FxHashSet<_>>();
 
@@ -99,7 +99,7 @@ pub(crate) fn complete_mod(
                 label.push(';');
             }
             let item = CompletionItem::new(SymbolKind::Module, ctx.source_range(), &label);
-            item.add_to(acc)
+            item.add_to(acc, ctx.db)
         });
 
     Some(())
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs
index 58d5bf114cc..40b2c831a5d 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs
@@ -64,7 +64,7 @@ pub(crate) fn complete_pattern(
 
     // FIXME: ideally, we should look at the type we are matching against and
     // suggest variants + auto-imports
-    ctx.process_all_names(&mut |name, res| {
+    ctx.process_all_names(&mut |name, res, _| {
         let add_simple_path = match res {
             hir::ScopeDef::ModuleDef(def) => match def {
                 hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => {
@@ -127,7 +127,7 @@ pub(crate) fn complete_pattern_path(
                         };
 
                         if add_resolution {
-                            acc.add_path_resolution(ctx, path_ctx, name, def);
+                            acc.add_path_resolution(ctx, path_ctx, name, def, vec![]);
                         }
                     }
                 }
@@ -164,7 +164,7 @@ pub(crate) fn complete_pattern_path(
         Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
         Qualified::No => {
             // this will only be hit if there are brackets or braces, otherwise this will be parsed as an ident pattern
-            ctx.process_all_names(&mut |name, res| {
+            ctx.process_all_names(&mut |name, res, doc_aliases| {
                 // FIXME: we should check what kind of pattern we are in and filter accordingly
                 let add_completion = match res {
                     ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => mac.is_fn_like(ctx.db),
@@ -175,7 +175,7 @@ pub(crate) fn complete_pattern_path(
                     _ => false,
                 };
                 if add_completion {
-                    acc.add_path_resolution(ctx, path_ctx, name, res);
+                    acc.add_path_resolution(ctx, path_ctx, name, res, doc_aliases);
                 }
             });
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
index c55bd9aaae5..2ffe1233744 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
@@ -64,7 +64,7 @@ pub(crate) fn complete_postfix(
                     &format!("drop($0{receiver_text})"),
                 );
                 item.set_documentation(drop_fn.docs(ctx.db));
-                item.add_to(acc);
+                item.add_to(acc, ctx.db);
             }
         }
     }
@@ -78,14 +78,14 @@ pub(crate) fn complete_postfix(
                     "if let Ok {}",
                     &format!("if let Ok($1) = {receiver_text} {{\n    $0\n}}"),
                 )
-                .add_to(acc);
+                .add_to(acc, ctx.db);
 
                 postfix_snippet(
                     "while",
                     "while let Ok {}",
                     &format!("while let Ok($1) = {receiver_text} {{\n    $0\n}}"),
                 )
-                .add_to(acc);
+                .add_to(acc, ctx.db);
             }
             TryEnum::Option => {
                 postfix_snippet(
@@ -93,22 +93,22 @@ pub(crate) fn complete_postfix(
                     "if let Some {}",
                     &format!("if let Some($1) = {receiver_text} {{\n    $0\n}}"),
                 )
-                .add_to(acc);
+                .add_to(acc, ctx.db);
 
                 postfix_snippet(
                     "while",
                     "while let Some {}",
                     &format!("while let Some($1) = {receiver_text} {{\n    $0\n}}"),
                 )
-                .add_to(acc);
+                .add_to(acc, ctx.db);
             }
         }
     } else if receiver_ty.is_bool() || receiver_ty.is_unknown() {
         postfix_snippet("if", "if expr {}", &format!("if {receiver_text} {{\n    $0\n}}"))
-            .add_to(acc);
+            .add_to(acc, ctx.db);
         postfix_snippet("while", "while expr {}", &format!("while {receiver_text} {{\n    $0\n}}"))
-            .add_to(acc);
-        postfix_snippet("not", "!expr", &format!("!{receiver_text}")).add_to(acc);
+            .add_to(acc, ctx.db);
+        postfix_snippet("not", "!expr", &format!("!{receiver_text}")).add_to(acc, ctx.db);
     } else if let Some(trait_) = ctx.famous_defs().core_iter_IntoIterator() {
         if receiver_ty.impls_trait(ctx.db, trait_, &[]) {
             postfix_snippet(
@@ -116,12 +116,12 @@ pub(crate) fn complete_postfix(
                 "for ele in expr {}",
                 &format!("for ele in {receiver_text} {{\n    $0\n}}"),
             )
-            .add_to(acc);
+            .add_to(acc, ctx.db);
         }
     }
 
-    postfix_snippet("ref", "&expr", &format!("&{receiver_text}")).add_to(acc);
-    postfix_snippet("refm", "&mut expr", &format!("&mut {receiver_text}")).add_to(acc);
+    postfix_snippet("ref", "&expr", &format!("&{receiver_text}")).add_to(acc, ctx.db);
+    postfix_snippet("refm", "&mut expr", &format!("&mut {receiver_text}")).add_to(acc, ctx.db);
 
     let mut unsafe_should_be_wrapped = true;
     if dot_receiver.syntax().kind() == BLOCK_EXPR {
@@ -137,7 +137,7 @@ pub(crate) fn complete_postfix(
     } else {
         format!("unsafe {receiver_text}")
     };
-    postfix_snippet("unsafe", "unsafe {}", &unsafe_completion_string).add_to(acc);
+    postfix_snippet("unsafe", "unsafe {}", &unsafe_completion_string).add_to(acc, ctx.db);
 
     // The rest of the postfix completions create an expression that moves an argument,
     // so it's better to consider references now to avoid breaking the compilation
@@ -162,7 +162,7 @@ pub(crate) fn complete_postfix(
                     "match expr {}",
                     &format!("match {receiver_text} {{\n    Ok(${{1:_}}) => {{$2}},\n    Err(${{3:_}}) => {{$0}},\n}}"),
                 )
-                .add_to(acc);
+                .add_to(acc, ctx.db);
             }
             TryEnum::Option => {
                 postfix_snippet(
@@ -172,7 +172,7 @@ pub(crate) fn complete_postfix(
                         "match {receiver_text} {{\n    Some(${{1:_}}) => {{$2}},\n    None => {{$0}},\n}}"
                     ),
                 )
-                .add_to(acc);
+                .add_to(acc, ctx.db);
             }
         },
         None => {
@@ -181,20 +181,23 @@ pub(crate) fn complete_postfix(
                 "match expr {}",
                 &format!("match {receiver_text} {{\n    ${{1:_}} => {{$0}},\n}}"),
             )
-            .add_to(acc);
+            .add_to(acc, ctx.db);
         }
     }
 
-    postfix_snippet("box", "Box::new(expr)", &format!("Box::new({receiver_text})")).add_to(acc);
-    postfix_snippet("dbg", "dbg!(expr)", &format!("dbg!({receiver_text})")).add_to(acc); // fixme
-    postfix_snippet("dbgr", "dbg!(&expr)", &format!("dbg!(&{receiver_text})")).add_to(acc);
-    postfix_snippet("call", "function(expr)", &format!("${{1}}({receiver_text})")).add_to(acc);
+    postfix_snippet("box", "Box::new(expr)", &format!("Box::new({receiver_text})"))
+        .add_to(acc, ctx.db);
+    postfix_snippet("dbg", "dbg!(expr)", &format!("dbg!({receiver_text})")).add_to(acc, ctx.db); // fixme
+    postfix_snippet("dbgr", "dbg!(&expr)", &format!("dbg!(&{receiver_text})")).add_to(acc, ctx.db);
+    postfix_snippet("call", "function(expr)", &format!("${{1}}({receiver_text})"))
+        .add_to(acc, ctx.db);
 
     if let Some(parent) = dot_receiver.syntax().parent().and_then(|p| p.parent()) {
         if matches!(parent.kind(), STMT_LIST | EXPR_STMT) {
-            postfix_snippet("let", "let", &format!("let $0 = {receiver_text};")).add_to(acc);
+            postfix_snippet("let", "let", &format!("let $0 = {receiver_text};"))
+                .add_to(acc, ctx.db);
             postfix_snippet("letm", "let mut", &format!("let mut $0 = {receiver_text};"))
-                .add_to(acc);
+                .add_to(acc, ctx.db);
         }
     }
 
@@ -315,7 +318,7 @@ fn add_custom_postfix_completions(
             for import in imports.into_iter() {
                 builder.add_import(import);
             }
-            builder.add_to(acc);
+            builder.add_to(acc, ctx.db);
         },
     );
     None
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs
index dfcc78e9230..cb242e4aa68 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs
@@ -60,7 +60,7 @@ pub(crate) fn add_format_like_completions(
                 format!(r#"{}({}, {})"#, macro_name, out, exprs.join(", "))
             };
 
-            postfix_snippet(label, macro_name, &snippet).add_to(acc);
+            postfix_snippet(label, macro_name, &snippet).add_to(acc, ctx.db);
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
index 0521e735ded..945c3945bfa 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
@@ -69,7 +69,7 @@ pub(crate) fn complete_record_expr_fields(
                 let mut item =
                     CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), "..");
                 item.insert_text(".");
-                item.add_to(acc);
+                item.add_to(acc, ctx.db);
                 return;
             }
             missing_fields
@@ -98,7 +98,7 @@ pub(crate) fn add_default_update(
             postfix_match: Some(CompletionRelevancePostfixMatch::Exact),
             ..Default::default()
         });
-        item.add_to(acc);
+        item.add_to(acc, ctx.db);
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs
index da1f0542d28..e9831a5b2a1 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs
@@ -32,8 +32,8 @@ pub(crate) fn complete_expr_snippet(
     }
 
     if in_block_expr {
-        snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
-        snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
+        snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc, ctx.db);
+        snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc, ctx.db);
         let item = snippet(
             ctx,
             cap,
@@ -45,7 +45,7 @@ macro_rules! $1 {
     };
 }",
         );
-        item.add_to(acc);
+        item.add_to(acc, ctx.db);
     }
 }
 
@@ -88,7 +88,7 @@ mod tests {
 }",
         );
         item.lookup_by("tmod");
-        item.add_to(acc);
+        item.add_to(acc, ctx.db);
 
         let mut item = snippet(
             ctx,
@@ -101,7 +101,7 @@ fn ${1:feature}() {
 }",
         );
         item.lookup_by("tfn");
-        item.add_to(acc);
+        item.add_to(acc, ctx.db);
 
         let item = snippet(
             ctx,
@@ -114,7 +114,7 @@ macro_rules! $1 {
     };
 }",
         );
-        item.add_to(acc);
+        item.add_to(acc, ctx.db);
     }
 }
 
@@ -146,7 +146,7 @@ fn add_custom_completions(
                 builder.add_import(import);
             }
             builder.set_detail(snip.description.clone());
-            builder.add_to(acc);
+            builder.add_to(acc, ctx.db);
         },
     );
     None
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs
index 69c05a76df4..e4705475638 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs
@@ -85,7 +85,7 @@ pub(crate) fn complete_type_path(
                     let module_scope = module.scope(ctx.db, Some(ctx.module));
                     for (name, def) in module_scope {
                         if scope_def_applicable(def) {
-                            acc.add_path_resolution(ctx, path_ctx, name, def);
+                            acc.add_path_resolution(ctx, path_ctx, name, def, vec![]);
                         }
                     }
                 }
@@ -141,7 +141,7 @@ pub(crate) fn complete_type_path(
             match location {
                 TypeLocation::TypeBound => {
                     acc.add_nameref_keywords_with_colon(ctx);
-                    ctx.process_all_names(&mut |name, res| {
+                    ctx.process_all_names(&mut |name, res, doc_aliases| {
                         let add_resolution = match res {
                             ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => {
                                 mac.is_fn_like(ctx.db)
@@ -152,7 +152,7 @@ pub(crate) fn complete_type_path(
                             _ => false,
                         };
                         if add_resolution {
-                            acc.add_path_resolution(ctx, path_ctx, name, res);
+                            acc.add_path_resolution(ctx, path_ctx, name, res, doc_aliases);
                         }
                     });
                     return;
@@ -215,9 +215,9 @@ pub(crate) fn complete_type_path(
             };
 
             acc.add_nameref_keywords_with_colon(ctx);
-            ctx.process_all_names(&mut |name, def| {
+            ctx.process_all_names(&mut |name, def, doc_aliases| {
                 if scope_def_applicable(def) {
-                    acc.add_path_resolution(ctx, path_ctx, name, def);
+                    acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases);
                 }
             });
         }
@@ -242,7 +242,7 @@ pub(crate) fn complete_ascribed_type(
         }
     }?
     .adjusted();
-    let ty_string = x.display_source_code(ctx.db, ctx.module.into()).ok()?;
+    let ty_string = x.display_source_code(ctx.db, ctx.module.into(), true).ok()?;
     acc.add(render_type_inference(ty_string, ctx));
     None
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs
index 2555c34aa74..7a60030e9ef 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs
@@ -52,6 +52,9 @@ pub(crate) fn complete_use_path(
                         )
                     };
                     for (name, def) in module_scope {
+                        if !ctx.check_stability(def.attrs(ctx.db).as_deref()) {
+                            continue;
+                        }
                         let is_name_already_imported = name
                             .as_text()
                             .map_or(false, |text| already_imported_names.contains(text.as_str()));
@@ -72,7 +75,7 @@ pub(crate) fn complete_use_path(
                                 is_name_already_imported,
                                 ..Default::default()
                             });
-                            acc.add(builder.build());
+                            acc.add(builder.build(ctx.db));
                         }
                     }
                 }
@@ -91,10 +94,10 @@ pub(crate) fn complete_use_path(
         // only show modules and non-std enum in a fresh UseTree
         Qualified::No => {
             cov_mark::hit!(unqualified_path_selected_only);
-            ctx.process_all_names(&mut |name, res| {
+            ctx.process_all_names(&mut |name, res, doc_aliases| {
                 match res {
                     ScopeDef::ModuleDef(hir::ModuleDef::Module(module)) => {
-                        acc.add_module(ctx, path_ctx, module, name);
+                        acc.add_module(ctx, path_ctx, module, name, doc_aliases);
                     }
                     ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => {
                         // exclude prelude enum
@@ -105,9 +108,9 @@ pub(crate) fn complete_use_path(
                             let item = CompletionItem::new(
                                 CompletionItemKind::SymbolKind(SymbolKind::Enum),
                                 ctx.source_range(),
-                                format!("{}::", e.name(ctx.db)),
+                                format!("{}::", e.name(ctx.db).display(ctx.db)),
                             );
-                            acc.add(item.build());
+                            acc.add(item.build(ctx.db));
                         }
                     }
                     _ => {}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs
index 5e6cf4bf9a5..e0a959ad0b3 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs
@@ -23,7 +23,7 @@ pub(crate) fn complete_vis_path(
             if let Some(next) = next_towards_current {
                 if let Some(name) = next.name(ctx.db) {
                     cov_mark::hit!(visibility_qualified);
-                    acc.add_module(ctx, path_ctx, next, name);
+                    acc.add_module(ctx, path_ctx, next, name, vec![]);
                 }
             }
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
index 8cbf89e9c30..7b145f3c14e 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
@@ -17,7 +17,7 @@ use ide_db::{
 };
 use syntax::{
     ast::{self, AttrKind, NameOrNameRef},
-    AstNode,
+    AstNode, SmolStr,
     SyntaxKind::{self, *},
     SyntaxToken, TextRange, TextSize, T,
 };
@@ -367,6 +367,8 @@ pub(crate) struct CompletionContext<'a> {
     pub(super) krate: hir::Crate,
     /// The module of the `scope`.
     pub(super) module: hir::Module,
+    /// Whether nightly toolchain is used. Cached since this is looked up a lot.
+    is_nightly: bool,
 
     /// The expected name of what we are completing.
     /// This is usually the parameter name of the function argument we are completing.
@@ -386,7 +388,7 @@ pub(crate) struct CompletionContext<'a> {
     pub(super) depth_from_crate_root: usize,
 }
 
-impl<'a> CompletionContext<'a> {
+impl CompletionContext<'_> {
     /// The range of the identifier that is being completed.
     pub(crate) fn source_range(&self) -> TextRange {
         let kind = self.original_token.kind();
@@ -441,6 +443,14 @@ impl<'a> CompletionContext<'a> {
         self.is_visible_impl(&vis, &attrs, item.krate(self.db))
     }
 
+    pub(crate) fn doc_aliases<I>(&self, item: &I) -> Vec<SmolStr>
+    where
+        I: hir::HasAttrs + Copy,
+    {
+        let attrs = item.attrs(self.db);
+        attrs.doc_aliases().collect()
+    }
+
     /// Check if an item is `#[doc(hidden)]`.
     pub(crate) fn is_item_hidden(&self, item: &hir::ItemInNs) -> bool {
         let attrs = item.attrs(self.db);
@@ -451,6 +461,12 @@ impl<'a> CompletionContext<'a> {
         }
     }
 
+    /// Checks whether this item should be listed in regards to stability. Returns `true` if we should.
+    pub(crate) fn check_stability(&self, attrs: Option<&hir::Attrs>) -> bool {
+        let Some(attrs) = attrs else { return true; };
+        !attrs.is_unstable() || self.is_nightly
+    }
+
     /// Whether the given trait is an operator trait or not.
     pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
         match trait_.attrs(self.db).lang() {
@@ -491,21 +507,22 @@ impl<'a> CompletionContext<'a> {
         );
     }
 
-    /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items.
-    pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
+    /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items and
+    /// passes all doc-aliases along, to funnel it into [`Completions::add_path_resolution`].
+    pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef, Vec<SmolStr>)) {
         let _p = profile::span("CompletionContext::process_all_names");
         self.scope.process_all_names(&mut |name, def| {
             if self.is_scope_def_hidden(def) {
                 return;
             }
-
-            f(name, def);
+            let doc_aliases = self.doc_aliases_in_scope(def);
+            f(name, def, doc_aliases);
         });
     }
 
     pub(crate) fn process_all_names_raw(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
         let _p = profile::span("CompletionContext::process_all_names_raw");
-        self.scope.process_all_names(&mut |name, def| f(name, def));
+        self.scope.process_all_names(f);
     }
 
     fn is_scope_def_hidden(&self, scope_def: ScopeDef) -> bool {
@@ -545,6 +562,14 @@ impl<'a> CompletionContext<'a> {
         // `doc(hidden)` items are only completed within the defining crate.
         self.krate != defining_crate && attrs.has_doc_hidden()
     }
+
+    pub(crate) fn doc_aliases_in_scope(&self, scope_def: ScopeDef) -> Vec<SmolStr> {
+        if let Some(attrs) = scope_def.attrs(self.db) {
+            attrs.doc_aliases().collect()
+        } else {
+            vec![]
+        }
+    }
 }
 
 // CompletionContext construction
@@ -615,6 +640,11 @@ impl<'a> CompletionContext<'a> {
         let krate = scope.krate();
         let module = scope.module();
 
+        let toolchain = db.crate_graph()[krate.into()].channel;
+        // `toolchain == None` means we're in some detached files. Since we have no information on
+        // the toolchain being used, let's just allow unstable items to be listed.
+        let is_nightly = matches!(toolchain, Some(base_db::ReleaseChannel::Nightly) | None);
+
         let mut locals = FxHashMap::default();
         scope.process_all_names(&mut |name, scope| {
             if let ScopeDef::Local(local) = scope {
@@ -634,6 +664,7 @@ impl<'a> CompletionContext<'a> {
             token,
             krate,
             module,
+            is_nightly,
             expected_name,
             expected_type,
             qualifier_ctx,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
index a94c404586b..cc5221cfccb 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
@@ -609,14 +609,14 @@ fn classify_name_ref(
                     _ => false,
                 };
 
-                let reciever_is_part_of_indivisible_expression = match &receiver {
+                let receiver_is_part_of_indivisible_expression = match &receiver {
                     Some(ast::Expr::IfExpr(_)) => {
                         let next_token_kind = next_non_trivia_token(name_ref.syntax().clone()).map(|t| t.kind());
                         next_token_kind == Some(SyntaxKind::ELSE_KW)
                     },
                     _ => false
                 };
-                if reciever_is_part_of_indivisible_expression {
+                if receiver_is_part_of_indivisible_expression {
                     return None;
                 }
 
@@ -1190,7 +1190,7 @@ fn pattern_context_for(
                                     })
                                 }).and_then(|variants| {
                                    Some(variants.iter().filter_map(|variant| {
-                                        let variant_name = variant.name(sema.db).to_string();
+                                        let variant_name = variant.name(sema.db).display(sema.db).to_string();
 
                                         let variant_already_present = match_arm_list.arms().any(|arm| {
                                             arm.pat().and_then(|pat| {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
index bb9fa7ccacc..e850f7bfdf3 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
@@ -3,7 +3,8 @@
 use std::fmt;
 
 use hir::{Documentation, Mutability};
-use ide_db::{imports::import_assets::LocatedImport, SnippetCap, SymbolKind};
+use ide_db::{imports::import_assets::LocatedImport, RootDatabase, SnippetCap, SymbolKind};
+use itertools::Itertools;
 use smallvec::SmallVec;
 use stdx::{impl_from, never};
 use syntax::{SmolStr, TextRange, TextSize};
@@ -45,7 +46,7 @@ pub struct CompletionItem {
     ///
     /// That is, in `foo.bar$0` lookup of `abracadabra` will be accepted (it
     /// contains `bar` sub sequence), and `quux` will rejected.
-    pub lookup: Option<SmolStr>,
+    pub lookup: SmolStr,
 
     /// Additional info to show in the UI pop up.
     pub detail: Option<String>,
@@ -75,7 +76,8 @@ pub struct CompletionItem {
     pub ref_match: Option<(Mutability, TextSize)>,
 
     /// The import data to add to completion's edits.
-    pub import_to_add: SmallVec<[LocatedImport; 1]>,
+    /// (ImportPath, LastSegment)
+    pub import_to_add: SmallVec<[(String, String); 1]>,
 }
 
 // We use custom debug for CompletionItem to make snapshot tests more readable.
@@ -353,12 +355,13 @@ impl CompletionItem {
             relevance: CompletionRelevance::default(),
             ref_match: None,
             imports_to_add: Default::default(),
+            doc_aliases: vec![],
         }
     }
 
     /// What string is used for filtering.
     pub fn lookup(&self) -> &str {
-        self.lookup.as_deref().unwrap_or(&self.label)
+        self.lookup.as_str()
     }
 
     pub fn ref_match(&self) -> Option<(String, text_edit::Indel, CompletionRelevance)> {
@@ -385,6 +388,7 @@ pub(crate) struct Builder {
     source_range: TextRange,
     imports_to_add: SmallVec<[LocatedImport; 1]>,
     trait_name: Option<SmolStr>,
+    doc_aliases: Vec<SmolStr>,
     label: SmolStr,
     insert_text: Option<String>,
     is_snippet: bool,
@@ -406,21 +410,31 @@ impl Builder {
         local_name: hir::Name,
         resolution: hir::ScopeDef,
     ) -> Self {
-        render_path_resolution(RenderContext::new(ctx), path_ctx, local_name, resolution)
+        let doc_aliases = ctx.doc_aliases_in_scope(resolution);
+        render_path_resolution(
+            RenderContext::new(ctx).doc_aliases(doc_aliases),
+            path_ctx,
+            local_name,
+            resolution,
+        )
     }
 
-    pub(crate) fn build(self) -> CompletionItem {
+    pub(crate) fn build(self, db: &RootDatabase) -> CompletionItem {
         let _p = profile::span("item::Builder::build");
 
         let mut label = self.label;
-        let mut lookup = self.lookup;
+        let mut lookup = self.lookup.unwrap_or_else(|| label.clone());
         let insert_text = self.insert_text.unwrap_or_else(|| label.to_string());
 
+        if !self.doc_aliases.is_empty() {
+            let doc_aliases = self.doc_aliases.into_iter().join(", ");
+            label = SmolStr::from(format!("{label} (alias {doc_aliases})"));
+            lookup = SmolStr::from(format!("{lookup} {doc_aliases}"));
+        }
         if let [import_edit] = &*self.imports_to_add {
             // snippets can have multiple imports, but normal completions only have up to one
             if let Some(original_path) = import_edit.original_path.as_ref() {
-                lookup = lookup.or_else(|| Some(label.clone()));
-                label = SmolStr::from(format!("{label} (use {original_path})"));
+                label = SmolStr::from(format!("{label} (use {})", original_path.display(db)));
             }
         } else if let Some(trait_name) = self.trait_name {
             label = SmolStr::from(format!("{label} (as {trait_name})"));
@@ -431,6 +445,17 @@ impl Builder {
             None => TextEdit::replace(self.source_range, insert_text),
         };
 
+        let import_to_add = self
+            .imports_to_add
+            .into_iter()
+            .filter_map(|import| {
+                Some((
+                    import.import_path.display(db).to_string(),
+                    import.import_path.segments().last()?.display(db).to_string(),
+                ))
+            })
+            .collect();
+
         CompletionItem {
             source_range: self.source_range,
             label,
@@ -444,7 +469,7 @@ impl Builder {
             trigger_call_info: self.trigger_call_info,
             relevance: self.relevance,
             ref_match: self.ref_match,
-            import_to_add: self.imports_to_add,
+            import_to_add,
         }
     }
     pub(crate) fn lookup_by(&mut self, lookup: impl Into<SmolStr>) -> &mut Builder {
@@ -459,6 +484,10 @@ impl Builder {
         self.trait_name = Some(trait_name);
         self
     }
+    pub(crate) fn doc_aliases(&mut self, doc_aliases: Vec<SmolStr>) -> &mut Builder {
+        self.doc_aliases = doc_aliases;
+        self
+    }
     pub(crate) fn insert_text(&mut self, insert_text: impl Into<String>) -> &mut Builder {
         self.insert_text = Some(insert_text.into());
         self
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
index 6fe78111403..106d4e1e52f 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
@@ -97,7 +97,7 @@ pub use crate::{
 
 /// Main entry point for completion. We run completion as a two-phase process.
 ///
-/// First, we look at the position and collect a so-called `CompletionContext.
+/// First, we look at the position and collect a so-called `CompletionContext`.
 /// This is a somewhat messy process, because, during completion, syntax tree is
 /// incomplete and can look really weird.
 ///
@@ -133,7 +133,7 @@ pub use crate::{
 ///
 /// Another case where this would be instrumental is macro expansion. We want to
 /// insert a fake ident and re-expand code. There's `expand_speculative` as a
-/// work-around for this.
+/// workaround for this.
 ///
 /// A different use-case is completion of injection (examples and links in doc
 /// comments). When computing completion for a path in a doc-comment, you want
@@ -243,7 +243,7 @@ pub fn resolve_completion_edits(
                     config.prefer_no_std,
                 )
             })
-            .find(|mod_path| mod_path.to_string() == full_import_path);
+            .find(|mod_path| mod_path.display(db).to_string() == full_import_path);
         if let Some(import_path) = import {
             insert_use::insert_use(&new_ast, mod_path_to_ast(&import_path), &config.insert_use);
         }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
index c1f51aabb96..1953eb47957 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
@@ -32,11 +32,17 @@ pub(crate) struct RenderContext<'a> {
     completion: &'a CompletionContext<'a>,
     is_private_editable: bool,
     import_to_add: Option<LocatedImport>,
+    doc_aliases: Vec<SmolStr>,
 }
 
 impl<'a> RenderContext<'a> {
     pub(crate) fn new(completion: &'a CompletionContext<'a>) -> RenderContext<'a> {
-        RenderContext { completion, is_private_editable: false, import_to_add: None }
+        RenderContext {
+            completion,
+            is_private_editable: false,
+            import_to_add: None,
+            doc_aliases: vec![],
+        }
     }
 
     pub(crate) fn private_editable(mut self, private_editable: bool) -> Self {
@@ -49,6 +55,11 @@ impl<'a> RenderContext<'a> {
         self
     }
 
+    pub(crate) fn doc_aliases(mut self, doc_aliases: Vec<SmolStr>) -> Self {
+        self.doc_aliases = doc_aliases;
+        self
+    }
+
     fn snippet_cap(&self) -> Option<SnippetCap> {
         self.completion.config.snippet_cap
     }
@@ -115,24 +126,25 @@ pub(crate) fn render_field(
     field: hir::Field,
     ty: &hir::Type,
 ) -> CompletionItem {
+    let db = ctx.db();
     let is_deprecated = ctx.is_deprecated(field);
-    let name = field.name(ctx.db());
+    let name = field.name(db);
     let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str());
     let mut item = CompletionItem::new(
         SymbolKind::Field,
         ctx.source_range(),
-        field_with_receiver(receiver.as_ref(), &name),
+        field_with_receiver(db, receiver.as_ref(), &name),
     );
     item.set_relevance(CompletionRelevance {
         type_match: compute_type_match(ctx.completion, ty),
         exact_name_match: compute_exact_name_match(ctx.completion, name.as_str()),
         ..CompletionRelevance::default()
     });
-    item.detail(ty.display(ctx.db()).to_string())
-        .set_documentation(field.docs(ctx.db()))
+    item.detail(ty.display(db).to_string())
+        .set_documentation(field.docs(db))
         .set_deprecated(is_deprecated)
         .lookup_by(name);
-    item.insert_text(field_with_receiver(receiver.as_ref(), &escaped_name));
+    item.insert_text(field_with_receiver(db, receiver.as_ref(), &escaped_name));
     if let Some(receiver) = &dot_access.receiver {
         if let Some(original) = ctx.completion.sema.original_ast_node(receiver.clone()) {
             if let Some(ref_match) = compute_ref_match(ctx.completion, ty) {
@@ -140,11 +152,19 @@ pub(crate) fn render_field(
             }
         }
     }
-    item.build()
+    item.doc_aliases(ctx.doc_aliases);
+    item.build(db)
 }
 
-fn field_with_receiver(receiver: Option<&hir::Name>, field_name: &str) -> SmolStr {
-    receiver.map_or_else(|| field_name.into(), |receiver| format!("{receiver}.{field_name}").into())
+fn field_with_receiver(
+    db: &RootDatabase,
+    receiver: Option<&hir::Name>,
+    field_name: &str,
+) -> SmolStr {
+    receiver.map_or_else(
+        || field_name.into(),
+        |receiver| format!("{}.{field_name}", receiver.display(db)).into(),
+    )
 }
 
 pub(crate) fn render_tuple_field(
@@ -156,10 +176,10 @@ pub(crate) fn render_tuple_field(
     let mut item = CompletionItem::new(
         SymbolKind::Field,
         ctx.source_range(),
-        field_with_receiver(receiver.as_ref(), &field.to_string()),
+        field_with_receiver(ctx.db(), receiver.as_ref(), &field.to_string()),
     );
     item.detail(ty.display(ctx.db()).to_string()).lookup_by(field.to_string());
-    item.build()
+    item.build(ctx.db())
 }
 
 pub(crate) fn render_type_inference(
@@ -169,7 +189,7 @@ pub(crate) fn render_type_inference(
     let mut builder =
         CompletionItem::new(CompletionItemKind::InferredType, ctx.source_range(), ty_string);
     builder.set_relevance(CompletionRelevance { is_definite: true, ..Default::default() });
-    builder.build()
+    builder.build(ctx.db)
 }
 
 pub(crate) fn render_path_resolution(
@@ -197,7 +217,9 @@ pub(crate) fn render_resolution_with_import(
 ) -> Option<Builder> {
     let resolution = ScopeDef::from(import_edit.original_item);
     let local_name = scope_def_to_name(resolution, &ctx, &import_edit)?;
-
+    //this now just renders the alias text, but we need to find the aliases earlier and call this with the alias instead
+    let doc_aliases = ctx.completion.doc_aliases_in_scope(resolution);
+    let ctx = ctx.doc_aliases(doc_aliases);
     Some(render_resolution_path(ctx, path_ctx, local_name, Some(import_edit), resolution))
 }
 
@@ -305,7 +327,7 @@ fn render_resolution_path(
                 item.lookup_by(name.clone())
                     .label(SmolStr::from_iter([&name, "<…>"]))
                     .trigger_call_info()
-                    .insert_snippet(cap, format!("{local_name}<$0>"));
+                    .insert_snippet(cap, format!("{}<$0>", local_name.display(db)));
             }
         }
     }
@@ -348,6 +370,8 @@ fn render_resolution_simple_(
     if let Some(import_to_add) = ctx.import_to_add {
         item.add_import(import_to_add);
     }
+
+    item.doc_aliases(ctx.doc_aliases);
     item
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs
index 70b19988ca7..3c73983c39a 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs
@@ -29,5 +29,5 @@ fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option<CompletionItem>
     }
     item.insert_text(escaped_name);
 
-    Some(item.build())
+    Some(item.build(ctx.db()))
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs
index 197592e78ce..8afce8db5ea 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs
@@ -52,8 +52,13 @@ fn render(
 
     let (call, escaped_call) = match &func_kind {
         FuncKind::Method(_, Some(receiver)) => (
-            format!("{}.{}", receiver.unescaped(), name.unescaped()).into(),
-            format!("{receiver}.{name}").into(),
+            format!(
+                "{}.{}",
+                receiver.unescaped().display(ctx.db()),
+                name.unescaped().display(ctx.db())
+            )
+            .into(),
+            format!("{}.{}", receiver.display(ctx.db()), name.display(ctx.db())).into(),
         ),
         _ => (name.unescaped().to_smol_str(), name.to_smol_str()),
     };
@@ -147,6 +152,8 @@ fn render(
             }
         }
     }
+
+    item.doc_aliases(ctx.doc_aliases);
     item
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
index ed78fcd8e65..728d236dff4 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
@@ -71,8 +71,10 @@ fn render(
         }
         None => (name.clone().into(), name.into(), false),
     };
-    let (qualified_name, escaped_qualified_name) =
-        (qualified_name.unescaped().to_string(), qualified_name.to_string());
+    let (qualified_name, escaped_qualified_name) = (
+        qualified_name.unescaped().display(ctx.db()).to_string(),
+        qualified_name.display(ctx.db()).to_string(),
+    );
     let snippet_cap = ctx.snippet_cap();
 
     let mut rendered = match kind {
@@ -98,7 +100,7 @@ fn render(
     }
     let label = format_literal_label(&qualified_name, kind, snippet_cap);
     let lookup = if qualified {
-        format_literal_lookup(&short_qualified_name.to_string(), kind)
+        format_literal_lookup(&short_qualified_name.display(ctx.db()).to_string(), kind)
     } else {
         format_literal_lookup(&qualified_name, kind)
     };
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
index 44e88607633..ce7af1d3400 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
@@ -74,7 +74,7 @@ fn render(
             item.insert_text(banged_name(&escaped_name)).lookup_by(banged_name(&name));
         }
         _ => {
-            cov_mark::hit!(dont_insert_macro_call_parens_unncessary);
+            cov_mark::hit!(dont_insert_macro_call_parens_unnecessary);
             item.insert_text(escaped_name);
         }
     };
@@ -140,8 +140,8 @@ mod tests {
     use crate::tests::check_edit;
 
     #[test]
-    fn dont_insert_macro_call_parens_unncessary() {
-        cov_mark::check!(dont_insert_macro_call_parens_unncessary);
+    fn dont_insert_macro_call_parens_unnecessary() {
+        cov_mark::check!(dont_insert_macro_call_parens_unnecessary);
         check_edit(
             "frobnicate",
             r#"
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs
index 9225c91bebf..d06abc5e91e 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs
@@ -57,7 +57,10 @@ pub(crate) fn render_variant_pat(
     let enum_ty = variant.parent_enum(ctx.db()).ty(ctx.db());
 
     let (name, escaped_name) = match path {
-        Some(path) => (path.unescaped().to_string().into(), path.to_string().into()),
+        Some(path) => (
+            path.unescaped().display(ctx.db()).to_string().into(),
+            path.display(ctx.db()).to_string().into(),
+        ),
         None => {
             let name = local_name.unwrap_or_else(|| variant.name(ctx.db()));
             (name.unescaped().to_smol_str(), name.to_smol_str())
@@ -121,7 +124,7 @@ fn build_completion(
         Some(snippet_cap) => item.insert_snippet(snippet_cap, pat),
         None => item.insert_text(pat),
     };
-    item.build()
+    item.build(ctx.db())
 }
 
 fn render_pat(
@@ -172,7 +175,7 @@ fn render_record_as_pat(
             format!(
                 "{name} {{ {}{} }}",
                 fields.enumerate().format_with(", ", |(idx, field), f| {
-                    f(&format_args!("{}${}", field.name(db), idx + 1))
+                    f(&format_args!("{}${}", field.name(db).display(db.upcast()), idx + 1))
                 }),
                 if fields_omitted { ", .." } else { "" },
                 name = name
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs
index fbe120d2ac9..343ba7e28d8 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs
@@ -53,5 +53,5 @@ fn render(
     }
     item.insert_text(escaped_name);
 
-    Some(item.build())
+    Some(item.build(ctx.db()))
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs
index 6e0c53ec94c..93e943dbed9 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs
@@ -21,8 +21,10 @@ pub(crate) fn render_union_literal(
     let name = local_name.unwrap_or_else(|| un.name(ctx.db()));
 
     let (qualified_name, escaped_qualified_name) = match path {
-        Some(p) => (p.unescaped().to_string(), p.to_string()),
-        None => (name.unescaped().to_string(), name.to_string()),
+        Some(p) => (p.unescaped().display(ctx.db()).to_string(), p.display(ctx.db()).to_string()),
+        None => {
+            (name.unescaped().display(ctx.db()).to_string(), name.display(ctx.db()).to_string())
+        }
     };
     let label = format_literal_label(&name.to_smol_str(), StructKind::Record, ctx.snippet_cap());
     let lookup = format_literal_lookup(&name.to_smol_str(), StructKind::Record);
@@ -51,9 +53,9 @@ pub(crate) fn render_union_literal(
         format!(
             "{} {{ {} }}",
             escaped_qualified_name,
-            fields
-                .iter()
-                .format_with(", ", |field, f| { f(&format_args!("{}: ()", field.name(ctx.db()))) })
+            fields.iter().format_with(", ", |field, f| {
+                f(&format_args!("{}: ()", field.name(ctx.db()).display(ctx.db())))
+            })
         )
     };
 
@@ -61,7 +63,11 @@ pub(crate) fn render_union_literal(
         "{} {{ {}{} }}",
         qualified_name,
         fields.iter().format_with(", ", |field, f| {
-            f(&format_args!("{}: {}", field.name(ctx.db()), field.ty(ctx.db()).display(ctx.db())))
+            f(&format_args!(
+                "{}: {}",
+                field.name(ctx.db()).display(ctx.db()),
+                field.ty(ctx.db()).display(ctx.db())
+            ))
         }),
         if fields_omitted { ", .." } else { "" }
     );
@@ -76,5 +82,5 @@ pub(crate) fn render_union_literal(
         None => item.insert_text(literal),
     };
 
-    Some(item.build())
+    Some(item.build(ctx.db()))
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs
index 55c55725be4..a9a01a3a30f 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs
@@ -27,14 +27,14 @@ pub(crate) fn render_record_lit(
     }
     let completions = fields.iter().enumerate().format_with(", ", |(idx, field), f| {
         if snippet_cap.is_some() {
-            f(&format_args!("{}: ${{{}:()}}", field.name(db), idx + 1))
+            f(&format_args!("{}: ${{{}:()}}", field.name(db).display(db.upcast()), idx + 1))
         } else {
-            f(&format_args!("{}: ()", field.name(db)))
+            f(&format_args!("{}: ()", field.name(db).display(db.upcast())))
         }
     });
 
     let types = fields.iter().format_with(", ", |field, f| {
-        f(&format_args!("{}: {}", field.name(db), field.ty(db).display(db)))
+        f(&format_args!("{}: {}", field.name(db).display(db.upcast()), field.ty(db).display(db)))
     });
 
     RenderedLiteral {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
index 1fe48b9e96f..2464e8d5f81 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
@@ -23,7 +23,8 @@ mod type_pos;
 mod use_tree;
 mod visibility;
 
-use hir::{db::DefDatabase, PrefixKind};
+use expect_test::Expect;
+use hir::PrefixKind;
 use ide_db::{
     base_db::{fixture::ChangeFixture, FileLoader, FilePosition},
     imports::insert_use::{ImportGranularity, InsertUseConfig},
@@ -104,7 +105,7 @@ fn completion_list_with_config(
     include_keywords: bool,
     trigger_character: Option<char>,
 ) -> String {
-    // filter out all but one builtintype completion for smaller test outputs
+    // filter out all but one built-in type completion for smaller test outputs
     let items = get_all_items(config, ra_fixture, trigger_character);
     let items = items
         .into_iter()
@@ -120,7 +121,7 @@ fn completion_list_with_config(
 pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
     let change_fixture = ChangeFixture::parse(ra_fixture);
     let mut database = RootDatabase::default();
-    database.set_enable_proc_attr_macros(true);
+    database.enable_proc_attr_macros();
     database.apply_change(change_fixture.change);
     let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
     let offset = range_or_offset.expect_offset();
@@ -197,11 +198,11 @@ pub(crate) fn check_edit_with_config(
         &db,
         &config,
         position,
-        completion.import_to_add.iter().filter_map(|import_edit| {
-            let import_path = &import_edit.import_path;
-            let import_name = import_path.segments().last()?;
-            Some((import_path.to_string(), import_name.to_string()))
-        }),
+        completion
+            .import_to_add
+            .iter()
+            .cloned()
+            .filter_map(|(import_path, import_name)| Some((import_path, import_name))),
     )
     .into_iter()
     .flatten()
@@ -215,6 +216,11 @@ pub(crate) fn check_edit_with_config(
     assert_eq_text!(&ra_fixture_after, &actual)
 }
 
+fn check_empty(ra_fixture: &str, expect: Expect) {
+    let actual = completion_list(ra_fixture);
+    expect.assert_eq(&actual);
+}
+
 pub(crate) fn get_all_items(
     config: CompletionConfig,
     code: &str,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
index c1c6a689eb1..be5b7f8a340 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
@@ -1,18 +1,13 @@
 //! Completion tests for expressions.
 use expect_test::{expect, Expect};
 
-use crate::tests::{check_edit, completion_list, BASE_ITEMS_FIXTURE};
+use crate::tests::{check_edit, check_empty, completion_list, BASE_ITEMS_FIXTURE};
 
 fn check(ra_fixture: &str, expect: Expect) {
     let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}"));
     expect.assert_eq(&actual)
 }
 
-fn check_empty(ra_fixture: &str, expect: Expect) {
-    let actual = completion_list(ra_fixture);
-    expect.assert_eq(&actual);
-}
-
 #[test]
 fn complete_literal_struct_with_a_private_field() {
     // `FooDesc.bar` is private, the completion should not be triggered.
@@ -672,7 +667,7 @@ fn main() {
 }
 
 #[test]
-fn varaiant_with_struct() {
+fn variant_with_struct() {
     check_empty(
         r#"
 pub struct YoloVariant {
@@ -997,3 +992,105 @@ fn foo() { if foo {} el$0 { let x = 92; } }
         "#]],
     );
 }
+
+#[test]
+fn expr_no_unstable_item_on_stable() {
+    check_empty(
+        r#"
+//- /main.rs crate:main deps:std
+use std::*;
+fn main() {
+    $0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct UnstableThisShouldNotBeListed;
+"#,
+        expect![[r#"
+            fn main()      fn()
+            md std
+            bt u32
+            kw const
+            kw crate::
+            kw enum
+            kw extern
+            kw false
+            kw fn
+            kw for
+            kw if
+            kw if let
+            kw impl
+            kw let
+            kw loop
+            kw match
+            kw mod
+            kw return
+            kw self::
+            kw static
+            kw struct
+            kw trait
+            kw true
+            kw type
+            kw union
+            kw unsafe
+            kw use
+            kw while
+            kw while let
+            sn macro_rules
+            sn pd
+            sn ppd
+        "#]],
+    );
+}
+
+#[test]
+fn expr_unstable_item_on_nightly() {
+    check_empty(
+        r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+use std::*;
+fn main() {
+    $0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct UnstableButWeAreOnNightlyAnyway;
+"#,
+        expect![[r#"
+            fn main()                 fn()
+            md std
+            st UnstableButWeAreOnNightlyAnyway
+            bt u32
+            kw const
+            kw crate::
+            kw enum
+            kw extern
+            kw false
+            kw fn
+            kw for
+            kw if
+            kw if let
+            kw impl
+            kw let
+            kw loop
+            kw match
+            kw mod
+            kw return
+            kw self::
+            kw static
+            kw struct
+            kw trait
+            kw true
+            kw type
+            kw union
+            kw unsafe
+            kw use
+            kw while
+            kw while let
+            sn macro_rules
+            sn pd
+            sn ppd
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs
index 0b485eb776d..8c038c0fbaa 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs
@@ -1108,6 +1108,41 @@ fn function() {
 }
 
 #[test]
+fn flyimport_pattern_no_unstable_item_on_stable() {
+    check(
+        r#"
+//- /main.rs crate:main deps:std
+fn function() {
+    let foo$0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct FooStruct {}
+"#,
+        expect![""],
+    );
+}
+
+#[test]
+fn flyimport_pattern_unstable_item_on_nightly() {
+    check(
+        r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+fn function() {
+    let foo$0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct FooStruct {}
+"#,
+        expect![[r#"
+            st FooStruct (use std::FooStruct)
+        "#]],
+    );
+}
+
+#[test]
 fn flyimport_item_name() {
     check(
         r#"
@@ -1230,3 +1265,24 @@ macro_rules! define_struct {
         "#]],
     );
 }
+
+#[test]
+fn macro_use_prelude_is_in_scope() {
+    check(
+        r#"
+//- /main.rs crate:main deps:dep
+#[macro_use]
+extern crate dep;
+
+fn main() {
+    print$0
+}
+//- /lib.rs crate:dep
+#[macro_export]
+macro_rules! println {
+    () => {}
+}
+"#,
+        expect![""],
+    )
+}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs
index 9fc731bb11d..2b5b4dd773c 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs
@@ -1,7 +1,7 @@
 //! Completion tests for item list position.
 use expect_test::{expect, Expect};
 
-use crate::tests::{check_edit, completion_list, BASE_ITEMS_FIXTURE};
+use crate::tests::{check_edit, check_empty, completion_list, BASE_ITEMS_FIXTURE};
 
 fn check(ra_fixture: &str, expect: Expect) {
     let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}"));
@@ -298,6 +298,58 @@ impl Test for () {
 }
 
 #[test]
+fn in_trait_impl_no_unstable_item_on_stable() {
+    check_empty(
+        r#"
+trait Test {
+    #[unstable]
+    type Type;
+    #[unstable]
+    const CONST: ();
+    #[unstable]
+    fn function();
+}
+
+impl Test for () {
+    $0
+}
+"#,
+        expect![[r#"
+            kw crate::
+            kw self::
+        "#]],
+    );
+}
+
+#[test]
+fn in_trait_impl_unstable_item_on_nightly() {
+    check_empty(
+        r#"
+//- toolchain:nightly
+trait Test {
+    #[unstable]
+    type Type;
+    #[unstable]
+    const CONST: ();
+    #[unstable]
+    fn function();
+}
+
+impl Test for () {
+    $0
+}
+"#,
+        expect![[r#"
+            ct const CONST: () =
+            fn fn function()
+            ta type Type =
+            kw crate::
+            kw self::
+        "#]],
+    );
+}
+
+#[test]
 fn after_unit_struct() {
     check(
         r#"struct S; f$0"#,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs
index c0e485c36fd..8af6cce98f6 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs
@@ -1,12 +1,7 @@
 //! Completion tests for pattern position.
 use expect_test::{expect, Expect};
 
-use crate::tests::{check_edit, completion_list, BASE_ITEMS_FIXTURE};
-
-fn check_empty(ra_fixture: &str, expect: Expect) {
-    let actual = completion_list(ra_fixture);
-    expect.assert_eq(&actual)
-}
+use crate::tests::{check_edit, check_empty, completion_list, BASE_ITEMS_FIXTURE};
 
 fn check(ra_fixture: &str, expect: Expect) {
     let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}\n{ra_fixture}"));
@@ -742,3 +737,56 @@ fn f(x: EnumAlias<u8>) {
         "#]],
     );
 }
+
+#[test]
+fn pat_no_unstable_item_on_stable() {
+    check_empty(
+        r#"
+//- /main.rs crate:main deps:std
+use std::*;
+fn foo() {
+    let a$0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct S;
+#[unstable]
+pub enum Enum {
+    Variant
+}
+"#,
+        expect![[r#"
+            md std
+            kw mut
+            kw ref
+        "#]],
+    );
+}
+
+#[test]
+fn pat_unstable_item_on_nightly() {
+    check_empty(
+        r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+use std::*;
+fn foo() {
+    let a$0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct S;
+#[unstable]
+pub enum Enum {
+    Variant
+}
+"#,
+        expect![[r#"
+            en Enum
+            md std
+            st S
+            kw mut
+            kw ref
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs
index 2656a4d545e..789ad66345b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs
@@ -1,7 +1,7 @@
 //! Completion tests for predicates and bounds.
 use expect_test::{expect, Expect};
 
-use crate::tests::{completion_list, BASE_ITEMS_FIXTURE};
+use crate::tests::{check_empty, completion_list, BASE_ITEMS_FIXTURE};
 
 fn check(ra_fixture: &str, expect: Expect) {
     let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}\n{ra_fixture}"));
@@ -129,3 +129,43 @@ impl Record {
         "#]],
     );
 }
+
+#[test]
+fn pred_no_unstable_item_on_stable() {
+    check_empty(
+        r#"
+//- /main.rs crate:main deps:std
+use std::*;
+struct Foo<T> where T: $0 {}
+//- /std.rs crate:std
+#[unstable]
+pub trait Trait {}
+"#,
+        expect![[r#"
+            md std
+            kw crate::
+            kw self::
+        "#]],
+    );
+}
+
+#[test]
+fn pred_unstable_item_on_nightly() {
+    check_empty(
+        r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+use std::*;
+struct Foo<T> where T: $0 {}
+//- /std.rs crate:std
+#[unstable]
+pub trait Trait {}
+"#,
+        expect![[r#"
+            md std
+            tt Trait
+            kw crate::
+            kw self::
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs
index 92ea4d15b85..2d6234e310c 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs
@@ -81,7 +81,7 @@ impl Foo {
 }
 
 #[proc_macros::input_replace(
-    fn suprise() {
+    fn surprise() {
         Foo.$0
     }
 )]
@@ -114,7 +114,7 @@ impl Foo {
 }
 
 #[proc_macros::input_replace(
-    fn suprise() {
+    fn surprise() {
         Foo.f$0
     }
 )]
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs
index f8a6f6cd3ed..3824720839e 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs
@@ -989,3 +989,294 @@ fn foo { crate::::$0 }
         expect![""],
     )
 }
+
+#[test]
+fn completes_struct_via_doc_alias_in_fn_body() {
+    check(
+        r#"
+#[doc(alias = "Bar")]
+struct Foo;
+
+fn here_we_go() {
+    $0
+}
+"#,
+        expect![[r#"
+            fn here_we_go()    fn()
+            st Foo (alias Bar)
+            bt u32
+            kw const
+            kw crate::
+            kw enum
+            kw extern
+            kw false
+            kw fn
+            kw for
+            kw if
+            kw if let
+            kw impl
+            kw let
+            kw loop
+            kw match
+            kw mod
+            kw return
+            kw self::
+            kw static
+            kw struct
+            kw trait
+            kw true
+            kw type
+            kw union
+            kw unsafe
+            kw use
+            kw while
+            kw while let
+            sn macro_rules
+            sn pd
+            sn ppd
+        "#]],
+    );
+}
+
+#[test]
+fn completes_struct_via_multiple_doc_aliases_in_fn_body() {
+    check(
+        r#"
+#[doc(alias("Bar", "Qux"))]
+#[doc(alias = "Baz")]
+struct Foo;
+
+fn here_we_go() {
+    B$0
+}
+"#,
+        expect![[r#"
+            fn here_we_go()           fn()
+            st Foo (alias Bar, Qux, Baz)
+            bt u32
+            kw const
+            kw crate::
+            kw enum
+            kw extern
+            kw false
+            kw fn
+            kw for
+            kw if
+            kw if let
+            kw impl
+            kw let
+            kw loop
+            kw match
+            kw mod
+            kw return
+            kw self::
+            kw static
+            kw struct
+            kw trait
+            kw true
+            kw type
+            kw union
+            kw unsafe
+            kw use
+            kw while
+            kw while let
+            sn macro_rules
+            sn pd
+            sn ppd
+        "#]],
+    );
+}
+
+#[test]
+fn completes_field_name_via_doc_alias_in_fn_body() {
+    check(
+        r#"
+struct Foo {
+    #[doc(alias = "qux")]
+    bar: u8
+};
+
+fn here_we_go() {
+    let foo = Foo { q$0 }
+}
+"#,
+        expect![[r#"
+            fd bar (alias qux) u8
+        "#]],
+    );
+}
+
+#[test]
+fn completes_struct_fn_name_via_doc_alias_in_fn_body() {
+    check(
+        r#"
+struct Foo;
+impl Foo {
+    #[doc(alias = "qux")]
+    fn bar() -> u8 { 1 }
+}
+
+fn here_we_go() {
+    Foo::q$0
+}
+"#,
+        expect![[r#"
+            fn bar() (alias qux) fn() -> u8
+        "#]],
+    );
+}
+
+#[test]
+fn completes_method_name_via_doc_alias_in_fn_body() {
+    check(
+        r#"
+struct Foo {
+    bar: u8
+}
+impl Foo {
+    #[doc(alias = "qux")]
+    fn baz(&self) -> u8 {
+        self.bar
+    }
+}
+
+fn here_we_go() {
+    let foo = Foo { field: 42 };
+    foo.q$0
+}
+"#,
+        expect![[r#"
+            fd bar               u8
+            me baz() (alias qux) fn(&self) -> u8
+            sn box               Box::new(expr)
+            sn call              function(expr)
+            sn dbg               dbg!(expr)
+            sn dbgr              dbg!(&expr)
+            sn let               let
+            sn letm              let mut
+            sn match             match expr {}
+            sn ref               &expr
+            sn refm              &mut expr
+            sn unsafe            unsafe {}
+        "#]],
+    );
+}
+
+#[test]
+fn completes_fn_name_via_doc_alias_in_fn_body() {
+    check(
+        r#"
+#[doc(alias = "qux")]
+fn foo() {}
+fn bar() { qu$0 }
+"#,
+        expect![[r#"
+            fn bar()             fn()
+            fn foo() (alias qux) fn()
+            bt u32
+            kw const
+            kw crate::
+            kw enum
+            kw extern
+            kw false
+            kw fn
+            kw for
+            kw if
+            kw if let
+            kw impl
+            kw let
+            kw loop
+            kw match
+            kw mod
+            kw return
+            kw self::
+            kw static
+            kw struct
+            kw trait
+            kw true
+            kw type
+            kw union
+            kw unsafe
+            kw use
+            kw while
+            kw while let
+            sn macro_rules
+            sn pd
+            sn ppd
+        "#]],
+    );
+}
+
+#[test]
+fn completes_struct_name_via_doc_alias_in_another_mod() {
+    check(
+        r#"
+mod foo {
+    #[doc(alias = "Qux")]
+    pub struct Bar(u8);
+}
+
+fn here_we_go() {
+    use foo;
+    let foo = foo::Q$0
+}
+"#,
+        expect![[r#"
+            st Bar (alias Qux)
+        "#]],
+    );
+}
+
+#[test]
+fn completes_use_via_doc_alias_in_another_mod() {
+    check(
+        r#"
+mod foo {
+    #[doc(alias = "Qux")]
+    pub struct Bar(u8);
+}
+
+fn here_we_go() {
+    use foo::Q$0;
+}
+"#,
+        expect![[r#"
+            st Bar (alias Qux)
+        "#]],
+    );
+}
+
+#[test]
+fn completes_flyimport_with_doc_alias_in_another_mod() {
+    check(
+        r#"
+mod foo {
+    #[doc(alias = "Qux")]
+    pub struct Bar();
+}
+
+fn here_we_go() {
+    let foo = Bar$0
+}
+"#,
+        expect![[r#"
+            fn here_we_go()           fn()
+            md foo
+            st Bar (alias Qux) (use foo::Bar)
+            bt u32
+            kw crate::
+            kw false
+            kw for
+            kw if
+            kw if let
+            kw loop
+            kw match
+            kw return
+            kw self::
+            kw true
+            kw unsafe
+            kw while
+            kw while let
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
index c3f4fb4d181..8cb1ff4a125 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
@@ -1,7 +1,7 @@
 //! Completion tests for type position.
 use expect_test::{expect, Expect};
 
-use crate::tests::{completion_list, BASE_ITEMS_FIXTURE};
+use crate::tests::{check_empty, completion_list, BASE_ITEMS_FIXTURE};
 
 fn check(ra_fixture: &str, expect: Expect) {
     let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}\n{ra_fixture}"));
@@ -669,3 +669,53 @@ fn f(t: impl MyTrait<Item1 = u8, Item2 = $0
         "#]],
     );
 }
+
+#[test]
+fn type_pos_no_unstable_type_on_stable() {
+    check_empty(
+        r#"
+//- /main.rs crate:main deps:std
+use std::*;
+struct Foo {
+    f: $0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct S;
+"#,
+        expect![[r#"
+            md std
+            sp Self
+            st Foo
+            bt u32
+            kw crate::
+            kw self::
+        "#]],
+    )
+}
+
+#[test]
+fn type_pos_unstable_type_on_nightly() {
+    check_empty(
+        r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+use std::*;
+struct Foo {
+    f: $0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct S;
+"#,
+        expect![[r#"
+            md std
+            sp Self
+            st Foo
+            st S
+            bt u32
+            kw crate::
+            kw self::
+        "#]],
+    )
+}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs
index 037d7dce52e..4c74dba526b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs
@@ -382,3 +382,50 @@ use self::foo::impl$0
         "#]],
     );
 }
+
+#[test]
+fn use_tree_no_unstable_items_on_stable() {
+    check(
+        r#"
+//- /lib.rs crate:main deps:std
+use std::$0
+//- /std.rs crate:std
+#[unstable]
+pub mod simd {}
+#[unstable]
+pub struct S;
+#[unstable]
+pub fn foo() {}
+#[unstable]
+#[macro_export]
+marco_rules! m { () => {} }
+"#,
+        expect![""],
+    );
+}
+
+#[test]
+fn use_tree_unstable_items_on_nightly() {
+    check(
+        r#"
+//- toolchain:nightly
+//- /lib.rs crate:main deps:std
+use std::$0
+//- /std.rs crate:std
+#[unstable]
+pub mod simd {}
+#[unstable]
+pub struct S;
+#[unstable]
+pub fn foo() {}
+#[unstable]
+#[macro_export]
+marco_rules! m { () => {} }
+"#,
+        expect![[r#"
+            fn foo  fn()
+            md simd
+            st S
+        "#]],
+    );
+}