1use std::fmt;
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_layout(&self) -> Layout {
458 match self {
459 Type::Path(path) => {
460 path.as_typedef()
462 }
463 Type::Slice(slice) => {
464 let inner = slice.inner.content.clone().as_layout();
465 Layout::Primitive(PrimitiveLayout::Slice(SliceLayout {
466 element_type: TypeDefinition::new((), inner),
467 data_ptr_offset: 0,
468 length_offset: 0,
469 }))
470 }
471 Type::StrSlice(_) => {
472 Layout::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_layout();
480 Layout::Primitive(PrimitiveLayout::Reference(ReferenceLayout {
481 mutable: ref_type.is_mutable(),
482 pointed_type: TypeDefinition::new((), inner),
483 }))
484 }
485 Type::Ptr(ptr_type) => {
486 let inner = ptr_type.inner.as_layout();
487 Layout::Primitive(PrimitiveLayout::Pointer(PointerLayout {
488 mutable: ptr_type.is_mutable(),
489 pointed_type: TypeDefinition::new((), inner),
490 }))
491 }
492 Type::Array(array) => {
493 let inner = array.inner().clone().as_layout();
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 Layout::Primitive(PrimitiveLayout::Array(ArrayLayout {
503 element_type: TypeDefinition::new((), inner),
504 length,
505 }))
506 }
507 Type::Tuple(tuple) => {
508 let elements: Vec<_> = tuple
509 .inner()
510 .iter()
511 .map(|t| (0, TypeDefinition::new((), t.as_layout())))
512 .collect();
513
514 if elements.is_empty() {
516 Layout::Primitive(PrimitiveLayout::Unit(UnitLayout))
517 } else {
518 Layout::Primitive(PrimitiveLayout::Tuple(TupleLayout {
519 elements,
520 size: 0, }))
522 }
523 }
524 Type::DynTrait(_dyn_trait) => {
525 Layout::Alias {
527 name: self.to_string(),
528 }
529 }
530 Type::Function(fn_type) => {
531 Layout::Primitive(PrimitiveLayout::Function(FunctionLayout {
532 return_type: fn_type
533 .ret()
534 .map(|l| TypeDefinition::new((), l.as_layout())),
535 arg_types: fn_type
536 .args()
537 .iter()
538 .map(|a| TypeDefinition::new((), a.as_layout()))
539 .collect(),
540 }))
541 }
542 Type::Never(_) => {
543 Layout::Primitive(PrimitiveLayout::Never(()))
545 }
546 }
547 }
548}
549
550impl Path {
551 fn as_typedef(&self) -> Layout {
552 let segments = self.segments();
554 if segments.is_empty() {
555 return Layout::Alias {
556 name: String::new(),
557 };
558 }
559
560 let last_segment = segments.last().unwrap();
562
563 if segments.len() == 1 {
565 match last_segment.as_str() {
566 "u8" => {
567 return UnsignedIntLayout { size: 1 }.into();
568 }
569 "u16" => {
570 return UnsignedIntLayout { size: 2 }.into();
571 }
572 "u32" => {
573 return UnsignedIntLayout { size: 4 }.into();
574 }
575 "u64" => {
576 return UnsignedIntLayout { size: 8 }.into();
577 }
578 "u128" => {
579 return UnsignedIntLayout { size: 16 }.into();
580 }
581 "usize" => {
582 return UnsignedIntLayout {
583 size: std::mem::size_of::<usize>(),
584 }
585 .into();
586 }
587 "i8" => return IntLayout { size: 1 }.into(),
588 "i16" => return IntLayout { size: 2 }.into(),
589 "i32" => return IntLayout { size: 4 }.into(),
590 "i64" => return IntLayout { size: 8 }.into(),
591 "i128" => return IntLayout { size: 16 }.into(),
592 "isize" => {
593 return IntLayout {
594 size: std::mem::size_of::<isize>(),
595 }
596 .into();
597 }
598 "f32" => return FloatLayout { size: 4 }.into(),
599 "f64" => return FloatLayout { size: 8 }.into(),
600 "bool" => return Layout::Primitive(PrimitiveLayout::Bool(())),
601 "char" => return Layout::Primitive(PrimitiveLayout::Char(())),
602 "str" => return Layout::Primitive(PrimitiveLayout::Str(())),
603 "()" => return Layout::Primitive(PrimitiveLayout::Unit(UnitLayout)),
604 _ => {}
605 }
606 }
607
608 let is_std = segments[0] == "std" || segments[0] == "core" || segments[0] == "alloc";
610 let is_hashbrown = segments[0] == "hashbrown";
611
612 tracing::trace!("Parser segments: {:?}, is_std: {}", segments, is_std);
613
614 if is_std || is_hashbrown || segments.len() == 1 {
615 if let Some(path_segment) = self.segments.0.last()
618 && let PathSegment::Segment(segment) = &path_segment.value
619 {
620 let type_name = segment.ident.to_string();
621
622 let get_generics = || {
623 segment
624 .generics
625 .0
626 .first()
627 .map_or_else(std::vec::Vec::new, |generic_args| {
628 match &generic_args.value {
629 GenericArgs::Parsed { inner, .. } => {
630 inner.0.iter().map(|d| d.value.clone()).collect()
631 }
632 GenericArgs::Unparsed(_) => vec![],
633 }
634 })
635 };
636
637 tracing::trace!("Checking std type: '{}' against known types", type_name);
638
639 match type_name.as_str() {
640 "String" => {
641 tracing::trace!("Matched String type!");
642 return Layout::Std(StdLayout::String(StringLayout(VecLayout {
643 length_offset: 0,
644 data_ptr_offset: 0,
645 capacity_offset: 0,
646 inner_type: TypeDefinition::new(
647 (),
648 Layout::Primitive(PrimitiveLayout::UnsignedInt(
649 UnsignedIntLayout { size: 1 },
650 )),
651 ),
652 })));
653 }
654 "Vec" => {
655 let inner = get_generics()
656 .first()
657 .map(|t| TypeDefinition::new((), t.as_layout()))
658 .unwrap_or_else(|| {
659 TypeDefinition::new(
660 (),
661 Layout::Alias {
662 name: "Unknown".to_string(),
663 },
664 )
665 });
666 return Layout::Std(StdLayout::Vec(VecLayout {
667 inner_type: inner,
668 length_offset: 0,
669 data_ptr_offset: 0,
670 capacity_offset: 0,
671 }));
672 }
673 "Option" => {
674 let inner = get_generics()
675 .first()
676 .map(|t| TypeDefinition::new((), t.as_layout()))
677 .unwrap_or_else(|| {
678 TypeDefinition::new(
679 (),
680 Layout::Alias {
681 name: "Unknown".to_string(),
682 },
683 )
684 });
685 return Layout::Std(StdLayout::Option(OptionLayout {
686 name: "Option".to_string(),
687 discriminant: Discriminant {
688 offset: 0,
689 ty: DiscriminantType::Implicit,
690 },
691 some_offset: 0,
692 some_type: inner,
693 size: 0,
694 }));
695 }
696 "Result" => {
697 let mut generics_iter = get_generics().into_iter();
698 let ok_type = generics_iter
699 .next()
700 .map(|t| TypeDefinition::new((), t.as_layout()))
701 .unwrap_or_else(|| {
702 TypeDefinition::new(
703 (),
704 Layout::Alias {
705 name: "Unknown".to_string(),
706 },
707 )
708 });
709 let err_type = generics_iter
710 .next()
711 .map(|t| TypeDefinition::new((), t.as_layout()))
712 .unwrap_or_else(|| {
713 TypeDefinition::new(
714 (),
715 Layout::Alias {
716 name: "Unknown".to_string(),
717 },
718 )
719 });
720 return Layout::Std(StdLayout::Result(ResultLayout {
721 name: "Result".to_string(),
722 discriminant: Discriminant {
723 offset: 0,
724 ty: DiscriminantType::Implicit,
725 },
726 ok_type,
727 ok_offset: 0,
728 err_type,
729 err_offset: 0,
730 size: 0,
731 }));
732 }
733 "HashMap" | "BTreeMap" => {
734 let mut generics_iter = get_generics().into_iter();
735 let key_type = generics_iter
736 .next()
737 .map(|t| TypeDefinition::new((), t.as_layout()))
738 .unwrap_or_else(|| {
739 TypeDefinition::new(
740 (),
741 Layout::Alias {
742 name: "Unknown".to_string(),
743 },
744 )
745 });
746 let value_type = generics_iter
747 .next()
748 .map(|t| TypeDefinition::new((), t.as_layout()))
749 .unwrap_or_else(|| {
750 TypeDefinition::new(
751 (),
752 Layout::Alias {
753 name: "Unknown".to_string(),
754 },
755 )
756 });
757 let variant = match type_name.as_str() {
758 "HashMap" => MapVariant::HashMap {
759 bucket_mask_offset: 0,
760 ctrl_offset: 0,
761 items_offset: 0,
762 pair_size: 0,
763 key_offset: 0,
764 value_offset: 0,
765 },
766 "BTreeMap" => MapVariant::BTreeMap {
767 length_offset: 0,
768 root_offset: 0,
769 root_layout: BTreeRootLayout {
770 node_offset: 0,
771 height_offset: 0,
772 },
773 node_layout: BTreeNodeLayout {
774 keys_offset: 0,
775 vals_offset: 0,
776 len_offset: 0,
777 edges_offset: 0,
778 },
779 },
780 _ => unreachable!(),
781 };
782 tracing::trace!("Matched Map type: '{type_name}'");
783 return Layout::Std(StdLayout::Map(MapLayout {
784 key_type,
785 value_type,
786 variant,
787 }));
788 }
789 "Box" | "Rc" | "Arc" | "Cell" | "RefCell" | "UnsafeCell" | "Mutex"
790 | "RwLock" => {
791 let inner = get_generics()
792 .into_iter()
793 .next()
794 .map(|t| TypeDefinition::new((), t.as_layout()))
795 .unwrap_or_else(|| {
796 TypeDefinition::new(
797 (),
798 Layout::Alias {
799 name: "Unknown".to_string(),
800 },
801 )
802 });
803 let variant = match type_name.as_str() {
804 "Box" => SmartPtrVariant::Box,
805 "Rc" => SmartPtrVariant::Rc,
806 "Arc" => SmartPtrVariant::Arc,
807 "Cell" => SmartPtrVariant::Cell,
808 "RefCell" => SmartPtrVariant::RefCell,
809 "UnsafeCell" => SmartPtrVariant::UnsafeCell,
810 "Mutex" => SmartPtrVariant::Mutex,
811 "RwLock" => SmartPtrVariant::RwLock,
812 _ => unreachable!(),
813 };
814 return Layout::Std(StdLayout::SmartPtr(SmartPtrLayout {
815 inner_type: inner,
816 inner_ptr_offset: 0,
817 data_ptr_offset: 0,
818 variant,
819 }));
820 }
821 _ => {}
822 }
823 }
824 }
825
826 Layout::Alias {
828 name: last_segment.clone(),
829 }
830 }
831}
832
833#[cfg(test)]
834mod test {
835 use itertools::Itertools;
836 use pretty_assertions::assert_eq;
837 use rudy_types::*;
838
839 use super::*;
840
841 #[track_caller]
842 fn parse_symbol(s: &str) -> ParsedSymbol {
843 match super::parse_symbol(s) {
844 Ok(s) => s,
845 Err(e) => {
846 panic!(
847 "Failed to parse symbol `{s}`: {e}\nTokens:\n{}",
848 s.to_token_iter().map(|t| format!("{t:?}")).join("\n")
849 );
850 }
851 }
852 }
853
854 #[track_caller]
855 fn parse_type(s: &str) -> Type {
856 match super::parse_type(s) {
857 Ok(p) => p,
858 Err(e) => {
859 panic!(
860 "Failed to parse type `{s}`: {e}\nTokens:\n{}",
861 s.to_token_iter().map(|t| format!("{t:?}")).join("\n")
862 );
863 }
864 }
865 }
866
867 #[allow(unused)]
868 #[track_caller]
869 fn parse_arbitrary<T>(s: &str) -> T
870 where
871 T: Parse,
872 {
873 let mut iter = s.to_token_iter();
874 match Cons::<T, EndOfStream>::parse(&mut iter) {
875 Ok(t) => t.first,
876 Err(e) => {
877 panic!(
878 "Failed to parse `{s}` as {}: {e}\nTokens:\n{}",
879 std::any::type_name::<T>(),
880 s.to_token_iter().map(|t| format!("{t:?}")).join("\n")
881 );
882 }
883 }
884 }
885
886 #[test]
887 fn test_symbol_parsing() {
888 parse_symbol("u8");
889 let mut iter = "<impl foo as bar>".to_token_iter();
890 AngleTokenTree::parse(&mut iter).unwrap();
891 parse_symbol("NonZero");
894 parse_symbol("NonZero<u8>");
895 parse_symbol("core::num::nonzero::NonZero");
896 parse_symbol("core::num::nonzero::NonZero<u8>");
897 parse_symbol("core::num::nonzero::NonZero<u8>::ilog2::hc1106854ed63a858");
898 parse_symbol(
899 "drop_in_place<std::backtrace_rs::symbolize::gimli::parse_running_mmaps::MapsEntry>",
900 );
901 parse_symbol(
902 "alloc::ffi::c_str::<
903 impl
904 core::convert::From<
905 &core::ffi::c_str::CStr
906 >
907 for
908 alloc::boxed::Box<
909 core::ffi::c_str::CStr
910 >
911 >::from::hec874816052de6db",
912 );
913
914 assert_eq!(
915 parse_symbol(
916 "alloc::ffi::c_str::<
917 impl
918 core::convert::From<
919 &core::ffi::c_str::CStr
920 >
921 for
922 alloc::boxed::Box<
923 core::ffi::c_str::CStr
924 >
925 >::from::hec874816052de6db"
926 )
927 ,
928 (
929 vec![
930 "alloc".to_string(),
931 "ffi".to_string(),
932 "c_str".to_string(),
933 "< impl core::convert::From< &core::ffi::c_str::CStr > for alloc::boxed::Box< core::ffi::c_str::CStr > >".to_string(),
934 ],
935 "from".to_string(),
936 Some("hec874816052de6db".to_string())
937 )
938 );
939 parse_symbol("core::ops::function::FnOnce::call_once{{vtable.shim}}::h7689c9dccb951788");
940
941 parse_symbol("_Unwind_SetIP@GCC_3.0");
943 parse_symbol("__rustc[95feac21a9532783]::__rust_alloc_zeroed");
944 }
945
946 #[test]
947 fn test_type_parsing() {
948 parse_type("u8");
949 parse_type("&u8");
950 parse_type("dyn core::fmt::Debug");
951 parse_type("dyn core::fmt::Debug + core::fmt::Display");
952 parse_type("&mut dyn core::fmt::Write");
953 parse_type("&[core::fmt::rt::Argument]");
954 parse_type("<&alloc::string::String as core::fmt::Debug>::{vtable_type}");
955 parse_type("(usize, core::option::Option<usize>)");
956 parse_type("*const [i32]");
957 parse_type("&mut dyn core::ops::function::FnMut<(usize), Output=bool>");
958 parse_type("&&i32");
959 parse_type("!");
960 }
961
962 #[test]
963 fn test_type_printing() {
964 let s = "hashbrown::map::HashMap<alloc::string::String, i32, std::hash::random::RandomState, alloc::alloc::Global>";
965 assert_eq!(parse_type(s).to_string(), s.to_string());
966 }
967
968 #[track_caller]
969 fn infer<T: Into<Layout> + fmt::Debug>(s: &str, expected: T) {
970 let ty = parse_type(s).as_layout();
971 assert_eq!(ty, expected.into(), "Failed to parse type `{s}`");
972 }
973
974 fn string_def() -> TypeDefinition {
975 TypeDefinition::new(
976 (),
977 Layout::Std(StdLayout::String(StringLayout(VecLayout {
978 length_offset: 0,
979 data_ptr_offset: 0,
980 capacity_offset: 0,
981 inner_type: TypeDefinition::new(
982 (),
983 Layout::Primitive(PrimitiveLayout::UnsignedInt(UnsignedIntLayout { size: 1 })),
984 ),
985 }))),
986 )
987 }
988
989 #[test]
990 fn test_type_inference() {
991 let _ = tracing_subscriber::fmt()
992 .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
993 .try_init();
994
995 infer("u8", UnsignedIntLayout::u8());
996 infer("u32", UnsignedIntLayout::u32());
997 infer("()", PrimitiveLayout::from(UnitLayout));
998 infer(
999 "(u8,)",
1000 PrimitiveLayout::Tuple(TupleLayout {
1001 elements: vec![(0, TypeDefinition::new((), UnsignedIntLayout::u8().into()))],
1002 size: 0, }),
1004 );
1005 infer(
1006 "(u8,u64)",
1007 PrimitiveLayout::Tuple(TupleLayout {
1008 elements: vec![
1009 (0, TypeDefinition::new((), UnsignedIntLayout::u8().into())),
1010 (0, TypeDefinition::new((), UnsignedIntLayout::u64().into())),
1011 ],
1012 size: 0, }),
1014 );
1015 infer(
1016 "&u8",
1017 ReferenceLayout::new_immutable(UnsignedIntLayout::u8()),
1018 );
1019 infer(
1020 "&mut u8",
1021 ReferenceLayout::new_mutable(UnsignedIntLayout::u8()),
1022 );
1023 infer(
1024 "dyn core::fmt::Debug",
1025 Layout::Alias {
1026 name: "dyn core::fmt::Debug".to_string(),
1027 },
1028 );
1029 infer(
1030 "alloc::vec::Vec<u8>",
1031 VecLayout::new(UnsignedIntLayout::u8()),
1032 );
1033 infer(
1034 "alloc::vec::Vec<alloc::vec::Vec<u8>>",
1035 VecLayout::new(VecLayout::new(UnsignedIntLayout::u8())),
1036 );
1037 infer(
1038 "alloc::vec::Vec<u8, alloc::alloc::Global>",
1039 VecLayout::new(UnsignedIntLayout::u8()),
1040 );
1041 infer(
1042 "core::option::Option<i32>",
1043 StdLayout::Option(OptionLayout {
1044 name: "Option".to_string(),
1045 discriminant: Discriminant {
1046 offset: 0,
1047 ty: DiscriminantType::Implicit,
1048 },
1049 some_offset: 0,
1050 some_type: TypeDefinition::new((), IntLayout::i32().into()),
1051 size: 0,
1052 }),
1053 );
1054 infer(
1055 "alloc::boxed::Box<i32>",
1056 SmartPtrLayout {
1057 inner_type: TypeDefinition::new((), IntLayout::i32().into()),
1058 variant: SmartPtrVariant::Box,
1059 inner_ptr_offset: 0,
1060 data_ptr_offset: 0,
1061 },
1062 );
1063 infer(
1064 "alloc::String::String",
1065 string_def().layout.as_ref().clone(),
1066 );
1067 infer(
1068 "std::collections::hash::map::HashMap<alloc::string::String, alloc::string::String>",
1069 MapLayout {
1070 key_type: string_def(),
1071 value_type: string_def(),
1072 variant: MapVariant::HashMap {
1073 bucket_mask_offset: 0,
1074 ctrl_offset: 0,
1075 items_offset: 0,
1076 pair_size: 0,
1077 key_offset: 0,
1078 value_offset: 0,
1079 },
1080 },
1081 );
1082
1083 infer(
1084 "core::num::nonzero::NonZero<u8>",
1085 Layout::Alias {
1086 name: "NonZero<u8>".to_string(),
1087 },
1088 );
1089
1090 infer(
1091 "fn(&u64, &mut core::fmt::Formatter) -> core::result::Result<(), core::fmt::Error>",
1092 Layout::Primitive(PrimitiveLayout::Function(FunctionLayout {
1093 arg_types: vec![
1094 TypeDefinition::new(
1095 (),
1096 ReferenceLayout::new_immutable(UnsignedIntLayout::u64()).into(),
1097 ),
1098 TypeDefinition::new(
1099 (),
1100 ReferenceLayout::new_mutable(Layout::Alias {
1101 name: "Formatter".to_string(),
1102 })
1103 .into(),
1104 ),
1105 ],
1106 return_type: Some(TypeDefinition::new(
1107 (),
1108 StdLayout::Result(ResultLayout {
1109 name: "Result".to_string(),
1110 discriminant: Discriminant {
1111 offset: 0,
1112 ty: DiscriminantType::Implicit,
1113 },
1114 ok_type: TypeDefinition::new(
1115 (),
1116 Layout::Primitive(PrimitiveLayout::Unit(UnitLayout)),
1117 ),
1118 ok_offset: 0,
1119 err_type: TypeDefinition::new(
1120 (),
1121 Layout::Alias {
1122 name: "Error".to_string(),
1123 },
1124 ),
1125 err_offset: 0,
1126 size: 0,
1127 })
1128 .into(),
1129 )),
1130 })),
1131 );
1132 infer(
1133 "&[u8]",
1134 Layout::Primitive(PrimitiveLayout::Slice(SliceLayout {
1135 element_type: TypeDefinition::new((), UnsignedIntLayout::u8().into()),
1136 data_ptr_offset: 0,
1137 length_offset: 0,
1138 })),
1139 );
1140 infer(
1141 "&str",
1142 Layout::Primitive(PrimitiveLayout::StrSlice(StrSliceLayout {
1143 data_ptr_offset: 0,
1144 length_offset: 0,
1145 })),
1146 )
1147 }
1148
1149 #[test]
1150 fn test_symbol_parsing_basic() {
1151 let (module_path, function_name, hash) = parse_symbol("core::num::ilog2::h12345");
1153 assert_eq!(module_path, vec!["core", "num"]);
1154 assert_eq!(function_name, "ilog2");
1155 assert_eq!(hash, Some("h12345".to_string()));
1156
1157 let (module_path, function_name, hash) =
1159 parse_symbol("core::num::nonzero::NonZero<u8>::ilog2::hc1106854ed63a858");
1160 assert_eq!(module_path, vec!["core", "num", "nonzero", "NonZero<u8>"]);
1161 assert_eq!(function_name, "ilog2");
1162 assert_eq!(hash, Some("hc1106854ed63a858".to_string()));
1163
1164 let (module_path, function_name, hash) =
1166 parse_symbol("std::collections::HashMap<String, i32>::insert");
1167 assert_eq!(
1168 module_path,
1169 vec!["std", "collections", "HashMap<String, i32>"]
1170 );
1171 assert_eq!(function_name, "insert");
1172 assert_eq!(hash, None);
1173
1174 let (module_path, function_name, hash) =
1176 parse_symbol("std::collections::HashMap<String, Vec<i32>>::get");
1177 assert_eq!(
1178 module_path,
1179 vec!["std", "collections", "HashMap<String, Vec<i32>>"]
1180 );
1181 assert_eq!(function_name, "get");
1182 assert_eq!(hash, None);
1183
1184 let (module_path, function_name, hash) = parse_symbol("main");
1186 assert_eq!(module_path, Vec::<String>::new());
1187 assert_eq!(function_name, "main");
1188 assert_eq!(hash, None);
1189
1190 let (module_path, function_name, hash) = parse_symbol("main::h123abc");
1192 assert_eq!(module_path, Vec::<String>::new());
1193 assert_eq!(function_name, "main");
1194 assert_eq!(hash, Some("h123abc".to_string()));
1195 }
1196
1197 #[test]
1198 fn test_symbol_parsing_complex_cases() {
1199 let (module_path, function_name, hash) = parse_symbol(
1201 "alloc::ffi::c_str::<impl core::convert::From<&core::ffi::c_str::CStr> for alloc::boxed::Box<core::ffi::c_str::CStr>>::from::hec874816052de6db",
1202 );
1203
1204 assert_eq!(
1205 module_path,
1206 vec![
1207 "alloc",
1208 "ffi",
1209 "c_str",
1210 "<impl core::convert::From<&core::ffi::c_str::CStr> for alloc::boxed::Box<core::ffi::c_str::CStr>>"
1211 ]
1212 );
1213 assert_eq!(function_name, "from");
1214 assert_eq!(hash, Some("hec874816052de6db".to_string()));
1215 }
1216
1217 #[test]
1218 fn test_symbol_parsing_errors() {
1219 assert!(super::parse_symbol("").is_err());
1221
1222 assert!(super::parse_symbol("h123abc").is_err());
1224 }
1225}