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}