Skip to main content

syn/
lifetime.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2
3#[cfg(feature = "parsing")]
4use crate::lookahead;
5use proc_macro2::{Ident, Span};
6use std::cmp::Ordering;
7use std::fmt::{self, Display};
8use std::hash::{Hash, Hasher};
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        if !symbol.starts_with('\'') {
42            panic!(
43                "lifetime name must start with apostrophe as in \"'a\", got {:?}",
44                symbol
45            );
46        }
47
48        if symbol == "'" {
49            panic!("lifetime name must not be empty");
50        }
51
52        if !crate::ident::xid_ok(&symbol[1..]) {
53            panic!("{:?} is not a valid lifetime name", symbol);
54        }
55
56        Lifetime {
57            apostrophe: span,
58            ident: Ident::new(&symbol[1..], span),
59        }
60    }
61
62    pub fn span(&self) -> Span {
63        self.apostrophe
64            .join(self.ident.span())
65            .unwrap_or(self.apostrophe)
66    }
67
68    pub fn set_span(&mut self, span: Span) {
69        self.apostrophe = span;
70        self.ident.set_span(span);
71    }
72}
73
74impl Display for Lifetime {
75    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
76        "'".fmt(formatter)?;
77        self.ident.fmt(formatter)
78    }
79}
80
81impl Clone for Lifetime {
82    fn clone(&self) -> Self {
83        Lifetime {
84            apostrophe: self.apostrophe,
85            ident: self.ident.clone(),
86        }
87    }
88}
89
90impl PartialEq for Lifetime {
91    fn eq(&self, other: &Lifetime) -> bool {
92        self.ident.eq(&other.ident)
93    }
94}
95
96impl Eq for Lifetime {}
97
98impl PartialOrd for Lifetime {
99    fn partial_cmp(&self, other: &Lifetime) -> Option<Ordering> {
100        Some(self.cmp(other))
101    }
102}
103
104impl Ord for Lifetime {
105    fn cmp(&self, other: &Lifetime) -> Ordering {
106        self.ident.cmp(&other.ident)
107    }
108}
109
110impl Hash for Lifetime {
111    fn hash<H: Hasher>(&self, h: &mut H) {
112        self.ident.hash(h);
113    }
114}
115
116#[cfg(feature = "parsing")]
117pub_if_not_doc! {
118    #[doc(hidden)]
119    #[allow(non_snake_case)]
120    pub fn Lifetime(marker: lookahead::TokenMarker) -> Lifetime {
121        match marker {}
122    }
123}
124
125#[cfg(feature = "parsing")]
126pub(crate) mod parsing {
127    use crate::error::Result;
128    use crate::lifetime::Lifetime;
129    use crate::parse::{Parse, ParseStream};
130
131    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
132    impl Parse for Lifetime {
133        fn parse(input: ParseStream) -> Result<Self> {
134            input.step(|cursor| {
135                cursor
136                    .lifetime()
137                    .ok_or_else(|| cursor.error("expected lifetime"))
138            })
139        }
140    }
141}
142
143#[cfg(feature = "printing")]
144mod printing {
145    use crate::lifetime::Lifetime;
146    use proc_macro2::{Punct, Spacing, TokenStream};
147    use quote::{ToTokens, TokenStreamExt};
148
149    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
150    impl ToTokens for Lifetime {
151        fn to_tokens(&self, tokens: &mut TokenStream) {
152            let mut apostrophe = Punct::new('\'', Spacing::Joint);
153            apostrophe.set_span(self.apostrophe);
154            tokens.append(apostrophe);
155            self.ident.to_tokens(tokens);
156        }
157    }
158}