From 6e07b17f692a1639e94687aadfcfea567236bf04 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 17 Jun 2022 10:45:19 +0200 Subject: [PATCH] Introduce NameRefKind for completions --- crates/ide-completion/src/completions/dot.rs | 6 +- crates/ide-completion/src/completions/expr.rs | 31 ++++---- .../ide-completion/src/completions/field.rs | 11 ++- .../src/completions/flyimport.rs | 33 ++++---- .../src/completions/item_list/trait_impl.rs | 11 ++- .../ide-completion/src/completions/keyword.rs | 7 +- .../ide-completion/src/completions/postfix.rs | 10 ++- .../ide-completion/src/completions/record.rs | 18 ++++- crates/ide-completion/src/completions/use_.rs | 14 +++- crates/ide-completion/src/context.rs | 77 +++++++++---------- crates/ide-completion/src/render/function.rs | 8 +- crates/ide-completion/src/tests/record.rs | 3 - 12 files changed, 134 insertions(+), 95 deletions(-) diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs index 4eb1fccd7d3..ade57bed958 100644 --- a/crates/ide-completion/src/completions/dot.rs +++ b/crates/ide-completion/src/completions/dot.rs @@ -4,7 +4,8 @@ use crate::{ context::{ - CompletionContext, DotAccess, DotAccessKind, NameRefContext, PathCompletionCtx, PathKind, + CompletionContext, DotAccess, DotAccessKind, NameRefContext, NameRefKind, + PathCompletionCtx, PathKind, }, CompletionItem, CompletionItemKind, Completions, }; @@ -13,7 +14,8 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { let (dot_access, receiver_ty) = match ctx.nameref_ctx() { Some(NameRefContext { - dot_access: Some(access @ DotAccess { receiver_ty: Some(receiver_ty), .. }), + kind: + Some(NameRefKind::DotAccess(access @ DotAccess { receiver_ty: Some(receiver_ty), .. })), .. }) => (access, &receiver_ty.original), _ => return complete_undotted_self(acc, ctx), diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index 7c3296a0b31..3d92a0cceff 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -5,7 +5,7 @@ use syntax::T; use crate::{ - context::{NameRefContext, PathCompletionCtx, PathKind, PathQualifierCtx}, + context::{NameRefContext, NameRefKind, PathCompletionCtx, PathKind, PathQualifierCtx}, CompletionContext, Completions, }; @@ -21,24 +21,29 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext) after_if_expr, wants_mut_token, ) = match ctx.nameref_ctx() { - Some(NameRefContext { - path_ctx: - Some(PathCompletionCtx { + Some(&NameRefContext { + kind: + Some(NameRefKind::Path(PathCompletionCtx { kind: - PathKind::Expr { in_block_expr, in_loop_body, after_if_expr, ref_expr_parent }, + PathKind::Expr { + in_block_expr, + in_loop_body, + after_if_expr, + ref ref_expr_parent, + ref is_func_update, + }, is_absolute_path, - qualifier, + ref qualifier, .. - }), - record_expr, + })), .. }) if ctx.qualifier_ctx.none() => ( - *is_absolute_path, + is_absolute_path, qualifier, - *in_block_expr, - *in_loop_body, - record_expr.as_ref().map_or(false, |&(_, it)| it), - *after_if_expr, + in_block_expr, + in_loop_body, + is_func_update.is_some(), + after_if_expr, ref_expr_parent.as_ref().map(|it| it.mut_token().is_none()).unwrap_or(false), ), _ => return, diff --git a/crates/ide-completion/src/completions/field.rs b/crates/ide-completion/src/completions/field.rs index 17395279178..bddfc1de1af 100644 --- a/crates/ide-completion/src/completions/field.rs +++ b/crates/ide-completion/src/completions/field.rs @@ -1,7 +1,10 @@ //! Completion of field list position. use crate::{ - context::{IdentContext, NameContext, NameKind, NameRefContext, PathCompletionCtx, PathKind}, + context::{ + IdentContext, NameContext, NameKind, NameRefContext, NameRefKind, PathCompletionCtx, + PathKind, + }, CompletionContext, Completions, }; @@ -9,8 +12,8 @@ pub(crate) fn complete_field_list(acc: &mut Completions, ctx: &CompletionContext match &ctx.ident_ctx { IdentContext::Name(NameContext { kind: NameKind::RecordField, .. }) | IdentContext::NameRef(NameRefContext { - path_ctx: - Some(PathCompletionCtx { + kind: + Some(NameRefKind::Path(PathCompletionCtx { has_macro_bang: false, is_absolute_path: false, qualifier: None, @@ -18,7 +21,7 @@ pub(crate) fn complete_field_list(acc: &mut Completions, ctx: &CompletionContext kind: PathKind::Type { in_tuple_struct: true }, has_type_args: false, .. - }), + })), .. }) => { if ctx.qualifier_ctx.vis_node.is_none() { diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs index 901f7519d25..b724f3cc51e 100644 --- a/crates/ide-completion/src/completions/flyimport.rs +++ b/crates/ide-completion/src/completions/flyimport.rs @@ -8,7 +8,9 @@ use syntax::{AstNode, SyntaxNode, T}; use crate::{ - context::{CompletionContext, NameRefContext, PathCompletionCtx, PathKind, PatternContext}, + context::{ + CompletionContext, NameRefContext, NameRefKind, PathCompletionCtx, PathKind, PatternContext, + }, patterns::ImmediateLocation, render::{render_resolution_with_import, RenderContext}, }; @@ -110,20 +112,21 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) if !ctx.config.enable_imports_on_the_fly { return None; } - let path_kind = match ctx.nameref_ctx() { - Some(NameRefContext { path_ctx: Some(PathCompletionCtx { kind, .. }), .. }) - if matches!( - kind, - PathKind::Expr { .. } - | PathKind::Type { .. } - | PathKind::Attr { .. } - | PathKind::Derive - | PathKind::Pat - ) => - { - Some(kind) - } - Some(NameRefContext { dot_access: Some(_), .. }) => None, + let path_kind = match dbg!(ctx.nameref_ctx()) { + Some(NameRefContext { + kind: + Some(NameRefKind::Path(PathCompletionCtx { + kind: + kind @ (PathKind::Expr { .. } + | PathKind::Type { .. } + | PathKind::Attr { .. } + | PathKind::Derive + | PathKind::Pat), + .. + })), + .. + }) => Some(kind), + Some(NameRefContext { kind: Some(NameRefKind::DotAccess(_)), .. }) => None, None if matches!(ctx.pattern_ctx, Some(PatternContext { record_pat: None, .. })) => { Some(&PathKind::Pat) } 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 56f656f4794..846d5f09028 100644 --- a/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -44,8 +44,8 @@ use crate::{ context::{ - IdentContext, ItemListKind, NameContext, NameKind, NameRefContext, PathCompletionCtx, - PathKind, + IdentContext, ItemListKind, NameContext, NameKind, NameRefContext, NameRefKind, + PathCompletionCtx, PathKind, }, CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance, Completions, }; @@ -106,14 +106,13 @@ fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, Text } IdentContext::NameRef(NameRefContext { nameref, - path_ctx: - Some( + kind: + Some(NameRefKind::Path( path_ctx @ PathCompletionCtx { kind: PathKind::Item { kind: ItemListKind::TraitImpl }, .. }, - ), - .. + )), }) if path_ctx.is_trivial_path() => Some(( ImplCompletionKind::All, match nameref { diff --git a/crates/ide-completion/src/completions/keyword.rs b/crates/ide-completion/src/completions/keyword.rs index 65fa1191781..2e266b7714c 100644 --- a/crates/ide-completion/src/completions/keyword.rs +++ b/crates/ide-completion/src/completions/keyword.rs @@ -4,11 +4,14 @@ use syntax::ast::Item; -use crate::{context::NameRefContext, CompletionContext, Completions}; +use crate::{ + context::{NameRefContext, NameRefKind}, + CompletionContext, Completions, +}; pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { let item = match ctx.nameref_ctx() { - Some(NameRefContext { keyword: Some(item), record_expr: None, .. }) => item, + Some(NameRefContext { kind: Some(NameRefKind::Keyword(item)), .. }) => item, _ => return, }; diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs index 4868225ce35..888b8f34884 100644 --- a/crates/ide-completion/src/completions/postfix.rs +++ b/crates/ide-completion/src/completions/postfix.rs @@ -13,7 +13,7 @@ use crate::{ completions::postfix::format_like::add_format_like_completions, - context::{CompletionContext, DotAccess, DotAccessKind, NameRefContext}, + context::{CompletionContext, DotAccess, DotAccessKind, NameRefContext, NameRefKind}, item::{Builder, CompletionRelevancePostfixMatch}, CompletionItem, CompletionItemKind, CompletionRelevance, Completions, SnippetScope, }; @@ -25,7 +25,13 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { let (dot_receiver, receiver_ty, receiver_is_ambiguous_float_literal) = match ctx.nameref_ctx() { Some(NameRefContext { - dot_access: Some(DotAccess { receiver_ty: Some(ty), receiver: Some(it), kind, .. }), + kind: + Some(NameRefKind::DotAccess(DotAccess { + receiver_ty: Some(ty), + receiver: Some(it), + kind, + .. + })), .. }) => ( it, diff --git a/crates/ide-completion/src/completions/record.rs b/crates/ide-completion/src/completions/record.rs index 65805dba1ce..d74ae260b5d 100644 --- a/crates/ide-completion/src/completions/record.rs +++ b/crates/ide-completion/src/completions/record.rs @@ -3,7 +3,7 @@ use syntax::{ast::Expr, T}; use crate::{ - context::{NameRefContext, PatternContext}, + context::{NameRefContext, NameRefKind, PathCompletionCtx, PathKind, PatternContext}, CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance, CompletionRelevancePostfixMatch, Completions, }; @@ -13,8 +13,18 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> &ctx.pattern_ctx { ctx.sema.record_pattern_missing_fields(record_pat) - } else if let Some(NameRefContext { record_expr: Some((record_expr, _)), .. }) = - ctx.nameref_ctx() + } else if let Some(NameRefContext { + kind: + Some( + NameRefKind::RecordExpr(record_expr) + | NameRefKind::Path(PathCompletionCtx { + kind: PathKind::Expr { is_func_update: Some(record_expr), .. }, + qualifier: None, + .. + }), + ), + .. + }) = ctx.nameref_ctx() { let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone())); @@ -39,7 +49,7 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> ty.original.impls_trait(ctx.db, default_trait, &[]) }); - if impl_default_trait && !missing_fields.is_empty() && ctx.path_qual().is_none() { + if impl_default_trait && !missing_fields.is_empty() { let completion_text = "..Default::default()"; let mut item = CompletionItem::new(SymbolKind::Field, ctx.source_range(), completion_text); diff --git a/crates/ide-completion/src/completions/use_.rs b/crates/ide-completion/src/completions/use_.rs index dc7a342dce2..5d062098d7d 100644 --- a/crates/ide-completion/src/completions/use_.rs +++ b/crates/ide-completion/src/completions/use_.rs @@ -5,7 +5,10 @@ use syntax::{ast, AstNode}; use crate::{ - context::{CompletionContext, NameRefContext, PathCompletionCtx, PathKind, PathQualifierCtx}, + context::{ + CompletionContext, NameRefContext, NameRefKind, PathCompletionCtx, PathKind, + PathQualifierCtx, + }, item::Builder, CompletionItem, CompletionItemKind, CompletionRelevance, Completions, }; @@ -13,8 +16,13 @@ pub(crate) fn complete_use_tree(acc: &mut Completions, ctx: &CompletionContext) { let (&is_absolute_path, qualifier, name_ref) = match ctx.nameref_ctx() { Some(NameRefContext { - path_ctx: - Some(PathCompletionCtx { kind: PathKind::Use, is_absolute_path, qualifier, .. }), + kind: + Some(NameRefKind::Path(PathCompletionCtx { + kind: PathKind::Use, + is_absolute_path, + qualifier, + .. + })), nameref, .. }) => (is_absolute_path, qualifier, nameref), diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 02307def9e6..f790aa56ebf 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -50,6 +50,7 @@ pub(super) enum PathKind { in_loop_body: bool, after_if_expr: bool, ref_expr_parent: Option, + is_func_update: Option, }, Type { in_tuple_struct: bool, @@ -199,13 +200,17 @@ pub(super) enum NameKind { pub(super) struct NameRefContext { /// NameRef syntax in the original file pub(super) nameref: Option, - // FIXME: these fields are actually disjoint -> enum - pub(super) dot_access: Option, - pub(super) path_ctx: Option, + pub(super) kind: Option, +} + +#[derive(Debug)] +pub(super) enum NameRefKind { + Path(PathCompletionCtx), + DotAccess(DotAccess), /// Position where we are only interested in keyword completions - pub(super) keyword: Option, + Keyword(ast::Item), /// The record expression this nameref is a field of - pub(super) record_expr: Option<(ast::RecordExpr, bool)>, + RecordExpr(ast::RecordExpr), } #[derive(Debug)] @@ -341,9 +346,10 @@ pub(super) fn lifetime_ctx(&self) -> Option<&LifetimeContext> { pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> { match self.nameref_ctx() { - Some(NameRefContext { dot_access: Some(DotAccess { receiver, .. }), .. }) => { - receiver.as_ref() - } + Some(NameRefContext { + kind: Some(NameRefKind::DotAccess(DotAccess { receiver, .. })), + .. + }) => receiver.as_ref(), _ => None, } } @@ -358,7 +364,10 @@ pub(crate) fn expects_generic_arg(&self) -> bool { } pub(crate) fn path_context(&self) -> Option<&PathCompletionCtx> { - self.nameref_ctx().and_then(|ctx| ctx.path_ctx.as_ref()) + self.nameref_ctx().and_then(|ctx| match &ctx.kind { + Some(NameRefKind::Path(path)) => Some(path), + _ => None, + }) } pub(crate) fn path_qual(&self) -> Option<&ast::Path> { @@ -857,7 +866,7 @@ fn fill( let parent = name_ref.syntax().parent()?; let (mut nameref_ctx, _, _) = Self::classify_name_ref(&self.sema, &original_file, name_ref, parent); - if let Some(path_ctx) = &mut nameref_ctx.path_ctx { + if let Some(NameRefKind::Path(path_ctx)) = &mut nameref_ctx.kind { path_ctx.kind = PathKind::Derive; } self.ident_ctx = IdentContext::NameRef(nameref_ctx); @@ -1026,23 +1035,13 @@ fn classify_name_ref( ) -> (NameRefContext, Option, QualifierCtx) { let nameref = find_node_at_offset(&original_file, name_ref.syntax().text_range().start()); - let mut res = ( - NameRefContext { - dot_access: None, - path_ctx: None, - nameref, - record_expr: None, - keyword: None, - }, - None, - QualifierCtx::default(), - ); + let mut res = (NameRefContext { nameref, kind: None }, None, QualifierCtx::default()); let (nameref_ctx, pattern_ctx, qualifier_ctx) = &mut res; if let Some(record_field) = ast::RecordExprField::for_field_name(&name_ref) { - nameref_ctx.record_expr = + nameref_ctx.kind = find_node_in_file_compensated(original_file, &record_field.parent_record_lit()) - .zip(Some(false)); + .map(NameRefKind::RecordExpr); return res; } if let Some(record_field) = ast::RecordPatField::for_field_name_ref(&name_ref) { @@ -1075,20 +1074,20 @@ fn classify_name_ref( }, _ => false, }; - nameref_ctx.dot_access = Some(DotAccess { + nameref_ctx.kind = Some(NameRefKind::DotAccess(DotAccess { receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)), kind: DotAccessKind::Field { receiver_is_ambiguous_float_literal }, receiver - }); + })); return res; }, ast::MethodCallExpr(method) => { let receiver = find_in_original_file(method.receiver(), original_file); - nameref_ctx.dot_access = Some(DotAccess { + nameref_ctx.kind = Some(NameRefKind::DotAccess(DotAccess { receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)), kind: DotAccessKind::Method { has_parens: method.arg_list().map_or(false, |it| it.l_paren_token().is_some()) }, receiver - }); + })); return res; }, _ => return res, @@ -1113,10 +1112,11 @@ fn classify_name_ref( }) .unwrap_or(false) }; - let mut fill_record_expr = |syn: &SyntaxNode| { + let func_update_record = |syn: &SyntaxNode| { if let Some(record_expr) = syn.ancestors().nth(2).and_then(ast::RecordExpr::cast) { - nameref_ctx.record_expr = - find_node_in_file_compensated(original_file, &record_expr).zip(Some(true)); + find_node_in_file_compensated(original_file, &record_expr) + } else { + None } }; let after_if_expr = |node: SyntaxNode| { @@ -1172,22 +1172,21 @@ fn classify_name_ref( if let Some(p) = it.syntax().parent() { if ast::ExprStmt::can_cast(p.kind()) { if let Some(kind) = inbetween_body_and_decl_check(p) { - nameref_ctx.keyword = Some(kind); + nameref_ctx.kind = Some(NameRefKind::Keyword(kind)); return None; } } } - fill_record_expr(it.syntax()); - - path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind())); + path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind())); let in_block_expr = is_in_block(it.syntax()); let in_loop_body = is_in_loop_body(it.syntax()); let after_if_expr = after_if_expr(it.syntax().clone()); let ref_expr_parent = path.as_single_name_ref() .and_then(|_| it.syntax().parent()).and_then(ast::RefExpr::cast); + let is_func_update = func_update_record(it.syntax()); - Some(PathKind::Expr { in_block_expr, in_loop_body, after_if_expr, ref_expr_parent }) + Some(PathKind::Expr { in_block_expr, in_loop_body, after_if_expr, ref_expr_parent, is_func_update }) }, ast::TupleStructPat(it) => { path_ctx.has_call_parens = true; @@ -1205,7 +1204,7 @@ fn classify_name_ref( }, ast::MacroCall(it) => { if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) { - nameref_ctx.keyword = Some(kind); + nameref_ctx.kind = Some(NameRefKind::Keyword(kind)); return None; } @@ -1236,10 +1235,10 @@ fn classify_name_ref( let in_loop_body = is_in_loop_body(it.syntax()); let in_block_expr = is_in_block(it.syntax()); let after_if_expr = after_if_expr(it.syntax().clone()); - fill_record_expr(it.syntax()); let ref_expr_parent = path.as_single_name_ref() .and_then(|_| it.syntax().parent()).and_then(ast::RefExpr::cast); - PathKind::Expr { in_block_expr, in_loop_body, after_if_expr, ref_expr_parent } + let is_func_update = func_update_record(it.syntax()); + PathKind::Expr { in_block_expr, in_loop_body, after_if_expr, ref_expr_parent, is_func_update } }); }, } @@ -1365,7 +1364,7 @@ fn classify_name_ref( } } } - nameref_ctx.path_ctx = Some(path_ctx); + nameref_ctx.kind = Some(NameRefKind::Path(path_ctx)); res } } diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs index 0be51b0e3ff..566eaa575d4 100644 --- a/crates/ide-completion/src/render/function.rs +++ b/crates/ide-completion/src/render/function.rs @@ -8,7 +8,8 @@ use crate::{ context::{ - CompletionContext, DotAccess, DotAccessKind, NameRefContext, PathCompletionCtx, PathKind, + CompletionContext, DotAccess, DotAccessKind, NameRefContext, NameRefKind, + PathCompletionCtx, PathKind, }, item::{Builder, CompletionItem, CompletionItemKind, CompletionRelevance}, render::{compute_exact_name_match, compute_ref_match, compute_type_match, RenderContext}, @@ -212,7 +213,10 @@ fn should_add_parens(ctx: &CompletionContext) -> bool { if matches!( ctx.nameref_ctx(), Some(NameRefContext { - dot_access: Some(DotAccess { kind: DotAccessKind::Method { has_parens: true }, .. }), + kind: Some(NameRefKind::DotAccess(DotAccess { + kind: DotAccessKind::Method { has_parens: true }, + .. + })), .. }) ) { diff --git a/crates/ide-completion/src/tests/record.rs b/crates/ide-completion/src/tests/record.rs index 9369034cc62..c7514e1b578 100644 --- a/crates/ide-completion/src/tests/record.rs +++ b/crates/ide-completion/src/tests/record.rs @@ -105,7 +105,6 @@ fn foo(f: Struct) { #[test] fn functional_update() { // FIXME: This should filter out all completions that do not have the type `Foo` - // FIXME: Fields should not show up after `.` check( r#" //- minicore:default @@ -192,8 +191,6 @@ fn main() { } "#, expect![[r#" - fd foo1 u32 - fd foo2 u32 fn default() (as Default) fn() -> Self "#]], );