1 // SPDX-License-Identifier: Apache-2.0 OR MIT 2 3 #[cfg(feature = "parsing")] 4 use crate::lookahead; 5 6 pub use proc_macro2::Ident; 7 8 #[cfg(feature = "parsing")] 9 pub_if_not_doc! { 10 #[doc(hidden)] 11 #[allow(non_snake_case)] 12 pub fn Ident(marker: lookahead::TokenMarker) -> Ident { 13 match marker {} 14 } 15 } 16 17 macro_rules! ident_from_token { 18 ($token:ident) => { 19 impl From<Token![$token]> for Ident { 20 fn from(token: Token![$token]) -> Ident { 21 Ident::new(stringify!($token), token.span) 22 } 23 } 24 }; 25 } 26 27 ident_from_token!(self); 28 ident_from_token!(Self); 29 ident_from_token!(super); 30 ident_from_token!(crate); 31 ident_from_token!(extern); 32 33 impl From<Token![_]> for Ident { 34 fn from(token: Token![_]) -> Ident { 35 Ident::new("_", token.span) 36 } 37 } 38 39 pub(crate) fn xid_ok(symbol: &str) -> bool { 40 let mut chars = symbol.chars(); 41 let first = chars.next().unwrap(); 42 if !(first == '_' || unicode_ident::is_xid_start(first)) { 43 return false; 44 } 45 for ch in chars { 46 if !unicode_ident::is_xid_continue(ch) { 47 return false; 48 } 49 } 50 true 51 } 52 53 #[cfg(feature = "parsing")] 54 mod parsing { 55 use crate::buffer::Cursor; 56 use crate::error::Result; 57 use crate::parse::{Parse, ParseStream}; 58 use crate::token::Token; 59 use proc_macro2::Ident; 60 61 fn accept_as_ident(ident: &Ident) -> bool { 62 match ident.to_string().as_str() { 63 "_" | 64 // Based on https://doc.rust-lang.org/1.65.0/reference/keywords.html 65 "abstract" | "as" | "async" | "await" | "become" | "box" | "break" | 66 "const" | "continue" | "crate" | "do" | "dyn" | "else" | "enum" | 67 "extern" | "false" | "final" | "fn" | "for" | "if" | "impl" | "in" | 68 "let" | "loop" | "macro" | "match" | "mod" | "move" | "mut" | 69 "override" | "priv" | "pub" | "ref" | "return" | "Self" | "self" | 70 "static" | "struct" | "super" | "trait" | "true" | "try" | "type" | 71 "typeof" | "unsafe" | "unsized" | "use" | "virtual" | "where" | 72 "while" | "yield" => false, 73 _ => true, 74 } 75 } 76 77 #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] 78 impl Parse for Ident { 79 fn parse(input: ParseStream) -> Result<Self> { 80 input.step(|cursor| { 81 if let Some((ident, rest)) = cursor.ident() { 82 if accept_as_ident(&ident) { 83 Ok((ident, rest)) 84 } else { 85 Err(cursor.error(format_args!( 86 "expected identifier, found keyword `{}`", 87 ident, 88 ))) 89 } 90 } else { 91 Err(cursor.error("expected identifier")) 92 } 93 }) 94 } 95 } 96 97 impl Token for Ident { 98 fn peek(cursor: Cursor) -> bool { 99 if let Some((ident, _rest)) = cursor.ident() { 100 accept_as_ident(&ident) 101 } else { 102 false 103 } 104 } 105 106 fn display() -> &'static str { 107 "identifier" 108 } 109 } 110 } 111