1 use proc_macro2::{Ident, Span};
2 use std::cmp::Ordering;
3 use std::fmt::{self, Display};
4 use std::hash::{Hash, Hasher};
5 
6 #[cfg(feature = "parsing")]
7 use 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.
19 pub struct Lifetime {
20     pub apostrophe: Span,
21     pub ident: Ident,
22 }
23 
24 impl 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     /// ```
newnull39     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 
spannull61     pub fn span(&self) -> Span {
62         self.apostrophe
63             .join(self.ident.span())
64             .unwrap_or(self.apostrophe)
65     }
66 
set_spannull67     pub fn set_span(&mut self, span: Span) {
68         self.apostrophe = span;
69         self.ident.set_span(span);
70     }
71 }
72 
73 impl Display for Lifetime {
fmtnull74     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
75         "'".fmt(formatter)?;
76         self.ident.fmt(formatter)
77     }
78 }
79 
80 impl Clone for Lifetime {
clonenull81     fn clone(&self) -> Self {
82         Lifetime {
83             apostrophe: self.apostrophe,
84             ident: self.ident.clone(),
85         }
86     }
87 }
88 
89 impl PartialEq for Lifetime {
eqnull90     fn eq(&self, other: &Lifetime) -> bool {
91         self.ident.eq(&other.ident)
92     }
93 }
94 
95 impl Eq for Lifetime {}
96 
97 impl PartialOrd for Lifetime {
partial_cmpnull98     fn partial_cmp(&self, other: &Lifetime) -> Option<Ordering> {
99         Some(self.cmp(other))
100     }
101 }
102 
103 impl Ord for Lifetime {
cmpnull104     fn cmp(&self, other: &Lifetime) -> Ordering {
105         self.ident.cmp(&other.ident)
106     }
107 }
108 
109 impl Hash for Lifetime {
hashnull110     fn hash<H: Hasher>(&self, h: &mut H) {
111         self.ident.hash(h);
112     }
113 }
114 
115 #[cfg(feature = "parsing")]
116 pub_if_not_doc! {
117     #[doc(hidden)]
118     #[allow(non_snake_case)]
Lifetimenull119     pub fn Lifetime(marker: lookahead::TokenMarker) -> Lifetime {
120         match marker {}
121     }
122 }
123 
124 #[cfg(feature = "parsing")]
125 pub(crate) mod parsing {
126     use super::*;
127     use crate::parse::{Parse, ParseStream, Result};
128 
129     #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
130     impl Parse for Lifetime {
parsenull131         fn parse(input: ParseStream) -> Result<Self> {
132             input.step(|cursor| {
133                 cursor
134                     .lifetime()
135                     .ok_or_else(|| cursor.error("expected lifetime"))
136             })
137         }
138     }
139 }
140 
141 #[cfg(feature = "printing")]
142 mod printing {
143     use super::*;
144     use proc_macro2::{Punct, Spacing, TokenStream};
145     use quote::{ToTokens, TokenStreamExt};
146 
147     #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
148     impl ToTokens for Lifetime {
to_tokensnull149         fn to_tokens(&self, tokens: &mut TokenStream) {
150             let mut apostrophe = Punct::new('\'', Spacing::Joint);
151             apostrophe.set_span(self.apostrophe);
152             tokens.append(apostrophe);
153             self.ident.to_tokens(tokens);
154         }
155     }
156 }
157