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::{BTreeSet, 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        let mut seen = BTreeSet::from([function_type_index]);
285        let mut seen = Seen::new(&mut seen);
286        match self.parse_type_index(function_type_index)? {
287            TypeData::MemberFunction(t) => {
288                if t.this_pointer_type.is_none() {
289                    self.maybe_emit_static(w)?;
290                }
291                self.maybe_emit_return_type(w, Some(t.return_type), t.attributes, &mut seen)?;
292                self.emit_name_str(w, name)?;
293                self.emit_method_args(w, t, true, &mut seen)?;
294            }
295            TypeData::Procedure(t) => {
296                self.maybe_emit_return_type(w, t.return_type, t.attributes, &mut seen)?;
297                self.emit_name_str(w, name)?;
298
299                if !self.has_flags(TypeFormatterFlags::NO_ARGUMENTS) {
300                    write!(w, "(")?;
301                    self.emit_type_index(w, t.argument_list, &mut seen)?;
302                    write!(w, ")")?;
303                }
304            }
305            _ => {
306                write!(w, "{}", name)?;
307            }
308        }
309        Ok(())
310    }
311
312    /// Write out the function or method signature, including return type (if requested),
313    /// namespace and/or class qualifiers, and arguments.
314    /// This method is used for inlined functions.
315    pub fn emit_id(&mut self, w: &mut impl Write, id_index: IdIndex) -> Result<()> {
316        let id_data = match self.parse_id_index(id_index) {
317            Ok(id_data) => id_data,
318            Err(Error::PdbError(pdb::Error::UnimplementedTypeKind(t))) => {
319                write!(w, "<unimplemented type kind 0x{:x}>", t)?;
320                return Ok(());
321            }
322            Err(Error::PdbError(pdb::Error::TypeNotFound(type_index))) => {
323                write!(w, "<missing type 0x{:x}>", type_index)?;
324                return Ok(());
325            }
326            Err(e) => return Err(e),
327        };
328        match id_data {
329            IdData::MemberFunction(m) => {
330                let mut seen = BTreeSet::from([m.function_type]);
331                let mut seen = Seen::new(&mut seen);
332                let t = match self.parse_type_index(m.function_type)? {
333                    TypeData::MemberFunction(t) => t,
334                    _ => return Err(Error::MemberFunctionIdIsNotMemberFunctionType),
335                };
336
337                if t.this_pointer_type.is_none() {
338                    self.maybe_emit_static(w)?;
339                }
340                self.maybe_emit_return_type(w, Some(t.return_type), t.attributes, &mut seen)?;
341                self.emit_type_index(w, m.parent, &mut seen)?;
342                write!(w, "::")?;
343                self.emit_name_str(w, &m.name.to_string())?;
344                self.emit_method_args(w, t, true, &mut seen)?;
345            }
346            IdData::Function(f) => {
347                let mut seen = BTreeSet::from([f.function_type]);
348                let mut seen = Seen::new(&mut seen);
349                let t = match self.parse_type_index(f.function_type)? {
350                    TypeData::Procedure(t) => t,
351                    _ => return Err(Error::FunctionIdIsNotProcedureType),
352                };
353
354                self.maybe_emit_return_type(w, t.return_type, t.attributes, &mut seen)?;
355                if let Some(scope) = f.scope {
356                    self.emit_id(w, scope)?;
357                    write!(w, "::")?;
358                }
359
360                self.emit_name_str(w, &f.name.to_string())?;
361
362                if !self.has_flags(TypeFormatterFlags::NO_ARGUMENTS) {
363                    write!(w, "(")?;
364                    self.emit_type_index(w, t.argument_list, &mut seen)?;
365                    write!(w, ")")?;
366                }
367            }
368            IdData::String(s) => {
369                let name = s.name.to_string();
370
371                if Self::is_anonymous_namespace(&name) {
372                    write!(w, "`anonymous namespace'")?;
373                } else {
374                    write!(w, "{}", name)?;
375                }
376            }
377            IdData::StringList(s) => {
378                let mut seen = BTreeSet::new();
379                let mut seen = Seen::new(&mut seen);
380                write!(w, "\"")?;
381                for (i, type_index) in s.substrings.iter().enumerate() {
382                    if i > 0 {
383                        write!(w, "\" \"")?;
384                    }
385                    self.emit_type_index(w, *type_index, &mut seen)?;
386                }
387                write!(w, "\"")?;
388            }
389            other => write!(w, "<unhandled id scope {:?}>::", other)?,
390        }
391        Ok(())
392    }
393
394    /// Checks whether the given name declares an anonymous namespace.
395    ///
396    /// ID records specify the mangled format for anonymous namespaces: `?A0x<id>`, where `id` is a hex
397    /// identifier of the namespace. Demanglers usually resolve this as "anonymous namespace".
398    fn is_anonymous_namespace(name: &str) -> bool {
399        name.strip_prefix("?A0x")
400            .is_some_and(|rest| u32::from_str_radix(rest, 16).is_ok())
401    }
402
403    fn resolve_index<I>(&mut self, index: I) -> Result<I>
404    where
405        I: ItemIndex,
406    {
407        if !index.is_cross_module() {
408            return Ok(index);
409        }
410
411        // We have a cross-module reference.
412        // First, we prepare some information which we will need below.
413
414        let string_table = self
415            .string_table
416            .ok_or(Error::CantResolveCrossModuleRefWithoutStringTable)?;
417
418        let TypeFormatterCache {
419            module_name_map,
420            module_imports,
421            module_exports,
422            ..
423        } = self.cache;
424        let modules = self.modules;
425        let module_provider = self.module_provider;
426        let self_module_index = self.module_index;
427
428        let get_module = |module_index: usize| -> Result<&'a ModuleInfo<'s>> {
429            let module = modules
430                .get(module_index)
431                .ok_or(Error::OutOfRangeModuleIndex(module_index))?;
432            let module_info = module_provider
433                .get_module_info(module_index, module)?
434                .ok_or(Error::ModuleInfoNotFound(module_index))?;
435            Ok(module_info)
436        };
437
438        let module_name_map = module_name_map.get_or_insert_with(|| {
439            modules
440                .iter()
441                .enumerate()
442                .map(|(module_index, module)| {
443                    let name = module.module_name().to_ascii_lowercase();
444                    (name, module_index)
445                })
446                .collect()
447        });
448
449        // Now we follow the steps outlined in the comment for is_cross_module.
450
451        //  1. Look up the index in [`CrossModuleImports`](crate::CrossModuleImports) of the current
452        //     module.
453        let imports = module_imports
454            .entry(self_module_index)
455            .or_insert_with(|| Ok(get_module(self_module_index)?.imports()?))
456            .as_mut()
457            .map_err(|err| mem::replace(err, Error::ModuleImportsUnsuccessful))?;
458
459        let CrossModuleRef(module_ref, local_index) = imports.resolve_import(index)?;
460
461        //  2. Use [`StringTable`](crate::StringTable) to resolve the name of the referenced module.
462        let ref_module_name = module_ref
463            .0
464            .to_string_lossy(string_table)?
465            .to_ascii_lowercase();
466
467        //  3. Find the [`Module`](crate::Module) with the same module name and load its
468        //     [`ModuleInfo`](crate::ModuleInfo).
469        let ref_module_index = *module_name_map
470            .get(&ref_module_name)
471            .ok_or(Error::ModuleNameNotFound(ref_module_name))?;
472
473        let module_exports = module_exports
474            .entry(ref_module_index)
475            .or_insert_with(|| Ok(get_module(ref_module_index)?.exports()?))
476            .as_mut()
477            .map_err(|err| mem::replace(err, Error::ModuleExportsUnsuccessful))?;
478
479        //  4. Resolve the [`Local`](crate::Local) index into a global one using
480        //     [`CrossModuleExports`](crate::CrossModuleExports).
481        let index = module_exports
482            .resolve_import(local_index)?
483            .ok_or_else(|| Error::LocalIndexNotInExports(local_index.0.into()))?;
484
485        Ok(index)
486    }
487
488    fn parse_type_index(&mut self, index: TypeIndex) -> Result<TypeData<'a>> {
489        let index = self.resolve_index(index)?;
490        let item = self.cache.type_map.try_get(index)?;
491        Ok(item.parse()?)
492    }
493
494    fn parse_id_index(&mut self, index: IdIndex) -> Result<IdData<'a>> {
495        let index = self.resolve_index(index)?;
496        let item = self.cache.id_map.try_get(index)?;
497        Ok(item.parse()?)
498    }
499
500    fn get_class_size(&mut self, index: TypeIndex, class_type: &ClassType<'a>) -> u64 {
501        if class_type.properties.forward_reference() {
502            let name = class_type.unique_name.unwrap_or(class_type.name);
503            let size = self.cache.type_size_cache.get_size_for_forward_reference(
504                index,
505                name,
506                &mut self.cache.type_map,
507            );
508
509            // Sometimes the name will not be in self.forward_ref_sizes - this can occur for
510            // the empty struct, which can be a forward reference to itself!
511            size.unwrap_or(class_type.size)
512        } else {
513            class_type.size
514        }
515    }
516
517    fn get_union_size(&mut self, index: TypeIndex, union_type: &UnionType<'a>) -> u64 {
518        if union_type.properties.forward_reference() {
519            let name = union_type.unique_name.unwrap_or(union_type.name);
520            let size = self.cache.type_size_cache.get_size_for_forward_reference(
521                index,
522                name,
523                &mut self.cache.type_map,
524            );
525
526            size.unwrap_or(union_type.size)
527        } else {
528            union_type.size
529        }
530    }
531
532    fn get_data_size(&mut self, type_index: TypeIndex, type_data: &TypeData<'a>) -> u64 {
533        match type_data {
534            TypeData::Primitive(t) => {
535                if t.indirection.is_some() {
536                    return self.ptr_size;
537                }
538                match t.kind {
539                    PrimitiveKind::NoType | PrimitiveKind::Void => 0,
540                    PrimitiveKind::Char
541                    | PrimitiveKind::UChar
542                    | PrimitiveKind::RChar
543                    | PrimitiveKind::Char8
544                    | PrimitiveKind::I8
545                    | PrimitiveKind::U8
546                    | PrimitiveKind::Bool8 => 1,
547                    PrimitiveKind::WChar
548                    | PrimitiveKind::RChar16
549                    | PrimitiveKind::Short
550                    | PrimitiveKind::UShort
551                    | PrimitiveKind::I16
552                    | PrimitiveKind::U16
553                    | PrimitiveKind::F16
554                    | PrimitiveKind::Bool16 => 2,
555                    PrimitiveKind::RChar32
556                    | PrimitiveKind::Long
557                    | PrimitiveKind::ULong
558                    | PrimitiveKind::I32
559                    | PrimitiveKind::U32
560                    | PrimitiveKind::F32
561                    | PrimitiveKind::F32PP
562                    | PrimitiveKind::Bool32
563                    | PrimitiveKind::HRESULT => 4,
564                    PrimitiveKind::I64
565                    | PrimitiveKind::U64
566                    | PrimitiveKind::Quad
567                    | PrimitiveKind::UQuad
568                    | PrimitiveKind::F64
569                    | PrimitiveKind::Complex32
570                    | PrimitiveKind::Bool64 => 8,
571                    PrimitiveKind::I128
572                    | PrimitiveKind::U128
573                    | PrimitiveKind::Octa
574                    | PrimitiveKind::UOcta
575                    | PrimitiveKind::F128
576                    | PrimitiveKind::Complex64 => 16,
577                    PrimitiveKind::F48 => 6,
578                    PrimitiveKind::F80 => 10,
579                    PrimitiveKind::Complex80 => 20,
580                    PrimitiveKind::Complex128 => 32,
581                    _ => panic!("Unknown PrimitiveKind {:?} in get_data_size", t.kind),
582                }
583            }
584            TypeData::Class(t) => self.get_class_size(type_index, t),
585            TypeData::MemberFunction(_) => self.ptr_size,
586            TypeData::Procedure(_) => self.ptr_size,
587            TypeData::Pointer(t) => t.attributes.size().into(),
588            TypeData::Array(t) => (*t.dimensions.last().unwrap()).into(),
589            TypeData::Union(t) => self.get_union_size(type_index, t),
590            TypeData::Enumeration(t) => self.get_type_size(t.underlying_type),
591            TypeData::Enumerate(t) => match t.value {
592                Variant::I8(_) | Variant::U8(_) => 1,
593                Variant::I16(_) | Variant::U16(_) => 2,
594                Variant::I32(_) | Variant::U32(_) => 4,
595                Variant::I64(_) | Variant::U64(_) => 8,
596            },
597            TypeData::Modifier(t) => self.get_type_size(t.underlying_type),
598            _ => 0,
599        }
600    }
601
602    fn has_flags(&self, flags: TypeFormatterFlags) -> bool {
603        self.flags.intersects(flags)
604    }
605
606    fn maybe_emit_static(&self, w: &mut impl Write) -> Result<()> {
607        if self.has_flags(TypeFormatterFlags::NO_MEMBER_FUNCTION_STATIC) {
608            return Ok(());
609        }
610
611        w.write_str("static ")?;
612        Ok(())
613    }
614
615    fn maybe_emit_return_type(
616        &mut self,
617        w: &mut impl Write,
618        type_index: Option<TypeIndex>,
619        attrs: FunctionAttributes,
620        seen: &mut Seen,
621    ) -> Result<()> {
622        if self.has_flags(TypeFormatterFlags::NO_FUNCTION_RETURN) {
623            return Ok(());
624        }
625
626        self.emit_return_type(w, type_index, attrs, seen)?;
627        Ok(())
628    }
629
630    fn emit_name_str(&mut self, w: &mut impl Write, name: &str) -> Result<()> {
631        if name.is_empty() {
632            write!(w, "<name omitted>")?;
633        } else {
634            write!(w, "{}", name)?;
635        }
636        Ok(())
637    }
638
639    fn emit_return_type(
640        &mut self,
641        w: &mut impl Write,
642        type_index: Option<TypeIndex>,
643        attrs: FunctionAttributes,
644        seen: &mut Seen,
645    ) -> Result<()> {
646        if !attrs.is_constructor() {
647            if let Some(index) = type_index {
648                self.emit_type_index(w, index, seen)?;
649                write!(w, " ")?;
650            }
651        }
652        Ok(())
653    }
654
655    /// Check if ptr points to the specified class, and if so, whether it points to const or non-const class.
656    /// If it points to a different class than the one supplied in the `class` argument, don'a check constness.
657    fn check_ptr_class(&mut self, ptr: TypeIndex, class: TypeIndex) -> Result<PtrToClassKind> {
658        if let TypeData::Pointer(ptr_type) = self.parse_type_index(ptr)? {
659            let underlying_type = ptr_type.underlying_type;
660            if underlying_type == class {
661                return Ok(PtrToClassKind::PtrToGivenClass { constant: false });
662            }
663            let underlying_type_data = self.parse_type_index(underlying_type)?;
664            if let TypeData::Modifier(modifier) = underlying_type_data {
665                if modifier.underlying_type == class {
666                    return Ok(PtrToClassKind::PtrToGivenClass {
667                        constant: modifier.constant,
668                    });
669                }
670            }
671        };
672        Ok(PtrToClassKind::OtherType)
673    }
674
675    /// Return value: (this is pointer to const class, optional extra first argument)
676    fn get_class_constness_and_extra_arguments(
677        &mut self,
678        this: TypeIndex,
679        class: TypeIndex,
680    ) -> Result<(bool, Option<TypeIndex>)> {
681        match self.check_ptr_class(this, class)? {
682            PtrToClassKind::PtrToGivenClass { constant } => {
683                // The this type looks normal. Don'a return an extra argument.
684                Ok((constant, None))
685            }
686            PtrToClassKind::OtherType => {
687                // The type of the "this" pointer did not match the class type.
688                // This is arguably bad type information.
689                // It looks like this bad type information is emitted for all Rust "associated
690                // functions" whose first argument is a reference. Associated functions don'a
691                // take a self argument, so it would make sense to treat them as static.
692                // But instead, these functions are marked as non-static, and the first argument's
693                // type, rather than being part of the arguments list, is stored in the "this" type.
694                // For example, for ProfileScope::new(name: &'static CStr), the arguments list is
695                // empty and the this type is CStr*.
696                // To work around this, return the this type as an extra first argument.
697                Ok((false, Some(this)))
698            }
699        }
700    }
701
702    fn emit_method_args(
703        &mut self,
704        w: &mut impl Write,
705        method_type: MemberFunctionType,
706        allow_emit_const: bool,
707        seen: &mut Seen,
708    ) -> Result<()> {
709        if self.has_flags(TypeFormatterFlags::NO_ARGUMENTS) {
710            return Ok(());
711        }
712
713        let mut seen = seen.insert_one(method_type.argument_list)?;
714
715        let args_list = match self.parse_type_index(method_type.argument_list)? {
716            TypeData::ArgumentList(t) => t,
717            _ => {
718                return Err(Error::ArgumentTypeNotArgumentList);
719            }
720        };
721
722        let (is_const_method, extra_first_arg) = match method_type.this_pointer_type {
723            None => {
724                // No this pointer - this is a static method.
725                // Static methods cannot be const, and they have the correct arguments.
726                (false, None)
727            }
728            Some(this_type) => {
729                // For non-static methods, check whether the method is const, and work around a
730                // problem with bad type information for Rust associated functions.
731                self.get_class_constness_and_extra_arguments(this_type, method_type.class_type)?
732            }
733        };
734
735        write!(w, "(")?;
736        if let Some(first_arg) = extra_first_arg {
737            self.emit_type_index(w, first_arg, &mut seen)?;
738            self.emit_arg_list(w, args_list, true, &mut seen)?;
739        } else {
740            self.emit_arg_list(w, args_list, false, &mut seen)?;
741        }
742        write!(w, ")")?;
743
744        if is_const_method && allow_emit_const {
745            write!(w, " const")?;
746        }
747
748        Ok(())
749    }
750
751    // Should we emit a space as the first byte from emit_attributes? It depends.
752    // "*" in a table cell means "value has no impact on the outcome".
753    //
754    //  caller allows space | attributes start with | SPACE_BEFORE_POINTER mode | previous byte was   | put space at the beginning?
755    // ---------------------+-----------------------+---------------------------+---------------------+----------------------------
756    //  no                  | *                     | *                         | *                   | no
757    //  yes                 | const                 | *                         | *                   | yes
758    //  yes                 | pointer sigil         | off                       | *                   | no
759    //  yes                 | pointer sigil         | on                        | pointer sigil       | no
760    //  yes                 | pointer sigil         | on                        | not a pointer sigil | yes
761    fn emit_attributes(
762        &mut self,
763        w: &mut impl Write,
764        attrs: Vec<PtrAttributes>,
765        allow_space_at_beginning: bool,
766        mut previous_byte_was_pointer_sigil: bool,
767    ) -> Result<()> {
768        let mut is_at_beginning = true;
769        for attr in attrs.iter().rev() {
770            if attr.is_pointee_const {
771                if !is_at_beginning || allow_space_at_beginning {
772                    write!(w, " ")?;
773                }
774                write!(w, "const")?;
775                is_at_beginning = false;
776                previous_byte_was_pointer_sigil = false;
777            }
778
779            if self.has_flags(TypeFormatterFlags::SPACE_BEFORE_POINTER)
780                && !previous_byte_was_pointer_sigil
781                && (!is_at_beginning || allow_space_at_beginning)
782            {
783                write!(w, " ")?;
784            }
785            is_at_beginning = false;
786            match attr.mode {
787                PointerMode::Pointer => write!(w, "*")?,
788                PointerMode::LValueReference => write!(w, "&")?,
789                PointerMode::Member => write!(w, "::*")?,
790                PointerMode::MemberFunction => write!(w, "::*")?,
791                PointerMode::RValueReference => write!(w, "&&")?,
792            }
793            previous_byte_was_pointer_sigil = true;
794            if attr.is_pointer_const {
795                write!(w, " const")?;
796                previous_byte_was_pointer_sigil = false;
797            }
798        }
799        Ok(())
800    }
801
802    fn emit_member_ptr(
803        &mut self,
804        w: &mut impl Write,
805        fun: MemberFunctionType,
806        attributes: Vec<PtrAttributes>,
807        seen: &mut Seen,
808    ) -> Result<()> {
809        self.emit_return_type(w, Some(fun.return_type), fun.attributes, seen)?;
810        write!(w, "(")?;
811        self.emit_type_index(w, fun.class_type, seen)?;
812        self.emit_attributes(w, attributes, false, false)?;
813        write!(w, ")")?;
814        self.emit_method_args(w, fun, false, seen)?;
815        Ok(())
816    }
817
818    fn emit_proc_ptr(
819        &mut self,
820        w: &mut impl Write,
821        fun: ProcedureType,
822        attributes: Vec<PtrAttributes>,
823        seen: &mut Seen,
824    ) -> Result<()> {
825        self.emit_return_type(w, fun.return_type, fun.attributes, seen)?;
826
827        write!(w, "(")?;
828        self.emit_attributes(w, attributes, false, false)?;
829        write!(w, ")")?;
830        write!(w, "(")?;
831        self.emit_type_index(w, fun.argument_list, seen)?;
832        write!(w, ")")?;
833        Ok(())
834    }
835
836    fn emit_other_ptr(
837        &mut self,
838        w: &mut impl Write,
839        type_data: TypeData,
840        attributes: Vec<PtrAttributes>,
841        seen: &mut Seen,
842    ) -> Result<()> {
843        let mut buf = String::new();
844        self.emit_type(&mut buf, type_data, seen)?;
845        let previous_byte_was_pointer_sigil = buf
846            .as_bytes()
847            .last()
848            .map(|&b| b == b'*' || b == b'&')
849            .unwrap_or(false);
850        w.write_str(&buf)?;
851        self.emit_attributes(w, attributes, true, previous_byte_was_pointer_sigil)?;
852
853        Ok(())
854    }
855
856    fn emit_ptr_helper(
857        &mut self,
858        w: &mut impl Write,
859        attributes: Vec<PtrAttributes>,
860        type_data: TypeData,
861        seen: &mut Seen,
862    ) -> Result<()> {
863        match type_data {
864            TypeData::MemberFunction(t) => self.emit_member_ptr(w, t, attributes, seen)?,
865            TypeData::Procedure(t) => self.emit_proc_ptr(w, t, attributes, seen)?,
866            _ => self.emit_other_ptr(w, type_data, attributes, seen)?,
867        };
868        Ok(())
869    }
870
871    fn emit_ptr(
872        &mut self,
873        w: &mut impl Write,
874        ptr: PointerType,
875        is_const: bool,
876        seen: &mut Seen,
877    ) -> Result<()> {
878        let mut attributes = vec![PtrAttributes {
879            is_pointer_const: ptr.attributes.is_const() || is_const,
880            is_pointee_const: false,
881            mode: ptr.attributes.pointer_mode(),
882        }];
883        let mut ptr = ptr;
884        let mut seen = seen.new_level();
885        loop {
886            seen.insert(ptr.underlying_type)?;
887
888            let type_data = self.parse_type_index(ptr.underlying_type)?;
889            match type_data {
890                TypeData::Pointer(t) => {
891                    attributes.push(PtrAttributes {
892                        is_pointer_const: t.attributes.is_const(),
893                        is_pointee_const: false,
894                        mode: t.attributes.pointer_mode(),
895                    });
896                    ptr = t;
897                }
898                TypeData::Modifier(t) => {
899                    // the vec cannot be empty since we push something in just before the loop
900                    attributes.last_mut().unwrap().is_pointee_const = t.constant;
901
902                    seen.insert(t.underlying_type)?;
903
904                    let underlying_type_data = self.parse_type_index(t.underlying_type)?;
905                    if let TypeData::Pointer(t) = underlying_type_data {
906                        attributes.push(PtrAttributes {
907                            is_pointer_const: t.attributes.is_const(),
908                            is_pointee_const: false,
909                            mode: t.attributes.pointer_mode(),
910                        });
911                        ptr = t;
912                    } else {
913                        self.emit_ptr_helper(w, attributes, underlying_type_data, &mut seen)?;
914                        return Ok(());
915                    }
916                }
917                _ => {
918                    self.emit_ptr_helper(w, attributes, type_data, &mut seen)?;
919                    return Ok(());
920                }
921            }
922        }
923    }
924
925    /// The returned Vec has the array dimensions in bytes, with the "lower" dimensions
926    /// aggregated into the "higher" dimensions.
927    fn get_array_info(&mut self, array: ArrayType) -> Result<(Vec<u64>, TypeIndex, TypeData<'a>)> {
928        // For an array int[12][34] it'll be represented as "int[34] *".
929        // For any reason the 12 is lost...
930        // The internal representation is: Pointer{ base: Array{ base: int, dim: 34 * sizeof(int)} }
931        let mut base = array;
932        let mut dims = Vec::new();
933        dims.push(base.dimensions[0].into());
934
935        // See the documentation for ArrayType::dimensions:
936        //
937        // > Contains array dimensions as specified in the PDB. This is not what you expect:
938        // >
939        // > * Dimensions are specified in terms of byte sizes, not element counts.
940        // > * Multidimensional arrays aggregate the lower dimensions into the sizes of the higher
941        // >   dimensions.
942        // >
943        // > Thus a `float[4][4]` has `dimensions: [16, 64]`. Determining array dimensions in terms
944        // > of element counts requires determining the size of the `element_type` and iteratively
945        // > dividing.
946        //
947        // XXXmstange the docs above imply that dimensions can have more than just one entry.
948        // But this code only processes dimensions[0]. Is that a bug?
949        loop {
950            let type_index = base.element_type;
951            let type_data = self.parse_type_index(type_index)?;
952            match type_data {
953                TypeData::Array(a) => {
954                    dims.push(a.dimensions[0].into());
955                    base = a;
956                }
957                _ => {
958                    return Ok((dims, type_index, type_data));
959                }
960            }
961        }
962    }
963
964    fn emit_array(&mut self, w: &mut impl Write, array: ArrayType, seen: &mut Seen) -> Result<()> {
965        let (dimensions_as_bytes, base_index, base) = self.get_array_info(array)?;
966        let base_size = self.get_data_size(base_index, &base);
967        self.emit_type(w, base, seen)?;
968
969        let mut iter = dimensions_as_bytes.into_iter().peekable();
970        while let Some(current_level_byte_size) = iter.next() {
971            let next_level_byte_size = *iter.peek().unwrap_or(&base_size);
972            if let Some(element_count) = current_level_byte_size.checked_div(next_level_byte_size) {
973                write!(w, "[{}]", element_count)?;
974            } else {
975                // The base size can be zero: struct A{}; void foo(A x[10])
976                // No way to get the array dimension in such a case
977                write!(w, "[]")?;
978            };
979        }
980
981        Ok(())
982    }
983
984    fn emit_modifier(
985        &mut self,
986        w: &mut impl Write,
987        modifier: ModifierType,
988        seen: &mut Seen,
989    ) -> Result<()> {
990        let mut seen = seen.insert_one(modifier.underlying_type)?;
991        let type_data = self.parse_type_index(modifier.underlying_type)?;
992        match type_data {
993            TypeData::Pointer(ptr) => self.emit_ptr(w, ptr, modifier.constant, &mut seen),
994            TypeData::Primitive(prim) => self.emit_primitive(w, prim, modifier.constant),
995            _ => {
996                if modifier.constant {
997                    write!(w, "const ")?
998                }
999                self.emit_type(w, type_data, &mut seen)
1000            }
1001        }
1002    }
1003
1004    fn emit_class(&mut self, w: &mut impl Write, class: ClassType) -> Result<()> {
1005        if self.has_flags(TypeFormatterFlags::NAME_ONLY) {
1006            write!(w, "{}", class.name)?;
1007        } else {
1008            let name = match class.kind {
1009                ClassKind::Class => "class",
1010                ClassKind::Interface => "interface",
1011                ClassKind::Struct => "struct",
1012            };
1013            write!(w, "{} {}", name, class.name)?
1014        }
1015        Ok(())
1016    }
1017
1018    fn emit_arg_list(
1019        &mut self,
1020        w: &mut impl Write,
1021        list: ArgumentList,
1022        comma_before_first: bool,
1023        seen: &mut Seen,
1024    ) -> Result<()> {
1025        if let Some((first, args)) = list.arguments.split_first() {
1026            if comma_before_first {
1027                write!(w, ",")?;
1028                if self.has_flags(TypeFormatterFlags::SPACE_AFTER_COMMA) {
1029                    write!(w, " ")?;
1030                }
1031            }
1032            self.emit_type_index(w, *first, seen)?;
1033            for index in args.iter() {
1034                write!(w, ",")?;
1035                if self.has_flags(TypeFormatterFlags::SPACE_AFTER_COMMA) {
1036                    write!(w, " ")?;
1037                }
1038                self.emit_type_index(w, *index, seen)?;
1039            }
1040        }
1041        Ok(())
1042    }
1043
1044    fn emit_primitive(
1045        &mut self,
1046        w: &mut impl Write,
1047        prim: PrimitiveType,
1048        is_const: bool,
1049    ) -> Result<()> {
1050        // TODO: check that these names are what we want to see
1051        let name = match prim.kind {
1052            PrimitiveKind::NoType => "<NoType>",
1053            PrimitiveKind::Void => "void",
1054            PrimitiveKind::Char => "signed char",
1055            PrimitiveKind::UChar => "unsigned char",
1056            PrimitiveKind::RChar => "char",
1057            PrimitiveKind::WChar => "wchar_t",
1058            PrimitiveKind::Char8 => "char8_t",
1059            PrimitiveKind::RChar16 => "char16_t",
1060            PrimitiveKind::RChar32 => "char32_t",
1061            PrimitiveKind::I8 => "int8_t",
1062            PrimitiveKind::U8 => "uint8_t",
1063            PrimitiveKind::Short => "short",
1064            PrimitiveKind::UShort => "unsigned short",
1065            PrimitiveKind::I16 => "int16_t",
1066            PrimitiveKind::U16 => "uint16_t",
1067            PrimitiveKind::Long => "long",
1068            PrimitiveKind::ULong => "unsigned long",
1069            PrimitiveKind::I32 => "int",
1070            PrimitiveKind::U32 => "unsigned int",
1071            PrimitiveKind::Quad => "long long",
1072            PrimitiveKind::UQuad => "unsigned long long",
1073            PrimitiveKind::I64 => "int64_t",
1074            PrimitiveKind::U64 => "uint64_t",
1075            PrimitiveKind::I128 | PrimitiveKind::Octa => "int128_t",
1076            PrimitiveKind::U128 | PrimitiveKind::UOcta => "uint128_t",
1077            PrimitiveKind::F16 => "float16_t",
1078            PrimitiveKind::F32 => "float",
1079            PrimitiveKind::F32PP => "float",
1080            PrimitiveKind::F48 => "float48_t",
1081            PrimitiveKind::F64 => "double",
1082            PrimitiveKind::F80 => "long double",
1083            PrimitiveKind::F128 => "long double",
1084            PrimitiveKind::Complex32 => "complex<float>",
1085            PrimitiveKind::Complex64 => "complex<double>",
1086            PrimitiveKind::Complex80 => "complex<long double>",
1087            PrimitiveKind::Complex128 => "complex<long double>",
1088            PrimitiveKind::Bool8 => "bool",
1089            PrimitiveKind::Bool16 => "bool16_t",
1090            PrimitiveKind::Bool32 => "bool32_t",
1091            PrimitiveKind::Bool64 => "bool64_t",
1092            PrimitiveKind::HRESULT => "HRESULT",
1093            _ => panic!("Unknown PrimitiveKind {:?} in emit_primitive", prim.kind),
1094        };
1095
1096        if prim.indirection.is_some() {
1097            if self.has_flags(TypeFormatterFlags::SPACE_BEFORE_POINTER) {
1098                if is_const {
1099                    write!(w, "{} const *", name)?
1100                } else {
1101                    write!(w, "{} *", name)?
1102                }
1103            } else if is_const {
1104                write!(w, "{} const*", name)?
1105            } else {
1106                write!(w, "{}*", name)?
1107            }
1108        } else if is_const {
1109            write!(w, "const {}", name)?
1110        } else {
1111            write!(w, "{}", name)?
1112        }
1113        Ok(())
1114    }
1115
1116    fn emit_named(&mut self, w: &mut impl Write, base: &str, name: RawString) -> Result<()> {
1117        if self.has_flags(TypeFormatterFlags::NAME_ONLY) {
1118            write!(w, "{}", name)?
1119        } else {
1120            write!(w, "{} {}", base, name)?
1121        }
1122
1123        Ok(())
1124    }
1125
1126    fn emit_type_index(
1127        &mut self,
1128        w: &mut impl Write,
1129        index: TypeIndex,
1130        seen: &mut Seen,
1131    ) -> Result<()> {
1132        match self.parse_type_index(index) {
1133            Ok(type_data) => self.emit_type(w, type_data, &mut seen.insert_one(index)?),
1134            Err(Error::PdbError(pdb::Error::UnimplementedTypeKind(t))) => {
1135                write!(w, "<unimplemented type kind 0x{:x}>", t)?;
1136                Ok(())
1137            }
1138            Err(Error::PdbError(pdb::Error::TypeNotFound(type_index))) => {
1139                write!(w, "<missing type 0x{:x}>", type_index)?;
1140                Ok(())
1141            }
1142            Err(e) => Err(e),
1143        }
1144    }
1145
1146    fn emit_type(
1147        &mut self,
1148        w: &mut impl Write,
1149        type_data: TypeData,
1150        seen: &mut Seen,
1151    ) -> Result<()> {
1152        match self.emit_type_inner(w, type_data, seen) {
1153            Ok(()) => Ok(()),
1154            Err(Error::PdbError(pdb::Error::TypeNotFound(type_index))) => {
1155                write!(w, "<missing type 0x{:x}>", type_index)?;
1156                Ok(())
1157            }
1158            Err(e) => Err(e),
1159        }
1160    }
1161
1162    fn emit_type_inner(
1163        &mut self,
1164        w: &mut impl Write,
1165        type_data: TypeData,
1166        seen: &mut Seen,
1167    ) -> Result<()> {
1168        match type_data {
1169            TypeData::Primitive(t) => self.emit_primitive(w, t, false)?,
1170            TypeData::Class(t) => self.emit_class(w, t)?,
1171            TypeData::MemberFunction(t) => {
1172                self.maybe_emit_return_type(w, Some(t.return_type), t.attributes, seen)?;
1173                write!(w, "()")?;
1174                self.emit_method_args(w, t, false, seen)?;
1175            }
1176            TypeData::Procedure(t) => {
1177                self.maybe_emit_return_type(w, t.return_type, t.attributes, seen)?;
1178                write!(w, "()(")?;
1179                self.emit_type_index(w, t.argument_list, seen)?;
1180                write!(w, "")?;
1181            }
1182            TypeData::ArgumentList(t) => self.emit_arg_list(w, t, false, seen)?,
1183            TypeData::Pointer(t) => self.emit_ptr(w, t, false, seen)?,
1184            TypeData::Array(t) => self.emit_array(w, t, seen)?,
1185            TypeData::Union(t) => self.emit_named(w, "union", t.name)?,
1186            TypeData::Enumeration(t) => self.emit_named(w, "enum", t.name)?,
1187            TypeData::Enumerate(t) => self.emit_named(w, "enum class", t.name)?,
1188            TypeData::Modifier(t) => self.emit_modifier(w, t, seen)?,
1189            _ => write!(w, "unhandled type /* {:?} */", type_data)?,
1190        }
1191
1192        Ok(())
1193    }
1194}
1195
1196#[derive(Eq, PartialEq)]
1197enum PtrToClassKind {
1198    PtrToGivenClass {
1199        /// If true, the pointer is a "pointer to const ClassType".
1200        constant: bool,
1201    },
1202    OtherType,
1203}
1204
1205#[derive(Debug)]
1206struct PtrAttributes {
1207    is_pointer_const: bool,
1208    is_pointee_const: bool,
1209    mode: PointerMode,
1210}
1211
1212struct ItemMap<'a, I: ItemIndex> {
1213    iter: ItemIter<'a, I>,
1214    finder: ItemFinder<'a, I>,
1215}
1216
1217impl<'a, I> ItemMap<'a, I>
1218where
1219    I: ItemIndex,
1220{
1221    pub fn try_get(&mut self, index: I) -> std::result::Result<Item<'a, I>, pdb::Error> {
1222        if index <= self.finder.max_index() {
1223            return self.finder.find(index);
1224        }
1225
1226        while let Some(item) = self.iter.next()? {
1227            self.finder.update(&self.iter);
1228            match item.index().partial_cmp(&index) {
1229                Some(Ordering::Equal) => return Ok(item),
1230                Some(Ordering::Greater) => break,
1231                _ => continue,
1232            }
1233        }
1234
1235        Err(pdb::Error::TypeNotFound(index.into()))
1236    }
1237}
1238
1239type IdMap<'a> = ItemMap<'a, IdIndex>;
1240type TypeMap<'a> = ItemMap<'a, TypeIndex>;
1241
1242struct TypeSizeCache<'a> {
1243    /// A hashmap that maps a type's (unique) name to its type size.
1244    ///
1245    /// When computing type sizes, special care must be taken for types which are
1246    /// marked as "forward references": For these types, the size must be taken from
1247    /// the occurrence of the type with the same (unique) name which is not marked as
1248    /// a forward reference.
1249    ///
1250    /// In order to be able to look up these sizes, we create a map which
1251    /// contains all sizes for non-forward_reference types. This map is populated on
1252    /// demand as the type iter is advanced.
1253    ///
1254    /// Type sizes are needed when computing array lengths based on byte lengths, when
1255    /// printing array types. They are also needed for the public get_type_size method.
1256    forward_ref_sizes: HashMap<RawString<'a>, u64>,
1257
1258    cached_ranges: RangeSet2<u32>,
1259}
1260
1261impl<'a> TypeSizeCache<'a> {
1262    pub fn get_size_for_forward_reference(
1263        &mut self,
1264        index: TypeIndex,
1265        name: RawString<'a>,
1266        type_map: &mut TypeMap<'a>,
1267    ) -> Option<u64> {
1268        if let Some(size) = self.forward_ref_sizes.get(&name) {
1269            return Some(*size);
1270        }
1271
1272        let start_index = index.0;
1273        let candidate_range = RangeSet::from((start_index + 1)..);
1274        let uncached_ranges = &candidate_range - &self.cached_ranges;
1275        for uncached_range in uncached_ranges.iter() {
1276            let (range_start, range_end) = match uncached_range {
1277                RangeSetRange::Range(r) => (*r.start, Some(*r.end)),
1278                RangeSetRange::RangeFrom(r) => (*r.start, None),
1279            };
1280            for index in range_start.. {
1281                if let Some(range_end) = range_end {
1282                    if index >= range_end {
1283                        break;
1284                    }
1285                }
1286                if let Ok(item) = type_map.try_get(TypeIndex(index)) {
1287                    let s = self.update_forward_ref_size_map(&item);
1288                    if let Some((found_name, found_size)) = s {
1289                        if found_name == name {
1290                            self.cached_ranges |= RangeSet::from(start_index..(index + 1));
1291                            return Some(found_size);
1292                        }
1293                    }
1294                } else {
1295                    break;
1296                }
1297            }
1298        }
1299        self.cached_ranges |= RangeSet::from(start_index..);
1300
1301        None
1302    }
1303
1304    pub fn update_forward_ref_size_map(
1305        &mut self,
1306        item: &Item<'a, TypeIndex>,
1307    ) -> Option<(RawString<'a>, u64)> {
1308        if let Ok(type_data) = item.parse() {
1309            match type_data {
1310                TypeData::Class(t) => {
1311                    if !t.properties.forward_reference() {
1312                        let name = t.unique_name.unwrap_or(t.name);
1313                        self.forward_ref_sizes.insert(name, t.size);
1314                        return Some((name, t.size));
1315                    }
1316                }
1317                TypeData::Union(t) if !t.properties.forward_reference() => {
1318                    let name = t.unique_name.unwrap_or(t.name);
1319                    self.forward_ref_sizes.insert(name, t.size);
1320                    return Some((name, t.size));
1321                }
1322                _ => {}
1323            }
1324        }
1325        None
1326    }
1327}
1328
1329#[derive(Debug, Clone)]
1330pub(crate) struct AlreadySeenError(pub(crate) TypeIndex);
1331
1332/// What has been inserted into a [`Seen`] at a given level.
1333#[derive(Debug, Clone)]
1334enum Inserted {
1335    /// Nothing has been inserted yet.
1336    Nothing,
1337    /// A single [`TypeIndex`] has been inserted.
1338    Single(TypeIndex),
1339    /// Multiple [`TypeIndex`]es have been inserted.
1340    Multiple(BTreeSet<TypeIndex>),
1341}
1342
1343/// An implementation of a "seen set" for [`TypeIndex`]es. This
1344/// is used to prevent infinite recursions due to circular
1345/// type definitions.
1346///
1347/// This structure works by keeping track of insertions into
1348/// an underlying [`BTreeSet`]. Attempting to insert an already
1349/// existing index returns an error.
1350///
1351/// An instance of this structure represents a group of insertions
1352/// that are undone together when the instance is dropped.
1353#[derive(Debug)]
1354struct Seen<'a> {
1355    set: &'a mut BTreeSet<TypeIndex>,
1356    inserted: Inserted,
1357}
1358
1359impl<'a> Seen<'a> {
1360    /// Creates a new [`Seen`] from a [`BTreeSet`], with
1361    /// no modifications.
1362    fn new(set: &'a mut BTreeSet<TypeIndex>) -> Self {
1363        Self {
1364            set,
1365            inserted: Inserted::Nothing,
1366        }
1367    }
1368
1369    /// Creates a new "level" of insertions.
1370    ///
1371    /// All elements [`inserted`](Self::insert) into the returned `Seen`
1372    /// instance are removed from the underlying set
1373    /// when the instance is dropped.
1374    fn new_level<'b>(&'b mut self) -> Seen<'b> {
1375        Seen {
1376            set: self.set,
1377            inserted: Inserted::Nothing,
1378        }
1379    }
1380
1381    /// Inserts a [`TypeIndex`] into a [`Seen`] instance.
1382    ///
1383    /// This returns an error if the underlying set already
1384    /// contains the index.
1385    fn insert(&mut self, idx: TypeIndex) -> std::result::Result<(), AlreadySeenError> {
1386        if !self.set.insert(idx) {
1387            return Err(AlreadySeenError(idx));
1388        }
1389
1390        match &mut self.inserted {
1391            Inserted::Nothing => self.inserted = Inserted::Single(idx),
1392            Inserted::Single(prev_idx) => {
1393                self.inserted = Inserted::Multiple(BTreeSet::from([*prev_idx, idx]))
1394            }
1395            Inserted::Multiple(ref mut multiple) => {
1396                multiple.insert(idx);
1397            }
1398        }
1399
1400        Ok(())
1401    }
1402
1403    /// Convenience method combining [`Self::new_level`] and [`Self::insert`].
1404    ///
1405    /// Effectively, this creates a new one-element modification group.
1406    /// This is used in the most common case where a function only
1407    /// encounters a single type index.
1408    fn insert_one<'b>(
1409        &'b mut self,
1410        idx: TypeIndex,
1411    ) -> std::result::Result<Seen<'b>, AlreadySeenError> {
1412        let mut out = self.new_level();
1413        out.insert(idx)?;
1414        Ok(out)
1415    }
1416}
1417
1418impl<'a> Drop for Seen<'a> {
1419    fn drop(&mut self) {
1420        match std::mem::replace(&mut self.inserted, Inserted::Nothing) {
1421            Inserted::Nothing => {}
1422            Inserted::Single(idx) => {
1423                self.set.remove(&idx);
1424            }
1425            Inserted::Multiple(idxs) => {
1426                for idx in idxs {
1427                    self.set.remove(&idx);
1428                }
1429            }
1430        }
1431    }
1432}
1433
1434#[cfg(test)]
1435mod tests {
1436    use std::collections::BTreeSet;
1437
1438    use pdb::TypeIndex;
1439
1440    use crate::type_formatter::Seen;
1441
1442    macro_rules! assert_contents {
1443        ($seen:expr, $($el:expr),+) => {
1444            assert_eq!(
1445                $seen.set,
1446                &mut BTreeSet::from([$(TypeIndex($el)),+])
1447            );
1448        };
1449    }
1450
1451    #[test]
1452    fn test_seen() {
1453        let mut set = BTreeSet::from([TypeIndex(0)]);
1454        let mut level_0 = Seen::new(&mut set);
1455
1456        assert_contents!(level_0, 0);
1457
1458        let mut level_1 = level_0.insert_one(TypeIndex(1)).unwrap();
1459        level_1.insert(TypeIndex(0)).unwrap_err();
1460        level_1.insert(TypeIndex(2)).unwrap();
1461
1462        assert_contents!(level_1, 0, 1, 2);
1463
1464        let mut level_2 = level_1.insert_one(TypeIndex(3)).unwrap();
1465
1466        assert_contents!(level_2, 0, 1, 2, 3);
1467
1468        let mut level_3 = level_2.new_level();
1469        level_3.insert(TypeIndex(4)).unwrap();
1470        level_3.insert(TypeIndex(5)).unwrap();
1471
1472        assert_contents!(level_3, 0, 1, 2, 3, 4, 5);
1473
1474        std::mem::drop(level_3);
1475        assert_contents!(level_2, 0, 1, 2, 3);
1476
1477        std::mem::drop(level_2);
1478        assert_contents!(level_1, 0, 1, 2);
1479
1480        std::mem::drop(level_1);
1481        assert_contents!(level_0, 0);
1482
1483        std::mem::drop(level_0);
1484
1485        assert_eq!(set, BTreeSet::from([TypeIndex(0)]));
1486    }
1487}