Skip to content

Commit 0ce71dd

Browse files
committed
completion: check stability
1 parent 584d269 commit 0ce71dd

File tree

6 files changed

+107
-20
lines changed

6 files changed

+107
-20
lines changed

crates/hir-def/src/attr.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,10 @@ impl Attrs {
269269
pub fn is_proc_macro_derive(&self) -> bool {
270270
self.by_key("proc_macro_derive").exists()
271271
}
272+
273+
pub fn is_unstable(&self) -> bool {
274+
self.by_key("unstable").exists()
275+
}
272276
}
273277

274278
use std::slice::Iter as SliceIter;

crates/ide-completion/src/completions.rs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub(crate) mod env_vars;
2323

2424
use std::iter;
2525

26-
use hir::{known, ScopeDef, Variant};
26+
use hir::{known, HasAttrs, ScopeDef, Variant};
2727
use ide_db::{imports::import_assets::LocatedImport, SymbolKind};
2828
use syntax::ast;
2929

@@ -181,6 +181,9 @@ impl Completions {
181181
resolution: hir::ScopeDef,
182182
doc_aliases: Vec<syntax::SmolStr>,
183183
) {
184+
if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) {
185+
return;
186+
}
184187
let is_private_editable = match ctx.def_is_visible(&resolution) {
185188
Visible::Yes => false,
186189
Visible::Editable => true,
@@ -206,6 +209,9 @@ impl Completions {
206209
local_name: hir::Name,
207210
resolution: hir::ScopeDef,
208211
) {
212+
if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) {
213+
return;
214+
}
209215
let is_private_editable = match ctx.def_is_visible(&resolution) {
210216
Visible::Yes => false,
211217
Visible::Editable => true,
@@ -228,6 +234,9 @@ impl Completions {
228234
path_ctx: &PathCompletionCtx,
229235
e: hir::Enum,
230236
) {
237+
if !ctx.check_stability(Some(&e.attrs(ctx.db))) {
238+
return;
239+
}
231240
e.variants(ctx.db)
232241
.into_iter()
233242
.for_each(|variant| self.add_enum_variant(ctx, path_ctx, variant, None));
@@ -241,6 +250,9 @@ impl Completions {
241250
local_name: hir::Name,
242251
doc_aliases: Vec<syntax::SmolStr>,
243252
) {
253+
if !ctx.check_stability(Some(&module.attrs(ctx.db))) {
254+
return;
255+
}
244256
self.add_path_resolution(
245257
ctx,
246258
path_ctx,
@@ -257,6 +269,9 @@ impl Completions {
257269
mac: hir::Macro,
258270
local_name: hir::Name,
259271
) {
272+
if !ctx.check_stability(Some(&mac.attrs(ctx.db))) {
273+
return;
274+
}
260275
let is_private_editable = match ctx.is_visible(&mac) {
261276
Visible::Yes => false,
262277
Visible::Editable => true,
@@ -280,6 +295,9 @@ impl Completions {
280295
func: hir::Function,
281296
local_name: Option<hir::Name>,
282297
) {
298+
if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
299+
return;
300+
}
283301
let is_private_editable = match ctx.is_visible(&func) {
284302
Visible::Yes => false,
285303
Visible::Editable => true,
@@ -304,6 +322,9 @@ impl Completions {
304322
receiver: Option<hir::Name>,
305323
local_name: Option<hir::Name>,
306324
) {
325+
if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
326+
return;
327+
}
307328
let is_private_editable = match ctx.is_visible(&func) {
308329
Visible::Yes => false,
309330
Visible::Editable => true,
@@ -328,6 +349,9 @@ impl Completions {
328349
func: hir::Function,
329350
import: LocatedImport,
330351
) {
352+
if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
353+
return;
354+
}
331355
let is_private_editable = match ctx.is_visible(&func) {
332356
Visible::Yes => false,
333357
Visible::Editable => true,
@@ -348,6 +372,9 @@ impl Completions {
348372
}
349373

350374
pub(crate) fn add_const(&mut self, ctx: &CompletionContext<'_>, konst: hir::Const) {
375+
if !ctx.check_stability(Some(&konst.attrs(ctx.db))) {
376+
return;
377+
}
351378
let is_private_editable = match ctx.is_visible(&konst) {
352379
Visible::Yes => false,
353380
Visible::Editable => true,
@@ -364,6 +391,9 @@ impl Completions {
364391
ctx: &CompletionContext<'_>,
365392
type_alias: hir::TypeAlias,
366393
) {
394+
if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) {
395+
return;
396+
}
367397
let is_private_editable = match ctx.is_visible(&type_alias) {
368398
Visible::Yes => false,
369399
Visible::Editable => true,
@@ -380,6 +410,9 @@ impl Completions {
380410
ctx: &CompletionContext<'_>,
381411
type_alias: hir::TypeAlias,
382412
) {
413+
if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) {
414+
return;
415+
}
383416
self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx), type_alias));
384417
}
385418

@@ -390,6 +423,9 @@ impl Completions {
390423
variant: hir::Variant,
391424
path: hir::ModPath,
392425
) {
426+
if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
427+
return;
428+
}
393429
if let Some(builder) =
394430
render_variant_lit(RenderContext::new(ctx), path_ctx, None, variant, Some(path))
395431
{
@@ -404,6 +440,9 @@ impl Completions {
404440
variant: hir::Variant,
405441
local_name: Option<hir::Name>,
406442
) {
443+
if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
444+
return;
445+
}
407446
if let PathCompletionCtx { kind: PathKind::Pat { pat_ctx }, .. } = path_ctx {
408447
cov_mark::hit!(enum_variant_pattern_path);
409448
self.add_variant_pat(ctx, pat_ctx, Some(path_ctx), variant, local_name);
@@ -425,6 +464,9 @@ impl Completions {
425464
field: hir::Field,
426465
ty: &hir::Type,
427466
) {
467+
if !ctx.check_stability(Some(&field.attrs(ctx.db))) {
468+
return;
469+
}
428470
let is_private_editable = match ctx.is_visible(&field) {
429471
Visible::Yes => false,
430472
Visible::Editable => true,
@@ -448,6 +490,9 @@ impl Completions {
448490
path: Option<hir::ModPath>,
449491
local_name: Option<hir::Name>,
450492
) {
493+
if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) {
494+
return;
495+
}
451496
if let Some(builder) =
452497
render_struct_literal(RenderContext::new(ctx), path_ctx, strukt, path, local_name)
453498
{
@@ -462,6 +507,9 @@ impl Completions {
462507
path: Option<hir::ModPath>,
463508
local_name: Option<hir::Name>,
464509
) {
510+
if !ctx.check_stability(Some(&un.attrs(ctx.db))) {
511+
return;
512+
}
465513
let item = render_union_literal(RenderContext::new(ctx), un, path, local_name);
466514
self.add_opt(item);
467515
}
@@ -473,6 +521,8 @@ impl Completions {
473521
field: usize,
474522
ty: &hir::Type,
475523
) {
524+
// Only used for (unnamed) tuples, whose all fields *are* stable. No need to check
525+
// stability here.
476526
let item = render_tuple_field(RenderContext::new(ctx), receiver, field, ty);
477527
self.add(item);
478528
}
@@ -494,6 +544,9 @@ impl Completions {
494544
variant: hir::Variant,
495545
local_name: Option<hir::Name>,
496546
) {
547+
if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
548+
return;
549+
}
497550
self.add_opt(render_variant_pat(
498551
RenderContext::new(ctx),
499552
pattern_ctx,
@@ -511,6 +564,9 @@ impl Completions {
511564
variant: hir::Variant,
512565
path: hir::ModPath,
513566
) {
567+
if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
568+
return;
569+
}
514570
let path = Some(&path);
515571
self.add_opt(render_variant_pat(
516572
RenderContext::new(ctx),
@@ -529,6 +585,9 @@ impl Completions {
529585
strukt: hir::Struct,
530586
local_name: Option<hir::Name>,
531587
) {
588+
if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) {
589+
return;
590+
}
532591
self.add_opt(render_struct_pat(RenderContext::new(ctx), pattern_ctx, strukt, local_name));
533592
}
534593
}

crates/ide-completion/src/completions/flyimport.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -267,8 +267,10 @@ fn import_on_the_fly(
267267
.into_iter()
268268
.filter(ns_filter)
269269
.filter(|import| {
270-
!ctx.is_item_hidden(&import.item_to_import)
271-
&& !ctx.is_item_hidden(&import.original_item)
270+
let item = &import.item_to_import;
271+
!ctx.is_item_hidden(item)
272+
&& !ctx.is_item_hidden(item)
273+
&& ctx.check_stability(item.attrs(ctx.db).as_deref())
272274
})
273275
.sorted_by_key(|located_import| {
274276
compute_fuzzy_completion_order_key(
@@ -315,8 +317,10 @@ fn import_on_the_fly_pat_(
315317
.into_iter()
316318
.filter(ns_filter)
317319
.filter(|import| {
318-
!ctx.is_item_hidden(&import.item_to_import)
319-
&& !ctx.is_item_hidden(&import.original_item)
320+
let item = &import.item_to_import;
321+
!ctx.is_item_hidden(item)
322+
&& !ctx.is_item_hidden(item)
323+
&& ctx.check_stability(item.attrs(ctx.db).as_deref())
320324
})
321325
.sorted_by_key(|located_import| {
322326
compute_fuzzy_completion_order_key(

crates/ide-completion/src/completions/item_list/trait_impl.rs

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -150,21 +150,24 @@ fn complete_trait_impl(
150150
impl_def: &ast::Impl,
151151
) {
152152
if let Some(hir_impl) = ctx.sema.to_def(impl_def) {
153-
get_missing_assoc_items(&ctx.sema, impl_def).into_iter().for_each(|item| {
154-
use self::ImplCompletionKind::*;
155-
match (item, kind) {
156-
(hir::AssocItem::Function(func), All | Fn) => {
157-
add_function_impl(acc, ctx, replacement_range, func, hir_impl)
153+
get_missing_assoc_items(&ctx.sema, impl_def)
154+
.into_iter()
155+
.filter(|item| ctx.check_stability(Some(&item.attrs(ctx.db))))
156+
.for_each(|item| {
157+
use self::ImplCompletionKind::*;
158+
match (item, kind) {
159+
(hir::AssocItem::Function(func), All | Fn) => {
160+
add_function_impl(acc, ctx, replacement_range, func, hir_impl)
161+
}
162+
(hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => {
163+
add_type_alias_impl(acc, ctx, replacement_range, type_alias, hir_impl)
164+
}
165+
(hir::AssocItem::Const(const_), All | Const) => {
166+
add_const_impl(acc, ctx, replacement_range, const_, hir_impl)
167+
}
168+
_ => {}
158169
}
159-
(hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => {
160-
add_type_alias_impl(acc, ctx, replacement_range, type_alias, hir_impl)
161-
}
162-
(hir::AssocItem::Const(const_), All | Const) => {
163-
add_const_impl(acc, ctx, replacement_range, const_, hir_impl)
164-
}
165-
_ => {}
166-
}
167-
});
170+
});
168171
}
169172
}
170173

crates/ide-completion/src/completions/use_.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ pub(crate) fn complete_use_path(
5252
)
5353
};
5454
for (name, def) in module_scope {
55+
if !ctx.check_stability(def.attrs(ctx.db).as_deref()) {
56+
continue;
57+
}
5558
let is_name_already_imported = name
5659
.as_text()
5760
.map_or(false, |text| already_imported_names.contains(text.as_str()));

crates/ide-completion/src/context.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,8 @@ pub(crate) struct CompletionContext<'a> {
367367
pub(super) krate: hir::Crate,
368368
/// The module of the `scope`.
369369
pub(super) module: hir::Module,
370+
/// Whether nightly toolchain is used. Cached since this is looked up a lot.
371+
is_nightly: bool,
370372

371373
/// The expected name of what we are completing.
372374
/// This is usually the parameter name of the function argument we are completing.
@@ -386,7 +388,7 @@ pub(crate) struct CompletionContext<'a> {
386388
pub(super) depth_from_crate_root: usize,
387389
}
388390

389-
impl<'a> CompletionContext<'a> {
391+
impl CompletionContext<'_> {
390392
/// The range of the identifier that is being completed.
391393
pub(crate) fn source_range(&self) -> TextRange {
392394
let kind = self.original_token.kind();
@@ -451,6 +453,12 @@ impl<'a> CompletionContext<'a> {
451453
}
452454
}
453455

456+
/// Checks whether this item should be listed in regards to stability. Returns `true` if we should.
457+
pub(crate) fn check_stability(&self, attrs: Option<&hir::Attrs>) -> bool {
458+
let Some(attrs) = attrs else { return true; };
459+
!attrs.is_unstable() || self.is_nightly
460+
}
461+
454462
/// Whether the given trait is an operator trait or not.
455463
pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
456464
match trait_.attrs(self.db).lang() {
@@ -624,6 +632,11 @@ impl<'a> CompletionContext<'a> {
624632
let krate = scope.krate();
625633
let module = scope.module();
626634

635+
let toolchain = db.crate_graph()[krate.into()].channel;
636+
// `toolchain == None` means we're in some detached files. Since we have no information on
637+
// the toolchain being used, let's just allow unstable items to be listed.
638+
let is_nightly = matches!(toolchain, Some(base_db::ReleaseChannel::Nightly) | None);
639+
627640
let mut locals = FxHashMap::default();
628641
scope.process_all_names(&mut |name, scope| {
629642
if let ScopeDef::Local(local) = scope {
@@ -643,6 +656,7 @@ impl<'a> CompletionContext<'a> {
643656
token,
644657
krate,
645658
module,
659+
is_nightly,
646660
expected_name,
647661
expected_type,
648662
qualifier_ctx,

0 commit comments

Comments
 (0)