Skip to content

Commit 13400c2

Browse files
Add _value methods to proc_macro lib
1 parent bba0d57 commit 13400c2

File tree

2 files changed

+115
-0
lines changed

2 files changed

+115
-0
lines changed

library/proc_macro/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ std = { path = "../std" }
99
# `core` when resolving doc links. Without this line a different `core` will be
1010
# loaded from sysroot causing duplicate lang items and other similar errors.
1111
core = { path = "../core" }
12+
rustc-literal-escaper = "0.0.1"

library/proc_macro/src/lib.rs

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,24 @@ use std::{error, fmt};
5151

5252
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
5353
pub use diagnostic::{Diagnostic, Level, MultiSpan};
54+
#[unstable(feature = "proc_macro_value", issue = "136652")]
55+
pub use rustc_literal_escaper::EscapeError;
56+
use rustc_literal_escaper::{MixedUnit, Mode, byte_from_char, unescape_mixed, unescape_unicode};
5457
#[unstable(feature = "proc_macro_totokens", issue = "130977")]
5558
pub use to_tokens::ToTokens;
5659

5760
use crate::escape::{EscapeOptions, escape_bytes};
5861

62+
/// Errors returned when trying to retrieve a literal unescaped value.
63+
#[unstable(feature = "proc_macro_value", issue = "136652")]
64+
#[derive(Debug, PartialEq, Eq)]
65+
pub enum ConversionErrorKind {
66+
/// The literal failed to be escaped, take a look at [`EscapeError`] for more information.
67+
FailedToUnescape(EscapeError),
68+
/// Trying to convert a literal with the wrong type.
69+
InvalidLiteralKind,
70+
}
71+
5972
/// Determines whether proc_macro has been made accessible to the currently
6073
/// running program.
6174
///
@@ -1451,6 +1464,107 @@ impl Literal {
14511464
}
14521465
})
14531466
}
1467+
1468+
/// Returns the unescaped string value if the current literal is a string or a string literal.
1469+
#[unstable(feature = "proc_macro_value", issue = "136652")]
1470+
pub fn str_value(&self) -> Result<String, ConversionErrorKind> {
1471+
self.0.symbol.with(|symbol| match self.0.kind {
1472+
bridge::LitKind::Str => {
1473+
if symbol.contains('\\') {
1474+
let mut buf = String::with_capacity(symbol.len());
1475+
let mut error = None;
1476+
// Force-inlining here is aggressive but the closure is
1477+
// called on every char in the string, so it can be hot in
1478+
// programs with many long strings containing escapes.
1479+
unescape_unicode(
1480+
symbol,
1481+
Mode::Str,
1482+
&mut #[inline(always)]
1483+
|_, c| match c {
1484+
Ok(c) => buf.push(c),
1485+
Err(err) => {
1486+
if err.is_fatal() {
1487+
error = Some(ConversionErrorKind::FailedToUnescape(err));
1488+
}
1489+
}
1490+
},
1491+
);
1492+
if let Some(error) = error { Err(error) } else { Ok(buf) }
1493+
} else {
1494+
Ok(symbol.to_string())
1495+
}
1496+
}
1497+
bridge::LitKind::StrRaw(_) => Ok(symbol.to_string()),
1498+
_ => Err(ConversionErrorKind::InvalidLiteralKind),
1499+
})
1500+
}
1501+
1502+
/// Returns the unescaped string value if the current literal is a c-string or a c-string
1503+
/// literal.
1504+
#[unstable(feature = "proc_macro_value", issue = "136652")]
1505+
pub fn cstr_value(&self) -> Result<Vec<u8>, ConversionErrorKind> {
1506+
self.0.symbol.with(|symbol| match self.0.kind {
1507+
bridge::LitKind::CStr => {
1508+
let mut error = None;
1509+
let mut buf = Vec::with_capacity(symbol.len());
1510+
1511+
unescape_mixed(symbol, Mode::CStr, &mut |_span, c| match c {
1512+
Ok(MixedUnit::Char(c)) => {
1513+
buf.extend_from_slice(c.encode_utf8(&mut [0; 4]).as_bytes())
1514+
}
1515+
Ok(MixedUnit::HighByte(b)) => buf.push(b),
1516+
Err(err) => {
1517+
if err.is_fatal() {
1518+
error = Some(ConversionErrorKind::FailedToUnescape(err));
1519+
}
1520+
}
1521+
});
1522+
if let Some(error) = error {
1523+
Err(error)
1524+
} else {
1525+
buf.push(0);
1526+
Ok(buf)
1527+
}
1528+
}
1529+
bridge::LitKind::CStrRaw(_) => {
1530+
// Raw strings have no escapes so we can convert the symbol
1531+
// directly to a `Lrc<u8>` after appending the terminating NUL
1532+
// char.
1533+
let mut buf = symbol.to_owned().into_bytes();
1534+
buf.push(0);
1535+
Ok(buf)
1536+
}
1537+
_ => Err(ConversionErrorKind::InvalidLiteralKind),
1538+
})
1539+
}
1540+
1541+
/// Returns the unescaped string value if the current literal is a byte string or a byte string
1542+
/// literal.
1543+
#[unstable(feature = "proc_macro_value", issue = "136652")]
1544+
pub fn byte_str_value(&self) -> Result<Vec<u8>, ConversionErrorKind> {
1545+
self.0.symbol.with(|symbol| match self.0.kind {
1546+
bridge::LitKind::ByteStr => {
1547+
let mut buf = Vec::with_capacity(symbol.len());
1548+
let mut error = None;
1549+
1550+
unescape_unicode(symbol, Mode::ByteStr, &mut |_, c| match c {
1551+
Ok(c) => buf.push(byte_from_char(c)),
1552+
Err(err) => {
1553+
if err.is_fatal() {
1554+
error = Some(ConversionErrorKind::FailedToUnescape(err));
1555+
}
1556+
}
1557+
});
1558+
if let Some(error) = error { Err(error) } else { Ok(buf) }
1559+
}
1560+
bridge::LitKind::ByteStrRaw(_) => {
1561+
// Raw strings have no escapes so we can convert the symbol
1562+
// directly to a `Lrc<u8>`.
1563+
Ok(symbol.to_owned().into_bytes())
1564+
}
1565+
_ => Err(ConversionErrorKind::InvalidLiteralKind),
1566+
})
1567+
}
14541568
}
14551569

14561570
/// Parse a single literal from its stringified representation.

0 commit comments

Comments
 (0)