1 use proc_macro2::{Ident, Span}; 2 use std::cmp::Ordering; 3 use std::fmt::{self, Display}; 4 use std::hash::{Hash, Hasher}; 5 6 #[cfg(feature = "parsing")] 7 use crate::lookahead; 8 9 /// A Rust lifetime: `'a`. 10 /// 11 /// Lifetime names must conform to the following rules: 12 /// 13 /// - Must start with an apostrophe. 14 /// - Must not consist of just an apostrophe: `'`. 15 /// - Character after the apostrophe must be `_` or a Unicode code point with 16 /// the XID_Start property. 17 /// - All following characters must be Unicode code points with the XID_Continue 18 /// property. 19 pub struct Lifetime { 20 pub apostrophe: Span, 21 pub ident: Ident, 22 } 23 24 impl Lifetime { 25 /// # Panics 26 /// 27 /// Panics if the lifetime does not conform to the bulleted rules above. 28 /// 29 /// # Invocation 30 /// 31 /// ``` 32 /// # use proc_macro2::Span; 33 /// # use syn::Lifetime; 34 /// # 35 /// # fn f() -> Lifetime { 36 /// Lifetime::new("'a", Span::call_site()) 37 /// # } 38 /// ``` newnull39 pub fn new(symbol: &str, span: Span) -> Self { 40 if !symbol.starts_with('\'') { 41 panic!( 42 "lifetime name must start with apostrophe as in \"'a\", got {:?}", 43 symbol 44 ); 45 } 46 47 if symbol == "'" { 48 panic!("lifetime name must not be empty"); 49 } 50 51 if !crate::ident::xid_ok(&symbol[1..]) { 52 panic!("{:?} is not a valid lifetime name", symbol); 53 } 54 55 Lifetime { 56 apostrophe: span, 57 ident: Ident::new(&symbol[1..], span), 58 } 59 } 60 spannull61 pub fn span(&self) -> Span { 62 self.apostrophe 63 .join(self.ident.span()) 64 .unwrap_or(self.apostrophe) 65 } 66 set_spannull67 pub fn set_span(&mut self, span: Span) { 68 self.apostrophe = span; 69 self.ident.set_span(span); 70 } 71 } 72 73 impl Display for Lifetime { fmtnull74 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 75 "'".fmt(formatter)?; 76 self.ident.fmt(formatter) 77 } 78 } 79 80 impl Clone for Lifetime { clonenull81 fn clone(&self) -> Self { 82 Lifetime { 83 apostrophe: self.apostrophe, 84 ident: self.ident.clone(), 85 } 86 } 87 } 88 89 impl PartialEq for Lifetime { eqnull90 fn eq(&self, other: &Lifetime) -> bool { 91 self.ident.eq(&other.ident) 92 } 93 } 94 95 impl Eq for Lifetime {} 96 97 impl PartialOrd for Lifetime { partial_cmpnull98 fn partial_cmp(&self, other: &Lifetime) -> Option<Ordering> { 99 Some(self.cmp(other)) 100 } 101 } 102 103 impl Ord for Lifetime { cmpnull104 fn cmp(&self, other: &Lifetime) -> Ordering { 105 self.ident.cmp(&other.ident) 106 } 107 } 108 109 impl Hash for Lifetime { hashnull110 fn hash<H: Hasher>(&self, h: &mut H) { 111 self.ident.hash(h); 112 } 113 } 114 115 #[cfg(feature = "parsing")] 116 pub_if_not_doc! { 117 #[doc(hidden)] 118 #[allow(non_snake_case)] Lifetimenull119 pub fn Lifetime(marker: lookahead::TokenMarker) -> Lifetime { 120 match marker {} 121 } 122 } 123 124 #[cfg(feature = "parsing")] 125 pub(crate) mod parsing { 126 use super::*; 127 use crate::parse::{Parse, ParseStream, Result}; 128 129 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] 130 impl Parse for Lifetime { parsenull131 fn parse(input: ParseStream) -> Result<Self> { 132 input.step(|cursor| { 133 cursor 134 .lifetime() 135 .ok_or_else(|| cursor.error("expected lifetime")) 136 }) 137 } 138 } 139 } 140 141 #[cfg(feature = "printing")] 142 mod printing { 143 use super::*; 144 use proc_macro2::{Punct, Spacing, TokenStream}; 145 use quote::{ToTokens, TokenStreamExt}; 146 147 #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] 148 impl ToTokens for Lifetime { to_tokensnull149 fn to_tokens(&self, tokens: &mut TokenStream) { 150 let mut apostrophe = Punct::new('\'', Spacing::Joint); 151 apostrophe.set_span(self.apostrophe); 152 tokens.append(apostrophe); 153 self.ident.to_tokens(tokens); 154 } 155 } 156 } 157