rudy_parser/
types.rs

1//! Type parsing using unsynn
2
3use std::fmt;
4
5use itertools::Itertools;
6use rudy_types::*;
7use unsynn::*;
8
9// Define some types
10unsynn! {
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    /// eats all tokens within two angle brackets
23    #[derive(Clone)]
24    pub struct AngleTokenTree {
25        _lt: Lt,
26        // inner can either be another nested AngleTokenTree, or
27        // arbitrary non-angled tokens
28        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        // fallback for cases we didn't handle above
40        // correctly
41        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        // for some weirdo cases like `core::ops::function::FnOnce::call_once{{vtable.shim}}`
54        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        // NOTE: the `,` here should not actually be optional, but it
120        // seems like rustc outputs these incorrectly
121        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        // note: for some reason it's better if path is last
171        Path(Path),
172    }
173
174    /// Symbol or binary paths as used in Dwarf information.
175    #[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, // Dynamic size is not concrete
191            }
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
365/// A simpler parsing approach for symbols
366///
367/// All we truly care about is splitting it into:
368///
369/// - the module path prefix
370/// - the type name
371/// - the hash (if present)
372///
373/// e.g. `core::num::nonzero::NonZero<u8>::ilog2::hc1106854ed63a858`
374/// would be parsed into:
375/// - `["core", "num", "nonzero", "NonZero<u8>"]`
376/// - `ilog2`
377/// - `Some("hc1106854ed63a858")`
378///
379/// We can do that without incurring the parsing overhead of the full
380/// `Path` and `Type` parsers, which are more complex and handle
381/// more cases than we need here.
382pub fn parse_symbol(s: &str) -> anyhow::Result<ParsedSymbol> {
383    // First, we need to split the string by `::` while respecting angle brackets
384    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                // We found `::` at the top level
401                chars.next(); // consume the second ':'
402                if !current_segment.is_empty() {
403                    segments.push(current_segment.trim().to_string());
404                    current_segment.clear();
405                }
406            }
407            '\n' | '\r' | '\t' | ' ' => {
408                // Ignore consecutive whitespace characters
409                // and replace with a single space character
410                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    // Don't forget the last segment
421    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    // Now we need to identify the hash, function name, and module path
430    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                // Convert path to typedef
461                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                // For str slices, we can just return a Str type
473                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                    // Try to extract numeric literal from the size
496                    // For now, we'll default to 0 if we can't parse it
497                    size_type.to_string().parse::<usize>().unwrap_or(0)
498                } else {
499                    0 // Unknown size
500                };
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                // 0-arity tuple is a unit
515                if elements.is_empty() {
516                    Layout::Primitive(PrimitiveLayout::Unit(UnitLayout))
517                } else {
518                    Layout::Primitive(PrimitiveLayout::Tuple(TupleLayout {
519                        elements,
520                        size: 0, // Would need to calculate from DWARF
521                    }))
522                }
523            }
524            Type::DynTrait(_dyn_trait) => {
525                // For trait objects, we'll use Other for now
526                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                // For the never type, we can just return a unit type
544                Layout::Primitive(PrimitiveLayout::Never(()))
545            }
546        }
547    }
548}
549
550impl Path {
551    fn as_typedef(&self) -> Layout {
552        // First, let's extract the segments
553        let segments = self.segments();
554        if segments.is_empty() {
555            return Layout::Alias {
556                name: String::new(),
557            };
558        }
559
560        // Get the last segment as the base type name
561        let last_segment = segments.last().unwrap();
562
563        // Check if this is a primitive type
564        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        // Check if this is a standard library type by examining the path
609        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            // Parse the last segment for generic types
616            // (we're guaranteed to have at least one segment here)
617            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        // Default case: treat as a custom type (struct/enum) or alias
827        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        // let mut iter = "NonZero<u8>".to_token_iter();
892        // Cons::<Ident, BracketGroupContaining<Path>>::parse(&mut iter).unwrap();
893        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        // other cases
942        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, // Would need to calculate from DWARF
1003            }),
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, // Would need to calculate from DWARF
1013            }),
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        // Test basic function without generics
1152        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        // Test function with generics in module path
1158        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        // Test function without hash
1165        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        // Test nested generics
1175        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        // Test single segment (just function name)
1185        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        // Test single segment with hash
1191        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        // Test from the original parser tests
1200        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        // Test empty string
1220        assert!(super::parse_symbol("").is_err());
1221
1222        // Test only hash
1223        assert!(super::parse_symbol("h123abc").is_err());
1224    }
1225}