@@ -164,8 +164,14 @@ mod imp;
164164#[ cfg( span_locations) ]  
165165mod  location; 
166166
167+ #[ cfg( procmacro2_semver_exempt) ]  
168+ #[ allow( dead_code) ]  
169+ mod  rustc_literal_escaper; 
170+ 
167171use  crate :: extra:: DelimSpan ; 
168172use  crate :: marker:: { ProcMacroAutoTraits ,  MARKER } ; 
173+ #[ cfg( procmacro2_semver_exempt) ]  
174+ use  crate :: rustc_literal_escaper:: MixedUnit ; 
169175use  core:: cmp:: Ordering ; 
170176use  core:: fmt:: { self ,  Debug ,  Display } ; 
171177use  core:: hash:: { Hash ,  Hasher } ; 
@@ -182,6 +188,10 @@ use std::path::PathBuf;
182188#[ cfg_attr( docsrs,  doc( cfg( feature = "span-locations" ) ) ) ]  
183189pub  use  crate :: location:: LineColumn ; 
184190
191+ #[ cfg( procmacro2_semver_exempt) ]  
192+ #[ cfg_attr( docsrs,  doc( cfg( procmacro2_semver_exempt) ) ) ]  
193+ pub  use  crate :: rustc_literal_escaper:: EscapeError ; 
194+ 
185195/// An abstract stream of tokens, or more concretely a sequence of token trees. 
186196/// 
187197/// This type provides interfaces for iterating over token trees and for 
@@ -1263,6 +1273,112 @@ impl Literal {
12631273        self . inner . subspan ( range) . map ( Span :: _new) 
12641274    } 
12651275
1276+     /// Returns the unescaped string value if this is a string literal. 
1277+ #[ cfg( procmacro2_semver_exempt) ]  
1278+     pub  fn  str_value ( & self )  -> Result < String ,  ConversionErrorKind >  { 
1279+         let  repr = self . to_string ( ) ; 
1280+ 
1281+         if  repr. starts_with ( '"' )  && repr[ 1 ..] . ends_with ( '"' )  { 
1282+             let  quoted = & repr[ 1 ..repr. len ( )  - 1 ] ; 
1283+             let  mut  value = String :: with_capacity ( quoted. len ( ) ) ; 
1284+             let  mut  error = None ; 
1285+             rustc_literal_escaper:: unescape_str ( quoted,  |_range,  res| match  res { 
1286+                 Ok ( ch)  => value. push ( ch) , 
1287+                 Err ( err)  => { 
1288+                     if  err. is_fatal ( )  { 
1289+                         error = Some ( ConversionErrorKind :: FailedToUnescape ( err) ) ; 
1290+                     } 
1291+                 } 
1292+             } ) ; 
1293+             return  match  error { 
1294+                 Some ( error)  => Err ( error) , 
1295+                 None  => Ok ( value) , 
1296+             } ; 
1297+         } 
1298+ 
1299+         if  repr. starts_with ( 'r' )  { 
1300+             if  let  Some ( raw)  = get_raw ( & repr[ 1 ..] )  { 
1301+                 return  Ok ( raw. to_owned ( ) ) ; 
1302+             } 
1303+         } 
1304+ 
1305+         Err ( ConversionErrorKind :: InvalidLiteralKind ) 
1306+     } 
1307+ 
1308+     /// Returns the unescaped string value (including nul terminator) if this is 
1309+ /// a c-string literal. 
1310+ #[ cfg( procmacro2_semver_exempt) ]  
1311+     pub  fn  cstr_value ( & self )  -> Result < Vec < u8 > ,  ConversionErrorKind >  { 
1312+         let  repr = self . to_string ( ) ; 
1313+ 
1314+         if  repr. starts_with ( "c\" " )  && repr[ 2 ..] . ends_with ( '"' )  { 
1315+             let  quoted = & repr[ 2 ..repr. len ( )  - 1 ] ; 
1316+             let  mut  value = Vec :: with_capacity ( quoted. len ( ) ) ; 
1317+             let  mut  error = None ; 
1318+             rustc_literal_escaper:: unescape_c_str ( quoted,  |_range,  res| match  res { 
1319+                 Ok ( MixedUnit :: Char ( ch) )  => { 
1320+                     value. extend_from_slice ( ch. get ( ) . encode_utf8 ( & mut  [ 0 ;  4 ] ) . as_bytes ( ) ) ; 
1321+                 } 
1322+                 Ok ( MixedUnit :: HighByte ( byte) )  => value. push ( byte. get ( ) ) , 
1323+                 Err ( err)  => { 
1324+                     if  err. is_fatal ( )  { 
1325+                         error = Some ( ConversionErrorKind :: FailedToUnescape ( err) ) ; 
1326+                     } 
1327+                 } 
1328+             } ) ; 
1329+             return  match  error { 
1330+                 Some ( error)  => Err ( error) , 
1331+                 None  => { 
1332+                     value. push ( b'\0' ) ; 
1333+                     Ok ( value) 
1334+                 } 
1335+             } ; 
1336+         } 
1337+ 
1338+         if  repr. starts_with ( "cr" )  { 
1339+             if  let  Some ( raw)  = get_raw ( & repr[ 2 ..] )  { 
1340+                 let  mut  value = Vec :: with_capacity ( raw. len ( )  + 1 ) ; 
1341+                 value. extend_from_slice ( raw. as_bytes ( ) ) ; 
1342+                 value. push ( b'\0' ) ; 
1343+                 return  Ok ( value) ; 
1344+             } 
1345+         } 
1346+ 
1347+         Err ( ConversionErrorKind :: InvalidLiteralKind ) 
1348+     } 
1349+ 
1350+     /// Returns the unescaped string value if this is a byte string literal. 
1351+ #[ cfg( procmacro2_semver_exempt) ]  
1352+     pub  fn  byte_str_value ( & self )  -> Result < Vec < u8 > ,  ConversionErrorKind >  { 
1353+         let  repr = self . to_string ( ) ; 
1354+ 
1355+         if  repr. starts_with ( "b\" " )  && repr[ 2 ..] . ends_with ( '"' )  { 
1356+             let  quoted = & repr[ 2 ..repr. len ( )  - 1 ] ; 
1357+             let  mut  value = Vec :: with_capacity ( quoted. len ( ) ) ; 
1358+             let  mut  error = None ; 
1359+             rustc_literal_escaper:: unescape_byte_str ( quoted,  |_range,  res| match  res { 
1360+                 Ok ( byte)  => value. push ( byte) , 
1361+                 Err ( err)  => { 
1362+                     if  err. is_fatal ( )  { 
1363+                         error = Some ( ConversionErrorKind :: FailedToUnescape ( err) ) ; 
1364+                     } 
1365+                 } 
1366+             } ) ; 
1367+             return  match  error { 
1368+                 Some ( error)  => Err ( error) , 
1369+                 None  => Ok ( value) , 
1370+             } ; 
1371+         } 
1372+ 
1373+         if  repr. starts_with ( "br" )  { 
1374+             if  let  Some ( raw)  = get_raw ( & repr[ 2 ..] )  { 
1375+                 return  Ok ( raw. as_bytes ( ) . to_owned ( ) ) ; 
1376+             } 
1377+         } 
1378+ 
1379+         Err ( ConversionErrorKind :: InvalidLiteralKind ) 
1380+     } 
1381+ 
12661382    // Intended for the `quote!` macro to use when constructing a proc-macro2 
12671383    // token out of a macro_rules $:literal token, which is already known to be 
12681384    // a valid literal. This avoids reparsing/validating the literal's string 
@@ -1299,6 +1415,33 @@ impl Display for Literal {
12991415    } 
13001416} 
13011417
1418+ /// Error when retrieving a string literal's unescaped value. 
1419+ #[ cfg( procmacro2_semver_exempt) ]  
1420+ #[ derive( Debug ,  PartialEq ,  Eq ) ]  
1421+ pub  enum  ConversionErrorKind  { 
1422+     /// The literal is of the right string kind, but its contents are malformed 
1423+ /// in a way that cannot be unescaped to a value. 
1424+ FailedToUnescape ( EscapeError ) , 
1425+     /// The literal is not of the string kind whose value was requested, for 
1426+ /// example byte string vs UTF-8 string. 
1427+ InvalidLiteralKind , 
1428+ } 
1429+ 
1430+ // ###"..."### -> ... 
1431+ #[ cfg( procmacro2_semver_exempt) ]  
1432+ fn  get_raw ( repr :  & str )  -> Option < & str >  { 
1433+     let  pounds = repr. len ( )  - repr. trim_start_matches ( '#' ) . len ( ) ; 
1434+     if  repr. len ( )  >= pounds + 1  + 1  + pounds
1435+         && repr[ pounds..] . starts_with ( '"' ) 
1436+         && repr. trim_end_matches ( '#' ) . len ( )  + pounds == repr. len ( ) 
1437+         && repr[ ..repr. len ( )  - pounds] . ends_with ( '"' ) 
1438+     { 
1439+         Some ( & repr[ pounds + 1 ..repr. len ( )  - pounds - 1 ] ) 
1440+     }  else  { 
1441+         None 
1442+     } 
1443+ } 
1444+ 
13021445/// Public implementation details for the `TokenStream` type, such as iterators. 
13031446pub  mod  token_stream { 
13041447    use  crate :: marker:: { ProcMacroAutoTraits ,  MARKER } ; 
0 commit comments