syn/
lifetime.rs

1use proc_macro2::{Ident, Span};
2use std::cmp::Ordering;
3use std::fmt::{self, Display};
4use std::hash::{Hash, Hasher};
5
6#[cfg(feature = "parsing")]
7use crate::lookahead;
8
9/// A Rust lifetime: `'a`.
10///
11/// Lifetime names must conform to the following rules:
12///
13/// - Must start with an apostrophe.
14/// - Must not consist of just an apostrophe: `'`.
15/// - Character after the apostrophe must be `_` or a Unicode code point with
16///   the XID_Start property.
17/// - All following characters must be Unicode code points with the XID_Continue
18///   property.
19pub struct Lifetime {
20    pub apostrophe: Span,
21    pub ident: Ident,
22}
23
24impl Lifetime {
25    /// # Panics
26    ///
27    /// Panics if the lifetime does not conform to the bulleted rules above.
28    ///
29    /// # Invocation
30    ///
31    /// ```
32    /// # use proc_macro2::Span;
33    /// # use syn::Lifetime;
34    /// #
35    /// # fn f() -> Lifetime {
36    /// Lifetime::new("'a", Span::call_site())
37    /// # }
38    /// ```
39    pub fn new(symbol: &str, span: Span) -> Self {
40        if !symbol.starts_with('\'') {
41            panic!(
42                "lifetime name must start with apostrophe as in \"'a\", got {:?}",
43                symbol
44            );
45        }
46
47        if symbol == "'" {
48            panic!("lifetime name must not be empty");
49        }
50
51        if !crate::ident::xid_ok(&symbol[1..]) {
52            panic!("{:?} is not a valid lifetime name", symbol);
53        }
54
55        Lifetime {
56            apostrophe: span,
57            ident: Ident::new(&symbol[1..], span),
58        }
59    }
60
61    pub fn span(&self) -> Span {
62        self.apostrophe
63            .join(self.ident.span())
64            .unwrap_or(self.apostrophe)
65    }
66
67    pub fn set_span(&mut self, span: Span) {
68        self.apostrophe = span;
69        self.ident.set_span(span);
70    }
71}
72
73impl Display for Lifetime {
74    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
75        "'".fmt(formatter)?;
76        self.ident.fmt(formatter)
77    }
78}
79
80impl Clone for Lifetime {
81    fn clone(&self) -> Self {
82        Lifetime {
83            apostrophe: self.apostrophe,
84            ident: self.ident.clone(),
85        }
86    }
87}
88
89impl PartialEq for Lifetime {
90    fn eq(&self, other: &Lifetime) -> bool {
91        self.ident.eq(&other.ident)
92    }
93}
94
95impl Eq for Lifetime {}
96
97impl PartialOrd for Lifetime {
98    fn partial_cmp(&self, other: &Lifetime) -> Option<Ordering> {
99        Some(self.cmp(other))
100    }
101}
102
103impl Ord for Lifetime {
104    fn cmp(&self, other: &Lifetime) -> Ordering {
105        self.ident.cmp(&other.ident)
106    }
107}
108
109impl Hash for Lifetime {
110    fn hash<H: Hasher>(&self, h: &mut H) {
111        self.ident.hash(h);
112    }
113}
114
115#[cfg(feature = "parsing")]
116#[doc(hidden)]
117#[allow(non_snake_case)]
118pub fn Lifetime(marker: lookahead::TokenMarker) -> Lifetime {
119    match marker {}
120}
121
122#[cfg(feature = "parsing")]
123pub mod parsing {
124    use super::*;
125    use crate::parse::{Parse, ParseStream, Result};
126
127    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
128    impl Parse for Lifetime {
129        fn parse(input: ParseStream) -> Result<Self> {
130            input.step(|cursor| {
131                cursor
132                    .lifetime()
133                    .ok_or_else(|| cursor.error("expected lifetime"))
134            })
135        }
136    }
137}
138
139#[cfg(feature = "printing")]
140mod printing {
141    use super::*;
142    use proc_macro2::{Punct, Spacing, TokenStream};
143    use quote::{ToTokens, TokenStreamExt};
144
145    #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
146    impl ToTokens for Lifetime {
147        fn to_tokens(&self, tokens: &mut TokenStream) {
148            let mut apostrophe = Punct::new('\'', Spacing::Joint);
149            apostrophe.set_span(self.apostrophe);
150            tokens.append(apostrophe);
151            self.ident.to_tokens(tokens);
152        }
153    }
154}