Skip to content

Commit bfc20bf

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 bfc20bf

File tree

1 file changed

+47
-0
lines changed

1 file changed

+47
-0
lines changed

src/lib.rs

Lines changed: 47 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,26 @@ 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+
// We've already checked that the character is ASCII
222+
// Once `char::is_ascii_punctuation` and `char::is_ascii_alphanumeric`
223+
// have been stable for long enough, use those instead for clarity
224+
c.is_alphanumeric() || is_ascii_punctuation(c)
225+
})
226+
}
227+
228+
// Copied from the documentation of `char::is_ascii_punctuation`
229+
fn is_ascii_punctuation(c: char) -> bool {
230+
match c {
231+
'\u{0021}' ... '\u{002F}' |
232+
'\u{003A}' ... '\u{0040}' |
233+
'\u{005B}' ... '\u{0060}' |
234+
'\u{007B}' ... '\u{007E}' => true,
235+
_ => false,
236+
}
237+
}
238+
205239
impl<'a> fmt::Display for Demangle<'a> {
206240
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
207241
// Alright, let's do this.
@@ -288,6 +322,8 @@ impl<'a> fmt::Display for Demangle<'a> {
288322
}
289323
}
290324

325+
try!(f.write_str(self.suffix));
326+
291327
Ok(())
292328
}
293329
}
@@ -398,6 +434,17 @@ mod tests {
398434
t_nohash!("_ZN9backtrace3foo17hbb467fcdaea5d79bE.llvm.A5310EB9", "backtrace::foo");
399435
}
400436

437+
#[test]
438+
fn demangle_llvm_ir_branch_labels() {
439+
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");
440+
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");
441+
}
442+
443+
#[test]
444+
fn demangle_ignores_suffix_that_doesnt_look_like_a_symbol() {
445+
t!("_ZN3fooE.llvm moocow", "_ZN3fooE.llvm moocow");
446+
}
447+
401448
#[test]
402449
fn dont_panic() {
403450
super::demangle("_ZN2222222222222222222222EE").to_string();

0 commit comments

Comments
 (0)