Repository: rust-analyzer/rust-analyzer
Pull request #10420 (2021-10-02 03:02:59)

Parse outer attributes on StructPatternEtCetera
Try to fix https://github.com/rust-analyzer/rust-analyzer/issues/8610  
Related pr in ungrammer: https://github.com/rust-analyzer/ungrammar/pull/41


Patch:
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index 804d98ce38..bafaa72661 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -821,7 +821,7 @@ impl ExprCollector<'_> {
                 let ellipsis = p
                     .record_pat_field_list()
                     .expect("every struct should have a field list")
-                    .dotdot_token()
+                    .rest_pat()
                     .is_some();
 
                 Pat::Record { path, args, ellipsis }
diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs
index 81e2051abb..7a49b1b8e8 100644
--- a/crates/parser/src/grammar/patterns.rs
+++ b/crates/parser/src/grammar/patterns.rs
@@ -200,46 +200,64 @@ fn tuple_pat_fields(p: &mut Parser) {
     p.expect(T![')']);
 }
 
+// test record_pat_field
+// fn foo() {
+//     let S { 0: 1 } = ();
+//     let S { x: 1 } = ();
+//     let S { #[cfg(any())] x: 1 } = ();
+// }
+fn record_pat_field(p: &mut Parser) {
+    match p.current() {
+        IDENT | INT_NUMBER if p.nth(1) == T![:] => {
+            name_ref_or_index(p);
+            p.bump(T![:]);
+            pattern(p);
+        }
+        T![.] => {
+            if p.at(T![..]) {
+                p.bump(T![..]);
+            } else {
+                ident_pat(p, false);
+            }
+        }
+        T![box] => {
+            // FIXME: not all box patterns should be allowed
+            box_pat(p);
+        }
+        _ => {
+            ident_pat(p, false);
+        }
+    }
+}
+
 // test record_pat_field_list
 // fn foo() {
 //     let S {} = ();
 //     let S { f, ref mut g } = ();
 //     let S { h: _, ..} = ();
 //     let S { h: _, } = ();
+//     let S { #[cfg(any())] .. } = ();
 // }
 fn record_pat_field_list(p: &mut Parser) {
     assert!(p.at(T!['{']));
     let m = p.start();
     p.bump(T!['{']);
     while !p.at(EOF) && !p.at(T!['}']) {
+        let m = p.start();
+        attributes::outer_attrs(p);
+
         match p.current() {
             // A trailing `..` is *not* treated as a REST_PAT.
-            T![.] if p.at(T![..]) => p.bump(T![..]),
-            T!['{'] => error_block(p, "expected ident"),
-
+            T![.] if p.at(T![..]) => {
+                p.bump(T![..]);
+                m.complete(p, REST_PAT);
+            }
+            T!['{'] => {
+                error_block(p, "expected ident");
+                m.abandon(p);
+            }
             _ => {
-                let m = p.start();
-                attributes::outer_attrs(p);
-                match p.current() {
-                    // test record_pat_field
-                    // fn foo() {
-                    //     let S { 0: 1 } = ();
-                    //     let S { x: 1 } = ();
-                    //     let S { #[cfg(any())] x: 1 } = ();
-                    // }
-                    IDENT | INT_NUMBER if p.nth(1) == T![:] => {
-                        name_ref_or_index(p);
-                        p.bump(T![:]);
-                        pattern(p);
-                    }
-                    T![box] => {
-                        // FIXME: not all box patterns should be allowed
-                        box_pat(p);
-                    }
-                    _ => {
-                        ident_pat(p, false);
-                    }
-                }
+                record_pat_field(p);
                 m.complete(p, RECORD_PAT_FIELD);
             }
         }
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index f889fa2a2c..3c5adb97fb 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -29,7 +29,7 @@ rayon = "1"
 expect-test = "1.1"
 proc-macro2 = "1.0.8"
 quote = "1.0.2"
-ungrammar = "=1.14.5"
+ungrammar = "=1.14.6"
 
 test_utils = { path = "../test_utils" }
 sourcegen = { path = "../sourcegen" }
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 5d92a0f9e5..9d4aad2d2b 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -1175,6 +1175,7 @@ impl BoxPat {
 pub struct RestPat {
     pub(crate) syntax: SyntaxNode,
 }
+impl ast::HasAttrs for RestPat {}
 impl RestPat {
     pub fn dotdot_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![..]) }
 }
@@ -1289,7 +1290,7 @@ pub struct RecordPatFieldList {
 impl RecordPatFieldList {
     pub fn l_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['{']) }
     pub fn fields(&self) -> AstChildren<RecordPatField> { support::children(&self.syntax) }
-    pub fn dotdot_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![..]) }
+    pub fn rest_pat(&self) -> Option<RestPat> { support::child(&self.syntax) }
     pub fn r_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) }
 }
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -3687,6 +3688,7 @@ impl AstNode for AnyHasAttrs {
             | MATCH_ARM_LIST
             | MATCH_ARM
             | IDENT_PAT
+            | REST_PAT
             | RECORD_PAT_FIELD => true,
             _ => false,
         }


Test patch:
diff --git a/crates/syntax/test_data/parser/inline/ok/0008_path_part.rast b/crates/syntax/test_data/parser/inline/ok/0008_path_part.rast
index ff81307128..c20e41ffae 100644
--- a/crates/syntax/test_data/parser/inline/ok/0008_path_part.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0008_path_part.rast
@@ -62,7 +62,8 @@ SOURCE_FILE@0..103
             RECORD_PAT_FIELD_LIST@66..72
               L_CURLY@66..67 "{"
               WHITESPACE@67..68 " "
-              DOT2@68..70 ".."
+              REST_PAT@68..70
+                DOT2@68..70 ".."
               WHITESPACE@70..71 " "
               R_CURLY@71..72 "}"
           WHITESPACE@72..73 " "
diff --git a/crates/syntax/test_data/parser/inline/ok/0102_record_pat_field_list.rast b/crates/syntax/test_data/parser/inline/ok/0102_record_pat_field_list.rast
index 4c44da9d55..761438d2ec 100644
--- a/crates/syntax/test_data/parser/inline/ok/0102_record_pat_field_list.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0102_record_pat_field_list.rast
@@ -1,5 +1,5 @@
-SOURCE_FILE@0..119
-  FN@0..118
+SOURCE_FILE@0..156
+  FN@0..155
     FN_KW@0..2 "fn"
     WHITESPACE@2..3 " "
     NAME@3..6
@@ -8,8 +8,8 @@ SOURCE_FILE@0..119
       L_PAREN@6..7 "("
       R_PAREN@7..8 ")"
     WHITESPACE@8..9 " "
-    BLOCK_EXPR@9..118
-      STMT_LIST@9..118
+    BLOCK_EXPR@9..155
+      STMT_LIST@9..155
         L_CURLY@9..10 "{"
         WHITESPACE@10..15 "\n    "
         LET_STMT@15..29
@@ -89,7 +89,8 @@ SOURCE_FILE@0..119
                   UNDERSCORE@78..79 "_"
               COMMA@79..80 ","
               WHITESPACE@80..81 " "
-              DOT2@81..83 ".."
+              REST_PAT@81..83
+                DOT2@81..83 ".."
               R_CURLY@83..84 "}"
           WHITESPACE@84..85 " "
           EQ@85..86 "="
@@ -128,6 +129,47 @@ SOURCE_FILE@0..119
             L_PAREN@113..114 "("
             R_PAREN@114..115 ")"
           SEMICOLON@115..116 ";"
-        WHITESPACE@116..117 "\n"
-        R_CURLY@117..118 "}"
-  WHITESPACE@118..119 "\n"
+        WHITESPACE@116..121 "\n    "
+        LET_STMT@121..153
+          LET_KW@121..124 "let"
+          WHITESPACE@124..125 " "
+          RECORD_PAT@125..147
+            PATH@125..126
+              PATH_SEGMENT@125..126
+                NAME_REF@125..126
+                  IDENT@125..126 "S"
+            WHITESPACE@126..127 " "
+            RECORD_PAT_FIELD_LIST@127..147
+              L_CURLY@127..128 "{"
+              WHITESPACE@128..129 " "
+              REST_PAT@129..145
+                ATTR@129..142
+                  POUND@129..130 "#"
+                  L_BRACK@130..131 "["
+                  META@131..141
+                    PATH@131..134
+                      PATH_SEGMENT@131..134
+                        NAME_REF@131..134
+                          IDENT@131..134 "cfg"
+                    TOKEN_TREE@134..141
+                      L_PAREN@134..135 "("
+                      IDENT@135..138 "any"
+                      TOKEN_TREE@138..140
+                        L_PAREN@138..139 "("
+                        R_PAREN@139..140 ")"
+                      R_PAREN@140..141 ")"
+                  R_BRACK@141..142 "]"
+                WHITESPACE@142..143 " "
+                DOT2@143..145 ".."
+              WHITESPACE@145..146 " "
+              R_CURLY@146..147 "}"
+          WHITESPACE@147..148 " "
+          EQ@148..149 "="
+          WHITESPACE@149..150 " "
+          TUPLE_EXPR@150..152
+            L_PAREN@150..151 "("
+            R_PAREN@151..152 ")"
+          SEMICOLON@152..153 ";"
+        WHITESPACE@153..154 "\n"
+        R_CURLY@154..155 "}"
+  WHITESPACE@155..156 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0102_record_pat_field_list.rs b/crates/syntax/test_data/parser/inline/ok/0102_record_pat_field_list.rs
index da3412fa8a..0bfaae7c4d 100644
--- a/crates/syntax/test_data/parser/inline/ok/0102_record_pat_field_list.rs
+++ b/crates/syntax/test_data/parser/inline/ok/0102_record_pat_field_list.rs
@@ -3,4 +3,5 @@ fn foo() {
     let S { f, ref mut g } = ();
     let S { h: _, ..} = ();
     let S { h: _, } = ();
+    let S { #[cfg(any())] .. } = ();
 }