Skip to content

Commit bf98d28

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

File tree

2 files changed

+116
-0
lines changed

2 files changed

+116
-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: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
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(procmacro2_semver_exempt, feature(proc_macro_value))]
9192
#![deny(unsafe_op_in_unsafe_fn)]
9293
#![allow(
9394
clippy::cast_lossless,
@@ -178,6 +179,11 @@ use std::ffi::CStr;
178179
#[cfg(span_locations)]
179180
use std::path::PathBuf;
180181

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

12761389
impl FromStr for Literal {

0 commit comments

Comments
 (0)