syn_pub_items/
lifetime.rs

1use std::cmp::Ordering;
2use std::fmt::{self, Display};
3use std::hash::{Hash, Hasher};
4
5use proc_macro2::{Ident, Span};
6use unicode_xid::UnicodeXID;
7
8#[cfg(feature = "parsing")]
9use lookahead;
10
11/// A Rust lifetime: `'a`.
12///
13/// Lifetime names must conform to the following rules:
14///
15/// - Must start with an apostrophe.
16/// - Must not consist of just an apostrophe: `'`.
17/// - Character after the apostrophe must be `_` or a Unicode code point with
18///   the XID_Start property.
19/// - All following characters must be Unicode code points with the XID_Continue
20///   property.
21///
22/// *This type is available if Syn is built with the `"derive"` or `"full"`
23/// feature.*
24#[cfg_attr(feature = "extra-traits", derive(Debug))]
25#[derive(Clone)]
26pub struct Lifetime {
27    pub apostrophe: Span,
28    pub ident: Ident,
29}
30
31impl Lifetime {
32    /// # Panics
33    ///
34    /// Panics if the lifetime does not conform to the bulleted rules above.
35    ///
36    /// # Invocation
37    ///
38    /// ```edition2018
39    /// # use proc_macro2::Span;
40    /// # use syn::Lifetime;
41    /// #
42    /// # fn f() -> Lifetime {
43    /// Lifetime::new("'a", Span::call_site())
44    /// # }
45    /// ```
46    pub fn new(symbol: &str, span: Span) -> Self {
47        if !symbol.starts_with('\'') {
48            panic!(
49                "lifetime name must start with apostrophe as in \"'a\", got {:?}",
50                symbol
51            );
52        }
53
54        if symbol == "'" {
55            panic!("lifetime name must not be empty");
56        }
57
58        fn xid_ok(symbol: &str) -> bool {
59            let mut chars = symbol.chars();
60            let first = chars.next().unwrap();
61            if !(UnicodeXID::is_xid_start(first) || first == '_') {
62                return false;
63            }
64            for ch in chars {
65                if !UnicodeXID::is_xid_continue(ch) {
66                    return false;
67                }
68            }
69            true
70        }
71
72        if !xid_ok(&symbol[1..]) {
73            panic!("{:?} is not a valid lifetime name", symbol);
74        }
75
76        Lifetime {
77            apostrophe: span,
78            ident: Ident::new(&symbol[1..], span),
79        }
80    }
81}
82
83impl Display for Lifetime {
84    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
85        "'".fmt(formatter)?;
86        self.ident.fmt(formatter)
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")]
117#[doc(hidden)]
118#[allow(non_snake_case)]
119pub fn Lifetime(marker: lookahead::TokenMarker) -> Lifetime {
120    match marker {}
121}
122
123#[cfg(feature = "parsing")]
124pub mod parsing {
125    use super::*;
126
127    use parse::{Parse, ParseStream, Result};
128
129    impl Parse for Lifetime {
130        fn parse(input: ParseStream) -> Result<Self> {
131            input.step(|cursor| {
132                cursor
133                    .lifetime()
134                    .ok_or_else(|| cursor.error("expected lifetime"))
135            })
136        }
137    }
138}
139
140#[cfg(feature = "printing")]
141mod printing {
142    use super::*;
143
144    use proc_macro2::{Punct, Spacing, TokenStream};
145    use quote::{ToTokens, TokenStreamExt};
146
147    impl ToTokens for Lifetime {
148        fn to_tokens(&self, tokens: &mut TokenStream) {
149            let mut apostrophe = Punct::new('\'', Spacing::Joint);
150            apostrophe.set_span(self.apostrophe);
151            tokens.append(apostrophe);
152            self.ident.to_tokens(tokens);
153        }
154    }
155}