Skip to content

Commit 4e8486a

Browse files
Add skippable sections as described in MCP 737.
See rust-lang/compiler-team#737.
1 parent f053741 commit 4e8486a

File tree

1 file changed

+170
-14
lines changed

1 file changed

+170
-14
lines changed

src/v0.rs

Lines changed: 170 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ pub enum ParseError {
3434
/// This function will take a **mangled** symbol and return a value. When printed,
3535
/// the de-mangled version will be written. If the symbol does not look like
3636
/// a mangled symbol, the original value will be written instead.
37-
pub fn demangle(s: &str) -> Result<(Demangle, &str), ParseError> {
37+
pub fn demangle(s: &str) -> Result<(Demangle<'_>, &str), ParseError> {
3838
// First validate the symbol. If it doesn't look like anything we're
3939
// expecting, we just print it literally. Note that we must handle non-Rust
4040
// symbols because we could have any function in the backtrace.
@@ -91,7 +91,7 @@ pub fn demangle(s: &str) -> Result<(Demangle, &str), ParseError> {
9191
}
9292

9393
impl<'s> fmt::Display for Demangle<'s> {
94-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
94+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
9595
let mut printer = Printer {
9696
parser: Ok(Parser {
9797
sym: self.inner,
@@ -238,7 +238,7 @@ impl<'s> Ident<'s> {
238238
}
239239

240240
impl<'s> fmt::Display for Ident<'s> {
241-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
241+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242242
self.try_small_punycode_decode(|chars| {
243243
for &c in chars {
244244
c.fmt(f)?;
@@ -563,9 +563,57 @@ impl<'s> Parser<'s> {
563563
})
564564
}
565565
}
566+
567+
/// Looks for the `C<digits>_` prefix that marks a skippable section.
568+
fn peek_skippable_prefix(&self) -> Option<SkippableSection> {
569+
let bytes = self.sym.as_bytes();
570+
let mut index = self.next;
571+
572+
if bytes.get(index).cloned() != Some(b'C') {
573+
return None;
574+
}
575+
576+
index += 1;
577+
578+
let length_digits_start = index;
579+
580+
while bytes.get(index).map_or(false, u8::is_ascii_digit) {
581+
index += 1;
582+
}
583+
584+
let length_digits_end = index;
585+
586+
if length_digits_start == length_digits_end {
587+
// No digits found, this also covers the case where we have
588+
// a normal crate production with disambiguator `Cs<hash><digits><ident>`
589+
return None;
590+
}
591+
592+
if length_digits_end >= bytes.len() {
593+
// We are at the end of the input, this is probably not a well-formed
594+
// symbol name.
595+
return None;
596+
}
597+
598+
if bytes[length_digits_end] != b'_' {
599+
// This is another kind of special `C` production, like `C3f16`
600+
return None;
601+
}
602+
603+
// Convert the digits to an int
604+
let mut num_bytes = 0;
605+
for &c in &bytes[length_digits_start..length_digits_end] {
606+
num_bytes = num_bytes * 10 + (c - b'0') as usize;
607+
}
608+
609+
Some(SkippableSection {
610+
content_start: length_digits_end + 1, // +1 to skip the `_` too
611+
section_end: length_digits_end + num_bytes,
612+
})
613+
}
566614
}
567615

568-
struct Printer<'a, 'b: 'a, 's> {
616+
struct Printer<'a, 'b, 's> {
569617
/// The input parser to demangle from, or `Err` if any (parse) error was
570618
/// encountered (in order to disallow further likely-incorrect demangling).
571619
///
@@ -661,6 +709,8 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> {
661709
return Ok(());
662710
}
663711

712+
// TODO: Keep going even if parsing the referenced section failed, so
713+
// that we can handle backrefs into skippable sections.
664714
let orig_parser = mem::replace(&mut self.parser, Ok(backref_parser));
665715
let r = f(self);
666716
self.parser = orig_parser;
@@ -889,14 +939,16 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> {
889939
}
890940

891941
fn print_generic_arg(&mut self) -> fmt::Result {
892-
if self.eat(b'L') {
893-
let lt = parse!(self, integer_62);
894-
self.print_lifetime_from_index(lt)
895-
} else if self.eat(b'K') {
896-
self.print_const(false)
897-
} else {
898-
self.print_type()
899-
}
942+
self.print_with_fallback(|this| {
943+
if this.eat(b'L') {
944+
let lt = parse!(this, integer_62);
945+
this.print_lifetime_from_index(lt)
946+
} else if this.eat(b'K') {
947+
this.print_const(false)
948+
} else {
949+
this.print_type()
950+
}
951+
})
900952
}
901953

902954
fn print_type(&mut self) -> fmt::Result {
@@ -1081,7 +1133,6 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> {
10811133

10821134
fn print_const(&mut self, in_value: bool) -> fmt::Result {
10831135
let tag = parse!(self, next);
1084-
10851136
parse!(self, push_depth);
10861137

10871138
// Only literals (and the names of `const` generic parameters, but they
@@ -1237,12 +1288,76 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> {
12371288
None => invalid!(self),
12381289
}
12391290
}
1291+
1292+
fn print_with_fallback(&mut self, mut f: impl FnMut(&mut Self) -> fmt::Result) -> fmt::Result {
1293+
let skippable_section = self
1294+
.parser
1295+
.as_ref()
1296+
.map(Parser::peek_skippable_prefix)
1297+
.unwrap_or_default();
1298+
1299+
if let Some(skippable_section) = skippable_section {
1300+
// If we got here, we know have a valid parser and can unwrap.
1301+
let self_parser = self.parser.as_mut().unwrap();
1302+
1303+
// Create a look-ahead printer to check if we can handle the skippable
1304+
// section without error.
1305+
let mut look_ahead = Printer {
1306+
bound_lifetime_depth: self.bound_lifetime_depth,
1307+
out: None, // Don't produce any output
1308+
parser: Ok(Parser {
1309+
depth: self_parser.depth,
1310+
sym: &self_parser.sym[..skippable_section.section_end],
1311+
next: skippable_section.content_start,
1312+
}),
1313+
};
1314+
1315+
// Do the look-ahead parsing. We ignore the result, which is unreliable,
1316+
// and instead check if the parser still Ok after the parsing
1317+
let _ = f(&mut look_ahead);
1318+
1319+
if look_ahead.parser.is_ok() {
1320+
// We succeeded, so we eat the skippable section prefix and do the
1321+
// printing for real.
1322+
self_parser.next = skippable_section.content_start;
1323+
f(self)
1324+
} else {
1325+
// Parsing failed so we emit the contents of the skippable section verbatim.
1326+
self_parser.next = skippable_section.section_end;
1327+
let verbatim = &self_parser.sym
1328+
[skippable_section.content_start..skippable_section.section_end];
1329+
1330+
self.print("{{{ skipped: ")?;
1331+
self.print(verbatim)?;
1332+
self.print(" }}}")?;
1333+
Ok(())
1334+
}
1335+
} else {
1336+
f(self)
1337+
}
1338+
}
1339+
}
1340+
1341+
/// A skippable section in a mangled name looks like `C<digits>_<content>`,
1342+
/// where <digits> is an ASCII decimal designating the number of bytes in
1343+
/// `<content>` plus one byte for the `_`.
1344+
/// The `section_end` field is the index in the input array right after the
1345+
/// end of `<content>`. The `content_start` field is the index at the first
1346+
/// byte of `<content>`.
1347+
#[derive(Debug, PartialEq, Eq)]
1348+
struct SkippableSection {
1349+
section_end: usize,
1350+
content_start: usize,
12401351
}
12411352

12421353
#[cfg(test)]
12431354
mod tests {
12441355
use std::prelude::v1::*;
12451356

1357+
use crate::v0::SkippableSection;
1358+
1359+
use super::Parser;
1360+
12461361
macro_rules! t {
12471362
($a:expr, $b:expr) => {{
12481363
assert_eq!(format!("{}", ::demangle($a)), $b);
@@ -1278,7 +1393,7 @@ mod tests {
12781393

12791394
#[test]
12801395
fn demangle_crate_with_leading_digit() {
1281-
t_nohash!("_RNvC6_123foo3bar", "123foo::bar");
1396+
t_nohash!("_RNvCs_6_123foo3bar", "123foo::bar");
12821397
}
12831398

12841399
#[test]
@@ -1533,4 +1648,45 @@ mod tests {
15331648

15341649
assert_contains!(::demangle(&sym).to_string(), "{recursion limit reached}");
15351650
}
1651+
1652+
#[test]
1653+
fn demangle_const_skippable() {
1654+
// Parsable
1655+
t_nohash!(
1656+
"_RINtCsHASH_7mycrate3FooxC11_KRe616263_E",
1657+
r#"mycrate::Foo::<i64, "abc">"#
1658+
);
1659+
1660+
// Not Parsable
1661+
t_nohash!(
1662+
"_RINtCsHASH_7mycrate3FooxC8_@$%^&*#E",
1663+
"mycrate::Foo::<i64, {{{ skipped: @$%^&*# }}}>"
1664+
);
1665+
}
1666+
1667+
#[test]
1668+
fn skippable_section_prefix() {
1669+
macro_rules! test_skippable_prefix {
1670+
($txt:expr, $expected:expr) => {{
1671+
let p = Parser {
1672+
depth: 0,
1673+
sym: $txt,
1674+
next: 0,
1675+
};
1676+
let actual = p.peek_skippable_prefix();
1677+
assert_eq!(actual, $expected);
1678+
}};
1679+
}
1680+
1681+
test_skippable_prefix!(
1682+
"C3_xx",
1683+
Some(SkippableSection {
1684+
content_start: 3,
1685+
section_end: 5,
1686+
})
1687+
);
1688+
1689+
test_skippable_prefix!("Cs1341adsasd_3_xx", None);
1690+
test_skippable_prefix!("C3f16", None);
1691+
}
15361692
}

0 commit comments

Comments
 (0)