From 9f5ee155c1a39c028f79e281d1edcaacc54bedb1 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 2 Feb 2022 18:09:30 +0100 Subject: [PATCH] Move path completions for patterns into pattern module --- .../ide_completion/src/completions/keyword.rs | 5 +- .../ide_completion/src/completions/pattern.rs | 145 +++++++++++++++++- .../src/completions/qualified_path.rs | 6 +- .../src/completions/unqualified_path.rs | 8 +- crates/ide_completion/src/context.rs | 26 ++-- crates/ide_completion/src/tests/fn_param.rs | 26 ++++ crates/ide_completion/src/tests/pattern.rs | 50 +++--- 7 files changed, 215 insertions(+), 51 deletions(-) diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs index 0c0c9719d3..4704e842e6 100644 --- a/crates/ide_completion/src/completions/keyword.rs +++ b/crates/ide_completion/src/completions/keyword.rs @@ -27,6 +27,9 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte cov_mark::hit!(no_keyword_completion_in_non_trivial_path); return; } + if ctx.pattern_ctx.is_some() { + return; + } let mut add_keyword = |kw, snippet| add_keyword(acc, ctx, kw, snippet); @@ -117,7 +120,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte add_keyword("else if", "else if $1 {\n $0\n}"); } - if ctx.expects_ident_pat_or_ref_expr() { + if ctx.expects_ident_ref_expr() { add_keyword("mut", "mut "); } diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs index a140ca4239..438230c58f 100644 --- a/crates/ide_completion/src/completions/pattern.rs +++ b/crates/ide_completion/src/completions/pattern.rs @@ -1,20 +1,52 @@ //! Completes constants and paths in unqualified patterns. -use hir::db::DefDatabase; +use hir::{db::DefDatabase, AssocItem, ScopeDef}; +use rustc_hash::FxHashSet; +use syntax::ast::Pat; use crate::{ - context::{PatternContext, PatternRefutability}, + context::{PathCompletionCtx, PathQualifierCtx, PatternRefutability}, CompletionContext, Completions, }; /// Completes constants and paths in unqualified patterns. pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { - let refutable = match ctx.pattern_ctx { - Some(PatternContext { refutability, .. }) if ctx.path_context.is_none() => { - refutability == PatternRefutability::Refutable - } + let patctx = match &ctx.pattern_ctx { + Some(ctx) => ctx, _ => return, }; + let refutable = patctx.refutability == PatternRefutability::Refutable; + + if let Some(path_ctx) = &ctx.path_context { + pattern_path_completion(acc, ctx, path_ctx); + return; + } + + match patctx.parent_pat.as_ref() { + Some(Pat::RangePat(_) | Pat::BoxPat(_)) => (), + Some(Pat::RefPat(r)) => { + if r.mut_token().is_none() { + acc.add_keyword(ctx, "mut"); + } + } + _ => { + let tok = ctx.token.text_range().start(); + match (patctx.ref_token.as_ref(), patctx.mut_token.as_ref()) { + (None, None) => { + acc.add_keyword(ctx, "ref"); + acc.add_keyword(ctx, "mut"); + } + (None, Some(m)) if tok < m.text_range().start() => { + acc.add_keyword(ctx, "ref"); + } + (Some(r), None) if tok > r.text_range().end() => { + acc.add_keyword(ctx, "mut"); + } + _ => (), + } + } + } + let single_variant_enum = |enum_: hir::Enum| ctx.db.enum_data(enum_.into()).variants.len() == 1; if let Some(hir::Adt::Enum(e)) = @@ -63,3 +95,104 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { } }); } + +fn pattern_path_completion( + acc: &mut Completions, + ctx: &CompletionContext, + PathCompletionCtx { qualifier, is_absolute_path, .. }: &PathCompletionCtx, +) { + match qualifier { + Some(PathQualifierCtx { resolution, is_super_chain, .. }) => { + if *is_super_chain { + acc.add_keyword(ctx, "super::"); + } + + let resolution = match resolution { + Some(it) => it, + None => return, + }; + + match resolution { + hir::PathResolution::Def(hir::ModuleDef::Module(module)) => { + let module_scope = module.scope(ctx.db, ctx.module); + for (name, def) in module_scope { + let add_resolution = match def { + ScopeDef::MacroDef(m) if m.is_fn_like() => true, + ScopeDef::ModuleDef(_) => true, + _ => false, + }; + + if add_resolution { + acc.add_resolution(ctx, name, def); + } + } + } + hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => { + cov_mark::hit!(enum_plain_qualified_use_tree); + e.variants(ctx.db) + .into_iter() + .for_each(|variant| acc.add_enum_variant(ctx, variant, None)); + } + res @ (hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_)) => { + if let Some(krate) = ctx.krate { + let ty = match res { + hir::PathResolution::TypeParam(param) => param.ty(ctx.db), + hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db), + _ => return, + }; + + // Note associated consts cannot be referenced in patterns + if let Some(hir::Adt::Enum(e)) = ty.as_adt() { + e.variants(ctx.db) + .into_iter() + .for_each(|variant| acc.add_enum_variant(ctx, variant, None)); + } + + let traits_in_scope = ctx.scope.visible_traits(); + let mut seen = FxHashSet::default(); + ty.iterate_path_candidates( + ctx.db, + krate, + &traits_in_scope, + ctx.module, + None, + |_ty, item| { + // We might iterate candidates of a trait multiple times here, so deduplicate + // them. + if let AssocItem::TypeAlias(ta) = item { + if seen.insert(item) { + acc.add_type_alias(ctx, ta); + } + } + None::<()> + }, + ); + } + } + _ => {} + } + } + // qualifier can only be none here if we are in a TuplePat or RecordPat in which case special characters have to follow the path + // so executing the rest of this completion doesn't make sense + // fresh use tree with leading colon2, only show crate roots + None if *is_absolute_path => { + cov_mark::hit!(use_tree_crate_roots_only); + ctx.process_all_names(&mut |name, res| match res { + ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root(ctx.db) => { + acc.add_resolution(ctx, name, res); + } + _ => (), + }); + } + // only show modules in a fresh UseTree + None => { + cov_mark::hit!(unqualified_path_only_modules_in_import); + ctx.process_all_names(&mut |name, res| { + if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res { + acc.add_resolution(ctx, name, res); + } + }); + ["self::", "super::", "crate::"].into_iter().for_each(|kw| acc.add_keyword(ctx, kw)); + } + } +} diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs index a3606c17f5..cf78f7c1ad 100644 --- a/crates/ide_completion/src/completions/qualified_path.rs +++ b/crates/ide_completion/src/completions/qualified_path.rs @@ -15,6 +15,9 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon if ctx.is_path_disallowed() || ctx.has_impl_or_trait_prev_sibling() { return; } + if ctx.pattern_ctx.is_some() { + return; + } let (qualifier, kind) = match ctx.path_context { // let ... else, syntax would come in really handy here right now Some(PathCompletionCtx { qualifier: Some(ref qualifier), kind, .. }) => (qualifier, kind), @@ -60,10 +63,9 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon } match kind { - Some(PathKind::Attr { .. } | PathKind::Vis { .. } | PathKind::Use) => { + Some(PathKind::Pat | PathKind::Attr { .. } | PathKind::Vis { .. } | PathKind::Use) => { return; } - Some(PathKind::Pat) => (), _ => { // Add associated types on type parameters and `Self`. ctx.scope.assoc_type_shorthand_candidates(&resolution, |_, alias| { diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs index c225b37d72..cca2785e2d 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs @@ -17,7 +17,13 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC } match ctx.path_context { Some(PathCompletionCtx { - kind: Some(PathKind::Vis { .. } | PathKind::Attr { .. } | PathKind::Use { .. }), + kind: + Some( + PathKind::Vis { .. } + | PathKind::Attr { .. } + | PathKind::Use { .. } + | PathKind::Pat, + ), .. }) => return, Some(PathCompletionCtx { is_absolute_path: false, qualifier: None, .. }) => (), diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index de1a378ea8..910652cba8 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs @@ -85,6 +85,9 @@ pub(super) struct PatternContext { pub(super) refutability: PatternRefutability, pub(super) param_ctx: Option<(ast::ParamList, ast::Param, ParamKind)>, pub(super) has_type_ascription: bool, + pub(super) parent_pat: Option, + pub(super) ref_token: Option, + pub(super) mut_token: Option, } #[derive(Debug)] @@ -219,11 +222,8 @@ impl<'a> CompletionContext<'a> { matches!(self.completion_location, Some(ImmediateLocation::StmtList)) } - pub(crate) fn expects_ident_pat_or_ref_expr(&self) -> bool { - matches!( - self.completion_location, - Some(ImmediateLocation::IdentPat | ImmediateLocation::RefExpr) - ) + pub(crate) fn expects_ident_ref_expr(&self) -> bool { + matches!(self.completion_location, Some(ImmediateLocation::RefExpr)) } pub(crate) fn expect_field(&self) -> bool { @@ -789,9 +789,6 @@ impl<'a> CompletionContext<'a> { if is_name_in_field_pat { return None; } - if !bind_pat.is_simple_ident() { - return None; - } Some(pattern_context_for(original_file, bind_pat.into())) } @@ -949,7 +946,18 @@ fn pattern_context_for(original_file: &SyntaxNode, pat: ast::Pat) -> PatternCont }; (refutability, false) }); - PatternContext { refutability, param_ctx: is_param, has_type_ascription } + let (ref_token, mut_token) = match &pat { + ast::Pat::IdentPat(it) => (it.ref_token(), it.mut_token()), + _ => (None, None), + }; + PatternContext { + refutability, + param_ctx: is_param, + has_type_ascription, + parent_pat: pat.syntax().parent().and_then(ast::Pat::cast), + mut_token, + ref_token, + } } fn find_node_in_file(syntax: &SyntaxNode, node: &N) -> Option { diff --git a/crates/ide_completion/src/tests/fn_param.rs b/crates/ide_completion/src/tests/fn_param.rs index 662fbe309b..779ec0c3a7 100644 --- a/crates/ide_completion/src/tests/fn_param.rs +++ b/crates/ide_completion/src/tests/fn_param.rs @@ -17,6 +17,7 @@ fn baz(file$0) {} "#, expect![[r#" bn file_id: usize + kw ref kw mut "#]], ); @@ -32,6 +33,7 @@ fn baz(foo: (), file$0) {} "#, expect![[r#" bn file_id: usize + kw ref kw mut "#]], ); @@ -47,6 +49,7 @@ fn baz(file$0 id: u32) {} "#, expect![[r#" bn file_id: usize, + kw ref kw mut "#]], ); @@ -60,6 +63,7 @@ fn foo(file_id: usize) {} fn bar(file_id: u32, $0) {} "#, expect![[r#" + kw ref kw mut "#]], ); @@ -76,6 +80,7 @@ pub(crate) trait SourceRoot { "#, expect![[r#" bn file_id: usize + kw ref kw mut "#]], ); @@ -91,6 +96,7 @@ fn outer(text: &str) { "#, expect![[r#" bn text: &str + kw ref kw mut "#]], ) @@ -106,6 +112,7 @@ fn foo2($0) {} "#, expect![[r#" bn Bar { bar }: Bar + kw ref kw mut bn Bar Bar { bar$1 }: Bar$0 st Bar @@ -130,6 +137,7 @@ impl A { bn mut self bn &mut self bn file_id: usize + kw ref kw mut sp Self st A @@ -150,6 +158,7 @@ impl A { "#, expect![[r#" bn file_id: usize + kw ref kw mut sp Self st A @@ -178,6 +187,7 @@ fn outer() { bn foo: i32 bn baz: i32 bn bar: i32 + kw ref kw mut "#]], ) @@ -202,6 +212,22 @@ fn outer() { bn baz: i32 bn bar: i32 bn foo: i32 + kw ref + kw mut + "#]], + ) +} + +#[test] +fn completes_fully_equal() { + check( + r#" +fn foo(bar: u32) {} +fn bar(bar$0) {} +"#, + expect![[r#" + bn bar: u32 + kw ref kw mut "#]], ) diff --git a/crates/ide_completion/src/tests/pattern.rs b/crates/ide_completion/src/tests/pattern.rs index 7f437e1a07..fe53257672 100644 --- a/crates/ide_completion/src/tests/pattern.rs +++ b/crates/ide_completion/src/tests/pattern.rs @@ -22,6 +22,7 @@ fn quux() { } "#, expect![[r#" + kw ref kw mut "#]], ); @@ -53,16 +54,13 @@ fn quux() { #[test] fn ident_ref_mut_pat() { - // FIXME mut is already here, don't complete it again check_empty( r#" fn quux() { let ref mut en$0 } "#, - expect![[r#" - kw mut - "#]], + expect![[r#""#]], ); check_empty( r#" @@ -70,9 +68,7 @@ fn quux() { let ref mut en$0 @ x } "#, - expect![[r#" - kw mut - "#]], + expect![[r#""#]], ); } @@ -88,16 +84,13 @@ fn quux() { kw mut "#]], ); - // FIXME mut is already here, don't complete it again check_empty( r#" fn quux() { let &mut en$0 } "#, - expect![[r#" - kw mut - "#]], + expect![[r#""#]], ); } @@ -110,6 +103,7 @@ fn foo() { } "#, expect![[r##" + kw ref kw mut en Enum bn Record Record { field$1 }$0 @@ -139,6 +133,7 @@ fn foo() { } "#, expect![[r##" + kw ref kw mut bn Record Record { field$1 }$0 st Record @@ -160,6 +155,7 @@ fn foo(a$0) { } "#, expect![[r##" + kw ref kw mut bn Record Record { field$1 }: Record$0 st Record @@ -175,6 +171,7 @@ fn foo(a$0: Tuple) { } "#, expect![[r##" + kw ref kw mut bn Record Record { field$1 }$0 st Record @@ -200,6 +197,7 @@ fn foo() { } "#, expect![[r#" + kw ref kw mut ma m!(…) macro_rules! m "#]], @@ -218,6 +216,7 @@ fn foo() { } "#, expect![[r#" + kw ref kw mut ev E::X () en E @@ -242,6 +241,7 @@ fn outer() { } "#, expect![[r#" + kw ref kw mut bn Record Record { field$1, .. }$0 st Record @@ -267,6 +267,7 @@ impl Foo { } "#, expect![[r#" + kw ref kw mut bn Self Self($1)$0 sp Self @@ -278,7 +279,6 @@ impl Foo { #[test] fn enum_qualified() { - // FIXME: Don't show functions, they aren't patterns check( r#" impl Enum { @@ -291,12 +291,9 @@ fn func() { } "#, expect![[r#" - ev TupleV(…) (u32) - ev RecordV {field: u32} - ev UnitV () - ct ASSOC_CONST const ASSOC_CONST: () - fn assoc_fn() fn() - ta AssocType type AssocType = () + ev TupleV(…) (u32) + ev RecordV {field: u32} + ev UnitV () "#]], ); } @@ -310,6 +307,7 @@ struct Bar(u32); fn outer(Foo { bar: $0 }: Foo) {} "#, expect![[r#" + kw ref kw mut bn Foo Foo { bar$1 }$0 st Foo @@ -340,6 +338,7 @@ struct Bar(u32); fn foo($0) {} "#, expect![[r#" + kw ref kw mut bn Foo Foo { bar$1 }: Foo$0 st Foo @@ -360,6 +359,7 @@ fn foo() { } "#, expect![[r#" + kw ref kw mut bn Foo Foo { bar$1 }$0 st Foo @@ -368,17 +368,3 @@ fn foo() { "#]], ) } - -#[test] -fn completes_fully_equal() { - check_empty( - r#" -fn foo(bar: u32) {} -fn bar(bar$0) {} -"#, - expect![[r#" - bn bar: u32 - kw mut - "#]], - ) -}