blob: 9a2bec4ad7ed6927389a68ed90da11e1d20e0bb8 [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
Alex Crichtona74a1c82018-05-16 10:20:44 -070013use proc_macro2::{Span, Ident};
David Tolnay63e3dee2017-06-03 20:13:17 -070014use unicode_xid::UnicodeXID;
15
Alex Crichton131308c2018-05-18 14:00:24 -070016use token::Apostrophe;
17
David Tolnay05658502018-01-07 09:56:37 -080018/// A Rust lifetime: `'a`.
19///
20/// Lifetime names must conform to the following rules:
21///
22/// - Must start with an apostrophe.
23/// - Must not consist of just an apostrophe: `'`.
David Tolnay05658502018-01-07 09:56:37 -080024/// - Character after the apostrophe must be `_` or a Unicode code point with
25/// the XID_Start property.
26/// - All following characters must be Unicode code points with the XID_Continue
27/// property.
David Tolnay461d98e2018-01-07 11:07:19 -080028///
29/// *This type is available if Syn is built with the `"derive"` or `"full"`
30/// feature.*
David Tolnay63e3dee2017-06-03 20:13:17 -070031#[cfg_attr(feature = "extra-traits", derive(Debug))]
Alex Crichtona74a1c82018-05-16 10:20:44 -070032#[derive(Clone)]
David Tolnay63e3dee2017-06-03 20:13:17 -070033pub struct Lifetime {
Alex Crichton131308c2018-05-18 14:00:24 -070034 pub apostrophe: Apostrophe,
35 pub ident: Ident,
David Tolnay63e3dee2017-06-03 20:13:17 -070036}
37
38impl Lifetime {
David Tolnaye2099f32018-03-31 18:42:43 +020039 pub fn new(s: &str, span: Span) -> Self {
David Tolnay63e3dee2017-06-03 20:13:17 -070040 if !s.starts_with('\'') {
David Tolnay51382052017-12-27 13:46:21 -050041 panic!(
42 "lifetime name must start with apostrophe as in \"'a\", \
43 got {:?}",
44 s
45 );
David Tolnay63e3dee2017-06-03 20:13:17 -070046 }
47
48 if s == "'" {
49 panic!("lifetime name must not be empty");
50 }
51
David Tolnay63e3dee2017-06-03 20:13:17 -070052 fn xid_ok(s: &str) -> bool {
53 let mut chars = s.chars();
54 let first = chars.next().unwrap();
55 if !(UnicodeXID::is_xid_start(first) || first == '_') {
56 return false;
57 }
58 for ch in chars {
59 if !UnicodeXID::is_xid_continue(ch) {
60 return false;
61 }
62 }
63 true
64 }
65
66 if !xid_ok(&s[1..]) {
David Tolnaybb4ca9f2017-12-26 12:28:58 -050067 panic!("{:?} is not a valid lifetime name", s);
David Tolnay63e3dee2017-06-03 20:13:17 -070068 }
69
70 Lifetime {
Alex Crichton131308c2018-05-18 14:00:24 -070071 apostrophe: Default::default(),
Alex Crichtona74a1c82018-05-16 10:20:44 -070072 ident: Ident::new(&s[1..], span),
David Tolnay63e3dee2017-06-03 20:13:17 -070073 }
74 }
75}
76
77impl Display for Lifetime {
78 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
Alex Crichtona74a1c82018-05-16 10:20:44 -070079 "'".fmt(formatter)?;
80 self.ident.fmt(formatter)
David Tolnay63e3dee2017-06-03 20:13:17 -070081 }
82}
83
84impl PartialEq for Lifetime {
85 fn eq(&self, other: &Lifetime) -> bool {
Alex Crichtona74a1c82018-05-16 10:20:44 -070086 self.ident.eq(&other.ident)
David Tolnay63e3dee2017-06-03 20:13:17 -070087 }
88}
89
90impl Eq for Lifetime {}
91
92impl PartialOrd for Lifetime {
93 fn partial_cmp(&self, other: &Lifetime) -> Option<Ordering> {
94 Some(self.cmp(other))
95 }
96}
97
98impl Ord for Lifetime {
99 fn cmp(&self, other: &Lifetime) -> Ordering {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700100 self.ident.cmp(&other.ident)
David Tolnay63e3dee2017-06-03 20:13:17 -0700101 }
102}
103
104impl Hash for Lifetime {
105 fn hash<H: Hasher>(&self, h: &mut H) {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700106 self.ident.hash(h)
David Tolnay63e3dee2017-06-03 20:13:17 -0700107 }
108}
109
110#[cfg(feature = "parsing")]
111pub mod parsing {
112 use super::*;
David Tolnaydfc886b2018-01-06 08:03:09 -0800113 use buffer::Cursor;
David Tolnay203557a2017-12-27 23:59:33 -0500114 use parse_error;
115 use synom::PResult;
David Tolnay94d2b792018-04-29 12:26:10 -0700116 use synom::Synom;
David Tolnay63e3dee2017-06-03 20:13:17 -0700117
118 impl Synom for Lifetime {
119 fn parse(input: Cursor) -> PResult<Self> {
Alex Crichton131308c2018-05-18 14:00:24 -0700120 let (apostrophe, rest) = Apostrophe::parse(input)?;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700121 let (ident, rest) = match rest.term() {
122 Some(pair) => pair,
123 None => return parse_error(),
124 };
David Tolnay63e3dee2017-06-03 20:13:17 -0700125
Alex Crichton131308c2018-05-18 14:00:24 -0700126 let ret = Lifetime {
127 ident: ident,
128 apostrophe: apostrophe,
129 };
130 Ok((ret, rest))
David Tolnay63e3dee2017-06-03 20:13:17 -0700131 }
Sergio Benitez5680d6a2017-12-29 11:20:29 -0800132
133 fn description() -> Option<&'static str> {
134 Some("lifetime")
135 }
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 quote::ToTokens;
143 use proc_macro2::TokenStream;
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}