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     /// ```
new(symbol: &str, span: Span) -> Self39     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 
span(&self) -> Span61     pub fn span(&self) -> Span {
62         self.apostrophe
63             .join(self.ident.span())
64             .unwrap_or(self.apostrophe)
65     }
66 
set_span(&mut self, span: Span)67     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 {
fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result74     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 {
clone(&self) -> Self81     fn clone(&self) -> Self {
82         Lifetime {
83             apostrophe: self.apostrophe,
84             ident: self.ident.clone(),
85         }
86     }
87 }
88 
89 impl PartialEq for Lifetime {
eq(&self, other: &Lifetime) -> bool90     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_cmp(&self, other: &Lifetime) -> Option<Ordering>98     fn partial_cmp(&self, other: &Lifetime) -> Option<Ordering> {
99         Some(self.cmp(other))
100     }
101 }
102 
103 impl Ord for Lifetime {
cmp(&self, other: &Lifetime) -> Ordering104     fn cmp(&self, other: &Lifetime) -> Ordering {
105         self.ident.cmp(&other.ident)
106     }
107 }
108 
109 impl Hash for Lifetime {
hash<H: Hasher>(&self, h: &mut H)110     fn hash<H: Hasher>(&self, h: &mut H) {
111         self.ident.hash(h);
112     }
113 }
114 
115 #[cfg(feature = "parsing")]
116 #[doc(hidden)]
117 #[allow(non_snake_case)]
Lifetime(marker: lookahead::TokenMarker) -> Lifetime118 pub fn Lifetime(marker: lookahead::TokenMarker) -> Lifetime {
119     match marker {}
120 }
121 
122 #[cfg(feature = "parsing")]
123 pub mod parsing {
124     use super::*;
125     use crate::parse::{Parse, ParseStream, Result};
126 
127     #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
128     impl Parse for Lifetime {
parse(input: ParseStream) -> Result<Self>129         fn parse(input: ParseStream) -> Result<Self> {
130             input.step(|cursor| {
131                 cursor
132                     .lifetime()
133                     .ok_or_else(|| cursor.error("expected lifetime"))
134             })
135         }
136     }
137 }
138 
139 #[cfg(feature = "printing")]
140 mod printing {
141     use super::*;
142     use proc_macro2::{Punct, Spacing, TokenStream};
143     use quote::{ToTokens, TokenStreamExt};
144 
145     #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
146     impl ToTokens for Lifetime {
to_tokens(&self, tokens: &mut TokenStream)147         fn to_tokens(&self, tokens: &mut TokenStream) {
148             let mut apostrophe = Punct::new('\'', Spacing::Joint);
149             apostrophe.set_span(self.apostrophe);
150             tokens.append(apostrophe);
151             self.ident.to_tokens(tokens);
152         }
153     }
154 }
155