2828#![ feature( restricted_std) ]
2929#![ feature( rustc_attrs) ]
3030#![ feature( extend_one) ]
31+ #![ feature( stmt_expr_attributes) ]
3132#![ recursion_limit = "256" ]
3233#![ allow( internal_features) ]
3334#![ deny( ffi_unwind_calls) ]
@@ -50,11 +51,23 @@ use std::{error, fmt};
5051
5152#[ unstable( feature = "proc_macro_diagnostic" , issue = "54140" ) ]
5253pub use diagnostic:: { Diagnostic , Level , MultiSpan } ;
54+ #[ unstable( feature = "proc_macro_value" , issue = "136652" ) ]
55+ pub use literal_escaper:: EscapeError ;
56+ use literal_escaper:: { MixedUnit , Mode , byte_from_char, unescape_mixed, unescape_unicode} ;
5357#[ unstable( feature = "proc_macro_totokens" , issue = "130977" ) ]
5458pub use to_tokens:: ToTokens ;
5559
5660use crate :: escape:: { EscapeOptions , escape_bytes} ;
5761
62+ /// Errors returned when trying to retrieve a literal unescaped value.
63+ #[ unstable( feature = "proc_macro_value" , issue = "136652" ) ]
64+ pub enum ConversionErrorKind {
65+ /// The literal failed to be escaped, take a look at [`EscapeError`] for more information.
66+ FailedToUnescape ( EscapeError ) ,
67+ /// Trying to convert a literal with the wrong type.
68+ InvalidLiteralKind ,
69+ }
70+
5871/// Determines whether proc_macro has been made accessible to the currently
5972/// running program.
6073///
@@ -1450,6 +1463,107 @@ impl Literal {
14501463 }
14511464 } )
14521465 }
1466+
1467+ /// Returns the unescaped string value if the current literal is a string or a string literal.
1468+ #[ unstable( feature = "proc_macro_value" , issue = "136652" ) ]
1469+ pub fn str_value ( & self ) -> Result < String , ConversionErrorKind > {
1470+ self . 0 . symbol . with ( |symbol| match self . 0 . kind {
1471+ bridge:: LitKind :: Str => {
1472+ if symbol. contains ( '\\' ) {
1473+ let mut buf = String :: with_capacity ( symbol. len ( ) ) ;
1474+ let mut error = None ;
1475+ // Force-inlining here is aggressive but the closure is
1476+ // called on every char in the string, so it can be hot in
1477+ // programs with many long strings containing escapes.
1478+ unescape_unicode (
1479+ symbol,
1480+ Mode :: Str ,
1481+ & mut #[ inline ( always) ]
1482+ |_, c| match c {
1483+ Ok ( c) => buf. push ( c) ,
1484+ Err ( err) => {
1485+ if err. is_fatal ( ) {
1486+ error = Some ( ConversionErrorKind :: FailedToUnescape ( err) ) ;
1487+ }
1488+ }
1489+ } ,
1490+ ) ;
1491+ if let Some ( error) = error { Err ( error) } else { Ok ( buf) }
1492+ } else {
1493+ Ok ( symbol. to_string ( ) )
1494+ }
1495+ }
1496+ bridge:: LitKind :: StrRaw ( _) => Ok ( symbol. to_string ( ) ) ,
1497+ _ => Err ( ConversionErrorKind :: InvalidLiteralKind ) ,
1498+ } )
1499+ }
1500+
1501+ /// Returns the unescaped string value if the current literal is a c-string or a c-string
1502+ /// literal.
1503+ #[ unstable( feature = "proc_macro_value" , issue = "136652" ) ]
1504+ pub fn cstr_value ( & self ) -> Result < Vec < u8 > , ConversionErrorKind > {
1505+ self . 0 . symbol . with ( |symbol| match self . 0 . kind {
1506+ bridge:: LitKind :: CStr => {
1507+ let mut error = None ;
1508+ let mut buf = Vec :: with_capacity ( symbol. len ( ) ) ;
1509+
1510+ unescape_mixed ( symbol, Mode :: CStr , & mut |_span, c| match c {
1511+ Ok ( MixedUnit :: Char ( c) ) => {
1512+ buf. extend_from_slice ( c. encode_utf8 ( & mut [ 0 ; 4 ] ) . as_bytes ( ) )
1513+ }
1514+ Ok ( MixedUnit :: HighByte ( b) ) => buf. push ( b) ,
1515+ Err ( err) => {
1516+ if err. is_fatal ( ) {
1517+ error = Some ( ConversionErrorKind :: FailedToUnescape ( err) ) ;
1518+ }
1519+ }
1520+ } ) ;
1521+ if let Some ( error) = error {
1522+ Err ( error)
1523+ } else {
1524+ buf. push ( 0 ) ;
1525+ Ok ( buf)
1526+ }
1527+ }
1528+ bridge:: LitKind :: CStrRaw ( _) => {
1529+ // Raw strings have no escapes so we can convert the symbol
1530+ // directly to a `Lrc<u8>` after appending the terminating NUL
1531+ // char.
1532+ let mut buf = symbol. to_owned ( ) . into_bytes ( ) ;
1533+ buf. push ( 0 ) ;
1534+ Ok ( buf)
1535+ }
1536+ _ => Err ( ConversionErrorKind :: InvalidLiteralKind ) ,
1537+ } )
1538+ }
1539+
1540+ /// Returns the unescaped string value if the current literal is a byte string or a byte string
1541+ /// literal.
1542+ #[ unstable( feature = "proc_macro_value" , issue = "136652" ) ]
1543+ pub fn byte_str_value ( & self ) -> Result < Vec < u8 > , ConversionErrorKind > {
1544+ self . 0 . symbol . with ( |symbol| match self . 0 . kind {
1545+ bridge:: LitKind :: ByteStr => {
1546+ let mut buf = Vec :: with_capacity ( symbol. len ( ) ) ;
1547+ let mut error = None ;
1548+
1549+ unescape_unicode ( symbol, Mode :: ByteStr , & mut |_, c| match c {
1550+ Ok ( c) => buf. push ( byte_from_char ( c) ) ,
1551+ Err ( err) => {
1552+ if err. is_fatal ( ) {
1553+ error = Some ( ConversionErrorKind :: FailedToUnescape ( err) ) ;
1554+ }
1555+ }
1556+ } ) ;
1557+ if let Some ( error) = error { Err ( error) } else { Ok ( buf) }
1558+ }
1559+ bridge:: LitKind :: ByteStrRaw ( _) => {
1560+ // Raw strings have no escapes so we can convert the symbol
1561+ // directly to a `Lrc<u8>`.
1562+ Ok ( symbol. to_owned ( ) . into_bytes ( ) )
1563+ }
1564+ _ => Err ( ConversionErrorKind :: InvalidLiteralKind ) ,
1565+ } )
1566+ }
14531567}
14541568
14551569/// Parse a single literal from its stringified representation.
0 commit comments