1- use std:: hash:: Hash ;
2-
31use proc_macro2:: { Span , TokenStream } ;
42use proc_macro_error:: emit_error;
53use quote:: { quote, ToTokens } ;
@@ -46,7 +44,9 @@ pub struct KebabIdent {
4644}
4745
4846impl KebabIdent {
49- /// Returns a reference to the repr of this [`KebabIdent`].
47+ /// Returns the string representation of the identifier, in kebab-case.
48+ ///
49+ /// This is not a raw identifier, i.e. it does not start with "r#".
5050 pub fn repr ( & self ) -> & str { self . repr . as_ref ( ) }
5151
5252 /// Returns the span of this [`KebabIdent`].
@@ -94,13 +94,15 @@ impl KebabIdent {
9494 /// The span will only be the first 'section' on stable, but correctly
9595 /// covers the full ident on nightly. See [`KebabIdent::span`] for more
9696 /// details.
97+ ///
98+ /// The ident will also be a raw identifier.
9799 pub fn to_snake_ident ( & self ) -> syn:: Ident {
98100 let snake_string = self . repr ( ) . replace ( '-' , "_" ) ;
99101 // This will always be valid as the first 'section' must be a `-` or rust ident,
100102 // which means it starts with `_` or another valid identifier beginning. The int
101103 // literals within the ident (e.g. between `-`s, like `blue-100`) are allowed
102104 // since the ident does not start with a number.
103- syn:: Ident :: new ( & snake_string, self . span ( ) )
105+ syn:: Ident :: new_raw ( & snake_string, self . span ( ) )
104106 }
105107}
106108
@@ -111,7 +113,9 @@ impl Parse for KebabIdent {
111113
112114 // Start with `-` or letter.
113115 if let Some ( ident) = rollback_err ( input, syn:: Ident :: parse_any) {
114- repr. push_str ( & ident. to_string ( ) ) ;
116+ // only store the non-raw representation: in expansion,
117+ // this should expand to a raw ident.
118+ repr. push_str ( & ident. unraw ( ) . to_string ( ) ) ;
115119 spans. push ( ident. span ( ) ) ;
116120 } else if let Some ( dash) = rollback_err ( input, <Token ! [ -] >:: parse) {
117121 repr. push ( '-' ) ;
@@ -142,7 +146,11 @@ impl Parse for KebabIdent {
142146
143147 // add ident or number
144148 if let Some ( ident) = rollback_err ( input, syn:: Ident :: parse_any) {
145- repr. push_str ( & ident. to_string ( ) ) ;
149+ let unraw = ident. unraw ( ) ;
150+ if ident != unraw {
151+ emit_error ! ( ident. span( ) , "invalid raw identifier within kebab-ident" ) ;
152+ }
153+ repr. push_str ( & unraw. to_string ( ) ) ;
146154 spans. push ( ident. span ( ) ) ;
147155 } else if let Some ( int) = rollback_err ( input, syn:: LitInt :: parse) {
148156 repr. push_str ( & int. to_string ( ) ) ;
@@ -160,7 +168,7 @@ impl From<proc_macro2::Ident> for KebabIdent {
160168 // repr is not empty as `proc_macro2::Ident` must be a valid Rust identifier,
161169 // and "" is not.
162170 Self {
163- repr : value. to_string ( ) ,
171+ repr : value. unraw ( ) . to_string ( ) ,
164172 spans : vec ! [ value. span( ) ] ,
165173 }
166174 }
@@ -174,10 +182,6 @@ impl PartialEq for KebabIdent {
174182
175183impl Eq for KebabIdent { }
176184
177- impl Hash for KebabIdent {
178- fn hash < H : std:: hash:: Hasher > ( & self , state : & mut H ) { self . repr . hash ( state) ; }
179- }
180-
181185// Parse either a kebab-case ident or a str literal.
182186#[ derive( Clone ) ]
183187pub enum KebabIdentOrStr {
@@ -250,6 +254,8 @@ impl Parse for BracedKebabIdent {
250254
251255#[ cfg( test) ]
252256mod tests {
257+ use std:: iter;
258+
253259 use super :: KebabIdent ;
254260
255261 #[ test]
@@ -294,4 +300,14 @@ mod tests {
294300 assert_eq ! ( ident. repr( ) , stream. replace( ' ' , "" ) ) ;
295301 }
296302 }
303+
304+ #[ test]
305+ fn raw ( ) {
306+ let raws = [ "r#move" , "move" , "r#some-thing" ] ;
307+ let results = [ "move" , "move" , "some-thing" ] ;
308+ for ( stream, res) in iter:: zip ( raws, results) {
309+ let ident = syn:: parse_str :: < KebabIdent > ( stream) . unwrap ( ) ;
310+ assert_eq ! ( ident. repr( ) , res) ;
311+ }
312+ }
297313}
0 commit comments