diff --git a/src/data.rs b/src/data.rs index a7174c4651..b217b8ca6f 100644 --- a/src/data.rs +++ b/src/data.rs @@ -316,6 +316,17 @@ pub mod parsing { impl Parse for Visibility { fn parse(input: ParseStream) -> Result { + // Recognize an empty None-delimited group, as produced by a $:vis + // matcher that matched no tokens. + if input.peek(token::Group) { + let ahead = input.fork(); + let group = crate::group::parse_group(&ahead)?; + if group.content.is_empty() { + input.advance_to(&ahead); + return Ok(Visibility::Inherited); + } + } + if input.peek(Token![pub]) { Self::parse_pub(input) } else if input.peek(Token![crate]) { diff --git a/tests/test_visibility.rs b/tests/test_visibility.rs index 21f9adf286..c3d0ac7a5b 100644 --- a/tests/test_visibility.rs +++ b/tests/test_visibility.rs @@ -1,6 +1,10 @@ -use proc_macro2::TokenStream; +#[macro_use] +mod macros; + +use proc_macro2::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree}; +use std::iter::FromIterator; use syn::parse::{Parse, ParseStream}; -use syn::{Result, Visibility}; +use syn::{DeriveInput, Result, Visibility}; #[derive(Debug)] struct VisRest { @@ -95,3 +99,47 @@ fn test_crate_path() { fn test_junk_after_in() { assert_vis_parse!("pub(in some::path @@garbage)", Err); } + +#[test] +fn test_empty_group_vis() { + // mimics `struct S { $vis $field: () }` where $vis is empty + let tokens = TokenStream::from_iter(vec![ + TokenTree::Ident(Ident::new("struct", Span::call_site())), + TokenTree::Ident(Ident::new("S", Span::call_site())), + TokenTree::Group(Group::new( + Delimiter::Brace, + TokenStream::from_iter(vec![ + TokenTree::Group(Group::new(Delimiter::None, TokenStream::new())), + TokenTree::Group(Group::new( + Delimiter::None, + TokenStream::from_iter(vec![TokenTree::Ident(Ident::new( + "f", + Span::call_site(), + ))]), + )), + TokenTree::Punct(Punct::new(':', Spacing::Alone)), + TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())), + ]), + )), + ]); + + snapshot!(tokens as DeriveInput, @r###" + DeriveInput { + vis: Inherited, + ident: "S", + generics: Generics, + data: Data::Struct { + fields: Fields::Named { + named: [ + Field { + vis: Inherited, + ident: Some("f"), + colon_token: Some, + ty: Type::Tuple, + }, + ], + }, + }, + } + "###); +}