Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 5c5ed38

Browse files
committedJan 31, 2025
Add _value methods to proc_macro lib
1 parent 9e57988 commit 5c5ed38

File tree

3 files changed

+117
-0
lines changed

3 files changed

+117
-0
lines changed
 

‎library/Cargo.lock

+5
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,10 @@ dependencies = [
158158
"rustc-std-workspace-core",
159159
]
160160

161+
[[package]]
162+
name = "literal-escaper"
163+
version = "0.0.1"
164+
161165
[[package]]
162166
name = "memchr"
163167
version = "2.7.4"
@@ -220,6 +224,7 @@ name = "proc_macro"
220224
version = "0.0.0"
221225
dependencies = [
222226
"core",
227+
"literal-escaper",
223228
"std",
224229
]
225230

‎library/proc_macro/Cargo.toml

+1
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

‎library/proc_macro/src/lib.rs

+111
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,22 @@ use std::{error, fmt};
5050

5151
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
5252
pub use diagnostic::{Diagnostic, Level, MultiSpan};
53+
#[stable(feature = "proc_macro_lib2", since = "1.86.0")]
54+
pub use literal_escaper::EscapeError;
5355
#[unstable(feature = "proc_macro_totokens", issue = "130977")]
5456
pub use to_tokens::ToTokens;
5557

5658
use crate::escape::{EscapeOptions, escape_bytes};
5759

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

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

0 commit comments

Comments
 (0)
Failed to load comments.