Repository: rust-lang/rust-analyzer
Pull request #12090 (2022-04-26 20:26:35)

fix: Use pattern recovery set when parsing ident patterns

bors: :pushpin: Commit d7d6cd79e71b6495da929515a34b23d50fa051db has been approved by `Veykril`

<!-- @bors r=Veykril d7d6cd79e71b6495da929515a34b23d50fa051db -->
<!-- homu: {"type":"Approved","sha":"d7d6cd79e71b6495da929515a34b23d50fa051db","approver":"Veykril"} -->
bors: :hourglass: Testing commit d7d6cd79e71b6495da929515a34b23d50fa051db with merge fb079d3d61463b9367a9f0f0140d8010ff2e7371...
<!-- homu: {"type":"BuildStarted","head_sha":"d7d6cd79e71b6495da929515a34b23d50fa051db","merge_sha":"fb079d3d61463b9367a9f0f0140d8010ff2e7371"} -->
bors: :broken_heart: Test failed - [checks-actions](https://github.com/rust-lang/rust-analyzer/runs/6183094411?check_suite_focus=true)
<!-- homu: {"type":"BuildFailed","builder_url":"https://github.com/rust-lang/rust-analyzer/runs/6183094411?check_suite_focus=true","builder_name":"checks-actions"} -->

Patch:
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index fa36241c8c..f78e70f7a3 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -36,13 +36,13 @@ pub enum LifetimeElisionHints {
 
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub enum InlayKind {
-    ChainingHint,
+    ImplicitReborrow,
+    TypeHint,
+    ParameterHint,
     ClosureReturnTypeHint,
+    ChainingHint,
     GenericParamListHint,
-    ImplicitReborrow,
     LifetimeHint,
-    ParameterHint,
-    TypeHint,
 }
 
 #[derive(Debug)]
@@ -110,11 +110,11 @@ fn hints(
     config: &InlayHintsConfig,
     node: SyntaxNode,
 ) {
-    let famous_defs = match sema.scope(&node) {
-        Some(it) => FamousDefs(sema, it.krate()),
+    let krate = match sema.scope(&node) {
+        Some(it) => it.krate(),
         None => return,
     };
-
+    let famous_defs = FamousDefs(sema, krate);
     if let Some(expr) = ast::Expr::cast(node.clone()) {
         chaining_hints(hints, sema, &famous_defs, config, &expr);
         match expr {
@@ -1637,7 +1637,10 @@ fn main() {
     fn skip_constructor_and_enum_type_hints() {
         check_with_config(
             InlayHintsConfig {
+                render_colons: true,
                 type_hints: true,
+                parameter_hints: true,
+                chaining_hints: true,
                 hide_named_constructor_hints: true,
                 ..DISABLED_CONFIG
             },
@@ -2144,7 +2147,7 @@ impl () {
     #[test]
     fn hints_implicit_reborrow() {
         check_with_config(
-            InlayHintsConfig { reborrow_hints: true, parameter_hints: true, ..DISABLED_CONFIG },
+            InlayHintsConfig { reborrow_hints: true, ..DISABLED_CONFIG },
             r#"
 fn __() {
     let unique = &mut ();
@@ -2152,15 +2155,12 @@ fn __() {
     let foo: &mut _ = unique;
                     //^^^^^^ &mut *
     ref_mut_id(unique);
-             //^^^^^^ mut_ref
              //^^^^^^ &mut *
     let shared = ref_id(unique);
-                      //^^^^^^ shared_ref
                       //^^^^^^ &*
     let mov = shared;
     let r_mov: &_ = shared;
     ref_id(shared);
-         //^^^^^^ shared_ref
 
     identity(unique);
     identity(shared);
@@ -2168,12 +2168,12 @@ fn __() {
 fn identity<T>(t: T) -> T {
     t
 }
-fn ref_mut_id(mut_ref: &mut ()) -> &mut () {
-    mut_ref
-  //^^^^^^^ &mut *
+fn ref_mut_id(x: &mut ()) -> &mut () {
+    x
+  //^ &mut *
 }
-fn ref_id(shared_ref: &()) -> &() {
-    shared_ref
+fn ref_id(x: &()) -> &() {
+    x
 }
 "#,
         );
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 8c2219860b..2c21bbc165 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -435,8 +435,7 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> {
                             ty_args.format_with(", ", |ty, cb| cb(&ty.display(db)))
                         );
                     }
-                    format_to!(path, "::{}", def_name);
-                    return Some(path);
+                    return Some(format!(r#""{}::{}""#, path, def_name));
                 }
             }
         }
@@ -978,7 +977,7 @@ impl Data {
                         },
                         kind: DocTest {
                             test_id: Path(
-                                "Data::foo",
+                                "\"Data::foo\"",
                             ),
                         },
                         cfg: None,
@@ -1372,7 +1371,7 @@ impl Foo {
                         },
                         kind: DocTest {
                             test_id: Path(
-                                "foo::Foo::foo",
+                                "\"foo::Foo::foo\"",
                             ),
                         },
                         cfg: None,
@@ -2078,7 +2077,7 @@ impl<T, U> Foo<T, U> {
                         },
                         kind: DocTest {
                             test_id: Path(
-                                "Foo<T, U>::t",
+                                "\"Foo<T, U>::t\"",
                             ),
                         },
                         cfg: None,
diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs
index 50187c933d..4eca69033a 100644
--- a/crates/ide/src/signature_help.rs
+++ b/crates/ide/src/signature_help.rs
@@ -115,10 +115,7 @@ fn signature_help_for_call(
         hir::CallableKind::Function(func) => {
             res.doc = func.docs(db).map(|it| it.into());
             format_to!(res.signature, "fn {}", func.name(db));
-            fn_params = Some(match func.self_param(db) {
-                Some(_self) => func.params_without_self(db),
-                None => func.assoc_fn_params(db),
-            });
+            fn_params = Some(func.assoc_fn_params(db));
         }
         hir::CallableKind::TupleStruct(strukt) => {
             res.doc = strukt.docs(db).map(|it| it.into());
@@ -1039,25 +1036,6 @@ fn f() {
         );
     }
 
-    #[test]
-    fn test_generic_param_in_method_call() {
-        check(
-            r#"
-struct Foo;
-impl Foo {
-    fn test<V>(&mut self, val: V) {}
-}
-fn sup() {
-    Foo.test($0)
-}
-"#,
-            expect![[r#"
-                fn test(&mut self, val: V)
-                                   ^^^^^^
-            "#]],
-        );
-    }
-
     #[test]
     fn test_generic_kinds() {
         check(
diff --git a/crates/ide_assists/src/handlers/move_module_to_file.rs b/crates/ide_assists/src/handlers/move_module_to_file.rs
index 759494dcf0..7dc71cf710 100644
--- a/crates/ide_assists/src/handlers/move_module_to_file.rs
+++ b/crates/ide_assists/src/handlers/move_module_to_file.rs
@@ -6,7 +6,7 @@ use itertools::Itertools;
 use stdx::format_to;
 use syntax::{
     ast::{self, edit::AstNodeEdit, HasName},
-    AstNode, SmolStr, TextRange,
+    AstNode, TextRange,
 };
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -58,18 +58,9 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext) -> Opt
                 }
                 let segments = iter::successors(Some(module_ast.clone()), |module| module.parent())
                     .filter_map(|it| it.name())
-                    .map(|name| SmolStr::from(name.text().trim_start_matches("r#")))
                     .collect::<Vec<_>>();
-
                 format_to!(buf, "{}", segments.into_iter().rev().format("/"));
-
-                // We need to special case mod named `r#mod` and place the file in a
-                // subdirectory as "mod.rs" would be of its parent module otherwise.
-                if module_name.text() == "r#mod" {
-                    format_to!(buf, "/mod.rs");
-                } else {
-                    format_to!(buf, ".rs");
-                }
+                format_to!(buf, ".rs");
                 buf
             };
             let contents = {
@@ -260,78 +251,4 @@ mod bar {
 "#,
         );
     }
-
-    #[test]
-    fn extract_mod_with_raw_ident() {
-        check_assist(
-            move_module_to_file,
-            r#"
-//- /main.rs
-mod $0r#static {}
-"#,
-            r#"
-//- /main.rs
-mod r#static;
-//- /static.rs
-"#,
-        )
-    }
-
-    #[test]
-    fn extract_r_mod() {
-        check_assist(
-            move_module_to_file,
-            r#"
-//- /main.rs
-mod $0r#mod {}
-"#,
-            r#"
-//- /main.rs
-mod r#mod;
-//- /mod/mod.rs
-"#,
-        )
-    }
-
-    #[test]
-    fn extract_r_mod_from_mod_rs() {
-        check_assist(
-            move_module_to_file,
-            r#"
-//- /main.rs
-mod foo;
-//- /foo/mod.rs
-mod $0r#mod {}
-"#,
-            r#"
-//- /foo/mod.rs
-mod r#mod;
-//- /foo/mod/mod.rs
-"#,
-        )
-    }
-
-    #[test]
-    fn extract_nested_r_mod() {
-        check_assist(
-            move_module_to_file,
-            r#"
-//- /main.rs
-mod r#mod {
-    mod foo {
-        mod $0r#mod {}
-    }
-}
-"#,
-            r#"
-//- /main.rs
-mod r#mod {
-    mod foo {
-        mod r#mod;
-    }
-}
-//- /mod/foo/mod/mod.rs
-"#,
-        )
-    }
 }
diff --git a/crates/ide_completion/src/completions/fn_param.rs b/crates/ide_completion/src/completions/fn_param.rs
index 47cead7d57..c010c49489 100644
--- a/crates/ide_completion/src/completions/fn_param.rs
+++ b/crates/ide_completion/src/completions/fn_param.rs
@@ -15,7 +15,8 @@ use crate::{
 
 /// Complete repeated parameters, both name and type. For example, if all
 /// functions in a file have a `spam: &mut Spam` parameter, a completion with
-/// `spam: &mut Spam` insert text/label will be suggested.
+/// `spam: &mut Spam` insert text/label and `spam` lookup string will be
+/// suggested.
 ///
 /// Also complete parameters for closure or local functions from the surrounding defined locals.
 pub(crate) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
@@ -25,16 +26,14 @@ pub(crate) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
     };
 
     let comma_wrapper = comma_wrapper(ctx);
-    let mut add_new_item_to_acc = |label: &str| {
+    let mut add_new_item_to_acc = |label: &str, lookup: String| {
         let mk_item = |label: &str, range: TextRange| {
             CompletionItem::new(CompletionItemKind::Binding, range, label)
         };
         let item = match &comma_wrapper {
-            Some((fmt, range)) => mk_item(&fmt(label), *range),
-            None => mk_item(label, ctx.source_range()),
+            Some((fmt, range, lookup)) => mk_item(&fmt(label), *range).lookup_by(lookup).to_owned(),
+            None => mk_item(label, ctx.source_range()).lookup_by(lookup).to_owned(),
         };
-        // Completion lookup is omitted intentionally here.
-        // See the full discussion: https://github.com/rust-lang/rust-analyzer/issues/12073
         item.add_to(acc)
     };
 
@@ -45,7 +44,7 @@ pub(crate) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
         ParamKind::Closure(closure) => {
             let stmt_list = closure.syntax().ancestors().find_map(ast::StmtList::cast)?;
             params_from_stmt_list_scope(ctx, stmt_list, |name, ty| {
-                add_new_item_to_acc(&format!("{name}: {ty}"));
+                add_new_item_to_acc(&format!("{name}: {ty}"), name.to_string());
             });
         }
     }
@@ -57,7 +56,7 @@ fn fill_fn_params(
     ctx: &CompletionContext,
     function: &ast::Fn,
     param_list: &ast::ParamList,
-    mut add_new_item_to_acc: impl FnMut(&str),
+    mut add_new_item_to_acc: impl FnMut(&str, String),
 ) {
     let mut file_params = FxHashMap::default();
 
@@ -97,13 +96,18 @@ fn fill_fn_params(
             file_params.entry(format!("{name}: {ty}")).or_insert(name.to_string());
         });
     }
+
     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) {
-        self_completion_items.into_iter().for_each(|self_item| add_new_item_to_acc(self_item));
+        self_completion_items
+            .into_iter()
+            .for_each(|self_item| add_new_item_to_acc(self_item, self_item.to_string()));
     }
 
-    file_params.keys().for_each(|whole_param| add_new_item_to_acc(whole_param));
+    file_params
+        .into_iter()
+        .for_each(|(whole_param, binding)| add_new_item_to_acc(&whole_param, binding));
 }
 
 fn params_from_stmt_list_scope(
@@ -157,7 +161,7 @@ fn should_add_self_completions(ctx: &CompletionContext, param_list: &ast::ParamL
     inside_impl && no_params
 }
 
-fn comma_wrapper(ctx: &CompletionContext) -> Option<(impl Fn(&str) -> String, TextRange)> {
+fn comma_wrapper(ctx: &CompletionContext) -> Option<(impl Fn(&str) -> String, TextRange, String)> {
     let param = ctx.token.ancestors().find(|node| node.kind() == SyntaxKind::PARAM)?;
 
     let next_token_kind = {
@@ -179,5 +183,9 @@ fn comma_wrapper(ctx: &CompletionContext) -> Option<(impl Fn(&str) -> String, Te
         matches!(prev_token_kind, SyntaxKind::COMMA | SyntaxKind::L_PAREN | SyntaxKind::PIPE);
     let leading = if has_leading_comma { "" } else { ", " };
 
-    Some((move |label: &_| (format!("{}{}{}", leading, label, trailing)), param.text_range()))
+    Some((
+        move |label: &_| (format!("{}{}{}", leading, label, trailing)),
+        param.text_range(),
+        format!("{}{}", leading, param.text()),
+    ))
 }
diff --git a/crates/ide_completion/src/completions/snippet.rs b/crates/ide_completion/src/completions/snippet.rs
index c00de6ff50..6a0e09dd64 100644
--- a/crates/ide_completion/src/completions/snippet.rs
+++ b/crates/ide_completion/src/completions/snippet.rs
@@ -43,7 +43,7 @@ pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionConte
 }
 
 pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) {
-    if !(ctx.expects_item() || ctx.has_block_expr_parent())
+    if !ctx.expects_item()
         || ctx.previous_token_is(T![unsafe])
         || ctx.path_qual().is_some()
         || ctx.has_impl_or_trait_prev_sibling()
@@ -63,13 +63,11 @@ pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionConte
         add_custom_completions(acc, ctx, cap, SnippetScope::Item);
     }
 
-    // Test-related snippets shouldn't be shown in blocks.
-    if !ctx.has_block_expr_parent() {
-        let mut item = snippet(
-            ctx,
-            cap,
-            "tmod (Test module)",
-            "\
+    let mut item = snippet(
+        ctx,
+        cap,
+        "tmod (Test module)",
+        "\
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -79,23 +77,22 @@ mod tests {
         $0
     }
 }",
-        );
-        item.lookup_by("tmod");
-        item.add_to(acc);
-
-        let mut item = snippet(
-            ctx,
-            cap,
-            "tfn (Test function)",
-            "\
+    );
+    item.lookup_by("tmod");
+    item.add_to(acc);
+
+    let mut item = snippet(
+        ctx,
+        cap,
+        "tfn (Test function)",
+        "\
 #[test]
 fn ${1:feature}() {
     $0
 }",
-        );
-        item.lookup_by("tfn");
-        item.add_to(acc);
-    }
+    );
+    item.lookup_by("tfn");
+    item.add_to(acc);
 
     let item = snippet(
         ctx,
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs
index d8183660e8..bd6833567c 100644
--- a/crates/ide_completion/src/render/function.rs
+++ b/crates/ide_completion/src/render/function.rs
@@ -570,7 +570,7 @@ fn main() {
     fn complete_fn_param() {
         // has mut kw
         check_edit(
-            "mut bar: u32",
+            "mut ba",
             r#"
 fn f(foo: (), mut bar: u32) {}
 fn g(foo: (), mut ba$0)
@@ -583,7 +583,7 @@ fn g(foo: (), mut bar: u32)
 
         // has type param
         check_edit(
-            "mut bar: u32",
+            "mut ba: u32",
             r#"
 fn g(foo: (), mut ba$0: u32)
 fn f(foo: (), mut bar: u32) {}
@@ -599,7 +599,7 @@ fn f(foo: (), mut bar: u32) {}
     fn complete_fn_mut_param_add_comma() {
         // add leading and trailing comma
         check_edit(
-            ", mut bar: u32,",
+            ", mut ba",
             r#"
 fn f(foo: (), mut bar: u32) {}
 fn g(foo: ()mut ba$0 baz: ())
@@ -614,7 +614,7 @@ fn g(foo: (), mut bar: u32, baz: ())
     #[test]
     fn complete_fn_mut_param_has_attribute() {
         check_edit(
-            r#"#[baz = "qux"] mut bar: u32"#,
+            "mut ba",
             r#"
 fn f(foo: (), #[baz = "qux"] mut bar: u32) {}
 fn g(foo: (), mut ba$0)
@@ -626,7 +626,7 @@ fn g(foo: (), #[baz = "qux"] mut bar: u32)
         );
 
         check_edit(
-            r#"#[baz = "qux"] mut bar: u32"#,
+            r#"#[baz = "qux"] mut ba"#,
             r#"
 fn f(foo: (), #[baz = "qux"] mut bar: u32) {}
 fn g(foo: (), #[baz = "qux"] mut ba$0)
@@ -638,7 +638,7 @@ fn g(foo: (), #[baz = "qux"] mut bar: u32)
         );
 
         check_edit(
-            r#", #[baz = "qux"] mut bar: u32"#,
+            r#", #[baz = "qux"] mut ba"#,
             r#"
 fn f(foo: (), #[baz = "qux"] mut bar: u32) {}
 fn g(foo: ()#[baz = "qux"] mut ba$0)
diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs
index 70ff87b238..1f622b32e5 100644
--- a/crates/parser/src/grammar/patterns.rs
+++ b/crates/parser/src/grammar/patterns.rs
@@ -223,20 +223,16 @@ fn record_pat_field(p: &mut Parser) {
             p.bump(T![:]);
             pattern(p);
         }
-        T![.] => {
-            if p.at(T![..]) {
-                p.bump(T![..]);
-            } else {
-                ident_pat(p, false);
-            }
-        }
         T![box] => {
             // FIXME: not all box patterns should be allowed
             box_pat(p);
         }
-        _ => {
+        T![ref] | T![mut] | IDENT => {
             ident_pat(p, false);
         }
+        _ => {
+            p.err_and_bump("expected identifier");
+        }
     }
 }
 
@@ -405,10 +401,11 @@ fn pat_list(p: &mut Parser, ket: SyntaxKind) {
 //     let ref mut f @ g @ _ = ();
 // }
 fn ident_pat(p: &mut Parser, with_at: bool) -> CompletedMarker {
+    assert!(matches!(p.current(), T![ref] | T![mut] | IDENT));
     let m = p.start();
     p.eat(T![ref]);
     p.eat(T![mut]);
-    name(p);
+    name_r(p, PAT_RECOVERY_SET);
     if with_at && p.eat(T![@]) {
         pattern_single(p);
     }
diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs
index 286affbba9..3ea5844c95 100644
--- a/crates/syntax/src/validation.rs
+++ b/crates/syntax/src/validation.rs
@@ -11,7 +11,7 @@ use rustc_lexer::unescape::{
 
 use crate::{
     algo,
-    ast::{self, HasAttrs, HasVisibility},
+    ast::{self, HasVisibility},
     match_ast, AstNode, SyntaxError,
     SyntaxKind::{CONST, FN, INT_NUMBER, TYPE_ALIAS},
     SyntaxNode, SyntaxToken, TextSize, T,
@@ -231,9 +231,7 @@ fn validate_visibility(vis: ast::Visibility, errors: &mut Vec<SyntaxError>) {
         Some(it) => it,
         None => return,
     };
-    // FIXME: disable validation if there's an attribute, since some proc macros use this syntax.
-    // ideally the validation would run only on the fully expanded code, then this wouldn't be necessary.
-    if impl_def.trait_().is_some() && impl_def.attrs().next().is_none() {
+    if impl_def.trait_().is_some() {
         errors.push(SyntaxError::new("Unnecessary visibility qualifier", vis.syntax.text_range()));
     }
 }


Test patch:
diff --git a/crates/ide_completion/src/tests/expression.rs b/crates/ide_completion/src/tests/expression.rs
index 5984149046..650c68134d 100644
--- a/crates/ide_completion/src/tests/expression.rs
+++ b/crates/ide_completion/src/tests/expression.rs
@@ -153,7 +153,6 @@ impl Unit {
             kw return
             sn pd
             sn ppd
-            sn macro_rules
             kw self
             kw super
             kw crate
@@ -247,11 +246,10 @@ fn complete_in_block() {
             kw return
             sn pd
             sn ppd
-            sn macro_rules
             kw self
             kw super
             kw crate
-            fn foo()       fn()
+            fn foo()     fn()
             bt u32
         "#]],
     )
@@ -295,11 +293,10 @@ fn complete_after_if_expr() {
             kw return
             sn pd
             sn ppd
-            sn macro_rules
             kw self
             kw super
             kw crate
-            fn foo()       fn()
+            fn foo()     fn()
             bt u32
         "#]],
     )
@@ -369,11 +366,10 @@ fn completes_in_loop_ctx() {
             kw return
             sn pd
             sn ppd
-            sn macro_rules
             kw self
             kw super
             kw crate
-            fn my()        fn()
+            fn my()      fn()
             bt u32
         "#]],
     );
diff --git a/crates/ide_completion/src/tests/fn_param.rs b/crates/ide_completion/src/tests/fn_param.rs
index 4578955da1..678c99aa7a 100644
--- a/crates/ide_completion/src/tests/fn_param.rs
+++ b/crates/ide_completion/src/tests/fn_param.rs
@@ -67,17 +67,6 @@ fn bar(file_id: u32, $0) {}
             kw mut
         "#]],
     );
-
-    check(
-        r#"
-fn f(#[foo = "bar"] baz: u32,) {}
-fn g(baz: (), ba$0)
-"#,
-        expect![[r##"
-                kw ref
-                kw mut
-            "##]],
-    )
 }
 
 #[test]