Skip to content

Commit 0537b1a

Browse files
Add _value methods to proc_macro lib
1 parent 39a0e63 commit 0537b1a

File tree

3 files changed

+123
-0
lines changed

3 files changed

+123
-0
lines changed

Cargo.lock

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

proc_macro/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ version = "0.0.0"
44
edition = "2021"
55

66
[dependencies]
7+
literal-escaper = { path = "../literal-escaper", features = ["rustc-dep-of-std"] }
78
std = { path = "../std" }
89
# Workaround: when documenting this crate rustdoc will try to load crate named
910
# `core` when resolving doc links. Without this line a different `core` will be

proc_macro/src/lib.rs

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
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")]
5253
pub 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")]
5458
pub use to_tokens::ToTokens;
5559

5660
use 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

Comments
 (0)