xref: /linux/rust/syn/lifetime.rs (revision 54e3eae855629702c566bd2e130d9f40e7f35bde)
1 // SPDX-License-Identifier: Apache-2.0 OR MIT
2 
3 #[cfg(feature = "parsing")]
4 use crate::lookahead;
5 use proc_macro2::{Ident, Span};
6 use std::cmp::Ordering;
7 use std::fmt::{self, Display};
8 use 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.
20 pub struct Lifetime {
21     pub apostrophe: Span,
22     pub ident: Ident,
23 }
24 
25 impl 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 
74 impl Display for Lifetime {
75     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
76         "'".fmt(formatter)?;
77         self.ident.fmt(formatter)
78     }
79 }
80 
81 impl Clone for Lifetime {
82     fn clone(&self) -> Self {
83         Lifetime {
84             apostrophe: self.apostrophe,
85             ident: self.ident.clone(),
86         }
87     }
88 }
89 
90 impl PartialEq for Lifetime {
91     fn eq(&self, other: &Lifetime) -> bool {
92         self.ident.eq(&other.ident)
93     }
94 }
95 
96 impl Eq for Lifetime {}
97 
98 impl PartialOrd for Lifetime {
99     fn partial_cmp(&self, other: &Lifetime) -> Option<Ordering> {
100         Some(self.cmp(other))
101     }
102 }
103 
104 impl Ord for Lifetime {
105     fn cmp(&self, other: &Lifetime) -> Ordering {
106         self.ident.cmp(&other.ident)
107     }
108 }
109 
110 impl 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 pub_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")]
126 pub(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")]
144 mod 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 }
159