28
28
#![ feature( restricted_std) ]
29
29
#![ feature( rustc_attrs) ]
30
30
#![ feature( extend_one) ]
31
+ #![ feature( stmt_expr_attributes) ]
31
32
#![ recursion_limit = "256" ]
32
33
#![ allow( internal_features) ]
33
34
#![ deny( ffi_unwind_calls) ]
@@ -50,11 +51,23 @@ use std::{error, fmt};
50
51
51
52
#[ unstable( feature = "proc_macro_diagnostic" , issue = "54140" ) ]
52
53
pub use diagnostic:: { Diagnostic , Level , MultiSpan } ;
54
+ #[ stable( feature = "proc_macro_value" , since = "1.86.0" ) ]
55
+ pub use literal_escaper:: EscapeError ;
56
+ use literal_escaper:: { MixedUnit , Mode , byte_from_char, unescape_mixed, unescape_unicode} ;
53
57
#[ unstable( feature = "proc_macro_totokens" , issue = "130977" ) ]
54
58
pub use to_tokens:: ToTokens ;
55
59
56
60
use crate :: escape:: { EscapeOptions , escape_bytes} ;
57
61
62
+ /// Errors returned when trying to retrieve a literal unescaped value.
63
+ #[ stable( feature = "proc_macro_value" , since = "1.86.0" ) ]
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
+
58
71
/// Determines whether proc_macro has been made accessible to the currently
59
72
/// running program.
60
73
///
@@ -1450,6 +1463,107 @@ impl Literal {
1450
1463
}
1451
1464
} )
1452
1465
}
1466
+
1467
+ /// Returns the unescaped string value if the current literal is a string or a string literal.
1468
+ #[ stable( feature = "proc_macro_value" , since = "1.86.0" ) ]
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
+ #[ stable( feature = "proc_macro_value" , since = "1.86.0" ) ]
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
+ #[ stable( feature = "proc_macro_value" , since = "1.86.0" ) ]
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
+ }
1453
1567
}
1454
1568
1455
1569
/// Parse a single literal from its stringified representation.
0 commit comments