syn/
lifetime.rs

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