Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-completion/src/completions/expr.rs')
| -rw-r--r-- | crates/ide-completion/src/completions/expr.rs | 87 |
1 files changed, 76 insertions, 11 deletions
diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index ff2c8da421..c2e5eefe10 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -1,6 +1,9 @@ //! Completion of names from the current scope in expression position. -use hir::{sym, Name, ScopeDef}; +use std::ops::ControlFlow; + +use hir::{sym, Name, PathCandidateCallback, ScopeDef}; +use ide_db::FxHashSet; use syntax::ast; use crate::{ @@ -9,6 +12,38 @@ use crate::{ CompletionContext, Completions, }; +struct PathCallback<'a, F> { + ctx: &'a CompletionContext<'a>, + acc: &'a mut Completions, + add_assoc_item: F, + seen: FxHashSet<hir::AssocItem>, +} + +impl<F> PathCandidateCallback for PathCallback<'_, F> +where + F: FnMut(&mut Completions, hir::AssocItem), +{ + fn on_inherent_item(&mut self, item: hir::AssocItem) -> ControlFlow<()> { + if self.seen.insert(item) { + (self.add_assoc_item)(self.acc, item); + } + ControlFlow::Continue(()) + } + + fn on_trait_item(&mut self, item: hir::AssocItem) -> ControlFlow<()> { + // The excluded check needs to come before the `seen` test, so that if we see the same method twice, + // once as inherent and once not, we will include it. + if item + .container_trait(self.ctx.db) + .is_none_or(|trait_| !self.ctx.exclude_traits.contains(&trait_)) + && self.seen.insert(item) + { + (self.add_assoc_item)(self.acc, item); + } + ControlFlow::Continue(()) + } +} + pub(crate) fn complete_expr_path( acc: &mut Completions, ctx: &CompletionContext<'_>, @@ -50,12 +85,18 @@ pub(crate) fn complete_expr_path( }; match qualified { + // We exclude associated types/consts of excluded traits here together with methods, + // even though we don't exclude them when completing in type position, because it's easier. Qualified::TypeAnchor { ty: None, trait_: None } => ctx .traits_in_scope() .iter() - .flat_map(|&it| hir::Trait::from(it).items(ctx.sema.db)) + .copied() + .map(hir::Trait::from) + .filter(|it| !ctx.exclude_traits.contains(it)) + .flat_map(|it| it.items(ctx.sema.db)) .for_each(|item| add_assoc_item(acc, item)), Qualified::TypeAnchor { trait_: Some(trait_), .. } => { + // Don't filter excluded traits here, user requested this specific trait. trait_.items(ctx.sema.db).into_iter().for_each(|item| add_assoc_item(acc, item)) } Qualified::TypeAnchor { ty: Some(ty), trait_: None } => { @@ -64,9 +105,14 @@ pub(crate) fn complete_expr_path( acc.add_enum_variants(ctx, path_ctx, e); } - ctx.iterate_path_candidates(ty, |item| { - add_assoc_item(acc, item); - }); + ty.iterate_path_candidates_split_inherent( + ctx.db, + &ctx.scope, + &ctx.traits_in_scope(), + Some(ctx.module), + None, + PathCallback { ctx, acc, add_assoc_item, seen: FxHashSet::default() }, + ); // Iterate assoc types separately ty.iterate_assoc_items(ctx.db, ctx.krate, |item| { @@ -121,9 +167,14 @@ pub(crate) fn complete_expr_path( // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType. // (where AssocType is defined on a trait, not an inherent impl) - ctx.iterate_path_candidates(&ty, |item| { - add_assoc_item(acc, item); - }); + ty.iterate_path_candidates_split_inherent( + ctx.db, + &ctx.scope, + &ctx.traits_in_scope(), + Some(ctx.module), + None, + PathCallback { ctx, acc, add_assoc_item, seen: FxHashSet::default() }, + ); // Iterate assoc types separately ty.iterate_assoc_items(ctx.db, ctx.krate, |item| { @@ -134,6 +185,7 @@ pub(crate) fn complete_expr_path( }); } hir::PathResolution::Def(hir::ModuleDef::Trait(t)) => { + // Don't filter excluded traits here, user requested this specific trait. // Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`. for item in t.items(ctx.db) { add_assoc_item(acc, item); @@ -151,9 +203,14 @@ pub(crate) fn complete_expr_path( acc.add_enum_variants(ctx, path_ctx, e); } - ctx.iterate_path_candidates(&ty, |item| { - add_assoc_item(acc, item); - }); + ty.iterate_path_candidates_split_inherent( + ctx.db, + &ctx.scope, + &ctx.traits_in_scope(), + Some(ctx.module), + None, + PathCallback { ctx, acc, add_assoc_item, seen: FxHashSet::default() }, + ); } _ => (), } @@ -236,9 +293,17 @@ pub(crate) fn complete_expr_path( [..] => acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases), } } + // synthetic names currently leak out as we lack synthetic hygiene, so filter them + // out here + ScopeDef::Local(_) => { + if !name.as_str().starts_with('<') { + acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases) + } + } _ if scope_def_applicable(def) => { acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases) } + _ => (), }); |