1 #[macro_use]
2 mod macros;
3
4 use proc_macro2::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
5 use std::iter::FromIterator;
6 use syn::parse::{Parse, ParseStream};
7 use syn::{DeriveInput, Result, Visibility};
8
9 #[derive(Debug)]
10 struct VisRest {
11 vis: Visibility,
12 rest: TokenStream,
13 }
14
15 impl Parse for VisRest {
parse(input: ParseStream) -> Result<Self>16 fn parse(input: ParseStream) -> Result<Self> {
17 Ok(VisRest {
18 vis: input.parse()?,
19 rest: input.parse()?,
20 })
21 }
22 }
23
24 macro_rules! assert_vis_parse {
25 ($input:expr, Ok($p:pat)) => {
26 assert_vis_parse!($input, Ok($p) + "");
27 };
28
29 ($input:expr, Ok($p:pat) + $rest:expr) => {
30 let expected = $rest.parse::<TokenStream>().unwrap();
31 let parse: VisRest = syn::parse_str($input).unwrap();
32
33 match parse.vis {
34 $p => {}
35 _ => panic!("Expected {}, got {:?}", stringify!($p), parse.vis),
36 }
37
38 // NOTE: Round-trips through `to_string` to avoid potential whitespace
39 // diffs.
40 assert_eq!(parse.rest.to_string(), expected.to_string());
41 };
42
43 ($input:expr, Err) => {
44 syn::parse2::<VisRest>($input.parse().unwrap()).unwrap_err();
45 };
46 }
47
48 #[test]
test_pub()49 fn test_pub() {
50 assert_vis_parse!("pub", Ok(Visibility::Public(_)));
51 }
52
53 #[test]
test_crate()54 fn test_crate() {
55 assert_vis_parse!("crate", Ok(Visibility::Crate(_)));
56 }
57
58 #[test]
test_inherited()59 fn test_inherited() {
60 assert_vis_parse!("", Ok(Visibility::Inherited));
61 }
62
63 #[test]
test_in()64 fn test_in() {
65 assert_vis_parse!("pub(in foo::bar)", Ok(Visibility::Restricted(_)));
66 }
67
68 #[test]
test_pub_crate()69 fn test_pub_crate() {
70 assert_vis_parse!("pub(crate)", Ok(Visibility::Restricted(_)));
71 }
72
73 #[test]
test_pub_self()74 fn test_pub_self() {
75 assert_vis_parse!("pub(self)", Ok(Visibility::Restricted(_)));
76 }
77
78 #[test]
test_pub_super()79 fn test_pub_super() {
80 assert_vis_parse!("pub(super)", Ok(Visibility::Restricted(_)));
81 }
82
83 #[test]
test_missing_in()84 fn test_missing_in() {
85 assert_vis_parse!("pub(foo::bar)", Ok(Visibility::Public(_)) + "(foo::bar)");
86 }
87
88 #[test]
test_missing_in_path()89 fn test_missing_in_path() {
90 assert_vis_parse!("pub(in)", Err);
91 }
92
93 #[test]
test_crate_path()94 fn test_crate_path() {
95 assert_vis_parse!(
96 "pub(crate::A, crate::B)",
97 Ok(Visibility::Public(_)) + "(crate::A, crate::B)"
98 );
99 }
100
101 #[test]
test_junk_after_in()102 fn test_junk_after_in() {
103 assert_vis_parse!("pub(in some::path @@garbage)", Err);
104 }
105
106 #[test]
test_empty_group_vis()107 fn test_empty_group_vis() {
108 // mimics `struct S { $vis $field: () }` where $vis is empty
109 let tokens = TokenStream::from_iter(vec![
110 TokenTree::Ident(Ident::new("struct", Span::call_site())),
111 TokenTree::Ident(Ident::new("S", Span::call_site())),
112 TokenTree::Group(Group::new(
113 Delimiter::Brace,
114 TokenStream::from_iter(vec![
115 TokenTree::Group(Group::new(Delimiter::None, TokenStream::new())),
116 TokenTree::Group(Group::new(
117 Delimiter::None,
118 TokenStream::from_iter(vec![TokenTree::Ident(Ident::new(
119 "f",
120 Span::call_site(),
121 ))]),
122 )),
123 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
124 TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())),
125 ]),
126 )),
127 ]);
128
129 snapshot!(tokens as DeriveInput, @r###"
130 DeriveInput {
131 vis: Inherited,
132 ident: "S",
133 generics: Generics,
134 data: Data::Struct {
135 fields: Fields::Named {
136 named: [
137 Field {
138 vis: Inherited,
139 ident: Some("f"),
140 colon_token: Some,
141 ty: Type::Tuple,
142 },
143 ],
144 },
145 },
146 }
147 "###);
148 }
149