Skip to content

Commit fcf9f7f

Browse files
committed
Support demangling symbols with dot-delimited words at the end
This accounts for values like LLVM IR branch labels. Closes #15
1 parent 7a8b109 commit fcf9f7f

File tree

1 file changed

+56
-0
lines changed

1 file changed

+56
-0
lines changed

src/lib.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ use core::fmt;
3636
pub struct Demangle<'a> {
3737
original: &'a str,
3838
inner: &'a str,
39+
suffix: &'a str,
3940
valid: bool,
4041
/// The number of ::-separated elements in the original name.
4142
elements: usize,
@@ -98,6 +99,18 @@ pub fn demangle(mut s: &str) -> Demangle {
9899
}
99100
}
100101

102+
// Output like LLVM IR adds extra period-delimited words. See if
103+
// we are in that case and save the trailing words if so.
104+
let mut suffix = "";
105+
if let Some(i) = s.rfind("E.") {
106+
let (head, tail) = s.split_at(i + 1); // After the E, before the period
107+
108+
if is_symbol_like(tail) {
109+
s = head;
110+
suffix = tail;
111+
}
112+
}
113+
101114
// First validate the symbol. If it doesn't look like anything we're
102115
// expecting, we just print it literally. Note that we must handle non-Rust
103116
// symbols because we could have any function in the backtrace.
@@ -155,6 +168,7 @@ pub fn demangle(mut s: &str) -> Demangle {
155168

156169
Demangle {
157170
inner: inner,
171+
suffix: suffix,
158172
valid: valid,
159173
elements: elements,
160174
original: s,
@@ -202,6 +216,35 @@ fn is_rust_hash(s: &str) -> bool {
202216
s.starts_with('h') && s[1..].chars().all(|c| c.is_digit(16))
203217
}
204218

219+
fn is_symbol_like(s: &str) -> bool {
220+
s.chars().all(|c| {
221+
// Once `char::is_ascii_punctuation` and `char::is_ascii_alphanumeric`
222+
// have been stable for long enough, use those instead for clarity
223+
is_ascii_alphanumeric(c) || is_ascii_punctuation(c)
224+
})
225+
}
226+
227+
// Copied from the documentation of `char::is_ascii_alphanumeric`
228+
fn is_ascii_alphanumeric(c: char) -> bool {
229+
match c {
230+
'\u{0041}' ... '\u{005A}' |
231+
'\u{0061}' ... '\u{007A}' |
232+
'\u{0030}' ... '\u{0039}' => true,
233+
_ => false,
234+
}
235+
}
236+
237+
// Copied from the documentation of `char::is_ascii_punctuation`
238+
fn is_ascii_punctuation(c: char) -> bool {
239+
match c {
240+
'\u{0021}' ... '\u{002F}' |
241+
'\u{003A}' ... '\u{0040}' |
242+
'\u{005B}' ... '\u{0060}' |
243+
'\u{007B}' ... '\u{007E}' => true,
244+
_ => false,
245+
}
246+
}
247+
205248
impl<'a> fmt::Display for Demangle<'a> {
206249
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
207250
// Alright, let's do this.
@@ -288,6 +331,8 @@ impl<'a> fmt::Display for Demangle<'a> {
288331
}
289332
}
290333

334+
try!(f.write_str(self.suffix));
335+
291336
Ok(())
292337
}
293338
}
@@ -398,6 +443,17 @@ mod tests {
398443
t_nohash!("_ZN9backtrace3foo17hbb467fcdaea5d79bE.llvm.A5310EB9", "backtrace::foo");
399444
}
400445

446+
#[test]
447+
fn demangle_llvm_ir_branch_labels() {
448+
t!("_ZN4core5slice77_$LT$impl$u20$core..ops..index..IndexMut$LT$I$GT$$u20$for$u20$$u5b$T$u5d$$GT$9index_mut17haf9727c2edfbc47bE.exit.i.i", "core::slice::<impl core::ops::index::IndexMut<I> for [T]>::index_mut::haf9727c2edfbc47b.exit.i.i");
449+
t_nohash!("_ZN4core5slice77_$LT$impl$u20$core..ops..index..IndexMut$LT$I$GT$$u20$for$u20$$u5b$T$u5d$$GT$9index_mut17haf9727c2edfbc47bE.exit.i.i", "core::slice::<impl core::ops::index::IndexMut<I> for [T]>::index_mut.exit.i.i");
450+
}
451+
452+
#[test]
453+
fn demangle_ignores_suffix_that_doesnt_look_like_a_symbol() {
454+
t!("_ZN3fooE.llvm moocow", "_ZN3fooE.llvm moocow");
455+
}
456+
401457
#[test]
402458
fn dont_panic() {
403459
super::demangle("_ZN2222222222222222222222EE").to_string();

0 commit comments

Comments
 (0)