rudy_parser/
types.rs

1//! Type parsing using unsynn
2
3use std::{fmt, sync::Arc};
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_typedef(&self) -> TypeLayout {
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_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                // For str slices, we can just return a Str type
473                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                    // 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                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                // 0-arity tuple is a unit
515                if elements.is_empty() {
516                    TypeLayout::Primitive(PrimitiveLayout::Unit(UnitLayout))
517                } else {
518                    TypeLayout::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                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                // For the never type, we can just return a unit type
546                TypeLayout::Primitive(PrimitiveLayout::Never(()))
547            }
548        }
549    }
550}
551
552impl Path {
553    fn as_typedef(&self) -> TypeLayout {
554        // First, let's extract the segments
555        let segments = self.segments();
556        if segments.is_empty() {
557            return TypeLayout::Other {
558                name: String::new(),
559            };
560        }
561
562        // Get the last segment as the base type name
563        let last_segment = segments.last().unwrap();
564
565        // Check if this is a primitive type
566        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        // Check if this is a standard library type by examining the path
611        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            // Parse the last segment for generic types
618            // (we're guaranteed to have at least one segment here)
619            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        // Default case: treat as a custom type (struct/enum) or alias
801        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        // let mut iter = "NonZero<u8>".to_token_iter();
868        // Cons::<Ident, BracketGroupContaining<Path>>::parse(&mut iter).unwrap();
869        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        // other cases
918        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, // Would need to calculate from DWARF
974            }),
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, // Would need to calculate from DWARF
984            }),
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        // Test basic function without generics
1109        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        // Test function with generics in module path
1115        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        // Test function without hash
1122        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        // Test nested generics
1132        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        // Test single segment (just function name)
1142        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        // Test single segment with hash
1148        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        // Test from the original parser tests
1157        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        // Test empty string
1177        assert!(super::parse_symbol("").is_err());
1178
1179        // Test only hash
1180        assert!(super::parse_symbol("h123abc").is_err());
1181    }
1182}