1 use std::cmp::Ordering;
2 use std::fmt::{self, Display};
3 use std::hash::{Hash, Hasher};
4
5 use proc_macro2::{Ident, Span};
6
7 #[cfg(feature = "parsing")]
8 use crate::lookahead;
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 ///
21 /// *This type is available if Syn is built with the `"derive"` or `"full"`
22 /// feature.*
23 #[cfg_attr(feature = "extra-traits", derive(Debug))]
24 #[derive(Clone)]
25 pub struct Lifetime {
26 pub apostrophe: Span,
27 pub ident: Ident,
28 }
29
30 impl Lifetime {
31 /// # Panics
32 ///
33 /// Panics if the lifetime does not conform to the bulleted rules above.
34 ///
35 /// # Invocation
36 ///
37 /// ```
38 /// # use proc_macro2::Span;
39 /// # use syn::Lifetime;
40 /// #
41 /// # fn f() -> Lifetime {
42 /// Lifetime::new("'a", Span::call_site())
43 /// # }
44 /// ```
new(symbol: &str, span: Span) -> Self45 pub fn new(symbol: &str, span: Span) -> Self {
46 if !symbol.starts_with('\'') {
47 panic!(
48 "lifetime name must start with apostrophe as in \"'a\", got {:?}",
49 symbol
50 );
51 }
52
53 if symbol == "'" {
54 panic!("lifetime name must not be empty");
55 }
56
57 if !crate::ident::xid_ok(&symbol[1..]) {
58 panic!("{:?} is not a valid lifetime name", symbol);
59 }
60
61 Lifetime {
62 apostrophe: span,
63 ident: Ident::new(&symbol[1..], span),
64 }
65 }
66 }
67
68 impl Display for Lifetime {
fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result69 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
70 "'".fmt(formatter)?;
71 self.ident.fmt(formatter)
72 }
73 }
74
75 impl PartialEq for Lifetime {
eq(&self, other: &Lifetime) -> bool76 fn eq(&self, other: &Lifetime) -> bool {
77 self.ident.eq(&other.ident)
78 }
79 }
80
81 impl Eq for Lifetime {}
82
83 impl PartialOrd for Lifetime {
partial_cmp(&self, other: &Lifetime) -> Option<Ordering>84 fn partial_cmp(&self, other: &Lifetime) -> Option<Ordering> {
85 Some(self.cmp(other))
86 }
87 }
88
89 impl Ord for Lifetime {
cmp(&self, other: &Lifetime) -> Ordering90 fn cmp(&self, other: &Lifetime) -> Ordering {
91 self.ident.cmp(&other.ident)
92 }
93 }
94
95 impl Hash for Lifetime {
hash<H: Hasher>(&self, h: &mut H)96 fn hash<H: Hasher>(&self, h: &mut H) {
97 self.ident.hash(h)
98 }
99 }
100
101 #[cfg(feature = "parsing")]
102 #[doc(hidden)]
103 #[allow(non_snake_case)]
Lifetime(marker: lookahead::TokenMarker) -> Lifetime104 pub fn Lifetime(marker: lookahead::TokenMarker) -> Lifetime {
105 match marker {}
106 }
107
108 #[cfg(feature = "parsing")]
109 pub mod parsing {
110 use super::*;
111
112 use crate::parse::{Parse, ParseStream, Result};
113
114 impl Parse for Lifetime {
parse(input: ParseStream) -> Result<Self>115 fn parse(input: ParseStream) -> Result<Self> {
116 input.step(|cursor| {
117 cursor
118 .lifetime()
119 .ok_or_else(|| cursor.error("expected lifetime"))
120 })
121 }
122 }
123 }
124
125 #[cfg(feature = "printing")]
126 mod printing {
127 use super::*;
128
129 use proc_macro2::{Punct, Spacing, TokenStream};
130 use quote::{ToTokens, TokenStreamExt};
131
132 impl ToTokens for Lifetime {
to_tokens(&self, tokens: &mut TokenStream)133 fn to_tokens(&self, tokens: &mut TokenStream) {
134 let mut apostrophe = Punct::new('\'', Spacing::Joint);
135 apostrophe.set_span(self.apostrophe);
136 tokens.append(apostrophe);
137 self.ident.to_tokens(tokens);
138 }
139 }
140 }
141