Unnamed repository; edit this file 'description' to name the repository.
Move path completions for patterns into pattern module
Lukas Wirth 2022-02-03
parent 33fd2d7 · commit 9f5ee15
-rw-r--r--crates/ide_completion/src/completions/keyword.rs5
-rw-r--r--crates/ide_completion/src/completions/pattern.rs145
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs6
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs8
-rw-r--r--crates/ide_completion/src/context.rs26
-rw-r--r--crates/ide_completion/src/tests/fn_param.rs26
-rw-r--r--crates/ide_completion/src/tests/pattern.rs50
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<ast::Pat>,
+ pub(super) ref_token: Option<SyntaxToken>,
+ pub(super) mut_token: Option<SyntaxToken>,
}
#[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<N: AstNode>(syntax: &SyntaxNode, node: &N) -> Option<N> {
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
- "#]],
- )
-}