blob: 321336d90514356eda6c85dfc81d0aa01d506c6d [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 Tolnay98942562017-12-26 21:24:35 -050013use proc_macro2::{Span, Term};
David Tolnay63e3dee2017-06-03 20:13:17 -070014use unicode_xid::UnicodeXID;
15
David Tolnay05658502018-01-07 09:56:37 -080016/// A Rust lifetime: `'a`.
17///
18/// Lifetime names must conform to the following rules:
19///
20/// - Must start with an apostrophe.
21/// - Must not consist of just an apostrophe: `'`.
David Tolnay05658502018-01-07 09:56:37 -080022/// - Character after the apostrophe must be `_` or a Unicode code point with
23/// the XID_Start property.
24/// - All following characters must be Unicode code points with the XID_Continue
25/// property.
David Tolnay461d98e2018-01-07 11:07:19 -080026///
27/// *This type is available if Syn is built with the `"derive"` or `"full"`
28/// feature.*
David Tolnay63e3dee2017-06-03 20:13:17 -070029#[cfg_attr(feature = "extra-traits", derive(Debug))]
David Tolnay42d0bc52018-01-09 14:36:04 -080030#[derive(Copy, Clone)]
David Tolnay63e3dee2017-06-03 20:13:17 -070031pub struct Lifetime {
David Tolnay73c98de2017-12-31 15:56:56 -050032 term: Term,
David Tolnay63e3dee2017-06-03 20:13:17 -070033}
34
35impl Lifetime {
Alex Crichton9a4dca22018-03-28 06:32:19 -070036 pub fn new(term: Term) -> Self {
David Tolnay73c98de2017-12-31 15:56:56 -050037 let s = term.as_str();
David Tolnay63e3dee2017-06-03 20:13:17 -070038
39 if !s.starts_with('\'') {
David Tolnay51382052017-12-27 13:46:21 -050040 panic!(
41 "lifetime name must start with apostrophe as in \"'a\", \
42 got {:?}",
43 s
44 );
David Tolnay63e3dee2017-06-03 20:13:17 -070045 }
46
47 if s == "'" {
48 panic!("lifetime name must not be empty");
49 }
50
David Tolnay63e3dee2017-06-03 20:13:17 -070051 fn xid_ok(s: &str) -> bool {
52 let mut chars = s.chars();
53 let first = chars.next().unwrap();
54 if !(UnicodeXID::is_xid_start(first) || first == '_') {
55 return false;
56 }
57 for ch in chars {
58 if !UnicodeXID::is_xid_continue(ch) {
59 return false;
60 }
61 }
62 true
63 }
64
65 if !xid_ok(&s[1..]) {
David Tolnaybb4ca9f2017-12-26 12:28:58 -050066 panic!("{:?} is not a valid lifetime name", s);
David Tolnay63e3dee2017-06-03 20:13:17 -070067 }
68
69 Lifetime {
David Tolnay73c98de2017-12-31 15:56:56 -050070 term: term,
David Tolnay63e3dee2017-06-03 20:13:17 -070071 }
72 }
Alex Crichton9a4dca22018-03-28 06:32:19 -070073
74 pub fn span(&self) -> Span {
75 self.term.span()
76 }
77
78 pub fn set_span(&mut self, span: Span) {
79 self.term.set_span(span);
80 }
David Tolnay63e3dee2017-06-03 20:13:17 -070081}
82
83impl Display for Lifetime {
84 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
David Tolnay73c98de2017-12-31 15:56:56 -050085 self.term.as_str().fmt(formatter)
David Tolnay63e3dee2017-06-03 20:13:17 -070086 }
87}
88
89impl PartialEq for Lifetime {
90 fn eq(&self, other: &Lifetime) -> bool {
David Tolnay73c98de2017-12-31 15:56:56 -050091 self.term.as_str() == other.term.as_str()
David Tolnay63e3dee2017-06-03 20:13:17 -070092 }
93}
94
95impl Eq for Lifetime {}
96
97impl PartialOrd for Lifetime {
98 fn partial_cmp(&self, other: &Lifetime) -> Option<Ordering> {
99 Some(self.cmp(other))
100 }
101}
102
103impl Ord for Lifetime {
104 fn cmp(&self, other: &Lifetime) -> Ordering {
David Tolnay73c98de2017-12-31 15:56:56 -0500105 self.term.as_str().cmp(other.term.as_str())
David Tolnay63e3dee2017-06-03 20:13:17 -0700106 }
107}
108
109impl Hash for Lifetime {
110 fn hash<H: Hasher>(&self, h: &mut H) {
David Tolnay73c98de2017-12-31 15:56:56 -0500111 self.term.as_str().hash(h)
David Tolnay63e3dee2017-06-03 20:13:17 -0700112 }
113}
114
115#[cfg(feature = "parsing")]
116pub mod parsing {
117 use super::*;
David Tolnayc5ab8c62017-12-26 16:43:39 -0500118 use synom::Synom;
David Tolnaydfc886b2018-01-06 08:03:09 -0800119 use buffer::Cursor;
David Tolnay203557a2017-12-27 23:59:33 -0500120 use parse_error;
121 use synom::PResult;
David Tolnay63e3dee2017-06-03 20:13:17 -0700122
123 impl Synom for Lifetime {
124 fn parse(input: Cursor) -> PResult<Self> {
Alex Crichton9a4dca22018-03-28 06:32:19 -0700125 let (term, rest) = match input.term() {
David Tolnay73c98de2017-12-31 15:56:56 -0500126 Some(term) => term,
David Tolnay63e3dee2017-06-03 20:13:17 -0700127 _ => return parse_error(),
128 };
David Tolnay73c98de2017-12-31 15:56:56 -0500129 if !term.as_str().starts_with('\'') {
David Tolnay63e3dee2017-06-03 20:13:17 -0700130 return parse_error();
131 }
132
David Tolnay51382052017-12-27 13:46:21 -0500133 Ok((
David Tolnay51382052017-12-27 13:46:21 -0500134 Lifetime {
David Tolnay73c98de2017-12-31 15:56:56 -0500135 term: term,
David Tolnay51382052017-12-27 13:46:21 -0500136 },
David Tolnayf4aa6b42017-12-31 16:40:33 -0500137 rest,
David Tolnay51382052017-12-27 13:46:21 -0500138 ))
David Tolnay63e3dee2017-06-03 20:13:17 -0700139 }
Sergio Benitez5680d6a2017-12-29 11:20:29 -0800140
141 fn description() -> Option<&'static str> {
142 Some("lifetime")
143 }
David Tolnay63e3dee2017-06-03 20:13:17 -0700144 }
145}
146
147#[cfg(feature = "printing")]
148mod printing {
149 use super::*;
David Tolnay51382052017-12-27 13:46:21 -0500150 use quote::{ToTokens, Tokens};
David Tolnay63e3dee2017-06-03 20:13:17 -0700151
152 impl ToTokens for Lifetime {
153 fn to_tokens(&self, tokens: &mut Tokens) {
Alex Crichton9a4dca22018-03-28 06:32:19 -0700154 self.term.to_tokens(tokens);
David Tolnay63e3dee2017-06-03 20:13:17 -0700155 }
156 }
157}