Skip to content

Commit 3898610

Browse files
Do not complete unstable items that use an internal feature
Unless the feature is enabled (and we're on nightly). We can choose to not complete any unstable item if its feature is not enabled. However people may first type the thing then insert the feature. Only doing that for internal features is a good middle ground: normal people don't want their completion list polluted by `std::intrinsics` (even if they use nightly), and std people still get their completion if the feature is enabled (and realistically, it will be).
1 parent f539111 commit 3898610

6 files changed

Lines changed: 238 additions & 5 deletions

File tree

crates/hir-def/src/attrs.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,6 +1076,41 @@ impl AttrFlags {
10761076
})
10771077
}
10781078
}
1079+
1080+
pub fn unstable_feature(self, db: &dyn DefDatabase, owner: AttrDefId) -> Option<Symbol> {
1081+
if !self.contains(AttrFlags::IS_UNSTABLE) {
1082+
return None;
1083+
}
1084+
1085+
return unstable_feature(db, owner);
1086+
1087+
#[salsa::tracked]
1088+
fn unstable_feature(db: &dyn DefDatabase, owner: AttrDefId) -> Option<Symbol> {
1089+
collect_attrs(db, owner, |attr| {
1090+
if let ast::Meta::TokenTreeMeta(attr) = attr
1091+
&& let path = attr.path()
1092+
&& path.is1("unstable")
1093+
&& let Some(tt) = attr.token_tree()
1094+
{
1095+
let mut tt = TokenTreeChildren::new(&tt);
1096+
// Technically the `feature = "..."` always comes first, but it's not a requirement.
1097+
while let Some(token) = tt.next() {
1098+
if let NodeOrToken::Token(token) = token
1099+
&& token.text() == "feature"
1100+
&& let Some(NodeOrToken::Token(eq)) = tt.next()
1101+
&& eq.kind() == T![=]
1102+
&& let Some(NodeOrToken::Token(feature)) = tt.next()
1103+
&& let Some(feature) = ast::String::cast(feature)
1104+
&& let Ok(feature) = feature.value()
1105+
{
1106+
return ControlFlow::Break(Symbol::intern(&feature));
1107+
}
1108+
}
1109+
}
1110+
ControlFlow::Continue(())
1111+
})
1112+
}
1113+
}
10791114
}
10801115

10811116
fn merge_repr(this: &mut ReprOptions, other: ReprOptions) {

crates/hir/src/attrs.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,19 @@ impl AttrsWithOwner {
8585
self.attrs.contains(AttrFlags::IS_UNSTABLE)
8686
}
8787

88+
/// Currently, it could be that `is_unstable() == true` but `unstable_feature == None`
89+
/// (due to unstable features not being retrieved for fields etc.).
90+
#[inline]
91+
pub fn unstable_feature(&self, db: &dyn HirDatabase) -> Option<Symbol> {
92+
match self.owner {
93+
AttrsOwner::AttrDef(owner) => self.attrs.unstable_feature(db, owner),
94+
AttrsOwner::Field(_)
95+
| AttrsOwner::LifetimeParam(_)
96+
| AttrsOwner::TypeOrConstParam(_)
97+
| AttrsOwner::Dummy => None,
98+
}
99+
}
100+
88101
#[inline]
89102
pub fn is_macro_export(&self) -> bool {
90103
self.attrs.contains(AttrFlags::IS_MACRO_EXPORT)

crates/hir/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,10 @@ impl Crate {
342342
})
343343
.map(Crate::from)
344344
}
345+
346+
pub fn is_unstable_feature_enabled(self, db: &dyn HirDatabase, feature: &Symbol) -> bool {
347+
crate_def_map(db, self.id).is_unstable_feature_enabled(feature)
348+
}
345349
}
346350

347351
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]

crates/ide-completion/src/context.rs

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ mod analysis;
44
#[cfg(test)]
55
mod tests;
66

7-
use std::iter;
7+
use std::{iter, sync::LazyLock};
88

99
use base_db::toolchain_channel;
1010
use hir::{
1111
DisplayTarget, HasAttrs, InFile, Local, ModuleDef, ModuleSource, Name, PathResolution,
12-
ScopeDef, Semantics, SemanticsScope, Symbol, Type, TypeInfo,
12+
ScopeDef, Semantics, SemanticsScope, Symbol, Type, TypeInfo, sym,
1313
};
1414
use ide_db::{
1515
FilePosition, FxHashMap, FxHashSet, RootDatabase, famous_defs::FamousDefs,
@@ -601,7 +601,18 @@ impl CompletionContext<'_> {
601601
let Some(attrs) = attrs else {
602602
return true;
603603
};
604-
!attrs.is_unstable() || self.is_nightly
604+
if !attrs.is_unstable() {
605+
return true;
606+
}
607+
if !self.is_nightly {
608+
return false;
609+
}
610+
// Unstable on nightly, but we still don't want to suggest internal features, unless the feature flag is enabled.
611+
let Some(unstable_feature) = attrs.unstable_feature(self.db) else {
612+
return true;
613+
};
614+
!INTERNAL_FEATURES.contains(&unstable_feature)
615+
|| self.krate.is_unstable_feature_enabled(self.db, &unstable_feature)
605616
}
606617

607618
pub(crate) fn check_stability_and_hidden<I>(&self, item: I) -> bool
@@ -924,3 +935,40 @@ const OP_TRAIT_LANG: &[hir::LangItem] = &[
924935
hir::LangItem::Shr,
925936
hir::LangItem::Sub,
926937
];
938+
939+
// FIXME: Find a way to keep this up to date somehow?
940+
const INTERNAL_FEATURES_LIST: &[Symbol] = &[
941+
sym::abi_unadjusted,
942+
sym::allocator_internals,
943+
sym::allow_internal_unsafe,
944+
sym::allow_internal_unstable,
945+
sym::cfg_emscripten_wasm_eh,
946+
sym::cfg_target_has_reliable_f16_f128,
947+
sym::compiler_builtins,
948+
sym::custom_mir,
949+
sym::eii_internals,
950+
sym::field_representing_type_raw,
951+
sym::intrinsics,
952+
sym::lang_items,
953+
sym::link_cfg,
954+
sym::more_maybe_bounds,
955+
sym::negative_bounds,
956+
sym::pattern_complexity_limit,
957+
sym::prelude_import,
958+
sym::profiler_runtime,
959+
sym::rustc_attrs,
960+
sym::staged_api,
961+
sym::test_unstable_lint,
962+
sym::builtin_syntax,
963+
sym::link_llvm_intrinsics,
964+
sym::needs_panic_runtime,
965+
sym::panic_runtime,
966+
sym::pattern_types,
967+
sym::rustdoc_internals,
968+
sym::contracts_internals,
969+
sym::freeze_impls,
970+
sym::unsized_fn_params,
971+
];
972+
973+
static INTERNAL_FEATURES: LazyLock<FxHashSet<Symbol>> =
974+
LazyLock::new(|| INTERNAL_FEATURES_LIST.iter().cloned().collect());

crates/ide-completion/src/tests/expression.rs

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2273,7 +2273,7 @@ fn main() {
22732273
$0
22742274
}
22752275
//- /std.rs crate:std
2276-
#[unstable]
2276+
#[unstable(feature = "some_non_internal_feature")]
22772277
pub struct UnstableButWeAreOnNightlyAnyway;
22782278
"#,
22792279
expect![[r#"
@@ -2317,6 +2317,112 @@ pub struct UnstableButWeAreOnNightlyAnyway;
23172317
);
23182318
}
23192319

2320+
#[test]
2321+
fn expr_unstable_item_internal_feature() {
2322+
check(
2323+
r#"
2324+
//- toolchain:nightly
2325+
//- /main.rs crate:main deps:std
2326+
use std::*;
2327+
fn main() {
2328+
$0
2329+
}
2330+
//- /std.rs crate:std
2331+
#[unstable(feature = "intrinsics")]
2332+
pub mod intrinsics {}
2333+
"#,
2334+
expect![[r#"
2335+
fn main() fn()
2336+
md std
2337+
bt u32 u32
2338+
kw async
2339+
kw const
2340+
kw crate::
2341+
kw enum
2342+
kw extern
2343+
kw false
2344+
kw fn
2345+
kw for
2346+
kw if
2347+
kw if let
2348+
kw impl
2349+
kw impl for
2350+
kw let
2351+
kw letm
2352+
kw loop
2353+
kw match
2354+
kw mod
2355+
kw return
2356+
kw self::
2357+
kw static
2358+
kw struct
2359+
kw trait
2360+
kw true
2361+
kw type
2362+
kw union
2363+
kw unsafe
2364+
kw use
2365+
kw while
2366+
kw while let
2367+
sn macro_rules
2368+
sn pd
2369+
sn ppd
2370+
"#]],
2371+
);
2372+
check(
2373+
r#"
2374+
//- toolchain:nightly
2375+
//- /main.rs crate:main deps:std
2376+
#![feature(intrinsics)]
2377+
use std::*;
2378+
fn main() {
2379+
$0
2380+
}
2381+
//- /std.rs crate:std
2382+
#[unstable(feature = "intrinsics")]
2383+
pub mod intrinsics {}
2384+
"#,
2385+
expect![[r#"
2386+
fn main() fn()
2387+
md intrinsics
2388+
md std
2389+
bt u32 u32
2390+
kw async
2391+
kw const
2392+
kw crate::
2393+
kw enum
2394+
kw extern
2395+
kw false
2396+
kw fn
2397+
kw for
2398+
kw if
2399+
kw if let
2400+
kw impl
2401+
kw impl for
2402+
kw let
2403+
kw letm
2404+
kw loop
2405+
kw match
2406+
kw mod
2407+
kw return
2408+
kw self::
2409+
kw static
2410+
kw struct
2411+
kw trait
2412+
kw true
2413+
kw type
2414+
kw union
2415+
kw unsafe
2416+
kw use
2417+
kw while
2418+
kw while let
2419+
sn macro_rules
2420+
sn pd
2421+
sn ppd
2422+
"#]],
2423+
);
2424+
}
2425+
23202426
#[test]
23212427
fn inside_format_args_completions_work() {
23222428
check(

crates/intern/src/symbol/symbols.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,6 @@ define_symbols! {
123123
all,
124124
alloc_layout,
125125
alloc,
126-
allow_internal_unsafe,
127126
allow,
128127
any,
129128
as_str,
@@ -541,4 +540,32 @@ define_symbols! {
541540
DispatchFromDyn,
542541
define_opaque,
543542
marker,
543+
abi_unadjusted,
544+
allocator_internals,
545+
allow_internal_unsafe,
546+
allow_internal_unstable,
547+
cfg_emscripten_wasm_eh,
548+
cfg_target_has_reliable_f16_f128,
549+
compiler_builtins,
550+
custom_mir,
551+
eii_internals,
552+
field_representing_type_raw,
553+
intrinsics,
554+
link_cfg,
555+
more_maybe_bounds,
556+
negative_bounds,
557+
pattern_complexity_limit,
558+
profiler_runtime,
559+
rustc_attrs,
560+
staged_api,
561+
test_unstable_lint,
562+
builtin_syntax,
563+
link_llvm_intrinsics,
564+
needs_panic_runtime,
565+
panic_runtime,
566+
pattern_types,
567+
rustdoc_internals,
568+
contracts_internals,
569+
freeze_impls,
570+
unsized_fn_params,
544571
}

0 commit comments

Comments
 (0)