//! Demangle Rust compiler symbol names. //! //! This crate provides a `demangle` function which will return a `Demangle` //! sentinel value that can be used to learn about the demangled version of a //! symbol name. The demangled representation will be the same as the original //! if it doesn't look like a mangled symbol name. //! //! `Demangle` can be formatted with the `Display` trait. The alternate //! modifier (`#`) can be used to format the symbol name without the //! trailing hash value. //! //! # Examples //! //! ``` //! use rustc_demangle::demangle; //! //! assert_eq!(demangle("_ZN4testE").to_string(), "test"); //! assert_eq!(demangle("_ZN3foo3barE").to_string(), "foo::bar"); //! assert_eq!(demangle("foo").to_string(), "foo"); //! // With hash //! assert_eq!(format!("{}", demangle("_ZN3foo17h05af221e174051e9E")), "foo::h05af221e174051e9"); //! // Without hash //! assert_eq!(format!("{:#}", demangle("_ZN3foo17h05af221e174051e9E")), "foo"); //! ```
/// De-mangles a Rust symbol into a more readable version /// /// This function will take a **mangled** symbol and return a value. When printed, /// the de-mangled version will be written. If the symbol does not look like /// a mangled symbol, the original value will be written instead. /// /// # Examples /// /// ``` /// use rustc_demangle::demangle; /// /// assert_eq!(demangle("_ZN4testE").to_string(), "test"); /// assert_eq!(demangle("_ZN3foo3barE").to_string(), "foo::bar"); /// assert_eq!(demangle("foo").to_string(), "foo"); /// ``` pubfn 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."; iflet 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,
_ => false,
});
if all_hex {
s = &s[..i];
}
}
letmut suffix = ""; letmut style = match legacy::demangle(s) {
Ok((d, s)) => {
suffix = s;
Some(DemangleStyle::Legacy(d))
}
Err(()) => match v0::demangle(s) {
Ok((d, s)) => {
suffix = s;
Some(DemangleStyle::V0(d))
} // FIXME(eddyb) would it make sense to treat an unknown-validity // symbol (e.g. one that errored with `RecursedTooDeep`) as // v0-mangled, and have the error show up in the demangling? // (that error already gets past this initial check, and therefore // will show up in the demangling, if hidden behind a backref)
Err(v0::ParseError::Invalid) | Err(v0::ParseError::RecursedTooDeep) => None,
},
};
// Output like LLVM IR adds extra period-delimited words. See if // we are in that case and save the trailing words if so. if !suffix.is_empty() { if suffix.starts_with('.') && is_symbol_like(suffix) { // Keep the suffix.
} else { // Reset the suffix and invalidate the demangling.
suffix = "";
style = None;
}
}
Demangle {
style,
original: s,
suffix,
}
}
/// Error returned from the `try_demangle` function below when demangling fails. #[derive(Debug, Clone)] pubstruct TryDemangleError {
_priv: (),
}
/// The same as `demangle`, except return an `Err` if the string does not appear /// to be a Rust symbol, rather than "demangling" the given string as a no-op. /// /// ``` /// extern crate rustc_demangle; /// /// let not_a_rust_symbol = "la la la"; /// /// // The `try_demangle` function will reject strings which are not Rust symbols. /// assert!(rustc_demangle::try_demangle(not_a_rust_symbol).is_err()); /// /// // 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); /// ``` pubfn try_demangle(s: &str) -> Result<Demangle, TryDemangleError> { let sym = demangle(s); if sym.style.is_some() {
Ok(sym)
} else {
Err(TryDemangleError { _priv: () })
}
}
impl<'a> Demangle<'a> { /// Returns the underlying string that's being demangled. pubfn as_str(&self) -> &'a str { self.original
}
}
fn is_symbol_like(s: &str) -> bool {
s.chars().all(|c| { // Once `char::is_ascii_punctuation` and `char::is_ascii_alphanumeric` // have been stable for long enough, use those instead for clarity
is_ascii_alphanumeric(c) || is_ascii_punctuation(c)
})
}
// Copied from the documentation of `char::is_ascii_alphanumeric` fn is_ascii_alphanumeric(c: char) -> bool { match c { '\u{0041}'..='\u{005A}' | '\u{0061}'..='\u{007A}' | '\u{0030}'..='\u{0039}' => true,
_ => false,
}
}
// Copied from the documentation of `char::is_ascii_punctuation` fn is_ascii_punctuation(c: char) -> bool { match c { '\u{0021}'..='\u{002F}'
| '\u{003A}'..='\u{0040}'
| '\u{005B}'..='\u{0060}'
| '\u{007B}'..='\u{007E}' => true,
_ => false,
}
}
// Translate a `fmt::Error` generated by `SizeLimitedFmtAdapter` // into an error message, instead of propagating it upwards // (which could cause panicking from inside e.g. `std::io::print`). match (fmt_result, size_limit_result) {
(Err(_), Err(SizeLimitExhausted)) => f.write_str("{size limit reached}")?,
_ => {
fmt_result?;
size_limit_result
.expect("`fmt::Error` from `SizeLimitedFmtAdapter` was discarded");
}
}
}
}
f.write_str(self.suffix)
}
}
#[test] fn demangle_without_hash() { let s = "_ZN3foo17h05af221e174051e9E";
t!(s, "foo::h05af221e174051e9");
t_nohash!(s, "foo");
}
#[test] fn demangle_without_hash_edgecases() { // One element, no hash.
t_nohash!("_ZN3fooE", "foo"); // Two elements, no hash.
t_nohash!("_ZN3foo3barE", "foo::bar"); // Longer-than-normal hash.
t_nohash!("_ZN3foo20h05af221e174051e9abcE", "foo"); // Shorter-than-normal hash.
t_nohash!("_ZN3foo5h05afE", "foo"); // Valid hash, but not at the end.
t_nohash!("_ZN17h05af221e174051e93fooE", "h05af221e174051e9::foo"); // Not a valid hash, missing the 'h'.
t_nohash!("_ZN3foo16ffaf221e174051e9E", "foo::ffaf221e174051e9"); // Not a valid hash, has a non-hex-digit.
t_nohash!("_ZN3foo17hg5af221e174051e9E", "foo::hg5af221e174051e9");
}
#[test] fn demangle_thinlto() { // One element, no hash.
t!("_ZN3fooE.llvm.9D1C9369", "foo");
t!("_ZN3fooE.llvm.9D1C9369@@16", "foo");
t_nohash!( "_ZN9backtrace3foo17hbb467fcdaea5d79bE.llvm.A5310EB9", "backtrace::foo"
);
}
#[test] fn demangle_llvm_ir_branch_labels() {
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");
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");
}
#[test] fn limit_output() {
assert_ends_with!( super::demangle("RYFG_FGyyEvRYFF_EvRYFFEvERLB_B_B_ERLRjB_B_B_").to_string(), "{size limit reached}"
); // NOTE(eddyb) somewhat reduced version of the above, effectively // `<for<...> fn()>` with a larger number of lifetimes in `...`.
assert_ends_with!( super::demangle("_RMC0FGZZZ_Eu").to_string(), "{size limit reached}"
);
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.19 Sekunden
(vorverarbeitet am 2026-06-18)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.