#![doc = include_str!("../README.md")]
#![warn(clippy::pedantic)]
#![deny(clippy::undocumented_unsafe_blocks)]
#![allow(
clippy::missing_errors_doc,
clippy::similar_names,
clippy::struct_field_names,
clippy::too_many_lines
)]
mod alloc;
mod cache;
mod demangler;
mod extensions;
mod mangled_string;
mod nodes;
#[cfg(test)]
mod tests;
use crate::demangler::Demangler;
use bumpalo::Bump;
use std::{
io,
str::Utf8Error,
string::FromUtf8Error,
};
type OutputFlags = Flags;
trait Writer: io::Write {
fn last_char(&self) -> Option<char>;
fn len_bytes(&self) -> usize;
}
#[non_exhaustive]
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("failed to demangle anonymous namespace name")]
InvalidAnonymousNamespaceName,
#[error("failed to demangle array type")]
InvalidArrayType,
#[error("tried to access a backref that does not exist")]
InvalidBackRef,
#[error("failed to demangle calling convention")]
InvalidCallingConvention,
#[error("failed to demangle char literal")]
InvalidCharLiteral,
#[error("failed to demangle class type")]
InvalidClassType,
#[error("failed to demangle custom type")]
InvalidCustomType,
#[error("failed to demangle declarator")]
InvalidDeclarator,
#[error("failed to demangle encoded symbol")]
InvalidEncodedSymbol,
#[error("failed to demangle fully qualified symbol name")]
InvalidFullyQualifiedSymbolName,
#[error("failed to demangle function class")]
InvalidFunctionClass,
#[error("failed to demangle function encoding")]
InvalidFunctionEncoding,
#[error("failed to demangle function identifier code")]
InvalidFunctionIdentifierCode,
#[error("failed to demangle function parameter list")]
InvalidFunctionParameterList,
#[error("failed to demangle function type")]
InvalidFunctionType,
#[error("failed to demangle init fini stub")]
InvalidInitFiniStub,
#[error("failed to demangle intrinsic function code")]
InvalidIntrinsicFunctionCode,
#[error("failed to demangle locally scoped name piece")]
InvalidLocallyScopedNamePiece,
#[error("failed to demangle local static guard")]
InvalidLocalStaticGuard,
#[error("failed to demangle md5 name")]
InvalidMd5Name,
#[error("failed to demangle member pointer type")]
InvalidMemberPointerType,
#[error("failed to demangle name scope chain")]
InvalidNameScopeChain,
#[error("failed to demangle number")]
InvalidNumber,
#[error("failed to demangle pointer cv qualifiers")]
InvalidPointerCVQualifiers,
#[error("failed to demangle pointer type")]
InvalidPointerType,
#[error("failed to demangle primitive type")]
InvalidPrimitiveType,
#[error("failed to demangle qualifiers")]
InvalidQualifiers,
#[error("failed to demangle rtti base class descriptor node")]
InvalidRttiBaseClassDescriptorNode,
#[error("failed to demangle signed number")]
InvalidSigned,
#[error("failed to demangle simple string")]
InvalidSimpleString,
#[error("failed to demangle special intrinsic")]
InvalidSpecialIntrinsic,
#[error("failed to demangle special table symbol node")]
InvalidSpecialTableSymbolNode,
#[error("failed to demangle string literal")]
InvalidStringLiteral,
#[error("failed to demangle tag unique name")]
InvalidTagUniqueName,
#[error("failed to demangle template instantiation name")]
InvalidTemplateInstantiationName,
#[error("failed to demangle template parameter list")]
InvalidTemplateParameterList,
#[error("failed to demangle throw specification")]
InvalidThrowSpecification,
#[error("failed to demangle type")]
InvalidType,
#[error("failed to demangle typinfo name")]
InvalidTypeinfoName,
#[error("failed to demangle unsigned number")]
InvalidUnsigned,
#[error("failed to demangle untyped variable")]
InvalidUntypedVariable,
#[error("failed to demangle variable storage class")]
InvalidVariableStorageClass,
#[error("failed to demangle vcall thunk node")]
InvalidVcallThunkNode,
#[error(transparent)]
Io(#[from] io::Error),
#[error("string demangled to an invalid utf-8 sequence")]
Utf8Error,
#[error("input string was likely malicious and would have triggered an out of memory panic")]
MaliciousInput,
}
impl From<Utf8Error> for Error {
fn from(_: Utf8Error) -> Self {
Self::Utf8Error
}
}
impl From<FromUtf8Error> for Error {
fn from(_: FromUtf8Error) -> Self {
Self::Utf8Error
}
}
pub type Result<T> = std::result::Result<T, Error>;
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub struct Flags: u16 {
const NO_CALLING_CONVENTION = 1 << 0;
const NO_ALLOCATION_LANGUAGE = Self::NO_CALLING_CONVENTION.bits();
const NO_TAG_SPECIFIER = 1 << 1;
const NO_ECSU = Self::NO_TAG_SPECIFIER.bits();
const NO_ACCESS_SPECIFIER = 1 << 2;
const NO_ACCESS_SPECIFIERS = Self::NO_ACCESS_SPECIFIER.bits();
const NO_MEMBER_TYPE = 1 << 3;
const NO_RETURN_TYPE = 1 << 4;
const NO_FUNCTION_RETURNS = Self::NO_RETURN_TYPE.bits();
const NO_VARIABLE_TYPE = 1 << 5;
const NO_THISTYPE = 1 << 6;
const NO_LEADING_UNDERSCORES = 1 << 7;
const NO_MS_KEYWORDS = 1 << 8;
const NAME_ONLY = 1 << 9;
}
}
impl Flags {
#[must_use]
fn no_calling_convention(self) -> bool {
self.contains(Self::NO_CALLING_CONVENTION)
}
#[must_use]
fn no_tag_specifier(self) -> bool {
self.contains(Self::NO_TAG_SPECIFIER)
}
#[must_use]
fn no_access_specifier(self) -> bool {
self.contains(Self::NO_ACCESS_SPECIFIER)
}
#[must_use]
fn no_member_type(self) -> bool {
self.contains(Self::NO_MEMBER_TYPE)
}
#[must_use]
fn no_return_type(self) -> bool {
self.contains(Self::NO_RETURN_TYPE)
}
#[must_use]
fn no_variable_type(self) -> bool {
self.contains(Self::NO_VARIABLE_TYPE)
}
#[must_use]
fn no_this_type(self) -> bool {
self.contains(Self::NO_THISTYPE)
}
#[must_use]
fn no_leading_underscores(self) -> bool {
self.contains(Self::NO_LEADING_UNDERSCORES)
}
#[must_use]
fn no_ms_keywords(self) -> bool {
self.contains(Self::NO_MS_KEYWORDS)
}
#[must_use]
fn name_only(self) -> bool {
self.contains(Self::NAME_ONLY)
}
}
pub fn demangle(mangled_name: &str, flags: Flags) -> Result<String> {
let mut result = String::default();
demangle_into(mangled_name, flags, &mut result)?;
Ok(result)
}
pub fn demangle_into(mangled_name: &str, flags: Flags, result: &mut String) -> Result<()> {
let alloc = Bump::default();
let d = Demangler::new(mangled_name, flags, &alloc);
result.clear();
d.parse_into(result)
}