Skip to main content

syn/
restriction.rs

1use crate::path::Path;
2use crate::token;
3use alloc::boxed::Box;
4
5ast_enum! {
6    /// The visibility level of an item: inherited or `pub` or
7    /// `pub(restricted)`.
8    ///
9    /// # Syntax tree enum
10    ///
11    /// This type is a [syntax tree enum].
12    ///
13    /// [syntax tree enum]: crate::expr::Expr#syntax-tree-enums
14    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
15    pub enum Visibility {
16        /// A public visibility level: `pub`.
17        Public(Token![pub]),
18
19        /// A visibility level restricted to some path: `pub(self)` or
20        /// `pub(super)` or `pub(crate)` or `pub(in some::module)`.
21        Restricted(VisRestricted),
22
23        /// An inherited visibility, which usually means private.
24        Inherited,
25    }
26}
27
28ast_struct! {
29    /// A visibility level restricted to some path: `pub(self)` or
30    /// `pub(super)` or `pub(crate)` or `pub(in some::module)`.
31    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
32    pub struct VisRestricted {
33        pub pub_token: Token![pub],
34        pub paren_token: token::Paren,
35        pub in_token: Option<Token![in]>,
36        pub path: Box<Path>,
37    }
38}
39
40#[cfg(feature = "parsing")]
41pub(crate) mod parsing {
42    use crate::error::Result;
43    use crate::ext::IdentExt as _;
44    use crate::ident::Ident;
45    use crate::parse::discouraged::Speculative as _;
46    use crate::parse::{Parse, ParseStream};
47    use crate::path::Path;
48    use crate::restriction::{VisRestricted, Visibility};
49    use crate::token;
50    use alloc::boxed::Box;
51
52    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
53    impl Parse for Visibility {
54        fn parse(input: ParseStream) -> Result<Self> {
55            // Recognize an empty None-delimited group, as produced by a $:vis
56            // matcher that matched no tokens.
57            if input.peek(token::Group) {
58                let ahead = input.fork();
59                let group = crate::group::parse_group(&ahead)?;
60                if group.content.is_empty() {
61                    input.advance_to(&ahead);
62                    return Ok(Visibility::Inherited);
63                }
64            }
65
66            if input.peek(Token![pub]) {
67                Self::parse_pub(input)
68            } else {
69                Ok(Visibility::Inherited)
70            }
71        }
72    }
73
74    impl Visibility {
75        fn parse_pub(input: ParseStream) -> Result<Self> {
76            let pub_token = input.parse::<Token![pub]>()?;
77
78            if input.peek(token::Paren) {
79                let ahead = input.fork();
80
81                let content;
82                let paren_token = parenthesized!(content in ahead);
83                if content.peek(Token![crate])
84                    || content.peek(Token![self])
85                    || content.peek(Token![super])
86                {
87                    let path = content.call(Ident::parse_any)?;
88
89                    // Ensure there are no additional tokens within `content`.
90                    // Without explicitly checking, we may misinterpret a tuple
91                    // field as a restricted visibility, causing a parse error.
92                    // e.g. `pub (crate::A, crate::B)` (Issue #720).
93                    if content.is_empty() {
94                        input.advance_to(&ahead);
95                        return Ok(Visibility::Restricted(VisRestricted {
96                            pub_token,
97                            paren_token,
98                            in_token: None,
99                            path: Box::new(Path::from(path)),
100                        }));
101                    }
102                } else if content.peek(Token![in]) {
103                    let in_token: Token![in] = content.parse()?;
104                    let path = content.call(Path::parse_mod_style)?;
105
106                    input.advance_to(&ahead);
107                    return Ok(Visibility::Restricted(VisRestricted {
108                        pub_token,
109                        paren_token,
110                        in_token: Some(in_token),
111                        path: Box::new(path),
112                    }));
113                }
114            }
115
116            Ok(Visibility::Public(pub_token))
117        }
118
119        #[cfg(feature = "full")]
120        pub(crate) fn is_some(&self) -> bool {
121            match self {
122                Visibility::Inherited => false,
123                _ => true,
124            }
125        }
126    }
127}
128
129#[cfg(feature = "printing")]
130mod printing {
131    use crate::path;
132    use crate::path::printing::PathStyle;
133    use crate::restriction::{VisRestricted, Visibility};
134    use proc_macro2::TokenStream;
135    use quote::ToTokens;
136
137    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
138    impl ToTokens for Visibility {
139        fn to_tokens(&self, tokens: &mut TokenStream) {
140            match self {
141                Visibility::Public(pub_token) => pub_token.to_tokens(tokens),
142                Visibility::Restricted(vis_restricted) => vis_restricted.to_tokens(tokens),
143                Visibility::Inherited => {}
144            }
145        }
146    }
147
148    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
149    impl ToTokens for VisRestricted {
150        fn to_tokens(&self, tokens: &mut TokenStream) {
151            self.pub_token.to_tokens(tokens);
152            self.paren_token.surround(tokens, |tokens| {
153                // TODO: If we have a path which is not "self" or "super" or
154                // "crate", automatically add the "in" token.
155                self.in_token.to_tokens(tokens);
156                path::printing::print_path(tokens, &self.path, PathStyle::Mod);
157            });
158        }
159    }
160}