Repository: rust-analyzer/rust-analyzer (rust)

Issue:
Fix tuple struct initializer parsing 
rustc allows initializing tuple struct fields just like a normal struct, but using field indices instead of field names.

example:
```rust
struct Weird(i32);

fn main() {
    let w = Weird { 0: 1 };
}
```

Rust analyzer currently marks the `0: 1` part as a syntax error. The message is:
> Syntax Error: expected identifierrust-analyzer

I did not find any documentation yet (must be well hidden) that describes that this should work, but rustc compiles it fine as is.

Pull request description:
Parse tuple struct field initialization
Closes #1218.

This PR modifies the parser to accept the following code:
```rust
fn main() {
    struct TupleStruct(usize);
    let s = TupleStruct {
        0: 1usize,
    };
    
    dbg!(s.0);
}
```

<details><summary>with following AST:</summary>

```
SOURCE_FILE@[0; 118)
  FN_DEF@[0; 116)
    FN_KW@[0; 2) "fn"
    WHITESPACE@[2; 3) " "
    NAME@[3; 7)
      IDENT@[3; 7) "main"
    PARAM_LIST@[7; 9)
      L_PAREN@[7; 8) "("
      R_PAREN@[8; 9) ")"
    WHITESPACE@[9; 10) " "
    BLOCK@[10; 116)
      L_CURLY@[10; 11) "{"
      WHITESPACE@[11; 16) "\n    "
      STRUCT_DEF@[16; 42)
        STRUCT_KW@[16; 22) "struct"
        WHITESPACE@[22; 23) " "
        NAME@[23; 34)
          IDENT@[23; 34) "TupleStruct"
        POS_FIELD_DEF_LIST@[34; 41)
          L_PAREN@[34; 35) "("
          POS_FIELD_DEF@[35; 40)
            PATH_TYPE@[35; 40)
              PATH@[35; 40)
                PATH_SEGMENT@[35; 40)
                  NAME_REF@[35; 40)
                    IDENT@[35; 40) "usize"
          R_PAREN@[40; 41) ")"
        SEMI@[41; 42) ";"
      WHITESPACE@[42; 47) "\n    "
      LET_STMT@[47; 94)
        LET_KW@[47; 50) "let"
        WHITESPACE@[50; 51) " "
        BIND_PAT@[51; 52)
          NAME@[51; 52)
            IDENT@[51; 52) "s"
        WHITESPACE@[52; 53) " "
        EQ@[53; 54) "="
        WHITESPACE@[54; 55) " "
        STRUCT_LIT@[55; 93)
          PATH@[55; 66)
            PATH_SEGMENT@[55; 66)
              NAME_REF@[55; 66)
                IDENT@[55; 66) "TupleStruct"
          WHITESPACE@[66; 67) " "
          NAMED_FIELD_LIST@[67; 93)
            L_CURLY@[67; 68) "{"
            WHITESPACE@[68; 77) "\n        "
            NAMED_FIELD@[77; 86)
              NAME_REF@[77; 78)
                INT_NUMBER@[77; 78) "0"
              COLON@[78; 79) ":"
              WHITESPACE@[79; 80) " "
              LITERAL@[80; 86)
                INT_NUMBER@[80; 86) "1usize"
            COMMA@[86; 87) ","
            WHITESPACE@[87; 92) "\n    "
            R_CURLY@[92; 93) "}"
        SEMI@[93; 94) ";"
      WHITESPACE@[94; 104) "\n    \n    "
      EXPR_STMT@[104; 114)
        MACRO_CALL@[104; 113)
          PATH@[104; 107)
            PATH_SEGMENT@[104; 107)
              NAME_REF@[104; 107)
                IDENT@[104; 107) "dbg"
          EXCL@[107; 108) "!"
          TOKEN_TREE@[108; 113)
            L_PAREN@[108; 109) "("
            IDENT@[109; 110) "s"
            DOT@[110; 111) "."
            INT_NUMBER@[111; 112) "0"
            R_PAREN@[112; 113) ")"
        SEMI@[113; 114) ";"
      WHITESPACE@[114; 115) "\n"
      R_CURLY@[115; 116) "}"
  WHITESPACE@[116; 118) "\n\n"
```

</summary>

Patch:
diff --git a/crates/ra_parser/src/grammar.rs b/crates/ra_parser/src/grammar.rs
index 6580340975..2ee121ccd0 100644
--- a/crates/ra_parser/src/grammar.rs
+++ b/crates/ra_parser/src/grammar.rs
@@ -273,8 +273,8 @@ fn name(p: &mut Parser) {
     name_r(p, TokenSet::empty())
 }
 
-fn name_ref(p: &mut Parser) {
-    if p.at(IDENT) {
+fn name_ref(p: &mut Parser, allow_numeric_names: bool) {
+    if p.at(IDENT) || (allow_numeric_names && p.at(INT_NUMBER)) {
         let m = p.start();
         p.bump();
         m.complete(p, NAME_REF);
diff --git a/crates/ra_parser/src/grammar/expressions.rs b/crates/ra_parser/src/grammar/expressions.rs
index 3e49e70c72..9f9e9cb0ee 100644
--- a/crates/ra_parser/src/grammar/expressions.rs
+++ b/crates/ra_parser/src/grammar/expressions.rs
@@ -458,7 +458,7 @@ fn method_call_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
     assert!(p.at(T![.]) && p.nth(1) == IDENT && (p.nth(2) == T!['('] || p.nth(2) == T![::]));
     let m = lhs.precede(p);
     p.bump();
-    name_ref(p);
+    name_ref(p, false);
     type_args::opt_type_arg_list(p, true);
     if p.at(T!['(']) {
         arg_list(p);
@@ -485,7 +485,7 @@ fn field_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
     let m = lhs.precede(p);
     p.bump();
     if p.at(IDENT) {
-        name_ref(p)
+        name_ref(p, false)
     } else if p.at(INT_NUMBER) {
         p.bump();
     } else if p.at(FLOAT_NUMBER) {
@@ -572,6 +572,7 @@ fn path_expr(p: &mut Parser, r: Restrictions) -> (CompletedMarker, BlockLike) {
 //     S {};
 //     S { x, y: 32, };
 //     S { x, y: 32, ..Default::default() };
+//     TupleStruct { 0: 1 };
 // }
 pub(crate) fn named_field_list(p: &mut Parser) {
     assert!(p.at(T!['{']));
@@ -583,10 +584,10 @@ pub(crate) fn named_field_list(p: &mut Parser) {
             // fn main() {
             //     S { #[cfg(test)] field: 1 }
             // }
-            IDENT | T![#] => {
+            IDENT | INT_NUMBER | T![#] => {
                 let m = p.start();
                 attributes::outer_attributes(p);
-                name_ref(p);
+                name_ref(p, true);
                 if p.eat(T![:]) {
                     expr(p);
                 }
diff --git a/crates/ra_parser/src/grammar/items.rs b/crates/ra_parser/src/grammar/items.rs
index 543af7c4bb..b0081f3960 100644
--- a/crates/ra_parser/src/grammar/items.rs
+++ b/crates/ra_parser/src/grammar/items.rs
@@ -279,7 +279,7 @@ fn extern_crate_item(p: &mut Parser, m: Marker) {
     p.bump();
     assert!(p.at(T![crate]));
     p.bump();
-    name_ref(p);
+    name_ref(p, false);
     opt_alias(p);
     p.expect(T![;]);
     m.complete(p, EXTERN_CRATE_ITEM);
diff --git a/crates/ra_parser/src/grammar/paths.rs b/crates/ra_parser/src/grammar/paths.rs
index 3537b0da11..2c8f0f7e82 100644
--- a/crates/ra_parser/src/grammar/paths.rs
+++ b/crates/ra_parser/src/grammar/paths.rs
@@ -71,7 +71,7 @@ fn path_segment(p: &mut Parser, mode: Mode, first: bool) {
         }
         match p.current() {
             IDENT => {
-                name_ref(p);
+                name_ref(p, false);
                 opt_path_type_args(p, mode);
             }
             // test crate_path
diff --git a/crates/ra_parser/src/grammar/type_args.rs b/crates/ra_parser/src/grammar/type_args.rs
index 3db08b280d..f1d999dead 100644
--- a/crates/ra_parser/src/grammar/type_args.rs
+++ b/crates/ra_parser/src/grammar/type_args.rs
@@ -38,12 +38,12 @@ fn type_arg(p: &mut Parser) {
         // test associated_type_bounds
         // fn print_all<T: Iterator<Item: Display>>(printables: T) {}
         IDENT if p.nth(1) == T![:] => {
-            name_ref(p);
+            name_ref(p, false);
             type_params::bounds(p);
             m.complete(p, ASSOC_TYPE_ARG);
         }
         IDENT if p.nth(1) == T![=] => {
-            name_ref(p);
+            name_ref(p, false);
             p.bump();
             types::type_(p);
             m.complete(p, ASSOC_TYPE_ARG);


Test patch:
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0061_struct_lit.rs b/crates/ra_syntax/test_data/parser/inline/ok/0061_struct_lit.rs
index eb711f68a1..6285e55497 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0061_struct_lit.rs
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0061_struct_lit.rs
@@ -2,4 +2,5 @@ fn foo() {
     S {};
     S { x, y: 32, };
     S { x, y: 32, ..Default::default() };
+    TupleStruct { 0: 1 };
 }
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0061_struct_lit.txt b/crates/ra_syntax/test_data/parser/inline/ok/0061_struct_lit.txt
index 94d1bfe2eb..d06594cae2 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0061_struct_lit.txt
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0061_struct_lit.txt
@@ -1,5 +1,5 @@
-SOURCE_FILE@[0; 86)
-  FN_DEF@[0; 85)
+SOURCE_FILE@[0; 112)
+  FN_DEF@[0; 111)
     FN_KW@[0; 2) "fn"
     WHITESPACE@[2; 3) " "
     NAME@[3; 6)
@@ -8,7 +8,7 @@ SOURCE_FILE@[0; 86)
       L_PAREN@[6; 7) "("
       R_PAREN@[7; 8) ")"
     WHITESPACE@[8; 9) " "
-    BLOCK@[9; 85)
+    BLOCK@[9; 111)
       L_CURLY@[9; 10) "{"
       WHITESPACE@[10; 15) "\n    "
       EXPR_STMT@[15; 20)
@@ -92,6 +92,27 @@ SOURCE_FILE@[0; 86)
             WHITESPACE@[80; 81) " "
             R_CURLY@[81; 82) "}"
         SEMI@[82; 83) ";"
-      WHITESPACE@[83; 84) "\n"
-      R_CURLY@[84; 85) "}"
-  WHITESPACE@[85; 86) "\n"
+      WHITESPACE@[83; 88) "\n    "
+      EXPR_STMT@[88; 109)
+        STRUCT_LIT@[88; 108)
+          PATH@[88; 99)
+            PATH_SEGMENT@[88; 99)
+              NAME_REF@[88; 99)
+                IDENT@[88; 99) "TupleStruct"
+          WHITESPACE@[99; 100) " "
+          NAMED_FIELD_LIST@[100; 108)
+            L_CURLY@[100; 101) "{"
+            WHITESPACE@[101; 102) " "
+            NAMED_FIELD@[102; 106)
+              NAME_REF@[102; 103)
+                INT_NUMBER@[102; 103) "0"
+              COLON@[103; 104) ":"
+              WHITESPACE@[104; 105) " "
+              LITERAL@[105; 106)
+                INT_NUMBER@[105; 106) "1"
+            WHITESPACE@[106; 107) " "
+            R_CURLY@[107; 108) "}"
+        SEMI@[108; 109) ";"
+      WHITESPACE@[109; 110) "\n"
+      R_CURLY@[110; 111) "}"
+  WHITESPACE@[111; 112) "\n"