diff -Nru rust-rustc-demangle-0.1.13/Cargo.toml rust-rustc-demangle-0.1.16/Cargo.toml --- rust-rustc-demangle-0.1.13/Cargo.toml 1970-01-01 00:00:00.000000000 +0000 +++ rust-rustc-demangle-0.1.16/Cargo.toml 1970-01-01 00:00:00.000000000 +0000 @@ -3,7 +3,7 @@ # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g. crates.io) dependencies +# to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're @@ -12,7 +12,7 @@ [package] name = "rustc-demangle" -version = "0.1.13" +version = "0.1.16" authors = ["Alex Crichton "] description = "Rust compiler symbol demangling.\n" homepage = "https://github.com/alexcrichton/rustc-demangle" @@ -20,3 +20,14 @@ readme = "README.md" license = "MIT/Apache-2.0" repository = "https://github.com/alexcrichton/rustc-demangle" +[dependencies.compiler_builtins] +version = "0.1.2" +optional = true + +[dependencies.core] +version = "1.0.0" +optional = true +package = "rustc-std-workspace-core" + +[features] +rustc-dep-of-std = ["core", "compiler_builtins"] diff -Nru rust-rustc-demangle-0.1.13/Cargo.toml.orig rust-rustc-demangle-0.1.16/Cargo.toml.orig --- rust-rustc-demangle-0.1.13/Cargo.toml.orig 2019-01-02 15:06:55.000000000 +0000 +++ rust-rustc-demangle-0.1.16/Cargo.toml.orig 2019-08-13 16:00:23.000000000 +0000 @@ -1,6 +1,6 @@ [package] name = "rustc-demangle" -version = "0.1.13" +version = "0.1.16" authors = ["Alex Crichton "] license = "MIT/Apache-2.0" readme = "README.md" @@ -10,3 +10,10 @@ description = """ Rust compiler symbol demangling. """ + +[dependencies] +core = { version = '1.0.0', optional = true, package = 'rustc-std-workspace-core' } +compiler_builtins = { version = '0.1.2', optional = true } + +[features] +rustc-dep-of-std = ['core', 'compiler_builtins'] diff -Nru rust-rustc-demangle-0.1.13/.cargo_vcs_info.json rust-rustc-demangle-0.1.16/.cargo_vcs_info.json --- rust-rustc-demangle-0.1.13/.cargo_vcs_info.json 1970-01-01 00:00:00.000000000 +0000 +++ rust-rustc-demangle-0.1.16/.cargo_vcs_info.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +1,5 @@ { "git": { - "sha1": "a099f0a490100f4f7c6b1a5717eda1c599c8b95e" + "sha1": "23f19337afbadec16b80d616551ada5ee02f73f0" } } diff -Nru rust-rustc-demangle-0.1.13/debian/cargo-checksum.json rust-rustc-demangle-0.1.16/debian/cargo-checksum.json --- rust-rustc-demangle-0.1.13/debian/cargo-checksum.json 2019-01-22 08:56:02.000000000 +0000 +++ rust-rustc-demangle-0.1.16/debian/cargo-checksum.json 2020-04-11 22:02:29.000000000 +0000 @@ -1 +1 @@ -{"package":"adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619","files":{}} +{"package":"4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783","files":{}} diff -Nru rust-rustc-demangle-0.1.13/debian/changelog rust-rustc-demangle-0.1.16/debian/changelog --- rust-rustc-demangle-0.1.13/debian/changelog 2019-01-22 08:56:02.000000000 +0000 +++ rust-rustc-demangle-0.1.16/debian/changelog 2020-04-11 22:02:29.000000000 +0000 @@ -1,3 +1,33 @@ +rust-rustc-demangle (0.1.16-4) unstable; urgency=medium + + * Team upload. + * Package rustc-demangle 0.1.16 from crates.io using debcargo 2.4.2 + * disable librust-rustc-demangle+core-dev + librust-rustc-demangle+rustc-dep-of-std-dev tests as they are using nightly + feature + + -- Sylvestre Ledru Sun, 12 Apr 2020 00:02:29 +0200 + +rust-rustc-demangle (0.1.16-3) unstable; urgency=medium + + * Package rustc-demangle 0.1.16 from crates.io using debcargo 2.4.2 + * Source-only reupload for migration to testing + + -- Wolfgang Silbermayr Thu, 23 Jan 2020 18:19:23 +0100 + +rust-rustc-demangle (0.1.16-2) unstable; urgency=medium + + * Team upload. + * Package rustc-demangle 0.1.16 from crates.io using debcargo 2.4.2 + + -- Ximin Luo Sat, 04 Jan 2020 22:12:48 +0000 + +rust-rustc-demangle (0.1.16-1) unstable; urgency=medium + + * Package rustc-demangle 0.1.16 from crates.io using debcargo 2.4.0 + + -- Wolfgang Silbermayr Sun, 10 Nov 2019 14:01:54 +0100 + rust-rustc-demangle (0.1.13-1) unstable; urgency=medium * Package rustc-demangle 0.1.13 from crates.io using debcargo 2.2.9 diff -Nru rust-rustc-demangle-0.1.13/debian/control rust-rustc-demangle-0.1.16/debian/control --- rust-rustc-demangle-0.1.13/debian/control 2019-01-22 08:56:02.000000000 +0000 +++ rust-rustc-demangle-0.1.16/debian/control 2020-04-11 22:02:29.000000000 +0000 @@ -2,7 +2,7 @@ Section: rust Priority: optional Build-Depends: debhelper (>= 11), - dh-cargo (>= 10), + dh-cargo (>= 18), cargo:native , rustc:native , libstd-rust-dev @@ -10,7 +10,7 @@ Uploaders: kpcyrd , Wolfgang Silbermayr -Standards-Version: 4.2.0 +Standards-Version: 4.4.1 Vcs-Git: https://salsa.debian.org/rust-team/debcargo-conf.git [src/rustc-demangle] Vcs-Browser: https://salsa.debian.org/rust-team/debcargo-conf/tree/master/src/rustc-demangle Homepage: https://github.com/alexcrichton/rustc-demangle @@ -20,14 +20,65 @@ Multi-Arch: same Depends: ${misc:Depends} +Suggests: + librust-rustc-demangle+compiler-builtins-dev (= ${binary:Version}), + librust-rustc-demangle+core-dev (= ${binary:Version}), + librust-rustc-demangle+rustc-dep-of-std-dev (= ${binary:Version}) Provides: librust-rustc-demangle+default-dev (= ${binary:Version}), librust-rustc-demangle-0-dev (= ${binary:Version}), librust-rustc-demangle-0+default-dev (= ${binary:Version}), librust-rustc-demangle-0.1-dev (= ${binary:Version}), librust-rustc-demangle-0.1+default-dev (= ${binary:Version}), - librust-rustc-demangle-0.1.13-dev (= ${binary:Version}), - librust-rustc-demangle-0.1.13+default-dev (= ${binary:Version}) + librust-rustc-demangle-0.1.16-dev (= ${binary:Version}), + librust-rustc-demangle-0.1.16+default-dev (= ${binary:Version}) Description: Rust compiler symbol demangling - Rust source code This package contains the source for the Rust rustc-demangle crate, packaged by debcargo for use with cargo and dh-cargo. + +Package: librust-rustc-demangle+compiler-builtins-dev +Architecture: any +Multi-Arch: same +Depends: + ${misc:Depends}, + librust-rustc-demangle-dev (= ${binary:Version}), + librust-compiler-builtins-0.1+default-dev (>= 0.1.2-~~) +Provides: + librust-rustc-demangle-0+compiler-builtins-dev (= ${binary:Version}), + librust-rustc-demangle-0.1+compiler-builtins-dev (= ${binary:Version}), + librust-rustc-demangle-0.1.16+compiler-builtins-dev (= ${binary:Version}) +Description: Rust compiler symbol demangling - feature "compiler_builtins" + This metapackage enables feature "compiler_builtins" for the Rust rustc- + demangle crate, by pulling in any additional dependencies needed by that + feature. + +Package: librust-rustc-demangle+core-dev +Architecture: any +Multi-Arch: same +Depends: + ${misc:Depends}, + librust-rustc-demangle-dev (= ${binary:Version}), + librust-rustc-std-workspace-core-1+default-dev +Provides: + librust-rustc-demangle-0+core-dev (= ${binary:Version}), + librust-rustc-demangle-0.1+core-dev (= ${binary:Version}), + librust-rustc-demangle-0.1.16+core-dev (= ${binary:Version}) +Description: Rust compiler symbol demangling - feature "core" + This metapackage enables feature "core" for the Rust rustc-demangle crate, by + pulling in any additional dependencies needed by that feature. + +Package: librust-rustc-demangle+rustc-dep-of-std-dev +Architecture: any +Multi-Arch: same +Depends: + ${misc:Depends}, + librust-rustc-demangle-dev (= ${binary:Version}), + librust-compiler-builtins-0.1+default-dev (>= 0.1.2-~~), + librust-rustc-std-workspace-core-1+default-dev +Provides: + librust-rustc-demangle-0+rustc-dep-of-std-dev (= ${binary:Version}), + librust-rustc-demangle-0.1+rustc-dep-of-std-dev (= ${binary:Version}), + librust-rustc-demangle-0.1.16+rustc-dep-of-std-dev (= ${binary:Version}) +Description: Rust compiler symbol demangling - feature "rustc-dep-of-std" + This metapackage enables feature "rustc-dep-of-std" for the Rust rustc-demangle + crate, by pulling in any additional dependencies needed by that feature. diff -Nru rust-rustc-demangle-0.1.13/debian/copyright rust-rustc-demangle-0.1.16/debian/copyright --- rust-rustc-demangle-0.1.13/debian/copyright 2019-01-22 08:56:02.000000000 +0000 +++ rust-rustc-demangle-0.1.16/debian/copyright 2020-04-11 22:02:29.000000000 +0000 @@ -9,9 +9,9 @@ Files: debian/* Copyright: - 2018-2019 Debian Rust Maintainers + 2018-2020 Debian Rust Maintainers 2018 kpcyrd - 2019 Wolfgang Silbermayr + 2019-2020 Wolfgang Silbermayr License: MIT or Apache-2.0 License: Apache-2.0 diff -Nru rust-rustc-demangle-0.1.13/debian/copyright.debcargo.hint rust-rustc-demangle-0.1.16/debian/copyright.debcargo.hint --- rust-rustc-demangle-0.1.13/debian/copyright.debcargo.hint 2019-01-22 08:56:02.000000000 +0000 +++ rust-rustc-demangle-0.1.16/debian/copyright.debcargo.hint 2020-04-11 22:02:29.000000000 +0000 @@ -21,9 +21,9 @@ Files: debian/* Copyright: - 2018-2019 Debian Rust Maintainers - 2018-2019 kpcyrd - 2018-2019 Wolfgang Silbermayr + 2018-2020 Debian Rust Maintainers + 2018-2020 kpcyrd + 2018-2020 Wolfgang Silbermayr License: MIT or Apache-2.0 License: Apache-2.0 diff -Nru rust-rustc-demangle-0.1.13/debian/debcargo.toml rust-rustc-demangle-0.1.16/debian/debcargo.toml --- rust-rustc-demangle-0.1.13/debian/debcargo.toml 2019-01-22 08:56:02.000000000 +0000 +++ rust-rustc-demangle-0.1.16/debian/debcargo.toml 2020-04-11 22:02:29.000000000 +0000 @@ -4,3 +4,9 @@ "Wolfgang Silbermayr ", ] bin = false + +[packages."lib+core"] +test_is_broken = true + +[packages."lib+rustc-dep-of-std"] +test_is_broken = true diff -Nru rust-rustc-demangle-0.1.13/debian/rules rust-rustc-demangle-0.1.16/debian/rules --- rust-rustc-demangle-0.1.13/debian/rules 2019-01-22 08:56:02.000000000 +0000 +++ rust-rustc-demangle-0.1.16/debian/rules 2020-04-11 22:02:29.000000000 +0000 @@ -1,3 +1,6 @@ #!/usr/bin/make -f %: dh $@ --buildsystem cargo + +override_dh_auto_test: + dh_auto_test -- test --all diff -Nru rust-rustc-demangle-0.1.13/debian/tests/control rust-rustc-demangle-0.1.16/debian/tests/control --- rust-rustc-demangle-0.1.13/debian/tests/control 1970-01-01 00:00:00.000000000 +0000 +++ rust-rustc-demangle-0.1.16/debian/tests/control 2020-04-11 22:02:29.000000000 +0000 @@ -0,0 +1,24 @@ +Test-Command: /usr/share/cargo/bin/cargo-auto-test rustc-demangle 0.1.16 --all-targets --all-features +Features: test-name=@ +Depends: dh-cargo (>= 18), @ +Restrictions: allow-stderr, skip-not-installable, flaky + +Test-Command: /usr/share/cargo/bin/cargo-auto-test rustc-demangle 0.1.16 --all-targets --no-default-features +Features: test-name=librust-rustc-demangle-dev +Depends: dh-cargo (>= 18), @ +Restrictions: allow-stderr, skip-not-installable + +Test-Command: /usr/share/cargo/bin/cargo-auto-test rustc-demangle 0.1.16 --all-targets --features compiler_builtins +Features: test-name=librust-rustc-demangle+compiler-builtins-dev +Depends: dh-cargo (>= 18), @ +Restrictions: allow-stderr, skip-not-installable + +Test-Command: /usr/share/cargo/bin/cargo-auto-test rustc-demangle 0.1.16 --all-targets --features core +Features: test-name=librust-rustc-demangle+core-dev +Depends: dh-cargo (>= 18), @ +Restrictions: allow-stderr, skip-not-installable, flaky + +Test-Command: /usr/share/cargo/bin/cargo-auto-test rustc-demangle 0.1.16 --all-targets --features rustc-dep-of-std +Features: test-name=librust-rustc-demangle+rustc-dep-of-std-dev +Depends: dh-cargo (>= 18), @ +Restrictions: allow-stderr, skip-not-installable, flaky diff -Nru rust-rustc-demangle-0.1.13/debian/watch rust-rustc-demangle-0.1.16/debian/watch --- rust-rustc-demangle-0.1.13/debian/watch 2019-01-22 08:56:02.000000000 +0000 +++ rust-rustc-demangle-0.1.16/debian/watch 2020-04-11 22:02:29.000000000 +0000 @@ -2,4 +2,3 @@ opts=filenamemangle=s/.*\/(.*)\/download/rustc-demangle-$1\.tar\.gz/g,\ uversionmangle=s/(\d)[_\.\-\+]?((RC|rc|pre|dev|beta|alpha)\d*)$/$1~$2/ \ https://qa.debian.org/cgi-bin/fakeupstream.cgi?upstream=crates.io/rustc-demangle .*/crates/rustc-demangle/@ANY_VERSION@/download - diff -Nru rust-rustc-demangle-0.1.13/src/legacy.rs rust-rustc-demangle-0.1.16/src/legacy.rs --- rust-rustc-demangle-0.1.13/src/legacy.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-rustc-demangle-0.1.16/src/legacy.rs 2019-08-13 16:00:15.000000000 +0000 @@ -0,0 +1,379 @@ +use core::char; +use core::fmt; + +/// Representation of a demangled symbol name. +pub struct Demangle<'a> { + inner: &'a str, + /// The number of ::-separated elements in the original name. + elements: usize, +} + +/// De-mangles a Rust symbol into a more readable version +/// +/// All Rust symbols by default are mangled as they contain characters that +/// cannot be represented in all object files. The mangling mechanism is similar +/// to C++'s, but Rust has a few specifics to handle items like lifetimes in +/// symbols. +/// +/// 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"); +/// ``` + +// All Rust symbols are in theory lists of "::"-separated identifiers. Some +// assemblers, however, can't handle these characters in symbol names. To get +// around this, we use C++-style mangling. The mangling method is: +// +// 1. Prefix the symbol with "_ZN" +// 2. For each element of the path, emit the length plus the element +// 3. End the path with "E" +// +// For example, "_ZN4testE" => "test" and "_ZN3foo3barE" => "foo::bar". +// +// We're the ones printing our backtraces, so we can't rely on anything else to +// demangle our symbols. It's *much* nicer to look at demangled symbols, so +// this function is implemented to give us nice pretty output. +// +// 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), ()> { + // 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. + let inner = if s.starts_with("_ZN") { + &s[3..] + } else if s.starts_with("ZN") { + // On Windows, dbghelp strips leading underscores, so we accept "ZN...E" + // form too. + &s[2..] + } else if s.starts_with("__ZN") { + // On OSX, symbols are prefixed with an extra _ + &s[4..] + } else { + return Err(()); + }; + + // only work with ascii text + if inner.bytes().any(|c| c & 0x80 != 0) { + return Err(()); + } + + let mut elements = 0; + let mut chars = inner.chars(); + let mut c = try!(chars.next().ok_or(())); + while c != 'E' { + // Decode an identifier element's length. + if !c.is_digit(10) { + return Err(()); + } + let mut len = 0usize; + while let Some(d) = c.to_digit(10) { + len = try!(len.checked_mul(10) + .and_then(|len| len.checked_add(d as usize)) + .ok_or(())); + c = try!(chars.next().ok_or(())); + } + + // `c` already contains the first character of this identifier, skip it and + // all the other characters of this identifier, to reach the next element. + for _ in 0..len { + c = try!(chars.next().ok_or(())); + } + + elements += 1; + } + + Ok((Demangle { + inner: inner, + elements: elements, + }, chars.as_str())) +} + +// Rust hashes are hex digits with an `h` prepended. +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 { + // Alright, let's do this. + let mut inner = self.inner; + for element in 0..self.elements { + let mut rest = inner; + while rest.chars().next().unwrap().is_digit(10) { + rest = &rest[1..]; + } + 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) { + break; + } + if element != 0 { + try!(f.write_str("::")); + } + if rest.starts_with("_$") { + rest = &rest[1..]; + } + loop { + if rest.starts_with('.') { + if let Some('.') = rest[1..].chars().next() { + try!(f.write_str("::")); + rest = &rest[2..]; + } else { + try!(f.write_str(".")); + rest = &rest[1..]; + } + } else if rest.starts_with('$') { + let (escape, after_escape) = if let Some(end) = rest[1..].find('$') { + (&rest[1..end + 1], &rest[end + 2..]) + } else { + break; + }; + + // see src/librustc_codegen_utils/symbol_names/legacy.rs for these mappings + let unescaped = match escape { + "SP" => "@", + "BP" => "*", + "RF" => "&", + "LT" => "<", + "GT" => ">", + "LP" => "(", + "RP" => ")", + "C" => ",", + + _ => { + if escape.starts_with('u') { + let digits = &escape[1..]; + let all_lower_hex = digits.chars().all(|c| match c { + '0'...'9' | 'a'...'f' => true, + _ => false, + }); + let c = u32::from_str_radix(digits, 16).ok() + .and_then(char::from_u32); + if let (true, Some(c)) = (all_lower_hex, c) { + // FIXME(eddyb) do we need to filter out control codepoints? + if !c.is_control() { + try!(c.fmt(f)); + rest = after_escape; + continue; + } + } + } + break; + } + }; + try!(f.write_str(unescaped)); + rest = after_escape; + } else if let Some(i) = rest.find(|c| c == '$' || c == '.') { + try!(f.write_str(&rest[..i])); + rest = &rest[i..]; + } else { + break; + } + } + try!(f.write_str(rest)); + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use std::prelude::v1::*; + + macro_rules! t { + ($a:expr, $b:expr) => (assert!(ok($a, $b))) + } + + macro_rules! t_err { + ($a:expr) => (assert!(ok_err($a))) + } + + macro_rules! t_nohash { + ($a:expr, $b:expr) => ({ + assert_eq!(format!("{:#}", ::demangle($a)), $b); + }) + } + + fn ok(sym: &str, expected: &str) -> bool { + match ::try_demangle(sym) { + Ok(s) => { + if s.to_string() == expected { + true + } else { + println!("\n{}\n!=\n{}\n", s, expected); + false + } + } + Err(_) => { + println!("error demangling"); + false + } + } + } + + fn ok_err(sym: &str) -> bool { + match ::try_demangle(sym) { + Ok(_) => { + println!("succeeded in demangling"); + false + } + Err(_) => ::demangle(sym).to_string() == sym, + } + } + + #[test] + fn demangle() { + t_err!("test"); + t!("_ZN4testE", "test"); + t_err!("_ZN4test"); + t!("_ZN4test1a2bcE", "test::a::bc"); + } + + #[test] + fn demangle_dollars() { + t!("_ZN4$RP$E", ")"); + t!("_ZN8$RF$testE", "&test"); + t!("_ZN8$BP$test4foobE", "*test::foob"); + t!("_ZN9$u20$test4foobE", " test::foob"); + t!("_ZN35Bar$LT$$u5b$u32$u3b$$u20$4$u5d$$GT$E", "Bar<[u32; 4]>"); + } + + #[test] + fn demangle_many_dollars() { + t!("_ZN13test$u20$test4foobE", "test test::foob"); + t!("_ZN12test$BP$test4foobE", "test*test::foob"); + } + + + #[test] + fn demangle_osx() { + t!("__ZN5alloc9allocator6Layout9for_value17h02a996811f781011E", "alloc::allocator::Layout::for_value::h02a996811f781011"); + t!("__ZN38_$LT$core..option..Option$LT$T$GT$$GT$6unwrap18_MSG_FILE_LINE_COL17haf7cb8d5824ee659E", ">::unwrap::_MSG_FILE_LINE_COL::haf7cb8d5824ee659"); + t!("__ZN4core5slice89_$LT$impl$u20$core..iter..traits..IntoIterator$u20$for$u20$$RF$$u27$a$u20$$u5b$T$u5d$$GT$9into_iter17h450e234d27262170E", "core::slice::::into_iter::h450e234d27262170"); + } + + #[test] + fn demangle_windows() { + t!("ZN4testE", "test"); + t!("ZN13test$u20$test4foobE", "test test::foob"); + t!("ZN12test$RF$test4foobE", "test&test::foob"); + } + + #[test] + fn demangle_elements_beginning_with_underscore() { + t!("_ZN13_$LT$test$GT$E", ""); + t!("_ZN28_$u7b$$u7b$closure$u7d$$u7d$E", "{{closure}}"); + t!("_ZN15__STATIC_FMTSTRE", "__STATIC_FMTSTR"); + } + + #[test] + fn demangle_trait_impls() { + t!("_ZN71_$LT$Test$u20$$u2b$$u20$$u27$static$u20$as$u20$foo..Bar$LT$Test$GT$$GT$3barE", + ">::bar"); + } + + #[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:: 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:: for [T]>::index_mut.exit.i.i"); + } + + #[test] + fn demangle_ignores_suffix_that_doesnt_look_like_a_symbol() { + t_err!("_ZN3fooE.llvm moocow"); + } + + #[test] + fn dont_panic() { + ::demangle("_ZN2222222222222222222222EE").to_string(); + ::demangle("_ZN5*70527e27.ll34csaғE").to_string(); + ::demangle("_ZN5*70527a54.ll34_$b.1E").to_string(); + ::demangle("\ + _ZN5~saäb4e\n\ + 2734cOsbE\n\ + 5usage20h)3\0\0\0\0\0\0\07e2734cOsbE\ + ").to_string(); + } + + #[test] + fn invalid_no_chop() { + t_err!("_ZNfooE"); + } + + #[test] + fn handle_assoc_types() { + t!("_ZN151_$LT$alloc..boxed..Box$LT$alloc..boxed..FnBox$LT$A$C$$u20$Output$u3d$R$GT$$u20$$u2b$$u20$$u27$a$GT$$u20$as$u20$core..ops..function..FnOnce$LT$A$GT$$GT$9call_once17h69e8f44b3723e1caE", " + 'a> as core::ops::function::FnOnce>::call_once::h69e8f44b3723e1ca"); + } + + #[test] + fn handle_bang() { + t!( + "_ZN88_$LT$core..result..Result$LT$$u21$$C$$u20$E$GT$$u20$as$u20$std..process..Termination$GT$6report17hfc41d0da4a40b3e8E", + " as std::process::Termination>::report::hfc41d0da4a40b3e8" + ); + } + + #[test] + fn demangle_utf8_idents() { + t_nohash!( + "_ZN11utf8_idents157_$u10e1$$u10d0$$u10ed$$u10db$$u10d4$$u10da$$u10d0$$u10d3$_$u10d2$$u10d4$$u10db$$u10e0$$u10d8$$u10d4$$u10da$$u10d8$_$u10e1$$u10d0$$u10d3$$u10d8$$u10da$$u10d8$17h21634fd5714000aaE", + "utf8_idents::საჭმელად_გემრიელი_სადილი" + ); + } + + #[test] + fn demangle_issue_60925() { + t_nohash!( + "_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17h059a991a004536adE", + "issue_60925::foo::Foo::foo" + ); + } +} diff -Nru rust-rustc-demangle-0.1.13/src/lib.rs rust-rustc-demangle-0.1.16/src/lib.rs --- rust-rustc-demangle-0.1.13/src/lib.rs 2019-01-02 15:04:49.000000000 +0000 +++ rust-rustc-demangle-0.1.16/src/lib.rs 2019-08-13 16:00:15.000000000 +0000 @@ -30,25 +30,25 @@ #[macro_use] extern crate std; +mod legacy; +mod v0; + use core::fmt; /// Representation of a demangled symbol name. pub struct Demangle<'a> { + style: Option>, original: &'a str, - inner: &'a str, suffix: &'a str, - valid: bool, - /// The number of ::-separated elements in the original name. - elements: usize, +} + +enum DemangleStyle<'a> { + Legacy(legacy::Demangle<'a>), + V0(v0::Demangle<'a>), } /// De-mangles a Rust symbol into a more readable version /// -/// All Rust symbols by default are mangled as they contain characters that -/// cannot be represented in all object files. The mangling mechanism is similar -/// to C++'s, but Rust has a few specifics to handle items like lifetimes in -/// symbols. -/// /// 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. @@ -62,24 +62,6 @@ /// assert_eq!(demangle("_ZN3foo3barE").to_string(), "foo::bar"); /// assert_eq!(demangle("foo").to_string(), "foo"); /// ``` - -// All Rust symbols are in theory lists of "::"-separated identifiers. Some -// assemblers, however, can't handle these characters in symbol names. To get -// around this, we use C++-style mangling. The mangling method is: -// -// 1. Prefix the symbol with "_ZN" -// 2. For each element of the path, emit the length plus the element -// 3. End the path with "E" -// -// For example, "_ZN4testE" => "test" and "_ZN3foo3barE" => "foo::bar". -// -// We're the ones printing our backtraces, so we can't rely on anything else to -// demangle our symbols. It's *much* nicer to look at demangled symbols, so -// this function is implemented to give us nice pretty output. -// -// 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(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 @@ -99,79 +81,37 @@ } } - // Output like LLVM IR adds extra period-delimited words. See if - // we are in that case and save the trailing words if so. let mut suffix = ""; - if let Some(i) = s.rfind("E.") { - let (head, tail) = s.split_at(i + 1); // After the E, before the period - - if is_symbol_like(tail) { - s = head; - suffix = tail; + let mut style = match legacy::demangle(s) { + Ok((d, s)) => { + suffix = s; + Some(DemangleStyle::Legacy(d)) } - } - - // 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. - let mut valid = true; - let mut inner = s; - if s.len() > 4 && s.starts_with("_ZN") && s.ends_with('E') { - inner = &s[3..s.len() - 1]; - } else if s.len() > 3 && s.starts_with("ZN") && s.ends_with('E') { - // On Windows, dbghelp strips leading underscores, so we accept "ZN...E" - // form too. - inner = &s[2..s.len() - 1]; - } else if s.len() > 5 && s.starts_with("__ZN") && s.ends_with('E') { - // On OSX, symbols are prefixed with an extra _ - inner = &s[4..s.len() - 1]; - } else { - valid = false; - } - - // only work with ascii text - if inner.bytes().any(|c| c & 0x80 != 0) { - valid = false; - } - - let mut elements = 0; - if valid { - let mut chars = inner.chars().peekable(); - while valid { - let mut i = 0usize; - while let Some(&c) = chars.peek() { - if !c.is_digit(10) { - break - } - chars.next(); - let next = i.checked_mul(10) - .and_then(|i| i.checked_add(c as usize - '0' as usize)); - i = match next { - Some(i) => i, - None => { - valid = false; - break - } - }; - } + Err(()) => match v0::demangle(s) { + Ok((d, s)) => { + suffix = s; + Some(DemangleStyle::V0(d)) + } + Err(v0::Invalid) => None, + }, + }; - if i == 0 { - valid = chars.next().is_none(); - break; - } else if chars.by_ref().take(i).count() != i { - valid = false; - } else { - elements += 1; - } + // 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 { - inner: inner, - suffix: suffix, - valid: valid, - elements: elements, + style: style, original: s, + suffix: suffix, } } @@ -197,7 +137,7 @@ /// ``` pub fn try_demangle(s: &str) -> Result { let sym = demangle(s); - if sym.valid { + if sym.style.is_some() { Ok(sym) } else { Err(TryDemangleError { _priv: () }) @@ -211,11 +151,6 @@ } } -// Rust hashes are hex digits with an `h` prepended. -fn is_rust_hash(s: &str) -> bool { - s.starts_with('h') && s[1..].chars().all(|c| c.is_digit(16)) -} - fn is_symbol_like(s: &str) -> bool { s.chars().all(|c| { // Once `char::is_ascii_punctuation` and `char::is_ascii_alphanumeric` @@ -247,95 +182,16 @@ impl<'a> fmt::Display for Demangle<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // Alright, let's do this. - if !self.valid { - return f.write_str(self.original); - } - - let mut inner = self.inner; - for element in 0..self.elements { - let mut rest = inner; - while rest.chars().next().unwrap().is_digit(10) { - rest = &rest[1..]; - } - 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) { - break; - } - if element != 0 { - try!(f.write_str("::")); + match self.style { + None => try!(f.write_str(self.original)), + Some(DemangleStyle::Legacy(ref d)) => { + try!(fmt::Display::fmt(d, f)) } - if rest.starts_with("_$") { - rest = &rest[1..]; - } - while !rest.is_empty() { - if rest.starts_with('.') { - if let Some('.') = rest[1..].chars().next() { - try!(f.write_str("::")); - rest = &rest[2..]; - } else { - try!(f.write_str(".")); - rest = &rest[1..]; - } - } else if rest.starts_with('$') { - macro_rules! demangle { - ($($pat:expr => $demangled:expr,)*) => ({ - $(if rest.starts_with($pat) { - try!(f.write_str($demangled)); - rest = &rest[$pat.len()..]; - } else)* - { - try!(f.write_str(rest)); - break; - } - - }) - } - - // see src/librustc/back/link.rs for these mappings - demangle! { - "$SP$" => "@", - "$BP$" => "*", - "$RF$" => "&", - "$LT$" => "<", - "$GT$" => ">", - "$LP$" => "(", - "$RP$" => ")", - "$C$" => ",", - - // in theory we can demangle any Unicode code point, but - // for simplicity we just catch the common ones. - "$u7e$" => "~", - "$u20$" => " ", - "$u27$" => "'", - "$u3d$" => "=", - "$u5b$" => "[", - "$u5d$" => "]", - "$u7b$" => "{", - "$u7d$" => "}", - "$u3b$" => ";", - "$u2b$" => "+", - "$u21$" => "!", - "$u22$" => "\"", - } - } else { - let idx = match rest.char_indices().find(|&(_, c)| c == '$' || c == '.') { - None => rest.len(), - Some((i, _)) => i, - }; - try!(f.write_str(&rest[..idx])); - rest = &rest[idx..]; - } + Some(DemangleStyle::V0(ref d)) => { + try!(fmt::Display::fmt(d, f)) } } - - try!(f.write_str(self.suffix)); - - Ok(()) + f.write_str(self.suffix) } } diff -Nru rust-rustc-demangle-0.1.13/src/v0.rs rust-rustc-demangle-0.1.16/src/v0.rs --- rust-rustc-demangle-0.1.13/src/v0.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-rustc-demangle-0.1.16/src/v0.rs 2019-08-13 16:00:15.000000000 +0000 @@ -0,0 +1,1084 @@ +use core::char; +use core::fmt; +use core::fmt::Display; + +/// Representation of a demangled symbol name. +pub struct Demangle<'a> { + inner: &'a str, +} + +/// 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. +pub 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. + let inner; + if s.len() > 2 && s.starts_with("_R") { + inner = &s[2..]; + } else if s.len() > 1 && s.starts_with("R") { + // On Windows, dbghelp strips leading underscores, so we accept "R..." + // form too. + inner = &s[1..]; + } else if s.len() > 3 && s.starts_with("__R") { + // On OSX, symbols are prefixed with an extra _ + inner = &s[3..]; + } else { + return Err(Invalid); + } + + // Paths always start with uppercase characters. + match inner.as_bytes()[0] { + b'A'...b'Z' => {} + _ => return Err(Invalid), + } + + // only work with ascii text + if inner.bytes().any(|c| c & 0x80 != 0) { + return Err(Invalid); + } + + // Verify that the symbol is indeed a valid path. + let mut parser = Parser { + sym: inner, + next: 0, + }; + try!(parser.skip_path()); + + // Instantiating crate (paths always start with uppercase characters). + match parser.sym.as_bytes().get(parser.next) { + Some(&b'A'...b'Z') => { + try!(parser.skip_path()); + } + _ => {} + } + + Ok((Demangle { + inner: inner, + }, &parser.sym[parser.next..])) +} + +impl<'s> Display for Demangle<'s> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut printer = Printer { + parser: Ok(Parser { + sym: self.inner, + next: 0, + }), + out: f, + bound_lifetime_depth: 0, + }; + printer.print_path(true) + } +} + +#[derive(PartialEq, Eq)] +pub struct Invalid; + +struct Ident<'s> { + /// ASCII part of the identifier. + ascii: &'s str, + /// Punycode insertion codes for Unicode codepoints, if any. + punycode: &'s str, +} + +const SMALL_PUNYCODE_LEN: usize = 128; + +impl<'s> Ident<'s> { + /// Attempt to decode punycode on the stack (allocation-free), + /// and pass the char slice to the closure, if successful. + /// This supports up to `SMALL_PUNYCODE_LEN` characters. + fn try_small_punycode_decode R, R>( + &self, + f: F, + ) -> Option { + let mut out = ['\0'; SMALL_PUNYCODE_LEN]; + let mut out_len = 0; + let r = self.punycode_decode(|i, c| { + // Check there's space left for another character. + try!(out.get(out_len).ok_or(())); + + // Move the characters after the insert position. + let mut j = out_len; + out_len += 1; + + while j > i { + out[j] = out[j - 1]; + j -= 1; + } + + // Insert the new character. + out[i] = c; + + Ok(()) + }); + if r.is_ok() { + Some(f(&out[..out_len])) + } else { + None + } + } + + /// Decode punycode as insertion positions and characters + /// and pass them to the closure, which can return `Err(())` + /// to stop the decoding process. + fn punycode_decode Result<(), ()>>( + &self, + mut insert: F, + ) -> Result<(), ()> { + let mut punycode_bytes = self.punycode.bytes().peekable(); + if punycode_bytes.peek().is_none() { + return Err(()); + } + + let mut len = 0; + + // Populate initial output from ASCII fragment. + for c in self.ascii.chars() { + try!(insert(len, c)); + len += 1; + } + + // Punycode parameters and initial state. + let base = 36; + let t_min = 1; + let t_max = 26; + let skew = 38; + let mut damp = 700; + let mut bias = 72; + let mut i: usize = 0; + let mut n: usize = 0x80; + + loop { + // Read one delta value. + let mut delta: usize = 0; + let mut w = 1; + let mut k: usize = 0; + loop { + use core::cmp::{min, max}; + + k += base; + let t = min(max(k.saturating_sub(bias), t_min), t_max); + + let d = match punycode_bytes.next() { + Some(d @ b'a'...b'z') => d - b'a', + Some(d @ b'0'...b'9') => 26 + (d - b'0'), + _ => return Err(()), + }; + let d = d as usize; + delta = try!(delta.checked_add( + try!(d.checked_mul(w).ok_or(())) + ).ok_or(())); + if d < t { + break; + } + w = try!(w.checked_mul(base - t).ok_or(())); + } + + // Compute the new insert position and character. + len += 1; + i = try!(i.checked_add(delta).ok_or(())); + n = try!(n.checked_add(i / len).ok_or(())); + i %= len; + + let n_u32 = n as u32; + let c = if n_u32 as usize == n { + try!(char::from_u32(n_u32).ok_or(())) + } else { + return Err(()); + }; + + // Insert the new character and increment the insert position. + try!(insert(i, c)); + i += 1; + + // If there are no more deltas, decoding is complete. + if punycode_bytes.peek().is_none() { + return Ok(()); + } + + // Perform bias adaptation. + delta /= damp; + damp = 2; + + delta += delta / len; + let mut k = 0; + while delta > ((base - t_min) * t_max) / 2 { + delta /= base - t_min; + k += base; + } + bias = k + ((base - t_min + 1) * delta) / (delta + skew); + } + } +} + +impl<'s> Display for Ident<'s> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.try_small_punycode_decode(|chars| { + for &c in chars { + try!(c.fmt(f)); + } + Ok(()) + }).unwrap_or_else(|| { + if !self.punycode.is_empty() { + try!(f.write_str("punycode{")); + + // Reconstruct a standard Punycode encoding, + // by using `-` as the separator. + if !self.ascii.is_empty() { + try!(f.write_str(self.ascii)); + try!(f.write_str("-")); + } + try!(f.write_str(self.punycode)); + + f.write_str("}") + } else { + f.write_str(self.ascii) + } + }) + } +} + +fn basic_type(tag: u8) -> Option<&'static str> { + Some(match tag { + b'b' => "bool", + b'c' => "char", + b'e' => "str", + b'u' => "()", + b'a' => "i8", + b's' => "i16", + b'l' => "i32", + b'x' => "i64", + b'n' => "i128", + b'i' => "isize", + b'h' => "u8", + b't' => "u16", + b'm' => "u32", + b'y' => "u64", + b'o' => "u128", + b'j' => "usize", + b'f' => "f32", + b'd' => "f64", + b'z' => "!", + b'p' => "_", + b'v' => "...", + + _ => return None, + }) +} + +struct Parser<'s> { + sym: &'s str, + next: usize, +} + +impl<'s> Parser<'s> { + fn peek(&self) -> Option { + self.sym.as_bytes().get(self.next).cloned() + } + + fn eat(&mut self, b: u8) -> bool { + if self.peek() == Some(b) { + self.next += 1; + true + } else { + false + } + } + + fn next(&mut self) -> Result { + let b = try!(self.peek().ok_or(Invalid)); + self.next += 1; + Ok(b) + } + + fn hex_nibbles(&mut self) -> Result<&'s str, Invalid> { + let start = self.next; + loop { + match try!(self.next()) { + b'0'...b'9' | b'a'...b'f' => {} + b'_' => break, + _ => return Err(Invalid), + } + } + Ok(&self.sym[start..self.next - 1]) + } + + fn digit_10(&mut self) -> Result { + let d = match self.peek() { + Some(d @ b'0'...b'9') => d - b'0', + _ => return Err(Invalid), + }; + self.next += 1; + Ok(d) + } + + fn digit_62(&mut self) -> Result { + let d = match self.peek() { + Some(d @ b'0'...b'9') => d - b'0', + Some(d @ b'a'...b'z') => 10 + (d - b'a'), + Some(d @ b'A'...b'Z') => 10 + 26 + (d - b'A'), + _ => return Err(Invalid), + }; + self.next += 1; + Ok(d) + } + + fn integer_62(&mut self) -> Result { + if self.eat(b'_') { + return Ok(0); + } + + let mut x: u64 = 0; + while !self.eat(b'_') { + let d = try!(self.digit_62()) as u64; + x = try!(x.checked_mul(62).ok_or(Invalid)); + x = try!(x.checked_add(d).ok_or(Invalid)); + } + x.checked_add(1).ok_or(Invalid) + } + + fn opt_integer_62(&mut self, tag: u8) -> Result { + if !self.eat(tag) { + return Ok(0); + } + try!(self.integer_62()).checked_add(1).ok_or(Invalid) + } + + fn disambiguator(&mut self) -> Result { + self.opt_integer_62(b's') + } + + fn namespace(&mut self) -> Result, Invalid> { + match try!(self.next()) { + // Special namespaces, like closures and shims. + ns @ b'A'...b'Z' => Ok(Some(ns as char)), + + // Implementation-specific/unspecified namespaces. + b'a'...b'z' => Ok(None), + + _ => Err(Invalid), + } + } + + fn backref(&mut self) -> Result, Invalid> { + let s_start = self.next - 1; + let i = try!(self.integer_62()); + if i >= s_start as u64 { + return Err(Invalid); + } + Ok(Parser { + sym: self.sym, + next: i as usize, + }) + } + + fn ident(&mut self) -> Result, Invalid> { + let is_punycode = self.eat(b'u'); + let mut len = try!(self.digit_10()) as usize; + if len != 0 { + loop { + match self.digit_10() { + Ok(d) => { + len = try!(len.checked_mul(10).ok_or(Invalid)); + len = try!(len.checked_add(d as usize).ok_or(Invalid)); + } + Err(Invalid) => break, + } + } + } + + // Skip past the optional `_` separator. + self.eat(b'_'); + + let start = self.next; + self.next = try!(self.next.checked_add(len).ok_or(Invalid)); + if self.next > self.sym.len() { + return Err(Invalid); + } + + let ident = &self.sym[start..self.next]; + + if is_punycode { + let ident = match ident.bytes().rposition(|b| b == b'_') { + Some(i) => Ident { + ascii: &ident[..i], + punycode: &ident[i + 1..], + }, + None => Ident { + ascii: "", + punycode: ident, + }, + }; + if ident.punycode.is_empty() { + return Err(Invalid); + } + Ok(ident) + } else { + Ok(Ident { + ascii: ident, + punycode: "", + }) + } + } + + fn skip_path(&mut self) -> Result<(), Invalid> { + match try!(self.next()) { + b'C' => { + try!(self.disambiguator()); + try!(self.ident()); + } + b'N' => { + try!(self.namespace()); + try!(self.skip_path()); + try!(self.disambiguator()); + try!(self.ident()); + } + b'M' => { + try!(self.disambiguator()); + try!(self.skip_path()); + try!(self.skip_type()); + } + b'X' => { + try!(self.disambiguator()); + try!(self.skip_path()); + try!(self.skip_type()); + try!(self.skip_path()); + } + b'Y' => { + try!(self.skip_type()); + try!(self.skip_path()); + } + b'I' => { + try!(self.skip_path()); + while !self.eat(b'E') { + try!(self.skip_generic_arg()); + } + } + b'B' => { + try!(self.backref()); + } + _ => return Err(Invalid), + } + Ok(()) + } + + fn skip_generic_arg(&mut self) -> Result<(), Invalid> { + if self.eat(b'L') { + try!(self.integer_62()); + Ok(()) + } else if self.eat(b'K') { + self.skip_const() + } else { + self.skip_type() + } + } + + fn skip_type(&mut self) -> Result<(), Invalid> { + match try!(self.next()) { + tag if basic_type(tag).is_some() => {} + + b'R' | b'Q' => { + if self.eat(b'L') { + try!(self.integer_62()); + } + try!(self.skip_type()); + } + b'P' | b'O' | b'S' => try!(self.skip_type()), + b'A' => { + try!(self.skip_type()); + try!(self.skip_const()); + } + b'T' => while !self.eat(b'E') { + try!(self.skip_type()); + }, + b'F' => { + let _binder = try!(self.opt_integer_62(b'G')); + let _is_unsafe = self.eat(b'U'); + if self.eat(b'K') { + let c_abi = self.eat(b'C'); + if !c_abi { + let abi = try!(self.ident()); + if abi.ascii.is_empty() || !abi.punycode.is_empty() { + return Err(Invalid); + } + } + } + while !self.eat(b'E') { + try!(self.skip_type()); + } + try!(self.skip_type()); + } + b'D' => { + let _binder = try!(self.opt_integer_62(b'G')); + while !self.eat(b'E') { + try!(self.skip_path()); + while self.eat(b'p') { + try!(self.ident()); + try!(self.skip_type()); + } + } + if !self.eat(b'L') { + return Err(Invalid); + } + try!(self.integer_62()); + } + b'B' => { + try!(self.backref()); + } + _ => { + // Go back to the tag, so `skip_path` also sees it. + self.next -= 1; + try!(self.skip_path()); + } + } + Ok(()) + } + + fn skip_const(&mut self) -> Result<(), Invalid> { + if self.eat(b'B') { + try!(self.backref()); + return Ok(()); + } + + match try!(self.next()) { + // Unsigned integer types. + b'h' | b't' | b'm' | b'y' | b'o' | b'j' => {} + + _ => return Err(Invalid), + } + + if self.eat(b'p') { + return Ok(()); + } + try!(self.hex_nibbles()); + Ok(()) + } +} + +struct Printer<'a, 'b: 'a, 's> { + parser: Result, Invalid>, + out: &'a mut fmt::Formatter<'b>, + bound_lifetime_depth: u32, +} + +/// Mark the parser as errored, print `?` and return early. +/// This allows callers to keep printing the approximate +/// syntax of the path/type/const, despite having errors. +/// E.g. `Vec<[(A, ?); ?]>` instead of `Vec<[(A, ?`. +macro_rules! invalid { + ($printer:ident) => {{ + $printer.parser = Err(Invalid); + return $printer.out.write_str("?"); + }} +} + +/// Call a parser method (if the parser hasn't errored yet), +/// and mark the parser as errored if it returns `Err(Invalid)`. +/// +/// If the parser errored, before or now, prints `?`, and +/// returns early the current function (see `invalid!` above). +macro_rules! parse { + ($printer:ident, $method:ident $(($($arg:expr),*))*) => { + match $printer.parser_mut().and_then(|p| p.$method($($($arg),*)*)) { + Ok(x) => x, + Err(Invalid) => invalid!($printer), + } + }; +} + +impl<'a, 'b, 's> Printer<'a, 'b, 's> { + fn parser_mut<'c>(&'c mut self) -> Result<&'c mut Parser<'s>, Invalid> { + self.parser.as_mut().map_err(|_| Invalid) + } + + /// Eat the given character from the parser, + /// returning `false` if the parser errored. + fn eat(&mut self, b: u8) -> bool { + self.parser_mut().map(|p| p.eat(b)) == Ok(true) + } + + /// Return a nested parser for a backref. + fn backref_printer<'c>(&'c mut self) -> Printer<'c, 'b, 's> { + Printer { + parser: self.parser_mut().and_then(|p| p.backref()), + out: self.out, + bound_lifetime_depth: self.bound_lifetime_depth, + } + } + + /// Print the lifetime according to the previously decoded index. + /// An index of `0` always refers to `'_`, but starting with `1`, + /// indices refer to late-bound lifetimes introduced by a binder. + fn print_lifetime_from_index(&mut self, lt: u64) -> fmt::Result { + try!(self.out.write_str("'")); + if lt == 0 { + return self.out.write_str("_"); + } + match (self.bound_lifetime_depth as u64).checked_sub(lt) { + Some(depth) => { + // Try to print lifetimes alphabetically first. + if depth < 26 { + let c = (b'a' + depth as u8) as char; + c.fmt(self.out) + } else { + // Use `'_123` after running out of letters. + try!(self.out.write_str("_")); + depth.fmt(self.out) + } + } + None => invalid!(self), + } + } + + /// Optionally enter a binder ('G') for late-bound lifetimes, + /// printing e.g. `for<'a, 'b> ` before calling the closure, + /// and make those lifetimes visible to it (via depth level). + fn in_binder(&mut self, f: F) -> fmt::Result + where F: FnOnce(&mut Self) -> fmt::Result, + { + let bound_lifetimes = parse!(self, opt_integer_62(b'G')); + + if bound_lifetimes > 0 { + try!(self.out.write_str("for<")); + for i in 0..bound_lifetimes { + if i > 0 { + try!(self.out.write_str(", ")); + } + self.bound_lifetime_depth += 1; + try!(self.print_lifetime_from_index(1)); + } + try!(self.out.write_str("> ")); + } + + let r = f(self); + + // Restore `bound_lifetime_depth` to the previous value. + self.bound_lifetime_depth -= bound_lifetimes as u32; + + r + } + + /// Print list elements using the given closure and separator, + /// until the end of the list ('E') is found, or the parser errors. + /// Returns the number of elements printed. + fn print_sep_list(&mut self, f: F, sep: &str) -> Result + where F: Fn(&mut Self) -> fmt::Result, + { + let mut i = 0; + while self.parser.is_ok() && !self.eat(b'E') { + if i > 0 { + try!(self.out.write_str(sep)); + } + try!(f(self)); + i += 1; + } + Ok(i) + } + + fn print_path(&mut self, in_value: bool) -> fmt::Result { + let tag = parse!(self, next); + match tag { + b'C' => { + let dis = parse!(self, disambiguator); + let name = parse!(self, ident); + + try!(name.fmt(self.out)); + if !self.out.alternate() { + try!(self.out.write_str("[")); + try!(fmt::LowerHex::fmt(&dis, self.out)); + try!(self.out.write_str("]")); + } + } + b'N' => { + let ns = parse!(self, namespace); + + try!(self.print_path(in_value)); + + let dis = parse!(self, disambiguator); + let name = parse!(self, ident); + + match ns { + // Special namespaces, like closures and shims. + Some(ns) => { + try!(self.out.write_str("::{")); + match ns { + 'C' => try!(self.out.write_str("closure")), + 'S' => try!(self.out.write_str("shim")), + _ => try!(ns.fmt(self.out)), + } + if !name.ascii.is_empty() || !name.punycode.is_empty() { + try!(self.out.write_str(":")); + try!(name.fmt(self.out)); + } + try!(self.out.write_str("#")); + try!(dis.fmt(self.out)); + try!(self.out.write_str("}")); + } + + // Implementation-specific/unspecified namespaces. + None => { + if !name.ascii.is_empty() || !name.punycode.is_empty() { + try!(self.out.write_str("::")); + try!(name.fmt(self.out)); + } + } + } + } + b'M' | b'X' | b'Y' => { + if tag != b'Y' { + // Ignore the `impl`'s own path. + parse!(self, disambiguator); + parse!(self, skip_path); + } + + try!(self.out.write_str("<")); + try!(self.print_type()); + if tag != b'M' { + try!(self.out.write_str(" as ")); + try!(self.print_path(false)); + } + try!(self.out.write_str(">")); + } + b'I' => { + try!(self.print_path(in_value)); + if in_value { + try!(self.out.write_str("::")); + } + try!(self.out.write_str("<")); + try!(self.print_sep_list(Self::print_generic_arg, ", ")); + try!(self.out.write_str(">")); + } + b'B' => { + try!(self.backref_printer().print_path(in_value)); + } + _ => invalid!(self), + } + Ok(()) + } + + fn print_generic_arg(&mut self) -> fmt::Result { + if self.eat(b'L') { + let lt = parse!(self, integer_62); + self.print_lifetime_from_index(lt) + } else if self.eat(b'K') { + self.print_const() + } else { + self.print_type() + } + } + + fn print_type(&mut self) -> fmt::Result { + let tag = parse!(self, next); + + match basic_type(tag) { + Some(ty) => return self.out.write_str(ty), + None => {} + } + + match tag { + b'R' | b'Q' => { + try!(self.out.write_str("&")); + if self.eat(b'L') { + let lt = parse!(self, integer_62); + if lt != 0 { + try!(self.print_lifetime_from_index(lt)); + try!(self.out.write_str(" ")); + } + } + if tag != b'R' { + try!(self.out.write_str("mut ")); + } + try!(self.print_type()); + } + + b'P' | b'O' => { + try!(self.out.write_str("*")); + if tag != b'P' { + try!(self.out.write_str("mut ")); + } else { + try!(self.out.write_str("const ")); + } + try!(self.print_type()); + } + + b'A' | b'S' => { + try!(self.out.write_str("[")); + try!(self.print_type()); + if tag == b'A' { + try!(self.out.write_str("; ")); + try!(self.print_const()); + } + try!(self.out.write_str("]")); + } + b'T' => { + try!(self.out.write_str("(")); + let count = try!(self.print_sep_list(Self::print_type, ", ")); + if count == 1 { + try!(self.out.write_str(",")); + } + try!(self.out.write_str(")")); + } + b'F' => try!(self.in_binder(|this| { + let is_unsafe = this.eat(b'U'); + let abi = if this.eat(b'K') { + if this.eat(b'C') { + Some("C") + } else { + let abi = parse!(this, ident); + if abi.ascii.is_empty() || !abi.punycode.is_empty() { + invalid!(this); + } + Some(abi.ascii) + } + } else { + None + }; + + if is_unsafe { + try!(this.out.write_str("unsafe ")); + } + + match abi { + Some(abi) => { + try!(this.out.write_str("extern \"")); + + // If the ABI had any `-`, they were replaced with `_`, + // so the parts between `_` have to be re-joined with `-`. + let mut parts = abi.split('_'); + try!(this.out.write_str(parts.next().unwrap())); + for part in parts { + try!(this.out.write_str("-")); + try!(this.out.write_str(part)); + } + + try!(this.out.write_str("\" ")); + } + None => {} + } + + try!(this.out.write_str("fn(")); + try!(this.print_sep_list(Self::print_type, ", ")); + try!(this.out.write_str(")")); + + if this.eat(b'u') { + // Skip printing the return type if it's 'u', i.e. `()`. + } else { + try!(this.out.write_str(" -> ")); + try!(this.print_type()); + } + + Ok(()) + })), + b'D' => { + try!(self.out.write_str("dyn ")); + try!(self.in_binder(|this| { + try!(this.print_sep_list(Self::print_dyn_trait, " + ")); + Ok(()) + })); + + if !self.eat(b'L') { + invalid!(self); + } + let lt = parse!(self, integer_62); + if lt != 0 { + try!(self.out.write_str(" + ")); + try!(self.print_lifetime_from_index(lt)); + } + } + b'B' => { + try!(self.backref_printer().print_type()); + } + _ => { + // Go back to the tag, so `print_path` also sees it. + let _ = self.parser_mut().map(|p| p.next -= 1); + try!(self.print_path(false)); + } + } + Ok(()) + } + + /// A trait in a trait object may have some "existential projections" + /// (i.e. associated type bindings) after it, which should be printed + /// in the `<...>` of the trait, e.g. `dyn Trait`. + /// To this end, this method will keep the `<...>` of an 'I' path + /// open, by omitting the `>`, and return `Ok(true)` in that case. + fn print_path_maybe_open_generics(&mut self) -> Result { + if self.eat(b'B') { + self.backref_printer().print_path_maybe_open_generics() + } else if self.eat(b'I') { + try!(self.print_path(false)); + try!(self.out.write_str("<")); + try!(self.print_sep_list(Self::print_generic_arg, ", ")); + Ok(true) + } else { + try!(self.print_path(false)); + Ok(false) + } + } + + fn print_dyn_trait(&mut self) -> fmt::Result { + let mut open = try!(self.print_path_maybe_open_generics()); + + while self.eat(b'p') { + if !open { + try!(self.out.write_str("<")); + open = true; + } else { + try!(self.out.write_str(", ")); + } + + let name = parse!(self, ident); + try!(name.fmt(self.out)); + try!(self.out.write_str(" = ")); + try!(self.print_type()); + } + + if open { + try!(self.out.write_str(">")); + } + + Ok(()) + } + + fn print_const(&mut self) -> fmt::Result { + if self.eat(b'B') { + return self.backref_printer().print_const(); + } + + let ty_tag = parse!(self, next); + let ty = match ty_tag { + // Unsigned integer types. + b'h' | b't' | b'm' | b'y' | b'o' | b'j' => { + basic_type(ty_tag).unwrap() + } + + _ => invalid!(self), + }; + + + if self.eat(b'p') { + try!(self.out.write_str("_")); + } else { + try!(self.print_const_uint()); + } + + if !self.out.alternate() { + try!(self.out.write_str(": ")); + try!(self.out.write_str(ty)); + } + + Ok(()) + } + + fn print_const_uint(&mut self) -> fmt::Result { + let hex = parse!(self, hex_nibbles); + + // Print anything that doesn't fit in `u64` verbatim. + if hex.len() > 16 { + try!(self.out.write_str("0x")); + return self.out.write_str(hex); + } + + let mut v = 0; + for c in hex.chars() { + v = (v << 4) | (c.to_digit(16).unwrap() as u64); + } + v.fmt(self.out) + } +} + +#[cfg(test)] +mod tests { + macro_rules! t_nohash { + ($a:expr, $b:expr) => ({ + assert_eq!(format!("{:#}", ::demangle($a)), $b); + }) + } + macro_rules! t_nohash_type { + ($a:expr, $b:expr) => ( + t_nohash!(concat!("_RMC0", $a), concat!("<", $b, ">")) + ) + } + + #[test] + fn demangle_crate_with_leading_digit() { + t_nohash!( + "_RNvC6_123foo3bar", + "123foo::bar" + ); + } + + #[test] + fn demangle_utf8_idents() { + t_nohash!( + "_RNqCs4fqI2P2rA04_11utf8_identsu30____7hkackfecea1cbdathfdh9hlq6y", + "utf8_idents::საჭმელად_გემრიელი_სადილი" + ); + } + + #[test] + fn demangle_closure() { + t_nohash!( + "_RNCNCNgCs6DXkGYLi8lr_2cc5spawn00B5_", + "cc::spawn::{closure#0}::{closure#0}" + ); + t_nohash!( + "_RNCINkXs25_NgCsbmNqQUJIY6D_4core5sliceINyB9_4IterhENuNgNoBb_4iter8iterator8Iterator9rpositionNCNgNpB9_6memchr7memrchrs_0E0Bb_", + " as core::iter::iterator::Iterator>::rposition::::{closure#0}" + ); + } + + #[test] + fn demangle_dyn_trait() { + t_nohash!( + "_RINbNbCskIICzLVDPPb_5alloc5alloc8box_freeDINbNiB4_5boxed5FnBoxuEp6OutputuEL_ECs1iopQbuBiw2_3std", + "alloc::alloc::box_free::>" + ); + } + + #[test] + fn demangle_const_generics() { + // NOTE(eddyb) this was hand-written, before rustc had working + // const generics support (but the mangling format did include them). + t_nohash_type!( + "INtC8arrayvec8ArrayVechKj7b_E", + "arrayvec::ArrayVec" + ); + } + + #[test] + fn demangle_exponential_explosion() { + // NOTE(eddyb) because of the prefix added by `t_nohash_type!` is + // 3 bytes long, `B2_` refers to the start of the type, not `B_`. + // 6 backrefs (`B8_E` through `B3_E`) result in 2^6 = 64 copies of `_`. + // Also, because the `p` (`_`) type is after all of the starts of the + // backrefs, it can be replaced with any other type, independently. + t_nohash_type!( + concat!("TTTTTT", "p", "B8_E", "B7_E", "B6_E", "B5_E", "B4_E", "B3_E"), + "((((((_, _), (_, _)), ((_, _), (_, _))), (((_, _), (_, _)), ((_, _), (_, _)))), \ + ((((_, _), (_, _)), ((_, _), (_, _))), (((_, _), (_, _)), ((_, _), (_, _))))), \ + (((((_, _), (_, _)), ((_, _), (_, _))), (((_, _), (_, _)), ((_, _), (_, _)))), \ + ((((_, _), (_, _)), ((_, _), (_, _))), (((_, _), (_, _)), ((_, _), (_, _))))))" + ); + } + + #[test] + fn demangle_thinlto() { + t_nohash!("_RC3foo.llvm.9D1C9369", "foo"); + t_nohash!("_RC3foo.llvm.9D1C9369@@16", "foo"); + t_nohash!("_RNvC9backtrace3foo.llvm.A5310EB9", "backtrace::foo"); + } + + #[test] + fn demangle_extra_suffix() { + // From alexcrichton/rustc-demangle#27: + t_nohash!( + "_RNvNtNtNtNtCs92dm3009vxr_4rand4rngs7adapter9reseeding4fork23FORK_HANDLER_REGISTERED.0.0", + "rand::rngs::adapter::reseeding::fork::FORK_HANDLER_REGISTERED.0.0" + ); + } +} diff -Nru rust-rustc-demangle-0.1.13/.travis.yml rust-rustc-demangle-0.1.16/.travis.yml --- rust-rustc-demangle-0.1.13/.travis.yml 2016-11-19 17:17:07.000000000 +0000 +++ rust-rustc-demangle-0.1.16/.travis.yml 2019-01-02 22:01:04.000000000 +0000 @@ -1,20 +1,22 @@ language: rust -rust: - - stable - - beta - - nightly -sudo: false -before_script: - - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH +matrix: + include: + - rust: stable + - rust: beta + - rust: nightly + - name: "master doc to gh-pages" + rust: nightly + script: + - cargo doc --no-deps + deploy: + provider: script + script: curl -LsSf https://git.io/fhJ8n | rustc - && (cd target/doc && ../../rust_out) + skip_cleanup: true + on: + branch: master script: - - cargo build - - cargo test - - cargo doc -after_success: - - travis-cargo --only nightly doc-upload + - cargo build --verbose + - cargo test --verbose notifications: email: on_success: never -env: - global: - secure: "VWE8DaftZLOj34USRPiq2jyFV1xWrPJP0lgMVWPGF52soyWV6L5FyMlbk00GvKH8y5h0urtuVFmInsLKz1E96eTLbD7Efa2snjSlOWMsTaWy3yq0NNuPraMWiXUkjDBxW3DTV47HN9thYA7TbrM35A0XGr7fXKl4CPT/UKEo2nNZPb2Y2XYaD6RID1sXMOvemOERGu7Lnq8dQ/PdGWWkpTV4FVsmq2wTyyZnzsEFsK0r/wqEPPHSl8r3NH6Rid9kCMw3h1d9s1eYSqzP09/fYfjy+X/6iGV9dL5WUQFL7xG7WjfZZG/e87lAxbXx+jXTlncdvXjm4BmIOYHeEMK9N5D3qE5C1gIIU+tk6ZuVKBLWMQJnAIDJKCsVdUhAXtWqbFK4djvNJUml1rIOF/CVUeynBVtacJ8nlxyh61XvDHbZGSBFBR2odEkhUaLzw/t/zqOIYd0u+mjtM4pX1nZ+ZFIMqVFsNTfVWWMDLyRVBNiAoHCdn/ug+mJJaYE3mZ2q1wPPqJVFffyaZVyg97zFO0y9v5ASHjb/7nYlIPoDGDPDUuoscuw5IYf+6CoCi7EgGqg5v7X1BsBXSYK+XfzOvrAqP31ixNGyVJEcBjAaJXwopx6KVIqWe71eHJDX68VeI0ncfCPDDQIReKA7MBhq5PzRDzr4iRHZAhNyuKgo+Fo="