diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index f8747b0724..284372f7fc 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -24,6 +24,7 @@ use std::iter; use hir::{db::HirDatabase, known, ScopeDef}; use ide_db::SymbolKind; +use syntax::ast; use crate::{ context::Visible, @@ -409,11 +410,12 @@ fn enum_variants_with_paths( acc: &mut Completions, ctx: &CompletionContext, enum_: hir::Enum, + impl_: &Option, cb: impl Fn(&mut Completions, &CompletionContext, hir::Variant, hir::ModPath), ) { let variants = enum_.variants(ctx.db); - if let Some(impl_) = ctx.impl_def.as_ref().and_then(|impl_| ctx.sema.to_def(impl_)) { + if let Some(impl_) = impl_.as_ref().and_then(|impl_| ctx.sema.to_def(impl_)) { if impl_.self_ty(ctx.db).as_adt() == Some(hir::Adt::Enum(enum_)) { for &variant in &variants { let self_path = hir::ModPath::from_segments( diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index e4d1c290c0..83ecb51aaa 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -27,6 +27,7 @@ pub(crate) fn complete_expr_path( in_condition, ty, incomplete_let, + impl_, ) = match path_ctx { &PathCompletionCtx { kind: @@ -39,6 +40,7 @@ pub(crate) fn complete_expr_path( ref ref_expr_parent, ref is_func_update, ref innermost_ret_ty, + ref impl_, .. }, ref qualified, @@ -53,6 +55,7 @@ pub(crate) fn complete_expr_path( in_condition, innermost_ret_ty, incomplete_let, + impl_, ), _ => return, }; @@ -181,8 +184,7 @@ pub(crate) fn complete_expr_path( if let Some(adt) = ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) { - let self_ty = - (|| ctx.sema.to_def(ctx.impl_def.as_ref()?)?.self_ty(ctx.db).as_adt())(); + let self_ty = (|| ctx.sema.to_def(impl_.as_ref()?)?.self_ty(ctx.db).as_adt())(); let complete_self = self_ty == Some(adt); match adt { @@ -210,9 +212,15 @@ pub(crate) fn complete_expr_path( } } hir::Adt::Enum(e) => { - super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| { - acc.add_qualified_enum_variant(ctx, variant, path) - }); + super::enum_variants_with_paths( + acc, + ctx, + e, + impl_, + |acc, ctx, variant, path| { + acc.add_qualified_enum_variant(ctx, variant, path) + }, + ); } } } diff --git a/crates/ide-completion/src/completions/fn_param.rs b/crates/ide-completion/src/completions/fn_param.rs index a62b966e01..94e2f489f5 100644 --- a/crates/ide-completion/src/completions/fn_param.rs +++ b/crates/ide-completion/src/completions/fn_param.rs @@ -24,8 +24,8 @@ pub(crate) fn complete_fn_param( ctx: &CompletionContext, pattern_ctx: &PatternContext, ) -> Option<()> { - let (param_list, _, param_kind) = match pattern_ctx { - PatternContext { param_ctx: Some(kind), .. } => kind, + let ((param_list, _, param_kind), impl_) = match pattern_ctx { + PatternContext { param_ctx: Some(kind), impl_, .. } => (kind, impl_), _ => return None, }; @@ -45,7 +45,7 @@ pub(crate) fn complete_fn_param( match param_kind { ParamKind::Function(function) => { - fill_fn_params(ctx, function, param_list, add_new_item_to_acc); + fill_fn_params(ctx, function, param_list, impl_, add_new_item_to_acc); } ParamKind::Closure(closure) => { let stmt_list = closure.syntax().ancestors().find_map(ast::StmtList::cast)?; @@ -62,6 +62,7 @@ fn fill_fn_params( ctx: &CompletionContext, function: &ast::Fn, param_list: &ast::ParamList, + impl_: &Option, mut add_new_item_to_acc: impl FnMut(&str), ) { let mut file_params = FxHashMap::default(); @@ -104,7 +105,7 @@ fn fill_fn_params( } remove_duplicated(&mut file_params, param_list.params()); let self_completion_items = ["self", "&self", "mut self", "&mut self"]; - if should_add_self_completions(ctx, param_list) { + if should_add_self_completions(param_list, impl_) { self_completion_items.into_iter().for_each(|self_item| add_new_item_to_acc(self_item)); } @@ -155,11 +156,10 @@ fn remove_duplicated( }) } -fn should_add_self_completions(ctx: &CompletionContext, param_list: &ast::ParamList) -> bool { - let inside_impl = ctx.impl_def.is_some(); +fn should_add_self_completions(param_list: &ast::ParamList, impl_: &Option) -> bool { let no_params = param_list.params().next().is_none() && param_list.self_param().is_none(); - inside_impl && no_params + impl_.is_some() && no_params } fn comma_wrapper(ctx: &CompletionContext) -> Option<(impl Fn(&str) -> String, TextRange)> { diff --git a/crates/ide-completion/src/completions/item_list.rs b/crates/ide-completion/src/completions/item_list.rs index f4402a3f87..941273fa97 100644 --- a/crates/ide-completion/src/completions/item_list.rs +++ b/crates/ide-completion/src/completions/item_list.rs @@ -66,7 +66,7 @@ fn add_keywords(acc: &mut Completions, ctx: &CompletionContext, kind: Option<&It let in_assoc_non_trait_impl = matches!(kind, Some(ItemListKind::Impl | ItemListKind::Trait)); let in_extern_block = matches!(kind, Some(ItemListKind::ExternBlock)); let in_trait = matches!(kind, Some(ItemListKind::Trait)); - let in_trait_impl = matches!(kind, Some(ItemListKind::TraitImpl)); + let in_trait_impl = matches!(kind, Some(ItemListKind::TraitImpl(_))); let in_inherent_impl = matches!(kind, Some(ItemListKind::Impl)); let no_qualifiers = ctx.qualifier_ctx.vis_node.is_none(); let in_block = matches!(kind, None); diff --git a/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs index c0c29d4cd2..58b894bdd4 100644 --- a/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -81,7 +81,7 @@ pub(crate) fn complete_trait_impl_name( kind, replacement_range(ctx, &item), // item -> ASSOC_ITEM_LIST -> IMPL - ast::Impl::cast(item.parent()?.parent()?)?, + &ast::Impl::cast(item.parent()?.parent()?)?, ); Some(()) } @@ -97,7 +97,7 @@ pub(crate) fn complete_trait_impl_name_ref( kind: NameRefKind::Path( path_ctx @ PathCompletionCtx { - kind: PathKind::Item { kind: ItemListKind::TraitImpl }, + kind: PathKind::Item { kind: ItemListKind::TraitImpl(Some(impl_)) }, .. }, ), @@ -109,7 +109,7 @@ pub(crate) fn complete_trait_impl_name_ref( Some(name) => name.syntax().text_range(), None => ctx.source_range(), }, - ctx.impl_def.clone()?, + impl_, ), _ => (), } @@ -121,10 +121,10 @@ fn complete_trait_impl( ctx: &CompletionContext, kind: ImplCompletionKind, replacement_range: TextRange, - impl_def: ast::Impl, + impl_def: &ast::Impl, ) { - if let Some(hir_impl) = ctx.sema.to_def(&impl_def) { - get_missing_assoc_items(&ctx.sema, &impl_def).into_iter().for_each(|item| { + if let Some(hir_impl) = ctx.sema.to_def(impl_def) { + get_missing_assoc_items(&ctx.sema, impl_def).into_iter().for_each(|item| { use self::ImplCompletionKind::*; match (item, kind) { (hir::AssocItem::Function(func), All | Fn) => { diff --git a/crates/ide-completion/src/completions/pattern.rs b/crates/ide-completion/src/completions/pattern.rs index dd08ef703e..149acb3c1b 100644 --- a/crates/ide-completion/src/completions/pattern.rs +++ b/crates/ide-completion/src/completions/pattern.rs @@ -51,9 +51,15 @@ pub(crate) fn complete_pattern( ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) { if refutable || single_variant_enum(e) { - super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| { - acc.add_qualified_variant_pat(ctx, variant, path); - }); + super::enum_variants_with_paths( + acc, + ctx, + e, + &patctx.impl_, + |acc, ctx, variant, path| { + acc.add_qualified_variant_pat(ctx, variant, path); + }, + ); } } diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index bc2c2fc713..0c7c6ab5af 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -98,6 +98,7 @@ pub(super) enum PathKind { is_func_update: Option, self_param: Option, innermost_ret_ty: Option, + impl_: Option, }, Type { location: TypeLocation, @@ -143,12 +144,12 @@ pub(crate) enum TypeAscriptionTarget { } /// The kind of item list a [`PathKind::Item`] belongs to. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq)] pub(super) enum ItemListKind { SourceFile, Module, Impl, - TraitImpl, + TraitImpl(Option), Trait, ExternBlock, } @@ -179,6 +180,7 @@ pub(super) struct PatternContext { pub(super) mut_token: Option, /// The record pattern this name or ref is a field of pub(super) record_pat: Option, + pub(super) impl_: Option, } /// The state of the lifetime we are completing. @@ -320,10 +322,6 @@ pub(crate) struct CompletionContext<'a> { /// The expected type of what we are completing. pub(super) expected_type: Option, - /// The parent impl of the cursor position if it exists. - // FIXME: This probably doesn't belong here - pub(super) impl_def: Option, - // FIXME: This shouldn't exist pub(super) previous_token: Option, @@ -497,7 +495,6 @@ impl<'a> CompletionContext<'a> { module, expected_name: None, expected_type: None, - impl_def: None, previous_token: None, // dummy value, will be overwritten ident_ctx: IdentContext::UnexpandedAttrTT { fake_attribute_under_caret: None }, diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index d416d8251d..e08fa6bdd3 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -389,16 +389,6 @@ impl<'a> CompletionContext<'a> { return Some(()); } }; - self.impl_def = self - .sema - .token_ancestors_with_macros(self.token.clone()) - .take_while(|it| it.kind() != SyntaxKind::SOURCE_FILE) - .filter_map(ast::Item::cast) - .take(2) - .find_map(|it| match it { - ast::Item::Impl(impl_) => Some(impl_), - _ => None, - }); match name_like { ast::NameLike::Lifetime(lifetime) => { @@ -452,7 +442,7 @@ impl<'a> CompletionContext<'a> { } fn classify_name( - _sema: &Semantics, + sema: &Semantics, original_file: &SyntaxNode, name: ast::Name, ) -> Option { @@ -464,7 +454,7 @@ impl<'a> CompletionContext<'a> { ast::Enum(_) => NameKind::Enum, ast::Fn(_) => NameKind::Function, ast::IdentPat(bind_pat) => { - let mut pat_ctx = pattern_context_for(original_file, bind_pat.into()); + let mut pat_ctx = pattern_context_for(sema, original_file, bind_pat.into()); if let Some(record_field) = ast::RecordPatField::for_field_name(&name) { pat_ctx.record_pat = find_node_in_file_compensated(original_file, &record_field.parent_record_pat()); } @@ -518,6 +508,7 @@ impl<'a> CompletionContext<'a> { &record_field.parent_record_pat(), ), ..pattern_context_for( + sema, original_file, record_field.parent_record_pat().clone().into(), ) @@ -766,6 +757,7 @@ impl<'a> CompletionContext<'a> { .parent() .and_then(ast::LetStmt::cast) .map_or(false, |it| it.semicolon_token().is_none()); + let impl_ = fetch_immediate_impl(sema, original_file, &expr); PathKind::Expr { in_block_expr, @@ -777,6 +769,7 @@ impl<'a> CompletionContext<'a> { innermost_ret_ty, self_param, incomplete_let, + impl_, } }; let make_path_kind_type = |ty: ast::Type| { @@ -804,14 +797,14 @@ impl<'a> CompletionContext<'a> { }, ast::TupleStructPat(it) => { path_ctx.has_call_parens = true; - PathKind::Pat { pat_ctx: pattern_context_for(original_file, it.into())} + PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())} }, ast::RecordPat(it) => { path_ctx.has_call_parens = true; - PathKind::Pat { pat_ctx: pattern_context_for(original_file, it.into())} + PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())} }, ast::PathPat(it) => { - PathKind::Pat { pat_ctx: pattern_context_for(original_file, it.into())} + PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())} }, ast::MacroCall(it) => { // A macro call in this position is usually a result of parsing recovery, so check that @@ -825,7 +818,7 @@ impl<'a> CompletionContext<'a> { match_ast! { match parent { ast::MacroExpr(expr) => make_path_kind_expr(expr.into()), - ast::MacroPat(it) => PathKind::Pat { pat_ctx: pattern_context_for(original_file, it.into())}, + ast::MacroPat(it) => PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}, ast::MacroType(ty) => make_path_kind_type(ty.into()), ast::ItemList(_) => PathKind::Item { kind: ItemListKind::Module }, ast::AssocItemList(_) => PathKind::Item { kind: match parent.parent() { @@ -833,7 +826,7 @@ impl<'a> CompletionContext<'a> { match it { ast::Trait(_) => ItemListKind::Trait, ast::Impl(it) => if it.trait_().is_some() { - ItemListKind::TraitImpl + ItemListKind::TraitImpl(find_node_in_file_compensated(original_file, &it)) } else { ItemListKind::Impl }, @@ -970,7 +963,11 @@ impl<'a> CompletionContext<'a> { } } -fn pattern_context_for(original_file: &SyntaxNode, pat: ast::Pat) -> PatternContext { +fn pattern_context_for( + sema: &Semantics, + original_file: &SyntaxNode, + pat: ast::Pat, +) -> PatternContext { let mut is_param = None; let (refutability, has_type_ascription) = pat @@ -1011,6 +1008,7 @@ fn pattern_context_for(original_file: &SyntaxNode, pat: ast::Pat) -> PatternCont ast::Pat::IdentPat(it) => (it.ref_token(), it.mut_token()), _ => (None, None), }; + PatternContext { refutability, param_ctx: is_param, @@ -1019,6 +1017,44 @@ fn pattern_context_for(original_file: &SyntaxNode, pat: ast::Pat) -> PatternCont mut_token, ref_token, record_pat: None, + impl_: fetch_immediate_impl(sema, original_file, &pat), + } +} + +fn fetch_immediate_impl( + sema: &Semantics, + original_file: &SyntaxNode, + node: &impl AstNode, +) -> Option { + // FIXME: The fallback here could be done better + let (f, s) = match find_node_in_file_compensated(original_file, node) { + Some(node) => { + let mut items = sema + .ancestors_with_macros(node.syntax().clone()) + .filter_map(ast::Item::cast) + .filter(|it| !matches!(it, ast::Item::MacroCall(_))) + .take(2); + (items.next(), items.next()) + } + None => { + let mut items = node + .syntax() + .ancestors() + .filter_map(ast::Item::cast) + .filter(|it| !matches!(it, ast::Item::MacroCall(_))) + .take(2); + (items.next(), items.next()) + } + }; + + match f? { + ast::Item::Const(_) | ast::Item::Fn(_) | ast::Item::TypeAlias(_) => (), + ast::Item::Impl(it) => return Some(it), + _ => return None, + } + match s? { + ast::Item::Impl(it) => Some(it), + _ => None, } }