Skip to main content

syn/
lifetime.rs

1#[cfg(feature = "parsing")]
2use crate::lookahead;
3#[cfg(feature = "parsing")]
4use crate::parse::{ParseStream, Result};
5use core::cmp::Ordering;
6use core::fmt::{self, Display};
7use core::hash::{Hash, Hasher};
8use proc_macro2::{Ident, Span};
9
10/// A Rust lifetime: `'a`.
11///
12/// Lifetime names must conform to the following rules:
13///
14/// - Must start with an apostrophe.
15/// - Must not consist of just an apostrophe: `'`.
16/// - Character after the apostrophe must be `_` or a Unicode code point with
17///   the XID_Start property.
18/// - All following characters must be Unicode code points with the XID_Continue
19///   property.
20pub struct Lifetime {
21    pub apostrophe: Span,
22    pub ident: Ident,
23}
24
25impl Lifetime {
26    /// # Panics
27    ///
28    /// Panics if the lifetime does not conform to the bulleted rules above.
29    ///
30    /// # Invocation
31    ///
32    /// ```
33    /// # use proc_macro2::Span;
34    /// # use syn::Lifetime;
35    /// #
36    /// # fn f() -> Lifetime {
37    /// Lifetime::new("'a", Span::call_site())
38    /// # }
39    /// ```
40    pub fn new(symbol: &str, span: Span) -> Self {
41        let ident = match symbol.strip_prefix('\'') {
42            Some(ident) => ident,
43            None => {
    ::core::panicking::panic_fmt(format_args!("lifetime name must start with apostrophe as in \"\'a\", got {0:?}",
            symbol));
}panic!(
44                "lifetime name must start with apostrophe as in \"'a\", got {:?}",
45                symbol,
46            ),
47        };
48
49        let unraw = ident.strip_prefix("r#");
50        let validate = unraw.unwrap_or(ident);
51        if validate.is_empty() {
52            {
    ::core::panicking::panic_fmt(format_args!("lifetime name must not be empty"));
};panic!("lifetime name must not be empty");
53        }
54        if !crate::ident::xid_ok(validate) {
55            {
    ::core::panicking::panic_fmt(format_args!("{0:?} is not a valid lifetime name",
            symbol));
};panic!("{:?} is not a valid lifetime name", symbol);
56        }
57
58        Lifetime {
59            apostrophe: span,
60            ident: match unraw {
61                Some(unraw) => Ident::new_raw(unraw, span),
62                None => Ident::new(ident, span),
63            },
64        }
65    }
66
67    pub fn span(&self) -> Span {
68        self.apostrophe
69            .join(self.ident.span())
70            .unwrap_or(self.apostrophe)
71    }
72
73    pub fn set_span(&mut self, span: Span) {
74        self.apostrophe = span;
75        self.ident.set_span(span);
76    }
77
78    #[cfg(feature = "parsing")]
79    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
80    pub fn parse_any(input: ParseStream) -> Result<Self> {
81        input.step(|cursor| match cursor.lifetime() {
82            Some((lifetime, rest)) => Ok((lifetime, rest)),
83            None => Err(cursor.error("expected lifetime")),
84        })
85    }
86}
87
88impl Display for Lifetime {
89    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
90        "'".fmt(formatter)?;
91        self.ident.fmt(formatter)
92    }
93}
94
95impl Clone for Lifetime {
96    fn clone(&self) -> Self {
97        Lifetime {
98            apostrophe: self.apostrophe,
99            ident: self.ident.clone(),
100        }
101    }
102}
103
104impl PartialEq for Lifetime {
105    fn eq(&self, other: &Lifetime) -> bool {
106        self.ident.eq(&other.ident)
107    }
108}
109
110impl Eq for Lifetime {}
111
112impl PartialOrd for Lifetime {
113    fn partial_cmp(&self, other: &Lifetime) -> Option<Ordering> {
114        Some(self.cmp(other))
115    }
116}
117
118impl Ord for Lifetime {
119    fn cmp(&self, other: &Lifetime) -> Ordering {
120        self.ident.cmp(&other.ident)
121    }
122}
123
124impl Hash for Lifetime {
125    fn hash<H: Hasher>(&self, h: &mut H) {
126        self.ident.hash(h);
127    }
128}
129
130#[cfg(feature = "parsing")]
131#[doc(hidden)]
#[allow(non_snake_case)]
pub(crate) fn Lifetime(marker: lookahead::TokenMarker) -> Lifetime {
    match marker {}
}pub_if_not_doc! {
132    #[doc(hidden)]
133    #[allow(non_snake_case)]
134    pub fn Lifetime(marker: lookahead::TokenMarker) -> Lifetime {
135        match marker {}
136    }
137}
138
139#[cfg(feature = "parsing")]
140pub(crate) mod parsing {
141    use crate::error::Result;
142    use crate::ident;
143    use crate::lifetime::Lifetime;
144    use crate::parse::{Parse, ParseStream};
145    use alloc::string::ToString;
146
147    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
148    impl Parse for Lifetime {
149        fn parse(input: ParseStream) -> Result<Self> {
150            input.step(|cursor| {
151                if let Some((lifetime, rest)) = cursor.lifetime() {
152                    let repr = lifetime.ident.to_string();
153                    if ident::parsing::accept_as_ident(&repr) || repr == "static" || repr == "_" {
154                        Ok((lifetime, rest))
155                    } else {
156                        Err(cursor
157                            .error(format_args!("unexpected keyword lifetime \"{0}\"", lifetime)format_args!("unexpected keyword lifetime \"{}\"", lifetime)))
158                    }
159                } else {
160                    Err(cursor.error("expected lifetime"))
161                }
162            })
163        }
164    }
165
166    impl Lifetime {
167        #[cfg(any(feature = "full", feature = "derive"))]
168        pub(crate) fn parse_optional_any(input: ParseStream) -> Option<Self> {
169            input
170                .step(|cursor| match cursor.lifetime() {
171                    Some((lifetime, rest)) => Ok((Some(lifetime), rest)),
172                    None => Ok((None, *cursor)),
173                })
174                .unwrap()
175        }
176    }
177}
178
179#[cfg(feature = "printing")]
180mod printing {
181    use crate::ext::PunctExt as _;
182    use crate::lifetime::Lifetime;
183    use proc_macro2::{Punct, Spacing, TokenStream};
184    use quote::{ToTokens, TokenStreamExt as _};
185
186    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
187    impl ToTokens for Lifetime {
188        fn to_tokens(&self, tokens: &mut TokenStream) {
189            tokens.append(Punct::new_spanned('\'', Spacing::Joint, self.apostrophe));
190            self.ident.to_tokens(tokens);
191        }
192    }
193}