Skip to content

Commit 88592ad

Browse files
Add new Literal::str_value, Literal::cstr_value and Literal::byte_str_value methods
1 parent 72aaf49 commit 88592ad

File tree

2 files changed

+119
-0
lines changed

2 files changed

+119
-0
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ features = ["span-locations"]
3131
[dependencies]
3232
unicode-ident = "1.0"
3333

34+
[target.'cfg(procmacro2_semver_exempt)'.dependencies]
35+
rustc-literal-escaper = "0.0.5"
36+
3437
[dev-dependencies]
3538
flate2 = "1.0"
3639
quote = { version = "1.0", default-features = false }

src/lib.rs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@
8888
#![cfg_attr(any(proc_macro_span, super_unstable), feature(proc_macro_span))]
8989
#![cfg_attr(super_unstable, feature(proc_macro_def_site))]
9090
#![cfg_attr(docsrs, feature(doc_cfg))]
91+
#![cfg_attr(
92+
all(procmacro2_semver_exempt, feature = "proc-macro"),
93+
feature(proc_macro_value)
94+
)]
9195
#![deny(unsafe_op_in_unsafe_fn)]
9296
#![allow(
9397
clippy::cast_lossless,
@@ -178,6 +182,11 @@ use std::ffi::CStr;
178182
#[cfg(span_locations)]
179183
use std::path::PathBuf;
180184

185+
#[cfg(all(procmacro2_semver_exempt, feature = "proc-macro"))]
186+
use proc_macro::ConversionErrorKind;
187+
#[cfg(all(procmacro2_semver_exempt, feature = "proc-macro"))]
188+
use rustc_literal_escaper::MixedUnit;
189+
181190
#[cfg(span_locations)]
182191
#[cfg_attr(docsrs, doc(cfg(feature = "span-locations")))]
183192
pub use crate::location::LineColumn;
@@ -1271,6 +1280,113 @@ impl Literal {
12711280
pub unsafe fn from_str_unchecked(repr: &str) -> Self {
12721281
Literal::_new(unsafe { imp::Literal::from_str_unchecked(repr) })
12731282
}
1283+
1284+
/// Returns the unescaped string value if the current literal is a string or a string literal.
1285+
#[cfg(all(procmacro2_semver_exempt, feature = "proc-macro"))]
1286+
pub fn str_value(&self) -> Result<String, ConversionErrorKind> {
1287+
match self.inner {
1288+
imp::Literal::Compiler(ref compiler_lit) => compiler_lit.str_value(),
1289+
imp::Literal::Fallback(ref fallback) => {
1290+
if !fallback.repr.starts_with('"') {
1291+
return Err(ConversionErrorKind::InvalidLiteralKind);
1292+
}
1293+
let mut error = None;
1294+
let mut buf = String::with_capacity(fallback.repr.len());
1295+
rustc_literal_escaper::unescape_str(&fallback.repr, |_, res| match res {
1296+
Ok(c) => buf.push(c),
1297+
Err(err) => {
1298+
if err.is_fatal() {
1299+
// `proc_macro::EscapeError` is the reexport of
1300+
// `rustc_literal_escaper::EscapeError` so we safely transmute between
1301+
// the two.
1302+
error = Some(ConversionErrorKind::FailedToUnescape(unsafe {
1303+
std::mem::transmute(err)
1304+
}));
1305+
}
1306+
}
1307+
});
1308+
if let Some(error) = error {
1309+
Err(error)
1310+
} else {
1311+
Ok(buf)
1312+
}
1313+
}
1314+
}
1315+
}
1316+
1317+
/// Returns the unescaped string value if the current literal is a c-string or a c-string
1318+
/// literal.
1319+
#[cfg(all(procmacro2_semver_exempt, feature = "proc-macro"))]
1320+
pub fn cstr_value(&self) -> Result<Vec<u8>, ConversionErrorKind> {
1321+
match self.inner {
1322+
imp::Literal::Compiler(ref compiler_lit) => compiler_lit.cstr_value(),
1323+
imp::Literal::Fallback(ref fallback) => {
1324+
if !fallback.repr.starts_with('c') {
1325+
return Err(ConversionErrorKind::InvalidLiteralKind);
1326+
}
1327+
let mut error = None;
1328+
let mut buf = Vec::with_capacity(fallback.repr.len());
1329+
1330+
rustc_literal_escaper::unescape_c_str(&fallback.repr, |_span, res| match res {
1331+
Ok(MixedUnit::Char(c)) => {
1332+
buf.extend_from_slice(c.get().encode_utf8(&mut [0; 4]).as_bytes())
1333+
}
1334+
Ok(MixedUnit::HighByte(b)) => buf.push(b.get()),
1335+
Err(err) => {
1336+
if err.is_fatal() {
1337+
// `proc_macro::EscapeError` is the reexport of
1338+
// `rustc_literal_escaper::EscapeError` so we safely transmute between
1339+
// the two.
1340+
error = Some(ConversionErrorKind::FailedToUnescape(unsafe {
1341+
std::mem::transmute(err)
1342+
}));
1343+
}
1344+
}
1345+
});
1346+
if let Some(error) = error {
1347+
Err(error)
1348+
} else {
1349+
buf.push(0);
1350+
Ok(buf)
1351+
}
1352+
}
1353+
}
1354+
}
1355+
1356+
/// Returns the unescaped string value if the current literal is a byte string or a byte string
1357+
/// literal.
1358+
#[cfg(all(procmacro2_semver_exempt, feature = "proc-macro"))]
1359+
pub fn byte_str_value(&self) -> Result<Vec<u8>, ConversionErrorKind> {
1360+
match self.inner {
1361+
imp::Literal::Compiler(ref compiler_lit) => compiler_lit.byte_str_value(),
1362+
imp::Literal::Fallback(ref fallback) => {
1363+
if !fallback.repr.starts_with('c') {
1364+
return Err(ConversionErrorKind::InvalidLiteralKind);
1365+
}
1366+
let mut error = None;
1367+
let mut buf = Vec::with_capacity(fallback.repr.len());
1368+
1369+
rustc_literal_escaper::unescape_byte_str(&fallback.repr, |_span, res| match res {
1370+
Ok(c) => buf.push(c),
1371+
Err(err) => {
1372+
if err.is_fatal() {
1373+
// `proc_macro::EscapeError` is the reexport of
1374+
// `rustc_literal_escaper::EscapeError` so we safely transmute between
1375+
// the two.
1376+
error = Some(ConversionErrorKind::FailedToUnescape(unsafe {
1377+
std::mem::transmute(err)
1378+
}));
1379+
}
1380+
}
1381+
});
1382+
if let Some(error) = error {
1383+
Err(error)
1384+
} else {
1385+
Ok(buf)
1386+
}
1387+
}
1388+
}
1389+
}
12741390
}
12751391

12761392
impl FromStr for Literal {

0 commit comments

Comments
 (0)