blob: c8101eb5eca0748aefe4d32b054ea26a64a57f6b [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 -070018use token::Apostrophe;
19
David Tolnay05658502018-01-07 09:56:37 -080020/// A Rust lifetime: `'a`.
21///
22/// Lifetime names must conform to the following rules:
23///
24/// - Must start with an apostrophe.
25/// - Must not consist of just an apostrophe: `'`.
David Tolnay05658502018-01-07 09:56:37 -080026/// - Character after the apostrophe must be `_` or a Unicode code point with
27/// the XID_Start property.
28/// - All following characters must be Unicode code points with the XID_Continue
29/// property.
David Tolnay461d98e2018-01-07 11:07:19 -080030///
31/// *This type is available if Syn is built with the `"derive"` or `"full"`
32/// feature.*
David Tolnay63e3dee2017-06-03 20:13:17 -070033#[cfg_attr(feature = "extra-traits", derive(Debug))]
Alex Crichtona74a1c82018-05-16 10:20:44 -070034#[derive(Clone)]
David Tolnay63e3dee2017-06-03 20:13:17 -070035pub struct Lifetime {
Alex Crichton131308c2018-05-18 14:00:24 -070036 pub apostrophe: Apostrophe,
37 pub ident: Ident,
David Tolnay63e3dee2017-06-03 20:13:17 -070038}
39
40impl Lifetime {
David Tolnaye2099f32018-03-31 18:42:43 +020041 pub fn new(s: &str, span: Span) -> Self {
David Tolnay63e3dee2017-06-03 20:13:17 -070042 if !s.starts_with('\'') {
David Tolnay51382052017-12-27 13:46:21 -050043 panic!(
David Tolnay76178be2018-07-31 23:06:15 -070044 "lifetime name must start with apostrophe as in \"'a\", got {:?}",
David Tolnay51382052017-12-27 13:46:21 -050045 s
46 );
David Tolnay63e3dee2017-06-03 20:13:17 -070047 }
48
49 if s == "'" {
50 panic!("lifetime name must not be empty");
51 }
52
David Tolnay63e3dee2017-06-03 20:13:17 -070053 fn xid_ok(s: &str) -> bool {
54 let mut chars = s.chars();
55 let first = chars.next().unwrap();
56 if !(UnicodeXID::is_xid_start(first) || first == '_') {
57 return false;
58 }
59 for ch in chars {
60 if !UnicodeXID::is_xid_continue(ch) {
61 return false;
62 }
63 }
64 true
65 }
66
67 if !xid_ok(&s[1..]) {
David Tolnaybb4ca9f2017-12-26 12:28:58 -050068 panic!("{:?} is not a valid lifetime name", s);
David Tolnay63e3dee2017-06-03 20:13:17 -070069 }
70
71 Lifetime {
David Tolnayd228b332018-06-27 23:56:05 -070072 apostrophe: Apostrophe::default(),
Alex Crichtona74a1c82018-05-16 10:20:44 -070073 ident: Ident::new(&s[1..], span),
David Tolnay63e3dee2017-06-03 20:13:17 -070074 }
75 }
76}
77
78impl Display for Lifetime {
79 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
Alex Crichtona74a1c82018-05-16 10:20:44 -070080 "'".fmt(formatter)?;
81 self.ident.fmt(formatter)
David Tolnay63e3dee2017-06-03 20:13:17 -070082 }
83}
84
85impl PartialEq for Lifetime {
86 fn eq(&self, other: &Lifetime) -> bool {
Alex Crichtona74a1c82018-05-16 10:20:44 -070087 self.ident.eq(&other.ident)
David Tolnay63e3dee2017-06-03 20:13:17 -070088 }
89}
90
91impl Eq for Lifetime {}
92
93impl PartialOrd for Lifetime {
94 fn partial_cmp(&self, other: &Lifetime) -> Option<Ordering> {
95 Some(self.cmp(other))
96 }
97}
98
99impl Ord for Lifetime {
100 fn cmp(&self, other: &Lifetime) -> Ordering {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700101 self.ident.cmp(&other.ident)
David Tolnay63e3dee2017-06-03 20:13:17 -0700102 }
103}
104
105impl Hash for Lifetime {
106 fn hash<H: Hasher>(&self, h: &mut H) {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700107 self.ident.hash(h)
David Tolnay63e3dee2017-06-03 20:13:17 -0700108 }
109}
110
111#[cfg(feature = "parsing")]
David Tolnay4fb71232018-08-25 23:14:50 -0400112#[doc(hidden)]
113#[allow(non_snake_case)]
114pub fn Lifetime(marker: lookahead::TokenMarker) -> Lifetime {
115 match marker {}
116}
117
118#[cfg(feature = "parsing")]
David Tolnay63e3dee2017-06-03 20:13:17 -0700119pub mod parsing {
120 use super::*;
David Tolnay7744d9d2018-08-25 22:15:52 -0400121 use parse::{Error, Parse, ParseStream, Result};
122 use synom::ext::IdentExt;
David Tolnay63e3dee2017-06-03 20:13:17 -0700123
David Tolnay7744d9d2018-08-25 22:15:52 -0400124 impl Parse for Lifetime {
125 fn parse(input: ParseStream) -> Result<Self> {
126 Ok(Lifetime {
127 apostrophe: match input.parse() {
128 Ok(apostrophe) => apostrophe,
129 Err(err) => {
130 return Err(Error::new(err.span(), "expected lifetime"));
131 }
132 },
133 ident: input.call(Ident::parse_any2)?,
134 })
Sergio Benitez5680d6a2017-12-29 11:20:29 -0800135 }
David Tolnay63e3dee2017-06-03 20:13:17 -0700136 }
137}
138
139#[cfg(feature = "printing")]
140mod printing {
141 use super::*;
Alex Crichton131308c2018-05-18 14:00:24 -0700142 use proc_macro2::TokenStream;
David Tolnay65fb5662018-05-20 20:02:28 -0700143 use quote::ToTokens;
David Tolnay63e3dee2017-06-03 20:13:17 -0700144
145 impl ToTokens for Lifetime {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700146 fn to_tokens(&self, tokens: &mut TokenStream) {
Alex Crichton131308c2018-05-18 14:00:24 -0700147 self.apostrophe.to_tokens(tokens);
Alex Crichtona74a1c82018-05-16 10:20:44 -0700148 self.ident.to_tokens(tokens);
David Tolnay63e3dee2017-06-03 20:13:17 -0700149 }
150 }
151}