about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2022-02-02 18:09:30 +0100
committerLukas Wirth <lukastw97@gmail.com>2022-02-03 15:52:03 +0100
commit9f5ee155c1a39c028f79e281d1edcaacc54bedb1 (patch)
tree493d36a61e09c1d58abe84b7d20f811fc4885562
parent33fd2d7aeffc13efc40a22a77116ae9a4de9dff1 (diff)
downloadrust-9f5ee155c1a39c028f79e281d1edcaacc54bedb1.tar.gz
rust-9f5ee155c1a39c028f79e281d1edcaacc54bedb1.zip
Move path completions for patterns into pattern module
-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 0c0c9719d31..4704e842e6a 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 a140ca4239b..438230c58f1 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 a3606c17f5a..cf78f7c1adf 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 c225b37d72d..cca2785e2dd 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 de1a378ea82..910652cba89 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 662fbe309bc..779ec0c3a76 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 7f437e1a077..fe532576729 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
-        "#]],
-    )
-}