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