diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs index 991955fca6..25178ddd77 100644 --- a/crates/parser/src/grammar.rs +++ b/crates/parser/src/grammar.rs @@ -75,7 +75,7 @@ pub(crate) mod entry_points { } 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 @@ impl BlockLike { } } -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 9de9afde5d..39be0e1a19 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 42ebecc6d6..c5bd5b14ba 100644 --- a/crates/parser/src/grammar/items/adt.rs +++ b/crates/parser/src/grammar/items/adt.rs @@ -128,7 +128,7 @@ pub(crate) fn record_field_list(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 0000000000..8a1f5903d2 --- /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 0000000000..00d8feba96 --- /dev/null +++ b/crates/syntax/test_data/parser/inline/ok/0196_pub_tuple_field.rs @@ -0,0 +1 @@ +struct MyStruct(pub (u32, u32));