use std::fmt;
use itertools::Itertools;
use rudy_types::*;
use unsynn::*;
unsynn! {
keyword As = "as";
keyword Const = "const";
keyword Dyn = "dyn";
keyword FnKw = "fn";
keyword For = "for";
keyword Impl = "impl";
keyword Mut = "mut";
keyword Str = "str";
keyword Unsafe = "unsafe";
type Amp = PunctAny<'&'>;
#[derive(Clone)]
pub struct AngleTokenTree {
_lt: Lt,
inner: Vec<Either<Cons<Except<Either<Lt, Gt>>, TokenTree>, AngleTokenTree>>,
_gt: Gt,
}
#[derive(Clone)]
pub enum GenericArgs {
Parsed {
_lt: Lt,
inner: CommaDelimitedVec<Type>,
_gt: Gt,
},
Unparsed(AngleTokenTree)
}
keyword VTable = "vtable";
keyword Shim = "shim";
type VTableShim = Cons<VTable, PunctAny<'.'>, Shim>;
keyword VTableType = "vtable_type";
#[derive(Clone)]
pub struct Segment {
pub ident: Ident,
generics: Optional<GenericArgs>,
vtable_shim: Optional<BraceGroupContaining<BraceGroupContaining<VTableShim>>>,
}
#[derive(Clone)]
struct ImplNumber {
_impl_kw: Impl,
pound: PunctAny<'#'>,
number: usize,
}
#[derive(Clone)]
enum PathSegment {
Segment(Segment),
QualifiedSegment(AngleTokenTree),
VTableType(BraceGroupContaining<VTableType>),
ImplSegment(BraceGroupContaining<ImplNumber>),
}
#[derive(Clone)]
pub struct DynTrait {
dyn_kw: Dyn,
pub traits: DelimitedVec<Type, PunctAny<'+'>>,
}
#[derive(Clone)]
pub struct PtrType {
pointer_type: Cons<PunctAny<'*'>, Either<Const, Mut>>,
pub inner: Box<Type>,
}
#[derive(Clone)]
pub struct RefType {
amp: Amp,
mutability: Optional<Mut>,
pub inner: Box<Type>,
}
#[derive(Clone)]
pub enum ArraySize {
Fixed(usize),
Dynamic(Type),
}
#[derive(Clone)]
struct ArrayInner {
inner: Box<Type>,
size: Optional<Cons<PunctAny<';'>, ArraySize>>,
}
#[derive(Clone)]
pub struct Array {
inner: BracketGroupContaining<ArrayInner>,
}
type TypeWithTrailingComma =Cons<Box<Type>, PunctAny<','>>;
type ManyTypes = AtLeast<1, TypeWithTrailingComma>;
type TypeWithOptionalTrailingComma = Cons<Box<Type>, Optional<PunctAny<','>>>;
#[derive(Clone)]
pub enum Tuple {
Arity0(ParenthesisGroupContaining<Nothing>),
Arity1(ParenthesisGroupContaining<TypeWithOptionalTrailingComma>),
ArityN(
ParenthesisGroupContaining<
Cons<
ManyTypes,
TypeWithOptionalTrailingComma
>
>,
)
}
#[derive(Clone)]
struct FnResult {
arrow: Cons<PunctJoint<'-'>, PunctAny<'>'>>,
ret: Type,
}
#[derive(Clone)]
pub struct FnType {
unsafe_kw: Optional<Unsafe>,
fn_kw: FnKw,
args: ParenthesisGroupContaining<DelimitedVec<Type, PunctAny<','>>>,
ret: Optional<FnResult>,
}
#[derive(Clone)]
pub struct Slice {
_amp: Amp,
inner: BracketGroupContaining<Box<Type>>,
}
#[derive(Clone)]
pub struct StrSlice {
_amp: Amp,
inner: Str,
}
#[derive(Clone)]
pub enum Type {
Slice(Slice),
StrSlice(StrSlice),
Ref(RefType),
Never(PunctAny<'!'>),
Array(Array),
Function(FnType),
DynTrait(DynTrait),
Tuple(Tuple),
Ptr(PtrType),
Path(Path),
}
#[derive(Clone)]
pub struct Path {
segments: PathSepDelimitedVec<PathSegment>
}
}
impl Array {
pub fn inner(&self) -> &Type {
&self.inner.content.inner
}
pub fn concrete_size(&self) -> Option<usize> {
self.inner.content.size.0.first().and_then(|c| {
match &c.value.second {
ArraySize::Fixed(size) => Some(*size),
ArraySize::Dynamic(_) => None, }
})
}
pub fn generic_size(&self) -> Option<&Type> {
self.inner
.content
.size
.0
.first()
.and_then(|c| match &c.value.second {
ArraySize::Fixed(_) => None,
ArraySize::Dynamic(t) => Some(t),
})
}
}
impl Tuple {
pub fn inner(&self) -> Vec<Type> {
match self {
Tuple::Arity0(_) => vec![],
Tuple::Arity1(inner) => vec![*inner.content.first.clone()],
Tuple::ArityN(inner) => inner
.content
.first
.0
.iter()
.map(|c| *c.value.clone().first.clone())
.chain(std::iter::once(*inner.content.second.first.clone()))
.collect(),
}
}
}
impl FnType {
pub fn args(&self) -> Vec<Type> {
self.args
.content
.0
.iter()
.map(|t| t.value.clone())
.collect()
}
pub fn ret(&self) -> Option<&Type> {
self.ret.0.first().map(|c| &c.value.ret)
}
}
impl fmt::Display for Array {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[{}", self.inner())?;
if let Some(size) = self.concrete_size() {
write!(f, "; {size}")?;
}
if let Some(size) = self.generic_size() {
write!(f, "; {size}")?;
}
write!(f, "]")?;
Ok(())
}
}
impl fmt::Display for DynTrait {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"dyn {}",
self.traits.0.iter().map(|t| &t.value).join(" + ")
)
}
}
impl PtrType {
pub fn is_mutable(&self) -> bool {
matches!(self.pointer_type.second, Either::Second(Mut(_)))
}
}
impl RefType {
pub fn is_mutable(&self) -> bool {
!self.mutability.0.is_empty()
}
}
impl fmt::Display for Path {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.segments().join("::"))
}
}
impl fmt::Display for FnType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "fn")?;
if !self.args.content.0.is_empty() {
write!(
f,
"({})",
self.args.content.0.iter().map(|t| &t.value).join(", ")
)?;
} else {
write!(f, "()")?;
}
if let Some(ret) = &self.ret.0.first() {
write!(f, " -> {}", ret.value.ret)?;
}
Ok(())
}
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Type::Ref(r) => {
write!(f, "&")?;
if r.is_mutable() {
write!(f, "mut ")?;
}
write!(f, "{}", r.inner)
}
Type::Slice(s) => write!(f, "&[{}]", s.inner.content),
Type::StrSlice(_) => write!(f, "&str"),
Type::Array(a) => write!(f, "{a}"),
Type::DynTrait(d) => write!(f, "{d}"),
Type::Tuple(t) => {
let elements = t.inner();
if elements.len() == 1 {
write!(f, "({},)", elements[0])
} else {
write!(f, "({})", elements.iter().map(|e| e.to_string()).join(", "))
}
}
Type::Ptr(p) => write!(f, "{}{}", p.pointer_type.tokens_to_string(), p.inner),
Type::Path(p) => write!(f, "{p}"),
Type::Function(fn_type) => write!(f, "{fn_type}"),
Type::Never(_) => write!(f, "!"),
}
}
}
impl Path {
pub fn segments(&self) -> Vec<String> {
self.segments
.0
.iter()
.map(|path_segment| match &path_segment.value {
PathSegment::Segment(segment) => {
format!(
"{}{}",
segment.ident,
match segment.generics.0.first().as_ref().map(|g| &g.value) {
Some(GenericArgs::Parsed {
_lt: _,
inner,
_gt: _,
}) => {
format!(
"<{}>",
inner.0.iter().map(|d| d.value.to_string()).join(", ")
)
}
Some(GenericArgs::Unparsed(angle_token_tree)) => {
angle_token_tree.tokens_to_string()
}
None => String::new(),
}
)
}
p => p.tokens_to_string(),
})
.collect()
}
}
pub type ParsedSymbol = (Vec<String>, String, Option<String>);
pub fn parse_symbol(s: &str) -> anyhow::Result<ParsedSymbol> {
let mut segments = Vec::with_capacity(4);
let mut current_segment = String::with_capacity(64);
let mut angle_depth = 0;
let mut chars = s.chars().peekable();
while let Some(ch) = chars.next() {
match ch {
'<' => {
angle_depth += 1;
current_segment.push(ch);
}
'>' => {
angle_depth -= 1;
current_segment.push(ch);
}
':' if angle_depth == 0 && chars.peek() == Some(&':') => {
chars.next(); if !current_segment.is_empty() {
segments.push(current_segment.trim().to_string());
current_segment.clear();
}
}
'\n' | '\r' | '\t' | ' ' => {
if !current_segment.is_empty() && !current_segment.ends_with(' ') {
current_segment.push(' ');
}
}
_ => {
current_segment.push(ch);
}
}
}
if !current_segment.is_empty() {
segments.push(current_segment.trim().to_string());
}
if segments.is_empty() {
anyhow::bail!("Empty symbol path");
}
let hash = if let Some(last) = segments.last() {
if last.starts_with('h') && last.chars().skip(1).all(|c| c.is_ascii_hexdigit()) {
segments.pop()
} else {
None
}
} else {
None
};
let Some(function_name) = segments.pop() else {
anyhow::bail!("No function name found");
};
segments.shrink_to_fit();
let module_path = segments;
Ok((module_path, function_name, hash))
}
pub fn parse_type(s: &str) -> unsynn::Result<Type> {
let mut iter = s.to_token_iter();
let ty = Cons::<Type, EndOfStream>::parse(&mut iter)?;
Ok(ty.first)
}
impl Type {
pub fn as_layout(&self) -> Layout {
match self {
Type::Path(path) => {
path.as_typedef()
}
Type::Slice(slice) => {
let inner = slice.inner.content.clone().as_layout();
Layout::Primitive(PrimitiveLayout::Slice(SliceLayout {
element_type: TypeDefinition::new((), inner),
data_ptr_offset: 0,
length_offset: 0,
}))
}
Type::StrSlice(_) => {
Layout::Primitive(PrimitiveLayout::StrSlice(StrSliceLayout {
data_ptr_offset: 0,
length_offset: 0,
}))
}
Type::Ref(ref_type) => {
let inner = ref_type.inner.as_layout();
Layout::Primitive(PrimitiveLayout::Reference(ReferenceLayout {
mutable: ref_type.is_mutable(),
pointed_type: TypeDefinition::new((), inner),
}))
}
Type::Ptr(ptr_type) => {
let inner = ptr_type.inner.as_layout();
Layout::Primitive(PrimitiveLayout::Pointer(PointerLayout {
mutable: ptr_type.is_mutable(),
pointed_type: TypeDefinition::new((), inner),
}))
}
Type::Array(array) => {
let inner = array.inner().clone().as_layout();
let length = if let Some(size_type) = array.concrete_size() {
size_type.to_string().parse::<usize>().unwrap_or(0)
} else {
0 };
Layout::Primitive(PrimitiveLayout::Array(ArrayLayout {
element_type: TypeDefinition::new((), inner),
length,
}))
}
Type::Tuple(tuple) => {
let elements: Vec<_> = tuple
.inner()
.iter()
.map(|t| (0, TypeDefinition::new((), t.as_layout())))
.collect();
if elements.is_empty() {
Layout::Primitive(PrimitiveLayout::Unit(UnitLayout))
} else {
Layout::Primitive(PrimitiveLayout::Tuple(TupleLayout {
elements,
size: 0, }))
}
}
Type::DynTrait(_dyn_trait) => {
Layout::Alias {
name: self.to_string(),
}
}
Type::Function(fn_type) => {
Layout::Primitive(PrimitiveLayout::Function(FunctionLayout {
return_type: fn_type
.ret()
.map(|l| TypeDefinition::new((), l.as_layout())),
arg_types: fn_type
.args()
.iter()
.map(|a| TypeDefinition::new((), a.as_layout()))
.collect(),
}))
}
Type::Never(_) => {
Layout::Primitive(PrimitiveLayout::Never(()))
}
}
}
}
impl Path {
fn as_typedef(&self) -> Layout {
let segments = self.segments();
if segments.is_empty() {
return Layout::Alias {
name: String::new(),
};
}
let last_segment = segments.last().unwrap();
if segments.len() == 1 {
match last_segment.as_str() {
"u8" => {
return UnsignedIntLayout { size: 1 }.into();
}
"u16" => {
return UnsignedIntLayout { size: 2 }.into();
}
"u32" => {
return UnsignedIntLayout { size: 4 }.into();
}
"u64" => {
return UnsignedIntLayout { size: 8 }.into();
}
"u128" => {
return UnsignedIntLayout { size: 16 }.into();
}
"usize" => {
return UnsignedIntLayout {
size: std::mem::size_of::<usize>(),
}
.into();
}
"i8" => return IntLayout { size: 1 }.into(),
"i16" => return IntLayout { size: 2 }.into(),
"i32" => return IntLayout { size: 4 }.into(),
"i64" => return IntLayout { size: 8 }.into(),
"i128" => return IntLayout { size: 16 }.into(),
"isize" => {
return IntLayout {
size: std::mem::size_of::<isize>(),
}
.into();
}
"f32" => return FloatLayout { size: 4 }.into(),
"f64" => return FloatLayout { size: 8 }.into(),
"bool" => return Layout::Primitive(PrimitiveLayout::Bool(())),
"char" => return Layout::Primitive(PrimitiveLayout::Char(())),
"str" => return Layout::Primitive(PrimitiveLayout::Str(())),
"()" => return Layout::Primitive(PrimitiveLayout::Unit(UnitLayout)),
_ => {}
}
}
let is_std = segments[0] == "std" || segments[0] == "core" || segments[0] == "alloc";
let is_hashbrown = segments[0] == "hashbrown";
tracing::trace!("Parser segments: {:?}, is_std: {}", segments, is_std);
if is_std || is_hashbrown || segments.len() == 1 {
if let Some(path_segment) = self.segments.0.last()
&& let PathSegment::Segment(segment) = &path_segment.value
{
let type_name = segment.ident.to_string();
let get_generics = || {
segment
.generics
.0
.first()
.map_or_else(std::vec::Vec::new, |generic_args| {
match &generic_args.value {
GenericArgs::Parsed { inner, .. } => {
inner.0.iter().map(|d| d.value.clone()).collect()
}
GenericArgs::Unparsed(_) => vec![],
}
})
};
tracing::trace!("Checking std type: '{}' against known types", type_name);
match type_name.as_str() {
"String" => {
tracing::trace!("Matched String type!");
return Layout::Std(StdLayout::String(StringLayout(VecLayout {
length_offset: 0,
data_ptr_offset: 0,
capacity_offset: 0,
inner_type: TypeDefinition::new(
(),
Layout::Primitive(PrimitiveLayout::UnsignedInt(
UnsignedIntLayout { size: 1 },
)),
),
})));
}
"Vec" => {
let inner = get_generics()
.first()
.map(|t| TypeDefinition::new((), t.as_layout()))
.unwrap_or_else(|| {
TypeDefinition::new(
(),
Layout::Alias {
name: "Unknown".to_string(),
},
)
});
return Layout::Std(StdLayout::Vec(VecLayout {
inner_type: inner,
length_offset: 0,
data_ptr_offset: 0,
capacity_offset: 0,
}));
}
"Option" => {
let inner = get_generics()
.first()
.map(|t| TypeDefinition::new((), t.as_layout()))
.unwrap_or_else(|| {
TypeDefinition::new(
(),
Layout::Alias {
name: "Unknown".to_string(),
},
)
});
return Layout::Std(StdLayout::Option(OptionLayout {
name: "Option".to_string(),
discriminant: Discriminant {
offset: 0,
ty: DiscriminantType::Implicit,
},
some_offset: 0,
some_type: inner,
size: 0,
}));
}
"Result" => {
let mut generics_iter = get_generics().into_iter();
let ok_type = generics_iter
.next()
.map(|t| TypeDefinition::new((), t.as_layout()))
.unwrap_or_else(|| {
TypeDefinition::new(
(),
Layout::Alias {
name: "Unknown".to_string(),
},
)
});
let err_type = generics_iter
.next()
.map(|t| TypeDefinition::new((), t.as_layout()))
.unwrap_or_else(|| {
TypeDefinition::new(
(),
Layout::Alias {
name: "Unknown".to_string(),
},
)
});
return Layout::Std(StdLayout::Result(ResultLayout {
name: "Result".to_string(),
discriminant: Discriminant {
offset: 0,
ty: DiscriminantType::Implicit,
},
ok_type,
ok_offset: 0,
err_type,
err_offset: 0,
size: 0,
}));
}
"HashMap" | "BTreeMap" => {
let mut generics_iter = get_generics().into_iter();
let key_type = generics_iter
.next()
.map(|t| TypeDefinition::new((), t.as_layout()))
.unwrap_or_else(|| {
TypeDefinition::new(
(),
Layout::Alias {
name: "Unknown".to_string(),
},
)
});
let value_type = generics_iter
.next()
.map(|t| TypeDefinition::new((), t.as_layout()))
.unwrap_or_else(|| {
TypeDefinition::new(
(),
Layout::Alias {
name: "Unknown".to_string(),
},
)
});
let variant = match type_name.as_str() {
"HashMap" => MapVariant::HashMap {
bucket_mask_offset: 0,
ctrl_offset: 0,
items_offset: 0,
pair_size: 0,
key_offset: 0,
value_offset: 0,
},
"BTreeMap" => MapVariant::BTreeMap {
length_offset: 0,
root_offset: 0,
root_layout: BTreeRootLayout {
node_offset: 0,
height_offset: 0,
},
node_layout: BTreeNodeLayout {
keys_offset: 0,
vals_offset: 0,
len_offset: 0,
edges_offset: 0,
},
},
_ => unreachable!(),
};
tracing::trace!("Matched Map type: '{type_name}'");
return Layout::Std(StdLayout::Map(MapLayout {
key_type,
value_type,
variant,
}));
}
"Box" | "Rc" | "Arc" | "Cell" | "RefCell" | "UnsafeCell" | "Mutex"
| "RwLock" => {
let inner = get_generics()
.into_iter()
.next()
.map(|t| TypeDefinition::new((), t.as_layout()))
.unwrap_or_else(|| {
TypeDefinition::new(
(),
Layout::Alias {
name: "Unknown".to_string(),
},
)
});
let variant = match type_name.as_str() {
"Box" => SmartPtrVariant::Box,
"Rc" => SmartPtrVariant::Rc,
"Arc" => SmartPtrVariant::Arc,
"Cell" => SmartPtrVariant::Cell,
"RefCell" => SmartPtrVariant::RefCell,
"UnsafeCell" => SmartPtrVariant::UnsafeCell,
"Mutex" => SmartPtrVariant::Mutex,
"RwLock" => SmartPtrVariant::RwLock,
_ => unreachable!(),
};
return Layout::Std(StdLayout::SmartPtr(SmartPtrLayout {
inner_type: inner,
inner_ptr_offset: 0,
data_ptr_offset: 0,
variant,
}));
}
_ => {}
}
}
}
Layout::Alias {
name: last_segment.clone(),
}
}
}
#[cfg(test)]
mod test {
use itertools::Itertools;
use pretty_assertions::assert_eq;
use rudy_types::*;
use super::*;
#[track_caller]
fn parse_symbol(s: &str) -> ParsedSymbol {
match super::parse_symbol(s) {
Ok(s) => s,
Err(e) => {
panic!(
"Failed to parse symbol `{s}`: {e}\nTokens:\n{}",
s.to_token_iter().map(|t| format!("{t:?}")).join("\n")
);
}
}
}
#[track_caller]
fn parse_type(s: &str) -> Type {
match super::parse_type(s) {
Ok(p) => p,
Err(e) => {
panic!(
"Failed to parse type `{s}`: {e}\nTokens:\n{}",
s.to_token_iter().map(|t| format!("{t:?}")).join("\n")
);
}
}
}
#[allow(unused)]
#[track_caller]
fn parse_arbitrary<T>(s: &str) -> T
where
T: Parse,
{
let mut iter = s.to_token_iter();
match Cons::<T, EndOfStream>::parse(&mut iter) {
Ok(t) => t.first,
Err(e) => {
panic!(
"Failed to parse `{s}` as {}: {e}\nTokens:\n{}",
std::any::type_name::<T>(),
s.to_token_iter().map(|t| format!("{t:?}")).join("\n")
);
}
}
}
#[test]
fn test_symbol_parsing() {
parse_symbol("u8");
let mut iter = "<impl foo as bar>".to_token_iter();
AngleTokenTree::parse(&mut iter).unwrap();
parse_symbol("NonZero");
parse_symbol("NonZero<u8>");
parse_symbol("core::num::nonzero::NonZero");
parse_symbol("core::num::nonzero::NonZero<u8>");
parse_symbol("core::num::nonzero::NonZero<u8>::ilog2::hc1106854ed63a858");
parse_symbol(
"drop_in_place<std::backtrace_rs::symbolize::gimli::parse_running_mmaps::MapsEntry>",
);
parse_symbol(
"alloc::ffi::c_str::<
impl
core::convert::From<
&core::ffi::c_str::CStr
>
for
alloc::boxed::Box<
core::ffi::c_str::CStr
>
>::from::hec874816052de6db",
);
assert_eq!(
parse_symbol(
"alloc::ffi::c_str::<
impl
core::convert::From<
&core::ffi::c_str::CStr
>
for
alloc::boxed::Box<
core::ffi::c_str::CStr
>
>::from::hec874816052de6db"
)
,
(
vec![
"alloc".to_string(),
"ffi".to_string(),
"c_str".to_string(),
"< impl core::convert::From< &core::ffi::c_str::CStr > for alloc::boxed::Box< core::ffi::c_str::CStr > >".to_string(),
],
"from".to_string(),
Some("hec874816052de6db".to_string())
)
);
parse_symbol("core::ops::function::FnOnce::call_once{{vtable.shim}}::h7689c9dccb951788");
parse_symbol("_Unwind_SetIP@GCC_3.0");
parse_symbol("__rustc[95feac21a9532783]::__rust_alloc_zeroed");
}
#[test]
fn test_type_parsing() {
parse_type("u8");
parse_type("&u8");
parse_type("dyn core::fmt::Debug");
parse_type("dyn core::fmt::Debug + core::fmt::Display");
parse_type("&mut dyn core::fmt::Write");
parse_type("&[core::fmt::rt::Argument]");
parse_type("<&alloc::string::String as core::fmt::Debug>::{vtable_type}");
parse_type("(usize, core::option::Option<usize>)");
parse_type("*const [i32]");
parse_type("&mut dyn core::ops::function::FnMut<(usize), Output=bool>");
parse_type("&&i32");
parse_type("!");
}
#[test]
fn test_type_printing() {
let s = "hashbrown::map::HashMap<alloc::string::String, i32, std::hash::random::RandomState, alloc::alloc::Global>";
assert_eq!(parse_type(s).to_string(), s.to_string());
}
#[track_caller]
fn infer<T: Into<Layout> + fmt::Debug>(s: &str, expected: T) {
let ty = parse_type(s).as_layout();
assert_eq!(ty, expected.into(), "Failed to parse type `{s}`");
}
fn string_def() -> TypeDefinition {
TypeDefinition::new(
(),
Layout::Std(StdLayout::String(StringLayout(VecLayout {
length_offset: 0,
data_ptr_offset: 0,
capacity_offset: 0,
inner_type: TypeDefinition::new(
(),
Layout::Primitive(PrimitiveLayout::UnsignedInt(UnsignedIntLayout { size: 1 })),
),
}))),
)
}
#[test]
fn test_type_inference() {
let _ = tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.try_init();
infer("u8", UnsignedIntLayout::u8());
infer("u32", UnsignedIntLayout::u32());
infer("()", PrimitiveLayout::from(UnitLayout));
infer(
"(u8,)",
PrimitiveLayout::Tuple(TupleLayout {
elements: vec![(0, TypeDefinition::new((), UnsignedIntLayout::u8().into()))],
size: 0, }),
);
infer(
"(u8,u64)",
PrimitiveLayout::Tuple(TupleLayout {
elements: vec![
(0, TypeDefinition::new((), UnsignedIntLayout::u8().into())),
(0, TypeDefinition::new((), UnsignedIntLayout::u64().into())),
],
size: 0, }),
);
infer(
"&u8",
ReferenceLayout::new_immutable(UnsignedIntLayout::u8()),
);
infer(
"&mut u8",
ReferenceLayout::new_mutable(UnsignedIntLayout::u8()),
);
infer(
"dyn core::fmt::Debug",
Layout::Alias {
name: "dyn core::fmt::Debug".to_string(),
},
);
infer(
"alloc::vec::Vec<u8>",
VecLayout::new(UnsignedIntLayout::u8()),
);
infer(
"alloc::vec::Vec<alloc::vec::Vec<u8>>",
VecLayout::new(VecLayout::new(UnsignedIntLayout::u8())),
);
infer(
"alloc::vec::Vec<u8, alloc::alloc::Global>",
VecLayout::new(UnsignedIntLayout::u8()),
);
infer(
"core::option::Option<i32>",
StdLayout::Option(OptionLayout {
name: "Option".to_string(),
discriminant: Discriminant {
offset: 0,
ty: DiscriminantType::Implicit,
},
some_offset: 0,
some_type: TypeDefinition::new((), IntLayout::i32().into()),
size: 0,
}),
);
infer(
"alloc::boxed::Box<i32>",
SmartPtrLayout {
inner_type: TypeDefinition::new((), IntLayout::i32().into()),
variant: SmartPtrVariant::Box,
inner_ptr_offset: 0,
data_ptr_offset: 0,
},
);
infer(
"alloc::String::String",
string_def().layout.as_ref().clone(),
);
infer(
"std::collections::hash::map::HashMap<alloc::string::String, alloc::string::String>",
MapLayout {
key_type: string_def(),
value_type: string_def(),
variant: MapVariant::HashMap {
bucket_mask_offset: 0,
ctrl_offset: 0,
items_offset: 0,
pair_size: 0,
key_offset: 0,
value_offset: 0,
},
},
);
infer(
"core::num::nonzero::NonZero<u8>",
Layout::Alias {
name: "NonZero<u8>".to_string(),
},
);
infer(
"fn(&u64, &mut core::fmt::Formatter) -> core::result::Result<(), core::fmt::Error>",
Layout::Primitive(PrimitiveLayout::Function(FunctionLayout {
arg_types: vec![
TypeDefinition::new(
(),
ReferenceLayout::new_immutable(UnsignedIntLayout::u64()).into(),
),
TypeDefinition::new(
(),
ReferenceLayout::new_mutable(Layout::Alias {
name: "Formatter".to_string(),
})
.into(),
),
],
return_type: Some(TypeDefinition::new(
(),
StdLayout::Result(ResultLayout {
name: "Result".to_string(),
discriminant: Discriminant {
offset: 0,
ty: DiscriminantType::Implicit,
},
ok_type: TypeDefinition::new(
(),
Layout::Primitive(PrimitiveLayout::Unit(UnitLayout)),
),
ok_offset: 0,
err_type: TypeDefinition::new(
(),
Layout::Alias {
name: "Error".to_string(),
},
),
err_offset: 0,
size: 0,
})
.into(),
)),
})),
);
infer(
"&[u8]",
Layout::Primitive(PrimitiveLayout::Slice(SliceLayout {
element_type: TypeDefinition::new((), UnsignedIntLayout::u8().into()),
data_ptr_offset: 0,
length_offset: 0,
})),
);
infer(
"&str",
Layout::Primitive(PrimitiveLayout::StrSlice(StrSliceLayout {
data_ptr_offset: 0,
length_offset: 0,
})),
)
}
#[test]
fn test_symbol_parsing_basic() {
let (module_path, function_name, hash) = parse_symbol("core::num::ilog2::h12345");
assert_eq!(module_path, vec!["core", "num"]);
assert_eq!(function_name, "ilog2");
assert_eq!(hash, Some("h12345".to_string()));
let (module_path, function_name, hash) =
parse_symbol("core::num::nonzero::NonZero<u8>::ilog2::hc1106854ed63a858");
assert_eq!(module_path, vec!["core", "num", "nonzero", "NonZero<u8>"]);
assert_eq!(function_name, "ilog2");
assert_eq!(hash, Some("hc1106854ed63a858".to_string()));
let (module_path, function_name, hash) =
parse_symbol("std::collections::HashMap<String, i32>::insert");
assert_eq!(
module_path,
vec!["std", "collections", "HashMap<String, i32>"]
);
assert_eq!(function_name, "insert");
assert_eq!(hash, None);
let (module_path, function_name, hash) =
parse_symbol("std::collections::HashMap<String, Vec<i32>>::get");
assert_eq!(
module_path,
vec!["std", "collections", "HashMap<String, Vec<i32>>"]
);
assert_eq!(function_name, "get");
assert_eq!(hash, None);
let (module_path, function_name, hash) = parse_symbol("main");
assert_eq!(module_path, Vec::<String>::new());
assert_eq!(function_name, "main");
assert_eq!(hash, None);
let (module_path, function_name, hash) = parse_symbol("main::h123abc");
assert_eq!(module_path, Vec::<String>::new());
assert_eq!(function_name, "main");
assert_eq!(hash, Some("h123abc".to_string()));
}
#[test]
fn test_symbol_parsing_complex_cases() {
let (module_path, function_name, hash) = parse_symbol(
"alloc::ffi::c_str::<impl core::convert::From<&core::ffi::c_str::CStr> for alloc::boxed::Box<core::ffi::c_str::CStr>>::from::hec874816052de6db",
);
assert_eq!(
module_path,
vec![
"alloc",
"ffi",
"c_str",
"<impl core::convert::From<&core::ffi::c_str::CStr> for alloc::boxed::Box<core::ffi::c_str::CStr>>"
]
);
assert_eq!(function_name, "from");
assert_eq!(hash, Some("hec874816052de6db".to_string()));
}
#[test]
fn test_symbol_parsing_errors() {
assert!(super::parse_symbol("").is_err());
assert!(super::parse_symbol("h123abc").is_err());
}
}