blob: 461560c91c281b1746178a09b329d490527bd5cb [file] [log] [blame]
David Tolnay63e3dee2017-06-03 20:13:17 -07001use std::cmp::Ordering;
2use std::fmt::{self, Display};
3use std::hash::{Hash, Hasher};
4
David Tolnay65fb5662018-05-20 20:02:28 -07005use proc_macro2::{Ident, Span};
David Tolnay63e3dee2017-06-03 20:13:17 -07006use unicode_xid::UnicodeXID;
7
David Tolnay4fb71232018-08-25 23:14:50 -04008#[cfg(feature = "parsing")]
9use lookahead;
Alex Crichton131308c2018-05-18 14:00:24 -070010
David Tolnay05658502018-01-07 09:56:37 -080011/// 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: `'`.
David Tolnay05658502018-01-07 09:56:37 -080017/// - 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.
David Tolnay461d98e2018-01-07 11:07:19 -080021///
22/// *This type is available if Syn is built with the `"derive"` or `"full"`
23/// feature.*
David Tolnay63e3dee2017-06-03 20:13:17 -070024#[cfg_attr(feature = "extra-traits", derive(Debug))]
Alex Crichtona74a1c82018-05-16 10:20:44 -070025#[derive(Clone)]
David Tolnay63e3dee2017-06-03 20:13:17 -070026pub struct Lifetime {
David Tolnay78612672018-08-31 08:47:41 -070027 pub apostrophe: Span,
Alex Crichton131308c2018-05-18 14:00:24 -070028 pub ident: Ident,
David Tolnay63e3dee2017-06-03 20:13:17 -070029}
30
31impl Lifetime {
David Tolnay092e21a2018-09-01 16:48:52 -070032 /// # Panics
33 ///
34 /// Panics if the lifetime does not conform to the bulleted rules above.
35 ///
36 /// # Invocation
37 ///
David Tolnay95989db2019-01-01 15:05:57 -050038 /// ```edition2018
David Tolnay092e21a2018-09-01 16:48:52 -070039 /// # use proc_macro2::Span;
40 /// # use syn::Lifetime;
41 /// #
42 /// # fn f() -> Lifetime {
43 /// Lifetime::new("'a", Span::call_site())
44 /// # }
45 /// ```
46 pub fn new(symbol: &str, span: Span) -> Self {
47 if !symbol.starts_with('\'') {
David Tolnay51382052017-12-27 13:46:21 -050048 panic!(
David Tolnay76178be2018-07-31 23:06:15 -070049 "lifetime name must start with apostrophe as in \"'a\", got {:?}",
David Tolnay092e21a2018-09-01 16:48:52 -070050 symbol
David Tolnay51382052017-12-27 13:46:21 -050051 );
David Tolnay63e3dee2017-06-03 20:13:17 -070052 }
53
David Tolnay092e21a2018-09-01 16:48:52 -070054 if symbol == "'" {
David Tolnay63e3dee2017-06-03 20:13:17 -070055 panic!("lifetime name must not be empty");
56 }
57
David Tolnay092e21a2018-09-01 16:48:52 -070058 fn xid_ok(symbol: &str) -> bool {
59 let mut chars = symbol.chars();
David Tolnay63e3dee2017-06-03 20:13:17 -070060 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
David Tolnay092e21a2018-09-01 16:48:52 -070072 if !xid_ok(&symbol[1..]) {
73 panic!("{:?} is not a valid lifetime name", symbol);
David Tolnay63e3dee2017-06-03 20:13:17 -070074 }
75
76 Lifetime {
David Tolnaya19a8192018-09-01 02:36:16 -070077 apostrophe: span,
David Tolnay092e21a2018-09-01 16:48:52 -070078 ident: Ident::new(&symbol[1..], span),
David Tolnay63e3dee2017-06-03 20:13:17 -070079 }
80 }
81}
82
83impl Display for Lifetime {
84 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
Alex Crichtona74a1c82018-05-16 10:20:44 -070085 "'".fmt(formatter)?;
86 self.ident.fmt(formatter)
David Tolnay63e3dee2017-06-03 20:13:17 -070087 }
88}
89
90impl PartialEq for Lifetime {
91 fn eq(&self, other: &Lifetime) -> bool {
Alex Crichtona74a1c82018-05-16 10:20:44 -070092 self.ident.eq(&other.ident)
David Tolnay63e3dee2017-06-03 20:13:17 -070093 }
94}
95
96impl Eq for Lifetime {}
97
98impl PartialOrd for Lifetime {
99 fn partial_cmp(&self, other: &Lifetime) -> Option<Ordering> {
100 Some(self.cmp(other))
101 }
102}
103
104impl Ord for Lifetime {
105 fn cmp(&self, other: &Lifetime) -> Ordering {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700106 self.ident.cmp(&other.ident)
David Tolnay63e3dee2017-06-03 20:13:17 -0700107 }
108}
109
110impl Hash for Lifetime {
111 fn hash<H: Hasher>(&self, h: &mut H) {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700112 self.ident.hash(h)
David Tolnay63e3dee2017-06-03 20:13:17 -0700113 }
114}
115
116#[cfg(feature = "parsing")]
David Tolnay4fb71232018-08-25 23:14:50 -0400117#[doc(hidden)]
118#[allow(non_snake_case)]
119pub fn Lifetime(marker: lookahead::TokenMarker) -> Lifetime {
120 match marker {}
121}
122
123#[cfg(feature = "parsing")]
David Tolnay63e3dee2017-06-03 20:13:17 -0700124pub mod parsing {
125 use super::*;
David Tolnay94d304f2018-08-30 23:43:53 -0700126
David Tolnay78612672018-08-31 08:47:41 -0700127 use parse::{Parse, ParseStream, Result};
David Tolnay63e3dee2017-06-03 20:13:17 -0700128
David Tolnay7744d9d2018-08-25 22:15:52 -0400129 impl Parse for Lifetime {
130 fn parse(input: ParseStream) -> Result<Self> {
David Tolnay66cb0c42018-08-31 09:01:30 -0700131 input.step(|cursor| {
132 cursor
133 .lifetime()
134 .ok_or_else(|| cursor.error("expected lifetime"))
David Tolnay7744d9d2018-08-25 22:15:52 -0400135 })
Sergio Benitez5680d6a2017-12-29 11:20:29 -0800136 }
David Tolnay63e3dee2017-06-03 20:13:17 -0700137 }
138}
139
140#[cfg(feature = "printing")]
141mod printing {
142 use super::*;
David Tolnay78612672018-08-31 08:47:41 -0700143
144 use proc_macro2::{Punct, Spacing, TokenStream};
145 use quote::{ToTokens, TokenStreamExt};
David Tolnay63e3dee2017-06-03 20:13:17 -0700146
147 impl ToTokens for Lifetime {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700148 fn to_tokens(&self, tokens: &mut TokenStream) {
David Tolnay78612672018-08-31 08:47:41 -0700149 let mut apostrophe = Punct::new('\'', Spacing::Joint);
150 apostrophe.set_span(self.apostrophe);
151 tokens.append(apostrophe);
Alex Crichtona74a1c82018-05-16 10:20:44 -0700152 self.ident.to_tokens(tokens);
David Tolnay63e3dee2017-06-03 20:13:17 -0700153 }
154 }
155}