1use std::{fmt, sync::Arc};
4
5use itertools::Itertools;
6use rudy_types::*;
7use unsynn::*;
8
9unsynn! {
11 keyword As = "as";
12 keyword Const = "const";
13 keyword Dyn = "dyn";
14 keyword FnKw = "fn";
15 keyword For = "for";
16 keyword Impl = "impl";
17 keyword Mut = "mut";
18 keyword Str = "str";
19 keyword Unsafe = "unsafe";
20 type Amp = PunctAny<'&'>;
21
22 #[derive(Clone)]
24 pub struct AngleTokenTree {
25 _lt: Lt,
26 inner: Vec<Either<Cons<Except<Either<Lt, Gt>>, TokenTree>, AngleTokenTree>>,
29 _gt: Gt,
30 }
31
32 #[derive(Clone)]
33 pub enum GenericArgs {
34 Parsed {
35 _lt: Lt,
36 inner: CommaDelimitedVec<Type>,
37 _gt: Gt,
38 },
39 Unparsed(AngleTokenTree)
42 }
43
44 keyword VTable = "vtable";
45 keyword Shim = "shim";
46 type VTableShim = Cons<VTable, PunctAny<'.'>, Shim>;
47 keyword VTableType = "vtable_type";
48
49 #[derive(Clone)]
50 pub struct Segment {
51 pub ident: Ident,
52 generics: Optional<GenericArgs>,
53 vtable_shim: Optional<BraceGroupContaining<BraceGroupContaining<VTableShim>>>,
55 }
56
57
58 #[derive(Clone)]
59 struct ImplNumber {
60 _impl_kw: Impl,
61 pound: PunctAny<'#'>,
62 number: usize,
63 }
64
65
66 #[derive(Clone)]
67 enum PathSegment {
68 Segment(Segment),
69 QualifiedSegment(AngleTokenTree),
70 VTableType(BraceGroupContaining<VTableType>),
71 ImplSegment(BraceGroupContaining<ImplNumber>),
72 }
73
74 #[derive(Clone)]
75 pub struct DynTrait {
76 dyn_kw: Dyn,
77 pub traits: DelimitedVec<Type, PunctAny<'+'>>,
78 }
79
80 #[derive(Clone)]
81 pub struct PtrType {
82 pointer_type: Cons<PunctAny<'*'>, Either<Const, Mut>>,
83 pub inner: Box<Type>,
84 }
85 #[derive(Clone)]
86 pub struct RefType {
87 amp: Amp,
88 mutability: Optional<Mut>,
89 pub inner: Box<Type>,
90 }
91
92
93
94 #[derive(Clone)]
95 pub enum ArraySize {
96 Fixed(usize),
97 Dynamic(Type),
98 }
99
100
101 #[derive(Clone)]
102 struct ArrayInner {
103 inner: Box<Type>,
104 size: Optional<Cons<PunctAny<';'>, ArraySize>>,
105 }
106
107 #[derive(Clone)]
108 pub struct Array {
109 inner: BracketGroupContaining<ArrayInner>,
110 }
111
112 type TypeWithTrailingComma =Cons<Box<Type>, PunctAny<','>>;
113 type ManyTypes = AtLeast<1, TypeWithTrailingComma>;
114 type TypeWithOptionalTrailingComma = Cons<Box<Type>, Optional<PunctAny<','>>>;
115
116 #[derive(Clone)]
117 pub enum Tuple {
118 Arity0(ParenthesisGroupContaining<Nothing>),
119 Arity1(ParenthesisGroupContaining<TypeWithOptionalTrailingComma>),
122 ArityN(
123 ParenthesisGroupContaining<
124 Cons<
125 ManyTypes,
126 TypeWithOptionalTrailingComma
127 >
128 >,
129 )
130 }
131
132 #[derive(Clone)]
133 struct FnResult {
134 arrow: Cons<PunctJoint<'-'>, PunctAny<'>'>>,
135 ret: Type,
136 }
137
138 #[derive(Clone)]
139 pub struct FnType {
140 unsafe_kw: Optional<Unsafe>,
141 fn_kw: FnKw,
142 args: ParenthesisGroupContaining<DelimitedVec<Type, PunctAny<','>>>,
143 ret: Optional<FnResult>,
144 }
145
146 #[derive(Clone)]
147 pub struct Slice {
148 _amp: Amp,
149 inner: BracketGroupContaining<Box<Type>>,
150 }
151
152 #[derive(Clone)]
153 pub struct StrSlice {
154 _amp: Amp,
155 inner: Str,
156 }
157
158
159 #[derive(Clone)]
160 pub enum Type {
161 Slice(Slice),
162 StrSlice(StrSlice),
163 Ref(RefType),
164 Never(PunctAny<'!'>),
165 Array(Array),
166 Function(FnType),
167 DynTrait(DynTrait),
168 Tuple(Tuple),
169 Ptr(PtrType),
170 Path(Path),
172 }
173
174 #[derive(Clone)]
176 pub struct Path {
177 segments: PathSepDelimitedVec<PathSegment>
178 }
179}
180
181impl Array {
182 pub fn inner(&self) -> &Type {
183 &self.inner.content.inner
184 }
185
186 pub fn concrete_size(&self) -> Option<usize> {
187 self.inner.content.size.0.first().and_then(|c| {
188 match &c.value.second {
189 ArraySize::Fixed(size) => Some(*size),
190 ArraySize::Dynamic(_) => None, }
192 })
193 }
194 pub fn generic_size(&self) -> Option<&Type> {
195 self.inner
196 .content
197 .size
198 .0
199 .first()
200 .and_then(|c| match &c.value.second {
201 ArraySize::Fixed(_) => None,
202 ArraySize::Dynamic(t) => Some(t),
203 })
204 }
205}
206
207impl Tuple {
208 pub fn inner(&self) -> Vec<Type> {
209 match self {
210 Tuple::Arity0(_) => vec![],
211 Tuple::Arity1(inner) => vec![*inner.content.first.clone()],
212 Tuple::ArityN(inner) => inner
213 .content
214 .first
215 .0
216 .iter()
217 .map(|c| *c.value.clone().first.clone())
218 .chain(std::iter::once(*inner.content.second.first.clone()))
219 .collect(),
220 }
221 }
222}
223
224impl FnType {
225 pub fn args(&self) -> Vec<Type> {
226 self.args
227 .content
228 .0
229 .iter()
230 .map(|t| t.value.clone())
231 .collect()
232 }
233
234 pub fn ret(&self) -> Option<&Type> {
235 self.ret.0.first().map(|c| &c.value.ret)
236 }
237}
238
239impl fmt::Display for Array {
240 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241 write!(f, "[{}", self.inner())?;
242 if let Some(size) = self.concrete_size() {
243 write!(f, "; {size}")?;
244 }
245 if let Some(size) = self.generic_size() {
246 write!(f, "; {size}")?;
247 }
248 write!(f, "]")?;
249 Ok(())
250 }
251}
252
253impl fmt::Display for DynTrait {
254 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
255 write!(
256 f,
257 "dyn {}",
258 self.traits.0.iter().map(|t| &t.value).join(" + ")
259 )
260 }
261}
262
263impl PtrType {
264 pub fn is_mutable(&self) -> bool {
265 matches!(self.pointer_type.second, Either::Second(Mut(_)))
266 }
267}
268impl RefType {
269 pub fn is_mutable(&self) -> bool {
270 !self.mutability.0.is_empty()
271 }
272}
273
274impl fmt::Display for Path {
275 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
276 write!(f, "{}", self.segments().join("::"))
277 }
278}
279
280impl fmt::Display for FnType {
281 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
282 write!(f, "fn")?;
283 if !self.args.content.0.is_empty() {
284 write!(
285 f,
286 "({})",
287 self.args.content.0.iter().map(|t| &t.value).join(", ")
288 )?;
289 } else {
290 write!(f, "()")?;
291 }
292 if let Some(ret) = &self.ret.0.first() {
293 write!(f, " -> {}", ret.value.ret)?;
294 }
295 Ok(())
296 }
297}
298
299impl fmt::Display for Type {
300 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
301 match self {
302 Type::Ref(r) => {
303 write!(f, "&")?;
304 if r.is_mutable() {
305 write!(f, "mut ")?;
306 }
307 write!(f, "{}", r.inner)
308 }
309 Type::Slice(s) => write!(f, "&[{}]", s.inner.content),
310 Type::StrSlice(_) => write!(f, "&str"),
311 Type::Array(a) => write!(f, "{a}"),
312 Type::DynTrait(d) => write!(f, "{d}"),
313 Type::Tuple(t) => {
314 let elements = t.inner();
315 if elements.len() == 1 {
316 write!(f, "({},)", elements[0])
317 } else {
318 write!(f, "({})", elements.iter().map(|e| e.to_string()).join(", "))
319 }
320 }
321 Type::Ptr(p) => write!(f, "{}{}", p.pointer_type.tokens_to_string(), p.inner),
322 Type::Path(p) => write!(f, "{p}"),
323 Type::Function(fn_type) => write!(f, "{fn_type}"),
324 Type::Never(_) => write!(f, "!"),
325 }
326 }
327}
328
329impl Path {
330 pub fn segments(&self) -> Vec<String> {
331 self.segments
332 .0
333 .iter()
334 .map(|path_segment| match &path_segment.value {
335 PathSegment::Segment(segment) => {
336 format!(
337 "{}{}",
338 segment.ident,
339 match segment.generics.0.first().as_ref().map(|g| &g.value) {
340 Some(GenericArgs::Parsed {
341 _lt: _,
342 inner,
343 _gt: _,
344 }) => {
345 format!(
346 "<{}>",
347 inner.0.iter().map(|d| d.value.to_string()).join(", ")
348 )
349 }
350 Some(GenericArgs::Unparsed(angle_token_tree)) => {
351 angle_token_tree.tokens_to_string()
352 }
353 None => String::new(),
354 }
355 )
356 }
357 p => p.tokens_to_string(),
358 })
359 .collect()
360 }
361}
362
363pub type ParsedSymbol = (Vec<String>, String, Option<String>);
364
365pub fn parse_symbol(s: &str) -> anyhow::Result<ParsedSymbol> {
383 let mut segments = Vec::with_capacity(4);
385 let mut current_segment = String::with_capacity(64);
386 let mut angle_depth = 0;
387 let mut chars = s.chars().peekable();
388
389 while let Some(ch) = chars.next() {
390 match ch {
391 '<' => {
392 angle_depth += 1;
393 current_segment.push(ch);
394 }
395 '>' => {
396 angle_depth -= 1;
397 current_segment.push(ch);
398 }
399 ':' if angle_depth == 0 && chars.peek() == Some(&':') => {
400 chars.next(); if !current_segment.is_empty() {
403 segments.push(current_segment.trim().to_string());
404 current_segment.clear();
405 }
406 }
407 '\n' | '\r' | '\t' | ' ' => {
408 if !current_segment.is_empty() && !current_segment.ends_with(' ') {
411 current_segment.push(' ');
412 }
413 }
414 _ => {
415 current_segment.push(ch);
416 }
417 }
418 }
419
420 if !current_segment.is_empty() {
422 segments.push(current_segment.trim().to_string());
423 }
424
425 if segments.is_empty() {
426 anyhow::bail!("Empty symbol path");
427 }
428
429 let hash = if let Some(last) = segments.last() {
431 if last.starts_with('h') && last.chars().skip(1).all(|c| c.is_ascii_hexdigit()) {
432 segments.pop()
433 } else {
434 None
435 }
436 } else {
437 None
438 };
439
440 let Some(function_name) = segments.pop() else {
441 anyhow::bail!("No function name found");
442 };
443
444 segments.shrink_to_fit();
445 let module_path = segments;
446
447 Ok((module_path, function_name, hash))
448}
449
450pub fn parse_type(s: &str) -> unsynn::Result<Type> {
451 let mut iter = s.to_token_iter();
452 let ty = Cons::<Type, EndOfStream>::parse(&mut iter)?;
453 Ok(ty.first)
454}
455
456impl Type {
457 pub fn as_typedef(&self) -> TypeLayout {
458 match self {
459 Type::Path(path) => {
460 path.as_typedef()
462 }
463 Type::Slice(slice) => {
464 let inner = slice.inner.content.clone().as_typedef();
465 TypeLayout::Primitive(PrimitiveLayout::Slice(SliceLayout {
466 element_type: Arc::new(inner),
467 data_ptr_offset: 0,
468 length_offset: 0,
469 }))
470 }
471 Type::StrSlice(_) => {
472 TypeLayout::Primitive(PrimitiveLayout::StrSlice(StrSliceLayout {
474 data_ptr_offset: 0,
475 length_offset: 0,
476 }))
477 }
478 Type::Ref(ref_type) => {
479 let inner = ref_type.inner.as_typedef();
480 TypeLayout::Primitive(PrimitiveLayout::Reference(ReferenceLayout {
481 mutable: ref_type.is_mutable(),
482 pointed_type: Arc::new(inner),
483 }))
484 }
485 Type::Ptr(ptr_type) => {
486 let inner = ptr_type.inner.as_typedef();
487 TypeLayout::Primitive(PrimitiveLayout::Pointer(PointerLayout {
488 mutable: ptr_type.is_mutable(),
489 pointed_type: Arc::new(inner),
490 }))
491 }
492 Type::Array(array) => {
493 let inner = array.inner().clone().as_typedef();
494 let length = if let Some(size_type) = array.concrete_size() {
495 size_type.to_string().parse::<usize>().unwrap_or(0)
498 } else {
499 0 };
501
502 TypeLayout::Primitive(PrimitiveLayout::Array(ArrayLayout {
503 element_type: Arc::new(inner),
504 length,
505 }))
506 }
507 Type::Tuple(tuple) => {
508 let elements: Vec<_> = tuple
509 .inner()
510 .iter()
511 .map(|t| (0, Arc::new(t.as_typedef())))
512 .collect();
513
514 if elements.is_empty() {
516 TypeLayout::Primitive(PrimitiveLayout::Unit(UnitLayout))
517 } else {
518 TypeLayout::Primitive(PrimitiveLayout::Tuple(TupleLayout {
519 elements,
520 size: 0, }))
522 }
523 }
524 Type::DynTrait(_dyn_trait) => {
525 TypeLayout::Other {
527 name: self.to_string(),
528 }
529 }
530 Type::Function(fn_type) => {
531 TypeLayout::Primitive(PrimitiveLayout::Function(FunctionLayout {
532 return_type: Arc::new(
533 fn_type
534 .ret()
535 .map_or(PrimitiveLayout::Unit(UnitLayout).into(), |r| r.as_typedef()),
536 ),
537 arg_types: fn_type
538 .args()
539 .iter()
540 .map(|a| Arc::new(a.as_typedef()))
541 .collect(),
542 }))
543 }
544 Type::Never(_) => {
545 TypeLayout::Primitive(PrimitiveLayout::Never(()))
547 }
548 }
549 }
550}
551
552impl Path {
553 fn as_typedef(&self) -> TypeLayout {
554 let segments = self.segments();
556 if segments.is_empty() {
557 return TypeLayout::Other {
558 name: String::new(),
559 };
560 }
561
562 let last_segment = segments.last().unwrap();
564
565 if segments.len() == 1 {
567 match last_segment.as_str() {
568 "u8" => {
569 return UnsignedIntLayout { size: 1 }.into();
570 }
571 "u16" => {
572 return UnsignedIntLayout { size: 2 }.into();
573 }
574 "u32" => {
575 return UnsignedIntLayout { size: 4 }.into();
576 }
577 "u64" => {
578 return UnsignedIntLayout { size: 8 }.into();
579 }
580 "u128" => {
581 return UnsignedIntLayout { size: 16 }.into();
582 }
583 "usize" => {
584 return UnsignedIntLayout {
585 size: std::mem::size_of::<usize>(),
586 }
587 .into();
588 }
589 "i8" => return IntLayout { size: 1 }.into(),
590 "i16" => return IntLayout { size: 2 }.into(),
591 "i32" => return IntLayout { size: 4 }.into(),
592 "i64" => return IntLayout { size: 8 }.into(),
593 "i128" => return IntLayout { size: 16 }.into(),
594 "isize" => {
595 return IntLayout {
596 size: std::mem::size_of::<isize>(),
597 }
598 .into();
599 }
600 "f32" => return FloatLayout { size: 4 }.into(),
601 "f64" => return FloatLayout { size: 8 }.into(),
602 "bool" => return TypeLayout::Primitive(PrimitiveLayout::Bool(())),
603 "char" => return TypeLayout::Primitive(PrimitiveLayout::Char(())),
604 "str" => return TypeLayout::Primitive(PrimitiveLayout::Str(())),
605 "()" => return TypeLayout::Primitive(PrimitiveLayout::Unit(UnitLayout)),
606 _ => {}
607 }
608 }
609
610 let is_std = segments[0] == "std" || segments[0] == "core" || segments[0] == "alloc";
612 let is_hashbrown = segments[0] == "hashbrown";
613
614 tracing::trace!("Parser segments: {:?}, is_std: {}", segments, is_std);
615
616 if is_std || is_hashbrown || segments.len() == 1 {
617 if let Some(path_segment) = self.segments.0.last() {
620 if let PathSegment::Segment(segment) = &path_segment.value {
621 let type_name = segment.ident.to_string();
622
623 let get_generics =
624 || {
625 segment.generics.0.first().map_or_else(
626 std::vec::Vec::new,
627 |generic_args| match &generic_args.value {
628 GenericArgs::Parsed { inner, .. } => {
629 inner.0.iter().map(|d| d.value.clone()).collect()
630 }
631 GenericArgs::Unparsed(_) => vec![],
632 },
633 )
634 };
635
636 tracing::trace!("Checking std type: '{}' against known types", type_name);
637
638 match type_name.as_str() {
639 "String" => {
640 tracing::trace!("Matched String type!");
641 return TypeLayout::Std(StdLayout::String(StringLayout(VecLayout {
642 length_offset: 0,
643 data_ptr_offset: 0,
644 inner_type: Arc::new(TypeLayout::Primitive(
645 PrimitiveLayout::UnsignedInt(UnsignedIntLayout { size: 1 }),
646 )),
647 })));
648 }
649 "Vec" => {
650 let inner = get_generics()
651 .first()
652 .map(|t| Arc::new(t.as_typedef()))
653 .unwrap_or_else(|| {
654 Arc::new(TypeLayout::Other {
655 name: "Unknown".to_string(),
656 })
657 });
658 return TypeLayout::Std(StdLayout::Vec(VecLayout {
659 inner_type: inner,
660 length_offset: 0,
661 data_ptr_offset: 0,
662 }));
663 }
664 "Option" => {
665 let inner = get_generics()
666 .first()
667 .map(|t| Arc::new(t.as_typedef()))
668 .unwrap_or_else(|| {
669 Arc::new(TypeLayout::Other {
670 name: "Unknown".to_string(),
671 })
672 });
673 return TypeLayout::Std(StdLayout::Option(OptionLayout {
674 name: "Option".to_string(),
675 discriminant: Discriminant {
676 offset: 0,
677 ty: DiscriminantType::Implicit,
678 },
679 some_offset: 0,
680 some_type: inner,
681 size: 0,
682 }));
683 }
684 "Result" => {
685 let mut generics_iter = get_generics().into_iter();
686 let ok_type = generics_iter
687 .next()
688 .map(|t| Arc::new(t.as_typedef()))
689 .unwrap_or_else(|| {
690 Arc::new(TypeLayout::Other {
691 name: "Unknown".to_string(),
692 })
693 });
694 let err_type = generics_iter
695 .next()
696 .map(|t| Arc::new(t.as_typedef()))
697 .unwrap_or_else(|| {
698 Arc::new(TypeLayout::Other {
699 name: "Unknown".to_string(),
700 })
701 });
702 return TypeLayout::Std(StdLayout::Result(ResultLayout {
703 name: "Result".to_string(),
704 discriminant: Discriminant {
705 offset: 0,
706 ty: DiscriminantType::Implicit,
707 },
708 ok_type,
709 ok_offset: 0,
710 err_type,
711 err_offset: 0,
712 size: 0,
713 }));
714 }
715 "HashMap" | "BTreeMap" => {
716 let mut generics_iter = get_generics().into_iter();
717 let key_type = generics_iter
718 .next()
719 .map(|t| Arc::new(t.as_typedef()))
720 .unwrap_or_else(|| {
721 Arc::new(TypeLayout::Other {
722 name: "Unknown".to_string(),
723 })
724 });
725 let value_type = generics_iter
726 .next()
727 .map(|t| Arc::new(t.as_typedef()))
728 .unwrap_or_else(|| {
729 Arc::new(TypeLayout::Other {
730 name: "Unknown".to_string(),
731 })
732 });
733 let variant = match type_name.as_str() {
734 "HashMap" => MapVariant::HashMap {
735 bucket_mask_offset: 0,
736 ctrl_offset: 0,
737 items_offset: 0,
738 pair_size: 0,
739 key_offset: 0,
740 value_offset: 0,
741 },
742 "BTreeMap" => MapVariant::BTreeMap {
743 length_offset: 0,
744 root_offset: 0,
745 root_layout: BTreeRootLayout {
746 node_offset: 0,
747 height_offset: 0,
748 },
749 node_layout: BTreeNodeLayout {
750 keys_offset: 0,
751 vals_offset: 0,
752 len_offset: 0,
753 edges_offset: 0,
754 },
755 },
756 _ => unreachable!(),
757 };
758 tracing::trace!("Matched Map type: '{type_name}'");
759 return TypeLayout::Std(StdLayout::Map(MapLayout {
760 key_type,
761 value_type,
762 variant,
763 }));
764 }
765 "Box" | "Rc" | "Arc" | "Cell" | "RefCell" | "UnsafeCell" | "Mutex"
766 | "RwLock" => {
767 let inner = get_generics()
768 .into_iter()
769 .next()
770 .map(|t| Arc::new(t.as_typedef()))
771 .unwrap_or_else(|| {
772 Arc::new(TypeLayout::Other {
773 name: "Unknown".to_string(),
774 })
775 });
776 let variant = match type_name.as_str() {
777 "Box" => SmartPtrVariant::Box,
778 "Rc" => SmartPtrVariant::Rc,
779 "Arc" => SmartPtrVariant::Arc,
780 "Cell" => SmartPtrVariant::Cell,
781 "RefCell" => SmartPtrVariant::RefCell,
782 "UnsafeCell" => SmartPtrVariant::UnsafeCell,
783 "Mutex" => SmartPtrVariant::Mutex,
784 "RwLock" => SmartPtrVariant::RwLock,
785 _ => unreachable!(),
786 };
787 return TypeLayout::Std(StdLayout::SmartPtr(SmartPtrLayout {
788 inner_type: inner,
789 inner_ptr_offset: 0,
790 data_ptr_offset: 0,
791 variant,
792 }));
793 }
794 _ => {}
795 }
796 }
797 }
798 }
799
800 TypeLayout::Other {
802 name: last_segment.clone(),
803 }
804 }
805}
806
807#[cfg(test)]
808mod test {
809 use std::sync::Arc;
810
811 use itertools::Itertools;
812 use pretty_assertions::assert_eq;
813 use rudy_types::*;
814
815 use super::*;
816
817 #[track_caller]
818 fn parse_symbol(s: &str) -> ParsedSymbol {
819 match super::parse_symbol(s) {
820 Ok(s) => s,
821 Err(e) => {
822 panic!(
823 "Failed to parse symbol `{s}`: {e}\nTokens:\n{}",
824 s.to_token_iter().map(|t| format!("{t:?}")).join("\n")
825 );
826 }
827 }
828 }
829
830 #[track_caller]
831 fn parse_type(s: &str) -> Type {
832 match super::parse_type(s) {
833 Ok(p) => p,
834 Err(e) => {
835 panic!(
836 "Failed to parse type `{s}`: {e}\nTokens:\n{}",
837 s.to_token_iter().map(|t| format!("{t:?}")).join("\n")
838 );
839 }
840 }
841 }
842
843 #[allow(unused)]
844 #[track_caller]
845 fn parse_arbitrary<T>(s: &str) -> T
846 where
847 T: Parse,
848 {
849 let mut iter = s.to_token_iter();
850 match Cons::<T, EndOfStream>::parse(&mut iter) {
851 Ok(t) => t.first,
852 Err(e) => {
853 panic!(
854 "Failed to parse `{s}` as {}: {e}\nTokens:\n{}",
855 std::any::type_name::<T>(),
856 s.to_token_iter().map(|t| format!("{t:?}")).join("\n")
857 );
858 }
859 }
860 }
861
862 #[test]
863 fn test_symbol_parsing() {
864 parse_symbol("u8");
865 let mut iter = "<impl foo as bar>".to_token_iter();
866 AngleTokenTree::parse(&mut iter).unwrap();
867 parse_symbol("NonZero");
870 parse_symbol("NonZero<u8>");
871 parse_symbol("core::num::nonzero::NonZero");
872 parse_symbol("core::num::nonzero::NonZero<u8>");
873 parse_symbol("core::num::nonzero::NonZero<u8>::ilog2::hc1106854ed63a858");
874 parse_symbol(
875 "drop_in_place<std::backtrace_rs::symbolize::gimli::parse_running_mmaps::MapsEntry>",
876 );
877 parse_symbol(
878 "alloc::ffi::c_str::<
879 impl
880 core::convert::From<
881 &core::ffi::c_str::CStr
882 >
883 for
884 alloc::boxed::Box<
885 core::ffi::c_str::CStr
886 >
887 >::from::hec874816052de6db",
888 );
889
890 assert_eq!(
891 parse_symbol(
892 "alloc::ffi::c_str::<
893 impl
894 core::convert::From<
895 &core::ffi::c_str::CStr
896 >
897 for
898 alloc::boxed::Box<
899 core::ffi::c_str::CStr
900 >
901 >::from::hec874816052de6db"
902 )
903 ,
904 (
905 vec![
906 "alloc".to_string(),
907 "ffi".to_string(),
908 "c_str".to_string(),
909 "< impl core::convert::From< &core::ffi::c_str::CStr > for alloc::boxed::Box< core::ffi::c_str::CStr > >".to_string(),
910 ],
911 "from".to_string(),
912 Some("hec874816052de6db".to_string())
913 )
914 );
915 parse_symbol("core::ops::function::FnOnce::call_once{{vtable.shim}}::h7689c9dccb951788");
916
917 parse_symbol("_Unwind_SetIP@GCC_3.0");
919 parse_symbol("__rustc[95feac21a9532783]::__rust_alloc_zeroed");
920 }
921
922 #[test]
923 fn test_type_parsing() {
924 parse_type("u8");
925 parse_type("&u8");
926 parse_type("dyn core::fmt::Debug");
927 parse_type("dyn core::fmt::Debug + core::fmt::Display");
928 parse_type("&mut dyn core::fmt::Write");
929 parse_type("&[core::fmt::rt::Argument]");
930 parse_type("<&alloc::string::String as core::fmt::Debug>::{vtable_type}");
931 parse_type("(usize, core::option::Option<usize>)");
932 parse_type("*const [i32]");
933 parse_type("&mut dyn core::ops::function::FnMut<(usize), Output=bool>");
934 parse_type("&&i32");
935 parse_type("!");
936 }
937
938 #[test]
939 fn test_type_printing() {
940 let s = "hashbrown::map::HashMap<alloc::string::String, i32, std::hash::random::RandomState, alloc::alloc::Global>";
941 assert_eq!(parse_type(s).to_string(), s.to_string());
942 }
943
944 #[track_caller]
945 fn infer<T: Into<TypeLayout> + fmt::Debug>(s: &str, expected: T) {
946 let ty = parse_type(s).as_typedef();
947 assert_eq!(ty, expected.into(), "Failed to parse type `{s}`");
948 }
949
950 fn string_def() -> TypeLayout {
951 TypeLayout::Std(StdLayout::String(StringLayout(VecLayout {
952 length_offset: 0,
953 data_ptr_offset: 0,
954 inner_type: Arc::new(TypeLayout::Primitive(PrimitiveLayout::UnsignedInt(
955 UnsignedIntLayout { size: 1 },
956 ))),
957 })))
958 }
959
960 #[test]
961 fn test_type_inference() {
962 let _ = tracing_subscriber::fmt()
963 .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
964 .try_init();
965
966 infer("u8", UnsignedIntLayout::u8());
967 infer("u32", UnsignedIntLayout::u32());
968 infer("()", PrimitiveLayout::from(UnitLayout));
969 infer(
970 "(u8,)",
971 PrimitiveLayout::Tuple(TupleLayout {
972 elements: vec![(0, Arc::new(UnsignedIntLayout::u8().into()))],
973 size: 0, }),
975 );
976 infer(
977 "(u8,u64)",
978 PrimitiveLayout::Tuple(TupleLayout {
979 elements: vec![
980 (0, Arc::new(UnsignedIntLayout::u8().into())),
981 (0, Arc::new(UnsignedIntLayout::u64().into())),
982 ],
983 size: 0, }),
985 );
986 infer(
987 "&u8",
988 ReferenceLayout::new_immutable(UnsignedIntLayout::u8()),
989 );
990 infer(
991 "&mut u8",
992 ReferenceLayout::new_mutable(UnsignedIntLayout::u8()),
993 );
994 infer(
995 "dyn core::fmt::Debug",
996 TypeLayout::Other {
997 name: "dyn core::fmt::Debug".to_string(),
998 },
999 );
1000 infer(
1001 "alloc::vec::Vec<u8>",
1002 VecLayout::new(UnsignedIntLayout::u8()),
1003 );
1004 infer(
1005 "alloc::vec::Vec<alloc::vec::Vec<u8>>",
1006 VecLayout::new(VecLayout::new(UnsignedIntLayout::u8())),
1007 );
1008 infer(
1009 "alloc::vec::Vec<u8, alloc::alloc::Global>",
1010 VecLayout::new(UnsignedIntLayout::u8()),
1011 );
1012 infer(
1013 "core::option::Option<i32>",
1014 StdLayout::Option(OptionLayout {
1015 name: "Option".to_string(),
1016 discriminant: Discriminant {
1017 offset: 0,
1018 ty: DiscriminantType::Implicit,
1019 },
1020 some_offset: 0,
1021 some_type: Arc::new(IntLayout::i32().into()),
1022 size: 0,
1023 }),
1024 );
1025 infer(
1026 "alloc::boxed::Box<i32>",
1027 SmartPtrLayout {
1028 inner_type: Arc::new(IntLayout::i32().into()),
1029 variant: SmartPtrVariant::Box,
1030 inner_ptr_offset: 0,
1031 data_ptr_offset: 0,
1032 },
1033 );
1034 infer("alloc::String::String", string_def());
1035 infer(
1036 "std::collections::hash::map::HashMap<alloc::string::String, alloc::string::String>",
1037 MapLayout {
1038 key_type: Arc::new(string_def()),
1039 value_type: Arc::new(string_def()),
1040 variant: MapVariant::HashMap {
1041 bucket_mask_offset: 0,
1042 ctrl_offset: 0,
1043 items_offset: 0,
1044 pair_size: 0,
1045 key_offset: 0,
1046 value_offset: 0,
1047 },
1048 },
1049 );
1050
1051 infer(
1052 "core::num::nonzero::NonZero<u8>",
1053 TypeLayout::Other {
1054 name: "NonZero<u8>".to_string(),
1055 },
1056 );
1057
1058 infer(
1059 "fn(&u64, &mut core::fmt::Formatter) -> core::result::Result<(), core::fmt::Error>",
1060 TypeLayout::Primitive(PrimitiveLayout::Function(FunctionLayout {
1061 arg_types: vec![
1062 Arc::new(ReferenceLayout::new_immutable(UnsignedIntLayout::u64()).into()),
1063 Arc::new(
1064 ReferenceLayout::new_mutable(TypeLayout::Other {
1065 name: "Formatter".to_string(),
1066 })
1067 .into(),
1068 ),
1069 ],
1070 return_type: Arc::new(
1071 StdLayout::Result(ResultLayout {
1072 name: "Result".to_string(),
1073 discriminant: Discriminant {
1074 offset: 0,
1075 ty: DiscriminantType::Implicit,
1076 },
1077 ok_type: Arc::new(TypeLayout::Primitive(PrimitiveLayout::Unit(UnitLayout))),
1078 ok_offset: 0,
1079 err_type: Arc::new(TypeLayout::Other {
1080 name: "Error".to_string(),
1081 }),
1082 err_offset: 0,
1083 size: 0,
1084 })
1085 .into(),
1086 ),
1087 })),
1088 );
1089 infer(
1090 "&[u8]",
1091 TypeLayout::Primitive(PrimitiveLayout::Slice(SliceLayout {
1092 element_type: Arc::new(UnsignedIntLayout::u8().into()),
1093 data_ptr_offset: 0,
1094 length_offset: 0,
1095 })),
1096 );
1097 infer(
1098 "&str",
1099 TypeLayout::Primitive(PrimitiveLayout::StrSlice(StrSliceLayout {
1100 data_ptr_offset: 0,
1101 length_offset: 0,
1102 })),
1103 )
1104 }
1105
1106 #[test]
1107 fn test_symbol_parsing_basic() {
1108 let (module_path, function_name, hash) = parse_symbol("core::num::ilog2::h12345");
1110 assert_eq!(module_path, vec!["core", "num"]);
1111 assert_eq!(function_name, "ilog2");
1112 assert_eq!(hash, Some("h12345".to_string()));
1113
1114 let (module_path, function_name, hash) =
1116 parse_symbol("core::num::nonzero::NonZero<u8>::ilog2::hc1106854ed63a858");
1117 assert_eq!(module_path, vec!["core", "num", "nonzero", "NonZero<u8>"]);
1118 assert_eq!(function_name, "ilog2");
1119 assert_eq!(hash, Some("hc1106854ed63a858".to_string()));
1120
1121 let (module_path, function_name, hash) =
1123 parse_symbol("std::collections::HashMap<String, i32>::insert");
1124 assert_eq!(
1125 module_path,
1126 vec!["std", "collections", "HashMap<String, i32>"]
1127 );
1128 assert_eq!(function_name, "insert");
1129 assert_eq!(hash, None);
1130
1131 let (module_path, function_name, hash) =
1133 parse_symbol("std::collections::HashMap<String, Vec<i32>>::get");
1134 assert_eq!(
1135 module_path,
1136 vec!["std", "collections", "HashMap<String, Vec<i32>>"]
1137 );
1138 assert_eq!(function_name, "get");
1139 assert_eq!(hash, None);
1140
1141 let (module_path, function_name, hash) = parse_symbol("main");
1143 assert_eq!(module_path, Vec::<String>::new());
1144 assert_eq!(function_name, "main");
1145 assert_eq!(hash, None);
1146
1147 let (module_path, function_name, hash) = parse_symbol("main::h123abc");
1149 assert_eq!(module_path, Vec::<String>::new());
1150 assert_eq!(function_name, "main");
1151 assert_eq!(hash, Some("h123abc".to_string()));
1152 }
1153
1154 #[test]
1155 fn test_symbol_parsing_complex_cases() {
1156 let (module_path, function_name, hash) = parse_symbol(
1158 "alloc::ffi::c_str::<impl core::convert::From<&core::ffi::c_str::CStr> for alloc::boxed::Box<core::ffi::c_str::CStr>>::from::hec874816052de6db",
1159 );
1160
1161 assert_eq!(
1162 module_path,
1163 vec![
1164 "alloc",
1165 "ffi",
1166 "c_str",
1167 "<impl core::convert::From<&core::ffi::c_str::CStr> for alloc::boxed::Box<core::ffi::c_str::CStr>>"
1168 ]
1169 );
1170 assert_eq!(function_name, "from");
1171 assert_eq!(hash, Some("hec874816052de6db".to_string()));
1172 }
1173
1174 #[test]
1175 fn test_symbol_parsing_errors() {
1176 assert!(super::parse_symbol("").is_err());
1178
1179 assert!(super::parse_symbol("h123abc").is_err());
1181 }
1182}