Skip to main content

sgx_demangle/
lib.rs

1//! Demangle Rust compiler symbol names.
2//!
3//! This crate provides a `demangle` function which will return a `Demangle`
4//! sentinel value that can be used to learn about the demangled version of a
5//! symbol name. The demangled representation will be the same as the original
6//! if it doesn't look like a mangled symbol name.
7//!
8//! `Demangle` can be formatted with the `Display` trait. The alternate
9//! modifier (`#`) can be used to format the symbol name without the
10//! trailing hash value.
11//!
12//! # Examples
13//!
14//! ```
15//! use rustc_demangle::demangle;
16//!
17//! assert_eq!(demangle("_ZN4testE").to_string(), "test");
18//! assert_eq!(demangle("_ZN3foo3barE").to_string(), "foo::bar");
19//! assert_eq!(demangle("foo").to_string(), "foo");
20//! // With hash
21//! assert_eq!(format!("{}", demangle("_ZN3foo17h05af221e174051e9E")), "foo::h05af221e174051e9");
22//! // Without hash
23//! assert_eq!(format!("{:#}", demangle("_ZN3foo17h05af221e174051e9E")), "foo");
24//! ```
25
26#![no_std]
27
28#![cfg_attr(target_env = "sgx", feature(rustc_private))]
29
30mod legacy;
31mod v0;
32
33use core::fmt;
34
35/// Representation of a demangled symbol name.
36pub struct Demangle<'a> {
37    style: Option<DemangleStyle<'a>>,
38    original: &'a str,
39    suffix: &'a str,
40}
41
42enum DemangleStyle<'a> {
43    Legacy(legacy::Demangle<'a>),
44    V0(v0::Demangle<'a>),
45}
46
47/// De-mangles a Rust symbol into a more readable version
48///
49/// This function will take a **mangled** symbol and return a value. When printed,
50/// the de-mangled version will be written. If the symbol does not look like
51/// a mangled symbol, the original value will be written instead.
52///
53/// # Examples
54///
55/// ```
56/// use rustc_demangle::demangle;
57///
58/// assert_eq!(demangle("_ZN4testE").to_string(), "test");
59/// assert_eq!(demangle("_ZN3foo3barE").to_string(), "foo::bar");
60/// assert_eq!(demangle("foo").to_string(), "foo");
61/// ```
62pub fn demangle(mut s: &str) -> Demangle {
63    // During ThinLTO LLVM may import and rename internal symbols, so strip out
64    // those endings first as they're one of the last manglings applied to symbol
65    // names.
66    let llvm = ".llvm.";
67    if let Some(i) = s.find(llvm) {
68        let candidate = &s[i + llvm.len()..];
69        let all_hex = candidate.chars().all(|c| {
70            match c {
71                'A' ..= 'F' | '0' ..= '9' | '@' => true,
72                _ => false,
73            }
74        });
75
76        if all_hex {
77            s = &s[..i];
78        }
79    }
80
81    // Output like LLVM IR adds extra period-delimited words. See if
82    // we are in that case and save the trailing words if so.
83    let mut suffix = "";
84    if let Some(i) = s.rfind("E.") {
85        let (head, tail) = s.split_at(i + 1); // After the E, before the period
86
87        if is_symbol_like(tail) {
88            s = head;
89            suffix = tail;
90        }
91    }
92
93    let style = match legacy::demangle(s) {
94        Ok(d) => Some(DemangleStyle::Legacy(d)),
95        Err(()) => match v0::demangle(s) {
96            Ok(d) => Some(DemangleStyle::V0(d)),
97            Err(v0::Invalid) => None,
98        },
99    };
100    Demangle {
101        style: style,
102        original: s,
103        suffix: suffix,
104    }
105}
106
107/// Error returned from the `try_demangle` function below when demangling fails.
108#[derive(Debug, Clone)]
109pub struct TryDemangleError {
110    _priv: (),
111}
112
113/// The same as `demangle`, except return an `Err` if the string does not appear
114/// to be a Rust symbol, rather than "demangling" the given string as a no-op.
115///
116/// ```
117/// extern crate rustc_demangle;
118///
119/// let not_a_rust_symbol = "la la la";
120///
121/// // The `try_demangle` function will reject strings which are not Rust symbols.
122/// assert!(rustc_demangle::try_demangle(not_a_rust_symbol).is_err());
123///
124/// // While `demangle` will just pass the non-symbol through as a no-op.
125/// assert_eq!(rustc_demangle::demangle(not_a_rust_symbol).as_str(), not_a_rust_symbol);
126/// ```
127pub fn try_demangle(s: &str) -> Result<Demangle, TryDemangleError> {
128    let sym = demangle(s);
129    if sym.style.is_some() {
130        Ok(sym)
131    } else {
132        Err(TryDemangleError { _priv: () })
133    }
134}
135
136impl<'a> Demangle<'a> {
137    /// Returns the underlying string that's being demangled.
138    pub fn as_str(&self) -> &'a str {
139        self.original
140    }
141}
142
143fn is_symbol_like(s: &str) -> bool {
144    s.chars().all(|c| {
145        // Once `char::is_ascii_punctuation` and `char::is_ascii_alphanumeric`
146        // have been stable for long enough, use those instead for clarity
147        is_ascii_alphanumeric(c) || is_ascii_punctuation(c)
148    })
149}
150
151// Copied from the documentation of `char::is_ascii_alphanumeric`
152fn is_ascii_alphanumeric(c: char) -> bool {
153    match c {
154        '\u{0041}' ..= '\u{005A}' |
155        '\u{0061}' ..= '\u{007A}' |
156        '\u{0030}' ..= '\u{0039}' => true,
157        _ => false,
158    }
159}
160
161// Copied from the documentation of `char::is_ascii_punctuation`
162fn is_ascii_punctuation(c: char) -> bool {
163    match c {
164        '\u{0021}' ..= '\u{002F}' |
165        '\u{003A}' ..= '\u{0040}' |
166        '\u{005B}' ..= '\u{0060}' |
167        '\u{007B}' ..= '\u{007E}' => true,
168        _ => false,
169    }
170}
171
172impl<'a> fmt::Display for Demangle<'a> {
173    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
174        match self.style {
175            None => f.write_str(self.original)?,
176            Some(DemangleStyle::Legacy(ref d)) => {
177                fmt::Display::fmt(d, f)?
178            }
179            Some(DemangleStyle::V0(ref d)) => {
180                fmt::Display::fmt(d, f)?
181            }
182        }
183        f.write_str(self.suffix)
184    }
185}
186
187impl<'a> fmt::Debug for Demangle<'a> {
188    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
189        fmt::Display::fmt(self, f)
190    }
191}