Unnamed repository; edit this file 'description' to name the repository.
| -rw-r--r-- | crates/hir-def/src/attrs.rs | 35 | ||||
| -rw-r--r-- | crates/hir/src/attrs.rs | 13 | ||||
| -rw-r--r-- | crates/hir/src/lib.rs | 4 | ||||
| -rw-r--r-- | crates/ide-completion/src/context.rs | 54 | ||||
| -rw-r--r-- | crates/ide-completion/src/tests/expression.rs | 108 | ||||
| -rw-r--r-- | crates/intern/src/symbol/symbols.rs | 29 |
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 7a4085c474..7664d07f0c 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, } |