blob: 6e4cce1a16d5f8a9eb957410c3a22385fed793d9 [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
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))]
Alex Crichtona74a1c82018-05-16 10:20:44 -070030#[derive(Clone)]
David Tolnay63e3dee2017-06-03 20:13:17 -070031pub struct Lifetime {
Alex Crichtona74a1c82018-05-16 10:20:44 -070032 ident: Ident,
David Tolnay63e3dee2017-06-03 20:13:17 -070033}
34
35impl Lifetime {
David Tolnaye2099f32018-03-31 18:42:43 +020036 pub fn new(s: &str, span: Span) -> Self {
David Tolnay63e3dee2017-06-03 20:13:17 -070037 if !s.starts_with('\'') {
David Tolnay51382052017-12-27 13:46:21 -050038 panic!(
39 "lifetime name must start with apostrophe as in \"'a\", \
40 got {:?}",
41 s
42 );
David Tolnay63e3dee2017-06-03 20:13:17 -070043 }
44
45 if s == "'" {
46 panic!("lifetime name must not be empty");
47 }
48
David Tolnay63e3dee2017-06-03 20:13:17 -070049 fn xid_ok(s: &str) -> bool {
50 let mut chars = s.chars();
51 let first = chars.next().unwrap();
52 if !(UnicodeXID::is_xid_start(first) || first == '_') {
53 return false;
54 }
55 for ch in chars {
56 if !UnicodeXID::is_xid_continue(ch) {
57 return false;
58 }
59 }
60 true
61 }
62
63 if !xid_ok(&s[1..]) {
David Tolnaybb4ca9f2017-12-26 12:28:58 -050064 panic!("{:?} is not a valid lifetime name", s);
David Tolnay63e3dee2017-06-03 20:13:17 -070065 }
66
67 Lifetime {
Alex Crichtona74a1c82018-05-16 10:20:44 -070068 ident: Ident::new(&s[1..], span),
David Tolnay63e3dee2017-06-03 20:13:17 -070069 }
70 }
Alex Crichton9a4dca22018-03-28 06:32:19 -070071
72 pub fn span(&self) -> Span {
Alex Crichtona74a1c82018-05-16 10:20:44 -070073 self.ident.span()
Alex Crichton9a4dca22018-03-28 06:32:19 -070074 }
75
76 pub fn set_span(&mut self, span: Span) {
Alex Crichtona74a1c82018-05-16 10:20:44 -070077 self.ident.set_span(span);
Alex Crichton9a4dca22018-03-28 06:32:19 -070078 }
David Tolnay63e3dee2017-06-03 20:13:17 -070079}
80
81impl Display for Lifetime {
82 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
Alex Crichtona74a1c82018-05-16 10:20:44 -070083 "'".fmt(formatter)?;
84 self.ident.fmt(formatter)
David Tolnay63e3dee2017-06-03 20:13:17 -070085 }
86}
87
88impl PartialEq for Lifetime {
89 fn eq(&self, other: &Lifetime) -> bool {
Alex Crichtona74a1c82018-05-16 10:20:44 -070090 self.ident.eq(&other.ident)
David Tolnay63e3dee2017-06-03 20:13:17 -070091 }
92}
93
94impl Eq for Lifetime {}
95
96impl PartialOrd for Lifetime {
97 fn partial_cmp(&self, other: &Lifetime) -> Option<Ordering> {
98 Some(self.cmp(other))
99 }
100}
101
102impl Ord for Lifetime {
103 fn cmp(&self, other: &Lifetime) -> Ordering {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700104 self.ident.cmp(&other.ident)
David Tolnay63e3dee2017-06-03 20:13:17 -0700105 }
106}
107
108impl Hash for Lifetime {
109 fn hash<H: Hasher>(&self, h: &mut H) {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700110 self.ident.hash(h)
David Tolnay63e3dee2017-06-03 20:13:17 -0700111 }
112}
113
114#[cfg(feature = "parsing")]
115pub mod parsing {
116 use super::*;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700117 use proc_macro2::Spacing;
David Tolnaydfc886b2018-01-06 08:03:09 -0800118 use buffer::Cursor;
David Tolnay203557a2017-12-27 23:59:33 -0500119 use parse_error;
120 use synom::PResult;
David Tolnay94d2b792018-04-29 12:26:10 -0700121 use synom::Synom;
David Tolnay63e3dee2017-06-03 20:13:17 -0700122
123 impl Synom for Lifetime {
124 fn parse(input: Cursor) -> PResult<Self> {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700125 let rest = match input.op() {
126 Some((op, rest)) => {
127 if op.as_char() == '\'' && op.spacing() == Spacing::Joint {
128 rest
129 } else {
130 return parse_error()
131 }
132 }
David Tolnay63e3dee2017-06-03 20:13:17 -0700133 _ => return parse_error(),
134 };
Alex Crichtona74a1c82018-05-16 10:20:44 -0700135 let (ident, rest) = match rest.term() {
136 Some(pair) => pair,
137 None => return parse_error(),
138 };
David Tolnay63e3dee2017-06-03 20:13:17 -0700139
Alex Crichtona74a1c82018-05-16 10:20:44 -0700140 Ok((Lifetime { ident: ident }, rest))
David Tolnay63e3dee2017-06-03 20:13:17 -0700141 }
Sergio Benitez5680d6a2017-12-29 11:20:29 -0800142
143 fn description() -> Option<&'static str> {
144 Some("lifetime")
145 }
David Tolnay63e3dee2017-06-03 20:13:17 -0700146 }
147}
148
149#[cfg(feature = "printing")]
150mod printing {
151 use super::*;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700152 use quote::{ToTokens, TokenStreamExt};
153 use proc_macro2::{TokenStream, Punct, Spacing};
David Tolnay63e3dee2017-06-03 20:13:17 -0700154
155 impl ToTokens for Lifetime {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700156 fn to_tokens(&self, tokens: &mut TokenStream) {
157 tokens.append(Punct::new('\'', Spacing::Joint));
158 self.ident.to_tokens(tokens);
David Tolnay63e3dee2017-06-03 20:13:17 -0700159 }
160 }
161}