blob: 3f093c81d911ba8b9f599645d3ae746f47678a2b [file] [log] [blame]
David Tolnay55535012018-01-05 16:39:23 -08001// Copyright 2018 Syn Developers
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
David Tolnay63e3dee2017-06-03 20:13:17 -07009use std::cmp::Ordering;
10use std::fmt::{self, Display};
11use std::hash::{Hash, Hasher};
12
David Tolnay65fb5662018-05-20 20:02:28 -070013use proc_macro2::{Ident, Span};
David Tolnay63e3dee2017-06-03 20:13:17 -070014use unicode_xid::UnicodeXID;
15
David Tolnay4fb71232018-08-25 23:14:50 -040016#[cfg(feature = "parsing")]
17use lookahead;
Alex Crichton131308c2018-05-18 14:00:24 -070018
David Tolnay05658502018-01-07 09:56:37 -080019/// A Rust lifetime: `'a`.
20///
21/// Lifetime names must conform to the following rules:
22///
23/// - Must start with an apostrophe.
24/// - Must not consist of just an apostrophe: `'`.
David Tolnay05658502018-01-07 09:56:37 -080025/// - Character after the apostrophe must be `_` or a Unicode code point with
26/// the XID_Start property.
27/// - All following characters must be Unicode code points with the XID_Continue
28/// property.
David Tolnay461d98e2018-01-07 11:07:19 -080029///
30/// *This type is available if Syn is built with the `"derive"` or `"full"`
31/// feature.*
David Tolnay63e3dee2017-06-03 20:13:17 -070032#[cfg_attr(feature = "extra-traits", derive(Debug))]
Alex Crichtona74a1c82018-05-16 10:20:44 -070033#[derive(Clone)]
David Tolnay63e3dee2017-06-03 20:13:17 -070034pub struct Lifetime {
David Tolnay78612672018-08-31 08:47:41 -070035 pub apostrophe: Span,
Alex Crichton131308c2018-05-18 14:00:24 -070036 pub ident: Ident,
David Tolnay63e3dee2017-06-03 20:13:17 -070037}
38
39impl Lifetime {
David Tolnay092e21a2018-09-01 16:48:52 -070040 /// # Panics
41 ///
42 /// Panics if the lifetime does not conform to the bulleted rules above.
43 ///
44 /// # Invocation
45 ///
46 /// ```
47 /// # extern crate proc_macro2;
48 /// # extern crate syn;
49 /// #
50 /// # use proc_macro2::Span;
51 /// # use syn::Lifetime;
52 /// #
53 /// # fn f() -> Lifetime {
54 /// Lifetime::new("'a", Span::call_site())
55 /// # }
56 /// ```
57 pub fn new(symbol: &str, span: Span) -> Self {
58 if !symbol.starts_with('\'') {
David Tolnay51382052017-12-27 13:46:21 -050059 panic!(
David Tolnay76178be2018-07-31 23:06:15 -070060 "lifetime name must start with apostrophe as in \"'a\", got {:?}",
David Tolnay092e21a2018-09-01 16:48:52 -070061 symbol
David Tolnay51382052017-12-27 13:46:21 -050062 );
David Tolnay63e3dee2017-06-03 20:13:17 -070063 }
64
David Tolnay092e21a2018-09-01 16:48:52 -070065 if symbol == "'" {
David Tolnay63e3dee2017-06-03 20:13:17 -070066 panic!("lifetime name must not be empty");
67 }
68
David Tolnay092e21a2018-09-01 16:48:52 -070069 fn xid_ok(symbol: &str) -> bool {
70 let mut chars = symbol.chars();
David Tolnay63e3dee2017-06-03 20:13:17 -070071 let first = chars.next().unwrap();
72 if !(UnicodeXID::is_xid_start(first) || first == '_') {
73 return false;
74 }
75 for ch in chars {
76 if !UnicodeXID::is_xid_continue(ch) {
77 return false;
78 }
79 }
80 true
81 }
82
David Tolnay092e21a2018-09-01 16:48:52 -070083 if !xid_ok(&symbol[1..]) {
84 panic!("{:?} is not a valid lifetime name", symbol);
David Tolnay63e3dee2017-06-03 20:13:17 -070085 }
86
87 Lifetime {
David Tolnaya19a8192018-09-01 02:36:16 -070088 apostrophe: span,
David Tolnay092e21a2018-09-01 16:48:52 -070089 ident: Ident::new(&symbol[1..], span),
David Tolnay63e3dee2017-06-03 20:13:17 -070090 }
91 }
92}
93
94impl Display for Lifetime {
95 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
Alex Crichtona74a1c82018-05-16 10:20:44 -070096 "'".fmt(formatter)?;
97 self.ident.fmt(formatter)
David Tolnay63e3dee2017-06-03 20:13:17 -070098 }
99}
100
101impl PartialEq for Lifetime {
102 fn eq(&self, other: &Lifetime) -> bool {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700103 self.ident.eq(&other.ident)
David Tolnay63e3dee2017-06-03 20:13:17 -0700104 }
105}
106
107impl Eq for Lifetime {}
108
109impl PartialOrd for Lifetime {
110 fn partial_cmp(&self, other: &Lifetime) -> Option<Ordering> {
111 Some(self.cmp(other))
112 }
113}
114
115impl Ord for Lifetime {
116 fn cmp(&self, other: &Lifetime) -> Ordering {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700117 self.ident.cmp(&other.ident)
David Tolnay63e3dee2017-06-03 20:13:17 -0700118 }
119}
120
121impl Hash for Lifetime {
122 fn hash<H: Hasher>(&self, h: &mut H) {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700123 self.ident.hash(h)
David Tolnay63e3dee2017-06-03 20:13:17 -0700124 }
125}
126
127#[cfg(feature = "parsing")]
David Tolnay4fb71232018-08-25 23:14:50 -0400128#[doc(hidden)]
129#[allow(non_snake_case)]
130pub fn Lifetime(marker: lookahead::TokenMarker) -> Lifetime {
131 match marker {}
132}
133
134#[cfg(feature = "parsing")]
David Tolnay63e3dee2017-06-03 20:13:17 -0700135pub mod parsing {
136 use super::*;
David Tolnay94d304f2018-08-30 23:43:53 -0700137
David Tolnay78612672018-08-31 08:47:41 -0700138 use parse::{Parse, ParseStream, Result};
David Tolnay63e3dee2017-06-03 20:13:17 -0700139
David Tolnay7744d9d2018-08-25 22:15:52 -0400140 impl Parse for Lifetime {
141 fn parse(input: ParseStream) -> Result<Self> {
David Tolnay66cb0c42018-08-31 09:01:30 -0700142 input.step(|cursor| {
143 cursor
144 .lifetime()
145 .ok_or_else(|| cursor.error("expected lifetime"))
David Tolnay7744d9d2018-08-25 22:15:52 -0400146 })
Sergio Benitez5680d6a2017-12-29 11:20:29 -0800147 }
David Tolnay63e3dee2017-06-03 20:13:17 -0700148 }
149}
150
151#[cfg(feature = "printing")]
152mod printing {
153 use super::*;
David Tolnay78612672018-08-31 08:47:41 -0700154
155 use proc_macro2::{Punct, Spacing, TokenStream};
156 use quote::{ToTokens, TokenStreamExt};
David Tolnay63e3dee2017-06-03 20:13:17 -0700157
158 impl ToTokens for Lifetime {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700159 fn to_tokens(&self, tokens: &mut TokenStream) {
David Tolnay78612672018-08-31 08:47:41 -0700160 let mut apostrophe = Punct::new('\'', Spacing::Joint);
161 apostrophe.set_span(self.apostrophe);
162 tokens.append(apostrophe);
Alex Crichtona74a1c82018-05-16 10:20:44 -0700163 self.ident.to_tokens(tokens);
David Tolnay63e3dee2017-06-03 20:13:17 -0700164 }
165 }
166}