Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add Config for extensible configuration #37

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions src/legacy.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use crate::Config;
use core::char;
use core::fmt;
use core::fmt::Display;

/// Representation of a demangled symbol name.
pub struct Demangle<'a> {
pub(crate) struct Demangle<'a> {
inner: &'a str,
/// The number of ::-separated elements in the original name.
elements: usize,
Expand Down Expand Up @@ -46,7 +48,7 @@ pub struct Demangle<'a> {
// Note that this demangler isn't quite as fancy as it could be. We have lots
// of other information in our symbols like hashes, version, type information,
// etc. Additionally, this doesn't handle glue symbols at all.
pub fn demangle(s: &str) -> Result<(Demangle, &str), ()> {
pub(crate) fn demangle(s: &str) -> Result<(Demangle, &str), ()> {
// First validate the symbol. If it doesn't look like anything we're
// expecting, we just print it literally. Note that we must handle non-Rust
// symbols because we could have any function in the backtrace.
Expand Down Expand Up @@ -102,8 +104,8 @@ fn is_rust_hash(s: &str) -> bool {
s.starts_with('h') && s[1..].chars().all(|c| c.is_digit(16))
}

impl<'a> fmt::Display for Demangle<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
impl<'a> Demangle<'a> {
pub(crate) fn fmt(&self, f: &mut fmt::Formatter, config: &Config) -> fmt::Result {
// Alright, let's do this.
let mut inner = self.inner;
for element in 0..self.elements {
Expand All @@ -114,9 +116,8 @@ impl<'a> fmt::Display for Demangle<'a> {
let i: usize = inner[..(inner.len() - rest.len())].parse().unwrap();
inner = &rest[i..];
rest = &rest[..i];
// Skip printing the hash if alternate formatting
// was requested.
if f.alternate() && element + 1 == self.elements && is_rust_hash(&rest) {
// Skip printing the hash if requested.
if !config.with_hash && element + 1 == self.elements && is_rust_hash(&rest) {
break;
}
if element != 0 {
Expand Down Expand Up @@ -208,7 +209,13 @@ mod tests {

macro_rules! t_nohash {
($a:expr, $b:expr) => {{
assert_eq!(format!("{:#}", ::demangle($a)), $b);
assert_eq!(
format!(
"{}",
crate::demangle_with_config($a, crate::Config::new().with_hash(false))
),
$b
);
}};
}

Expand Down
106 changes: 100 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
//! # Examples
//!
//! ```
//! use rustc_demangle::demangle;
//! use rustc_demangle::{demangle, Config, demangle_with_config};
//!
//! assert_eq!(demangle("_ZN4testE").to_string(), "test");
//! assert_eq!(demangle("_ZN3foo3barE").to_string(), "foo::bar");
Expand All @@ -21,6 +21,8 @@
//! assert_eq!(format!("{}", demangle("_ZN3foo17h05af221e174051e9E")), "foo::h05af221e174051e9");
//! // Without hash
//! assert_eq!(format!("{:#}", demangle("_ZN3foo17h05af221e174051e9E")), "foo");
//! // Without hash by clearing Config::with_hash
//! assert_eq!(format!("{}", demangle_with_config("_ZN3foo17h05af221e174051e9E", Config::new().with_hash(false))), "foo");
//! ```

#![no_std]
Expand All @@ -35,9 +37,36 @@ mod v0;

use core::fmt;

/// demangle configuration
#[derive(Clone, Debug)]
pub struct Config {
with_hash: bool,
// extend with more config options in the future
}

impl Default for Config {
fn default() -> Self {
Self { with_hash: true }
}
}

impl Config {
/// create the default demangle configuration
pub fn new() -> Self {
Self::default()
}
/// set if disambiguating hashes should be displayed. This is the default.
///
/// using `format!("{:#}", ...)` is the alternate method of disabling `with_hash`.
pub fn with_hash(self, with_hash: bool) -> Self {
Self { with_hash, ..self }
}
}

/// Representation of a demangled symbol name.
pub struct Demangle<'a> {
style: Option<DemangleStyle<'a>>,
config: Config,
original: &'a str,
suffix: &'a str,
}
Expand All @@ -62,7 +91,26 @@ enum DemangleStyle<'a> {
/// assert_eq!(demangle("_ZN3foo3barE").to_string(), "foo::bar");
/// assert_eq!(demangle("foo").to_string(), "foo");
/// ```
pub fn demangle(mut s: &str) -> Demangle {
pub fn demangle(s: &str) -> Demangle {
demangle_with_config(s, Config::new())
}

/// 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");
/// ```
pub fn demangle_with_config(mut s: &str, config: Config) -> 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.
Expand Down Expand Up @@ -108,6 +156,7 @@ pub fn demangle(mut s: &str) -> Demangle {

Demangle {
style,
config,
original: s,
suffix,
}
Expand All @@ -134,7 +183,25 @@ pub struct TryDemangleError {
/// assert_eq!(rustc_demangle::demangle(not_a_rust_symbol).as_str(), not_a_rust_symbol);
/// ```
pub fn try_demangle(s: &str) -> Result<Demangle, TryDemangleError> {
let sym = demangle(s);
try_demangle_with_config(s, Config::new())
}

/// 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);
/// ```
pub fn try_demangle_with_config(s: &str, config: Config) -> Result<Demangle, TryDemangleError> {
let sym = demangle_with_config(s, config);
if sym.style.is_some() {
Ok(sym)
} else {
Expand All @@ -147,6 +214,10 @@ impl<'a> Demangle<'a> {
pub fn as_str(&self) -> &'a str {
self.original
}
/// Set the demangling configuration
pub fn with_config(self, config: Config) -> Self {
Self { config, ..self }
}
}

fn is_symbol_like(s: &str) -> bool {
Expand Down Expand Up @@ -178,10 +249,14 @@ fn is_ascii_punctuation(c: char) -> bool {

impl<'a> fmt::Display for Demangle<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut config = self.config.clone();
if f.alternate() {
config = config.with_hash(false);
}
match self.style {
None => f.write_str(self.original)?,
Some(DemangleStyle::Legacy(ref d)) => fmt::Display::fmt(d, f)?,
Some(DemangleStyle::V0(ref d)) => fmt::Display::fmt(d, f)?,
Some(DemangleStyle::Legacy(ref d)) => d.fmt(f, &config)?,
Some(DemangleStyle::V0(ref d)) => d.fmt(f, &config)?,
}
f.write_str(self.suffix)
}
Expand Down Expand Up @@ -209,12 +284,24 @@ mod tests {
};
}

macro_rules! t_nohash {
macro_rules! t_nohash_alt {
($a:expr, $b:expr) => {{
assert_eq!(format!("{:#}", super::demangle($a)), $b);
}};
}

macro_rules! t_nohash {
($a:expr, $b:expr) => {{
assert_eq!(
format!(
"{}",
super::demangle_with_config($a, super::Config::new().with_hash(false))
),
$b
);
}};
}

fn ok(sym: &str, expected: &str) -> bool {
match super::try_demangle(sym) {
Ok(s) => {
Expand Down Expand Up @@ -304,6 +391,13 @@ mod tests {
t_nohash!(s, "foo");
}

#[test]
fn demangle_without_hash_alt() {
let s = "_ZN3foo17h05af221e174051e9E";
t!(s, "foo::h05af221e174051e9");
t_nohash_alt!(s, "foo");
}

#[test]
fn demangle_without_hash_edgecases() {
// One element, no hash.
Expand Down
28 changes: 19 additions & 9 deletions src/v0.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use crate::Config;
use core::char;
use core::fmt;
use core::fmt::Display;

/// Representation of a demangled symbol name.
pub struct Demangle<'a> {
pub(crate) struct Demangle<'a> {
inner: &'a str,
}

Expand All @@ -12,7 +13,7 @@ pub struct Demangle<'a> {
/// 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.
pub fn demangle(s: &str) -> Result<(Demangle, &str), Invalid> {
pub(crate) fn demangle(s: &str) -> Result<(Demangle, &str), Invalid> {
// First validate the symbol. If it doesn't look like anything we're
// expecting, we just print it literally. Note that we must handle non-Rust
// symbols because we could have any function in the backtrace.
Expand Down Expand Up @@ -56,22 +57,23 @@ pub fn demangle(s: &str) -> Result<(Demangle, &str), Invalid> {
Ok((Demangle { inner }, &parser.sym[parser.next..]))
}

impl<'s> Display for Demangle<'s> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
impl<'s> Demangle<'s> {
pub(crate) fn fmt(&self, f: &mut fmt::Formatter, config: &Config) -> fmt::Result {
let mut printer = Printer {
parser: Ok(Parser {
sym: self.inner,
next: 0,
}),
out: f,
config,
bound_lifetime_depth: 0,
};
printer.print_path(true)
}
}

#[derive(PartialEq, Eq)]
pub struct Invalid;
pub(crate) struct Invalid;

struct Ident<'s> {
/// ASCII part of the identifier.
Expand Down Expand Up @@ -205,7 +207,7 @@ impl<'s> Ident<'s> {
}
}

impl<'s> Display for Ident<'s> {
impl<'s> Ident<'s> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.try_small_punycode_decode(|chars| {
for &c in chars {
Expand Down Expand Up @@ -550,6 +552,7 @@ impl<'s> Parser<'s> {
struct Printer<'a, 'b: 'a, 's> {
parser: Result<Parser<'s>, Invalid>,
out: &'a mut fmt::Formatter<'b>,
config: &'a Config,
bound_lifetime_depth: u32,
}

Expand Down Expand Up @@ -594,6 +597,7 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> {
Printer {
parser: self.parser_mut().and_then(|p| p.backref()),
out: self.out,
config: self.config,
bound_lifetime_depth: self.bound_lifetime_depth,
}
}
Expand Down Expand Up @@ -677,7 +681,7 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> {
let name = parse!(self, ident);

name.fmt(self.out)?;
if !self.out.alternate() {
if self.config.with_hash {
self.out.write_str("[")?;
fmt::LowerHex::fmt(&dis, self.out)?;
self.out.write_str("]")?;
Expand Down Expand Up @@ -949,7 +953,7 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> {
self.print_const_uint()?;
}

if !self.out.alternate() {
if self.config.with_hash {
self.out.write_str(": ")?;
self.out.write_str(ty)?;
}
Expand Down Expand Up @@ -978,7 +982,13 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> {
mod tests {
macro_rules! t_nohash {
($a:expr, $b:expr) => {{
assert_eq!(format!("{:#}", ::demangle($a)), $b);
assert_eq!(
format!(
"{}",
crate::demangle_with_config($a, crate::Config::new().with_hash(false))
),
$b
);
}};
}
macro_rules! t_nohash_type {
Expand Down