Skip to main content

pdb_addr2line/
type_formatter.rs

1use crate::error::Error;
2use bitflags::bitflags;
3use pdb::{
4    ArgumentList, ArrayType, ClassKind, ClassType, CrossModuleExports, CrossModuleImports,
5    CrossModuleRef, DebugInformation, FallibleIterator, FunctionAttributes, IdData, IdIndex,
6    IdInformation, Item, ItemFinder, ItemIndex, ItemIter, MachineType, MemberFunctionType,
7    ModifierType, Module, ModuleInfo, PointerMode, PointerType, PrimitiveKind, PrimitiveType,
8    ProcedureType, RawString, StringTable, TypeData, TypeIndex, TypeInformation, UnionType,
9    Variant,
10};
11use range_collections::range_set::RangeSetRange;
12use range_collections::{RangeSet, RangeSet2};
13use std::cmp::Ordering;
14use std::collections::HashMap;
15use std::fmt::Write;
16use std::mem;
17use std::sync::Mutex;
18
19type Result<V> = std::result::Result<V, Error>;
20
21bitflags! {
22    /// Flags for [`TypeFormatter`].
23    #[derive(Clone, Copy)]
24    pub struct TypeFormatterFlags: u32 {
25        /// Do not print the return type for the root function.
26        const NO_FUNCTION_RETURN = 0b1;
27
28        /// Do not print static before the signature of a static method.
29        const NO_MEMBER_FUNCTION_STATIC = 0b10;
30
31        /// Add a space after each comma in an argument list.
32        const SPACE_AFTER_COMMA = 0b100;
33
34        /// Add a space before the * or & sigil of a pointer or reference.
35        const SPACE_BEFORE_POINTER = 0b1000;
36
37        /// Only print "MyClassName" instead of "class MyClassName", "struct MyClassName", or "interface MyClassName".
38        const NAME_ONLY = 0b10000;
39
40        /// Do not print a functions argument types.
41        const NO_ARGUMENTS = 0b100000;
42    }
43}
44
45impl Default for TypeFormatterFlags {
46    fn default() -> Self {
47        Self::NO_FUNCTION_RETURN
48            | Self::NO_MEMBER_FUNCTION_STATIC
49            | Self::SPACE_AFTER_COMMA
50            | Self::NAME_ONLY
51    }
52}
53
54/// This trait is only needed for consumers who want to call Context::new_from_parts
55/// or TypeFormatter::new_from_parts manually, instead of using ContextPdbData. If you
56/// use ContextPdbData you do not need to worry about this trait.
57/// This trait allows Context and TypeFormatter to request parsing of module info
58/// on-demand. It also does some lifetime acrobatics so that Context can cache objects
59/// which have a lifetime dependency on the module info.
60pub trait ModuleProvider<'s> {
61    /// Get the module info for this module from the PDB.
62    fn get_module_info(
63        &self,
64        module_index: usize,
65        module: &Module,
66    ) -> std::result::Result<Option<&ModuleInfo<'s>>, pdb::Error>;
67}
68
69/// Allows printing function signatures, for example for use in stack traces.
70///
71/// Procedure symbols in PDBs usually have a name string which only includes the function name,
72/// and no function arguments. Instead, the arguments need to be obtained from the symbol's type
73/// information. [`TypeFormatter`] handles that.
74///
75/// The same is true for "inlinee" functions - these are referenced by their [`pdb::IdIndex`], and their
76/// [`IdData`]'s name string again only contains the raw function name but no arguments and also
77/// no namespace or class name. [`TypeFormatter`] handles those, too, in [`TypeFormatter::format_id`].
78// Lifetimes:
79// 'a: Lifetime of the thing that owns the various streams, e.g. ContextPdbData.
80// 's: The PDB Source lifetime.
81pub struct TypeFormatter<'a, 's> {
82    module_provider: &'a (dyn ModuleProvider<'s> + Sync),
83    modules: Vec<Module<'a>>,
84    string_table: Option<&'a StringTable<'s>>,
85    cache: Mutex<TypeFormatterCache<'a>>,
86    ptr_size: u64,
87    flags: TypeFormatterFlags,
88}
89
90struct TypeFormatterCache<'a> {
91    type_map: TypeMap<'a>,
92    type_size_cache: TypeSizeCache<'a>,
93    id_map: IdMap<'a>,
94    /// lower case module_name() -> module_index
95    module_name_map: Option<HashMap<String, usize>>,
96    module_imports: HashMap<usize, Result<CrossModuleImports<'a>>>,
97    module_exports: HashMap<usize, Result<CrossModuleExports>>,
98}
99
100// 'a: Lifetime of the thing that owns the various streams.
101// 's: The PDB Source lifetime.
102// 'cache: Lifetime of the exclusive reference to the TypeFormatterCache, outlived by
103//         the reference to the TypeFormatter.
104struct TypeFormatterForModule<'cache, 'a, 's> {
105    module_index: usize,
106    module_provider: &'a (dyn ModuleProvider<'s> + Sync),
107    modules: &'cache [Module<'a>],
108    string_table: Option<&'a StringTable<'s>>,
109    cache: &'cache mut TypeFormatterCache<'a>,
110    ptr_size: u64,
111    flags: TypeFormatterFlags,
112}
113
114impl<'a, 's> TypeFormatter<'a, 's> {
115    /// Create a [`TypeFormatter`] manually. Most consumers will want to use
116    /// [`ContextPdbData::make_type_formatter`] instead.
117    ///
118    /// However, if you interact with a PDB directly and parse some of its contents
119    /// for other uses, you may want to call this method in order to avoid overhead
120    /// from repeatedly parsing the same streams.
121    pub fn new_from_parts(
122        module_provider: &'a (dyn ModuleProvider<'s> + Sync),
123        modules: Vec<Module<'a>>,
124        debug_info: &DebugInformation<'s>,
125        type_info: &'a TypeInformation<'s>,
126        id_info: &'a IdInformation<'s>,
127        string_table: Option<&'a StringTable<'s>>,
128        flags: TypeFormatterFlags,
129    ) -> std::result::Result<Self, pdb::Error> {
130        let type_map = TypeMap {
131            iter: type_info.iter(),
132            finder: type_info.finder(),
133        };
134        let type_size_cache = TypeSizeCache {
135            forward_ref_sizes: HashMap::new(),
136            cached_ranges: RangeSet::empty(),
137        };
138
139        let id_map = IdMap {
140            iter: id_info.iter(),
141            finder: id_info.finder(),
142        };
143
144        let ptr_size = match debug_info.machine_type()? {
145            MachineType::Amd64 | MachineType::Arm64 | MachineType::Ia64 | MachineType::RiscV64 => 8,
146            MachineType::RiscV128 => 16,
147            _ => 4,
148        };
149
150        Ok(Self {
151            module_provider,
152            modules,
153            string_table,
154            cache: Mutex::new(TypeFormatterCache {
155                type_map,
156                type_size_cache,
157                id_map,
158                module_name_map: None,
159                module_imports: HashMap::new(),
160                module_exports: HashMap::new(),
161            }),
162            ptr_size,
163            flags,
164        })
165    }
166
167    /// A reference to the `Module` list that is owned by the type formatter.
168    pub fn modules(&self) -> &[Module<'a>] {
169        &self.modules
170    }
171
172    fn for_module<F, R>(&self, module_index: usize, f: F) -> R
173    where
174        F: FnOnce(&mut TypeFormatterForModule<'_, 'a, 's>) -> R,
175    {
176        let mut cache = self.cache.lock().unwrap();
177        let mut for_module = TypeFormatterForModule {
178            module_index,
179            module_provider: self.module_provider,
180            modules: &self.modules,
181            string_table: self.string_table,
182            cache: &mut cache,
183            ptr_size: self.ptr_size,
184            flags: self.flags,
185        };
186        f(&mut for_module)
187    }
188
189    /// Get the size, in bytes, of the type at `index`.
190    pub fn get_type_size(&self, module_index: usize, index: TypeIndex) -> u64 {
191        self.for_module(module_index, |tf| tf.get_type_size(index))
192    }
193
194    /// Return a string with the function or method signature, including return type (if
195    /// requested), namespace and/or class qualifiers, and arguments.
196    /// If the TypeIndex is 0, then only the raw name is emitted. In that case, the
197    /// name may need to go through additional demangling / "undecorating", but this
198    /// is the responsibility of the caller.
199    /// This method is used for [`ProcedureSymbol`s](pdb::ProcedureSymbol).
200    /// The module_index is the index of the module in which this procedure was found. It
201    /// is necessary in order to properly resolve cross-module references.
202    pub fn format_function(
203        &self,
204        name: &str,
205        module_index: usize,
206        function_type_index: TypeIndex,
207    ) -> Result<String> {
208        let mut s = String::new();
209        self.emit_function(&mut s, name, module_index, function_type_index)?;
210        Ok(s)
211    }
212
213    /// Write out the function or method signature, including return type (if requested),
214    /// namespace and/or class qualifiers, and arguments.
215    /// If the TypeIndex is 0, then only the raw name is emitted. In that case, the
216    /// name may need to go through additional demangling / "undecorating", but this
217    /// is the responsibility of the caller.
218    /// This method is used for [`ProcedureSymbol`s](pdb::ProcedureSymbol).
219    /// The module_index is the index of the module in which this procedure was found. It
220    /// is necessary in order to properly resolve cross-module references.
221    pub fn emit_function(
222        &self,
223        w: &mut impl Write,
224        name: &str,
225        module_index: usize,
226        function_type_index: TypeIndex,
227    ) -> Result<()> {
228        self.for_module(module_index, |tf| {
229            tf.emit_function(w, name, function_type_index)
230        })
231    }
232
233    /// Return a string with the function or method signature, including return type (if
234    /// requested), namespace and/or class qualifiers, and arguments.
235    /// This method is used for inlined functions.
236    /// The module_index is the index of the module in which this IdIndex was found. It
237    /// is necessary in order to properly resolve cross-module references.
238    pub fn format_id(&self, module_index: usize, id_index: IdIndex) -> Result<String> {
239        let mut s = String::new();
240        self.emit_id(&mut s, module_index, id_index)?;
241        Ok(s)
242    }
243
244    /// Write out the function or method signature, including return type (if requested),
245    /// namespace and/or class qualifiers, and arguments.
246    /// This method is used for inlined functions.
247    /// The module_index is the index of the module in which this IdIndex was found. It
248    /// is necessary in order to properly resolve cross-module references.
249    pub fn emit_id(
250        &self,
251        w: &mut impl Write,
252        module_index: usize,
253        id_index: IdIndex,
254    ) -> Result<()> {
255        self.for_module(module_index, |tf| tf.emit_id(w, id_index))
256    }
257}
258
259impl<'a, 's> TypeFormatterForModule<'_, 'a, 's> {
260    /// Get the size, in bytes, of the type at `index`.
261    pub fn get_type_size(&mut self, index: TypeIndex) -> u64 {
262        if let Ok(type_data) = self.parse_type_index(index) {
263            self.get_data_size(index, &type_data)
264        } else {
265            0
266        }
267    }
268    /// Write out the function or method signature, including return type (if requested),
269    /// namespace and/or class qualifiers, and arguments.
270    /// If the TypeIndex is 0, then only the raw name is emitted. In that case, the
271    /// name may need to go through additional demangling / "undecorating", but this
272    /// is the responsibility of the caller.
273    /// This method is used for [`ProcedureSymbol`s](pdb::ProcedureSymbol).
274    pub fn emit_function(
275        &mut self,
276        w: &mut impl Write,
277        name: &str,
278        function_type_index: TypeIndex,
279    ) -> Result<()> {
280        if function_type_index == TypeIndex(0) {
281            return self.emit_name_str(w, name);
282        }
283
284        match self.parse_type_index(function_type_index)? {
285            TypeData::MemberFunction(t) => {
286                if t.this_pointer_type.is_none() {
287                    self.maybe_emit_static(w)?;
288                }
289                self.maybe_emit_return_type(w, Some(t.return_type), t.attributes)?;
290                self.emit_name_str(w, name)?;
291                self.emit_method_args(w, t, true)?;
292            }
293            TypeData::Procedure(t) => {
294                self.maybe_emit_return_type(w, t.return_type, t.attributes)?;
295                self.emit_name_str(w, name)?;
296
297                if !self.has_flags(TypeFormatterFlags::NO_ARGUMENTS) {
298                    write!(w, "(")?;
299                    self.emit_type_index(w, t.argument_list)?;
300                    write!(w, ")")?;
301                }
302            }
303            _ => {
304                write!(w, "{}", name)?;
305            }
306        }
307        Ok(())
308    }
309
310    /// Write out the function or method signature, including return type (if requested),
311    /// namespace and/or class qualifiers, and arguments.
312    /// This method is used for inlined functions.
313    pub fn emit_id(&mut self, w: &mut impl Write, id_index: IdIndex) -> Result<()> {
314        let id_data = match self.parse_id_index(id_index) {
315            Ok(id_data) => id_data,
316            Err(Error::PdbError(pdb::Error::UnimplementedTypeKind(t))) => {
317                write!(w, "<unimplemented type kind 0x{:x}>", t)?;
318                return Ok(());
319            }
320            Err(Error::PdbError(pdb::Error::TypeNotFound(type_index))) => {
321                write!(w, "<missing type 0x{:x}>", type_index)?;
322                return Ok(());
323            }
324            Err(e) => return Err(e),
325        };
326        match id_data {
327            IdData::MemberFunction(m) => {
328                let t = match self.parse_type_index(m.function_type)? {
329                    TypeData::MemberFunction(t) => t,
330                    _ => return Err(Error::MemberFunctionIdIsNotMemberFunctionType),
331                };
332
333                if t.this_pointer_type.is_none() {
334                    self.maybe_emit_static(w)?;
335                }
336                self.maybe_emit_return_type(w, Some(t.return_type), t.attributes)?;
337                self.emit_type_index(w, m.parent)?;
338                write!(w, "::")?;
339                self.emit_name_str(w, &m.name.to_string())?;
340                self.emit_method_args(w, t, true)?;
341            }
342            IdData::Function(f) => {
343                let t = match self.parse_type_index(f.function_type)? {
344                    TypeData::Procedure(t) => t,
345                    _ => return Err(Error::FunctionIdIsNotProcedureType),
346                };
347
348                self.maybe_emit_return_type(w, t.return_type, t.attributes)?;
349                if let Some(scope) = f.scope {
350                    self.emit_id(w, scope)?;
351                    write!(w, "::")?;
352                }
353
354                self.emit_name_str(w, &f.name.to_string())?;
355
356                if !self.has_flags(TypeFormatterFlags::NO_ARGUMENTS) {
357                    write!(w, "(")?;
358                    self.emit_type_index(w, t.argument_list)?;
359                    write!(w, ")")?;
360                }
361            }
362            IdData::String(s) => {
363                let name = s.name.to_string();
364
365                if Self::is_anonymous_namespace(&name) {
366                    write!(w, "`anonymous namespace'")?;
367                } else {
368                    write!(w, "{}", name)?;
369                }
370            }
371            IdData::StringList(s) => {
372                write!(w, "\"")?;
373                for (i, type_index) in s.substrings.iter().enumerate() {
374                    if i > 0 {
375                        write!(w, "\" \"")?;
376                    }
377                    self.emit_type_index(w, *type_index)?;
378                }
379                write!(w, "\"")?;
380            }
381            other => write!(w, "<unhandled id scope {:?}>::", other)?,
382        }
383        Ok(())
384    }
385
386    /// Checks whether the given name declares an anonymous namespace.
387    ///
388    /// ID records specify the mangled format for anonymous namespaces: `?A0x<id>`, where `id` is a hex
389    /// identifier of the namespace. Demanglers usually resolve this as "anonymous namespace".
390    fn is_anonymous_namespace(name: &str) -> bool {
391        name.strip_prefix("?A0x")
392            .is_some_and(|rest| u32::from_str_radix(rest, 16).is_ok())
393    }
394
395    fn resolve_index<I>(&mut self, index: I) -> Result<I>
396    where
397        I: ItemIndex,
398    {
399        if !index.is_cross_module() {
400            return Ok(index);
401        }
402
403        // We have a cross-module reference.
404        // First, we prepare some information which we will need below.
405
406        let string_table = self
407            .string_table
408            .ok_or(Error::CantResolveCrossModuleRefWithoutStringTable)?;
409
410        let TypeFormatterCache {
411            module_name_map,
412            module_imports,
413            module_exports,
414            ..
415        } = self.cache;
416        let modules = self.modules;
417        let module_provider = self.module_provider;
418        let self_module_index = self.module_index;
419
420        let get_module = |module_index: usize| -> Result<&'a ModuleInfo<'s>> {
421            let module = modules
422                .get(module_index)
423                .ok_or(Error::OutOfRangeModuleIndex(module_index))?;
424            let module_info = module_provider
425                .get_module_info(module_index, module)?
426                .ok_or(Error::ModuleInfoNotFound(module_index))?;
427            Ok(module_info)
428        };
429
430        let module_name_map = module_name_map.get_or_insert_with(|| {
431            modules
432                .iter()
433                .enumerate()
434                .map(|(module_index, module)| {
435                    let name = module.module_name().to_ascii_lowercase();
436                    (name, module_index)
437                })
438                .collect()
439        });
440
441        // Now we follow the steps outlined in the comment for is_cross_module.
442
443        //  1. Look up the index in [`CrossModuleImports`](crate::CrossModuleImports) of the current
444        //     module.
445        let imports = module_imports
446            .entry(self_module_index)
447            .or_insert_with(|| Ok(get_module(self_module_index)?.imports()?))
448            .as_mut()
449            .map_err(|err| mem::replace(err, Error::ModuleImportsUnsuccessful))?;
450
451        let CrossModuleRef(module_ref, local_index) = imports.resolve_import(index)?;
452
453        //  2. Use [`StringTable`](crate::StringTable) to resolve the name of the referenced module.
454        let ref_module_name = module_ref
455            .0
456            .to_string_lossy(string_table)?
457            .to_ascii_lowercase();
458
459        //  3. Find the [`Module`](crate::Module) with the same module name and load its
460        //     [`ModuleInfo`](crate::ModuleInfo).
461        let ref_module_index = *module_name_map
462            .get(&ref_module_name)
463            .ok_or(Error::ModuleNameNotFound(ref_module_name))?;
464
465        let module_exports = module_exports
466            .entry(ref_module_index)
467            .or_insert_with(|| Ok(get_module(ref_module_index)?.exports()?))
468            .as_mut()
469            .map_err(|err| mem::replace(err, Error::ModuleExportsUnsuccessful))?;
470
471        //  4. Resolve the [`Local`](crate::Local) index into a global one using
472        //     [`CrossModuleExports`](crate::CrossModuleExports).
473        let index = module_exports
474            .resolve_import(local_index)?
475            .ok_or_else(|| Error::LocalIndexNotInExports(local_index.0.into()))?;
476
477        Ok(index)
478    }
479
480    fn parse_type_index(&mut self, index: TypeIndex) -> Result<TypeData<'a>> {
481        let index = self.resolve_index(index)?;
482        let item = self.cache.type_map.try_get(index)?;
483        Ok(item.parse()?)
484    }
485
486    fn parse_id_index(&mut self, index: IdIndex) -> Result<IdData<'a>> {
487        let index = self.resolve_index(index)?;
488        let item = self.cache.id_map.try_get(index)?;
489        Ok(item.parse()?)
490    }
491
492    fn get_class_size(&mut self, index: TypeIndex, class_type: &ClassType<'a>) -> u64 {
493        if class_type.properties.forward_reference() {
494            let name = class_type.unique_name.unwrap_or(class_type.name);
495            let size = self.cache.type_size_cache.get_size_for_forward_reference(
496                index,
497                name,
498                &mut self.cache.type_map,
499            );
500
501            // Sometimes the name will not be in self.forward_ref_sizes - this can occur for
502            // the empty struct, which can be a forward reference to itself!
503            size.unwrap_or(class_type.size)
504        } else {
505            class_type.size
506        }
507    }
508
509    fn get_union_size(&mut self, index: TypeIndex, union_type: &UnionType<'a>) -> u64 {
510        if union_type.properties.forward_reference() {
511            let name = union_type.unique_name.unwrap_or(union_type.name);
512            let size = self.cache.type_size_cache.get_size_for_forward_reference(
513                index,
514                name,
515                &mut self.cache.type_map,
516            );
517
518            size.unwrap_or(union_type.size)
519        } else {
520            union_type.size
521        }
522    }
523
524    fn get_data_size(&mut self, type_index: TypeIndex, type_data: &TypeData<'a>) -> u64 {
525        match type_data {
526            TypeData::Primitive(t) => {
527                if t.indirection.is_some() {
528                    return self.ptr_size;
529                }
530                match t.kind {
531                    PrimitiveKind::NoType | PrimitiveKind::Void => 0,
532                    PrimitiveKind::Char
533                    | PrimitiveKind::UChar
534                    | PrimitiveKind::RChar
535                    | PrimitiveKind::Char8
536                    | PrimitiveKind::I8
537                    | PrimitiveKind::U8
538                    | PrimitiveKind::Bool8 => 1,
539                    PrimitiveKind::WChar
540                    | PrimitiveKind::RChar16
541                    | PrimitiveKind::Short
542                    | PrimitiveKind::UShort
543                    | PrimitiveKind::I16
544                    | PrimitiveKind::U16
545                    | PrimitiveKind::F16
546                    | PrimitiveKind::Bool16 => 2,
547                    PrimitiveKind::RChar32
548                    | PrimitiveKind::Long
549                    | PrimitiveKind::ULong
550                    | PrimitiveKind::I32
551                    | PrimitiveKind::U32
552                    | PrimitiveKind::F32
553                    | PrimitiveKind::F32PP
554                    | PrimitiveKind::Bool32
555                    | PrimitiveKind::HRESULT => 4,
556                    PrimitiveKind::I64
557                    | PrimitiveKind::U64
558                    | PrimitiveKind::Quad
559                    | PrimitiveKind::UQuad
560                    | PrimitiveKind::F64
561                    | PrimitiveKind::Complex32
562                    | PrimitiveKind::Bool64 => 8,
563                    PrimitiveKind::I128
564                    | PrimitiveKind::U128
565                    | PrimitiveKind::Octa
566                    | PrimitiveKind::UOcta
567                    | PrimitiveKind::F128
568                    | PrimitiveKind::Complex64 => 16,
569                    PrimitiveKind::F48 => 6,
570                    PrimitiveKind::F80 => 10,
571                    PrimitiveKind::Complex80 => 20,
572                    PrimitiveKind::Complex128 => 32,
573                    _ => panic!("Unknown PrimitiveKind {:?} in get_data_size", t.kind),
574                }
575            }
576            TypeData::Class(t) => self.get_class_size(type_index, t),
577            TypeData::MemberFunction(_) => self.ptr_size,
578            TypeData::Procedure(_) => self.ptr_size,
579            TypeData::Pointer(t) => t.attributes.size().into(),
580            TypeData::Array(t) => (*t.dimensions.last().unwrap()).into(),
581            TypeData::Union(t) => self.get_union_size(type_index, t),
582            TypeData::Enumeration(t) => self.get_type_size(t.underlying_type),
583            TypeData::Enumerate(t) => match t.value {
584                Variant::I8(_) | Variant::U8(_) => 1,
585                Variant::I16(_) | Variant::U16(_) => 2,
586                Variant::I32(_) | Variant::U32(_) => 4,
587                Variant::I64(_) | Variant::U64(_) => 8,
588            },
589            TypeData::Modifier(t) => self.get_type_size(t.underlying_type),
590            _ => 0,
591        }
592    }
593
594    fn has_flags(&self, flags: TypeFormatterFlags) -> bool {
595        self.flags.intersects(flags)
596    }
597
598    fn maybe_emit_static(&self, w: &mut impl Write) -> Result<()> {
599        if self.has_flags(TypeFormatterFlags::NO_MEMBER_FUNCTION_STATIC) {
600            return Ok(());
601        }
602
603        w.write_str("static ")?;
604        Ok(())
605    }
606
607    fn maybe_emit_return_type(
608        &mut self,
609        w: &mut impl Write,
610        type_index: Option<TypeIndex>,
611        attrs: FunctionAttributes,
612    ) -> Result<()> {
613        if self.has_flags(TypeFormatterFlags::NO_FUNCTION_RETURN) {
614            return Ok(());
615        }
616
617        self.emit_return_type(w, type_index, attrs)?;
618        Ok(())
619    }
620
621    fn emit_name_str(&mut self, w: &mut impl Write, name: &str) -> Result<()> {
622        if name.is_empty() {
623            write!(w, "<name omitted>")?;
624        } else {
625            write!(w, "{}", name)?;
626        }
627        Ok(())
628    }
629
630    fn emit_return_type(
631        &mut self,
632        w: &mut impl Write,
633        type_index: Option<TypeIndex>,
634        attrs: FunctionAttributes,
635    ) -> Result<()> {
636        if !attrs.is_constructor() {
637            if let Some(index) = type_index {
638                self.emit_type_index(w, index)?;
639                write!(w, " ")?;
640            }
641        }
642        Ok(())
643    }
644
645    /// Check if ptr points to the specified class, and if so, whether it points to const or non-const class.
646    /// If it points to a different class than the one supplied in the `class` argument, don'a check constness.
647    fn check_ptr_class(&mut self, ptr: TypeIndex, class: TypeIndex) -> Result<PtrToClassKind> {
648        if let TypeData::Pointer(ptr_type) = self.parse_type_index(ptr)? {
649            let underlying_type = ptr_type.underlying_type;
650            if underlying_type == class {
651                return Ok(PtrToClassKind::PtrToGivenClass { constant: false });
652            }
653            let underlying_type_data = self.parse_type_index(underlying_type)?;
654            if let TypeData::Modifier(modifier) = underlying_type_data {
655                if modifier.underlying_type == class {
656                    return Ok(PtrToClassKind::PtrToGivenClass {
657                        constant: modifier.constant,
658                    });
659                }
660            }
661        };
662        Ok(PtrToClassKind::OtherType)
663    }
664
665    /// Return value: (this is pointer to const class, optional extra first argument)
666    fn get_class_constness_and_extra_arguments(
667        &mut self,
668        this: TypeIndex,
669        class: TypeIndex,
670    ) -> Result<(bool, Option<TypeIndex>)> {
671        match self.check_ptr_class(this, class)? {
672            PtrToClassKind::PtrToGivenClass { constant } => {
673                // The this type looks normal. Don'a return an extra argument.
674                Ok((constant, None))
675            }
676            PtrToClassKind::OtherType => {
677                // The type of the "this" pointer did not match the class type.
678                // This is arguably bad type information.
679                // It looks like this bad type information is emitted for all Rust "associated
680                // functions" whose first argument is a reference. Associated functions don'a
681                // take a self argument, so it would make sense to treat them as static.
682                // But instead, these functions are marked as non-static, and the first argument's
683                // type, rather than being part of the arguments list, is stored in the "this" type.
684                // For example, for ProfileScope::new(name: &'static CStr), the arguments list is
685                // empty and the this type is CStr*.
686                // To work around this, return the this type as an extra first argument.
687                Ok((false, Some(this)))
688            }
689        }
690    }
691
692    fn emit_method_args(
693        &mut self,
694        w: &mut impl Write,
695        method_type: MemberFunctionType,
696        allow_emit_const: bool,
697    ) -> Result<()> {
698        if self.has_flags(TypeFormatterFlags::NO_ARGUMENTS) {
699            return Ok(());
700        }
701
702        let args_list = match self.parse_type_index(method_type.argument_list)? {
703            TypeData::ArgumentList(t) => t,
704            _ => {
705                return Err(Error::ArgumentTypeNotArgumentList);
706            }
707        };
708
709        let (is_const_method, extra_first_arg) = match method_type.this_pointer_type {
710            None => {
711                // No this pointer - this is a static method.
712                // Static methods cannot be const, and they have the correct arguments.
713                (false, None)
714            }
715            Some(this_type) => {
716                // For non-static methods, check whether the method is const, and work around a
717                // problem with bad type information for Rust associated functions.
718                self.get_class_constness_and_extra_arguments(this_type, method_type.class_type)?
719            }
720        };
721
722        write!(w, "(")?;
723        if let Some(first_arg) = extra_first_arg {
724            self.emit_type_index(w, first_arg)?;
725            self.emit_arg_list(w, args_list, true)?;
726        } else {
727            self.emit_arg_list(w, args_list, false)?;
728        }
729        write!(w, ")")?;
730
731        if is_const_method && allow_emit_const {
732            write!(w, " const")?;
733        }
734
735        Ok(())
736    }
737
738    // Should we emit a space as the first byte from emit_attributes? It depends.
739    // "*" in a table cell means "value has no impact on the outcome".
740    //
741    //  caller allows space | attributes start with | SPACE_BEFORE_POINTER mode | previous byte was   | put space at the beginning?
742    // ---------------------+-----------------------+---------------------------+---------------------+----------------------------
743    //  no                  | *                     | *                         | *                   | no
744    //  yes                 | const                 | *                         | *                   | yes
745    //  yes                 | pointer sigil         | off                       | *                   | no
746    //  yes                 | pointer sigil         | on                        | pointer sigil       | no
747    //  yes                 | pointer sigil         | on                        | not a pointer sigil | yes
748    fn emit_attributes(
749        &mut self,
750        w: &mut impl Write,
751        attrs: Vec<PtrAttributes>,
752        allow_space_at_beginning: bool,
753        mut previous_byte_was_pointer_sigil: bool,
754    ) -> Result<()> {
755        let mut is_at_beginning = true;
756        for attr in attrs.iter().rev() {
757            if attr.is_pointee_const {
758                if !is_at_beginning || allow_space_at_beginning {
759                    write!(w, " ")?;
760                }
761                write!(w, "const")?;
762                is_at_beginning = false;
763                previous_byte_was_pointer_sigil = false;
764            }
765
766            if self.has_flags(TypeFormatterFlags::SPACE_BEFORE_POINTER)
767                && !previous_byte_was_pointer_sigil
768                && (!is_at_beginning || allow_space_at_beginning)
769            {
770                write!(w, " ")?;
771            }
772            is_at_beginning = false;
773            match attr.mode {
774                PointerMode::Pointer => write!(w, "*")?,
775                PointerMode::LValueReference => write!(w, "&")?,
776                PointerMode::Member => write!(w, "::*")?,
777                PointerMode::MemberFunction => write!(w, "::*")?,
778                PointerMode::RValueReference => write!(w, "&&")?,
779            }
780            previous_byte_was_pointer_sigil = true;
781            if attr.is_pointer_const {
782                write!(w, " const")?;
783                previous_byte_was_pointer_sigil = false;
784            }
785        }
786        Ok(())
787    }
788
789    fn emit_member_ptr(
790        &mut self,
791        w: &mut impl Write,
792        fun: MemberFunctionType,
793        attributes: Vec<PtrAttributes>,
794    ) -> Result<()> {
795        self.emit_return_type(w, Some(fun.return_type), fun.attributes)?;
796        write!(w, "(")?;
797        self.emit_type_index(w, fun.class_type)?;
798        self.emit_attributes(w, attributes, false, false)?;
799        write!(w, ")")?;
800        self.emit_method_args(w, fun, false)?;
801        Ok(())
802    }
803
804    fn emit_proc_ptr(
805        &mut self,
806        w: &mut impl Write,
807        fun: ProcedureType,
808        attributes: Vec<PtrAttributes>,
809    ) -> Result<()> {
810        self.emit_return_type(w, fun.return_type, fun.attributes)?;
811
812        write!(w, "(")?;
813        self.emit_attributes(w, attributes, false, false)?;
814        write!(w, ")")?;
815        write!(w, "(")?;
816        self.emit_type_index(w, fun.argument_list)?;
817        write!(w, ")")?;
818        Ok(())
819    }
820
821    fn emit_other_ptr(
822        &mut self,
823        w: &mut impl Write,
824        type_data: TypeData,
825        attributes: Vec<PtrAttributes>,
826    ) -> Result<()> {
827        let mut buf = String::new();
828        self.emit_type(&mut buf, type_data)?;
829        let previous_byte_was_pointer_sigil = buf
830            .as_bytes()
831            .last()
832            .map(|&b| b == b'*' || b == b'&')
833            .unwrap_or(false);
834        w.write_str(&buf)?;
835        self.emit_attributes(w, attributes, true, previous_byte_was_pointer_sigil)?;
836
837        Ok(())
838    }
839
840    fn emit_ptr_helper(
841        &mut self,
842        w: &mut impl Write,
843        attributes: Vec<PtrAttributes>,
844        type_data: TypeData,
845    ) -> Result<()> {
846        match type_data {
847            TypeData::MemberFunction(t) => self.emit_member_ptr(w, t, attributes)?,
848            TypeData::Procedure(t) => self.emit_proc_ptr(w, t, attributes)?,
849            _ => self.emit_other_ptr(w, type_data, attributes)?,
850        };
851        Ok(())
852    }
853
854    fn emit_ptr(&mut self, w: &mut impl Write, ptr: PointerType, is_const: bool) -> Result<()> {
855        let mut attributes = vec![PtrAttributes {
856            is_pointer_const: ptr.attributes.is_const() || is_const,
857            is_pointee_const: false,
858            mode: ptr.attributes.pointer_mode(),
859        }];
860        let mut ptr = ptr;
861        loop {
862            let type_data = self.parse_type_index(ptr.underlying_type)?;
863            match type_data {
864                TypeData::Pointer(t) => {
865                    attributes.push(PtrAttributes {
866                        is_pointer_const: t.attributes.is_const(),
867                        is_pointee_const: false,
868                        mode: t.attributes.pointer_mode(),
869                    });
870                    ptr = t;
871                }
872                TypeData::Modifier(t) => {
873                    // the vec cannot be empty since we push something in just before the loop
874                    attributes.last_mut().unwrap().is_pointee_const = t.constant;
875                    let underlying_type_data = self.parse_type_index(t.underlying_type)?;
876                    if let TypeData::Pointer(t) = underlying_type_data {
877                        attributes.push(PtrAttributes {
878                            is_pointer_const: t.attributes.is_const(),
879                            is_pointee_const: false,
880                            mode: t.attributes.pointer_mode(),
881                        });
882                        ptr = t;
883                    } else {
884                        self.emit_ptr_helper(w, attributes, underlying_type_data)?;
885                        return Ok(());
886                    }
887                }
888                _ => {
889                    self.emit_ptr_helper(w, attributes, type_data)?;
890                    return Ok(());
891                }
892            }
893        }
894    }
895
896    /// The returned Vec has the array dimensions in bytes, with the "lower" dimensions
897    /// aggregated into the "higher" dimensions.
898    fn get_array_info(&mut self, array: ArrayType) -> Result<(Vec<u64>, TypeIndex, TypeData<'a>)> {
899        // For an array int[12][34] it'll be represented as "int[34] *".
900        // For any reason the 12 is lost...
901        // The internal representation is: Pointer{ base: Array{ base: int, dim: 34 * sizeof(int)} }
902        let mut base = array;
903        let mut dims = Vec::new();
904        dims.push(base.dimensions[0].into());
905
906        // See the documentation for ArrayType::dimensions:
907        //
908        // > Contains array dimensions as specified in the PDB. This is not what you expect:
909        // >
910        // > * Dimensions are specified in terms of byte sizes, not element counts.
911        // > * Multidimensional arrays aggregate the lower dimensions into the sizes of the higher
912        // >   dimensions.
913        // >
914        // > Thus a `float[4][4]` has `dimensions: [16, 64]`. Determining array dimensions in terms
915        // > of element counts requires determining the size of the `element_type` and iteratively
916        // > dividing.
917        //
918        // XXXmstange the docs above imply that dimensions can have more than just one entry.
919        // But this code only processes dimensions[0]. Is that a bug?
920        loop {
921            let type_index = base.element_type;
922            let type_data = self.parse_type_index(type_index)?;
923            match type_data {
924                TypeData::Array(a) => {
925                    dims.push(a.dimensions[0].into());
926                    base = a;
927                }
928                _ => {
929                    return Ok((dims, type_index, type_data));
930                }
931            }
932        }
933    }
934
935    fn emit_array(&mut self, w: &mut impl Write, array: ArrayType) -> Result<()> {
936        let (dimensions_as_bytes, base_index, base) = self.get_array_info(array)?;
937        let base_size = self.get_data_size(base_index, &base);
938        self.emit_type(w, base)?;
939
940        let mut iter = dimensions_as_bytes.into_iter().peekable();
941        while let Some(current_level_byte_size) = iter.next() {
942            let next_level_byte_size = *iter.peek().unwrap_or(&base_size);
943            if next_level_byte_size != 0 {
944                let element_count = current_level_byte_size / next_level_byte_size;
945                write!(w, "[{}]", element_count)?;
946            } else {
947                // The base size can be zero: struct A{}; void foo(A x[10])
948                // No way to get the array dimension in such a case
949                write!(w, "[]")?;
950            };
951        }
952
953        Ok(())
954    }
955
956    fn emit_modifier(&mut self, w: &mut impl Write, modifier: ModifierType) -> Result<()> {
957        let type_data = self.parse_type_index(modifier.underlying_type)?;
958        match type_data {
959            TypeData::Pointer(ptr) => self.emit_ptr(w, ptr, modifier.constant)?,
960            TypeData::Primitive(prim) => self.emit_primitive(w, prim, modifier.constant)?,
961            _ => {
962                if modifier.constant {
963                    write!(w, "const ")?
964                }
965                self.emit_type(w, type_data)?;
966            }
967        }
968        Ok(())
969    }
970
971    fn emit_class(&mut self, w: &mut impl Write, class: ClassType) -> Result<()> {
972        if self.has_flags(TypeFormatterFlags::NAME_ONLY) {
973            write!(w, "{}", class.name)?;
974        } else {
975            let name = match class.kind {
976                ClassKind::Class => "class",
977                ClassKind::Interface => "interface",
978                ClassKind::Struct => "struct",
979            };
980            write!(w, "{} {}", name, class.name)?
981        }
982        Ok(())
983    }
984
985    fn emit_arg_list(
986        &mut self,
987        w: &mut impl Write,
988        list: ArgumentList,
989        comma_before_first: bool,
990    ) -> Result<()> {
991        if let Some((first, args)) = list.arguments.split_first() {
992            if comma_before_first {
993                write!(w, ",")?;
994                if self.has_flags(TypeFormatterFlags::SPACE_AFTER_COMMA) {
995                    write!(w, " ")?;
996                }
997            }
998            self.emit_type_index(w, *first)?;
999            for index in args.iter() {
1000                write!(w, ",")?;
1001                if self.has_flags(TypeFormatterFlags::SPACE_AFTER_COMMA) {
1002                    write!(w, " ")?;
1003                }
1004                self.emit_type_index(w, *index)?;
1005            }
1006        }
1007        Ok(())
1008    }
1009
1010    fn emit_primitive(
1011        &mut self,
1012        w: &mut impl Write,
1013        prim: PrimitiveType,
1014        is_const: bool,
1015    ) -> Result<()> {
1016        // TODO: check that these names are what we want to see
1017        let name = match prim.kind {
1018            PrimitiveKind::NoType => "<NoType>",
1019            PrimitiveKind::Void => "void",
1020            PrimitiveKind::Char => "signed char",
1021            PrimitiveKind::UChar => "unsigned char",
1022            PrimitiveKind::RChar => "char",
1023            PrimitiveKind::WChar => "wchar_t",
1024            PrimitiveKind::Char8 => "char8_t",
1025            PrimitiveKind::RChar16 => "char16_t",
1026            PrimitiveKind::RChar32 => "char32_t",
1027            PrimitiveKind::I8 => "int8_t",
1028            PrimitiveKind::U8 => "uint8_t",
1029            PrimitiveKind::Short => "short",
1030            PrimitiveKind::UShort => "unsigned short",
1031            PrimitiveKind::I16 => "int16_t",
1032            PrimitiveKind::U16 => "uint16_t",
1033            PrimitiveKind::Long => "long",
1034            PrimitiveKind::ULong => "unsigned long",
1035            PrimitiveKind::I32 => "int",
1036            PrimitiveKind::U32 => "unsigned int",
1037            PrimitiveKind::Quad => "long long",
1038            PrimitiveKind::UQuad => "unsigned long long",
1039            PrimitiveKind::I64 => "int64_t",
1040            PrimitiveKind::U64 => "uint64_t",
1041            PrimitiveKind::I128 | PrimitiveKind::Octa => "int128_t",
1042            PrimitiveKind::U128 | PrimitiveKind::UOcta => "uint128_t",
1043            PrimitiveKind::F16 => "float16_t",
1044            PrimitiveKind::F32 => "float",
1045            PrimitiveKind::F32PP => "float",
1046            PrimitiveKind::F48 => "float48_t",
1047            PrimitiveKind::F64 => "double",
1048            PrimitiveKind::F80 => "long double",
1049            PrimitiveKind::F128 => "long double",
1050            PrimitiveKind::Complex32 => "complex<float>",
1051            PrimitiveKind::Complex64 => "complex<double>",
1052            PrimitiveKind::Complex80 => "complex<long double>",
1053            PrimitiveKind::Complex128 => "complex<long double>",
1054            PrimitiveKind::Bool8 => "bool",
1055            PrimitiveKind::Bool16 => "bool16_t",
1056            PrimitiveKind::Bool32 => "bool32_t",
1057            PrimitiveKind::Bool64 => "bool64_t",
1058            PrimitiveKind::HRESULT => "HRESULT",
1059            _ => panic!("Unknown PrimitiveKind {:?} in emit_primitive", prim.kind),
1060        };
1061
1062        if prim.indirection.is_some() {
1063            if self.has_flags(TypeFormatterFlags::SPACE_BEFORE_POINTER) {
1064                if is_const {
1065                    write!(w, "{} const *", name)?
1066                } else {
1067                    write!(w, "{} *", name)?
1068                }
1069            } else if is_const {
1070                write!(w, "{} const*", name)?
1071            } else {
1072                write!(w, "{}*", name)?
1073            }
1074        } else if is_const {
1075            write!(w, "const {}", name)?
1076        } else {
1077            write!(w, "{}", name)?
1078        }
1079        Ok(())
1080    }
1081
1082    fn emit_named(&mut self, w: &mut impl Write, base: &str, name: RawString) -> Result<()> {
1083        if self.has_flags(TypeFormatterFlags::NAME_ONLY) {
1084            write!(w, "{}", name)?
1085        } else {
1086            write!(w, "{} {}", base, name)?
1087        }
1088
1089        Ok(())
1090    }
1091
1092    fn emit_type_index(&mut self, w: &mut impl Write, index: TypeIndex) -> Result<()> {
1093        match self.parse_type_index(index) {
1094            Ok(type_data) => self.emit_type(w, type_data),
1095            Err(Error::PdbError(pdb::Error::UnimplementedTypeKind(t))) => {
1096                write!(w, "<unimplemented type kind 0x{:x}>", t)?;
1097                Ok(())
1098            }
1099            Err(Error::PdbError(pdb::Error::TypeNotFound(type_index))) => {
1100                write!(w, "<missing type 0x{:x}>", type_index)?;
1101                Ok(())
1102            }
1103            Err(e) => Err(e),
1104        }
1105    }
1106
1107    fn emit_type(&mut self, w: &mut impl Write, type_data: TypeData) -> Result<()> {
1108        match self.emit_type_inner(w, type_data) {
1109            Ok(()) => Ok(()),
1110            Err(Error::PdbError(pdb::Error::TypeNotFound(type_index))) => {
1111                write!(w, "<missing type 0x{:x}>", type_index)?;
1112                Ok(())
1113            }
1114            Err(e) => Err(e),
1115        }
1116    }
1117
1118    fn emit_type_inner(&mut self, w: &mut impl Write, type_data: TypeData) -> Result<()> {
1119        match type_data {
1120            TypeData::Primitive(t) => self.emit_primitive(w, t, false)?,
1121            TypeData::Class(t) => self.emit_class(w, t)?,
1122            TypeData::MemberFunction(t) => {
1123                self.maybe_emit_return_type(w, Some(t.return_type), t.attributes)?;
1124                write!(w, "()")?;
1125                self.emit_method_args(w, t, false)?;
1126            }
1127            TypeData::Procedure(t) => {
1128                self.maybe_emit_return_type(w, t.return_type, t.attributes)?;
1129                write!(w, "()(")?;
1130                self.emit_type_index(w, t.argument_list)?;
1131                write!(w, "")?;
1132            }
1133            TypeData::ArgumentList(t) => self.emit_arg_list(w, t, false)?,
1134            TypeData::Pointer(t) => self.emit_ptr(w, t, false)?,
1135            TypeData::Array(t) => self.emit_array(w, t)?,
1136            TypeData::Union(t) => self.emit_named(w, "union", t.name)?,
1137            TypeData::Enumeration(t) => self.emit_named(w, "enum", t.name)?,
1138            TypeData::Enumerate(t) => self.emit_named(w, "enum class", t.name)?,
1139            TypeData::Modifier(t) => self.emit_modifier(w, t)?,
1140            _ => write!(w, "unhandled type /* {:?} */", type_data)?,
1141        }
1142
1143        Ok(())
1144    }
1145}
1146
1147#[derive(Eq, PartialEq)]
1148enum PtrToClassKind {
1149    PtrToGivenClass {
1150        /// If true, the pointer is a "pointer to const ClassType".
1151        constant: bool,
1152    },
1153    OtherType,
1154}
1155
1156#[derive(Debug)]
1157struct PtrAttributes {
1158    is_pointer_const: bool,
1159    is_pointee_const: bool,
1160    mode: PointerMode,
1161}
1162
1163struct ItemMap<'a, I: ItemIndex> {
1164    iter: ItemIter<'a, I>,
1165    finder: ItemFinder<'a, I>,
1166}
1167
1168impl<'a, I> ItemMap<'a, I>
1169where
1170    I: ItemIndex,
1171{
1172    pub fn try_get(&mut self, index: I) -> std::result::Result<Item<'a, I>, pdb::Error> {
1173        if index <= self.finder.max_index() {
1174            return self.finder.find(index);
1175        }
1176
1177        while let Some(item) = self.iter.next()? {
1178            self.finder.update(&self.iter);
1179            match item.index().partial_cmp(&index) {
1180                Some(Ordering::Equal) => return Ok(item),
1181                Some(Ordering::Greater) => break,
1182                _ => continue,
1183            }
1184        }
1185
1186        Err(pdb::Error::TypeNotFound(index.into()))
1187    }
1188}
1189
1190type IdMap<'a> = ItemMap<'a, IdIndex>;
1191type TypeMap<'a> = ItemMap<'a, TypeIndex>;
1192
1193struct TypeSizeCache<'a> {
1194    /// A hashmap that maps a type's (unique) name to its type size.
1195    ///
1196    /// When computing type sizes, special care must be taken for types which are
1197    /// marked as "forward references": For these types, the size must be taken from
1198    /// the occurrence of the type with the same (unique) name which is not marked as
1199    /// a forward reference.
1200    ///
1201    /// In order to be able to look up these sizes, we create a map which
1202    /// contains all sizes for non-forward_reference types. This map is populated on
1203    /// demand as the type iter is advanced.
1204    ///
1205    /// Type sizes are needed when computing array lengths based on byte lengths, when
1206    /// printing array types. They are also needed for the public get_type_size method.
1207    forward_ref_sizes: HashMap<RawString<'a>, u64>,
1208
1209    cached_ranges: RangeSet2<u32>,
1210}
1211
1212impl<'a> TypeSizeCache<'a> {
1213    pub fn get_size_for_forward_reference(
1214        &mut self,
1215        index: TypeIndex,
1216        name: RawString<'a>,
1217        type_map: &mut TypeMap<'a>,
1218    ) -> Option<u64> {
1219        if let Some(size) = self.forward_ref_sizes.get(&name) {
1220            return Some(*size);
1221        }
1222
1223        let start_index = index.0;
1224        let candidate_range = RangeSet::from((start_index + 1)..);
1225        let uncached_ranges = &candidate_range - &self.cached_ranges;
1226        for uncached_range in uncached_ranges.iter() {
1227            let (range_start, range_end) = match uncached_range {
1228                RangeSetRange::Range(r) => (*r.start, Some(*r.end)),
1229                RangeSetRange::RangeFrom(r) => (*r.start, None),
1230            };
1231            for index in range_start.. {
1232                if let Some(range_end) = range_end {
1233                    if index >= range_end {
1234                        break;
1235                    }
1236                }
1237                if let Ok(item) = type_map.try_get(TypeIndex(index)) {
1238                    let s = self.update_forward_ref_size_map(&item);
1239                    if let Some((found_name, found_size)) = s {
1240                        if found_name == name {
1241                            self.cached_ranges |= RangeSet::from(start_index..(index + 1));
1242                            return Some(found_size);
1243                        }
1244                    }
1245                } else {
1246                    break;
1247                }
1248            }
1249        }
1250        self.cached_ranges |= RangeSet::from(start_index..);
1251
1252        None
1253    }
1254
1255    pub fn update_forward_ref_size_map(
1256        &mut self,
1257        item: &Item<'a, TypeIndex>,
1258    ) -> Option<(RawString<'a>, u64)> {
1259        if let Ok(type_data) = item.parse() {
1260            match type_data {
1261                TypeData::Class(t) => {
1262                    if !t.properties.forward_reference() {
1263                        let name = t.unique_name.unwrap_or(t.name);
1264                        self.forward_ref_sizes.insert(name, t.size);
1265                        return Some((name, t.size));
1266                    }
1267                }
1268                TypeData::Union(t) => {
1269                    if !t.properties.forward_reference() {
1270                        let name = t.unique_name.unwrap_or(t.name);
1271                        self.forward_ref_sizes.insert(name, t.size);
1272                        return Some((name, t.size));
1273                    }
1274                }
1275                _ => {}
1276            }
1277        }
1278        None
1279    }
1280}