Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/hir-def/src/attrs.rs35
-rw-r--r--crates/hir/src/attrs.rs13
-rw-r--r--crates/hir/src/lib.rs4
-rw-r--r--crates/ide-completion/src/context.rs54
-rw-r--r--crates/ide-completion/src/tests/expression.rs108
-rw-r--r--crates/intern/src/symbol/symbols.rs29
6 files changed, 238 insertions, 5 deletions
diff --git a/crates/hir-def/src/attrs.rs b/crates/hir-def/src/attrs.rs
index b560d08492..9da5b98d83 100644
--- a/crates/hir-def/src/attrs.rs
+++ b/crates/hir-def/src/attrs.rs
@@ -1076,6 +1076,41 @@ impl AttrFlags {
})
}
}
+
+ pub fn unstable_feature(self, db: &dyn DefDatabase, owner: AttrDefId) -> Option<Symbol> {
+ if !self.contains(AttrFlags::IS_UNSTABLE) {
+ return None;
+ }
+
+ return unstable_feature(db, owner);
+
+ #[salsa::tracked]
+ fn unstable_feature(db: &dyn DefDatabase, owner: AttrDefId) -> Option<Symbol> {
+ collect_attrs(db, owner, |attr| {
+ if let ast::Meta::TokenTreeMeta(attr) = attr
+ && let path = attr.path()
+ && path.is1("unstable")
+ && let Some(tt) = attr.token_tree()
+ {
+ let mut tt = TokenTreeChildren::new(&tt);
+ // Technically the `feature = "..."` always comes first, but it's not a requirement.
+ while let Some(token) = tt.next() {
+ if let NodeOrToken::Token(token) = token
+ && token.text() == "feature"
+ && let Some(NodeOrToken::Token(eq)) = tt.next()
+ && eq.kind() == T![=]
+ && let Some(NodeOrToken::Token(feature)) = tt.next()
+ && let Some(feature) = ast::String::cast(feature)
+ && let Ok(feature) = feature.value()
+ {
+ return ControlFlow::Break(Symbol::intern(&feature));
+ }
+ }
+ }
+ ControlFlow::Continue(())
+ })
+ }
+ }
}
fn merge_repr(this: &mut ReprOptions, other: ReprOptions) {
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs
index 27e7985146..bec91032b9 100644
--- a/crates/hir/src/attrs.rs
+++ b/crates/hir/src/attrs.rs
@@ -85,6 +85,19 @@ impl AttrsWithOwner {
self.attrs.contains(AttrFlags::IS_UNSTABLE)
}
+ /// Currently, it could be that `is_unstable() == true` but `unstable_feature == None`
+ /// (due to unstable features not being retrieved for fields etc.).
+ #[inline]
+ pub fn unstable_feature(&self, db: &dyn HirDatabase) -> Option<Symbol> {
+ match self.owner {
+ AttrsOwner::AttrDef(owner) => self.attrs.unstable_feature(db, owner),
+ AttrsOwner::Field(_)
+ | AttrsOwner::LifetimeParam(_)
+ | AttrsOwner::TypeOrConstParam(_)
+ | AttrsOwner::Dummy => None,
+ }
+ }
+
#[inline]
pub fn is_macro_export(&self) -> bool {
self.attrs.contains(AttrFlags::IS_MACRO_EXPORT)
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 53240259e0..c9af4aa263 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -342,6 +342,10 @@ impl Crate {
})
.map(Crate::from)
}
+
+ pub fn is_unstable_feature_enabled(self, db: &dyn HirDatabase, feature: &Symbol) -> bool {
+ crate_def_map(db, self.id).is_unstable_feature_enabled(feature)
+ }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index a9f9f7997c..b9520e9132 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -4,12 +4,12 @@ mod analysis;
#[cfg(test)]
mod tests;
-use std::iter;
+use std::{iter, sync::LazyLock};
use base_db::toolchain_channel;
use hir::{
DisplayTarget, HasAttrs, InFile, Local, ModuleDef, ModuleSource, Name, PathResolution,
- ScopeDef, Semantics, SemanticsScope, Symbol, Type, TypeInfo,
+ ScopeDef, Semantics, SemanticsScope, Symbol, Type, TypeInfo, sym,
};
use ide_db::{
FilePosition, FxHashMap, FxHashSet, RootDatabase, famous_defs::FamousDefs,
@@ -601,7 +601,18 @@ impl CompletionContext<'_> {
let Some(attrs) = attrs else {
return true;
};
- !attrs.is_unstable() || self.is_nightly
+ if !attrs.is_unstable() {
+ return true;
+ }
+ if !self.is_nightly {
+ return false;
+ }
+ // Unstable on nightly, but we still don't want to suggest internal features, unless the feature flag is enabled.
+ let Some(unstable_feature) = attrs.unstable_feature(self.db) else {
+ return true;
+ };
+ !INTERNAL_FEATURES.contains(&unstable_feature)
+ || self.krate.is_unstable_feature_enabled(self.db, &unstable_feature)
}
pub(crate) fn check_stability_and_hidden<I>(&self, item: I) -> bool
@@ -924,3 +935,40 @@ const OP_TRAIT_LANG: &[hir::LangItem] = &[
hir::LangItem::Shr,
hir::LangItem::Sub,
];
+
+// FIXME: Find a way to keep this up to date somehow?
+const INTERNAL_FEATURES_LIST: &[Symbol] = &[
+ sym::abi_unadjusted,
+ sym::allocator_internals,
+ sym::allow_internal_unsafe,
+ sym::allow_internal_unstable,
+ sym::cfg_emscripten_wasm_eh,
+ sym::cfg_target_has_reliable_f16_f128,
+ sym::compiler_builtins,
+ sym::custom_mir,
+ sym::eii_internals,
+ sym::field_representing_type_raw,
+ sym::intrinsics,
+ sym::lang_items,
+ sym::link_cfg,
+ sym::more_maybe_bounds,
+ sym::negative_bounds,
+ sym::pattern_complexity_limit,
+ sym::prelude_import,
+ sym::profiler_runtime,
+ sym::rustc_attrs,
+ sym::staged_api,
+ sym::test_unstable_lint,
+ sym::builtin_syntax,
+ sym::link_llvm_intrinsics,
+ sym::needs_panic_runtime,
+ sym::panic_runtime,
+ sym::pattern_types,
+ sym::rustdoc_internals,
+ sym::contracts_internals,
+ sym::freeze_impls,
+ sym::unsized_fn_params,
+];
+
+static INTERNAL_FEATURES: LazyLock<FxHashSet<Symbol>> =
+ LazyLock::new(|| INTERNAL_FEATURES_LIST.iter().cloned().collect());
diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs
index 4a5983097a..294434297e 100644
--- a/crates/ide-completion/src/tests/expression.rs
+++ b/crates/ide-completion/src/tests/expression.rs
@@ -2273,7 +2273,7 @@ fn main() {
$0
}
//- /std.rs crate:std
-#[unstable]
+#[unstable(feature = "some_non_internal_feature")]
pub struct UnstableButWeAreOnNightlyAnyway;
"#,
expect![[r#"
@@ -2318,6 +2318,112 @@ pub struct UnstableButWeAreOnNightlyAnyway;
}
#[test]
+fn expr_unstable_item_internal_feature() {
+ check(
+ r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+use std::*;
+fn main() {
+ $0
+}
+//- /std.rs crate:std
+#[unstable(feature = "intrinsics")]
+pub mod intrinsics {}
+ "#,
+ expect![[r#"
+ fn main() fn()
+ md std
+ bt u32 u32
+ kw async
+ kw const
+ kw crate::
+ kw enum
+ kw extern
+ kw false
+ kw fn
+ kw for
+ kw if
+ kw if let
+ kw impl
+ kw impl for
+ kw let
+ kw letm
+ 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
+ "#]],
+ );
+ check(
+ r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+#![feature(intrinsics)]
+use std::*;
+fn main() {
+ $0
+}
+//- /std.rs crate:std
+#[unstable(feature = "intrinsics")]
+pub mod intrinsics {}
+ "#,
+ expect![[r#"
+ fn main() fn()
+ md intrinsics
+ md std
+ bt u32 u32
+ kw async
+ kw const
+ kw crate::
+ kw enum
+ kw extern
+ kw false
+ kw fn
+ kw for
+ kw if
+ kw if let
+ kw impl
+ kw impl for
+ kw let
+ kw letm
+ 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 inside_format_args_completions_work() {
check(
r#"
diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs
index cc09a1aae7..4be1f79fb5 100644
--- a/crates/intern/src/symbol/symbols.rs
+++ b/crates/intern/src/symbol/symbols.rs
@@ -123,7 +123,6 @@ define_symbols! {
all,
alloc_layout,
alloc,
- allow_internal_unsafe,
allow,
any,
as_str,
@@ -541,4 +540,32 @@ define_symbols! {
DispatchFromDyn,
define_opaque,
marker,
+ abi_unadjusted,
+ allocator_internals,
+ allow_internal_unsafe,
+ allow_internal_unstable,
+ cfg_emscripten_wasm_eh,
+ cfg_target_has_reliable_f16_f128,
+ compiler_builtins,
+ custom_mir,
+ eii_internals,
+ field_representing_type_raw,
+ intrinsics,
+ link_cfg,
+ more_maybe_bounds,
+ negative_bounds,
+ pattern_complexity_limit,
+ profiler_runtime,
+ rustc_attrs,
+ staged_api,
+ test_unstable_lint,
+ builtin_syntax,
+ link_llvm_intrinsics,
+ needs_panic_runtime,
+ panic_runtime,
+ pattern_types,
+ rustdoc_internals,
+ contracts_internals,
+ freeze_impls,
+ unsized_fn_params,
}