1 #[cfg(feature = "parsing")]
2 use crate::lookahead;
3 use proc_macro2::{Ident, Span};
4 use std::cmp::Ordering;
5 use std::fmt::{self, Display};
6 use std::hash::{Hash, Hasher};
7 
8 /// A Rust lifetime: `'a`.
9 ///
10 /// Lifetime names must conform to the following rules:
11 ///
12 /// - Must start with an apostrophe.
13 /// - Must not consist of just an apostrophe: `'`.
14 /// - Character after the apostrophe must be `_` or a Unicode code point with
15 ///   the XID_Start property.
16 /// - All following characters must be Unicode code points with the XID_Continue
17 ///   property.
18 pub struct Lifetime {
19     pub apostrophe: Span,
20     pub ident: Ident,
21 }
22 
23 impl Lifetime {
24     /// # Panics
25     ///
26     /// Panics if the lifetime does not conform to the bulleted rules above.
27     ///
28     /// # Invocation
29     ///
30     /// ```
31     /// # use proc_macro2::Span;
32     /// # use syn::Lifetime;
33     /// #
34     /// # fn f() -> Lifetime {
35     /// Lifetime::new("'a", Span::call_site())
36     /// # }
37     /// ```
new(symbol: &str, span: Span) -> Self38     pub fn new(symbol: &str, span: Span) -> Self {
39         if !symbol.starts_with('\'') {
40             panic!(
41                 "lifetime name must start with apostrophe as in \"'a\", got {:?}",
42                 symbol
43             );
44         }
45 
46         if symbol == "'" {
47             panic!("lifetime name must not be empty");
48         }
49 
50         if !crate::ident::xid_ok(&symbol[1..]) {
51             panic!("{:?} is not a valid lifetime name", symbol);
52         }
53 
54         Lifetime {
55             apostrophe: span,
56             ident: Ident::new(&symbol[1..], span),
57         }
58     }
59 
span(&self) -> Span60     pub fn span(&self) -> Span {
61         self.apostrophe
62             .join(self.ident.span())
63             .unwrap_or(self.apostrophe)
64     }
65 
set_span(&mut self, span: Span)66     pub fn set_span(&mut self, span: Span) {
67         self.apostrophe = span;
68         self.ident.set_span(span);
69     }
70 }
71 
72 impl Display for Lifetime {
fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result73     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
74         "'".fmt(formatter)?;
75         self.ident.fmt(formatter)
76     }
77 }
78 
79 impl Clone for Lifetime {
clone(&self) -> Self80     fn clone(&self) -> Self {
81         Lifetime {
82             apostrophe: self.apostrophe,
83             ident: self.ident.clone(),
84         }
85     }
86 }
87 
88 impl PartialEq for Lifetime {
eq(&self, other: &Lifetime) -> bool89     fn eq(&self, other: &Lifetime) -> bool {
90         self.ident.eq(&other.ident)
91     }
92 }
93 
94 impl Eq for Lifetime {}
95 
96 impl PartialOrd for Lifetime {
partial_cmp(&self, other: &Lifetime) -> Option<Ordering>97     fn partial_cmp(&self, other: &Lifetime) -> Option<Ordering> {
98         Some(self.cmp(other))
99     }
100 }
101 
102 impl Ord for Lifetime {
cmp(&self, other: &Lifetime) -> Ordering103     fn cmp(&self, other: &Lifetime) -> Ordering {
104         self.ident.cmp(&other.ident)
105     }
106 }
107 
108 impl Hash for Lifetime {
hash<H: Hasher>(&self, h: &mut H)109     fn hash<H: Hasher>(&self, h: &mut H) {
110         self.ident.hash(h);
111     }
112 }
113 
114 #[cfg(feature = "parsing")]
115 pub_if_not_doc! {
116     #[doc(hidden)]
117     #[allow(non_snake_case)]
118     pub fn Lifetime(marker: lookahead::TokenMarker) -> Lifetime {
119         match marker {}
120     }
121 }
122 
123 #[cfg(feature = "parsing")]
124 pub(crate) mod parsing {
125     use crate::error::Result;
126     use crate::lifetime::Lifetime;
127     use crate::parse::{Parse, ParseStream};
128 
129     #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
130     impl Parse for Lifetime {
parse(input: ParseStream) -> Result<Self>131         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 crate::lifetime::Lifetime;
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_tokens(&self, tokens: &mut TokenStream)149         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