@@ -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