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