/rust/registry/src/index.crates.io-6f17d22bba15001f/rustc-demangle-0.1.24/src/legacy.rs
| Line | Count | Source (jump to first uncovered line) | 
| 1 |  | use core::char; | 
| 2 |  | use core::fmt; | 
| 3 |  |  | 
| 4 |  | /// Representation of a demangled symbol name. | 
| 5 |  | pub struct Demangle<'a> { | 
| 6 |  |     inner: &'a str, | 
| 7 |  |     /// The number of ::-separated elements in the original name. | 
| 8 |  |     elements: usize, | 
| 9 |  | } | 
| 10 |  |  | 
| 11 |  | /// De-mangles a Rust symbol into a more readable version | 
| 12 |  | /// | 
| 13 |  | /// All Rust symbols by default are mangled as they contain characters that | 
| 14 |  | /// cannot be represented in all object files. The mangling mechanism is similar | 
| 15 |  | /// to C++'s, but Rust has a few specifics to handle items like lifetimes in | 
| 16 |  | /// symbols. | 
| 17 |  | /// | 
| 18 |  | /// This function will take a **mangled** symbol and return a value. When printed, | 
| 19 |  | /// the de-mangled version will be written. If the symbol does not look like | 
| 20 |  | /// a mangled symbol, the original value will be written instead. | 
| 21 |  | /// | 
| 22 |  | /// # Examples | 
| 23 |  | /// | 
| 24 |  | /// ``` | 
| 25 |  | /// use rustc_demangle::demangle; | 
| 26 |  | /// | 
| 27 |  | /// assert_eq!(demangle("_ZN4testE").to_string(), "test"); | 
| 28 |  | /// assert_eq!(demangle("_ZN3foo3barE").to_string(), "foo::bar"); | 
| 29 |  | /// assert_eq!(demangle("foo").to_string(), "foo"); | 
| 30 |  | /// ``` | 
| 31 |  |  | 
| 32 |  | // All Rust symbols are in theory lists of "::"-separated identifiers. Some | 
| 33 |  | // assemblers, however, can't handle these characters in symbol names. To get | 
| 34 |  | // around this, we use C++-style mangling. The mangling method is: | 
| 35 |  | // | 
| 36 |  | // 1. Prefix the symbol with "_ZN" | 
| 37 |  | // 2. For each element of the path, emit the length plus the element | 
| 38 |  | // 3. End the path with "E" | 
| 39 |  | // | 
| 40 |  | // For example, "_ZN4testE" => "test" and "_ZN3foo3barE" => "foo::bar". | 
| 41 |  | // | 
| 42 |  | // We're the ones printing our backtraces, so we can't rely on anything else to | 
| 43 |  | // demangle our symbols. It's *much* nicer to look at demangled symbols, so | 
| 44 |  | // this function is implemented to give us nice pretty output. | 
| 45 |  | // | 
| 46 |  | // Note that this demangler isn't quite as fancy as it could be. We have lots | 
| 47 |  | // of other information in our symbols like hashes, version, type information, | 
| 48 |  | // etc. Additionally, this doesn't handle glue symbols at all. | 
| 49 | 0 | pub fn demangle(s: &str) -> Result<(Demangle, &str), ()> { | 
| 50 |  |     // First validate the symbol. If it doesn't look like anything we're | 
| 51 |  |     // expecting, we just print it literally. Note that we must handle non-Rust | 
| 52 |  |     // symbols because we could have any function in the backtrace. | 
| 53 | 0 |     let inner = if s.starts_with("_ZN") { | 
| 54 | 0 |         &s[3..] | 
| 55 | 0 |     } else if s.starts_with("ZN") { | 
| 56 |  |         // On Windows, dbghelp strips leading underscores, so we accept "ZN...E" | 
| 57 |  |         // form too. | 
| 58 | 0 |         &s[2..] | 
| 59 | 0 |     } else if s.starts_with("__ZN") { | 
| 60 |  |         // On OSX, symbols are prefixed with an extra _ | 
| 61 | 0 |         &s[4..] | 
| 62 |  |     } else { | 
| 63 | 0 |         return Err(()); | 
| 64 |  |     }; | 
| 65 |  |  | 
| 66 |  |     // only work with ascii text | 
| 67 | 0 |     if inner.bytes().any(|c| c & 0x80 != 0) { | 
| 68 | 0 |         return Err(()); | 
| 69 | 0 |     } | 
| 70 | 0 | 
 | 
| 71 | 0 |     let mut elements = 0; | 
| 72 | 0 |     let mut chars = inner.chars(); | 
| 73 | 0 |     let mut c = chars.next().ok_or(())?; | 
| 74 | 0 |     while c != 'E' { | 
| 75 |  |         // Decode an identifier element's length. | 
| 76 | 0 |         if !c.is_digit(10) { | 
| 77 | 0 |             return Err(()); | 
| 78 | 0 |         } | 
| 79 | 0 |         let mut len = 0usize; | 
| 80 | 0 |         while let Some(d) = c.to_digit(10) { | 
| 81 | 0 |             len = len | 
| 82 | 0 |                 .checked_mul(10) | 
| 83 | 0 |                 .and_then(|len| len.checked_add(d as usize)) | 
| 84 | 0 |                 .ok_or(())?; | 
| 85 | 0 |             c = chars.next().ok_or(())?; | 
| 86 |  |         } | 
| 87 |  |  | 
| 88 |  |         // `c` already contains the first character of this identifier, skip it and | 
| 89 |  |         // all the other characters of this identifier, to reach the next element. | 
| 90 | 0 |         for _ in 0..len { | 
| 91 | 0 |             c = chars.next().ok_or(())?; | 
| 92 |  |         } | 
| 93 |  |  | 
| 94 | 0 |         elements += 1; | 
| 95 |  |     } | 
| 96 |  |  | 
| 97 | 0 |     Ok((Demangle { inner, elements }, chars.as_str())) | 
| 98 | 0 | } | 
| 99 |  |  | 
| 100 |  | // Rust hashes are hex digits with an `h` prepended. | 
| 101 | 0 | fn is_rust_hash(s: &str) -> bool { | 
| 102 | 0 |     s.starts_with('h') && s[1..].chars().all(|c| c.is_digit(16)) | 
| 103 | 0 | } | 
| 104 |  |  | 
| 105 |  | impl<'a> fmt::Display for Demangle<'a> { | 
| 106 | 0 |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 
| 107 | 0 |         // Alright, let's do this. | 
| 108 | 0 |         let mut inner = self.inner; | 
| 109 | 0 |         for element in 0..self.elements { | 
| 110 | 0 |             let mut rest = inner; | 
| 111 | 0 |             while rest.chars().next().unwrap().is_digit(10) { | 
| 112 | 0 |                 rest = &rest[1..]; | 
| 113 | 0 |             } | 
| 114 | 0 |             let i: usize = inner[..(inner.len() - rest.len())].parse().unwrap(); | 
| 115 | 0 |             inner = &rest[i..]; | 
| 116 | 0 |             rest = &rest[..i]; | 
| 117 | 0 |             // Skip printing the hash if alternate formatting | 
| 118 | 0 |             // was requested. | 
| 119 | 0 |             if f.alternate() && element + 1 == self.elements && is_rust_hash(&rest) { | 
| 120 | 0 |                 break; | 
| 121 | 0 |             } | 
| 122 | 0 |             if element != 0 { | 
| 123 | 0 |                 f.write_str("::")?; | 
| 124 | 0 |             } | 
| 125 | 0 |             if rest.starts_with("_$") { | 
| 126 | 0 |                 rest = &rest[1..]; | 
| 127 | 0 |             } | 
| 128 | 0 |             loop { | 
| 129 | 0 |                 if rest.starts_with('.') { | 
| 130 | 0 |                     if let Some('.') = rest[1..].chars().next() { | 
| 131 | 0 |                         f.write_str("::")?; | 
| 132 | 0 |                         rest = &rest[2..]; | 
| 133 |  |                     } else { | 
| 134 | 0 |                         f.write_str(".")?; | 
| 135 | 0 |                         rest = &rest[1..]; | 
| 136 |  |                     } | 
| 137 | 0 |                 } else if rest.starts_with('$') { | 
| 138 | 0 |                     let (escape, after_escape) = if let Some(end) = rest[1..].find('$') { | 
| 139 | 0 |                         (&rest[1..=end], &rest[end + 2..]) | 
| 140 |  |                     } else { | 
| 141 | 0 |                         break; | 
| 142 |  |                     }; | 
| 143 |  |  | 
| 144 |  |                     // see src/librustc_codegen_utils/symbol_names/legacy.rs for these mappings | 
| 145 | 0 |                     let unescaped = match escape { | 
| 146 | 0 |                         "SP" => "@", | 
| 147 | 0 |                         "BP" => "*", | 
| 148 | 0 |                         "RF" => "&", | 
| 149 | 0 |                         "LT" => "<", | 
| 150 | 0 |                         "GT" => ">", | 
| 151 | 0 |                         "LP" => "(", | 
| 152 | 0 |                         "RP" => ")", | 
| 153 | 0 |                         "C" => ",", | 
| 154 |  |  | 
| 155 |  |                         _ => { | 
| 156 | 0 |                             if escape.starts_with('u') { | 
| 157 | 0 |                                 let digits = &escape[1..]; | 
| 158 | 0 |                                 let all_lower_hex = digits.chars().all(|c| match c { | 
| 159 | 0 |                                     '0'..='9' | 'a'..='f' => true, | 
| 160 | 0 |                                     _ => false, | 
| 161 | 0 |                                 }); | 
| 162 | 0 |                                 let c = u32::from_str_radix(digits, 16) | 
| 163 | 0 |                                     .ok() | 
| 164 | 0 |                                     .and_then(char::from_u32); | 
| 165 | 0 |                                 if let (true, Some(c)) = (all_lower_hex, c) { | 
| 166 |  |                                     // FIXME(eddyb) do we need to filter out control codepoints? | 
| 167 | 0 |                                     if !c.is_control() { | 
| 168 | 0 |                                         c.fmt(f)?; | 
| 169 | 0 |                                         rest = after_escape; | 
| 170 | 0 |                                         continue; | 
| 171 | 0 |                                     } | 
| 172 | 0 |                                 } | 
| 173 | 0 |                             } | 
| 174 | 0 |                             break; | 
| 175 |  |                         } | 
| 176 |  |                     }; | 
| 177 | 0 |                     f.write_str(unescaped)?; | 
| 178 | 0 |                     rest = after_escape; | 
| 179 | 0 |                 } else if let Some(i) = rest.find(|c| c == '$' || c == '.') { | 
| 180 | 0 |                     f.write_str(&rest[..i])?; | 
| 181 | 0 |                     rest = &rest[i..]; | 
| 182 |  |                 } else { | 
| 183 | 0 |                     break; | 
| 184 |  |                 } | 
| 185 |  |             } | 
| 186 | 0 |             f.write_str(rest)?; | 
| 187 |  |         } | 
| 188 |  |  | 
| 189 | 0 |         Ok(()) | 
| 190 | 0 |     } | 
| 191 |  | } | 
| 192 |  |  | 
| 193 |  | #[cfg(test)] | 
| 194 |  | mod tests { | 
| 195 |  |     use std::prelude::v1::*; | 
| 196 |  |  | 
| 197 |  |     macro_rules! t { | 
| 198 |  |         ($a:expr, $b:expr) => { | 
| 199 |  |             assert!(ok($a, $b)) | 
| 200 |  |         }; | 
| 201 |  |     } | 
| 202 |  |  | 
| 203 |  |     macro_rules! t_err { | 
| 204 |  |         ($a:expr) => { | 
| 205 |  |             assert!(ok_err($a)) | 
| 206 |  |         }; | 
| 207 |  |     } | 
| 208 |  |  | 
| 209 |  |     macro_rules! t_nohash { | 
| 210 |  |         ($a:expr, $b:expr) => {{ | 
| 211 |  |             assert_eq!(format!("{:#}", ::demangle($a)), $b); | 
| 212 |  |         }}; | 
| 213 |  |     } | 
| 214 |  |  | 
| 215 |  |     fn ok(sym: &str, expected: &str) -> bool { | 
| 216 |  |         match ::try_demangle(sym) { | 
| 217 |  |             Ok(s) => { | 
| 218 |  |                 if s.to_string() == expected { | 
| 219 |  |                     true | 
| 220 |  |                 } else { | 
| 221 |  |                     println!("\n{}\n!=\n{}\n", s, expected); | 
| 222 |  |                     false | 
| 223 |  |                 } | 
| 224 |  |             } | 
| 225 |  |             Err(_) => { | 
| 226 |  |                 println!("error demangling"); | 
| 227 |  |                 false | 
| 228 |  |             } | 
| 229 |  |         } | 
| 230 |  |     } | 
| 231 |  |  | 
| 232 |  |     fn ok_err(sym: &str) -> bool { | 
| 233 |  |         match ::try_demangle(sym) { | 
| 234 |  |             Ok(_) => { | 
| 235 |  |                 println!("succeeded in demangling"); | 
| 236 |  |                 false | 
| 237 |  |             } | 
| 238 |  |             Err(_) => ::demangle(sym).to_string() == sym, | 
| 239 |  |         } | 
| 240 |  |     } | 
| 241 |  |  | 
| 242 |  |     #[test] | 
| 243 |  |     fn demangle() { | 
| 244 |  |         t_err!("test"); | 
| 245 |  |         t!("_ZN4testE", "test"); | 
| 246 |  |         t_err!("_ZN4test"); | 
| 247 |  |         t!("_ZN4test1a2bcE", "test::a::bc"); | 
| 248 |  |     } | 
| 249 |  |  | 
| 250 |  |     #[test] | 
| 251 |  |     fn demangle_dollars() { | 
| 252 |  |         t!("_ZN4$RP$E", ")"); | 
| 253 |  |         t!("_ZN8$RF$testE", "&test"); | 
| 254 |  |         t!("_ZN8$BP$test4foobE", "*test::foob"); | 
| 255 |  |         t!("_ZN9$u20$test4foobE", " test::foob"); | 
| 256 |  |         t!("_ZN35Bar$LT$$u5b$u32$u3b$$u20$4$u5d$$GT$E", "Bar<[u32; 4]>"); | 
| 257 |  |     } | 
| 258 |  |  | 
| 259 |  |     #[test] | 
| 260 |  |     fn demangle_many_dollars() { | 
| 261 |  |         t!("_ZN13test$u20$test4foobE", "test test::foob"); | 
| 262 |  |         t!("_ZN12test$BP$test4foobE", "test*test::foob"); | 
| 263 |  |     } | 
| 264 |  |  | 
| 265 |  |     #[test] | 
| 266 |  |     fn demangle_osx() { | 
| 267 |  |         t!( | 
| 268 |  |             "__ZN5alloc9allocator6Layout9for_value17h02a996811f781011E", | 
| 269 |  |             "alloc::allocator::Layout::for_value::h02a996811f781011" | 
| 270 |  |         ); | 
| 271 |  |         t!("__ZN38_$LT$core..option..Option$LT$T$GT$$GT$6unwrap18_MSG_FILE_LINE_COL17haf7cb8d5824ee659E", "<core::option::Option<T>>::unwrap::_MSG_FILE_LINE_COL::haf7cb8d5824ee659"); | 
| 272 |  |         t!("__ZN4core5slice89_$LT$impl$u20$core..iter..traits..IntoIterator$u20$for$u20$$RF$$u27$a$u20$$u5b$T$u5d$$GT$9into_iter17h450e234d27262170E", "core::slice::<impl core::iter::traits::IntoIterator for &'a [T]>::into_iter::h450e234d27262170"); | 
| 273 |  |     } | 
| 274 |  |  | 
| 275 |  |     #[test] | 
| 276 |  |     fn demangle_windows() { | 
| 277 |  |         t!("ZN4testE", "test"); | 
| 278 |  |         t!("ZN13test$u20$test4foobE", "test test::foob"); | 
| 279 |  |         t!("ZN12test$RF$test4foobE", "test&test::foob"); | 
| 280 |  |     } | 
| 281 |  |  | 
| 282 |  |     #[test] | 
| 283 |  |     fn demangle_elements_beginning_with_underscore() { | 
| 284 |  |         t!("_ZN13_$LT$test$GT$E", "<test>"); | 
| 285 |  |         t!("_ZN28_$u7b$$u7b$closure$u7d$$u7d$E", "{{closure}}"); | 
| 286 |  |         t!("_ZN15__STATIC_FMTSTRE", "__STATIC_FMTSTR"); | 
| 287 |  |     } | 
| 288 |  |  | 
| 289 |  |     #[test] | 
| 290 |  |     fn demangle_trait_impls() { | 
| 291 |  |         t!( | 
| 292 |  |             "_ZN71_$LT$Test$u20$$u2b$$u20$$u27$static$u20$as$u20$foo..Bar$LT$Test$GT$$GT$3barE", | 
| 293 |  |             "<Test + 'static as foo::Bar<Test>>::bar" | 
| 294 |  |         ); | 
| 295 |  |     } | 
| 296 |  |  | 
| 297 |  |     #[test] | 
| 298 |  |     fn demangle_without_hash() { | 
| 299 |  |         let s = "_ZN3foo17h05af221e174051e9E"; | 
| 300 |  |         t!(s, "foo::h05af221e174051e9"); | 
| 301 |  |         t_nohash!(s, "foo"); | 
| 302 |  |     } | 
| 303 |  |  | 
| 304 |  |     #[test] | 
| 305 |  |     fn demangle_without_hash_edgecases() { | 
| 306 |  |         // One element, no hash. | 
| 307 |  |         t_nohash!("_ZN3fooE", "foo"); | 
| 308 |  |         // Two elements, no hash. | 
| 309 |  |         t_nohash!("_ZN3foo3barE", "foo::bar"); | 
| 310 |  |         // Longer-than-normal hash. | 
| 311 |  |         t_nohash!("_ZN3foo20h05af221e174051e9abcE", "foo"); | 
| 312 |  |         // Shorter-than-normal hash. | 
| 313 |  |         t_nohash!("_ZN3foo5h05afE", "foo"); | 
| 314 |  |         // Valid hash, but not at the end. | 
| 315 |  |         t_nohash!("_ZN17h05af221e174051e93fooE", "h05af221e174051e9::foo"); | 
| 316 |  |         // Not a valid hash, missing the 'h'. | 
| 317 |  |         t_nohash!("_ZN3foo16ffaf221e174051e9E", "foo::ffaf221e174051e9"); | 
| 318 |  |         // Not a valid hash, has a non-hex-digit. | 
| 319 |  |         t_nohash!("_ZN3foo17hg5af221e174051e9E", "foo::hg5af221e174051e9"); | 
| 320 |  |     } | 
| 321 |  |  | 
| 322 |  |     #[test] | 
| 323 |  |     fn demangle_thinlto() { | 
| 324 |  |         // One element, no hash. | 
| 325 |  |         t!("_ZN3fooE.llvm.9D1C9369", "foo"); | 
| 326 |  |         t!("_ZN3fooE.llvm.9D1C9369@@16", "foo"); | 
| 327 |  |         t_nohash!( | 
| 328 |  |             "_ZN9backtrace3foo17hbb467fcdaea5d79bE.llvm.A5310EB9", | 
| 329 |  |             "backtrace::foo" | 
| 330 |  |         ); | 
| 331 |  |     } | 
| 332 |  |  | 
| 333 |  |     #[test] | 
| 334 |  |     fn demangle_llvm_ir_branch_labels() { | 
| 335 |  |         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"); | 
| 336 |  |         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"); | 
| 337 |  |     } | 
| 338 |  |  | 
| 339 |  |     #[test] | 
| 340 |  |     fn demangle_ignores_suffix_that_doesnt_look_like_a_symbol() { | 
| 341 |  |         t_err!("_ZN3fooE.llvm moocow"); | 
| 342 |  |     } | 
| 343 |  |  | 
| 344 |  |     #[test] | 
| 345 |  |     fn dont_panic() { | 
| 346 |  |         ::demangle("_ZN2222222222222222222222EE").to_string(); | 
| 347 |  |         ::demangle("_ZN5*70527e27.ll34csaғE").to_string(); | 
| 348 |  |         ::demangle("_ZN5*70527a54.ll34_$b.1E").to_string(); | 
| 349 |  |         ::demangle( | 
| 350 |  |             "\ | 
| 351 |  |              _ZN5~saäb4e\n\ | 
| 352 |  |              2734cOsbE\n\ | 
| 353 |  |              5usage20h)3\0\0\0\0\0\0\07e2734cOsbE\ | 
| 354 |  |              ", | 
| 355 |  |         ) | 
| 356 |  |         .to_string(); | 
| 357 |  |     } | 
| 358 |  |  | 
| 359 |  |     #[test] | 
| 360 |  |     fn invalid_no_chop() { | 
| 361 |  |         t_err!("_ZNfooE"); | 
| 362 |  |     } | 
| 363 |  |  | 
| 364 |  |     #[test] | 
| 365 |  |     fn handle_assoc_types() { | 
| 366 |  |         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", "<alloc::boxed::Box<alloc::boxed::FnBox<A, Output=R> + 'a> as core::ops::function::FnOnce<A>>::call_once::h69e8f44b3723e1ca"); | 
| 367 |  |     } | 
| 368 |  |  | 
| 369 |  |     #[test] | 
| 370 |  |     fn handle_bang() { | 
| 371 |  |         t!( | 
| 372 |  |             "_ZN88_$LT$core..result..Result$LT$$u21$$C$$u20$E$GT$$u20$as$u20$std..process..Termination$GT$6report17hfc41d0da4a40b3e8E", | 
| 373 |  |             "<core::result::Result<!, E> as std::process::Termination>::report::hfc41d0da4a40b3e8" | 
| 374 |  |         ); | 
| 375 |  |     } | 
| 376 |  |  | 
| 377 |  |     #[test] | 
| 378 |  |     fn demangle_utf8_idents() { | 
| 379 |  |         t_nohash!( | 
| 380 |  |             "_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", | 
| 381 |  |             "utf8_idents::საჭმელად_გემრიელი_სადილი" | 
| 382 |  |         ); | 
| 383 |  |     } | 
| 384 |  |  | 
| 385 |  |     #[test] | 
| 386 |  |     fn demangle_issue_60925() { | 
| 387 |  |         t_nohash!( | 
| 388 |  |             "_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17h059a991a004536adE", | 
| 389 |  |             "issue_60925::foo::Foo<issue_60925::llvm::Foo>::foo" | 
| 390 |  |         ); | 
| 391 |  |     } | 
| 392 |  | } |