From 0d54754ca73d8f370a902f605c94f4b588ded532 Mon Sep 17 00:00:00 2001 From: Adam Bratschi-Kaye Date: Wed, 3 Nov 2021 23:57:46 +0100 Subject: [PATCH] Handle pub tuple fields in tuple structs The current implementation will throw a parser error for tuple structs that contain a pub tuple field. For example, ```rust struct Foo(pub (u32, u32)); ``` is valid Rust, but rust-analyzer will throw a parser error. This is because the parens after `pub` is treated as a visibility context. Allowing a tuple type to follow `pub` in the special case when we are defining fields in a tuple struct can fix the issue. --- crates/parser/src/grammar.rs | 18 +++++++---- crates/parser/src/grammar/items.rs | 2 +- crates/parser/src/grammar/items/adt.rs | 4 +-- .../inline/ok/0196_pub_tuple_field.rast | 30 +++++++++++++++++++ .../parser/inline/ok/0196_pub_tuple_field.rs | 1 + 5 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 crates/syntax/test_data/parser/inline/ok/0196_pub_tuple_field.rast create mode 100644 crates/syntax/test_data/parser/inline/ok/0196_pub_tuple_field.rs diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs index 991955fca6d..25178ddd775 100644 --- a/crates/parser/src/grammar.rs +++ b/crates/parser/src/grammar.rs @@ -75,7 +75,7 @@ pub(crate) fn stmt_optional_semi(p: &mut Parser) { } pub(crate) fn visibility(p: &mut Parser) { - let _ = opt_visibility(p); + let _ = opt_visibility(p, false); } // Parse a meta item , which excluded [], e.g : #[ MetaItem ] @@ -149,7 +149,7 @@ fn is_block(self) -> bool { } } -fn opt_visibility(p: &mut Parser) -> bool { +fn opt_visibility(p: &mut Parser, in_tuple_field: bool) -> bool { match p.current() { T![pub] => { let m = p.start(); @@ -165,9 +165,17 @@ fn opt_visibility(p: &mut Parser) -> bool { // struct B(pub (super::A)); // struct B(pub (crate::A,)); T![crate] | T![self] | T![super] | T![ident] if p.nth(2) != T![:] => { - p.bump(T!['(']); - paths::use_path(p); - p.expect(T![')']); + // If we are in a tuple struct, then the parens following `pub` + // might be an tuple field, not part of the visibility. So in that + // case we don't want to consume an identifier. + + // test pub_tuple_field + // struct MyStruct(pub (u32, u32)); + if !(in_tuple_field && matches!(p.nth(1), T![ident])) { + p.bump(T!['(']); + paths::use_path(p); + p.expect(T![')']); + } } // test crate_visibility_in // pub(in super::A) struct S; diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs index 9de9afde5d3..39be0e1a192 100644 --- a/crates/parser/src/grammar/items.rs +++ b/crates/parser/src/grammar/items.rs @@ -87,7 +87,7 @@ pub(super) fn item_or_macro(p: &mut Parser, stop_on_r_curly: bool) { pub(super) fn opt_item(p: &mut Parser, m: Marker) -> Result<(), Marker> { // test_err pub_expr // fn foo() { pub 92; } - let has_visibility = opt_visibility(p); + let has_visibility = opt_visibility(p, false); let m = match opt_item_without_modifiers(p, m) { Ok(()) => return Ok(()), diff --git a/crates/parser/src/grammar/items/adt.rs b/crates/parser/src/grammar/items/adt.rs index 42ebecc6d6c..c5bd5b14bae 100644 --- a/crates/parser/src/grammar/items/adt.rs +++ b/crates/parser/src/grammar/items/adt.rs @@ -128,7 +128,7 @@ fn record_field(p: &mut Parser) { // test record_field_attrs // struct S { #[attr] f: f32 } attributes::outer_attrs(p); - opt_visibility(p); + opt_visibility(p, false); if p.at(IDENT) { name(p); p.expect(T![:]); @@ -150,7 +150,7 @@ fn tuple_field_list(p: &mut Parser) { // test tuple_field_attrs // struct S (#[attr] f32); attributes::outer_attrs(p); - opt_visibility(p); + opt_visibility(p, true); if !p.at_ts(types::TYPE_FIRST) { p.error("expected a type"); m.complete(p, ERROR); diff --git a/crates/syntax/test_data/parser/inline/ok/0196_pub_tuple_field.rast b/crates/syntax/test_data/parser/inline/ok/0196_pub_tuple_field.rast new file mode 100644 index 00000000000..8a1f5903d20 --- /dev/null +++ b/crates/syntax/test_data/parser/inline/ok/0196_pub_tuple_field.rast @@ -0,0 +1,30 @@ +SOURCE_FILE@0..33 + STRUCT@0..32 + STRUCT_KW@0..6 "struct" + WHITESPACE@6..7 " " + NAME@7..15 + IDENT@7..15 "MyStruct" + TUPLE_FIELD_LIST@15..31 + L_PAREN@15..16 "(" + TUPLE_FIELD@16..30 + VISIBILITY@16..19 + PUB_KW@16..19 "pub" + WHITESPACE@19..20 " " + TUPLE_TYPE@20..30 + L_PAREN@20..21 "(" + PATH_TYPE@21..24 + PATH@21..24 + PATH_SEGMENT@21..24 + NAME_REF@21..24 + IDENT@21..24 "u32" + COMMA@24..25 "," + WHITESPACE@25..26 " " + PATH_TYPE@26..29 + PATH@26..29 + PATH_SEGMENT@26..29 + NAME_REF@26..29 + IDENT@26..29 "u32" + R_PAREN@29..30 ")" + R_PAREN@30..31 ")" + SEMICOLON@31..32 ";" + WHITESPACE@32..33 "\n" diff --git a/crates/syntax/test_data/parser/inline/ok/0196_pub_tuple_field.rs b/crates/syntax/test_data/parser/inline/ok/0196_pub_tuple_field.rs new file mode 100644 index 00000000000..00d8feba967 --- /dev/null +++ b/crates/syntax/test_data/parser/inline/ok/0196_pub_tuple_field.rs @@ -0,0 +1 @@ +struct MyStruct(pub (u32, u32));