about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir-def/src/attr.rs4
-rw-r--r--crates/ide-completion/src/completions.rs61
-rw-r--r--crates/ide-completion/src/completions/flyimport.rs12
-rw-r--r--crates/ide-completion/src/completions/item_list/trait_impl.rs31
-rw-r--r--crates/ide-completion/src/completions/use_.rs3
-rw-r--r--crates/ide-completion/src/context.rs16
6 files changed, 107 insertions, 20 deletions
diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs
index 9224773aa80..cc59e780938 100644
--- a/crates/hir-def/src/attr.rs
+++ b/crates/hir-def/src/attr.rs
@@ -269,6 +269,10 @@ impl Attrs {
     pub fn is_proc_macro_derive(&self) -> bool {
         self.by_key("proc_macro_derive").exists()
     }
+
+    pub fn is_unstable(&self) -> bool {
+        self.by_key("unstable").exists()
+    }
 }
 
 use std::slice::Iter as SliceIter;
diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs
index b6a066f4f51..6f23bc5c747 100644
--- a/crates/ide-completion/src/completions.rs
+++ b/crates/ide-completion/src/completions.rs
@@ -23,7 +23,7 @@ pub(crate) mod env_vars;
 
 use std::iter;
 
-use hir::{known, ScopeDef, Variant};
+use hir::{known, HasAttrs, ScopeDef, Variant};
 use ide_db::{imports::import_assets::LocatedImport, SymbolKind};
 use syntax::ast;
 
@@ -181,6 +181,9 @@ impl Completions {
         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,
@@ -206,6 +209,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,
@@ -228,6 +234,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));
@@ -241,6 +250,9 @@ impl Completions {
         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,
@@ -257,6 +269,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,
@@ -280,6 +295,9 @@ 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,
@@ -304,6 +322,9 @@ 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,
@@ -328,6 +349,9 @@ 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,
@@ -348,6 +372,9 @@ impl Completions {
     }
 
     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,
@@ -364,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,
@@ -380,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));
     }
 
@@ -390,6 +423,9 @@ 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))
         {
@@ -404,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);
@@ -425,6 +464,9 @@ 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,
@@ -448,6 +490,9 @@ 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)
         {
@@ -462,6 +507,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);
     }
@@ -473,6 +521,8 @@ 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);
     }
@@ -494,6 +544,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,
@@ -511,6 +564,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),
@@ -529,6 +585,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/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs
index 0979f6a6dfc..32d3fb8c624 100644
--- a/crates/ide-completion/src/completions/flyimport.rs
+++ b/crates/ide-completion/src/completions/flyimport.rs
@@ -267,8 +267,10 @@ fn import_on_the_fly(
             .into_iter()
             .filter(ns_filter)
             .filter(|import| {
-                !ctx.is_item_hidden(&import.item_to_import)
-                    && !ctx.is_item_hidden(&import.original_item)
+                let item = &import.item_to_import;
+                !ctx.is_item_hidden(item)
+                    && !ctx.is_item_hidden(item)
+                    && ctx.check_stability(item.attrs(ctx.db).as_deref())
             })
             .sorted_by_key(|located_import| {
                 compute_fuzzy_completion_order_key(
@@ -315,8 +317,10 @@ fn import_on_the_fly_pat_(
             .into_iter()
             .filter(ns_filter)
             .filter(|import| {
-                !ctx.is_item_hidden(&import.item_to_import)
-                    && !ctx.is_item_hidden(&import.original_item)
+                let item = &import.item_to_import;
+                !ctx.is_item_hidden(item)
+                    && !ctx.is_item_hidden(item)
+                    && ctx.check_stability(item.attrs(ctx.db).as_deref())
             })
             .sorted_by_key(|located_import| {
                 compute_fuzzy_completion_order_key(
diff --git a/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs
index 889d90095fa..e82908a361e 100644
--- a/crates/ide-completion/src/completions/item_list/trait_impl.rs
+++ b/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)
-                }
-                _ => {}
-            }
-        });
+            });
     }
 }
 
diff --git a/crates/ide-completion/src/completions/use_.rs b/crates/ide-completion/src/completions/use_.rs
index 546a1f4c499..45be4fb2054 100644
--- a/crates/ide-completion/src/completions/use_.rs
+++ b/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()));
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index f6478d2ceb2..d99414fe358 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -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();
@@ -451,6 +453,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() {
@@ -624,6 +632,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 {
@@ -643,6 +656,7 @@ impl<'a> CompletionContext<'a> {
             token,
             krate,
             module,
+            is_nightly,
             expected_name,
             expected_type,
             qualifier_ctx,