Skip to content
Draft
51 changes: 30 additions & 21 deletions src/legacy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,19 @@ pub struct Demangle<'a> {
// Note that this demangler isn't quite as fancy as it could be. We have lots
// of other information in our symbols like hashes, version, type information,
// etc. Additionally, this doesn't handle glue symbols at all.
pub fn demangle(s: &str) -> Result<(Demangle, &str), ()> {
pub fn demangle(s: &str) -> Result<(Demangle<'_>, &str), ()> {
// First validate the symbol. If it doesn't look like anything we're
// expecting, we just print it literally. Note that we must handle non-Rust
// symbols because we could have any function in the backtrace.
let inner = if s.starts_with("_ZN") {
&s[3..]
} else if s.starts_with("ZN") {
let inner = if let Some(s) = s.strip_prefix("_ZN") {
s
} else if let Some(s) = s.strip_prefix("ZN") {
// On Windows, dbghelp strips leading underscores, so we accept "ZN...E"
// form too.
&s[2..]
} else if s.starts_with("__ZN") {
s
} else if let Some(s) = s.strip_prefix("__ZN") {
// On OSX, symbols are prefixed with an extra _
&s[4..]
s
} else {
return Err(());
};
Expand All @@ -69,15 +69,16 @@ pub fn demangle(s: &str) -> Result<(Demangle, &str), ()> {
}

let mut elements = 0;
let mut chars = inner.chars();
// we checked, that string is in ascii range
let mut chars = inner.bytes();
let mut c = chars.next().ok_or(())?;
while c != 'E' {
while c != b'E' {
// Decode an identifier element's length.
if !c.is_digit(10) {
if !(c as char).is_digit(10) {
return Err(());
}
let mut len = 0usize;
while let Some(d) = c.to_digit(10) {
while let Some(d) = (c as char).to_digit(10) {
len = len
.checked_mul(10)
.and_then(|len| len.checked_add(d as usize))
Expand All @@ -94,29 +95,38 @@ pub fn demangle(s: &str) -> Result<(Demangle, &str), ()> {
elements += 1;
}

Ok((Demangle { inner, elements }, chars.as_str()))
let chars_left = chars.count();
Ok((
Demangle { inner, elements },
&inner[inner.len() - chars_left..],
))
}

// Rust hashes are hex digits with an `h` prepended.
fn is_rust_hash(s: &str) -> bool {
s.starts_with('h') && s[1..].chars().all(|c| c.is_digit(16))
s.starts_with('h') && s[1..].bytes().all(|c| (c as char).is_digit(16))
}

impl<'a> fmt::Display for Demangle<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// Alright, let's do this.
let mut inner = self.inner;
for element in 0..self.elements {
let mut rest = inner;
while rest.chars().next().unwrap().is_digit(10) {
while rest
.bytes()
.next()
.map(|c| (c as char).is_digit(10))
.unwrap_or(false)
{
rest = &rest[1..];
}
let i: usize = inner[..(inner.len() - rest.len())].parse().unwrap();
inner = &rest[i..];
rest = &rest[..i];
// Skip printing the hash if alternate formatting
// was requested.
if f.alternate() && element + 1 == self.elements && is_rust_hash(&rest) {
if f.alternate() && element + 1 == self.elements && is_rust_hash(rest) {
break;
}
if element != 0 {
Expand All @@ -127,7 +137,7 @@ impl<'a> fmt::Display for Demangle<'a> {
}
loop {
if rest.starts_with('.') {
if let Some('.') = rest[1..].chars().next() {
if let Some(b'.') = rest[1..].bytes().next() {
f.write_str("::")?;
rest = &rest[2..];
} else {
Expand All @@ -153,10 +163,9 @@ impl<'a> fmt::Display for Demangle<'a> {
"C" => ",",

_ => {
if escape.starts_with('u') {
let digits = &escape[1..];
let all_lower_hex = digits.chars().all(|c| match c {
'0'..='9' | 'a'..='f' => true,
if let Some(digits) = escape.strip_prefix('u') {
let all_lower_hex = digits.bytes().all(|c| match c {
b'0'..=b'9' | b'a'..=b'f' => true,
_ => false,
});
let c = u32::from_str_radix(digits, 16)
Expand Down
12 changes: 6 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,15 @@ enum DemangleStyle<'a> {
/// assert_eq!(demangle("_ZN3foo3barE").to_string(), "foo::bar");
/// assert_eq!(demangle("foo").to_string(), "foo");
/// ```
pub fn demangle(mut s: &str) -> Demangle {
pub fn demangle(mut s: &str) -> Demangle<'_> {
// During ThinLTO LLVM may import and rename internal symbols, so strip out
// those endings first as they're one of the last manglings applied to symbol
// names.
let llvm = ".llvm.";
if let Some(i) = s.find(llvm) {
let candidate = &s[i + llvm.len()..];
let all_hex = candidate.chars().all(|c| match c {
'A'..='F' | '0'..='9' | '@' => true,
let all_hex = candidate.bytes().all(|c| match c {
b'A'..=b'F' | b'0'..=b'9' | b'@' => true,
_ => false,
});

Expand Down Expand Up @@ -164,7 +164,7 @@ pub struct TryDemangleError {
/// // While `demangle` will just pass the non-symbol through as a no-op.
/// assert_eq!(rustc_demangle::demangle(not_a_rust_symbol).as_str(), not_a_rust_symbol);
/// ```
pub fn try_demangle(s: &str) -> Result<Demangle, TryDemangleError> {
pub fn try_demangle(s: &str) -> Result<Demangle<'_>, TryDemangleError> {
let sym = demangle(s);
if sym.style.is_some() {
Ok(sym)
Expand Down Expand Up @@ -241,7 +241,7 @@ impl<F: fmt::Write> fmt::Write for SizeLimitedFmtAdapter<F> {
}

impl<'a> fmt::Display for Demangle<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.style {
None => f.write_str(self.original)?,
Some(ref d) => {
Expand Down Expand Up @@ -276,7 +276,7 @@ impl<'a> fmt::Display for Demangle<'a> {
}

impl<'a> fmt::Debug for Demangle<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
Expand Down
23 changes: 11 additions & 12 deletions src/v0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,23 +38,22 @@ pub fn demangle(s: &str) -> Result<(Demangle, &str), ParseError> {
// First validate the symbol. If it doesn't look like anything we're
// expecting, we just print it literally. Note that we must handle non-Rust
// symbols because we could have any function in the backtrace.
let inner;
if s.len() > 2 && s.starts_with("_R") {
inner = &s[2..];
} else if s.len() > 1 && s.starts_with('R') {
let inner = if let Some(s) = s.strip_prefix("_R") {
s
} else if let Some(s) = s.strip_prefix('R') {
// On Windows, dbghelp strips leading underscores, so we accept "R..."
// form too.
inner = &s[1..];
} else if s.len() > 3 && s.starts_with("__R") {
s
} else if let Some(s) = s.strip_prefix("__R") {
// On OSX, symbols are prefixed with an extra _
inner = &s[3..];
s
} else {
return Err(ParseError::Invalid);
}
};

// Paths always start with uppercase characters.
match inner.as_bytes()[0] {
b'A'..=b'Z' => {}
match inner.bytes().next() {
Some(b'A'..=b'Z') => {}
_ => return Err(ParseError::Invalid),
}

Expand Down Expand Up @@ -161,8 +160,8 @@ impl<'s> Ident<'s> {
let mut len = 0;

// Populate initial output from ASCII fragment.
for c in self.ascii.chars() {
insert(len, c)?;
for c in self.ascii.bytes() {
insert(len, c as char)?;
len += 1;
}

Expand Down