• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 use unicode_xid::UnicodeXID;
7 
8 #[cfg(feature = "parsing")]
9 use lookahead;
10 
11 /// A Rust lifetime: `'a`.
12 ///
13 /// Lifetime names must conform to the following rules:
14 ///
15 /// - Must start with an apostrophe.
16 /// - Must not consist of just an apostrophe: `'`.
17 /// - Character after the apostrophe must be `_` or a Unicode code point with
18 ///   the XID_Start property.
19 /// - All following characters must be Unicode code points with the XID_Continue
20 ///   property.
21 ///
22 /// *This type is available if Syn is built with the `"derive"` or `"full"`
23 /// feature.*
24 #[cfg_attr(feature = "extra-traits", derive(Debug))]
25 #[derive(Clone)]
26 pub struct Lifetime {
27     pub apostrophe: Span,
28     pub ident: Ident,
29 }
30 
31 impl Lifetime {
32     /// # Panics
33     ///
34     /// Panics if the lifetime does not conform to the bulleted rules above.
35     ///
36     /// # Invocation
37     ///
38     /// ```edition2018
39     /// # use proc_macro2::Span;
40     /// # use syn::Lifetime;
41     /// #
42     /// # fn f() -> Lifetime {
43     /// Lifetime::new("'a", Span::call_site())
44     /// # }
45     /// ```
new(symbol: &str, span: Span) -> Self46     pub fn new(symbol: &str, span: Span) -> Self {
47         if !symbol.starts_with('\'') {
48             panic!(
49                 "lifetime name must start with apostrophe as in \"'a\", got {:?}",
50                 symbol
51             );
52         }
53 
54         if symbol == "'" {
55             panic!("lifetime name must not be empty");
56         }
57 
58         fn xid_ok(symbol: &str) -> bool {
59             let mut chars = symbol.chars();
60             let first = chars.next().unwrap();
61             if !(UnicodeXID::is_xid_start(first) || first == '_') {
62                 return false;
63             }
64             for ch in chars {
65                 if !UnicodeXID::is_xid_continue(ch) {
66                     return false;
67                 }
68             }
69             true
70         }
71 
72         if !xid_ok(&symbol[1..]) {
73             panic!("{:?} is not a valid lifetime name", symbol);
74         }
75 
76         Lifetime {
77             apostrophe: span,
78             ident: Ident::new(&symbol[1..], span),
79         }
80     }
81 }
82 
83 impl Display for Lifetime {
fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result84     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
85         "'".fmt(formatter)?;
86         self.ident.fmt(formatter)
87     }
88 }
89 
90 impl PartialEq for Lifetime {
eq(&self, other: &Lifetime) -> bool91     fn eq(&self, other: &Lifetime) -> bool {
92         self.ident.eq(&other.ident)
93     }
94 }
95 
96 impl Eq for Lifetime {}
97 
98 impl PartialOrd for Lifetime {
partial_cmp(&self, other: &Lifetime) -> Option<Ordering>99     fn partial_cmp(&self, other: &Lifetime) -> Option<Ordering> {
100         Some(self.cmp(other))
101     }
102 }
103 
104 impl Ord for Lifetime {
cmp(&self, other: &Lifetime) -> Ordering105     fn cmp(&self, other: &Lifetime) -> Ordering {
106         self.ident.cmp(&other.ident)
107     }
108 }
109 
110 impl Hash for Lifetime {
hash<H: Hasher>(&self, h: &mut H)111     fn hash<H: Hasher>(&self, h: &mut H) {
112         self.ident.hash(h)
113     }
114 }
115 
116 #[cfg(feature = "parsing")]
117 #[doc(hidden)]
118 #[allow(non_snake_case)]
Lifetime(marker: lookahead::TokenMarker) -> Lifetime119 pub fn Lifetime(marker: lookahead::TokenMarker) -> Lifetime {
120     match marker {}
121 }
122 
123 #[cfg(feature = "parsing")]
124 pub mod parsing {
125     use super::*;
126 
127     use parse::{Parse, ParseStream, Result};
128 
129     impl Parse for Lifetime {
parse(input: ParseStream) -> Result<Self>130         fn parse(input: ParseStream) -> Result<Self> {
131             input.step(|cursor| {
132                 cursor
133                     .lifetime()
134                     .ok_or_else(|| cursor.error("expected lifetime"))
135             })
136         }
137     }
138 }
139 
140 #[cfg(feature = "printing")]
141 mod printing {
142     use super::*;
143 
144     use proc_macro2::{Punct, Spacing, TokenStream};
145     use quote::{ToTokens, TokenStreamExt};
146 
147     impl ToTokens for Lifetime {
to_tokens(&self, tokens: &mut TokenStream)148         fn to_tokens(&self, tokens: &mut TokenStream) {
149             let mut apostrophe = Punct::new('\'', Spacing::Joint);
150             apostrophe.set_span(self.apostrophe);
151             tokens.append(apostrophe);
152             self.ident.to_tokens(tokens);
153         }
154     }
155 }
156