Repository: rust-analyzer/rust-analyzer
Pull request #6964 (2020-12-20 17:39:27)

Add full pattern completions for Struct and Variant patterns

Just gonna call it full pattern completion as pattern completion is already implemented in a sense by showing idents in pattern position. What this does is basically complete struct and variant patterns where applicable(function params, let statements and refutable pattern locations).

This does not replace just completing the corresponding idents of the structs and variants, instead two completions are shown for these, a completion for the ident itself and a completion for the pattern(if the pattern make sense to be used that is). I figured in some cases one would rather type out the pattern manually if it has a lot of fields but you only care about one since this completion would cause one more work in the end since you would have to delete all the extra matched fields again.

These completions are tagged as `CompletionKind::Snippet`, not sure if that is the right one here.
<details>
  <summary>some gifs</summary>

![dx2lxgzhj3](https://user-images.githubusercontent.com/3757771/102719967-6987ef80-42f1-11eb-8ae0-8aff53777860.gif)
![EP2E7sJLkB](https://user-images.githubusercontent.com/3757771/102785777-c7264580-439e-11eb-8a64-f142e19fb65b.gif)
![JMNHHWknr9](https://user-images.githubusercontent.com/3757771/102785796-d1e0da80-439e-11eb-934b-218ada31b51c.gif)
</details>


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index fbb79e01fc..1051c4ec63 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1251,9 +1251,9 @@ dependencies = [
 
 [[package]]
 name = "quote"
-version = "1.0.8"
+version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
+checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
 dependencies = [
  "proc-macro2",
 ]
@@ -1588,9 +1588,9 @@ version = "0.0.0"
 
 [[package]]
 name = "syn"
-version = "1.0.55"
+version = "1.0.54"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a571a711dddd09019ccc628e1b17fe87c59b09d513c06c026877aa708334f37a"
+checksum = "9a2af957a63d6bd42255c359c93d9bfdb97076bd3b820897ce55ffbfbf107f44"
 dependencies = [
  "proc-macro2",
  "quote",
diff --git a/crates/assists/src/assist_context.rs b/crates/assists/src/assist_context.rs
index 80cf9aba11..69499ea326 100644
--- a/crates/assists/src/assist_context.rs
+++ b/crates/assists/src/assist_context.rs
@@ -4,10 +4,10 @@ use std::mem;
 
 use algo::find_covering_element;
 use hir::Semantics;
-use ide_db::base_db::{AnchoredPathBuf, FileId, FileRange};
+use ide_db::base_db::{FileId, FileRange};
 use ide_db::{
     label::Label,
-    source_change::{FileSystemEdit, SourceChange, SourceFileEdit},
+    source_change::{SourceChange, SourceFileEdit},
     RootDatabase,
 };
 use syntax::{
@@ -209,7 +209,6 @@ pub(crate) struct AssistBuilder {
     file_id: FileId,
     is_snippet: bool,
     source_file_edits: Vec<SourceFileEdit>,
-    file_system_edits: Vec<FileSystemEdit>,
 }
 
 impl AssistBuilder {
@@ -219,7 +218,6 @@ impl AssistBuilder {
             file_id,
             is_snippet: false,
             source_file_edits: Vec::default(),
-            file_system_edits: Vec::default(),
         }
     }
 
@@ -284,17 +282,12 @@ impl AssistBuilder {
             algo::diff(&node, &new).into_text_edit(&mut self.edit);
         }
     }
-    pub(crate) fn create_file(&mut self, dst: AnchoredPathBuf, content: impl Into<String>) {
-        let file_system_edit =
-            FileSystemEdit::CreateFile { dst: dst.clone(), initial_contents: content.into() };
-        self.file_system_edits.push(file_system_edit);
-    }
 
     fn finish(mut self) -> SourceChange {
         self.commit();
         SourceChange {
             source_file_edits: mem::take(&mut self.source_file_edits),
-            file_system_edits: mem::take(&mut self.file_system_edits),
+            file_system_edits: Default::default(),
             is_snippet: self.is_snippet,
         }
     }
diff --git a/crates/assists/src/handlers/extract_module_to_file.rs b/crates/assists/src/handlers/extract_module_to_file.rs
deleted file mode 100644
index 5fc190fa67..0000000000
--- a/crates/assists/src/handlers/extract_module_to_file.rs
+++ /dev/null
@@ -1,170 +0,0 @@
-use ast::edit::IndentLevel;
-use ide_db::base_db::{AnchoredPathBuf, SourceDatabaseExt};
-use syntax::{
-    ast::{self, edit::AstNodeEdit, NameOwner},
-    AstNode,
-};
-
-use crate::{AssistContext, AssistId, AssistKind, Assists};
-
-// Assist: extract_module_to_file
-//
-// This assist extract module to file.
-//
-// ```
-// mod foo {<|>
-//     fn t() {}
-// }
-// ```
-// ->
-// ```
-// mod foo;
-// ```
-pub(crate) fn extract_module_to_file(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
-    let assist_id = AssistId("extract_module_to_file", AssistKind::RefactorExtract);
-    let assist_label = "Extract module to file";
-    let db = ctx.db();
-    let module_ast = ctx.find_node_at_offset::<ast::Module>()?;
-    let module_items = module_ast.item_list()?;
-    let dedent_module_items_text = module_items.dedent(IndentLevel(1)).to_string();
-    let module_name = module_ast.name()?;
-    let target = module_ast.syntax().text_range();
-    let anchor_file_id = ctx.frange.file_id;
-    let sr = db.file_source_root(anchor_file_id);
-    let sr = db.source_root(sr);
-    let file_path = sr.path_for_file(&anchor_file_id)?;
-    let (file_name, file_ext) = file_path.name_and_extension()?;
-    acc.add(assist_id, assist_label, target, |builder| {
-        builder.replace(target, format!("mod {};", module_name));
-        let path = if is_main_or_lib(file_name) {
-            format!("./{}.{}", module_name, file_ext.unwrap())
-        } else {
-            format!("./{}/{}.{}", file_name, module_name, file_ext.unwrap())
-        };
-        let dst = AnchoredPathBuf { anchor: anchor_file_id, path };
-        let contents = update_module_items_string(dedent_module_items_text);
-        builder.create_file(dst, contents);
-    })
-}
-fn is_main_or_lib(file_name: &str) -> bool {
-    file_name == "main".to_string() || file_name == "lib".to_string()
-}
-fn update_module_items_string(items_str: String) -> String {
-    let mut items_string_lines: Vec<&str> = items_str.lines().collect();
-    items_string_lines.pop(); // Delete last line
-    items_string_lines.reverse();
-    items_string_lines.pop(); // Delete first line
-    items_string_lines.reverse();
-
-    let string = items_string_lines.join("\n");
-    format!("{}", string)
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::tests::check_assist;
-
-    use super::*;
-
-    #[test]
-    fn extract_module_to_file_with_basic_module() {
-        check_assist(
-            extract_module_to_file,
-            r#"
-//- /foo.rs crate:foo
-mod tests {<|>
-    #[test] fn t() {}
-}
-"#,
-            r#"
-//- /foo.rs
-mod tests;
-//- /foo/tests.rs
-#[test] fn t() {}"#,
-        )
-    }
-
-    #[test]
-    fn extract_module_to_file_with_file_path() {
-        check_assist(
-            extract_module_to_file,
-            r#"
-//- /src/foo.rs crate:foo
-mod bar {<|>
-    fn f() {
-
-    }
-}
-fn main() {
-    println!("Hello, world!");
-}
-"#,
-            r#"
-//- /src/foo.rs
-mod bar;
-fn main() {
-    println!("Hello, world!");
-}
-//- /src/foo/bar.rs
-fn f() {
-
-}"#,
-        )
-    }
-
-    #[test]
-    fn extract_module_to_file_with_main_filw() {
-        check_assist(
-            extract_module_to_file,
-            r#"
-//- /main.rs
-mod foo {<|>
-    fn f() {
-
-    }
-}
-fn main() {
-    println!("Hello, world!");
-}
-"#,
-            r#"
-//- /main.rs
-mod foo;
-fn main() {
-    println!("Hello, world!");
-}
-//- /foo.rs
-fn f() {
-
-}"#,
-        )
-    }
-
-    #[test]
-    fn extract_module_to_file_with_lib_file() {
-        check_assist(
-            extract_module_to_file,
-            r#"
-//- /lib.rs
-mod foo {<|>
-    fn f() {
-
-    }
-}
-fn main() {
-    println!("Hello, world!");
-}
-"#,
-            r#"
-//- /lib.rs
-mod foo;
-fn main() {
-    println!("Hello, world!");
-}
-//- /foo.rs
-fn f() {
-
-}"#,
-        )
-    }
-}
diff --git a/crates/assists/src/handlers/invert_if.rs b/crates/assists/src/handlers/invert_if.rs
index f9c33b3f7d..91e2f5c8cb 100644
--- a/crates/assists/src/handlers/invert_if.rs
+++ b/crates/assists/src/handlers/invert_if.rs
@@ -77,15 +77,6 @@ mod tests {
         )
     }
 
-    #[test]
-    fn invert_if_remove_not_parentheses() {
-        check_assist(
-            invert_if,
-            "fn f() { i<|>f !(x == 3 || x == 4 || x == 5) { 3 * 2 } else { 1 } }",
-            "fn f() { if x == 3 || x == 4 || x == 5 { 1 } else { 3 * 2 } }",
-        )
-    }
-
     #[test]
     fn invert_if_remove_inequality() {
         check_assist(
diff --git a/crates/assists/src/handlers/remove_unused_param.rs b/crates/assists/src/handlers/remove_unused_param.rs
index f72dd49edc..1ff5e92b04 100644
--- a/crates/assists/src/handlers/remove_unused_param.rs
+++ b/crates/assists/src/handlers/remove_unused_param.rs
@@ -2,10 +2,9 @@ use ide_db::{defs::Definition, search::Reference};
 use syntax::{
     algo::find_node_at_range,
     ast::{self, ArgListOwner},
-    AstNode, SyntaxKind, SyntaxNode, TextRange, T,
+    AstNode, SyntaxNode, TextRange, T,
 };
 use test_utils::mark;
-use SyntaxKind::WHITESPACE;
 
 use crate::{
     assist_context::AssistBuilder, utils::next_prev, AssistContext, AssistId, AssistKind, Assists,
@@ -57,7 +56,7 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext) -> Opt
         "Remove unused parameter",
         param.syntax().text_range(),
         |builder| {
-            builder.delete(range_to_remove(param.syntax()));
+            builder.delete(range_with_coma(param.syntax()));
             for usage in fn_def.usages(&ctx.sema).all() {
                 process_usage(ctx, builder, usage, param_position);
             }
@@ -81,34 +80,19 @@ fn process_usage(
     let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?;
 
     builder.edit_file(usage.file_range.file_id);
-    builder.delete(range_to_remove(arg.syntax()));
+    builder.delete(range_with_coma(arg.syntax()));
 
     Some(())
 }
 
-fn range_to_remove(node: &SyntaxNode) -> TextRange {
-    let up_to_comma = next_prev().find_map(|dir| {
+fn range_with_coma(node: &SyntaxNode) -> TextRange {
+    let up_to = next_prev().find_map(|dir| {
         node.siblings_with_tokens(dir)
             .filter_map(|it| it.into_token())
             .find(|it| it.kind() == T![,])
-            .map(|it| (dir, it))
     });
-    if let Some((dir, token)) = up_to_comma {
-        if node.next_sibling().is_some() {
-            let up_to_space = token
-                .siblings_with_tokens(dir)
-                .skip(1)
-                .take_while(|it| it.kind() == WHITESPACE)
-                .last()
-                .and_then(|it| it.into_token());
-            return node
-                .text_range()
-                .cover(up_to_space.map_or(token.text_range(), |it| it.text_range()));
-        }
-        node.text_range().cover(token.text_range())
-    } else {
-        node.text_range()
-    }
+    let up_to = up_to.map_or(node.text_range(), |it| it.text_range());
+    node.text_range().cover(up_to)
 }
 
 #[cfg(test)]
@@ -134,57 +118,6 @@ fn b() { foo(9, ) }
         );
     }
 
-    #[test]
-    fn remove_unused_first_param() {
-        check_assist(
-            remove_unused_param,
-            r#"
-fn foo(<|>x: i32, y: i32) { y; }
-fn a() { foo(1, 2) }
-fn b() { foo(1, 2,) }
-"#,
-            r#"
-fn foo(y: i32) { y; }
-fn a() { foo(2) }
-fn b() { foo(2,) }
-"#,
-        );
-    }
-
-    #[test]
-    fn remove_unused_single_param() {
-        check_assist(
-            remove_unused_param,
-            r#"
-fn foo(<|>x: i32) { 0; }
-fn a() { foo(1) }
-fn b() { foo(1, ) }
-"#,
-            r#"
-fn foo() { 0; }
-fn a() { foo() }
-fn b() { foo( ) }
-"#,
-        );
-    }
-
-    #[test]
-    fn remove_unused_surrounded_by_parms() {
-        check_assist(
-            remove_unused_param,
-            r#"
-fn foo(x: i32, <|>y: i32, z: i32) { x; }
-fn a() { foo(1, 2, 3) }
-fn b() { foo(1, 2, 3,) }
-"#,
-            r#"
-fn foo(x: i32, z: i32) { x; }
-fn a() { foo(1, 3) }
-fn b() { foo(1, 3,) }
-"#,
-        );
-    }
-
     #[test]
     fn remove_unused_qualified_call() {
         check_assist(
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index 6b89b2d044..6e736ccb38 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -129,7 +129,6 @@ mod handlers {
     mod convert_integer_literal;
     mod early_return;
     mod expand_glob_import;
-    mod extract_module_to_file;
     mod extract_struct_from_enum_variant;
     mod extract_variable;
     mod fill_match_arms;
@@ -180,7 +179,6 @@ mod handlers {
             convert_integer_literal::convert_integer_literal,
             early_return::convert_to_guarded_return,
             expand_glob_import::expand_glob_import,
-            extract_module_to_file::extract_module_to_file,
             extract_struct_from_enum_variant::extract_struct_from_enum_variant,
             extract_variable::extract_variable,
             fill_match_arms::fill_match_arms,
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index d41084b599..f2cacf7c80 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -232,13 +232,7 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
             };
             Some(make::expr_method_call(receiver, method, arg_list))
         }
-        ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::PrefixOp::Not => {
-            if let ast::Expr::ParenExpr(parexpr) = pe.expr()? {
-                parexpr.expr()
-            } else {
-                pe.expr()
-            }
-        }
+        ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::PrefixOp::Not => pe.expr(),
         // FIXME:
         // ast::Expr::Literal(true | false )
         _ => None,
diff --git a/crates/completion/src/completions.rs b/crates/completion/src/completions.rs
index 1ef6b5f48f..d9fe134855 100644
--- a/crates/completion/src/completions.rs
+++ b/crates/completion/src/completions.rs
@@ -19,9 +19,14 @@ use hir::{ModPath, ScopeDef, Type};
 use crate::{
     item::Builder,
     render::{
-        const_::render_const, enum_variant::render_variant, function::render_fn,
-        macro_::render_macro, render_field, render_resolution, render_tuple_field,
-        type_alias::render_type_alias, RenderContext,
+        const_::render_const,
+        enum_variant::render_variant,
+        function::render_fn,
+        macro_::render_macro,
+        pattern::{render_struct_pat, render_variant_pat},
+        render_field, render_resolution, render_tuple_field,
+        type_alias::render_type_alias,
+        RenderContext,
     },
     CompletionContext, CompletionItem,
 };
@@ -105,6 +110,28 @@ impl Completions {
         self.add(item)
     }
 
+    pub(crate) fn add_variant_pat(
+        &mut self,
+        ctx: &CompletionContext,
+        variant: hir::Variant,
+        local_name: Option<hir::Name>,
+    ) {
+        if let Some(item) = render_variant_pat(RenderContext::new(ctx), variant, local_name) {
+            self.add(item);
+        }
+    }
+
+    pub(crate) fn add_struct_pat(
+        &mut self,
+        ctx: &CompletionContext,
+        strukt: hir::Struct,
+        local_name: Option<hir::Name>,
+    ) {
+        if let Some(item) = render_struct_pat(RenderContext::new(ctx), strukt, local_name) {
+            self.add(item);
+        }
+    }
+
     pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) {
         if let Some(item) = render_const(RenderContext::new(ctx), constant) {
             self.add(item);
diff --git a/crates/completion/src/completions/pattern.rs b/crates/completion/src/completions/pattern.rs
index 4d56731ec6..eee31098d1 100644
--- a/crates/completion/src/completions/pattern.rs
+++ b/crates/completion/src/completions/pattern.rs
@@ -2,9 +2,9 @@
 
 use crate::{CompletionContext, Completions};
 
-/// Completes constats and paths in patterns.
+/// Completes constants and paths in patterns.
 pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
-    if !(ctx.is_pat_binding_or_const || ctx.is_irrefutable_let_pat_binding) {
+    if !(ctx.is_pat_binding_or_const || ctx.is_irrefutable_pat_binding) {
         return;
     }
     if ctx.record_pat_syntax.is_some() {
@@ -15,20 +15,21 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
     // suggest variants + auto-imports
     ctx.scope.process_all_names(&mut |name, res| {
         let add_resolution = match &res {
-            hir::ScopeDef::ModuleDef(def) => {
-                if ctx.is_irrefutable_let_pat_binding {
-                    matches!(def, hir::ModuleDef::Adt(hir::Adt::Struct(_)))
-                } else {
-                    matches!(
-                        def,
-                        hir::ModuleDef::Adt(hir::Adt::Enum(..))
-                            | hir::ModuleDef::Adt(hir::Adt::Struct(..))
-                            | hir::ModuleDef::Variant(..)
-                            | hir::ModuleDef::Const(..)
-                            | hir::ModuleDef::Module(..)
-                    )
+            hir::ScopeDef::ModuleDef(def) => match def {
+                hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => {
+                    acc.add_struct_pat(ctx, strukt.clone(), Some(name.clone()));
+                    true
                 }
-            }
+                hir::ModuleDef::Variant(variant) if !ctx.is_irrefutable_pat_binding => {
+                    acc.add_variant_pat(ctx, variant.clone(), Some(name.clone()));
+                    true
+                }
+                hir::ModuleDef::Adt(hir::Adt::Enum(..))
+                | hir::ModuleDef::Variant(..)
+                | hir::ModuleDef::Const(..)
+                | hir::ModuleDef::Module(..) => !ctx.is_irrefutable_pat_binding,
+                _ => false,
+            },
             hir::ScopeDef::MacroDef(_) => true,
             _ => false,
         };
@@ -42,13 +43,21 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
 mod tests {
     use expect_test::{expect, Expect};
 
-    use crate::{test_utils::completion_list, CompletionKind};
+    use crate::{
+        test_utils::{check_edit, completion_list},
+        CompletionKind,
+    };
 
     fn check(ra_fixture: &str, expect: Expect) {
         let actual = completion_list(ra_fixture, CompletionKind::Reference);
         expect.assert_eq(&actual)
     }
 
+    fn check_snippet(ra_fixture: &str, expect: Expect) {
+        let actual = completion_list(ra_fixture, CompletionKind::Snippet);
+        expect.assert_eq(&actual)
+    }
+
     #[test]
     fn completes_enum_variants_and_modules() {
         check(
@@ -69,7 +78,7 @@ fn foo() {
                 en E
                 ct Z
                 st Bar
-                ev X   ()
+                ev X
                 md m
             "#]],
         );
@@ -114,4 +123,139 @@ fn foo() {
             "#]],
         );
     }
+
+    #[test]
+    fn completes_in_param() {
+        check(
+            r#"
+enum E { X }
+
+static FOO: E = E::X;
+struct Bar { f: u32 }
+
+fn foo(<|>) {
+}
+"#,
+            expect![[r#"
+                st Bar
+            "#]],
+        );
+    }
+
+    #[test]
+    fn completes_pat_in_let() {
+        check_snippet(
+            r#"
+struct Bar { f: u32 }
+
+fn foo() {
+   let <|>
+}
+"#,
+            expect![[r#"
+                bn Bar Bar { f$1 }$0
+            "#]],
+        );
+    }
+
+    #[test]
+    fn completes_param_pattern() {
+        check_snippet(
+            r#"
+struct Foo { bar: String, baz: String }
+struct Bar(String, String);
+struct Baz;
+fn outer(<|>) {}
+"#,
+            expect![[r#"
+                bn Foo Foo { bar$1, baz$2 }: Foo$0
+                bn Bar Bar($1, $2): Bar$0
+            "#]],
+        )
+    }
+
+    #[test]
+    fn completes_let_pattern() {
+        check_snippet(
+            r#"
+struct Foo { bar: String, baz: String }
+struct Bar(String, String);
+struct Baz;
+fn outer() {
+    let <|>
+}
+"#,
+            expect![[r#"
+                bn Foo Foo { bar$1, baz$2 }$0
+                bn Bar Bar($1, $2)$0
+            "#]],
+        )
+    }
+
+    #[test]
+    fn completes_refutable_pattern() {
+        check_snippet(
+            r#"
+struct Foo { bar: i32, baz: i32 }
+struct Bar(String, String);
+struct Baz;
+fn outer() {
+    match () {
+        <|>
+    }
+}
+"#,
+            expect![[r#"
+                bn Foo Foo { bar$1, baz$2 }$0
+                bn Bar Bar($1, $2)$0
+            "#]],
+        )
+    }
+
+    #[test]
+    fn omits_private_fields_pat() {
+        check_snippet(
+            r#"
+mod foo {
+    pub struct Foo { pub bar: i32, baz: i32 }
+    pub struct Bar(pub String, String);
+    pub struct Invisible(String, String);
+}
+use foo::*;
+
+fn outer() {
+    match () {
+        <|>
+    }
+}
+"#,
+            expect![[r#"
+                bn Foo Foo { bar$1, .. }$0
+                bn Bar Bar($1, ..)$0
+            "#]],
+        )
+    }
+
+    #[test]
+    fn only_shows_ident_completion() {
+        check_edit(
+            "Foo",
+            r#"
+struct Foo(i32);
+fn main() {
+    match Foo(92) {
+        <|>(92) => (),
+    }
+}
+"#,
+            r#"
+struct Foo(i32);
+fn main() {
+    match Foo(92) {
+        Foo(92) => (),
+    }
+}
+"#,
+        );
+    }
 }
diff --git a/crates/completion/src/context.rs b/crates/completion/src/context.rs
index 5cd11cf775..41de324d8d 100644
--- a/crates/completion/src/context.rs
+++ b/crates/completion/src/context.rs
@@ -51,7 +51,7 @@ pub(crate) struct CompletionContext<'a> {
     /// If a name-binding or reference to a const in a pattern.
     /// Irrefutable patterns (like let) are excluded.
     pub(super) is_pat_binding_or_const: bool,
-    pub(super) is_irrefutable_let_pat_binding: bool,
+    pub(super) is_irrefutable_pat_binding: bool,
     /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
     pub(super) is_trivial_path: bool,
     /// If not a trivial path, the prefix (qualifier).
@@ -147,7 +147,7 @@ impl<'a> CompletionContext<'a> {
             active_parameter: ActiveParameter::at(db, position),
             is_param: false,
             is_pat_binding_or_const: false,
-            is_irrefutable_let_pat_binding: false,
+            is_irrefutable_pat_binding: false,
             is_trivial_path: false,
             path_qual: None,
             after_if: false,
@@ -327,14 +327,19 @@ impl<'a> CompletionContext<'a> {
                 if bind_pat.syntax().parent().and_then(ast::RecordPatFieldList::cast).is_some() {
                     self.is_pat_binding_or_const = false;
                 }
-                if let Some(let_stmt) = bind_pat.syntax().ancestors().find_map(ast::LetStmt::cast) {
-                    if let Some(pat) = let_stmt.pat() {
-                        if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range())
-                        {
-                            self.is_pat_binding_or_const = false;
-                            self.is_irrefutable_let_pat_binding = true;
+                if let Some(Some(pat)) = bind_pat.syntax().ancestors().find_map(|node| {
+                    match_ast! {
+                        match node {
+                            ast::LetStmt(it) => Some(it.pat()),
+                            ast::Param(it) => Some(it.pat()),
+                            _ => None,
                         }
                     }
+                }) {
+                    if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) {
+                        self.is_pat_binding_or_const = false;
+                        self.is_irrefutable_pat_binding = true;
+                    }
                 }
             }
             if is_node::<ast::Param>(name.syntax()) {
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs
index 1092a48256..1ba7201a13 100644
--- a/crates/completion/src/render.rs
+++ b/crates/completion/src/render.rs
@@ -5,6 +5,7 @@ pub(crate) mod macro_;
 pub(crate) mod function;
 pub(crate) mod enum_variant;
 pub(crate) mod const_;
+pub(crate) mod pattern;
 pub(crate) mod type_alias;
 
 mod builder_ext;
@@ -159,6 +160,12 @@ impl<'a> Render<'a> {
                 let item = render_fn(self.ctx, import_to_add, Some(local_name), *func);
                 return Some(item);
             }
+            ScopeDef::ModuleDef(Variant(_))
+                if self.ctx.completion.is_pat_binding_or_const
+                    | self.ctx.completion.is_irrefutable_pat_binding =>
+            {
+                CompletionItemKind::EnumVariant
+            }
             ScopeDef::ModuleDef(Variant(var)) => {
                 let item = render_variant(self.ctx, import_to_add, Some(local_name), *var, None);
                 return Some(item);
diff --git a/crates/completion/src/render/builder_ext.rs b/crates/completion/src/render/builder_ext.rs
index ce8718bd54..d053a988bc 100644
--- a/crates/completion/src/render/builder_ext.rs
+++ b/crates/completion/src/render/builder_ext.rs
@@ -34,7 +34,6 @@ impl Builder {
             return false;
         }
         if ctx.is_pattern_call {
-            mark::hit!(dont_duplicate_pattern_parens);
             return false;
         }
         if ctx.is_call {
diff --git a/crates/completion/src/render/enum_variant.rs b/crates/completion/src/render/enum_variant.rs
index 7176fd9b3d..732e139eca 100644
--- a/crates/completion/src/render/enum_variant.rs
+++ b/crates/completion/src/render/enum_variant.rs
@@ -124,51 +124,6 @@ use Option::*;
 fn main() -> Option<i32> {
     Some($0)
 }
-"#,
-        );
-        check_edit(
-            "Some",
-            r#"
-enum Option<T> { Some(T), None }
-use Option::*;
-fn main(value: Option<i32>) {
-    match value {
-        Som<|>
-    }
-}
-"#,
-            r#"
-enum Option<T> { Some(T), None }
-use Option::*;
-fn main(value: Option<i32>) {
-    match value {
-        Some($0)
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn dont_duplicate_pattern_parens() {
-        mark::check!(dont_duplicate_pattern_parens);
-        check_edit(
-            "Var",
-            r#"
-enum E { Var(i32) }
-fn main() {
-    match E::Var(92) {
-        E::<|>(92) => (),
-    }
-}
-"#,
-            r#"
-enum E { Var(i32) }
-fn main() {
-    match E::Var(92) {
-        E::Var(92) => (),
-    }
-}
 "#,
         );
     }
diff --git a/crates/completion/src/render/pattern.rs b/crates/completion/src/render/pattern.rs
new file mode 100644
index 0000000000..a3b6a3cac7
--- /dev/null
+++ b/crates/completion/src/render/pattern.rs
@@ -0,0 +1,148 @@
+//! Renderer for patterns.
+
+use hir::{db::HirDatabase, HasAttrs, HasVisibility, Name, StructKind};
+use itertools::Itertools;
+
+use crate::{
+    config::SnippetCap, item::CompletionKind, render::RenderContext, CompletionItem,
+    CompletionItemKind,
+};
+
+fn visible_fields(
+    ctx: &RenderContext<'_>,
+    fields: &[hir::Field],
+    item: impl HasAttrs,
+) -> Option<(Vec<hir::Field>, bool)> {
+    let module = ctx.completion.scope.module()?;
+    let n_fields = fields.len();
+    let fields = fields
+        .into_iter()
+        .filter(|field| field.is_visible_from(ctx.db(), module))
+        .copied()
+        .collect::<Vec<_>>();
+
+    let fields_omitted =
+        n_fields - fields.len() > 0 || item.attrs(ctx.db()).by_key("non_exhaustive").exists();
+    Some((fields, fields_omitted))
+}
+
+pub(crate) fn render_struct_pat(
+    ctx: RenderContext<'_>,
+    strukt: hir::Struct,
+    local_name: Option<Name>,
+) -> Option<CompletionItem> {
+    let _p = profile::span("render_struct_pat");
+
+    let fields = strukt.fields(ctx.db());
+    let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, strukt)?;
+
+    if visible_fields.is_empty() {
+        // Matching a struct without matching its fields is pointless, unlike matching a Variant without its fields
+        return None;
+    }
+
+    let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())).to_string();
+    let pat = render_pat(&ctx, &name, strukt.kind(ctx.db()), &visible_fields, fields_omitted)?;
+
+    Some(build_completion(ctx, name, pat, strukt))
+}
+
+pub(crate) fn render_variant_pat(
+    ctx: RenderContext<'_>,
+    variant: hir::Variant,
+    local_name: Option<Name>,
+) -> Option<CompletionItem> {
+    let _p = profile::span("render_variant_pat");
+
+    let fields = variant.fields(ctx.db());
+    let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, variant)?;
+
+    let name = local_name.unwrap_or_else(|| variant.name(ctx.db())).to_string();
+    let pat = render_pat(&ctx, &name, variant.kind(ctx.db()), &visible_fields, fields_omitted)?;
+
+    Some(build_completion(ctx, name, pat, variant))
+}
+
+fn build_completion(
+    ctx: RenderContext<'_>,
+    name: String,
+    pat: String,
+    item: impl HasAttrs + Copy,
+) -> CompletionItem {
+    let completion = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name)
+        .kind(CompletionItemKind::Binding)
+        .set_documentation(ctx.docs(item))
+        .set_deprecated(ctx.is_deprecated(item))
+        .detail(&pat);
+    let completion = if let Some(snippet_cap) = ctx.snippet_cap() {
+        completion.insert_snippet(snippet_cap, pat)
+    } else {
+        completion.insert_text(pat)
+    };
+    completion.build()
+}
+
+fn render_pat(
+    ctx: &RenderContext<'_>,
+    name: &str,
+    kind: StructKind,
+    fields: &[hir::Field],
+    fields_omitted: bool,
+) -> Option<String> {
+    let mut pat = match kind {
+        StructKind::Tuple if ctx.snippet_cap().is_some() => {
+            render_tuple_as_pat(&fields, &name, fields_omitted)
+        }
+        StructKind::Record => {
+            render_record_as_pat(ctx.db(), ctx.snippet_cap(), &fields, &name, fields_omitted)
+        }
+        _ => return None,
+    };
+
+    if ctx.completion.is_param {
+        pat.push(':');
+        pat.push(' ');
+        pat.push_str(&name);
+    }
+    if ctx.snippet_cap().is_some() {
+        pat.push_str("$0");
+    }
+    Some(pat)
+}
+
+fn render_record_as_pat(
+    db: &dyn HirDatabase,
+    snippet_cap: Option<SnippetCap>,
+    fields: &[hir::Field],
+    name: &str,
+    fields_omitted: bool,
+) -> String {
+    let fields = fields.iter();
+    if snippet_cap.is_some() {
+        format!(
+            "{name} {{ {}{} }}",
+            fields
+                .enumerate()
+                .map(|(idx, field)| format!("{}${}", field.name(db), idx + 1))
+                .format(", "),
+            if fields_omitted { ", .." } else { "" },
+            name = name
+        )
+    } else {
+        format!(
+            "{name} {{ {}{} }}",
+            fields.map(|field| field.name(db)).format(", "),
+            if fields_omitted { ", .." } else { "" },
+            name = name
+        )
+    }
+}
+
+fn render_tuple_as_pat(fields: &[hir::Field], name: &str, fields_omitted: bool) -> String {
+    format!(
+        "{name}({}{})",
+        fields.iter().enumerate().map(|(idx, _)| format!("${}", idx + 1)).format(", "),
+        if fields_omitted { ", .." } else { "" },
+        name = name
+    )
+}
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index 73ca6ba9f4..dbc937e4f6 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -511,6 +511,10 @@ impl Struct {
         db.struct_data(self.id).repr.clone()
     }
 
+    pub fn kind(self, db: &dyn HirDatabase) -> StructKind {
+        self.variant_data(db).kind()
+    }
+
     fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
         db.struct_data(self.id).variant_data.clone()
     }
@@ -1268,6 +1272,7 @@ impl LifetimeParam {
     }
 }
 
+// FIXME: rename from `ImplDef` to `Impl`
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct Impl {
     pub(crate) id: ImplId,
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs
index bb8fca009a..41134d23b3 100644
--- a/crates/hir_def/src/generics.rs
+++ b/crates/hir_def/src/generics.rs
@@ -62,7 +62,6 @@ pub struct GenericParams {
 pub enum WherePredicate {
     TypeBound { target: WherePredicateTypeTarget, bound: TypeBound },
     Lifetime { target: LifetimeRef, bound: LifetimeRef },
-    ForLifetime { lifetimes: Box<[Name]>, target: WherePredicateTypeTarget, bound: TypeBound },
 }
 
 #[derive(Clone, PartialEq, Eq, Debug)]
@@ -70,6 +69,7 @@ pub enum WherePredicateTypeTarget {
     TypeRef(TypeRef),
     /// For desugared where predicates that can directly refer to a type param.
     TypeParam(LocalTypeParamId),
+    // FIXME: ForLifetime(Vec<LifetimeParamId>, TypeRef)
 }
 
 #[derive(Default)]
@@ -234,7 +234,7 @@ impl GenericParams {
         for bound in
             node.type_bound_list().iter().flat_map(|type_bound_list| type_bound_list.bounds())
         {
-            self.add_where_predicate_from_bound(lower_ctx, bound, None, target.clone());
+            self.add_where_predicate_from_bound(lower_ctx, bound, target.clone());
         }
     }
 
@@ -279,25 +279,8 @@ impl GenericParams {
             } else {
                 continue;
             };
-
-            let lifetimes: Option<Box<_>> = pred.generic_param_list().map(|param_list| {
-                // Higher-Ranked Trait Bounds
-                param_list
-                    .lifetime_params()
-                    .map(|lifetime_param| {
-                        lifetime_param
-                            .lifetime()
-                            .map_or_else(Name::missing, |lt| Name::new_lifetime(&lt))
-                    })
-                    .collect()
-            });
             for bound in pred.type_bound_list().iter().flat_map(|l| l.bounds()) {
-                self.add_where_predicate_from_bound(
-                    lower_ctx,
-                    bound,
-                    lifetimes.as_ref(),
-                    target.clone(),
-                );
+                self.add_where_predicate_from_bound(lower_ctx, bound, target.clone());
             }
         }
     }
@@ -306,7 +289,6 @@ impl GenericParams {
         &mut self,
         lower_ctx: &LowerCtx,
         bound: ast::TypeBound,
-        hrtb_lifetimes: Option<&Box<[Name]>>,
         target: Either<TypeRef, LifetimeRef>,
     ) {
         if bound.question_mark_token().is_some() {
@@ -315,16 +297,9 @@ impl GenericParams {
         }
         let bound = TypeBound::from_ast(lower_ctx, bound);
         let predicate = match (target, bound) {
-            (Either::Left(type_ref), bound) => match hrtb_lifetimes {
-                Some(hrtb_lifetimes) => WherePredicate::ForLifetime {
-                    lifetimes: hrtb_lifetimes.clone(),
-                    target: WherePredicateTypeTarget::TypeRef(type_ref),
-                    bound,
-                },
-                None => WherePredicate::TypeBound {
-                    target: WherePredicateTypeTarget::TypeRef(type_ref),
-                    bound,
-                },
+            (Either::Left(type_ref), bound) => WherePredicate::TypeBound {
+                target: WherePredicateTypeTarget::TypeRef(type_ref),
+                bound,
             },
             (Either::Right(lifetime), TypeBound::Lifetime(bound)) => {
                 WherePredicate::Lifetime { target: lifetime, bound }
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index 8da56cd11c..8392cb7706 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -675,8 +675,7 @@ impl GenericPredicate {
         where_predicate: &'a WherePredicate,
     ) -> impl Iterator<Item = GenericPredicate> + 'a {
         match where_predicate {
-            WherePredicate::ForLifetime { target, bound, .. }
-            | WherePredicate::TypeBound { target, bound } => {
+            WherePredicate::TypeBound { target, bound } => {
                 let self_ty = match target {
                     WherePredicateTypeTarget::TypeRef(type_ref) => Ty::from_hir(ctx, type_ref),
                     WherePredicateTypeTarget::TypeParam(param_id) => {
@@ -889,13 +888,14 @@ pub(crate) fn generic_predicates_for_param_query(
         .where_predicates_in_scope()
         // we have to filter out all other predicates *first*, before attempting to lower them
         .filter(|pred| match pred {
-            WherePredicate::ForLifetime { target, .. }
-            | WherePredicate::TypeBound { target, .. } => match target {
-                WherePredicateTypeTarget::TypeRef(type_ref) => {
-                    Ty::from_hir_only_param(&ctx, type_ref) == Some(param_id)
-                }
-                WherePredicateTypeTarget::TypeParam(local_id) => *local_id == param_id.local_id,
-            },
+            WherePredicate::TypeBound {
+                target: WherePredicateTypeTarget::TypeRef(type_ref),
+                ..
+            } => Ty::from_hir_only_param(&ctx, type_ref) == Some(param_id),
+            WherePredicate::TypeBound {
+                target: WherePredicateTypeTarget::TypeParam(local_id),
+                ..
+            } => *local_id == param_id.local_id,
             WherePredicate::Lifetime { .. } => false,
         })
         .flat_map(|pred| {
diff --git a/crates/hir_ty/src/utils.rs b/crates/hir_ty/src/utils.rs
index 65b79df0d5..af880c0658 100644
--- a/crates/hir_ty/src/utils.rs
+++ b/crates/hir_ty/src/utils.rs
@@ -5,9 +5,7 @@ use std::sync::Arc;
 use hir_def::{
     adt::VariantData,
     db::DefDatabase,
-    generics::{
-        GenericParams, TypeParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
-    },
+    generics::{GenericParams, TypeParamData, TypeParamProvenance, WherePredicateTypeTarget},
     path::Path,
     resolver::{HasResolver, TypeNs},
     type_ref::TypeRef,
@@ -29,8 +27,7 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> {
         .where_predicates
         .iter()
         .filter_map(|pred| match pred {
-            WherePredicate::ForLifetime { target, bound, .. }
-            | WherePredicate::TypeBound { target, bound } => match target {
+            hir_def::generics::WherePredicate::TypeBound { target, bound } => match target {
                 WherePredicateTypeTarget::TypeRef(TypeRef::Path(p))
                     if p == &Path::from(name![Self]) =>
                 {
@@ -41,7 +38,7 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> {
                 }
                 _ => None,
             },
-            WherePredicate::Lifetime { .. } => None,
+            hir_def::generics::WherePredicate::Lifetime { .. } => None,
         })
         .filter_map(|path| match resolver.resolve_path_in_type_ns_fully(db, path.mod_path()) {
             Some(TypeNs::TraitId(t)) => Some(t),
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 3ad30f0c9e..049f808dc6 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -619,7 +619,6 @@ fn test_fn() {
                                                 ),
                                                 path: "foo.rs",
                                             },
-                                            initial_contents: "",
                                         },
                                     ],
                                     is_snippet: false,
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs
index d79f5c1700..e8b8966238 100644
--- a/crates/ide/src/diagnostics/fixes.rs
+++ b/crates/ide/src/diagnostics/fixes.rs
@@ -40,7 +40,6 @@ impl DiagnosticWithFix for UnresolvedModule {
                     anchor: self.file.original_file(sema.db),
                     path: self.candidate.clone(),
                 },
-                initial_contents: "".to_string(),
             }
             .into(),
             unresolved_module.syntax().text_range(),
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 431da5d9c7..7a12e99652 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1077,32 +1077,4 @@ fn foo<'foobar>(_: &'foobar ()) {
 }"#,
         )
     }
-
-    #[test]
-    #[ignore] // requires the HIR to somehow track these hrtb lifetimes
-    fn goto_lifetime_hrtb() {
-        check(
-            r#"trait Foo<T> {}
-fn foo<T>() where for<'a> T: Foo<&'a<|> (u8, u16)>, {}
-                    //^^
-"#,
-        );
-        check(
-            r#"trait Foo<T> {}
-fn foo<T>() where for<'a<|>> T: Foo<&'a (u8, u16)>, {}
-                    //^^
-"#,
-        );
-    }
-
-    #[test]
-    #[ignore] // requires ForTypes to be implemented
-    fn goto_lifetime_hrtb_for_type() {
-        check(
-            r#"trait Foo<T> {}
-fn foo<T>() where T: for<'a> Foo<&'a<|> (u8, u16)>, {}
-                       //^^
-"#,
-        );
-    }
 }
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 33b7358f72..18ea193052 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -147,20 +147,20 @@ fn find_name(
 ) -> Option<RangeInfo<Definition>> {
     if let Some(name) = opt_name {
         let def = NameClass::classify(sema, &name)?.referenced_or_defined(sema.db);
-        let FileRange { range, .. } = sema.original_range(name.syntax());
+        let range = name.syntax().text_range();
         return Some(RangeInfo::new(range, def));
     }
 
-    let (FileRange { range, .. }, def) = if let Some(lifetime) =
+    let (text_range, def) = if let Some(lifetime) =
         sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset)
     {
         if let Some(def) = NameRefClass::classify_lifetime(sema, &lifetime)
             .map(|class| NameRefClass::referenced(class, sema.db))
         {
-            (sema.original_range(lifetime.syntax()), def)
+            (lifetime.syntax().text_range(), def)
         } else {
             (
-                sema.original_range(lifetime.syntax()),
+                lifetime.syntax().text_range(),
                 NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db),
             )
         }
@@ -168,11 +168,11 @@ fn find_name(
         let name_ref =
             sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)?;
         (
-            sema.original_range(name_ref.syntax()),
+            name_ref.syntax().text_range(),
             NameRefClass::classify(sema, &name_ref)?.referenced(sema.db),
         )
     };
-    Some(RangeInfo::new(range, def))
+    Some(RangeInfo::new(text_range, def))
 }
 
 fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> {
@@ -1086,40 +1086,4 @@ impl<'a> Foo<'a> for &'a () {
             "#]],
         );
     }
-
-    #[test]
-    fn test_map_range_to_original() {
-        check(
-            r#"
-macro_rules! foo {($i:ident) => {$i} }
-fn main() {
-    let a<|> = "test";
-    foo!(a);
-}
-"#,
-            expect![[r#"
-                a Local FileId(0) 59..60 Other
-
-                FileId(0) 80..81 Other Read
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_map_range_to_original_ref() {
-        check(
-            r#"
-macro_rules! foo {($i:ident) => {$i} }
-fn main() {
-    let a = "test";
-    foo!(a<|>);
-}
-"#,
-            expect![[r#"
-                a Local FileId(0) 59..60 Other
-
-                FileId(0) 80..81 Other Read
-            "#]],
-        );
-    }
 }
diff --git a/crates/ide_db/src/source_change.rs b/crates/ide_db/src/source_change.rs
index 10c0abdaca..e87d98dadc 100644
--- a/crates/ide_db/src/source_change.rs
+++ b/crates/ide_db/src/source_change.rs
@@ -44,7 +44,7 @@ impl From<Vec<SourceFileEdit>> for SourceChange {
 
 #[derive(Debug, Clone)]
 pub enum FileSystemEdit {
-    CreateFile { dst: AnchoredPathBuf, initial_contents: String },
+    CreateFile { dst: AnchoredPathBuf },
     MoveFile { src: FileId, dst: AnchoredPathBuf },
 }
 
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index f08c8bab74..1a078f6b4c 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -55,7 +55,7 @@ pub(crate) mod fragments {
     use super::*;
 
     pub(crate) use super::{
-        expressions::block_expr, paths::type_path as path, patterns::pattern_single, types::type_,
+        expressions::block_expr, paths::type_path as path, patterns::pattern, types::type_,
     };
 
     pub(crate) fn expr(p: &mut Parser) {
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index e897d5a52f..18b63feb7b 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -156,13 +156,11 @@ fn tuple_expr(p: &mut Parser) -> CompletedMarker {
     let mut saw_expr = false;
     while !p.at(EOF) && !p.at(T![')']) {
         saw_expr = true;
-
-        // test tuple_attrs
-        // const A: (i64, i64) = (1, #[cfg(test)] 2);
-        if !expr_with_attrs(p) {
+        if !p.at_ts(EXPR_FIRST) {
+            p.error("expected expression");
             break;
         }
-
+        expr(p);
         if !p.at(T![')']) {
             saw_comma = true;
             p.expect(T![,]);
diff --git a/crates/parser/src/grammar/params.rs b/crates/parser/src/grammar/params.rs
index 2d006a1d59..3ee4e4fca1 100644
--- a/crates/parser/src/grammar/params.rs
+++ b/crates/parser/src/grammar/params.rs
@@ -47,23 +47,20 @@ fn list_(p: &mut Parser, flavor: Flavor) {
     if let FnDef = flavor {
         // test self_param_outer_attr
         // fn f(#[must_use] self) {}
-        let m = p.start();
         attributes::outer_attrs(p);
-        opt_self_param(p, m);
+        opt_self_param(p);
     }
 
     while !p.at(EOF) && !p.at(ket) {
         // test param_outer_arg
         // fn f(#[attr1] pat: Type) {}
-        let m = p.start();
         attributes::outer_attrs(p);
 
         if !p.at_ts(PARAM_FIRST) {
             p.error("expected value parameter");
-            m.abandon(p);
             break;
         }
-        let param = param(p, m, flavor);
+        let param = param(p, flavor);
         if !p.at(ket) {
             p.expect(T![,]);
         }
@@ -80,8 +77,9 @@ const PARAM_FIRST: TokenSet = patterns::PATTERN_FIRST.union(types::TYPE_FIRST);
 
 struct Variadic(bool);
 
-fn param(p: &mut Parser, m: Marker, flavor: Flavor) -> Variadic {
+fn param(p: &mut Parser, flavor: Flavor) -> Variadic {
     let mut res = Variadic(false);
+    let m = p.start();
     match flavor {
         // test param_list_vararg
         // extern "C" { fn printf(format: *const i8, ...) -> i32; }
@@ -153,8 +151,10 @@ fn variadic_param(p: &mut Parser) -> bool {
 //     fn d(&'a mut self, x: i32) {}
 //     fn e(mut self) {}
 // }
-fn opt_self_param(p: &mut Parser, m: Marker) {
+fn opt_self_param(p: &mut Parser) {
+    let m;
     if p.at(T![self]) || p.at(T![mut]) && p.nth(1) == T![self] {
+        m = p.start();
         p.eat(T![mut]);
         p.eat(T![self]);
         // test arb_self_types
@@ -174,8 +174,9 @@ fn opt_self_param(p: &mut Parser, m: Marker) {
             (T![&], T![mut], T![self], _) => 3,
             (T![&], LIFETIME_IDENT, T![self], _) => 3,
             (T![&], LIFETIME_IDENT, T![mut], T![self]) => 4,
-            _ => return m.abandon(p),
+            _ => return,
         };
+        m = p.start();
         p.bump_any();
         if p.at(LIFETIME_IDENT) {
             lifetime(p);
diff --git a/crates/parser/src/grammar/type_params.rs b/crates/parser/src/grammar/type_params.rs
index 4aeccd193c..9c3f7c28a2 100644
--- a/crates/parser/src/grammar/type_params.rs
+++ b/crates/parser/src/grammar/type_params.rs
@@ -113,7 +113,7 @@ fn type_bound(p: &mut Parser) -> bool {
     p.eat(T![?]);
     match p.current() {
         LIFETIME_IDENT => lifetime(p),
-        T![for] => types::for_type(p, false),
+        T![for] => types::for_type(p),
         _ if paths::is_use_path_start(p) => types::path_type_(p, false),
         _ => {
             m.abandon(p);
diff --git a/crates/parser/src/grammar/types.rs b/crates/parser/src/grammar/types.rs
index 94cbf7d85a..36a15eace8 100644
--- a/crates/parser/src/grammar/types.rs
+++ b/crates/parser/src/grammar/types.rs
@@ -44,7 +44,7 @@ fn type_with_bounds_cond(p: &mut Parser, allow_bounds: bool) {
         T![&] => ref_type(p),
         T![_] => infer_type(p),
         T![fn] | T![unsafe] | T![extern] => fn_ptr_type(p),
-        T![for] => for_type(p, allow_bounds),
+        T![for] => for_type(p),
         T![impl] => impl_trait_type(p),
         T![dyn] => dyn_trait_type(p),
         // Some path types are not allowed to have bounds (no plus)
@@ -227,7 +227,7 @@ pub(super) fn for_binder(p: &mut Parser) {
 // type A = for<'a> fn() -> ();
 // type B = for<'a> unsafe extern "C" fn(&'a ()) -> ();
 // type Obj = for<'a> PartialEq<&'a i32>;
-pub(super) fn for_type(p: &mut Parser, allow_bounds: bool) {
+pub(super) fn for_type(p: &mut Parser) {
     assert!(p.at(T![for]));
     let m = p.start();
     for_binder(p);
@@ -240,13 +240,7 @@ pub(super) fn for_type(p: &mut Parser, allow_bounds: bool) {
         }
     }
     type_no_bounds(p);
-    let completed = m.complete(p, FOR_TYPE);
-
-    // test no_dyn_trait_leading_for
-    // type A = for<'a> Test<'a> + Send;
-    if allow_bounds {
-        opt_type_bounds_as_dyn_trait_type(p, completed);
-    }
+    m.complete(p, FOR_TYPE);
 }
 
 // test impl_trait_type
@@ -296,7 +290,7 @@ fn path_or_macro_type_(p: &mut Parser, allow_bounds: bool) {
     let path = m.complete(p, kind);
 
     if allow_bounds {
-        opt_type_bounds_as_dyn_trait_type(p, path);
+        opt_path_type_bounds_as_dyn_trait_type(p, path);
     }
 }
 
@@ -310,23 +304,19 @@ pub(super) fn path_type_(p: &mut Parser, allow_bounds: bool) {
     // fn foo() -> Box<dyn T + 'f> {}
     let path = m.complete(p, PATH_TYPE);
     if allow_bounds {
-        opt_type_bounds_as_dyn_trait_type(p, path);
+        opt_path_type_bounds_as_dyn_trait_type(p, path);
     }
 }
 
-/// This turns a parsed PATH_TYPE or FOR_TYPE optionally into a DYN_TRAIT_TYPE
+/// This turns a parsed PATH_TYPE optionally into a DYN_TRAIT_TYPE
 /// with a TYPE_BOUND_LIST
-fn opt_type_bounds_as_dyn_trait_type(p: &mut Parser, type_marker: CompletedMarker) {
-    assert!(matches!(
-        type_marker.kind(),
-        SyntaxKind::PATH_TYPE | SyntaxKind::FOR_TYPE | SyntaxKind::MACRO_CALL
-    ));
+fn opt_path_type_bounds_as_dyn_trait_type(p: &mut Parser, path_type_marker: CompletedMarker) {
     if !p.at(T![+]) {
         return;
     }
 
     // First create a TYPE_BOUND from the completed PATH_TYPE
-    let m = type_marker.precede(p).complete(p, TYPE_BOUND);
+    let m = path_type_marker.precede(p).complete(p, TYPE_BOUND);
 
     // Next setup a marker for the TYPE_BOUND_LIST
     let m = m.precede(p);
diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs
index 811e740f98..ab8e4c70e8 100644
--- a/crates/parser/src/lib.rs
+++ b/crates/parser/src/lib.rs
@@ -112,7 +112,7 @@ pub fn parse_fragment(
         FragmentKind::Path => grammar::fragments::path,
         FragmentKind::Expr => grammar::fragments::expr,
         FragmentKind::Type => grammar::fragments::type_,
-        FragmentKind::Pattern => grammar::fragments::pattern_single,
+        FragmentKind::Pattern => grammar::fragments::pattern,
         FragmentKind::Item => grammar::fragments::item,
         FragmentKind::Block => grammar::fragments::block_expr,
         FragmentKind::Visibility => grammar::fragments::opt_visibility,
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 5407591987..f16f97131e 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -319,10 +319,6 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
                 message: "original diagnostic".to_string(),
             };
             for info in &related_information {
-                // Filter out empty/non-existent messages, as they greatly confuse VS Code.
-                if info.message.is_empty() {
-                    continue;
-                }
                 diagnostics.push(MappedRustDiagnostic {
                     url: info.location.uri.clone(),
                     fixes: fixes.clone(), // share fixes to make them easier to apply
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 5a1ae96aa0..e0561b5a7a 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -634,47 +634,30 @@ pub(crate) fn snippet_text_document_edit(
     Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits })
 }
 
-pub(crate) fn snippet_text_document_ops(
+pub(crate) fn resource_op(
     snap: &GlobalStateSnapshot,
     file_system_edit: FileSystemEdit,
-) -> Vec<lsp_ext::SnippetDocumentChangeOperation> {
-    let mut ops = Vec::new();
+) -> lsp_types::ResourceOp {
     match file_system_edit {
-        FileSystemEdit::CreateFile { dst, initial_contents } => {
+        FileSystemEdit::CreateFile { dst } => {
             let uri = snap.anchored_path(&dst);
-            let create_file = lsp_types::ResourceOp::Create(lsp_types::CreateFile {
-                uri: uri.clone(),
+            lsp_types::ResourceOp::Create(lsp_types::CreateFile {
+                uri,
                 options: None,
                 annotation_id: None,
-            });
-            ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(create_file));
-            if !initial_contents.is_empty() {
-                let text_document =
-                    lsp_types::OptionalVersionedTextDocumentIdentifier { uri, version: None };
-                let range = range(&LineIndex::new(""), TextRange::empty(TextSize::from(0)));
-                let text_edit = lsp_ext::SnippetTextEdit {
-                    range,
-                    new_text: initial_contents,
-                    insert_text_format: Some(lsp_types::InsertTextFormat::PlainText),
-                };
-                let edit_file =
-                    lsp_ext::SnippetTextDocumentEdit { text_document, edits: vec![text_edit] };
-                ops.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit_file));
-            }
+            })
         }
         FileSystemEdit::MoveFile { src, dst } => {
             let old_uri = snap.file_id_to_url(src);
             let new_uri = snap.anchored_path(&dst);
-            let rename_file = lsp_types::ResourceOp::Rename(lsp_types::RenameFile {
+            lsp_types::ResourceOp::Rename(lsp_types::RenameFile {
                 old_uri,
                 new_uri,
                 options: None,
                 annotation_id: None,
-            });
-            ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(rename_file))
+            })
         }
     }
-    ops
 }
 
 pub(crate) fn snippet_workspace_edit(
@@ -683,8 +666,8 @@ pub(crate) fn snippet_workspace_edit(
 ) -> Result<lsp_ext::SnippetWorkspaceEdit> {
     let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new();
     for op in source_change.file_system_edits {
-        let ops = snippet_text_document_ops(snap, op);
-        document_changes.extend_from_slice(&ops);
+        let op = resource_op(&snap, op);
+        document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Op(op));
     }
     for edit in source_change.source_file_edits {
         let edit = snippet_text_document_edit(&snap, source_change.is_snippet, edit)?;
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index 40f10972f4..1ec6e81bb1 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -46,8 +46,8 @@ You can install the latest release of the plugin from
 https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer[the marketplace].
 
 Note that the plugin may cause conflicts with the
-https://marketplace.visualstudio.com/items?itemName=rust-lang.rust[official Rust plugin].
-It is recommended to disable the Rust plugin when using the rust-analyzer extension.
+https://marketplace.visualstudio.com/items?itemName=rust-lang.rust[official Rust plugin]. It is
+recommended to disable the Rust plugin when using the rust-analyzer extension.
 
 By default, the plugin will prompt you to download the matching version of the server as well:
 
@@ -74,21 +74,18 @@ Note that we only support two most recent versions of VS Code.
 
 ==== Updates
 
-The extension will be updated automatically as new versions become available.
-It will ask your permission to download the matching language server version binary if needed.
+The extension will be updated automatically as new versions become available. It will ask your permission to download the matching language server version binary if needed.
 
 ===== Nightly
 
-We ship nightly releases for VS Code.
-To help us out with testing the newest code and follow the bleeding edge of our `master`, please use the following config:
+We ship nightly releases for VS Code. To help us out with testing the newest code and follow the bleeding edge of our `master`, please use the following config:
 
 [source,json]
 ----
 { "rust-analyzer.updates.channel": "nightly" }
 ----
 
-You will be prompted to install the `nightly` extension version.
-Just click `Download now` and from that moment you will get automatic updates every 24 hours.
+You will be prompted to install the `nightly` extension version. Just click `Download now` and from that moment you will get automatic updates every 24 hours.
 
 If you don't want to be asked for `Download now` every day when the new nightly version is released add the following to your `settings.json`:
 [source,json]
@@ -163,8 +160,7 @@ $ chmod +x ~/.local/bin/rust-analyzer
 
 Ensure `~/.local/bin` is listed in the `$PATH` variable.
 
-Alternatively, you can install it from source using the command below.
-You'll need the latest stable version of the Rust toolchain.
+Alternatively, you can install it from source using the following command:
 
 [source,bash]
 ----
@@ -202,8 +198,7 @@ Emacs support is maintained as part of the https://github.com/emacs-lsp/lsp-mode
 
 === Vim/NeoVim
 
-Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
-Not needed if the extension can install/update it on its own, coc-rust-analyzer is one example.
+Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>. Not needed if the extension can install/update it on its own, coc-rust-analyzer is one example.
 
 The are several LSP client implementations for vim or neovim:
 
@@ -275,16 +270,14 @@ Once `neovim/nvim-lspconfig` is installed, use `+lua require'nvim_lsp'.rust_anal
 
 Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
 
-You also need the `LSP` package.
-To install it:
+You also need the `LSP` package. To install it:
 
 1. If you've never installed a Sublime Text package, install Package Control:
    * Open the command palette (Win/Linux: `ctrl+shift+p`, Mac: `cmd+shift+p`)
    * Type `Install Package Control`, press enter
 2. In the command palette, run `Package control: Install package`, and in the list that pops up, type `LSP` and press enter.
 
-Finally, with your Rust project open, in the command palette, run `LSP: Enable Language Server In Project` or `LSP: Enable Language Server Globally`, then select `rust-analyzer` in the list that pops up to enable the rust-analyzer LSP.
-The latter means that rust-analyzer is enabled by default in Rust projects.
+Finally, with your Rust project open, in the command palette, run `LSP: Enable Language Server In Project` or `LSP: Enable Language Server Globally`, then select `rust-analyzer` in the list that pops up to enable the rust-analyzer LSP. The latter means that rust-analyzer is enabled by default in Rust projects.
 
 If it worked, you should see "rust-analyzer, Line X, Column Y" on the left side of the bottom bar, and after waiting a bit, functionality like tooltips on hovering over variables should become available.
 
@@ -292,8 +285,7 @@ If you get an error saying `No such file or directory: 'rust-analyzer'`, see the
 
 === GNOME Builder
 
-GNOME Builder 3.37.1 and newer has native `rust-analyzer` support.
-If the LSP binary is not available, GNOME Builder can install it when opening a Rust file.
+GNOME Builder 3.37.1 and newer has native `rust-analyzer` support. If the LSP binary is not available, GNOME Builder can install it when opening a Rust file.
 
 == Configuration
 
@@ -421,8 +413,8 @@ include::./generated_diagnostic.adoc[]
 
 ==== Color configurations
 
-It is possible to change the foreground/background color of inlay hints.
-Just add this to your `settings.json`:
+It is possible to change the foreground/background color of inlay hints. Just add this to your
+`settings.json`:
 
 [source,jsonc]
 ----
@@ -444,8 +436,7 @@ Just add this to your `settings.json`:
 
 ==== Semantic style customizations
 
-You can customize the look of different semantic elements in the source code.
-For example, mutable bindings are underlined by default and you can override this behavior by adding the following section to your `settings.json`:
+You can customize the look of different semantic elements in the source code. For example, mutable bindings are underlined by default and you can override this behavior by adding the following section to your `settings.json`:
 
 [source,jsonc]
 ----
@@ -461,9 +452,7 @@ For example, mutable bindings are underlined by default and you can override thi
 ----
 
 ==== Special `when` clause context for keybindings.
-You may use `inRustProject` context to configure keybindings for rust projects only.
-For example:
-
+You may use `inRustProject` context to configure keybindings for rust projects only. For example:
 [source,json]
 ----
 {
@@ -502,8 +491,7 @@ Or it is possible to specify vars more granularly:
 ]
 ```
 
-You can use any valid regular expression as a mask.
-Also note that a full runnable name is something like *run bin_or_example_name*, *test some::mod::test_name* or *test-mod some::mod*, so it is possible to distinguish binaries, single tests, and test modules with this masks: `"^run"`, `"^test "` (the trailing space matters!), and `"^test-mod"` respectively.
+You can use any valid RegExp as a mask. Also note that a full runnable name is something like *run bin_or_example_name*, *test some::mod::test_name* or *test-mod some::mod*, so it is possible to distinguish binaries, single tests, and test modules with this masks: `"^run"`, `"^test "` (the trailing space matters!), and `"^test-mod"` respectively.
 
 ==== Compiler feedback from external commands
 
diff --git a/editors/code/.vscodeignore b/editors/code/.vscodeignore
index c1cf00fa01..5096ce4dab 100644
--- a/editors/code/.vscodeignore
+++ b/editors/code/.vscodeignore
@@ -4,5 +4,6 @@
 !package.json
 !package-lock.json
 !ra_syntax_tree.tmGrammar.json
+!rust.tmGrammar.json
 !icon.png
 !README.md
diff --git a/editors/code/package.json b/editors/code/package.json
index abcc84edac..ad01fea7b6 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -748,6 +748,11 @@
             }
         ],
         "grammars": [
+            {
+                "language": "rust",
+                "scopeName": "source.rust",
+                "path": "rust.tmGrammar.json"
+            },
             {
                 "language": "ra_syntax_tree",
                 "scopeName": "source.ra_syntax_tree",
diff --git a/editors/code/rust.tmGrammar.json b/editors/code/rust.tmGrammar.json
new file mode 100644
index 0000000000..4759bb1162
--- /dev/null
+++ b/editors/code/rust.tmGrammar.json
@@ -0,0 +1,1140 @@
+{
+    "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
+    "name": "Rust",
+    "fileTypes": [
+        "rs"
+    ],
+    "scopeName": "source.rust",
+    "patterns": [
+        {
+            "comment": "boxed slice literal",
+            "begin": "(<)(\\[)",
+            "beginCaptures": {
+                "1": {
+                    "name": "punctuation.brackets.angle.rust"
+                },
+                "2": {
+                    "name": "punctuation.brackets.square.rust"
+                }
+            },
+            "end": ">",
+            "endCaptures": {
+                "0": {
+                    "name": "punctuation.brackets.angle.rust"
+                }
+            },
+            "patterns": [
+                {
+                    "include": "#block-comments"
+                },
+                {
+                    "include": "#comments"
+                },
+                {
+                    "include": "#gtypes"
+                },
+                {
+                    "include": "#lvariables"
+                },
+                {
+                    "include": "#lifetimes"
+                },
+                {
+                    "include": "#punctuation"
+                },
+                {
+                    "include": "#types"
+                }
+            ]
+        },
+        {
+            "comment": "macro type metavariables",
+            "name": "meta.macro.metavariable.type.rust",
+            "match": "(\\$)((crate)|([A-Z][A-Za-z0-9_]*))((:)(block|expr|ident|item|lifetime|literal|meta|path?|stmt|tt|ty|vis))?",
+            "captures": {
+                "1": {
+                    "name": "keyword.operator.macro.dollar.rust"
+                },
+                "3": {
+                    "name": "keyword.other.crate.rust"
+                },
+                "4": {
+                    "name": "entity.name.type.metavariable.rust"
+                },
+                "6": {
+                    "name": "keyword.operator.key-value.rust"
+                },
+                "7": {
+                    "name": "variable.other.metavariable.specifier.rust"
+                }
+            },
+            "patterns": [
+                {
+                    "include": "#keywords"
+                }
+            ]
+        },
+        {
+            "comment": "macro metavariables",
+            "name": "meta.macro.metavariable.rust",
+            "match": "(\\$)([a-z][A-Za-z0-9_]*)((:)(block|expr|ident|item|lifetime|literal|meta|path?|stmt|tt|ty|vis))?",
+            "captures": {
+                "1": {
+                    "name": "keyword.operator.macro.dollar.rust"
+                },
+                "2": {
+                    "name": "variable.other.metavariable.name.rust"
+                },
+                "4": {
+                    "name": "keyword.operator.key-value.rust"
+                },
+                "5": {
+                    "name": "variable.other.metavariable.specifier.rust"
+                }
+            },
+            "patterns": [
+                {
+                    "include": "#keywords"
+                }
+            ]
+        },
+        {
+            "comment": "macro rules",
+            "name": "meta.macro.rules.rust",
+            "match": "\\b(macro_rules!)\\s+(([a-z0-9_]+)|([A-Z][a-z0-9_]*))\\s+(\\{)",
+            "captures": {
+                "1": {
+                    "name": "entity.name.function.macro.rules.rust"
+                },
+                "3": {
+                    "name": "entity.name.function.macro.rust"
+                },
+                "4": {
+                    "name": "entity.name.type.macro.rust"
+                },
+                "5": {
+                    "name": "punctuation.brackets.curly.rust"
+                }
+            }
+        },
+        {
+            "comment": "attributes",
+            "name": "meta.attribute.rust",
+            "begin": "(#)(\\!?)(\\[)",
+            "beginCaptures": {
+                "1": {
+                    "name": "punctuation.definition.attribute.rust"
+                },
+                "2": {
+                    "name": "keyword.operator.attribute.inner.rust"
+                },
+                "3": {
+                    "name": "punctuation.brackets.attribute.rust"
+                }
+            },
+            "end": "\\]",
+            "endCaptures": {
+                "0": {
+                    "name": "punctuation.brackets.attribute.rust"
+                }
+            },
+            "patterns": [
+                {
+                    "include": "#block-comments"
+                },
+                {
+                    "include": "#comments"
+                },
+                {
+                    "include": "#keywords"
+                },
+                {
+                    "include": "#punctuation"
+                },
+                {
+                    "include": "#strings"
+                },
+                {
+                    "include": "#gtypes"
+                },
+                {
+                    "include": "#types"
+                }
+            ]
+        },
+        {
+            "comment": "modules",
+            "match": "(mod)\\s+((?:r#(?!crate|[Ss]elf|super))?[a-z][A-Za-z0-9_]*)",
+            "captures": {
+                "1": {
+                    "name": "storage.type.rust"
+                },
+                "2": {
+                    "name": "entity.name.module.rust"
+                }
+            }
+        },
+        {
+            "comment": "external crate imports",
+            "name": "meta.import.rust",
+            "begin": "\\b(extern)\\s+(crate)",
+            "beginCaptures": {
+                "1": {
+                    "name": "storage.type.rust"
+                },
+                "2": {
+                    "name": "keyword.other.crate.rust"
+                }
+            },
+            "end": ";",
+            "endCaptures": {
+                "0": {
+                    "name": "punctuation.semi.rust"
+                }
+            },
+            "patterns": [
+                {
+                    "include": "#block-comments"
+                },
+                {
+                    "include": "#comments"
+                },
+                {
+                    "include": "#keywords"
+                },
+                {
+                    "include": "#punctuation"
+                }
+            ]
+        },
+        {
+            "comment": "use statements",
+            "name": "meta.use.rust",
+            "begin": "\\b(use)\\s",
+            "beginCaptures": {
+                "1": {
+                    "name": "keyword.other.rust"
+                }
+            },
+            "end": ";",
+            "endCaptures": {
+                "0": {
+                    "name": "punctuation.semi.rust"
+                }
+            },
+            "patterns": [
+                {
+                    "include": "#block-comments"
+                },
+                {
+                    "include": "#comments"
+                },
+                {
+                    "include": "#keywords"
+                },
+                {
+                    "include": "#namespaces"
+                },
+                {
+                    "include": "#punctuation"
+                },
+                {
+                    "include": "#types"
+                },
+                {
+                    "include": "#lvariables"
+                }
+            ]
+        },
+        {
+            "include": "#block-comments"
+        },
+        {
+            "include": "#comments"
+        },
+        {
+            "include": "#lvariables"
+        },
+        {
+            "include": "#constants"
+        },
+        {
+            "include": "#gtypes"
+        },
+        {
+            "include": "#functions"
+        },
+        {
+            "include": "#types"
+        },
+        {
+            "include": "#keywords"
+        },
+        {
+            "include": "#lifetimes"
+        },
+        {
+            "include": "#macros"
+        },
+        {
+            "include": "#namespaces"
+        },
+        {
+            "include": "#punctuation"
+        },
+        {
+            "include": "#strings"
+        },
+        {
+            "include": "#variables"
+        }
+    ],
+    "repository": {
+        "comments": {
+            "patterns": [
+                {
+                    "comment": "documentation comments",
+                    "name": "comment.line.documentation.rust",
+                    "match": "^\\s*///.*"
+                },
+                {
+                    "comment": "line comments",
+                    "name": "comment.line.double-slash.rust",
+                    "match": "\\s*//.*"
+                }
+            ]
+        },
+        "block-comments": {
+            "patterns": [
+                {
+                    "comment": "empty block comments",
+                    "name": "comment.block.rust",
+                    "match": "/\\*\\*/"
+                },
+                {
+                    "comment": "block documentation comments",
+                    "name": "comment.block.documentation.rust",
+                    "begin": "/\\*\\*",
+                    "end": "\\*/",
+                    "patterns": [
+                        {
+                            "include": "#block-comments"
+                        }
+                    ]
+                },
+                {
+                    "comment": "block comments",
+                    "name": "comment.block.rust",
+                    "begin": "/\\*(?!\\*)",
+                    "end": "\\*/",
+                    "patterns": [
+                        {
+                            "include": "#block-comments"
+                        }
+                    ]
+                }
+            ]
+        },
+        "constants": {
+            "patterns": [
+                {
+                    "comment": "ALL CAPS constants",
+                    "name": "constant.other.caps.rust",
+                    "match": "\\b[A-Z]{2}[A-Z0-9_]*\\b"
+                },
+                {
+                    "comment": "constant declarations",
+                    "match": "\\b(const)\\s+([A-Z][A-Za-z0-9_]*)\\b",
+                    "captures": {
+                        "1": {
+                            "name": "storage.type.rust"
+                        },
+                        "2": {
+                            "name": "constant.other.caps.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "decimal integers and floats",
+                    "name": "constant.numeric.decimal.rust",
+                    "match": "\\b\\d[\\d_]*(\\.?)[\\d_]*(?:(E)([+-])([\\d_]+))?(f32|f64|i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\\b",
+                    "captures": {
+                        "1": {
+                            "name": "punctuation.separator.dot.decimal.rust"
+                        },
+                        "2": {
+                            "name": "keyword.operator.exponent.rust"
+                        },
+                        "3": {
+                            "name": "keyword.operator.exponent.sign.rust"
+                        },
+                        "4": {
+                            "name": "constant.numeric.decimal.exponent.mantissa.rust"
+                        },
+                        "5": {
+                            "name": "entity.name.type.numeric.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "hexadecimal integers",
+                    "name": "constant.numeric.hex.rust",
+                    "match": "\\b0x[\\da-fA-F_]+(i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\\b",
+                    "captures": {
+                        "1": {
+                            "name": "entity.name.type.numeric.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "octal integers",
+                    "name": "constant.numeric.oct.rust",
+                    "match": "\\b0o[0-7_]+(i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\\b",
+                    "captures": {
+                        "1": {
+                            "name": "entity.name.type.numeric.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "binary integers",
+                    "name": "constant.numeric.bin.rust",
+                    "match": "\\b0b[01_]+(i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\\b",
+                    "captures": {
+                        "1": {
+                            "name": "entity.name.type.numeric.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "booleans",
+                    "name": "constant.language.bool.rust",
+                    "match": "\\b(true|false)\\b"
+                }
+            ]
+        },
+        "escapes": {
+            "comment": "escapes: ASCII, byte, Unicode, quote, regex",
+            "name": "constant.character.escape.rust",
+            "match": "(\\\\)(?:(?:(x[0-7][0-7a-fA-F])|(u(\\{)[\\da-fA-F]{4,6}(\\}))|.))",
+            "captures": {
+                "1": {
+                    "name": "constant.character.escape.backslash.rust"
+                },
+                "2": {
+                    "name": "constant.character.escape.bit.rust"
+                },
+                "3": {
+                    "name": "constant.character.escape.unicode.rust"
+                },
+                "4": {
+                    "name": "constant.character.escape.unicode.punctuation.rust"
+                },
+                "5": {
+                    "name": "constant.character.escape.unicode.punctuation.rust"
+                }
+            }
+        },
+        "functions": {
+            "patterns": [
+                {
+                    "comment": "pub as a function",
+                    "match": "\\b(pub)(\\()",
+                    "captures": {
+                        "1": {
+                            "name": "keyword.other.rust"
+                        },
+                        "2": {
+                            "name": "punctuation.brackets.round.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "function definition",
+                    "name": "meta.function.definition.rust",
+                    "begin": "\\b(fn)\\s+((?:r#(?!crate|[Ss]elf|super))?[A-Za-z0-9_]+)((\\()|(<))",
+                    "beginCaptures": {
+                        "1": {
+                            "name": "keyword.other.fn.rust"
+                        },
+                        "2": {
+                            "name": "entity.name.function.rust"
+                        },
+                        "4": {
+                            "name": "punctuation.brackets.round.rust"
+                        },
+                        "5": {
+                            "name": "punctuation.brackets.angle.rust"
+                        }
+                    },
+                    "end": "\\{|;",
+                    "endCaptures": {
+                        "0": {
+                            "name": "punctuation.brackets.curly.rust"
+                        }
+                    },
+                    "patterns": [
+                        {
+                            "include": "#block-comments"
+                        },
+                        {
+                            "include": "#comments"
+                        },
+                        {
+                            "include": "#keywords"
+                        },
+                        {
+                            "include": "#lvariables"
+                        },
+                        {
+                            "include": "#constants"
+                        },
+                        {
+                            "include": "#gtypes"
+                        },
+                        {
+                            "include": "#functions"
+                        },
+                        {
+                            "include": "#lifetimes"
+                        },
+                        {
+                            "include": "#macros"
+                        },
+                        {
+                            "include": "#namespaces"
+                        },
+                        {
+                            "include": "#punctuation"
+                        },
+                        {
+                            "include": "#strings"
+                        },
+                        {
+                            "include": "#types"
+                        },
+                        {
+                            "include": "#variables"
+                        }
+                    ]
+                },
+                {
+                    "comment": "function/method calls, chaining",
+                    "name": "meta.function.call.rust",
+                    "begin": "((?:r#(?!crate|[Ss]elf|super))?[A-Za-z0-9_]+)(\\()",
+                    "beginCaptures": {
+                        "1": {
+                            "name": "entity.name.function.rust"
+                        },
+                        "2": {
+                            "name": "punctuation.brackets.round.rust"
+                        }
+                    },
+                    "end": "\\)",
+                    "endCaptures": {
+                        "0": {
+                            "name": "punctuation.brackets.round.rust"
+                        }
+                    },
+                    "patterns": [
+                        {
+                            "include": "#block-comments"
+                        },
+                        {
+                            "include": "#comments"
+                        },
+                        {
+                            "include": "#keywords"
+                        },
+                        {
+                            "include": "#lvariables"
+                        },
+                        {
+                            "include": "#constants"
+                        },
+                        {
+                            "include": "#gtypes"
+                        },
+                        {
+                            "include": "#functions"
+                        },
+                        {
+                            "include": "#lifetimes"
+                        },
+                        {
+                            "include": "#macros"
+                        },
+                        {
+                            "include": "#namespaces"
+                        },
+                        {
+                            "include": "#punctuation"
+                        },
+                        {
+                            "include": "#strings"
+                        },
+                        {
+                            "include": "#types"
+                        },
+                        {
+                            "include": "#variables"
+                        }
+                    ]
+                },
+                {
+                    "comment": "function/method calls with turbofish",
+                    "name": "meta.function.call.rust",
+                    "begin": "((?:r#(?!crate|[Ss]elf|super))?[A-Za-z0-9_]+)(?=::<.*>\\()",
+                    "beginCaptures": {
+                        "1": {
+                            "name": "entity.name.function.rust"
+                        }
+                    },
+                    "end": "\\)",
+                    "endCaptures": {
+                        "0": {
+                            "name": "punctuation.brackets.round.rust"
+                        }
+                    },
+                    "patterns": [
+                        {
+                            "include": "#block-comments"
+                        },
+                        {
+                            "include": "#comments"
+                        },
+                        {
+                            "include": "#keywords"
+                        },
+                        {
+                            "include": "#lvariables"
+                        },
+                        {
+                            "include": "#constants"
+                        },
+                        {
+                            "include": "#gtypes"
+                        },
+                        {
+                            "include": "#functions"
+                        },
+                        {
+                            "include": "#lifetimes"
+                        },
+                        {
+                            "include": "#macros"
+                        },
+                        {
+                            "include": "#namespaces"
+                        },
+                        {
+                            "include": "#punctuation"
+                        },
+                        {
+                            "include": "#strings"
+                        },
+                        {
+                            "include": "#types"
+                        },
+                        {
+                            "include": "#variables"
+                        }
+                    ]
+                }
+            ]
+        },
+        "keywords": {
+            "patterns": [
+                {
+                    "comment": "control flow keywords",
+                    "name": "keyword.control.rust",
+                    "match": "\\b(await|break|continue|do|else|for|if|loop|match|return|try|while|yield)\\b"
+                },
+                {
+                    "comment": "storage keywords",
+                    "name": "storage.type.rust",
+                    "match": "\\b(const|enum|extern|let|macro|mod|struct|trait|type)\\b"
+                },
+                {
+                    "comment": "storage modifiers",
+                    "name": "storage.modifier.rust",
+                    "match": "\\b(abstract|static)\\b"
+                },
+                {
+                    "comment": "other keywords",
+                    "name": "keyword.other.rust",
+                    "match": "\\b(as|async|become|box|dyn|move|final|impl|in|override|priv|pub|ref|typeof|union|unsafe|unsized|use|virtual|where)\\b"
+                },
+                {
+                    "comment": "fn",
+                    "name": "keyword.other.fn.rust",
+                    "match": "\\bfn\\b"
+                },
+                {
+                    "comment": "crate",
+                    "name": "keyword.other.crate.rust",
+                    "match": "\\bcrate\\b"
+                },
+                {
+                    "comment": "mut",
+                    "name": "storage.modifier.mut.rust",
+                    "match": "\\bmut\\b"
+                },
+                {
+                    "comment": "logical operators",
+                    "name": "keyword.operator.logical.rust",
+                    "match": "(\\^|\\||\\|\\||&&|<<|>>|!)(?!=)"
+                },
+                {
+                    "comment": "logical AND, borrow references",
+                    "name": "keyword.operator.borrow.and.rust",
+                    "match": "&(?![&=])"
+                },
+                {
+                    "comment": "assignment operators",
+                    "name": "keyword.operator.assignment.rust",
+                    "match": "(\\+=|-=|\\*=|/=|%=|\\^=|&=|\\|=|<<=|>>=)"
+                },
+                {
+                    "comment": "single equal",
+                    "name": "keyword.operator.assignment.equal.rust",
+                    "match": "(?<![<>])=(?!=|>)"
+                },
+                {
+                    "comment": "comparison operators",
+                    "name": "keyword.operator.comparison.rust",
+                    "match": "(=(=)?(?!>)|!=|<=|(?<!=)>=)"
+                },
+                {
+                    "comment": "math operators",
+                    "name": "keyword.operator.math.rust",
+                    "match": "(([+%]|(\\*(?!\\w)))(?!=))|(-(?!>))|(/(?!/))"
+                },
+                {
+                    "comment": "less than, greater than (special case)",
+                    "match": "(?:\\b|(?:(\\))|(\\])|(\\})))[ \\t]+([<>])[ \\t]+(?:\\b|(?:(\\()|(\\[)|(\\{)))",
+                    "captures": {
+                        "1": {
+                            "name": "punctuation.brackets.round.rust"
+                        },
+                        "2": {
+                            "name": "punctuation.brackets.square.rust"
+                        },
+                        "3": {
+                            "name": "punctuation.brackets.curly.rust"
+                        },
+                        "4": {
+                            "name": "keyword.operator.comparison.rust"
+                        },
+                        "5": {
+                            "name": "punctuation.brackets.round.rust"
+                        },
+                        "6": {
+                            "name": "punctuation.brackets.square.rust"
+                        },
+                        "7": {
+                            "name": "punctuation.brackets.curly.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "namespace operator",
+                    "name": "keyword.operator.namespace.rust",
+                    "match": "::"
+                },
+                {
+                    "comment": "dereference asterisk",
+                    "match": "(\\*)(?=\\w+)",
+                    "captures": {
+                        "1": {
+                            "name": "keyword.operator.dereference.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "subpattern binding",
+                    "name": "keyword.operator.subpattern.rust",
+                    "match": "@"
+                },
+                {
+                    "comment": "dot access",
+                    "name": "keyword.operator.access.dot.rust",
+                    "match": "\\.(?!\\.)"
+                },
+                {
+                    "comment": "ranges, range patterns",
+                    "name": "keyword.operator.range.rust",
+                    "match": "\\.{2}(=|\\.)?"
+                },
+                {
+                    "comment": "colon",
+                    "name": "keyword.operator.key-value.rust",
+                    "match": ":(?!:)"
+                },
+                {
+                    "comment": "dashrocket, skinny arrow",
+                    "name": "keyword.operator.arrow.skinny.rust",
+                    "match": "->"
+                },
+                {
+                    "comment": "hashrocket, fat arrow",
+                    "name": "keyword.operator.arrow.fat.rust",
+                    "match": "=>"
+                },
+                {
+                    "comment": "dollar macros",
+                    "name": "keyword.operator.macro.dollar.rust",
+                    "match": "\\$"
+                },
+                {
+                    "comment": "question mark operator, questionably sized, macro kleene matcher",
+                    "name": "keyword.operator.question.rust",
+                    "match": "\\?"
+                }
+            ]
+        },
+        "interpolations": {
+            "comment": "curly brace interpolations",
+            "name": "meta.interpolation.rust",
+            "match": "({)[^\"{}]*(})",
+            "captures": {
+                "1": {
+                    "name": "punctuation.definition.interpolation.rust"
+                },
+                "2": {
+                    "name": "punctuation.definition.interpolation.rust"
+                }
+            }
+        },
+        "lifetimes": {
+            "patterns": [
+                {
+                    "comment": "named lifetime parameters",
+                    "match": "(['])([a-zA-Z_][0-9a-zA-Z_]*)(?!['])\\b",
+                    "captures": {
+                        "1": {
+                            "name": "punctuation.definition.lifetime.rust"
+                        },
+                        "2": {
+                            "name": "entity.name.type.lifetime.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "borrowing references to named lifetimes",
+                    "match": "(\\&)(['])([a-zA-Z_][0-9a-zA-Z_]*)(?!['])\\b",
+                    "captures": {
+                        "1": {
+                            "name": "keyword.operator.borrow.rust"
+                        },
+                        "2": {
+                            "name": "punctuation.definition.lifetime.rust"
+                        },
+                        "3": {
+                            "name": "entity.name.type.lifetime.rust"
+                        }
+                    }
+                }
+            ]
+        },
+        "macros": {
+            "patterns": [
+                {
+                    "comment": "macros",
+                    "name": "meta.macro.rust",
+                    "match": "(([a-z_][A-Za-z0-9_]*!)|([A-Z_][A-Za-z0-9_]*!))",
+                    "captures": {
+                        "2": {
+                            "name": "entity.name.function.macro.rust"
+                        },
+                        "3": {
+                            "name": "entity.name.type.macro.rust"
+                        }
+                    }
+                }
+            ]
+        },
+        "namespaces": {
+            "patterns": [
+                {
+                    "comment": "namespace (non-type, non-function path segment)",
+                    "match": "(?<![A-Za-z0-9_])([a-z0-9_]+)((?<!super|self)::)",
+                    "captures": {
+                        "1": {
+                            "name": "entity.name.namespace.rust"
+                        },
+                        "2": {
+                            "name": "keyword.operator.namespace.rust"
+                        }
+                    }
+                }
+            ]
+        },
+        "types": {
+            "patterns": [
+                {
+                    "comment": "numeric types",
+                    "match": "(?<![A-Za-z])(f32|f64|i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)\\b",
+                    "captures": {
+                        "1": {
+                            "name": "entity.name.type.numeric.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "parameterized types",
+                    "begin": "\\b([A-Z][A-Za-z0-9]*)(<)",
+                    "beginCaptures": {
+                        "1": {
+                            "name": "entity.name.type.rust"
+                        },
+                        "2": {
+                            "name": "punctuation.brackets.angle.rust"
+                        }
+                    },
+                    "end": ">",
+                    "endCaptures": {
+                        "0": {
+                            "name": "punctuation.brackets.angle.rust"
+                        }
+                    },
+                    "patterns": [
+                        {
+                            "include": "#block-comments"
+                        },
+                        {
+                            "include": "#comments"
+                        },
+                        {
+                            "include": "#keywords"
+                        },
+                        {
+                            "include": "#lvariables"
+                        },
+                        {
+                            "include": "#lifetimes"
+                        },
+                        {
+                            "include": "#punctuation"
+                        },
+                        {
+                            "include": "#types"
+                        },
+                        {
+                            "include": "#variables"
+                        }
+                    ]
+                },
+                {
+                    "comment": "primitive types",
+                    "name": "entity.name.type.primitive.rust",
+                    "match": "\\b(bool|char|str)\\b"
+                },
+                {
+                    "comment": "trait declarations",
+                    "match": "\\b(trait)\\s+([A-Z][A-Za-z0-9]*)\\b",
+                    "captures": {
+                        "1": {
+                            "name": "storage.type.rust"
+                        },
+                        "2": {
+                            "name": "entity.name.type.trait.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "struct declarations",
+                    "match": "\\b(struct)\\s+([A-Z][A-Za-z0-9]*)\\b",
+                    "captures": {
+                        "1": {
+                            "name": "storage.type.rust"
+                        },
+                        "2": {
+                            "name": "entity.name.type.struct.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "enum declarations",
+                    "match": "\\b(enum)\\s+([A-Z][A-Za-z0-9_]*)\\b",
+                    "captures": {
+                        "1": {
+                            "name": "storage.type.rust"
+                        },
+                        "2": {
+                            "name": "entity.name.type.enum.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "type declarations",
+                    "match": "\\b(type)\\s+([A-Z][A-Za-z0-9_]*)\\b",
+                    "captures": {
+                        "1": {
+                            "name": "storage.type.rust"
+                        },
+                        "2": {
+                            "name": "entity.name.type.declaration.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "types",
+                    "name": "entity.name.type.rust",
+                    "match": "\\b[A-Z][A-Za-z0-9]*\\b(?!!)"
+                }
+            ]
+        },
+        "gtypes": {
+            "patterns": [
+                {
+                    "comment": "option types",
+                    "name": "entity.name.type.option.rust",
+                    "match": "\\b(Some|None)\\b"
+                },
+                {
+                    "comment": "result types",
+                    "name": "entity.name.type.result.rust",
+                    "match": "\\b(Ok|Err)\\b"
+                }
+            ]
+        },
+        "punctuation": {
+            "patterns": [
+                {
+                    "comment": "comma",
+                    "name": "punctuation.comma.rust",
+                    "match": ","
+                },
+                {
+                    "comment": "curly braces",
+                    "name": "punctuation.brackets.curly.rust",
+                    "match": "[{}]"
+                },
+                {
+                    "comment": "parentheses, round brackets",
+                    "name": "punctuation.brackets.round.rust",
+                    "match": "[()]"
+                },
+                {
+                    "comment": "semicolon",
+                    "name": "punctuation.semi.rust",
+                    "match": ";"
+                },
+                {
+                    "comment": "square brackets",
+                    "name": "punctuation.brackets.square.rust",
+                    "match": "[\\[\\]]"
+                },
+                {
+                    "comment": "angle brackets",
+                    "name": "punctuation.brackets.angle.rust",
+                    "match": "(?<!=)[<>]"
+                }
+            ]
+        },
+        "strings": {
+            "patterns": [
+                {
+                    "comment": "double-quoted strings and byte strings",
+                    "name": "string.quoted.double.rust",
+                    "begin": "(b?)(\")",
+                    "beginCaptures": {
+                        "1": {
+                            "name": "string.quoted.byte.raw.rust"
+                        },
+                        "2": {
+                            "name": "punctuation.definition.string.rust"
+                        }
+                    },
+                    "end": "\"",
+                    "endCaptures": {
+                        "0": {
+                            "name": "punctuation.definition.string.rust"
+                        }
+                    },
+                    "patterns": [
+                        {
+                            "include": "#escapes"
+                        },
+                        {
+                            "include": "#interpolations"
+                        }
+                    ]
+                },
+                {
+                    "comment": "double-quoted raw strings and raw byte strings",
+                    "name": "string.quoted.double.rust",
+                    "begin": "(b?r)(#*)(\")",
+                    "beginCaptures": {
+                        "1": {
+                            "name": "string.quoted.byte.raw.rust"
+                        },
+                        "2": {
+                            "name": "punctuation.definition.string.raw.rust"
+                        },
+                        "3": {
+                            "name": "punctuation.definition.string.rust"
+                        }
+                    },
+                    "end": "(\")(\\2)",
+                    "endCaptures": {
+                        "1": {
+                            "name": "punctuation.definition.string.rust"
+                        },
+                        "2": {
+                            "name": "punctuation.definition.string.raw.rust"
+                        }
+                    }
+                },
+                {
+                    "comment": "characters and bytes",
+                    "name": "string.quoted.single.char.rust",
+                    "begin": "(b)?(')",
+                    "beginCaptures": {
+                        "1": {
+                            "name": "string.quoted.byte.raw.rust"
+                        },
+                        "2": {
+                            "name": "punctuation.definition.char.rust"
+                        }
+                    },
+                    "end": "'",
+                    "endCaptures": {
+                        "0": {
+                            "name": "punctuation.definition.char.rust"
+                        }
+                    },
+                    "patterns": [
+                        {
+                            "include": "#escapes"
+                        }
+                    ]
+                }
+            ]
+        },
+        "lvariables": {
+            "patterns": [
+                {
+                    "comment": "self",
+                    "name": "variable.language.self.rust",
+                    "match": "\\b[Ss]elf\\b"
+                },
+                {
+                    "comment": "super",
+                    "name": "variable.language.super.rust",
+                    "match": "\\bsuper\\b"
+                }
+            ]
+        },
+        "variables": {
+            "patterns": [
+                {
+                    "comment": "variables",
+                    "name": "variable.other.rust",
+                    "match": "\\b(?<!(?<!\\.)\\.)(?:r#(?!(crate|[Ss]elf|super)))?[a-z0-9_]+\\b"
+                }
+            ]
+        }
+    }
+}
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index 9d4823a34d..92bc4d7f72 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -470,7 +470,7 @@ export function resolveCodeAction(ctx: Ctx): Cmd {
             return;
         }
         const edit = client.protocol2CodeConverter.asWorkspaceEdit(item.edit);
-        await vscode.workspace.applyEdit(edit);
+        await applySnippetWorkspaceEdit(edit);
     };
 }
 
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 4b2d3c8a55..191960960a 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -19,8 +19,9 @@ let ctx: Ctx | undefined;
 const RUST_PROJECT_CONTEXT_NAME = "inRustProject";
 
 export async function activate(context: vscode.ExtensionContext) {
-    // VS Code doesn't show a notification when an extension fails to activate
-    // so we do it ourselves.
+    // For some reason vscode not always shows pop-up error notifications
+    // when an extension fails to activate, so we do it explicitly by ourselves.
+    // FIXME: remove this bit of code once vscode fixes this issue: https://github.com/microsoft/vscode/issues/101242
     await tryActivate(context).catch(err => {
         void vscode.window.showErrorMessage(`Cannot activate rust-analyzer: ${err.message}`);
         throw err;
@@ -291,8 +292,6 @@ async function getServer(config: Config, state: PersistentState): Promise<string
         if (process.platform === "linux") platform = "linux";
         if (process.platform === "darwin") platform = "mac";
         if (process.platform === "win32") platform = "windows";
-    } else if (process.arch === "arm64" && process.platform === "darwin") {
-        platform = "mac";
     }
     if (platform === undefined) {
         vscode.window.showErrorMessage(
@@ -341,7 +340,7 @@ async function getServer(config: Config, state: PersistentState): Promise<string
     });
 
     // Patching executable if that's NixOS.
-    if (await isNixOs()) {
+    if (await fs.stat("/etc/nixos").then(_ => true).catch(_ => false)) {
         await patchelf(dest);
     }
 
@@ -349,15 +348,6 @@ async function getServer(config: Config, state: PersistentState): Promise<string
     return dest;
 }
 
-async function isNixOs(): Promise<boolean> {
-    try {
-        const contents = await fs.readFile("/etc/os-release");
-        return contents.indexOf("ID=nixos") !== -1;
-    } catch (e) {
-        return false;
-    }
-}
-
 async function downloadWithRetryDialog<T>(state: PersistentState, downloadFunc: () => Promise<T>): Promise<T> {
     while (true) {
         try {


Test patch:
diff --git a/crates/assists/src/tests.rs b/crates/assists/src/tests.rs
index b41f4874a5..709a34d03a 100644
--- a/crates/assists/src/tests.rs
+++ b/crates/assists/src/tests.rs
@@ -2,7 +2,6 @@ mod generated;
 
 use hir::Semantics;
 use ide_db::base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt};
-use ide_db::source_change::FileSystemEdit;
 use ide_db::RootDatabase;
 use syntax::TextRange;
 use test_utils::{assert_eq_text, extract_offset, extract_range};
@@ -48,7 +47,7 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) {
     let before = db.file_text(file_id).to_string();
     let frange = FileRange { file_id, range: selection.into() };
 
-    let assist = Assist::resolved(&db, &AssistConfig::default(), frange)
+    let mut assist = Assist::resolved(&db, &AssistConfig::default(), frange)
         .into_iter()
         .find(|assist| assist.assist.id.0 == assist_id)
         .unwrap_or_else(|| {
@@ -64,12 +63,9 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) {
         });
 
     let actual = {
+        let change = assist.source_change.source_file_edits.pop().unwrap();
         let mut actual = before;
-        for source_file_edit in assist.source_change.source_file_edits {
-            if source_file_edit.file_id == file_id {
-                source_file_edit.edit.apply(&mut actual)
-            }
-        }
+        change.edit.apply(&mut actual);
         actual
     };
     assert_eq_text!(&after, &actual);
@@ -103,8 +99,7 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label:
         (Some(assist), ExpectedResult::After(after)) => {
             let mut source_change = assist.source_change;
             assert!(!source_change.source_file_edits.is_empty());
-            let skip_header = source_change.source_file_edits.len() == 1
-                && source_change.file_system_edits.len() == 0;
+            let skip_header = source_change.source_file_edits.len() == 1;
             source_change.source_file_edits.sort_by_key(|it| it.file_id);
 
             let mut buf = String::new();
@@ -120,21 +115,6 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label:
                 buf.push_str(&text);
             }
 
-            for file_system_edit in source_change.file_system_edits.clone() {
-                match file_system_edit {
-                    FileSystemEdit::CreateFile { dst, initial_contents } => {
-                        let sr = db.file_source_root(dst.anchor);
-                        let sr = db.source_root(sr);
-                        let mut base = sr.path_for_file(&dst.anchor).unwrap().clone();
-                        base.pop();
-                        let created_file_path = format!("{}{}", base.to_string(), &dst.path[1..]);
-                        format_to!(buf, "//- {}\n", created_file_path);
-                        buf.push_str(&initial_contents);
-                    }
-                    _ => (),
-                }
-            }
-
             assert_eq_text!(after, &buf);
         }
         (Some(assist), ExpectedResult::Target(target)) => {
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index e9093ec536..cc7c4a3433 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -235,21 +235,6 @@ fn qux(bar: Bar, baz: Baz) {}
     )
 }
 
-#[test]
-fn doctest_extract_module_to_file() {
-    check_doc_test(
-        "extract_module_to_file",
-        r#####"
-mod foo {<|>
-    fn t() {}
-}
-"#####,
-        r#####"
-mod foo;
-"#####,
-    )
-}
-
 #[test]
 fn doctest_extract_struct_from_enum_variant() {
     check_doc_test(
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs
index 451fa1456a..f10e7a9b69 100644
--- a/crates/mbe/src/tests.rs
+++ b/crates/mbe/src/tests.rs
@@ -1003,18 +1003,6 @@ fn test_underscore() {
     .assert_expand_items(r#"foo! { => }"#, r#"0"#);
 }
 
-#[test]
-fn test_vertical_bar_with_pat() {
-    parse_macro(
-        r#"
-            macro_rules! foo {
-                 (| $pat:pat | ) => { 0 }
-            }
-    "#,
-    )
-    .assert_expand_items(r#"foo! { | x | }"#, r#"0"#);
-}
-
 #[test]
 fn test_lifetime() {
     parse_macro(
diff --git a/crates/syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rast b/crates/syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rast
index d3219f0b24..6403ff8d5c 100644
--- a/crates/syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rast
@@ -6,16 +6,16 @@ SOURCE_FILE@0..26
       IDENT@3..4 "f"
     PARAM_LIST@4..22
       L_PAREN@4..5 "("
-      SELF_PARAM@5..21
-        ATTR@5..16
-          POUND@5..6 "#"
-          L_BRACK@6..7 "["
-          PATH@7..15
-            PATH_SEGMENT@7..15
-              NAME_REF@7..15
-                IDENT@7..15 "must_use"
-          R_BRACK@15..16 "]"
-        WHITESPACE@16..17 " "
+      ATTR@5..16
+        POUND@5..6 "#"
+        L_BRACK@6..7 "["
+        PATH@7..15
+          PATH_SEGMENT@7..15
+            NAME_REF@7..15
+              IDENT@7..15 "must_use"
+        R_BRACK@15..16 "]"
+      WHITESPACE@16..17 " "
+      SELF_PARAM@17..21
         SELF_KW@17..21 "self"
       R_PAREN@21..22 ")"
     WHITESPACE@22..23 " "
diff --git a/crates/syntax/test_data/parser/inline/ok/0154_no_dyn_trait_leading_for.rast b/crates/syntax/test_data/parser/inline/ok/0154_no_dyn_trait_leading_for.rast
deleted file mode 100644
index edfcb288ce..0000000000
--- a/crates/syntax/test_data/parser/inline/ok/0154_no_dyn_trait_leading_for.rast
+++ /dev/null
@@ -1,43 +0,0 @@
-SOURCE_FILE@0..34
-  TYPE_ALIAS@0..33
-    TYPE_KW@0..4 "type"
-    WHITESPACE@4..5 " "
-    NAME@5..6
-      IDENT@5..6 "A"
-    WHITESPACE@6..7 " "
-    EQ@7..8 "="
-    WHITESPACE@8..9 " "
-    DYN_TRAIT_TYPE@9..32
-      TYPE_BOUND_LIST@9..32
-        TYPE_BOUND@9..25
-          FOR_TYPE@9..25
-            FOR_KW@9..12 "for"
-            GENERIC_PARAM_LIST@12..16
-              L_ANGLE@12..13 "<"
-              LIFETIME_PARAM@13..15
-                LIFETIME@13..15
-                  LIFETIME_IDENT@13..15 "\'a"
-              R_ANGLE@15..16 ">"
-            WHITESPACE@16..17 " "
-            PATH_TYPE@17..25
-              PATH@17..25
-                PATH_SEGMENT@17..25
-                  NAME_REF@17..21
-                    IDENT@17..21 "Test"
-                  GENERIC_ARG_LIST@21..25
-                    L_ANGLE@21..22 "<"
-                    LIFETIME_ARG@22..24
-                      LIFETIME@22..24
-                        LIFETIME_IDENT@22..24 "\'a"
-                    R_ANGLE@24..25 ">"
-        WHITESPACE@25..26 " "
-        PLUS@26..27 "+"
-        WHITESPACE@27..28 " "
-        TYPE_BOUND@28..32
-          PATH_TYPE@28..32
-            PATH@28..32
-              PATH_SEGMENT@28..32
-                NAME_REF@28..32
-                  IDENT@28..32 "Send"
-    SEMICOLON@32..33 ";"
-  WHITESPACE@33..34 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0154_no_dyn_trait_leading_for.rs b/crates/syntax/test_data/parser/inline/ok/0154_no_dyn_trait_leading_for.rs
deleted file mode 100644
index 47a71fd191..0000000000
--- a/crates/syntax/test_data/parser/inline/ok/0154_no_dyn_trait_leading_for.rs
+++ /dev/null
@@ -1,1 +0,0 @@
-type A = for<'a> Test<'a> + Send;
diff --git a/crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rast b/crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rast
deleted file mode 100644
index d34b21abe8..0000000000
--- a/crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rast
+++ /dev/null
@@ -1,50 +0,0 @@
-SOURCE_FILE@0..43
-  CONST@0..42
-    CONST_KW@0..5 "const"
-    WHITESPACE@5..6 " "
-    NAME@6..7
-      IDENT@6..7 "A"
-    COLON@7..8 ":"
-    WHITESPACE@8..9 " "
-    TUPLE_TYPE@9..19
-      L_PAREN@9..10 "("
-      PATH_TYPE@10..13
-        PATH@10..13
-          PATH_SEGMENT@10..13
-            NAME_REF@10..13
-              IDENT@10..13 "i64"
-      COMMA@13..14 ","
-      WHITESPACE@14..15 " "
-      PATH_TYPE@15..18
-        PATH@15..18
-          PATH_SEGMENT@15..18
-            NAME_REF@15..18
-              IDENT@15..18 "i64"
-      R_PAREN@18..19 ")"
-    WHITESPACE@19..20 " "
-    EQ@20..21 "="
-    WHITESPACE@21..22 " "
-    TUPLE_EXPR@22..41
-      L_PAREN@22..23 "("
-      LITERAL@23..24
-        INT_NUMBER@23..24 "1"
-      COMMA@24..25 ","
-      WHITESPACE@25..26 " "
-      LITERAL@26..40
-        ATTR@26..38
-          POUND@26..27 "#"
-          L_BRACK@27..28 "["
-          PATH@28..31
-            PATH_SEGMENT@28..31
-              NAME_REF@28..31
-                IDENT@28..31 "cfg"
-          TOKEN_TREE@31..37
-            L_PAREN@31..32 "("
-            IDENT@32..36 "test"
-            R_PAREN@36..37 ")"
-          R_BRACK@37..38 "]"
-        WHITESPACE@38..39 " "
-        INT_NUMBER@39..40 "2"
-      R_PAREN@40..41 ")"
-    SEMICOLON@41..42 ";"
-  WHITESPACE@42..43 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rs b/crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rs
deleted file mode 100644
index f84b7ab31d..0000000000
--- a/crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rs
+++ /dev/null
@@ -1,1 +0,0 @@
-const A: (i64, i64) = (1, #[cfg(test)] 2);
diff --git a/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast b/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast
index 3fed118382..8974f9e40e 100644
--- a/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast
+++ b/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast
@@ -107,16 +107,16 @@ SOURCE_FILE@0..519
                       IDENT@102..104 "i8"
           COMMA@104..105 ","
           WHITESPACE@105..106 " "
-          PARAM@106..117
-            ATTR@106..113
-              POUND@106..107 "#"
-              L_BRACK@107..108 "["
-              PATH@108..112
-                PATH_SEGMENT@108..112
-                  NAME_REF@108..112
-                    IDENT@108..112 "attr"
-              R_BRACK@112..113 "]"
-            WHITESPACE@113..114 " "
+          ATTR@106..113
+            POUND@106..107 "#"
+            L_BRACK@107..108 "["
+            PATH@108..112
+              PATH_SEGMENT@108..112
+                NAME_REF@108..112
+                  IDENT@108..112 "attr"
+            R_BRACK@112..113 "]"
+          WHITESPACE@113..114 " "
+          PARAM@114..117
             DOT3@114..117 "..."
           R_PAREN@117..118 ")"
         WHITESPACE@118..119 " "
@@ -153,16 +153,16 @@ SOURCE_FILE@0..519
                     IDENT@140..145 "FnMut"
                   PARAM_LIST@145..167
                     L_PAREN@145..146 "("
-                    PARAM@146..166
-                      ATTR@146..153
-                        POUND@146..147 "#"
-                        L_BRACK@147..148 "["
-                        PATH@148..152
-                          PATH_SEGMENT@148..152
-                            NAME_REF@148..152
-                              IDENT@148..152 "attr"
-                        R_BRACK@152..153 "]"
-                      WHITESPACE@153..154 " "
+                    ATTR@146..153
+                      POUND@146..147 "#"
+                      L_BRACK@147..148 "["
+                      PATH@148..152
+                        PATH_SEGMENT@148..152
+                          NAME_REF@148..152
+                            IDENT@148..152 "attr"
+                      R_BRACK@152..153 "]"
+                    WHITESPACE@153..154 " "
+                    PARAM@154..166
                       REF_TYPE@154..166
                         AMP@154..155 "&"
                         MUT_KW@155..158 "mut"
@@ -224,17 +224,17 @@ SOURCE_FILE@0..519
                     IDENT@208..211 "u64"
           COMMA@211..212 ","
           WHITESPACE@212..213 " "
-          PARAM@213..232
-            ATTR@213..221
-              POUND@213..214 "#"
-              WHITESPACE@214..215 " "
-              L_BRACK@215..216 "["
-              PATH@216..220
-                PATH_SEGMENT@216..220
-                  NAME_REF@216..220
-                    IDENT@216..220 "attr"
-              R_BRACK@220..221 "]"
-            WHITESPACE@221..222 " "
+          ATTR@213..221
+            POUND@213..214 "#"
+            WHITESPACE@214..215 " "
+            L_BRACK@215..216 "["
+            PATH@216..220
+              PATH_SEGMENT@216..220
+                NAME_REF@216..220
+                  IDENT@216..220 "attr"
+            R_BRACK@220..221 "]"
+          WHITESPACE@221..222 " "
+          PARAM@222..232
             IDENT_PAT@222..227
               MUT_KW@222..225 "mut"
               WHITESPACE@225..226 " "
@@ -271,16 +271,16 @@ SOURCE_FILE@0..519
           IDENT@255..256 "f"
         PARAM_LIST@256..274
           L_PAREN@256..257 "("
-          SELF_PARAM@257..273
-            ATTR@257..268
-              POUND@257..258 "#"
-              L_BRACK@258..259 "["
-              PATH@259..267
-                PATH_SEGMENT@259..267
-                  NAME_REF@259..267
-                    IDENT@259..267 "must_use"
-              R_BRACK@267..268 "]"
-            WHITESPACE@268..269 " "
+          ATTR@257..268
+            POUND@257..258 "#"
+            L_BRACK@258..259 "["
+            PATH@259..267
+              PATH_SEGMENT@259..267
+                NAME_REF@259..267
+                  IDENT@259..267 "must_use"
+            R_BRACK@267..268 "]"
+          WHITESPACE@268..269 " "
+          SELF_PARAM@269..273
             SELF_KW@269..273 "self"
           R_PAREN@273..274 ")"
         WHITESPACE@274..275 " "
@@ -295,16 +295,16 @@ SOURCE_FILE@0..519
           IDENT@286..288 "g1"
         PARAM_LIST@288..302
           L_PAREN@288..289 "("
-          SELF_PARAM@289..301
-            ATTR@289..296
-              POUND@289..290 "#"
-              L_BRACK@290..291 "["
-              PATH@291..295
-                PATH_SEGMENT@291..295
-                  NAME_REF@291..295
-                    IDENT@291..295 "attr"
-              R_BRACK@295..296 "]"
-            WHITESPACE@296..297 " "
+          ATTR@289..296
+            POUND@289..290 "#"
+            L_BRACK@290..291 "["
+            PATH@291..295
+              PATH_SEGMENT@291..295
+                NAME_REF@291..295
+                  IDENT@291..295 "attr"
+            R_BRACK@295..296 "]"
+          WHITESPACE@296..297 " "
+          SELF_PARAM@297..301
             SELF_KW@297..301 "self"
           R_PAREN@301..302 ")"
         WHITESPACE@302..303 " "
@@ -319,16 +319,16 @@ SOURCE_FILE@0..519
           IDENT@314..316 "g2"
         PARAM_LIST@316..331
           L_PAREN@316..317 "("
-          SELF_PARAM@317..330
-            ATTR@317..324
-              POUND@317..318 "#"
-              L_BRACK@318..319 "["
-              PATH@319..323
-                PATH_SEGMENT@319..323
-                  NAME_REF@319..323
-                    IDENT@319..323 "attr"
-              R_BRACK@323..324 "]"
-            WHITESPACE@324..325 " "
+          ATTR@317..324
+            POUND@317..318 "#"
+            L_BRACK@318..319 "["
+            PATH@319..323
+              PATH_SEGMENT@319..323
+                NAME_REF@319..323
+                  IDENT@319..323 "attr"
+            R_BRACK@323..324 "]"
+          WHITESPACE@324..325 " "
+          SELF_PARAM@325..330
             AMP@325..326 "&"
             SELF_KW@326..330 "self"
           R_PAREN@330..331 ")"
@@ -350,16 +350,16 @@ SOURCE_FILE@0..519
           R_ANGLE@348..349 ">"
         PARAM_LIST@349..368
           L_PAREN@349..350 "("
-          SELF_PARAM@350..367
-            ATTR@350..357
-              POUND@350..351 "#"
-              L_BRACK@351..352 "["
-              PATH@352..356
-                PATH_SEGMENT@352..356
-                  NAME_REF@352..356
-                    IDENT@352..356 "attr"
-              R_BRACK@356..357 "]"
-            WHITESPACE@357..358 " "
+          ATTR@350..357
+            POUND@350..351 "#"
+            L_BRACK@351..352 "["
+            PATH@352..356
+              PATH_SEGMENT@352..356
+                NAME_REF@352..356
+                  IDENT@352..356 "attr"
+            R_BRACK@356..357 "]"
+          WHITESPACE@357..358 " "
+          SELF_PARAM@358..367
             AMP@358..359 "&"
             MUT_KW@359..362 "mut"
             WHITESPACE@362..363 " "
@@ -383,16 +383,16 @@ SOURCE_FILE@0..519
           R_ANGLE@385..386 ">"
         PARAM_LIST@386..404
           L_PAREN@386..387 "("
-          SELF_PARAM@387..403
-            ATTR@387..394
-              POUND@387..388 "#"
-              L_BRACK@388..389 "["
-              PATH@389..393
-                PATH_SEGMENT@389..393
-                  NAME_REF@389..393
-                    IDENT@389..393 "attr"
-              R_BRACK@393..394 "]"
-            WHITESPACE@394..395 " "
+          ATTR@387..394
+            POUND@387..388 "#"
+            L_BRACK@388..389 "["
+            PATH@389..393
+              PATH_SEGMENT@389..393
+                NAME_REF@389..393
+                  IDENT@389..393 "attr"
+            R_BRACK@393..394 "]"
+          WHITESPACE@394..395 " "
+          SELF_PARAM@395..403
             AMP@395..396 "&"
             LIFETIME@396..398
               LIFETIME_IDENT@396..398 "\'a"
@@ -417,16 +417,16 @@ SOURCE_FILE@0..519
           R_ANGLE@421..422 ">"
         PARAM_LIST@422..444
           L_PAREN@422..423 "("
-          SELF_PARAM@423..443
-            ATTR@423..430
-              POUND@423..424 "#"
-              L_BRACK@424..425 "["
-              PATH@425..429
-                PATH_SEGMENT@425..429
-                  NAME_REF@425..429
-                    IDENT@425..429 "attr"
-              R_BRACK@429..430 "]"
-            WHITESPACE@430..431 " "
+          ATTR@423..430
+            POUND@423..424 "#"
+            L_BRACK@424..425 "["
+            PATH@425..429
+              PATH_SEGMENT@425..429
+                NAME_REF@425..429
+                  IDENT@425..429 "attr"
+            R_BRACK@429..430 "]"
+          WHITESPACE@430..431 " "
+          SELF_PARAM@431..443
             AMP@431..432 "&"
             LIFETIME@432..434
               LIFETIME_IDENT@432..434 "\'a"
@@ -447,16 +447,16 @@ SOURCE_FILE@0..519
           IDENT@456..457 "c"
         PARAM_LIST@457..477
           L_PAREN@457..458 "("
-          SELF_PARAM@458..476
-            ATTR@458..465
-              POUND@458..459 "#"
-              L_BRACK@459..460 "["
-              PATH@460..464
-                PATH_SEGMENT@460..464
-                  NAME_REF@460..464
-                    IDENT@460..464 "attr"
-              R_BRACK@464..465 "]"
-            WHITESPACE@465..466 " "
+          ATTR@458..465
+            POUND@458..459 "#"
+            L_BRACK@459..460 "["
+            PATH@460..464
+              PATH_SEGMENT@460..464
+                NAME_REF@460..464
+                  IDENT@460..464 "attr"
+            R_BRACK@464..465 "]"
+          WHITESPACE@465..466 " "
+          SELF_PARAM@466..476
             SELF_KW@466..470 "self"
             COLON@470..471 ":"
             WHITESPACE@471..472 " "
@@ -478,16 +478,16 @@ SOURCE_FILE@0..519
           IDENT@489..490 "d"
         PARAM_LIST@490..514
           L_PAREN@490..491 "("
-          SELF_PARAM@491..513
-            ATTR@491..498
-              POUND@491..492 "#"
-              L_BRACK@492..493 "["
-              PATH@493..497
-                PATH_SEGMENT@493..497
-                  NAME_REF@493..497
-                    IDENT@493..497 "attr"
-              R_BRACK@497..498 "]"
-            WHITESPACE@498..499 " "
+          ATTR@491..498
+            POUND@491..492 "#"
+            L_BRACK@492..493 "["
+            PATH@493..497
+              PATH_SEGMENT@493..497
+                NAME_REF@493..497
+                  IDENT@493..497 "attr"
+            R_BRACK@497..498 "]"
+          WHITESPACE@498..499 " "
+          SELF_PARAM@499..513
             SELF_KW@499..503 "self"
             COLON@503..504 ":"
             WHITESPACE@504..505 " "
diff --git a/crates/syntax/test_data/parser/ok/0063_variadic_fun.rast b/crates/syntax/test_data/parser/ok/0063_variadic_fun.rast
index f7c0948982..4009b3ff8d 100644
--- a/crates/syntax/test_data/parser/ok/0063_variadic_fun.rast
+++ b/crates/syntax/test_data/parser/ok/0063_variadic_fun.rast
@@ -92,20 +92,20 @@ SOURCE_FILE@0..126
                       IDENT@88..90 "u8"
           COMMA@90..91 ","
           WHITESPACE@91..92 " "
-          PARAM@92..120
-            ATTR@92..105
-              POUND@92..93 "#"
-              L_BRACK@93..94 "["
-              PATH@94..97
-                PATH_SEGMENT@94..97
-                  NAME_REF@94..97
-                    IDENT@94..97 "cfg"
-              TOKEN_TREE@97..104
-                L_PAREN@97..98 "("
-                IDENT@98..103 "never"
-                R_PAREN@103..104 ")"
-              R_BRACK@104..105 "]"
-            WHITESPACE@105..106 " "
+          ATTR@92..105
+            POUND@92..93 "#"
+            L_BRACK@93..94 "["
+            PATH@94..97
+              PATH_SEGMENT@94..97
+                NAME_REF@94..97
+                  IDENT@94..97 "cfg"
+            TOKEN_TREE@97..104
+              L_PAREN@97..98 "("
+              IDENT@98..103 "never"
+              R_PAREN@103..104 ")"
+            R_BRACK@104..105 "]"
+          WHITESPACE@105..106 " "
+          PARAM@106..120
             SLICE_PAT@106..115
               L_BRACK@106..107 "["
               IDENT_PAT@107..108